예제 #1
0
def responseForHref(request, responses, href, resource, propertiesForResource,
                    propertyreq):

    if propertiesForResource is not None:
        properties_by_status = waitForDeferred(
            propertiesForResource(request, propertyreq, resource))
        yield properties_by_status
        properties_by_status = properties_by_status.getResult()

        propstats = []

        for status in properties_by_status:
            properties = properties_by_status[status]
            if properties:
                xml_status = element.Status.fromResponseCode(status)
                xml_container = element.PropertyContainer(*properties)
                xml_propstat = element.PropertyStatus(xml_container,
                                                      xml_status)

                propstats.append(xml_propstat)

        if propstats:
            responses.append(element.PropertyStatusResponse(href, *propstats))

    else:
        responses.append(
            element.StatusResponse(
                href,
                element.Status.fromResponseCode(responsecode.OK),
            ))
예제 #2
0
    def test_calendar_query_bogus_timezone_id(self):
        """
        Partial retrieval of events by time range.
        (CalDAV-access-09, section 7.6.1)
        """
        TimezoneCache.create()
        self.addCleanup(TimezoneCache.clear)

        calendar_properties = (
            davxml.GETETag(),
            caldavxml.CalendarData(),
        )

        query_timerange = caldavxml.TimeRange(
            start="%04d1001T000000Z" % (DateTime.getToday().getYear(), ),
            end="%04d1101T000000Z" % (DateTime.getToday().getYear(), ),
        )

        query = caldavxml.CalendarQuery(
            davxml.PropertyContainer(*calendar_properties),
            caldavxml.Filter(
                caldavxml.ComponentFilter(
                    caldavxml.ComponentFilter(
                        query_timerange,
                        name="VEVENT",
                    ),
                    name="VCALENDAR",
                ), ),
            caldavxml.TimeZoneID.fromString("bogus"),
        )

        result = yield self.calendar_query(
            query, got_xml=None, expected_code=responsecode.FORBIDDEN)
        self.assertTrue("valid-timezone" in result)
예제 #3
0
    def test_PROPPATCH_liveprop(self):
        """
        PROPPATCH on a live property
        """
        prop = davxml.GETETag.fromString("some-etag-string")
        patch = davxml.PropertyUpdate(davxml.Set(davxml.PropertyContainer(prop)))

        return self._simple_PROPPATCH(patch, prop, responsecode.FORBIDDEN, "edit of live property")
예제 #4
0
    def test_PROPPATCH_exists_not(self):
        """
        PROPPATCH remove a non-existant property
        """
        prop = davxml.Timeout() # Timeout isn't a valid property, so it won't exist.
        patch = davxml.PropertyUpdate(davxml.Remove(davxml.PropertyContainer(prop)))

        return self._simple_PROPPATCH(patch, prop, responsecode.OK, "remove of non-existant property")
예제 #5
0
    def principalSearchPropertySet(self):
        props = []
        for _ignore_field, _ignore_converter, description, xmlClass in self._fieldList:
            props.append(
                davxml.PrincipalSearchProperty(
                    davxml.PropertyContainer(xmlClass()),
                    davxml.Description(davxml.PCDATAElement(description),
                                       **{"xml:lang": "en"}),
                ))

        return davxml.PrincipalSearchPropertySet(*props)
예제 #6
0
    def _defer(properties_by_status):
        propstats = []

        for status in properties_by_status:
            properties = properties_by_status[status]
            if properties:
                xml_status = element.Status.fromResponseCode(status)
                xml_container = element.PropertyContainer(*properties)
                xml_propstat = element.PropertyStatus(xml_container, xml_status)

                propstats.append(xml_propstat)

        # Always need to have at least one propstat present (required by Prefer header behavior)
        if len(propstats) == 0:
            propstats.append(element.PropertyStatus(
                element.PropertyContainer(),
                element.Status.fromResponseCode(responsecode.OK)
            ))

        if propstats:
            responses.append(element.PropertyStatusResponse(href, *propstats))
