Example #1
0
    def __init__(self, code, error, description=None):
        """
        @param code: a response code.
        @param error: an L{WebDAVElement} identifying the error, or a
            tuple C{(namespace, name)} with which to create an empty element
            denoting the error.  (The latter is useful in the case of
            preconditions ans postconditions, not all of which have defined
            XML element classes.)
        @param description: an optional string that, if present, will get
            wrapped in a (twisted_dav_namespace, error-description) element.
        """
        if type(error) is tuple:
            xml_namespace, xml_name = error
            error = element.WebDAVUnknownElement()
            error.namespace = xml_namespace
            error.name = xml_name

        self.description = description
        if self.description:
            output = element.Error(
                error, element.ErrorDescription(self.description)
            ).toxml()
        else:
            output = element.Error(error).toxml()

        Response.__init__(self, code=code, stream=output)

        self.headers.setHeader("content-type", MimeType("text", "xml"))

        self.error = error
    def doPOSTGet(self, request):
        """
        Return the specified timezone data.
        """

        tzid = request.args.get("tzid", ())
        if len(tzid) != 1:
            raise HTTPError(ErrorResponse(
                responsecode.BAD_REQUEST,
                (calendarserver_namespace, "valid-timezone"),
                "Invalid tzid query parameter",
            ))
        tzid = tzid[0]

        try:
            tzdata = readTZ(tzid)
        except TimezoneException:
            raise HTTPError(ErrorResponse(
                responsecode.NOT_FOUND,
                (calendarserver_namespace, "timezone-available"),
                "Timezone not found",
            ))

        response = Response()
        response.stream = MemoryStream(tzdata)
        response.headers.setHeader("content-type", MimeType.fromString("text/calendar; charset=utf-8"))
        return response
Example #3
0
    def __init__(self, xml_responses):
        """
        @param xml_responses: an interable of element.Response objects.
        """
        Response.__init__(self, code=responsecode.MULTI_STATUS,
                          stream=element.MultiStatus(*xml_responses).toxml())

        self.headers.setHeader("content-type", MimeType("text", "xml"))
Example #4
0
    def render(self, request):
        response = Response()
        response.stream = MemoryStream((yield self.iCalendarZipArchiveData()))

        # FIXME: Use content-encoding instead?
        response.headers.setHeader(
            b"content-type",
            MimeType.fromString(b"application/zip")
        )

        returnValue(response)
Example #5
0
    def render(self, request):
        # yield self.handleQueryArguments(request)

        htmlContent = yield flattenString(request, self.elementClass())

        response = Response()
        response.stream = MemoryStream(htmlContent)
        response.headers.setHeader(
            b"content-type", MimeType.fromString(b"text/html; charset=utf-8")
        )

        returnValue(response)
Example #6
0
    def __init__(self, schedule_response_element, xml_responses, location=None):
        """
        @param xml_responses: an iterable of davxml.Response objects.
        @param location:      the value of the location header to return in the response,
            or None.
        """

        Response.__init__(self, code=responsecode.OK,
                          stream=schedule_response_element(*xml_responses).toxml())

        self.headers.setHeader("content-type", MimeType("text", "xml"))

        if location is not None:
            self.headers.setHeader("location", location)
    def render(self, request):
        lastID = request.headers.getRawHeaders(u"last-event-id")

        response = Response()
        response.stream = EventStream(self._eventDecoder, self._events, lastID)
        response.headers.setHeader(b"content-type", MimeType.fromString(b"text/event-stream"))

        # Keep track of the event streams
        def cleanupFilter(_request, _response):
            self._streams.remove(response.stream)
            return _response

        request.addResponseFilter(cleanupFilter)
        self._streams.add(response.stream)

        return response
 def render(self, request):
     """
     Create a L{WebAdminPage} to render HTML content for this request, and
     return a response.
     """
     resourceId = request.args.get('resourceId', [''])[0]
     if resourceId:
         principal = self.getResourceById(request, resourceId)
         yield self.resourceActions(request, principal)
     htmlContent = yield flattenString(request, WebAdminPage(self))
     response = Response()
     response.stream = MemoryStream(htmlContent)
     for (header, value) in (
             ("content-type", self.contentType()),
             ("content-encoding", self.contentEncoding()),
         ):
         if value is not None:
             response.headers.setHeader(header, value)
     returnValue(response)
    def doGet(self, request):
        """
        Return the specified timezone data.
        """

        tzids = request.args.get("tzid", ())
        if len(tzids) != 1:
            raise HTTPError(JSONResponse(
                responsecode.BAD_REQUEST,
                {
                    "error": "invalid-tzid",
                    "description": "Invalid tzid query parameter",
                },
            ))

        format = request.args.get("format", ("text/calendar",))
        if len(format) != 1 or format[0] not in self.formats:
            raise HTTPError(JSONResponse(
                responsecode.BAD_REQUEST,
                {
                    "error": "invalid-format",
                    "description": "Invalid format query parameter",
                },
            ))
        format = format[0]

        calendar = self.timezones.getTimezone(tzids[0])
        if calendar is None:
            raise HTTPError(JSONResponse(
                responsecode.NOT_FOUND,
                {
                    "error": "missing-tzid",
                    "description": "Tzid could not be found",
                }
            ))

        tzdata = calendar.getText(format=format if format != "text/plain" else None)

        response = Response()
        response.stream = MemoryStream(tzdata)
        response.headers.setHeader("content-type", MimeType.fromString("%s; charset=utf-8" % (format,)))
        return response
