Esempio n. 1
0
def http_PROPFIND(self, request):
    """
    Respond to a PROPFIND request. (RFC 2518, section 8.1)
    """
    if not self.exists():
        # Return 403 if parent does not allow Bind
        parentURL = parentForURL(request.uri)
        parent = (yield request.locateResource(parentURL))
        yield parent.authorize(request, (davxml.Bind(),))

        log.error("Resource not found: %s" % (self,))
        raise HTTPError(responsecode.NOT_FOUND)

    #
    # Check authentication and access controls
    #
    yield self.authorize(request, (davxml.Read(),))

    #
    # Read request body
    #
    try:
        doc = (yield davXMLFromStream(request.stream))
    except ValueError, e:
        log.error("Error while handling PROPFIND body: %s" % (e,))
        raise HTTPError(StatusResponse(responsecode.BAD_REQUEST, str(e)))
Esempio n. 2
0
def http_MKCOL(self, request):
    """
    Respond to a MKCOL request. (RFC 2518, section 8.3)
    """
    parent = waitForDeferred(request.locateResource(parentForURL(request.uri)))
    yield parent
    parent = parent.getResult()

    x = waitForDeferred(parent.authorize(request, (davxml.Bind(), )))
    yield x
    x.getResult()

    if self.exists():
        log.error("Attempt to create collection where file exists: %s" %
                  (self, ))
        raise HTTPError(responsecode.NOT_ALLOWED)

    if not parent.isCollection():
        log.error(
            "Attempt to create collection with non-collection parent: %s" %
            (self, ))
        raise HTTPError(
            StatusResponse(responsecode.CONFLICT,
                           "Parent resource is not a collection."))

    #
    # Read request body
    #
    x = waitForDeferred(noDataFromStream(request.stream))
    yield x
    try:
        x.getResult()
    except ValueError, e:
        log.error("Error while handling MKCOL body: %s" % (e, ))
        raise HTTPError(responsecode.UNSUPPORTED_MEDIA_TYPE)
Esempio n. 3
0
def authorize(self, request):
    if self.exists():
        d = self.authorize(request, (davxml.Read(),))
    else:
        d = request.locateResource(parentForURL(request.uri))
        d.addCallback(lambda parent: parent.authorize(request, (davxml.Bind(),)))
    return d
Esempio n. 4
0
def http_PROPFIND(self, request):
    """
    Respond to a PROPFIND request. (RFC 2518, section 8.1)
    """
    if not self.exists():
        # Return 403 if parent does not allow Bind
        parentURL = parentForURL(request.uri)
        parent = (yield request.locateResource(parentURL))
        yield parent.authorize(request, (davxml.Bind(), ))

        log.error("Resource not found: %s" % (self, ))
        raise HTTPError(responsecode.NOT_FOUND)

    #
    # Check authentication and access controls
    #
    yield self.authorize(request, (davxml.Read(), ))

    #
    # Read request body
    #
    try:
        doc = (yield davXMLFromStream(request.stream))
    except ValueError, e:
        log.error("Error while handling PROPFIND body: %s" % (e, ))
        raise HTTPError(StatusResponse(responsecode.BAD_REQUEST, str(e)))
Esempio n. 5
0
def http_MKCOL(self, request):
    """
    Respond to a MKCOL request. (RFC 2518, section 8.3)
    """
    parent = waitForDeferred(request.locateResource(parentForURL(request.uri)))
    yield parent
    parent = parent.getResult()

    x = waitForDeferred(parent.authorize(request, (davxml.Bind(),)))
    yield x
    x.getResult()

    if self.exists():
        log.error("Attempt to create collection where file exists: %s"
                  % (self,))
        raise HTTPError(responsecode.NOT_ALLOWED)

    if not parent.isCollection():
        log.error("Attempt to create collection with non-collection parent: %s"
                  % (self,))
        raise HTTPError(StatusResponse(
            responsecode.CONFLICT,
            "Parent resource is not a collection."
        ))

    #
    # Read request body
    #
    x = waitForDeferred(noDataFromStream(request.stream))
    yield x
    try:
        x.getResult()
    except ValueError, e:
        log.error("Error while handling MKCOL body: %s" % (e,))
        raise HTTPError(responsecode.UNSUPPORTED_MEDIA_TYPE)
Esempio n. 6
0
def authorize(self, request):
    if self.exists():
        d = self.authorize(request, (davxml.Read(), ))
    else:
        d = request.locateResource(parentForURL(request.uri))
        d.addCallback(lambda parent: parent.authorize(request,
                                                      (davxml.Bind(), )))
    return d