예제 #7
0
    def test_calendar_query_timezone(self):
        """
        Partial retrieval of events by time range.
        (CalDAV-access-09, section 7.6.1)
        """
        TimezoneCache.create()
        self.addCleanup(TimezoneCache.clear)

        tzid1 = "Etc/GMT+1"
        tz1 = Component(None, pycalendar=readVTZ(tzid1))

        calendar_properties = (
            davxml.GETETag(),
            caldavxml.CalendarData(),
        )

        query_timerange = caldavxml.TimeRange(
            start="%04d1001T000000Z" % (DateTime.getToday().getYear(), ),
            end="%04d1101T000000Z" % (DateTime.getToday().getYear(), ),
        )

        query = caldavxml.CalendarQuery(
            davxml.PropertyContainer(*calendar_properties),
            caldavxml.Filter(
                caldavxml.ComponentFilter(
                    caldavxml.ComponentFilter(
                        query_timerange,
                        name="VEVENT",
                    ),
                    name="VCALENDAR",
                ), ),
            caldavxml.TimeZone.fromCalendar(tz1),
        )

        def got_xml(doc):
            if not isinstance(doc.root_element, davxml.MultiStatus):
                self.fail(
                    "REPORT response XML root element is not multistatus: %r" %
                    (doc.root_element, ))

        return self.calendar_query(query, got_xml)
예제 #8
0
    def test_calendar_query_wrong_timezone_elements(self):
        """
        Partial retrieval of events by time range.
        (CalDAV-access-09, section 7.6.1)
        """
        TimezoneCache.create()
        self.addCleanup(TimezoneCache.clear)

        tzid1 = "Etc/GMT+1"
        tz1 = Component(None, pycalendar=readVTZ(tzid1))

        calendar_properties = (
            davxml.GETETag(),
            caldavxml.CalendarData(),
        )

        query_timerange = caldavxml.TimeRange(
            start="%04d1001T000000Z" % (DateTime.getToday().getYear(), ),
            end="%04d1101T000000Z" % (DateTime.getToday().getYear(), ),
        )

        query = caldavxml.CalendarQuery(
            davxml.PropertyContainer(*calendar_properties),
            caldavxml.Filter(
                caldavxml.ComponentFilter(
                    caldavxml.ComponentFilter(
                        query_timerange,
                        name="VEVENT",
                    ),
                    name="VCALENDAR",
                ), ),
            caldavxml.TimeZone.fromCalendar(tz1),
        )
        query.children += (caldavxml.TimeZoneID.fromString(tzid1), )

        result = yield self.calendar_query(
            query, got_xml=None, expected_code=responsecode.BAD_REQUEST)
        self.assertTrue("Only one of" in result)
예제 #9
0
    def add(self, what, property):
        """
        Add a response.
        @param what: a status code or a L{Failure} for the given path.
        @param property: the property whose status is being reported.
        """
        if type(what) is int:
            code = what
            error = None
            message = responsecode.RESPONSES[code]
        elif isinstance(what, Failure):
            code = statusForFailure(what)
            error = errorForFailure(what)
            message = messageForFailure(what)
        else:
            raise AssertionError("Unknown data type: %r" % (what, ))

        if len(property.children) > 0:
            # Re-instantiate as empty element.
            property = element.WebDAVUnknownElement.withName(
                property.namespace, property.name)

        if code > 400:  # Error codes only
            log.error("Error during {method} for {property}: {msg}",
                      method=self.method,
                      property=property,
                      msg=message)

        children = []
        children.append(element.PropertyContainer(property))
        children.append(element.Status.fromResponseCode(code))
        if error is not None:
            children.append(error)
        if message is not None:
            children.append(element.ResponseDescription(message))
        self.propstats.append(element.PropertyStatus(*children))