Example #10
0
    def render(self, request):
        output = """<html>
<head>
<title>{rtype} Inbox Resource</title>
</head>
<body>
<h1>{rtype} Inbox Resource.</h1>
</body
</html>""".format(rtype="Podding" if self._podding else "iSchedule")

        response = Response(200, {}, output)
        response.headers.setHeader("content-type", MimeType("text", "html"))
        return response
    def actionGet(self, request, tzid):
        """
        Return the specified timezone data.
        """

        if set(request.args.keys()) - set(("start", "end",)):
            self.problemReport("invalid-action", "Invalid request-URI query parameters", responsecode.BAD_REQUEST)

        accepted_type = bestAcceptType(request.headers.getHeader("accept"), self.formats)
        if accepted_type is None:
            self.problemReport("invalid-format", "Accept header does not match available media types", responsecode.NOT_ACCEPTABLE)

        calendar = self.timezones.getTimezone(tzid)
        if calendar is None:
            self.problemReport("tzid-not-found", "Time zone identifier not found", responsecode.NOT_FOUND)

        tzdata = calendar.getText(format=accepted_type if accepted_type != "text/plain" else None)

        response = Response()
        response.stream = MemoryStream(tzdata)
        response.headers.setHeader("content-type", MimeType.fromString("%s; charset=utf-8" % (accepted_type,)))
        return response
    def render(self, request):
        output = """<html>
<head>
<title>Timezone Standard Service Resource</title>
</head>
<body>
<h1>Timezone Standard Service Resource.</h1>
</body
</html>"""

        response = Response(200, {}, output)
        response.headers.setHeader("content-type", MimeType("text", "html"))
        return response
Example #13
0
    def render(self, request):
        output = """<html>
<head>
<title>Podding Conduit Resource</title>
</head>
<body>
<h1>Podding Conduit Resource.</h1>
</body
</html>"""

        response = Response(200, {}, output)
        response.headers.setHeader("content-type", MimeType("text", "html"))
        return response
Example #14
0
    def declineShare(self, request, inviteUID):

        # Remove it if it is in the DB
        try:
            result = yield self._newStoreHome.declineShare(inviteUID)
        except DirectoryRecordNotFoundError:
            # Missing sharer record => just treat decline as success
            result = True
        if not result:
            raise HTTPError(ErrorResponse(
                responsecode.FORBIDDEN,
                (calendarserver_namespace, "invalid-share"),
                "Invite UID not valid",
            ))
        returnValue(Response(code=responsecode.NO_CONTENT))
Example #15
0
    def render(self, request):
        output = """<html>
<head>
<title>DomainKey Resource</title>
</head>
<body>
<h1>DomainKey Resource.</h1>
<a href="%s">Domain: %s<br>
Selector: %s</a>
</body
</html>""" % (joinURL(request.uri, self.domain, self.selector), self.domain, self.selector,)

        response = Response(200, {}, output)
        response.headers.setHeader("content-type", MimeType("text", "html"))
        return response
Example #16
0
    def actionGet(self, request, tzid):
        """
        Return the specified timezone data.
        """

        if set(request.args.keys()) - set((
                "start",
                "end",
        )):
            self.problemReport("invalid-action",
                               "Invalid request-URI query parameters",
                               responsecode.BAD_REQUEST)

        accepted_type = bestAcceptType(request.headers.getHeader("accept"),
                                       self.formats)
        if accepted_type is None:
            self.problemReport(
                "invalid-format",
                "Accept header does not match available media types",
                responsecode.NOT_ACCEPTABLE)

        calendar = self.timezones.getTimezone(tzid)
        if calendar is None:
            self.problemReport("tzid-not-found",
                               "Time zone identifier not found",
                               responsecode.NOT_FOUND)

        tzdata = calendar.getText(
            format=accepted_type if accepted_type != "text/plain" else None)

        response = Response()
        response.stream = MemoryStream(tzdata)
        response.headers.setHeader(
            "content-type",
            MimeType.fromString("%s; charset=utf-8" % (accepted_type, )))
        return response