Esempio n. 7
0
def http_COPY(self, request):
    """
    Respond to a COPY request. (RFC 2518, section 8.8)
    """
    r = waitForDeferred(prepareForCopy(self, request))
    yield r
    r = r.getResult()

    destination, destination_uri, depth = r

    #
    # Check authentication and access controls
    #
    x = waitForDeferred(
        self.authorize(request, (davxml.Read(), ), recurse=True))
    yield x
    x.getResult()

    if destination.exists():
        x = waitForDeferred(
            destination.authorize(
                request, (davxml.WriteContent(), davxml.WriteProperties()),
                recurse=True))
        yield x
        x.getResult()
    else:
        destparent = waitForDeferred(
            request.locateResource(parentForURL(destination_uri)))
        yield destparent
        destparent = destparent.getResult()

        x = waitForDeferred(destparent.authorize(request, (davxml.Bind(), )))
        yield x
        x.getResult()

        # May need to add a location header
        addLocation(request, destination_uri)

    # x = waitForDeferred(copy(self.fp, destination.fp, destination_uri, depth))
    x = waitForDeferred(
        put_common.storeResource(request,
                                 source=self,
                                 source_uri=request.uri,
                                 destination=destination,
                                 destination_uri=destination_uri,
                                 deletesource=False,
                                 depth=depth))
    yield x
    yield x.getResult()
Esempio n. 8
0
def http_COPY(self, request):
    """
    Respond to a COPY request. (RFC 2518, section 8.8)
    """
    r = waitForDeferred(prepareForCopy(self, request))
    yield r
    r = r.getResult()

    destination, destination_uri, depth = r

    #
    # Check authentication and access controls
    #
    x = waitForDeferred(self.authorize(request, (davxml.Read(),), recurse=True))
    yield x
    x.getResult()

    if destination.exists():
        x = waitForDeferred(destination.authorize(
            request,
            (davxml.WriteContent(), davxml.WriteProperties()),
            recurse=True
        ))
        yield x
        x.getResult()
    else:
        destparent = waitForDeferred(request.locateResource(parentForURL(destination_uri)))
        yield destparent
        destparent = destparent.getResult()

        x = waitForDeferred(destparent.authorize(request, (davxml.Bind(),)))
        yield x
        x.getResult()

        # May need to add a location header
        addLocation(request, destination_uri)

    #x = waitForDeferred(copy(self.fp, destination.fp, destination_uri, depth))
    x = waitForDeferred(put_common.storeResource(request,
                                                 source=self,
                                                 source_uri=request.uri,
                                                 destination=destination,
                                                 destination_uri=destination_uri,
                                                 deletesource=False,
                                                 depth=depth
                                                 ))
    yield x
    yield x.getResult()
Esempio n. 9
0
def preconditions_PUT(self, request):
    #
    # Check authentication and access controls
    #
    if self.exists():
        x = waitForDeferred(self.authorize(request, (davxml.WriteContent(),)))
        yield x
        x.getResult()
    else:
        parent = waitForDeferred(request.locateResource(parentForURL(request.uri)))
        yield parent
        parent = parent.getResult()

        if not parent.exists():
            raise HTTPError(
                StatusResponse(
                    responsecode.CONFLICT,
                    "cannot PUT to non-existent parent"))
        x = waitForDeferred(parent.authorize(request, (davxml.Bind(),)))
        yield x
        x.getResult()


    #
    # HTTP/1.1 (RFC 2068, section 9.6) requires that we respond with a Not
    # Implemented error if we get a Content-* header which we don't
    # recognize and handle properly.
    #
    for header, _ignore_value in request.headers.getAllRawHeaders():
        if header.startswith("Content-") and header not in (
            # "Content-Base",     # Doesn't make sense in PUT?
            # "Content-Encoding", # Requires that we decode it?
            "Content-Language",
            "Content-Length",
            # "Content-Location", # Doesn't make sense in PUT?
            "Content-MD5",
            # "Content-Range",    # FIXME: Need to implement this
            "Content-Type",
        ):
            log.error("Client sent unrecognized content header in PUT request: %s"
                      % (header,))
            raise HTTPError(StatusResponse(
                responsecode.NOT_IMPLEMENTED,
                "Unrecognized content header %r in request." % (header,)
            ))
Esempio n. 10
0
def preconditions_PUT(self, request):
    #
    # Check authentication and access controls
    #
    if self.exists():
        x = waitForDeferred(self.authorize(request, (davxml.WriteContent(), )))
        yield x
        x.getResult()
    else:
        parent = waitForDeferred(
            request.locateResource(parentForURL(request.uri)))
        yield parent
        parent = parent.getResult()

        if not parent.exists():
            raise HTTPError(
                StatusResponse(responsecode.CONFLICT,
                               "cannot PUT to non-existent parent"))
        x = waitForDeferred(parent.authorize(request, (davxml.Bind(), )))
        yield x
        x.getResult()

    #
    # HTTP/1.1 (RFC 2068, section 9.6) requires that we respond with a Not
    # Implemented error if we get a Content-* header which we don't
    # recognize and handle properly.
    #
    for header, _ignore_value in request.headers.getAllRawHeaders():
        if header.startswith("Content-") and header not in (
                # "Content-Base",     # Doesn't make sense in PUT?
                # "Content-Encoding", # Requires that we decode it?
                "Content-Language",
                "Content-Length",
                # "Content-Location", # Doesn't make sense in PUT?
                "Content-MD5",
                # "Content-Range",    # FIXME: Need to implement this
                "Content-Type",
        ):
            log.error(
                "Client sent unrecognized content header in PUT request: %s" %
                (header, ))
            raise HTTPError(
                StatusResponse(
                    responsecode.NOT_IMPLEMENTED,
                    "Unrecognized content header %r in request." % (header, )))