예제 #10
0
    def test_make_calendar_with_props(self):
        """
        Make calendar with properties (CalDAV-access-09, section 5.3.1.2)
        """
        uri = "/calendars/users/user01/calendar_prop/"
        path = os.path.join(self.docroot, uri[1:])

        if os.path.exists(path):
            rmdir(path)

        @inlineCallbacks
        def do_test(response):
            response = IResponse(response)

            if response.code != responsecode.CREATED:
                self.fail("MKCALENDAR failed: %s" % (response.code, ))

            resource = (yield request.locateResource(uri))
            if not resource.isCalendarCollection():
                self.fail("MKCALENDAR made non-calendar collection")

            for qname, value in (
                (davxml.DisplayName.qname(), "Lisa's Events"),
                (caldavxml.CalendarDescription.qname(),
                 "Calendar restricted to events."),
            ):
                stored = yield resource.readProperty(qname, None)
                stored = str(stored)
                if stored != value:
                    self.fail(
                        "MKCALENDAR failed to set property %s: %s != %s" %
                        (qname, stored, value))

            supported_components = yield resource.readProperty(
                caldavxml.SupportedCalendarComponentSet, None)
            supported_components = supported_components.children
            if len(supported_components) != 1:
                self.fail(
                    "MKCALENDAR failed to set property %s: len(%s) != 1" %
                    (caldavxml.SupportedCalendarComponentSet.qname(),
                     supported_components))
            if supported_components[0] != caldavxml.CalendarComponent(
                    name="VEVENT"):
                self.fail("MKCALENDAR failed to set property %s: %s != %s" %
                          (caldavxml.SupportedCalendarComponentSet.qname(),
                           supported_components[0].toxml(),
                           caldavxml.CalendarComponent(name="VEVENT").toxml()))

            tz = (yield resource.readProperty(caldavxml.CalendarTimeZone,
                                              None))
            tz = tz.calendar()
            self.failUnless(tz.resourceType() == "VTIMEZONE")
            self.failUnless(
                tuple(tz.subcomponents())[0].propertyValue("TZID") ==
                "US-Eastern")

        mk = caldavxml.MakeCalendar(
            davxml.Set(
                davxml.PropertyContainer(
                    davxml.DisplayName("Lisa's Events"),
                    caldavxml.CalendarDescription(
                        "Calendar restricted to events."),  # FIXME: lang=en
                    caldavxml.SupportedCalendarComponentSet(
                        caldavxml.CalendarComponent(name="VEVENT")),
                    caldavxml.CalendarTimeZone("""BEGIN:VCALENDAR
PRODID:-//Example Corp.//CalDAV Client//EN
VERSION:2.0
BEGIN:VTIMEZONE
TZID:US-Eastern
LAST-MODIFIED:19870101T000000Z
BEGIN:STANDARD
DTSTART:19671029T020000
RRULE:FREQ=YEARLY;BYDAY=-1SU;BYMONTH=10
TZOFFSETFROM:-0400
TZOFFSETTO:-0500
TZNAME:Eastern Standard Time (US & Canada)
END:STANDARD
BEGIN:DAYLIGHT
DTSTART:19870405T020000
RRULE:FREQ=YEARLY;BYDAY=1SU;BYMONTH=4
TZOFFSETFROM:-0500
TZOFFSETTO:-0400
TZNAME:Eastern Daylight Time (US & Canada)
END:DAYLIGHT
END:VTIMEZONE
END:VCALENDAR
"""))))

        request = SimpleStoreRequest(self,
                                     "MKCALENDAR",
                                     uri,
                                     authPrincipal=self.authPrincipal)
        request.stream = MemoryStream(mk.toxml())
        return self.send(request, do_test)