Example #17
0
def report_DAV__principal_search_property_set(self, request, principal_search_property_set):
    """
    Generate a principal-search-property-set REPORT. (RFC 3744, section 9.5)
    """
    # Verify root element
    if not isinstance(principal_search_property_set, davxml.PrincipalSearchPropertySet):
        raise ValueError("%s expected as root element, not %s."
                         % (davxml.PrincipalSearchPropertySet.sname(), principal_search_property_set.sname()))

    # Only handle Depth: 0
    depth = request.headers.getHeader("depth", "0")
    if depth != "0":
        log.error("Error in principal-search-property-set REPORT, Depth set to %s" % (depth,))
        raise HTTPError(StatusResponse(responsecode.BAD_REQUEST, "Depth %s not allowed" % (depth,)))

    # Get details from the resource
    result = self.principalSearchPropertySet()
    if result is None:
        log.error("Error in principal-search-property-set REPORT not supported on: %s" % (self,))
        raise HTTPError(StatusResponse(responsecode.BAD_REQUEST, "Not allowed on this resource"))

    yield Response(code=responsecode.OK, stream=MemoryStream(result.toxml()))
Example #18
0
    def createRequest(self):
        self.stream = ProducerStream(self.length)
        self.response = Response(self.code, self.inHeaders, self.stream)
        self.stream.registerProducer(self, True)

        del self.inHeaders
Example #19
0
 def render(self, request):
     response = Response(200, {}, self.data)
     response.headers.setHeader("content-type", self.content_type)
     return response
Example #20
0
 def gotBody(output):
     mime_params = {"charset": "utf-8"}
     response = Response(200, {}, output)
     response.headers.setHeader("content-type",
                                MimeType("text", "html", mime_params))
     return response
Example #21
0
                            caldavxml.caldav_namespace,
                            "supported-calendar-component-set"):
                        yield self.setSupportedComponentSet(property)
                        set_supported_component_set = True
                    elif not isinstance(property, davxml.ResourceType):
                        yield self.writeProperty(property, request)
                except HTTPError:
                    errors.add(Failure(), property)
                    got_an_error = True
                else:
                    errors.add(responsecode.OK, property)

        if got_an_error:
            # Clean up
            errors.error()
            raise HTTPError(
                Response(code=responsecode.FORBIDDEN,
                         stream=mkcolxml.MakeCollectionResponse(
                             errors.response()).toxml()))

        # When calendar collections are single component only, default MKCALENDAR is VEVENT only
        if rtype == "calendar" and not set_supported_component_set and config.RestrictCalendarsToOneComponentType:
            yield self.setSupportedComponents(("VEVENT", ))

        yield returnValue(responsecode.CREATED)

    else:
        # No request body so it is a standard MKCOL
        result = yield super(CalDAVResource, self).http_MKCOL(request)
        returnValue(result)
Example #22
0
 def render(self, req):
     return Response(200)
Example #23
0
 def render(self, request):
     response = Response()
     response.stream = MemoryStream(self.renderOutput)
     return response