Esempio n. 11
0
def http_ACL(self, request):
    #
    # Override base ACL request handling to ensure that the calendar/address book
    # homes cannot have ACL's set, and calendar/address object resources too.
    #

    if self.exists():
        if isinstance(self, CalendarHomeResource) or isinstance(self, AddressBookHomeResource):
            raise HTTPError(responsecode.NOT_ALLOWED)

        parentURL = parentForURL(request.uri)
        parent = (yield request.locateResource(parentURL))
        if isPseudoCalendarCollectionResource(parent) or isAddressBookCollectionResource(parent):
            raise HTTPError(responsecode.NOT_ALLOWED)

    # Do normal ACL behavior
    response = (yield super(CalDAVResource, self).http_ACL(request))
    returnValue(response)
Esempio n. 12
0
def http_MKCALENDAR(self, request):
    """
    Respond to a MKCALENDAR request.
    (CalDAV-access-09, section 5.3.1)
    """

    #
    # 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" %
                  (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" %
            (self, ))
        raise HTTPError(
            ErrorResponse(
                responsecode.CONFLICT,
                (caldavxml.caldav_namespace,
                 "calendar-collection-location-ok"),
                "Cannot create calendar inside another calendar",
            ))

    #
    # Read request body
    #
    try:
        doc = (yield davXMLFromStream(request.stream))
        yield self.createCalendar(request)
    except ValueError, e:
        log.error("Error while handling MKCALENDAR: %s" % (e, ))
        raise HTTPError(StatusResponse(responsecode.BAD_REQUEST, str(e)))
Esempio n. 13
0
def http_ACL(self, request):
    #
    # Override base ACL request handling to ensure that the calendar/address book
    # homes cannot have ACL's set, and calendar/address object resources too.
    #

    if self.exists():
        if isinstance(self, CalendarHomeResource) or isinstance(
                self, AddressBookHomeResource):
            raise HTTPError(responsecode.NOT_ALLOWED)

        parentURL = parentForURL(request.uri)
        parent = (yield request.locateResource(parentURL))
        if isPseudoCalendarCollectionResource(
                parent) or isAddressBookCollectionResource(parent):
            raise HTTPError(responsecode.NOT_ALLOWED)

    # Do normal ACL behavior
    response = (yield super(CalDAVResource, self).http_ACL(request))
    returnValue(response)
Esempio n. 14
0
def http_MKCALENDAR(self, request):
    """
    Respond to a MKCALENDAR request.
    (CalDAV-access-09, section 5.3.1)
    """

    #
    # 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" % (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" % (self,))
        raise HTTPError(
            ErrorResponse(
                responsecode.CONFLICT,
                (caldavxml.caldav_namespace, "calendar-collection-location-ok"),
                "Cannot create calendar inside another calendar",
            )
        )

    #
    # Read request body
    #
    try:
        doc = (yield davXMLFromStream(request.stream))
        yield self.createCalendar(request)
    except ValueError, e:
        log.error("Error while handling MKCALENDAR: %s" % (e,))
        raise HTTPError(StatusResponse(responsecode.BAD_REQUEST, str(e)))
Esempio n. 15
0
def http_DELETE(self, request):
    """
    Respond to a DELETE request. (RFC 2518, section 8.6)
    """
    if not self.exists():
        log.error("File not found: %s" % (self,))
        raise HTTPError(responsecode.NOT_FOUND)

    depth = request.headers.getHeader("depth", "infinity")

    #
    # Check authentication and access controls
    #
    parent = waitForDeferred(request.locateResource(parentForURL(request.uri)))
    yield parent
    parent = parent.getResult()

    x = waitForDeferred(parent.authorize(request, (davxml.Unbind(),)))
    yield x
    x.getResult()

    x = waitForDeferred(deleteResource(request, self, request.uri, depth))
    yield x
    yield x.getResult()