예제 #11
0
    def simple_event_multiget(self,
                              cal_uri,
                              okuids,
                              baduids,
                              data=None,
                              no_init=False,
                              withData=True):

        cal_uri = joinURL("/calendars/users/wsanchez", cal_uri)
        props = (davxml.GETETag(), )
        if withData:
            props += (caldavxml.CalendarData(), )
        children = []
        children.append(davxml.PropertyContainer(*props))

        okhrefs = [joinURL(cal_uri, x + ".ics") for x in okuids]
        badhrefs = [joinURL(cal_uri, x + ".ics") for x in baduids]
        for href in okhrefs + badhrefs:
            children.append(davxml.HRef.fromString(href))

        query = caldavxml.CalendarMultiGet(*children)

        def got_xml(doc):
            if not isinstance(doc.root_element, davxml.MultiStatus):
                self.fail(
                    "REPORT response XML root element is not multistatus: %r" %
                    (doc.root_element, ))

            for response in doc.root_element.childrenOfType(
                    davxml.PropertyStatusResponse):
                href = str(response.childOfType(davxml.HRef))
                for propstat in response.childrenOfType(davxml.PropertyStatus):
                    status = propstat.childOfType(davxml.Status)

                    if status.code != responsecode.OK:
                        self.fail(
                            "REPORT failed (status %s) to locate properties: %r"
                            % (status.code, href))

                    properties = propstat.childOfType(
                        davxml.PropertyContainer).children

                    for property in properties:
                        qname = property.qname()
                        if qname == (davxml.dav_namespace, "getetag"):
                            continue
                        if qname != (caldavxml.caldav_namespace,
                                     "calendar-data"):
                            self.fail(
                                "Response included unexpected property %r" %
                                (property, ))

                        result_calendar = property.calendar()

                        if result_calendar is None:
                            self.fail(
                                "Invalid response CalDAV:calendar-data: %r" %
                                (property, ))

                        uid = result_calendar.resourceUID()

                        if uid in okuids:
                            okuids.remove(uid)
                        else:
                            self.fail("Got calendar for unexpected UID %r" %
                                      (uid, ))

                        if data:
                            original_calendar = ical.Component.fromString(
                                data[uid])
                        else:
                            original_filename = file(
                                os.path.join(self.holidays_dir, uid + ".ics"))
                            original_calendar = ical.Component.fromStream(
                                original_filename)

                        self.assertEqual(result_calendar, original_calendar)

            for response in doc.root_element.childrenOfType(
                    davxml.StatusResponse):
                href = str(response.childOfType(davxml.HRef))
                propstatus = response.childOfType(davxml.PropertyStatus)
                if propstatus is not None:
                    status = propstatus.childOfType(davxml.Status)
                else:
                    status = response.childOfType(davxml.Status)
                if status.code != responsecode.OK:
                    if href in okhrefs:
                        self.fail(
                            "REPORT failed (status %s) to locate properties: %r"
                            % (status.code, href))
                    else:
                        if href in badhrefs:
                            badhrefs.remove(href)
                            continue
                        else:
                            self.fail("Got unexpected href %r" % (href, ))

            if withData and (len(okuids) + len(badhrefs)):
                self.fail("Some components were not returned: %r, %r" %
                          (okuids, badhrefs))

        return self.calendar_query(cal_uri, query, got_xml, data, no_init)
    def simple_vcard_query(self, vcard_uri, vcard_filter, uids, withData=True, limit=None):

        vcard_uri = joinURL("/addressbooks/users/wsanchez", vcard_uri)

        props = (
            davxml.GETETag(),
        )
        if withData:
            props += (
                carddavxml.AddressData(),
            )
        query = carddavxml.AddressBookQuery(
            davxml.PropertyContainer(*props),
            carddavxml.Filter(
                vcard_filter,
            ),
        )

        def got_xml(doc):
            if not isinstance(doc.root_element, davxml.MultiStatus):
                self.fail("REPORT response XML root element is not multistatus: %r" % (doc.root_element,))

            count = 0
            for response in doc.root_element.childrenOfType(davxml.PropertyStatusResponse):
                for propstat in response.childrenOfType(davxml.PropertyStatus):
                    status = propstat.childOfType(davxml.Status)

                    if status.code == responsecode.INSUFFICIENT_STORAGE_SPACE and limit is not None:
                        continue
                    if status.code != responsecode.OK:
                        self.fail("REPORT failed (status %s) to locate properties: %r"
                                  % (status.code, propstat))
                    elif limit is not None:
                        count += 1
                        continue

                    properties = propstat.childOfType(davxml.PropertyContainer).children

                    for property in properties:
                        qname = property.qname()
                        if qname == (davxml.dav_namespace, "getetag"):
                            continue
                        if qname != (carddavxml.carddav_namespace, "address-data"):
                            self.fail("Response included unexpected property %r" % (property,))

                        result_addressbook = property.address()

                        if result_addressbook is None:
                            self.fail("Invalid response CardDAV:address-data: %r" % (property,))

                        uid = result_addressbook.resourceUID()

                        if uid in uids:
                            uids.remove(uid)
                        else:
                            self.fail("Got addressbook for unexpected UID %r" % (uid,))

                        original_filename = file(os.path.join(self.vcards_dir, uid + ".vcf"))
                        original_addressbook = vcard.Component.fromStream(original_filename)

                        self.assertEqual(result_addressbook, original_addressbook)

                if limit is not None and count != limit:
                    self.fail("Wrong number of limited results: %d" % (count,))

        return self.addressbook_query(vcard_uri, query, got_xml)