Example #24
0
    def _processFBURL(self, request):
        #
        # Check authentication and access controls
        #
        yield self.authorize(request, (davxml.Read(),))

        # Extract query parameters from the URL
        args = ('start', 'end', 'duration', 'token', 'format', 'user',)
        for arg in args:
            setattr(self, arg, request.args.get(arg, [None])[0])

        # Some things we do not handle
        if self.token or self.user:
            raise HTTPError(ErrorResponse(
                responsecode.NOT_ACCEPTABLE,
                (calendarserver_namespace, "supported-query-parameter"),
                "Invalid query parameter",
            ))

        # Check format
        if self.format:
            self.format = self.format.split(";")[0]
            if self.format not in ("text/calendar", "text/plain"):
                raise HTTPError(ErrorResponse(
                    responsecode.NOT_ACCEPTABLE,
                    (calendarserver_namespace, "supported-format"),
                    "Invalid return format requested",
                ))
        else:
            self.format = "text/calendar"

        # Start/end/duration must be valid iCalendar DATE-TIME UTC or DURATION values
        try:
            if self.start:
                self.start = DateTime.parseText(self.start)
                if not self.start.utc():
                    raise ValueError()
            if self.end:
                self.end = DateTime.parseText(self.end)
                if not self.end.utc():
                    raise ValueError()
            if self.duration:
                self.duration = Duration.parseText(self.duration)
        except ValueError:
            raise HTTPError(ErrorResponse(
                responsecode.BAD_REQUEST,
                (calendarserver_namespace, "valid-query-parameters"),
                "Invalid query parameters",
            ))

        # Sanity check start/end/duration

        # End and duration cannot both be present
        if self.end and self.duration:
            raise HTTPError(ErrorResponse(
                responsecode.NOT_ACCEPTABLE,
                (calendarserver_namespace, "valid-query-parameters"),
                "Invalid query parameters",
            ))

        # Duration must be positive
        if self.duration and self.duration.getTotalSeconds() < 0:
            raise HTTPError(ErrorResponse(
                responsecode.BAD_REQUEST,
                (calendarserver_namespace, "valid-query-parameters"),
                "Invalid query parameters",
            ))

        # Now fill in the missing pieces
        if self.start is None:
            self.start = DateTime.getNowUTC()
            self.start.setHHMMSS(0, 0, 0)
        if self.duration:
            self.end = self.start + self.duration
        if self.end is None:
            self.end = self.start + Duration(days=config.FreeBusyURL.TimePeriod)

        # End > start
        if self.end <= self.start:
            raise HTTPError(ErrorResponse(
                responsecode.BAD_REQUEST,
                (calendarserver_namespace, "valid-query-parameters"),
                "Invalid query parameters",
            ))

        # TODO: We should probably verify that the actual time-range is within sensible bounds (e.g. not too far in the past or future and not too long)

        # Now lookup the principal details for the targeted user
        principal = (yield self.parent.principalForRecord())

        # Pick the first mailto cu address or the first other type
        cuaddr = None
        for item in principal.calendarUserAddresses():
            if cuaddr is None:
                cuaddr = item
            if item.startswith("mailto:"):
                cuaddr = item
                break

        # Get inbox details
        inboxURL = principal.scheduleInboxURL()
        if inboxURL is None:
            raise HTTPError(StatusResponse(responsecode.INTERNAL_SERVER_ERROR, "No schedule inbox URL for principal: %s" % (principal,)))
        try:
            inbox = (yield request.locateResource(inboxURL))
        except:
            log.error("No schedule inbox for principal: %s" % (principal,))
            inbox = None
        if inbox is None:
            raise HTTPError(StatusResponse(responsecode.INTERNAL_SERVER_ERROR, "No schedule inbox for principal: %s" % (principal,)))

        organizer = recipient = LocalCalendarUser(cuaddr, principal.record)
        recipient.inbox = inbox._newStoreObject
        attendeeProp = Property("ATTENDEE", recipient.cuaddr)
        timerange = Period(self.start, self.end)

        fbresult = yield FreebusyQuery(
            organizer=organizer,
            recipient=recipient,
            attendeeProp=attendeeProp,
            timerange=timerange,
        ).generateAttendeeFreeBusyResponse()

        response = Response()
        response.stream = MemoryStream(str(fbresult))
        response.headers.setHeader("content-type", MimeType.fromString("%s; charset=utf-8" % (self.format,)))

        returnValue(response)
Example #25
0
 def _defer(data):
     response = Response()
     response.stream = MemoryStream(str(data))
     response.headers.setHeader(
         "content-type", MimeType.fromString("text/calendar"))
     return response
Example #26
0
    def _processFBURL(self, request):
        #
        # Check authentication and access controls
        #
        yield self.authorize(request, (davxml.Read(),))

        # Extract query parameters from the URL
        args = ('start', 'end', 'duration', 'token', 'format', 'user',)
        for arg in args:
            setattr(self, arg, request.args.get(arg, [None])[0])

        # Some things we do not handle
        if self.token or self.user:
            raise HTTPError(ErrorResponse(
                responsecode.NOT_ACCEPTABLE,
                (calendarserver_namespace, "supported-query-parameter"),
                "Invalid query parameter",
            ))

        # Check format
        if self.format:
            self.format = self.format.split(";")[0]
            if self.format not in ("text/calendar", "text/plain"):
                raise HTTPError(ErrorResponse(
                    responsecode.NOT_ACCEPTABLE,
                    (calendarserver_namespace, "supported-format"),
                    "Invalid return format requested",
                ))
        else:
            self.format = "text/calendar"

        # Start/end/duration must be valid iCalendar DATE-TIME UTC or DURATION values
        try:
            if self.start:
                self.start = DateTime.parseText(self.start)
                if not self.start.utc():
                    raise ValueError()
            if self.end:
                self.end = DateTime.parseText(self.end)
                if not self.end.utc():
                    raise ValueError()
            if self.duration:
                self.duration = Duration.parseText(self.duration)
        except ValueError:
            raise HTTPError(ErrorResponse(
                responsecode.BAD_REQUEST,
                (calendarserver_namespace, "valid-query-parameters"),
                "Invalid query parameters",
            ))

        # Sanity check start/end/duration

        # End and duration cannot both be present
        if self.end and self.duration:
            raise HTTPError(ErrorResponse(
                responsecode.NOT_ACCEPTABLE,
                (calendarserver_namespace, "valid-query-parameters"),
                "Invalid query parameters",
            ))

        # Duration must be positive
        if self.duration and self.duration.getTotalSeconds() < 0:
            raise HTTPError(ErrorResponse(
                responsecode.BAD_REQUEST,
                (calendarserver_namespace, "valid-query-parameters"),
                "Invalid query parameters",
            ))

        # Now fill in the missing pieces
        if self.start is None:
            self.start = DateTime.getNowUTC()
            self.start.setHHMMSS(0, 0, 0)
        if self.duration:
            self.end = self.start + self.duration
        if self.end is None:
            self.end = self.start + Duration(days=config.FreeBusyURL.TimePeriod)

        # End > start
        if self.end <= self.start:
            raise HTTPError(ErrorResponse(
                responsecode.BAD_REQUEST,
                (calendarserver_namespace, "valid-query-parameters"),
                "Invalid query parameters",
            ))

        # TODO: We should probably verify that the actual time-range is within sensible bounds (e.g. not too far in the past or future and not too long)

        # Now lookup the principal details for the targeted user
        principal = (yield self.parent.principalForRecord())

        # Pick the first mailto cu address or the first other type
        cuaddr = None
        for item in principal.calendarUserAddresses():
            if cuaddr is None:
                cuaddr = item
            if item.startswith("mailto:"):
                cuaddr = item
                break

        # Get inbox details
        inboxURL = principal.scheduleInboxURL()
        if inboxURL is None:
            raise HTTPError(StatusResponse(responsecode.INTERNAL_SERVER_ERROR, "No schedule inbox URL for principal: %s" % (principal,)))
        try:
            inbox = (yield request.locateResource(inboxURL))
        except:
            log.error("No schedule inbox for principal: {p}", p=principal)
            inbox = None
        if inbox is None:
            raise HTTPError(StatusResponse(responsecode.INTERNAL_SERVER_ERROR, "No schedule inbox for principal: %s" % (principal,)))

        organizer = recipient = LocalCalendarUser(cuaddr, principal.record)
        recipient.inbox = inbox._newStoreObject
        attendeeProp = Property("ATTENDEE", recipient.cuaddr)
        timerange = Period(self.start, self.end)

        fbresult = yield FreebusyQuery(
            organizer=organizer,
            recipient=recipient,
            attendeeProp=attendeeProp,
            timerange=timerange,
        ).generateAttendeeFreeBusyResponse()

        response = Response()
        response.stream = MemoryStream(str(fbresult))
        response.headers.setHeader("content-type", MimeType.fromString("%s; charset=utf-8" % (self.format,)))

        returnValue(response)