Esempio n. 16
0
def http_DELETE(self, request):
    """
    Respond to a DELETE request. (RFC 2518, section 8.6)
    """
    if not self.exists():
        log.error("File not found: %s" % (self, ))
        raise HTTPError(responsecode.NOT_FOUND)

    depth = request.headers.getHeader("depth", "infinity")

    #
    # Check authentication and access controls
    #
    parent = waitForDeferred(request.locateResource(parentForURL(request.uri)))
    yield parent
    parent = parent.getResult()

    x = waitForDeferred(parent.authorize(request, (davxml.Unbind(), )))
    yield x
    x.getResult()

    x = waitForDeferred(deleteResource(request, self, request.uri, depth))
    yield x
    yield x.getResult()
Esempio n. 17
0
def http_GET(self, request):

    if self.exists():
        # Special sharing request on a calendar or address book
        if self.isCalendarCollection() or self.isAddressBookCollection():

            # Check for action=share
            if request.args:
                action = request.args.get("action", ("",))
                if len(action) != 1:
                    raise HTTPError(ErrorResponse(
                        responsecode.BAD_REQUEST,
                        (calendarserver_namespace, "valid-action"),
                        "Invalid action parameter: %s" % (action,),
                    ))
                action = action[0]

                dispatch = {
                    "share"   : self.directShare,
                }.get(action, None)

                if dispatch is None:
                    raise HTTPError(ErrorResponse(
                        responsecode.BAD_REQUEST,
                        (calendarserver_namespace, "supported-action"),
                        "Action not supported: %s" % (action,),
                    ))

                response = (yield dispatch(request))
                returnValue(response)

        else:
            # FIXME: this should be implemented in storebridge.CalendarObject.render

            # Look for calendar access restriction on existing resource.
            parentURL = parentForURL(request.uri)
            parent = (yield request.locateResource(parentURL))
            if isPseudoCalendarCollectionResource(parent):

                # Check authorization first
                yield self.authorize(request, (davxml.Read(),))

                # Accept header handling
                accepted_type = bestAcceptType(request.headers.getHeader("accept"), Component.allowedTypes())
                if accepted_type is None:
                    raise HTTPError(StatusResponse(responsecode.NOT_ACCEPTABLE, "Cannot generate requested data type"))

                caldata = (yield self.iCalendarForUser(request))

                # Filter any attendee hidden instances
                caldata = HiddenInstanceFilter().filter(caldata)

                if self.accessMode:

                    # Non DAV:owner's have limited access to the data
                    isowner = (yield self.isOwner(request))

                    # Now "filter" the resource calendar data
                    caldata = PrivateEventFilter(self.accessMode, isowner).filter(caldata)

                response = Response()
                response.stream = MemoryStream(caldata.getTextWithTimezones(includeTimezones=not config.EnableTimezonesByReference, format=accepted_type))
                response.headers.setHeader("content-type", MimeType.fromString("%s; charset=utf-8" % (accepted_type,)))

                # Add Schedule-Tag header if property is present
                if self.scheduleTag:
                    response.headers.setHeader("Schedule-Tag", self.scheduleTag)

                returnValue(response)

    # Do normal GET behavior
    response = (yield super(CalDAVResource, self).http_GET(request))
    returnValue(response)
Esempio n. 18
0
def http_MOVE(self, request):
    """
    Respond to a MOVE request. (RFC 2518, section 8.9)
    """
    r = waitForDeferred(prepareForCopy(self, request))
    yield r
    r = r.getResult()

    destination, destination_uri, depth = r

    #
    # Check authentication and access controls
    #
    parentURL = parentForURL(request.uri)
    parent = waitForDeferred(request.locateResource(parentURL))
    yield parent
    parent = parent.getResult()

    x = waitForDeferred(parent.authorize(request, (davxml.Unbind(),)))
    yield x
    x.getResult()

    if destination.exists():
        x = waitForDeferred(destination.authorize(
            request,
            (davxml.Bind(), davxml.Unbind()),
            recurse=True
        ))
        yield x
        x.getResult()
    else:
        destparentURL = parentForURL(destination_uri)
        destparent = waitForDeferred(request.locateResource(destparentURL))
        yield destparent
        destparent = destparent.getResult()

        x = waitForDeferred(destparent.authorize(request, (davxml.Bind(),)))
        yield x
        x.getResult()

        # May need to add a location header
        addLocation(request, destination_uri)

    #
    # RFC 2518, section 8.9 says that we must act as if the Depth header is set
    # to infinity, and that the client must omit the Depth header or set it to
    # infinity.
    #
    # This seems somewhat at odds with the notion that a bad request should be
    # rejected outright; if the client sends a bad depth header, the client is
    # broken, and section 8 suggests that a bad request should be rejected...
    #
    # Let's play it safe for now and ignore broken clients.
    #
    if self.isCollection() and depth != "infinity":
        msg = "Client sent illegal depth header value for MOVE: %s" % (depth,)
        log.error(msg)
        raise HTTPError(StatusResponse(responsecode.BAD_REQUEST, msg))

    # Lets optimise a move within the same directory to a new resource as a simple move
    # rather than using the full transaction based storeResource api. This allows simple
    # "rename" operations to work quickly.
    if (not destination.exists()) and destparent == parent:
        x = waitForDeferred(move(self.fp, request.uri, destination.fp, destination_uri, depth))
    else:
        x = waitForDeferred(put_common.storeResource(request,
                                                     source=self,
                                                     source_uri=request.uri,
                                                     destination=destination,
                                                     destination_uri=destination_uri,
                                                     deletesource=True,
                                                     depth=depth))
    yield x
    yield x.getResult()