예제 #13
0
        def mkcalendar_cb(response):
            response = IResponse(response)

            if response.code != responsecode.CREATED:
                self.fail("MKCALENDAR failed: %s" % (response.code,))

            def propfind_cb(response):
                response = IResponse(response)

                if response.code != responsecode.MULTI_STATUS:
                    self.fail("Incorrect response to PROPFIND: %s" % (response.code,))

                def got_xml(doc):
                    if not isinstance(doc.root_element, davxml.MultiStatus):
                        self.fail("PROPFIND response XML root element is not multistatus: %r" % (doc.root_element,))

                    response = doc.root_element.childOfType(davxml.Response)
                    href = response.childOfType(davxml.HRef)
                    self.failUnless(str(href) == calendar_uri)

                    for propstat in response.childrenOfType(davxml.PropertyStatus):
                        status = propstat.childOfType(davxml.Status)
                        if status.code != responsecode.OK:
                            self.fail("Unable to read requested properties (%s): %r"
                                      % (status, propstat.childOfType(davxml.PropertyContainer).toxml()))

                    container = propstat.childOfType(davxml.PropertyContainer)

                    #
                    # Check CalDAV:supported-calendar-component-set
                    #

                    supported_components = container.childOfType(caldavxml.SupportedCalendarComponentSet)
                    if not supported_components:
                        self.fail("Expected CalDAV:supported-calendar-component-set element; but got none.")

                    supported = set(("VEVENT",))

                    for component in supported_components.children:
                        if component.type in supported:
                            supported.remove(component.type)

                    if supported:
                        self.fail("Expected supported calendar component types: %s" % (tuple(supported),))

                    #
                    # Check CalDAV:supported-calendar-data
                    #

                    supported_calendar = container.childOfType(caldavxml.SupportedCalendarData)
                    if not supported_calendar:
                        self.fail("Expected CalDAV:supported-calendar-data element; but got none.")

                    for calendar in supported_calendar.children:
                        if calendar.content_type not in ("text/calendar", "application/calendar+json"):
                            self.fail("Expected a text/calendar calendar-data type restriction")
                        if calendar.version != "2.0":
                            self.fail("Expected a version 2.0 calendar-data restriction")

                    #
                    # Check DAV:supported-report-set
                    #

                    supported_reports = container.childOfType(davxml.SupportedReportSet)
                    if not supported_reports:
                        self.fail("Expected DAV:supported-report-set element; but got none.")

                    cal_query = False
                    cal_multiget = False
                    cal_freebusy = False
                    for supported in supported_reports.childrenOfType(davxml.SupportedReport):
                        report = supported.childOfType(davxml.Report)
                        if report.childOfType(caldavxml.CalendarQuery) is not None:
                            cal_query = True
                        if report.childOfType(caldavxml.CalendarMultiGet) is not None:
                            cal_multiget = True
                        if report.childOfType(caldavxml.FreeBusyQuery) is not None:
                            cal_freebusy = True

                    if not cal_query:
                        self.fail("Expected CalDAV:CalendarQuery element; but got none.")
                    if not cal_multiget:
                        self.fail("Expected CalDAV:CalendarMultiGet element; but got none.")
                    if not cal_freebusy:
                        self.fail("Expected CalDAV:FreeBusyQuery element; but got none.")

                return davXMLFromStream(response.stream).addCallback(got_xml)

            query = davxml.PropertyFind(
                davxml.PropertyContainer(
                    caldavxml.SupportedCalendarData(),
                    caldavxml.SupportedCalendarComponentSet(),
                    davxml.SupportedReportSet(),
                ),
            )

            request = SimpleStoreRequest(
                self,
                "PROPFIND",
                calendar_uri,
                headers=http_headers.Headers({"Depth": "0"}),
                authPrincipal=self.authPrincipal,
            )
            request.stream = MemoryStream(query.toxml())
            return self.send(request, propfind_cb)