Example #27
0
class WebCalendarResource(ReadOnlyResourceMixIn, DAVFile):
    def defaultAccessControlList(self):
        return succeed(
            davxml.ACL(
                davxml.ACE(
                    davxml.Principal(davxml.Authenticated()),
                    davxml.Grant(davxml.Privilege(davxml.Read()), ),
                    davxml.Protected(),
                    TwistedACLInheritable(),
                ), ))

    def etag(self):
        # Can't be calculated here
        return succeed(None)

    def contentLength(self):
        # Can't be calculated here
        return None

    def lastModified(self):
        return None

    def exists(self):
        return True

    def displayName(self):
        return "Web Calendar"

    def contentType(self):
        return MimeType.fromString("text/html; charset=utf-8")

    def contentEncoding(self):
        return None

    def createSimilarFile(self, path):
        return DAVFile(path, principalCollections=self.principalCollections())

    _htmlContent_lastCheck = 0
    _htmlContent_statInfo = 0
    _htmlContentDebug_lastCheck = 0
    _htmlContentDebug_statInfo = 0

    def htmlContent(self, debug=False):
        if debug:
            cacheAttr = "_htmlContentDebug"
            templateFileName = "debug_standalone.html"
        else:
            cacheAttr = "_htmlContent"
            templateFileName = "standalone.html"

        templateFileName = os.path.join(config.WebCalendarRoot,
                                        templateFileName)

        #
        # See if the file changed, and dump the cached template if so.
        # Don't bother to check if we've checked in the past minute.
        # We don't cache if debug is true.
        #
        if not debug and hasattr(self, cacheAttr):
            currentTime = time()
            if currentTime - getattr(self, cacheAttr + "_lastCheck") > 60:
                statInfo = os.stat(templateFileName)
                statInfo = (statInfo.st_mtime, statInfo.st_size)
                if statInfo != getattr(self, cacheAttr + "_statInfo"):
                    delattr(self, cacheAttr)
                    setattr(self, cacheAttr + "_statInfo", statInfo)
                setattr(self, cacheAttr + "_lastCheck", currentTime)

        #
        # If we don't have a cached template, load it up.
        #
        if not hasattr(self, cacheAttr):
            templateFile = open(templateFileName)
            try:
                htmlContent = templateFile.read()
            finally:
                templateFile.close()

            if debug:
                # Don't cache
                return htmlContent
            setattr(self, cacheAttr, htmlContent)

        return getattr(self, cacheAttr)

    def render(self, request):
        if not self.fp.isdir():
            return responsecode.NOT_FOUND

        #
        # Get URL of authenticated principal.
        # Don't need to authenticate here because the ACL will have already
        # required it.
        #
        authenticatedPrincipalURL = request.authnUser.principalURL()

        def queryValue(arg):
            query = parse_qs(urlparse(request.uri).query, True)
            return query.get(arg, [""])[0]

        #
        # Parse debug query arg
        #
        debug = queryValue("debug")
        debug = debug is not None and debug.lower() in ("1", "true", "yes")

        #
        # Parse TimeZone query arg
        #
        tzid = queryValue("tzid")
        if not tzid:
            tzid = getLocalTimezone()
            self.log.debug("Determined timezone to be %s" % (tzid, ))

        #
        # Make some HTML
        #
        try:
            htmlContent = self.htmlContent(debug) % {
                "tzid": tzid,
                "principalURL": authenticatedPrincipalURL,
            }
        except IOError, e:
            self.log.error("Unable to obtain WebCalendar template: %s" % (e, ))
            return responsecode.NOT_FOUND

        response = Response()
        response.stream = MemoryStream(htmlContent)

        for (header, value) in (
            ("content-type", self.contentType()),
            ("content-encoding", self.contentEncoding()),
        ):
            if value is not None:
                response.headers.setHeader(header, value)

        return response