Esempio n. 19
0
 def test_parentForURL(self):
     """
     parentForURL()
     """
     self.assertEquals(util.parentForURL("http://server/"), None)
     self.assertEquals(util.parentForURL("http://server//"), None)
     self.assertEquals(util.parentForURL("http://server/foo/.."), None)
     self.assertEquals(util.parentForURL("http://server/foo/../"), None)
     self.assertEquals(util.parentForURL("http://server/foo/."), "http://server/")
     self.assertEquals(util.parentForURL("http://server/foo/./"), "http://server/")
     self.assertEquals(util.parentForURL("http://server/foo"), "http://server/")
     self.assertEquals(util.parentForURL("http://server//foo"), "http://server/")
     self.assertEquals(util.parentForURL("http://server/foo/bar/.."), "http://server/")
     self.assertEquals(util.parentForURL("http://server/foo/bar/."), "http://server/foo/")
     self.assertEquals(util.parentForURL("http://server/foo/bar"), "http://server/foo/")
     self.assertEquals(util.parentForURL("http://server/foo/bar/"), "http://server/foo/")
     self.assertEquals(util.parentForURL("http://server/foo/bar?x=1&y=2"), "http://server/foo/")
     self.assertEquals(util.parentForURL("http://server/foo/bar/?x=1&y=2"), "http://server/foo/")
     self.assertEquals(util.parentForURL("/"), None)
     self.assertEquals(util.parentForURL("/foo/.."), None)
     self.assertEquals(util.parentForURL("/foo/../"), None)
     self.assertEquals(util.parentForURL("/foo/."), "/")
     self.assertEquals(util.parentForURL("/foo/./"), "/")
     self.assertEquals(util.parentForURL("/foo"), "/")
     self.assertEquals(util.parentForURL("/foo"), "/")
     self.assertEquals(util.parentForURL("/foo/bar/.."), "/")
     self.assertEquals(util.parentForURL("/foo/bar/."), "/foo/")
     self.assertEquals(util.parentForURL("/foo/bar"), "/foo/")
     self.assertEquals(util.parentForURL("/foo/bar/"), "/foo/")
     self.assertEquals(util.parentForURL("/foo/bar?x=1&y=2"), "/foo/")
     self.assertEquals(util.parentForURL("/foo/bar/?x=1&y=2"), "/foo/")