예제 #14
0
    def test_calendar_query_time_range(self):
        """
        Partial retrieval of events by time range.
        (CalDAV-access-09, section 7.6.1)
        """
        calendar_properties = (
            davxml.GETETag(),
            caldavxml.CalendarData(
                caldavxml.CalendarComponent(
                    caldavxml.AllProperties(),
                    caldavxml.CalendarComponent(
                        caldavxml.Property(name="X-ABC-GUID"),
                        caldavxml.Property(name="UID"),
                        caldavxml.Property(name="DTSTART"),
                        caldavxml.Property(name="DTEND"),
                        caldavxml.Property(name="DURATION"),
                        caldavxml.Property(name="EXDATE"),
                        caldavxml.Property(name="EXRULE"),
                        caldavxml.Property(name="RDATE"),
                        caldavxml.Property(name="RRULE"),
                        caldavxml.Property(name="LOCATION"),
                        caldavxml.Property(name="SUMMARY"),
                        name="VEVENT",
                    ),
                    caldavxml.CalendarComponent(
                        caldavxml.AllProperties(),
                        caldavxml.AllComponents(),
                        name="VTIMEZONE",
                    ),
                    name="VCALENDAR",
                ), ),
        )

        query_timerange = caldavxml.TimeRange(
            start="%04d1001T000000Z" % (DateTime.getToday().getYear(), ),
            end="%04d1101T000000Z" % (DateTime.getToday().getYear(), ),
        )

        query = caldavxml.CalendarQuery(
            davxml.PropertyContainer(*calendar_properties),
            caldavxml.Filter(
                caldavxml.ComponentFilter(
                    caldavxml.ComponentFilter(
                        query_timerange,
                        name="VEVENT",
                    ),
                    name="VCALENDAR",
                ), ),
        )

        def got_xml(doc):
            if not isinstance(doc.root_element, davxml.MultiStatus):
                self.fail(
                    "REPORT response XML root element is not multistatus: %r" %
                    (doc.root_element, ))

            for response in doc.root_element.childrenOfType(
                    davxml.PropertyStatusResponse):
                properties_to_find = [p.qname() for p in calendar_properties]

                for propstat in response.childrenOfType(davxml.PropertyStatus):
                    status = propstat.childOfType(davxml.Status)
                    properties = propstat.childOfType(
                        davxml.PropertyContainer).children

                    if status.code != responsecode.OK:
                        self.fail(
                            "REPORT failed (status %s) to locate properties: %r"
                            % (status.code, properties))

                    for property in properties:
                        qname = property.qname()
                        if qname in properties_to_find:
                            properties_to_find.remove(qname)
                        else:
                            self.fail(
                                "REPORT found property we didn't ask for: %r" %
                                (property, ))

                        if isinstance(property, caldavxml.CalendarData):
                            cal = property.calendar()
                            instances = cal.expandTimeRanges(
                                query_timerange.end)
                            vevents = [
                                x for x in cal.subcomponents()
                                if x.name() == "VEVENT"
                            ]
                            if not TimeRange(query_timerange).matchinstance(
                                    vevents[0], instances):
                                self.fail(
                                    "REPORT property %r returned calendar %s outside of request time range %r"
                                    % (property, property.calendar,
                                       query_timerange))

        return self.calendar_query(query, got_xml)