Example #28
0
            (caldavxml.ReadFreeBusy(), ))
    except NumberOfMatchesWithinLimits:
        log.error("Too many matching components in free-busy report")
        raise HTTPError(
            ErrorResponse(responsecode.FORBIDDEN,
                          davxml.NumberOfMatchesWithinLimits(),
                          "Too many components"))
    except TimeRangeLowerLimit, e:
        raise HTTPError(
            ErrorResponse(
                responsecode.FORBIDDEN, caldavxml.MinDateTime(),
                "Time-range value too far in the past. Must be on or after %s."
                % (str(e.limit), )))
    except TimeRangeUpperLimit, e:
        raise HTTPError(
            ErrorResponse(
                responsecode.FORBIDDEN, caldavxml.MaxDateTime(),
                "Time-range value too far in the future. Must be on or before %s."
                % (str(e.limit), )))

    # Now build a new calendar object with the free busy info we have
    fbcalendar = report_common.buildFreeBusyResult(fbinfo, timerange)

    response = Response()
    response.stream = MemoryStream(fbcalendar.getText(accepted_type))
    response.headers.setHeader(
        "content-type",
        MimeType.fromString("%s; charset=utf-8" % (accepted_type, )))

    returnValue(response)
    principal = yield self.resourceOwnerPrincipal(request)
    organizer = recipient = LocalCalendarUser(principal.canonicalCalendarUserAddress(), principal.record)
    timerange = Period(timerange.start, timerange.end)
    try:
        fbresult = yield FreebusyQuery(organizer=organizer, recipient=recipient, timerange=timerange).generateAttendeeFreeBusyResponse(fbset=fbset, method=None)
    except NumberOfMatchesWithinLimits:
        log.error("Too many matching components in free-busy report")
        raise HTTPError(ErrorResponse(
            responsecode.FORBIDDEN,
            davxml.NumberOfMatchesWithinLimits(),
            "Too many components"
        ))
    except TimeRangeLowerLimit, e:
        raise HTTPError(ErrorResponse(
            responsecode.FORBIDDEN,
            caldavxml.MinDateTime(),
            "Time-range value too far in the past. Must be on or after %s." % (str(e.limit),)
        ))
    except TimeRangeUpperLimit, e:
        raise HTTPError(ErrorResponse(
            responsecode.FORBIDDEN,
            caldavxml.MaxDateTime(),
            "Time-range value too far in the future. Must be on or before %s." % (str(e.limit),)
        ))

    response = Response()
    response.stream = MemoryStream(fbresult.getText(accepted_type))
    response.headers.setHeader("content-type", MimeType.fromString("%s; charset=utf-8" % (accepted_type,)))

    returnValue(response)
Example #30
0
        fbresult = yield FreebusyQuery(
            organizer=organizer, recipient=recipient,
            timerange=timerange).generateAttendeeFreeBusyResponse(fbset=fbset,
                                                                  method=None)
    except NumberOfMatchesWithinLimits:
        log.error("Too many matching components in free-busy report")
        raise HTTPError(
            ErrorResponse(responsecode.FORBIDDEN,
                          davxml.NumberOfMatchesWithinLimits(),
                          "Too many components"))
    except TimeRangeLowerLimit, e:
        raise HTTPError(
            ErrorResponse(
                responsecode.FORBIDDEN, caldavxml.MinDateTime(),
                "Time-range value too far in the past. Must be on or after %s."
                % (str(e.limit), )))
    except TimeRangeUpperLimit, e:
        raise HTTPError(
            ErrorResponse(
                responsecode.FORBIDDEN, caldavxml.MaxDateTime(),
                "Time-range value too far in the future. Must be on or before %s."
                % (str(e.limit), )))

    response = Response()
    response.stream = MemoryStream(fbresult.getText(accepted_type))
    response.headers.setHeader(
        "content-type",
        MimeType.fromString("%s; charset=utf-8" % (accepted_type, )))

    returnValue(response)
Example #31
0
 def render(self, request):
     response = Response()
     response.stream = MemoryStream(self.renderOutput)
     return response