Esempio n. 20
0
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)))
Esempio n. 21
0
def report_DAV__expand_property(self, request, expand_property):
    """
    Generate an expand-property REPORT. (RFC 3253, section 3.8)

    TODO: for simplicity we will only support one level of expansion.
    """
    # Verify root element
    if not isinstance(expand_property, element.ExpandProperty):
        raise ValueError(
            "%s expected as root element, not %s." %
            (element.ExpandProperty.sname(), expand_property.sname()))

    # Only handle Depth: 0
    depth = request.headers.getHeader("depth", "0")
    if depth != "0":
        log.error("Non-zero depth is not allowed: %s" % (depth, ))
        raise HTTPError(
            StatusResponse(responsecode.BAD_REQUEST,
                           "Depth %s not allowed" % (depth, )))

    #
    # Get top level properties to expand and make sure we only have one level
    #
    properties = {}

    for property in expand_property.children:
        namespace = property.attributes.get("namespace", dav_namespace)
        name = property.attributes.get("name", "")

        # Make sure children have no children
        props_to_find = []
        for child in property.children:
            if child.children:
                log.error(
                    "expand-property REPORT only supports single level expansion"
                )
                raise HTTPError(
                    StatusResponse(
                        responsecode.NOT_IMPLEMENTED,
                        "expand-property REPORT only supports single level expansion"
                    ))
            child_namespace = child.attributes.get("namespace", dav_namespace)
            child_name = child.attributes.get("name", "")
            props_to_find.append((child_namespace, child_name))

        properties[(namespace, name)] = props_to_find

    #
    # Generate the expanded responses status for each top-level property
    #
    properties_by_status = {
        responsecode.OK: [],
        responsecode.NOT_FOUND: [],
    }

    filteredaces = None
    lastParent = None

    for qname in properties.iterkeys():
        try:
            prop = (yield self.readProperty(qname, request))

            # Form the PROPFIND-style DAV:prop element we need later
            props_to_return = element.PropertyContainer(*properties[qname])

            # Now dereference any HRefs
            responses = []
            for href in prop.children:
                if isinstance(href, element.HRef):

                    # Locate the Href resource and its parent
                    resource_uri = str(href)
                    child = (yield request.locateResource(resource_uri))

                    if not child or not child.exists():
                        responses.append(
                            element.StatusResponse(
                                href,
                                element.Status.fromResponseCode(
                                    responsecode.NOT_FOUND)))
                        continue
                    parent = (yield request.locateResource(
                        parentForURL(resource_uri)))

                    # Check privileges on parent - must have at least DAV:read
                    try:
                        yield parent.checkPrivileges(request,
                                                     (element.Read(), ))
                    except AccessDeniedError:
                        responses.append(
                            element.StatusResponse(
                                href,
                                element.Status.fromResponseCode(
                                    responsecode.FORBIDDEN)))
                        continue

                    # Cache the last parent's inherited aces for checkPrivileges optimization
                    if lastParent != parent:
                        lastParent = parent

                        # Do some optimisation of access control calculation by determining any inherited ACLs outside of
                        # the child resource loop and supply those to the checkPrivileges on each child.
                        filteredaces = (
                            yield parent.inheritedACEsforChildren(request))

                    # Check privileges - must have at least DAV:read
                    try:
                        yield child.checkPrivileges(
                            request, (element.Read(), ),
                            inherited_aces=filteredaces)
                    except AccessDeniedError:
                        responses.append(
                            element.StatusResponse(
                                href,
                                element.Status.fromResponseCode(
                                    responsecode.FORBIDDEN)))
                        continue

                    # Now retrieve all the requested properties on the HRef resource
                    yield prop_common.responseForHref(
                        request,
                        responses,
                        href,
                        child,
                        prop_common.propertyListForResource,
                        props_to_return,
                    )

            prop.children = responses
            properties_by_status[responsecode.OK].append(prop)
        except:
            f = Failure()

            log.error(
                "Error reading property {qname} for resource {req}: {failure}",
                qname=qname,
                req=request.uri,
                failure=f.value)

            status = statusForFailure(f, "getting property: %s" % (qname, ))
            if status not in properties_by_status:
                properties_by_status[status] = []
            properties_by_status[status].append(propertyName(qname))

    # Build the overall response
    propstats = [
        element.PropertyStatus(
            element.PropertyContainer(*properties_by_status[pstatus]),
            element.Status.fromResponseCode(pstatus))
        for pstatus in properties_by_status if properties_by_status[pstatus]
    ]

    returnValue(
        MultiStatusResponse(
            (element.PropertyStatusResponse(element.HRef(request.uri),
                                            *propstats), )))
Esempio n. 22
0
def http_MOVE(self, request):
    """
    Respond to a MOVE request. (RFC 2518, section 8.9)
    """
    r = waitForDeferred(prepareForCopy(self, request))
    yield r
    r = r.getResult()

    destination, destination_uri, depth = r

    #
    # Check authentication and access controls
    #
    parentURL = parentForURL(request.uri)
    parent = waitForDeferred(request.locateResource(parentURL))
    yield parent
    parent = parent.getResult()

    x = waitForDeferred(parent.authorize(request, (davxml.Unbind(), )))
    yield x
    x.getResult()

    if destination.exists():
        x = waitForDeferred(
            destination.authorize(request, (davxml.Bind(), davxml.Unbind()),
                                  recurse=True))
        yield x
        x.getResult()
    else:
        destparentURL = parentForURL(destination_uri)
        destparent = waitForDeferred(request.locateResource(destparentURL))
        yield destparent
        destparent = destparent.getResult()

        x = waitForDeferred(destparent.authorize(request, (davxml.Bind(), )))
        yield x
        x.getResult()

        # May need to add a location header
        addLocation(request, destination_uri)

    #
    # RFC 2518, section 8.9 says that we must act as if the Depth header is set
    # to infinity, and that the client must omit the Depth header or set it to
    # infinity.
    #
    # This seems somewhat at odds with the notion that a bad request should be
    # rejected outright; if the client sends a bad depth header, the client is
    # broken, and section 8 suggests that a bad request should be rejected...
    #
    # Let's play it safe for now and ignore broken clients.
    #
    if self.isCollection() and depth != "infinity":
        msg = "Client sent illegal depth header value for MOVE: %s" % (depth, )
        log.error(msg)
        raise HTTPError(StatusResponse(responsecode.BAD_REQUEST, msg))

    # Lets optimise a move within the same directory to a new resource as a simple move
    # rather than using the full transaction based storeResource api. This allows simple
    # "rename" operations to work quickly.
    if (not destination.exists()) and destparent == parent:
        x = waitForDeferred(
            move(self.fp, request.uri, destination.fp, destination_uri, depth))
    else:
        x = waitForDeferred(
            put_common.storeResource(request,
                                     source=self,
                                     source_uri=request.uri,
                                     destination=destination,
                                     destination_uri=destination_uri,
                                     deletesource=True,
                                     depth=depth))
    yield x
    yield x.getResult()