예제 #15
0
    def simple_event_query(self, event_filter, uids, withData=True):
        props = (davxml.GETETag(), )
        if withData:
            props += (caldavxml.CalendarData(), )
        query = caldavxml.CalendarQuery(
            davxml.PropertyContainer(*props),
            caldavxml.Filter(
                caldavxml.ComponentFilter(
                    caldavxml.ComponentFilter(
                        event_filter,
                        name="VEVENT",
                    ),
                    name="VCALENDAR",
                ), ),
        )

        def got_xml(doc):
            if not isinstance(doc.root_element, davxml.MultiStatus):
                self.fail(
                    "REPORT response XML root element is not multistatus: %r" %
                    (doc.root_element, ))

            for response in doc.root_element.childrenOfType(
                    davxml.PropertyStatusResponse):
                for propstat in response.childrenOfType(davxml.PropertyStatus):
                    status = propstat.childOfType(davxml.Status)

                    if status.code != responsecode.OK:
                        self.fail(
                            "REPORT failed (status %s) to locate properties: %r"
                            % (status.code, propstat))

                    properties = propstat.childOfType(
                        davxml.PropertyContainer).children

                    for property in properties:
                        qname = property.qname()
                        if qname == (davxml.dav_namespace, "getetag"):
                            continue
                        if qname != (caldavxml.caldav_namespace,
                                     "calendar-data"):
                            self.fail(
                                "Response included unexpected property %r" %
                                (property, ))

                        result_calendar = property.calendar()

                        if result_calendar is None:
                            self.fail(
                                "Invalid response CalDAV:calendar-data: %r" %
                                (property, ))

                        uid = result_calendar.resourceUID()

                        if uid in uids:
                            uids.remove(uid)
                        else:
                            self.fail("Got calendar for unexpected UID %r" %
                                      (uid, ))

                        original_filename = file(
                            os.path.join(self.holidays_dir, uid + ".ics"))
                        original_calendar = ical.Component.fromStream(
                            original_filename)

                        self.assertEqual(result_calendar, original_calendar)

        return self.calendar_query(query, got_xml)
예제 #16
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), )))
예제 #17
0
    def test_PROPPATCH_basic(self):
        """
        PROPPATCH
        """

        # FIXME:
        # Do PROPFIND to make sure it's still there
        # Test nonexistant resource
        # Test None namespace in property

        def check_patch_response(response):
            response = IResponse(response)

            if response.code != responsecode.MULTI_STATUS:
                self.fail("Incorrect response code for PROPFIND (%s != %s)" %
                          (response.code, responsecode.MULTI_STATUS))

            content_type = response.headers.getHeader("content-type")
            if content_type not in (http_headers.MimeType("text", "xml"),
                                    http_headers.MimeType(
                                        "application", "xml")):
                self.fail(
                    "Incorrect content-type for PROPPATCH response (%r not in %r)"
                    % (content_type,
                       (http_headers.MimeType("text", "xml"),
                        http_headers.MimeType("application", "xml"))))

            return davXMLFromStream(
                response.stream).addCallback(check_patch_xml)

        def check_patch_xml(doc):
            multistatus = doc.root_element

            if not isinstance(multistatus, davxml.MultiStatus):
                self.fail(
                    "PROPFIND response XML root element is not multistatus: %r"
                    % (multistatus, ))

            # Requested a property change one resource, so there should be exactly one response
            response = multistatus.childOfType(davxml.Response)

            # Should have a response description (its contents are arbitrary)
            response.childOfType(davxml.ResponseDescription)

            # Requested property change was on /
            self.failUnless(
                response.childOfType(davxml.HRef) == "/",
                "Incorrect response URI: %s != /" %
                (response.childOfType(davxml.HRef), ))

            # Requested one property change, so there should be exactly one property status
            propstat = response.childOfType(davxml.PropertyStatus)

            # And the contained property should be a SpiffyProperty
            self.failIf(
                propstat.childOfType(
                    davxml.PropertyContainer).childOfType(SpiffyProperty) is
                None, "Not a SpiffyProperty in PROPPATCH property status: %s" %
                (propstat.toxml()))

            # And the status should be 200
            self.failUnless(
                propstat.childOfType(davxml.Status).code == responsecode.OK,
                "Incorrect status code for PROPPATCH of property %s: %s != %s"
                % (propstat.childOfType(davxml.PropertyContainer).toxml(),
                   propstat.childOfType(davxml.Status).code, responsecode.OK))

        patch = davxml.PropertyUpdate(
            davxml.Set(
                davxml.PropertyContainer(
                    SpiffyProperty.fromString("This is a spiffy resource."))))

        request = SimpleRequest(self.site, "PROPPATCH", "/")
        request.stream = MemoryStream(patch.toxml())
        return self.send(request, check_patch_response)