Example #32
0
 def _defer(data):
     response = Response()
     response.stream = MemoryStream(str(data))
     response.headers.setHeader("content-type", MimeType.fromString("text/calendar"))
     return response
Example #33
0
    def getResponseForRequest(self, request):
        """
        Try to match a request and a response cache entry. We first get the request key and match that, then pull
        the cache entry and decompose it into tokens and response. We then compare the cached tokens with their current values.
        If all match, we can return the cached response data.
        """
        try:
            key = (yield self._hashedRequestKey(request))

            self.log.debug("Checking cache for: {key!r}", key=key)
            _ignore_flags, value = (yield self.getCachePool().get(key))

            if value is None:
                self.log.debug("Not in cache: {key!r}", key=key)
                returnValue(None)

            (principalToken, directoryToken, uriToken, childTokens, (code, headers, body)) = cPickle.loads(value)
            self.log.debug(
                "Found in cache: {key!r} = {value!r}",
                key=key,
                value=(
                    principalToken,
                    directoryToken,
                    uriToken,
                    childTokens,
                )
            )

            currentTokens = (yield self._getTokens(request))

            if currentTokens[0] != principalToken:
                self.log.debug(
                    "Principal token doesn't match for {key!r}: {currentToken!r} != {principalToken!r}",
                    key=request.cacheKey,
                    currentToken=currentTokens[0],
                    principalToken=principalToken,
                )
                returnValue(None)

            if currentTokens[1] != directoryToken:
                self.log.debug(
                    "Directory Record Token doesn't match for {key!r}: {currentToken!r} != {directoryToken!r}",
                    key=request.cacheKey,
                    currentToken=currentTokens[1],
                    directoryToken=directoryToken,
                )
                returnValue(None)

            if currentTokens[2] != uriToken:
                self.log.debug(
                    "URI token doesn't match for {key!r}: {currentToken!r} != {uriToken!r}",
                    key=request.cacheKey,
                    currentToken=currentTokens[2],
                    uriToken=uriToken,
                )
                returnValue(None)

            for childuri, token in childTokens.items():
                currentToken = (yield self._tokenForURI(childuri))
                if currentToken != token:
                    self.log.debug(
                        "Child {uri} token doesn't match for {key!r}: {currentToken!r} != {token!r}",
                        uri=childuri,
                        key=request.cacheKey,
                        currentToken=currentToken,
                        token=token,
                    )
                    returnValue(None)

            self.log.debug("Response cache matched")
            r = Response(code, stream=MemoryStream(body))

            for key, value in headers.iteritems():
                r.headers.setRawHeaders(key, value)

            returnValue(r)

        except URINotFoundException, e:
            self.log.debug("Could not locate URI: {e!r}", e=e)
            returnValue(None)
Example #34
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)
    except NumberOfMatchesWithinLimits:
        log.error("Too many matching components in free-busy report")
        raise HTTPError(
            ErrorResponse(responsecode.FORBIDDEN, davxml.NumberOfMatchesWithinLimits(), "Too many components")
        )
    except TimeRangeLowerLimit, e:
        raise HTTPError(
            ErrorResponse(
                responsecode.FORBIDDEN,
                caldavxml.MinDateTime(),
                "Time-range value too far in the past. Must be on or after %s." % (str(e.limit),),
            )
        )
    except TimeRangeUpperLimit, e:
        raise HTTPError(
            ErrorResponse(
                responsecode.FORBIDDEN,
                caldavxml.MaxDateTime(),
                "Time-range value too far in the future. Must be on or before %s." % (str(e.limit),),
            )
        )

    # Now build a new calendar object with the free busy info we have
    fbcalendar = report_common.buildFreeBusyResult(fbinfo, timerange)

    response = Response()
    response.stream = MemoryStream(fbcalendar.getText(accepted_type))
    response.headers.setHeader("content-type", MimeType.fromString("%s; charset=utf-8" % (accepted_type,)))

    returnValue(response)
Example #36
0
 def renderResponse(self, code, body=None):
     response = Response(code, {}, body)
     response.headers.setHeader("content-type", MimeType("text", "html"))
     return response