Esempio n. 23
0
def report_DAV__expand_property(self, request, expand_property):
    """
    Generate an expand-property REPORT. (RFC 3253, section 3.8)

    TODO: for simplicity we will only support one level of expansion.
    """
    # Verify root element
    if not isinstance(expand_property, element.ExpandProperty):
        raise ValueError("%s expected as root element, not %s."
                         % (element.ExpandProperty.sname(), expand_property.sname()))

    # Only handle Depth: 0
    depth = request.headers.getHeader("depth", "0")
    if depth != "0":
        log.error("Non-zero depth is not allowed: %s" % (depth,))
        raise HTTPError(StatusResponse(responsecode.BAD_REQUEST, "Depth %s not allowed" % (depth,)))

    #
    # Get top level properties to expand and make sure we only have one level
    #
    properties = {}

    for property in expand_property.children:
        namespace = property.attributes.get("namespace", dav_namespace)
        name = property.attributes.get("name", "")

        # Make sure children have no children
        props_to_find = []
        for child in property.children:
            if child.children:
                log.error("expand-property REPORT only supports single level expansion")
                raise HTTPError(StatusResponse(
                    responsecode.NOT_IMPLEMENTED,
                    "expand-property REPORT only supports single level expansion"
                ))
            child_namespace = child.attributes.get("namespace", dav_namespace)
            child_name = child.attributes.get("name", "")
            props_to_find.append((child_namespace, child_name))

        properties[(namespace, name)] = props_to_find

    #
    # Generate the expanded responses status for each top-level property
    #
    properties_by_status = {
        responsecode.OK        : [],
        responsecode.NOT_FOUND : [],
    }

    filteredaces = None
    lastParent = None

    for qname in properties.iterkeys():
        try:
            prop = (yield self.readProperty(qname, request))

            # Form the PROPFIND-style DAV:prop element we need later
            props_to_return = element.PropertyContainer(*properties[qname])

            # Now dereference any HRefs
            responses = []
            for href in prop.children:
                if isinstance(href, element.HRef):

                    # Locate the Href resource and its parent
                    resource_uri = str(href)
                    child = (yield request.locateResource(resource_uri))

                    if not child or not child.exists():
                        responses.append(element.StatusResponse(href, element.Status.fromResponseCode(responsecode.NOT_FOUND)))
                        continue
                    parent = (yield request.locateResource(parentForURL(resource_uri)))

                    # Check privileges on parent - must have at least DAV:read
                    try:
                        yield parent.checkPrivileges(request, (element.Read(),))
                    except AccessDeniedError:
                        responses.append(element.StatusResponse(href, element.Status.fromResponseCode(responsecode.FORBIDDEN)))
                        continue

                    # Cache the last parent's inherited aces for checkPrivileges optimization
                    if lastParent != parent:
                        lastParent = parent

                        # Do some optimisation of access control calculation by determining any inherited ACLs outside of
                        # the child resource loop and supply those to the checkPrivileges on each child.
                        filteredaces = (yield parent.inheritedACEsforChildren(request))

                    # Check privileges - must have at least DAV:read
                    try:
                        yield child.checkPrivileges(request, (element.Read(),), inherited_aces=filteredaces)
                    except AccessDeniedError:
                        responses.append(element.StatusResponse(href, element.Status.fromResponseCode(responsecode.FORBIDDEN)))
                        continue

                    # Now retrieve all the requested properties on the HRef resource
                    yield prop_common.responseForHref(
                        request,
                        responses,
                        href,
                        child,
                        prop_common.propertyListForResource,
                        props_to_return,
                    )

            prop.children = responses
            properties_by_status[responsecode.OK].append(prop)
        except:
            f = Failure()

            log.error(
                "Error reading property {qname} for resource {req}: {failure}",
                qname=qname, req=request.uri, failure=f.value
            )

            status = statusForFailure(f, "getting property: %s" % (qname,))
            if status not in properties_by_status:
                properties_by_status[status] = []
            properties_by_status[status].append(propertyName(qname))

    # Build the overall response
    propstats = [
        element.PropertyStatus(
            element.PropertyContainer(*properties_by_status[pstatus]),
            element.Status.fromResponseCode(pstatus)
        )
        for pstatus in properties_by_status if properties_by_status[pstatus]
    ]

    returnValue(MultiStatusResponse((element.PropertyStatusResponse(element.HRef(request.uri), *propstats),)))