예제 #18
0
                            properties_by_status[
                                responsecode.NOT_FOUND].append(
                                    propertyName(property))
                elif not returnMinimal:
                    properties_by_status[responsecode.NOT_FOUND].append(
                        propertyName(property))

        propstats = []

        for status in properties_by_status:
            properties = properties_by_status[status]
            if not properties:
                continue

            xml_status = davxml.Status.fromResponseCode(status)
            xml_container = davxml.PropertyContainer(*properties)
            xml_propstat = davxml.PropertyStatus(xml_container, xml_status)

            propstats.append(xml_propstat)

        # Always need to have at least one propstat present (required by Prefer header behavior)
        if len(propstats) == 0:
            propstats.append(
                davxml.PropertyStatus(
                    davxml.PropertyContainer(),
                    davxml.Status.fromResponseCode(responsecode.OK)))

        xml_resource = davxml.HRef(uri)
        xml_response = davxml.PropertyStatusResponse(xml_resource, *propstats)

        xml_responses.append(xml_response)
예제 #19
0
    def test_PROPFIND_basic(self):
        """
        PROPFIND request
        """
        def check_result(response):
            response = IResponse(response)

            if response.code != responsecode.MULTI_STATUS:
                self.fail("Incorrect response code for PROPFIND (%s != %s)" %
                          (response.code, responsecode.MULTI_STATUS))

            content_type = response.headers.getHeader("content-type")
            if content_type not in (http_headers.MimeType("text", "xml"),
                                    http_headers.MimeType(
                                        "application", "xml")):
                self.fail(
                    "Incorrect content-type for PROPFIND response (%r not in %r)"
                    % (content_type,
                       (http_headers.MimeType("text", "xml"),
                        http_headers.MimeType("application", "xml"))))

            return davXMLFromStream(response.stream).addCallback(check_xml)

        def check_xml(doc):
            multistatus = doc.root_element

            if not isinstance(multistatus, davxml.MultiStatus):
                self.fail(
                    "PROPFIND response XML root element is not multistatus: %r"
                    % (multistatus, ))

            for response in multistatus.childrenOfType(
                    davxml.PropertyStatusResponse):
                if response.childOfType(davxml.HRef) == "/":
                    for propstat in response.childrenOfType(
                            davxml.PropertyStatus):
                        status = propstat.childOfType(davxml.Status)
                        properties = propstat.childOfType(
                            davxml.PropertyContainer).children

                        if status.code != responsecode.OK:
                            self.fail(
                                "PROPFIND failed (status %s) to locate live properties: %s"
                                % (status.code, properties))

                        properties_to_find = [
                            p.qname() for p in self.liveProperties()
                        ]

                        for property in properties:
                            qname = property.qname()
                            if qname in properties_to_find:
                                properties_to_find.remove(qname)
                            else:
                                self.fail(
                                    "PROPFIND found property we didn't ask for: %r"
                                    % (property, ))

                        if properties_to_find:
                            self.fail(
                                "PROPFIND failed to find properties: %r" %
                                (properties_to_find, ))

                    break

            else:
                self.fail("No response for URI /")

        query = davxml.PropertyFind(
            davxml.PropertyContainer(*self.liveProperties()))

        request = SimpleRequest(self.site, "PROPFIND", "/")

        depth = "1"
        if depth is not None:
            request.headers.setHeader("depth", depth)

        request.stream = MemoryStream(query.toxml())

        return self.send(request, check_result)