Example #37
0
    def http_POST(self, request):
        """
        The server-to-server POST method.
        """

        # Check shared secret
        if not self.store.directoryService().serversDB().getThisServer(
        ).checkSharedSecret(request.headers):
            self.log.error("Invalid shared secret header in cross-pod request")
            raise HTTPError(
                StatusResponse(responsecode.FORBIDDEN,
                               "Not authorized to make this request"))

        # Look for XPOD header
        xpod = request.headers.getRawHeaders("XPOD")
        contentType = request.headers.getHeader("content-type")
        if xpod is not None:
            # Attachments are sent in the request body with the JSON data in a header. We
            # decode the header and add the request.stream as an attribute of the JSON object.
            xpod = xpod[0]
            try:
                j = json.loads(base64.b64decode(xpod))
            except (TypeError, ValueError) as e:
                self.log.error("Invalid JSON header in request: {ex}\n{xpod}",
                               ex=e,
                               xpod=xpod)
                raise HTTPError(
                    StatusResponse(
                        responsecode.BAD_REQUEST,
                        "Invalid JSON header in request: {}\n{}".format(
                            e, xpod)))
            j["stream"] = request.stream
            j["streamType"] = contentType
        else:
            # Check content first
            if "{}/{}".format(contentType.mediaType,
                              contentType.mediaSubtype) != "application/json":
                self.log.error("MIME type {mime} not allowed in request",
                               mime=contentType)
                raise HTTPError(
                    StatusResponse(
                        responsecode.BAD_REQUEST,
                        "MIME type {} not allowed in request".format(
                            contentType)))

            body = (yield allDataFromStream(request.stream))
            try:
                j = json.loads(body)
            except ValueError as e:
                self.log.error("Invalid JSON data in request: {ex}\n{body}",
                               ex=e,
                               body=body)
                raise HTTPError(
                    StatusResponse(
                        responsecode.BAD_REQUEST,
                        "Invalid JSON data in request: {}\n{}".format(e,
                                                                      body)))

        # Log extended item
        if not hasattr(request, "extendedLogItems"):
            request.extendedLogItems = {}
        request.extendedLogItems[
            "xpod"] = j["action"] if "action" in j else "unknown"

        # Look for a streaming action which needs special handling
        if self.store.conduit.isStreamAction(j):
            # Get the conduit to process the data stream
            try:

                stream = ProducerStream()

                class StreamProtocol(Protocol):
                    def connectionMade(self):
                        stream.registerProducer(self.transport, False)

                    def dataReceived(self, data):
                        stream.write(data)

                    def connectionLost(self, reason):
                        stream.finish()

                result = yield self.store.conduit.processRequestStream(
                    j, StreamProtocol())

                try:
                    ct, name = result
                except ValueError:
                    code = responsecode.BAD_REQUEST
                else:
                    headers = {"content-type": MimeType.fromString(ct)}
                    headers["content-disposition"] = MimeDisposition(
                        "attachment", params={"filename": name})
                    returnValue(Response(responsecode.OK, headers, stream))

            except Exception as e:
                # Send the exception over to the other side
                result = {
                    "result":
                    "exception",
                    "class":
                    ".".join((
                        e.__class__.__module__,
                        e.__class__.__name__,
                    )),
                    "details":
                    str(e),
                }
                code = responsecode.BAD_REQUEST

        else:
            # Get the conduit to process the data
            try:
                result = yield self.store.conduit.processRequest(j)
                code = responsecode.OK if result[
                    "result"] == "ok" else responsecode.BAD_REQUEST
            except Exception as e:
                # Send the exception over to the other side
                result = {
                    "result":
                    "exception",
                    "class":
                    ".".join((
                        e.__class__.__module__,
                        e.__class__.__name__,
                    )),
                    "details":
                    str(e),
                }
                code = responsecode.BAD_REQUEST

        response = JSONResponse(code, result)
        returnValue(response)
Example #38
0
    def _processRequest(self):
        """
        Process the request by sending it to the relevant server.

        @return: the HTTP response.
        @rtype: L{Response}
        """

        store = self.storeMap[self.server.details()]

        # Force a failure of the entire request with the supplied exception type
        if getattr(store, "_poddingFailure", None) is not None:
            raise store._poddingFailure("Failed cross-pod request")

        j = json.loads(self.data)
        if self.stream is not None:
            j["stream"] = self.stream
            j["streamType"] = self.streamType
        try:
            # Force a BAD cross-pod request with the supplied exception type
            if getattr(store, "_poddingError", None) is not None:
                raise store._poddingError("Failed cross-pod request")

            if store.conduit.isStreamAction(j):
                stream = ProducerStream()

                class StreamProtocol(Protocol):
                    def connectionMade(self):
                        stream.registerProducer(self.transport, False)

                    def dataReceived(self, data):
                        stream.write(data)

                    def connectionLost(self, reason):
                        stream.finish()

                result = yield store.conduit.processRequestStream(
                    j, StreamProtocol())

                try:
                    ct, name = result
                except ValueError:
                    code = responsecode.BAD_REQUEST
                else:
                    headers = {"content-type": MimeType.fromString(ct)}
                    headers["content-disposition"] = MimeDisposition(
                        "attachment", params={"filename": name})
                    returnValue(Response(responsecode.OK, headers, stream))
            else:
                result = yield store.conduit.processRequest(j)
                code = responsecode.OK
        except Exception as e:
            # Send the exception over to the other side
            result = {
                "result": "exception",
                "class": ".".join((
                    e.__class__.__module__,
                    e.__class__.__name__,
                )),
                "details": str(e),
            }
            code = responsecode.BAD_REQUEST

        response = JSONResponse(code, result)
        returnValue(response)
Example #39
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)