Esempio n. 24
0
 def test_parentForURL(self):
     """
     parentForURL()
     """
     self.assertEquals(util.parentForURL("http://server/"), None)
     self.assertEquals(util.parentForURL("http://server//"), None)
     self.assertEquals(util.parentForURL("http://server/foo/.."), None)
     self.assertEquals(util.parentForURL("http://server/foo/../"), None)
     self.assertEquals(util.parentForURL("http://server/foo/."),
                       "http://server/")
     self.assertEquals(util.parentForURL("http://server/foo/./"),
                       "http://server/")
     self.assertEquals(util.parentForURL("http://server/foo"),
                       "http://server/")
     self.assertEquals(util.parentForURL("http://server//foo"),
                       "http://server/")
     self.assertEquals(util.parentForURL("http://server/foo/bar/.."),
                       "http://server/")
     self.assertEquals(util.parentForURL("http://server/foo/bar/."),
                       "http://server/foo/")
     self.assertEquals(util.parentForURL("http://server/foo/bar"),
                       "http://server/foo/")
     self.assertEquals(util.parentForURL("http://server/foo/bar/"),
                       "http://server/foo/")
     self.assertEquals(util.parentForURL("http://server/foo/bar?x=1&y=2"),
                       "http://server/foo/")
     self.assertEquals(util.parentForURL("http://server/foo/bar/?x=1&y=2"),
                       "http://server/foo/")
     self.assertEquals(util.parentForURL("/"), None)
     self.assertEquals(util.parentForURL("/foo/.."), None)
     self.assertEquals(util.parentForURL("/foo/../"), None)
     self.assertEquals(util.parentForURL("/foo/."), "/")
     self.assertEquals(util.parentForURL("/foo/./"), "/")
     self.assertEquals(util.parentForURL("/foo"), "/")
     self.assertEquals(util.parentForURL("/foo"), "/")
     self.assertEquals(util.parentForURL("/foo/bar/.."), "/")
     self.assertEquals(util.parentForURL("/foo/bar/."), "/foo/")
     self.assertEquals(util.parentForURL("/foo/bar"), "/foo/")
     self.assertEquals(util.parentForURL("/foo/bar/"), "/foo/")
     self.assertEquals(util.parentForURL("/foo/bar?x=1&y=2"), "/foo/")
     self.assertEquals(util.parentForURL("/foo/bar/?x=1&y=2"), "/foo/")
Esempio n. 25
0
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"
                  % (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"
                  % (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: %s" % (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))
        )
Esempio n. 26
0
def http_GET(self, request):

    if self.exists():
        # Special sharing request on a calendar or address book
        if self.isCalendarCollection() or self.isAddressBookCollection():

            # Check for action=share
            if request.args:
                action = request.args.get("action", ("",))
                if len(action) != 1:
                    raise HTTPError(ErrorResponse(
                        responsecode.BAD_REQUEST,
                        (calendarserver_namespace, "valid-action"),
                        "Invalid action parameter: %s" % (action,),
                    ))
                action = action[0]

                dispatch = {
                    "share": self.directShare,
                }.get(action, None)

                if dispatch is None:
                    raise HTTPError(ErrorResponse(
                        responsecode.BAD_REQUEST,
                        (calendarserver_namespace, "supported-action"),
                        "Action not supported: %s" % (action,),
                    ))

                response = (yield dispatch(request))
                returnValue(response)

        else:
            # FIXME: this should be implemented in storebridge.CalendarObject.render

            # Look for calendar access restriction on existing resource.
            parentURL = parentForURL(request.uri)
            parent = (yield request.locateResource(parentURL))
            if isPseudoCalendarCollectionResource(parent):

                # Check authorization first
                yield self.authorize(request, (davxml.Read(),))

                # Accept header handling
                accepted_type = bestAcceptType(request.headers.getHeader("accept"), Component.allowedTypes())
                if accepted_type is None:
                    raise HTTPError(StatusResponse(responsecode.NOT_ACCEPTABLE, "Cannot generate requested data type"))

                caldata = (yield self.componentForUser())

                # Filter any attendee hidden instances
                caldata = HiddenInstanceFilter().filter(caldata)

                if self.accessMode:

                    # Non DAV:owner's have limited access to the data
                    isowner = (yield self.isOwner(request))

                    # Now "filter" the resource calendar data
                    caldata = PrivateEventFilter(self.accessMode, isowner).filter(caldata)

                response = Response()
                response.stream = MemoryStream(caldata.getTextWithTimezones(includeTimezones=not config.EnableTimezonesByReference, format=accepted_type))
                response.headers.setHeader("content-type", MimeType.fromString("%s; charset=utf-8" % (accepted_type,)))

                # Add Schedule-Tag header if property is present
                if self.scheduleTag:
                    response.headers.setHeader("Schedule-Tag", self.scheduleTag)

                returnValue(response)

    # Do normal GET behavior
    response = (yield super(CalDAVResource, self).http_GET(request))
    returnValue(response)