def test_vcalendar_no_effect(self): data = """BEGIN:VCALENDAR VERSION:2.0 PRODID:-//CALENDARSERVER.ORG//NONSGML Version 1//EN BEGIN:VEVENT UID:12345-67890 DTSTART:20080601T120000Z DTEND:20080601T130000Z ATTENDEE:mailto:[email protected] ATTENDEE:mailto:[email protected] ORGANIZER;CN=User 01:mailto:[email protected] END:VEVENT END:VCALENDAR """.replace("\n", "\r\n") no_effect = CalendarData(CalendarComponent(name="VCALENDAR")) for item in ( data, Component.fromString(data), ): self.assertEqual(str(CalendarDataFilter(no_effect).filter(item)), data) no_effect = CalendarData( CalendarComponent(AllComponents(), AllProperties(), name="VCALENDAR")) for item in ( data, Component.fromString(data), ): self.assertEqual(str(CalendarDataFilter(no_effect).filter(item)), data)
def test_vevent_some_props(self): data = """BEGIN:VCALENDAR VERSION:2.0 PRODID:-//CALENDARSERVER.ORG//NONSGML Version 1//EN BEGIN:VEVENT UID:12345-67890 DTSTART:20080601T120000Z DTEND:20080601T130000Z ATTENDEE:mailto:[email protected] ATTENDEE:mailto:[email protected] ORGANIZER;CN=User 01:mailto:[email protected] BEGIN:VALARM ACTION:DISPLAY DESCRIPTION:Test TRIGGER;RELATED=START:-PT10M END:VALARM END:VEVENT END:VCALENDAR """.replace("\n", "\r\n") result = """BEGIN:VCALENDAR VERSION:2.0 PRODID:-//CALENDARSERVER.ORG//NONSGML Version 1//EN BEGIN:VEVENT UID:12345-67890 DTSTART:20080601T120000Z DTEND:20080601T130000Z BEGIN:VALARM ACTION:DISPLAY DESCRIPTION:Test TRIGGER;RELATED=START:-PT10M END:VALARM END:VEVENT END:VCALENDAR """.replace("\n", "\r\n") empty = CalendarData( CalendarComponent(CalendarComponent(AllComponents(), Property(name="UID", ), Property(name="DTSTART", ), Property(name="DTEND", ), name="VEVENT"), AllProperties(), name="VCALENDAR")) for item in ( data, Component.fromString(data), ): self.assertEqual(str(CalendarDataFilter(empty).filter(item)), result)
def test_empty(self): data = """BEGIN:VCALENDAR VERSION:2.0 PRODID:-//CALENDARSERVER.ORG//NONSGML Version 1//EN BEGIN:VEVENT UID:12345-67890 DTSTART:20080601T120000Z DTEND:20080601T130000Z ATTENDEE:mailto:[email protected] ATTENDEE:mailto:[email protected] ORGANIZER;CN=User 01:mailto:[email protected] END:VEVENT END:VCALENDAR """.replace("\n", "\r\n") empty = CalendarData() for item in ( data, Component.fromString(data), ): self.assertEqual(str(CalendarDataFilter(empty).filter(item)), data)
def _namedPropertiesForResource(request, props, resource, calendar=None, timezone=None, vcard=None, isowner=True, dataAllowed=True, forbidden=False): """ Return the specified properties on the specified resource. @param request: the L{IRequest} for the current request. @param props: a list of property elements or qname tuples for the properties of interest. @param resource: the L{CalDAVResource} for the targeted resource. @param calendar: the L{Component} for the calendar for the resource. This may be None if the calendar has not already been read in, in which case the resource will be used to get the calendar if needed. @param timezone: the L{Component} the VTIMEZONE to use for floating/all-day. @param vcard: the L{Component} for the vcard for the resource. This may be None if the vcard has not already been read in, in which case the resource will be used to get the vcard if needed. @param isowner: C{True} if the authorized principal making the request is the DAV:owner, C{False} otherwise. @param dataAllowed: C{True} if calendar/address data is allowed to be returned, C{False} otherwise. @param forbidden: if C{True} then return 403 status for all properties, C{False} otherwise. @return: a map of OK and NOT FOUND property values. """ properties_by_status = { responsecode.OK : [], responsecode.FORBIDDEN : [], responsecode.NOT_FOUND : [], } # Look for Prefer header first, then try Brief prefer = request.headers.getHeader("prefer", {}) returnMinimal = any([key == "return" and value == "minimal" for key, value, _ignore_args in prefer]) if not returnMinimal: returnMinimal = request.headers.getHeader("brief", False) for property in props: if isinstance(property, element.WebDAVElement): qname = property.qname() else: qname = property if forbidden: properties_by_status[responsecode.FORBIDDEN].append(propertyName(qname)) continue if isinstance(property, caldavxml.CalendarData): if dataAllowed: # Handle private events access restrictions if calendar is None: calendar = (yield resource.componentForUser()) filtered = HiddenInstanceFilter().filter(calendar) filtered = PrivateEventFilter(resource.accessMode, isowner).filter(filtered) filtered = CalendarDataFilter(property, timezone).filter(filtered) propvalue = CalendarData.fromCalendar(filtered, format=property.content_type) properties_by_status[responsecode.OK].append(propvalue) else: properties_by_status[responsecode.FORBIDDEN].append(propertyName(qname)) continue if isinstance(property, carddavxml.AddressData): if dataAllowed: if vcard is None: vcard = (yield resource.vCard()) filtered = AddressDataFilter(property).filter(vcard) propvalue = AddressData.fromAddress(filtered, format=property.content_type) properties_by_status[responsecode.OK].append(propvalue) else: properties_by_status[responsecode.FORBIDDEN].append(propertyName(qname)) continue has = (yield resource.hasProperty(property, request)) if has: try: prop = (yield resource.readProperty(property, request)) if prop is not None: properties_by_status[responsecode.OK].append(prop) elif not returnMinimal: properties_by_status[responsecode.NOT_FOUND].append(propertyName(qname)) except HTTPError: f = Failure() status = statusForFailure(f, "getting property: %s" % (qname,)) if status not in properties_by_status: properties_by_status[status] = [] if not returnMinimal or status != responsecode.NOT_FOUND: properties_by_status[status].append(propertyName(qname)) elif not returnMinimal: properties_by_status[responsecode.NOT_FOUND].append(propertyName(qname)) returnValue(properties_by_status)
def _namedPropertiesForResource(request, props, resource, calendar=None, timezone=None, vcard=None, isowner=True): """ Return the specified properties on the specified resource. @param request: the L{IRequest} for the current request. @param props: a list of property elements or qname tuples for the properties of interest. @param resource: the L{CalDAVResource} for the targeted resource. @param calendar: the L{Component} for the calendar for the resource. This may be None if the calendar has not already been read in, in which case the resource will be used to get the calendar if needed. @param timezone: the L{Component} the VTIMEZONE to use for floating/all-day. @param vcard: the L{Component} for the vcard for the resource. This may be None if the vcard has not already been read in, in which case the resource will be used to get the vcard if needed. @param isowner: C{True} if the authorized principal making the request is the DAV:owner, C{False} otherwise. @return: a map of OK and NOT FOUND property values. """ properties_by_status = { responsecode.OK : [], responsecode.NOT_FOUND : [], } # Look for Prefer header first, then try Brief prefer = request.headers.getHeader("prefer", {}) returnMinimal = any([key == "return" and value == "minimal" for key, value, _ignore_args in prefer]) if not returnMinimal: returnMinimal = request.headers.getHeader("brief", False) for property in props: if isinstance(property, caldavxml.CalendarData): # Handle private events access restrictions if calendar is None: calendar = (yield resource.iCalendarForUser(request)) filtered = HiddenInstanceFilter().filter(calendar) filtered = PrivateEventFilter(resource.accessMode, isowner).filter(filtered) filtered = CalendarDataFilter(property, timezone).filter(filtered) propvalue = CalendarData.fromCalendar(filtered, format=property.content_type) properties_by_status[responsecode.OK].append(propvalue) continue if isinstance(property, carddavxml.AddressData): if vcard is None: vcard = (yield resource.vCard()) filtered = AddressDataFilter(property).filter(vcard) propvalue = AddressData.fromAddress(filtered, format=property.content_type) properties_by_status[responsecode.OK].append(propvalue) continue if isinstance(property, element.WebDAVElement): qname = property.qname() else: qname = property has = (yield resource.hasProperty(property, request)) if has: try: prop = (yield resource.readProperty(property, request)) if prop is not None: properties_by_status[responsecode.OK].append(prop) elif not returnMinimal: properties_by_status[responsecode.NOT_FOUND].append(propertyName(qname)) except HTTPError: f = Failure() status = statusForFailure(f, "getting property: %s" % (qname,)) if status not in properties_by_status: properties_by_status[status] = [] if not returnMinimal or status != responsecode.NOT_FOUND: properties_by_status[status].append(propertyName(qname)) elif not returnMinimal: properties_by_status[responsecode.NOT_FOUND].append(propertyName(qname)) returnValue(properties_by_status)
def filter(self, ical): """ Filter the supplied iCalendar object using the request information. @param ical: iCalendar object @type ical: L{Component} or C{str} @return: L{Component} for the filtered calendar data """ if self.isowner or self.accessRestriction == Component.ACCESS_PUBLIC or not self.accessRestriction: # No need to filter for the owner or public event return ical elif self.accessRestriction == Component.ACCESS_PRIVATE: # We should never get here because ACCESS_PRIVATE is protected via an ACL raise HTTPError( StatusResponse(responsecode.FORBIDDEN, "Access Denied")) elif self.accessRestriction == Component.ACCESS_PUBLIC: return ical elif self.accessRestriction in (Component.ACCESS_CONFIDENTIAL, Component.ACCESS_RESTRICTED): # Create a CALDAV:calendar-data element with the appropriate iCalendar Component/Property # filter in place for the access restriction in use extra_access = () if self.accessRestriction == Component.ACCESS_RESTRICTED: extra_access = ( Property(name="SUMMARY"), Property(name="LOCATION"), ) calendardata = CalendarData( CalendarComponent( # VCALENDAR properties Property(name="PRODID"), Property(name="VERSION"), Property(name="CALSCALE"), Property(name=Component.ACCESS_PROPERTY), # VEVENT CalendarComponent(Property(name="UID"), Property(name="RECURRENCE-ID"), Property(name="SEQUENCE"), Property(name="DTSTAMP"), Property(name="STATUS"), Property(name="TRANSP"), Property(name="DTSTART"), Property(name="DTEND"), Property(name="DURATION"), Property(name="RRULE"), Property(name="RDATE"), Property(name="EXRULE"), Property(name="EXDATE"), *extra_access, **{"name": "VEVENT"}), # VTODO CalendarComponent(Property(name="UID"), Property(name="RECURRENCE-ID"), Property(name="SEQUENCE"), Property(name="DTSTAMP"), Property(name="STATUS"), Property(name="DTSTART"), Property(name="COMPLETED"), Property(name="DUE"), Property(name="DURATION"), Property(name="RRULE"), Property(name="RDATE"), Property(name="EXRULE"), Property(name="EXDATE"), *extra_access, **{"name": "VTODO"}), # VJOURNAL CalendarComponent(Property(name="UID"), Property(name="RECURRENCE-ID"), Property(name="SEQUENCE"), Property(name="DTSTAMP"), Property(name="STATUS"), Property(name="TRANSP"), Property(name="DTSTART"), Property(name="RRULE"), Property(name="RDATE"), Property(name="EXRULE"), Property(name="EXDATE"), *extra_access, **{"name": "VJOURNAL"}), # VFREEBUSY CalendarComponent(Property(name="UID"), Property(name="DTSTAMP"), Property(name="DTSTART"), Property(name="DTEND"), Property(name="DURATION"), Property(name="FREEBUSY"), *extra_access, **{"name": "VFREEBUSY"}), # VTIMEZONE CalendarComponent( AllProperties(), AllComponents(), name="VTIMEZONE", ), name="VCALENDAR", ), ) # Now "filter" the resource calendar data through the CALDAV:calendar-data element return CalendarDataFilter(calendardata).filter(ical) else: # Unknown access restriction raise HTTPError( StatusResponse(responsecode.FORBIDDEN, "Access Denied"))
def test_CalendarDataTextAndJSON(self): """ Text that we can both parse and generate CalendarData elements with both text and json formats. """ dataText = """BEGIN:VCALENDAR VERSION:2.0 PRODID:-//CALENDARSERVER.ORG//NONSGML Version 1//EN BEGIN:VEVENT UID:12345-67890 DTSTART:20080601T120000Z DTEND:20080601T130000Z ATTENDEE:mailto:[email protected] ATTENDEE:mailto:[email protected] DTSTAMP:20080601T120000Z EXDATE:20080602T120000Z EXDATE:20080603T120000Z ORGANIZER;CN=User 01:mailto:[email protected] RRULE:FREQ=DAILY;COUNT=400 SUMMARY:Test END:VEVENT END:VCALENDAR """.replace("\n", "\r\n") dataXML = """<?xml version='1.0' encoding='UTF-8'?> <calendar-data xmlns='urn:ietf:params:xml:ns:caldav'><![CDATA[%s]]></calendar-data>""" % ( dataText, ) jsonText = """[ "vcalendar", [ ["version", {}, "text", "2.0"], ["prodid", {}, "text", "-//CALENDARSERVER.ORG//NONSGML Version 1//EN"] ], [ ["vevent", [ ["uid", {}, "text", "12345-67890"], ["dtstart", {}, "date-time", "2008-06-01T12:00:00Z"], ["dtend", {}, "date-time", "2008-06-01T13:00:00Z"], ["attendee", {}, "cal-address", "mailto:[email protected]"], ["attendee", {}, "cal-address", "mailto:[email protected]"], ["dtstamp", {}, "date-time", "2008-06-01T12:00:00Z"], ["exdate", {}, "date-time", "2008-06-02T12:00:00Z"], ["exdate", {}, "date-time", "2008-06-03T12:00:00Z"], ["organizer", {"cn": "User 01"}, "cal-address", "mailto:[email protected]"], ["rrule", {}, "recur", {"count": 400, "freq": "DAILY"}], ["summary", {}, "text", "Test"] ], [ ] ] ] ] """ jsonXML = """<?xml version='1.0' encoding='UTF-8'?> <calendar-data content-type='application/calendar+json' xmlns='urn:ietf:params:xml:ns:caldav'><![CDATA[%s]]></calendar-data>""" % ( jsonText, ) cd = CalendarData.fromTextData(dataText) self.assertEqual( normalize_iCalStr(cd.calendar().getTextWithTimezones( True, format="text/calendar")), normalize_iCalStr(dataText)) self.assertEqual( normalizeJSON(cd.calendar().getTextWithTimezones( True, format="application/calendar+json")), normalizeJSON(jsonText)) self.assertEqual(cd.content_type, "text/calendar") self.assertEqual(cd.toxml(), dataXML) comp = Component.fromString(dataText) cd = CalendarData.fromCalendar(comp) self.assertEqual( normalize_iCalStr(cd.calendar().getTextWithTimezones( True, format="text/calendar")), normalize_iCalStr(dataText)) self.assertEqual( normalizeJSON(cd.calendar().getTextWithTimezones( True, format="application/calendar+json")), normalizeJSON(jsonText)) self.assertEqual(cd.content_type, "text/calendar") self.assertEqual(cd.toxml(), dataXML) cd = CalendarData.fromCalendar(comp, format="application/calendar+json") self.assertEqual( normalize_iCalStr(cd.calendar().getTextWithTimezones( True, format="text/calendar")), normalize_iCalStr(dataText)) self.assertEqual( normalizeJSON(cd.calendar().getTextWithTimezones( True, format="application/calendar+json")), normalizeJSON(jsonText)) self.assertEqual(cd.content_type, "application/calendar+json") self.assertEqual(normalizeJSON(cd.toxml()), normalizeJSON(jsonXML)) cd = CalendarData.fromTextData(jsonText, format="application/calendar+json") self.assertEqual( normalize_iCalStr(cd.calendar().getTextWithTimezones( True, format="text/calendar")), normalize_iCalStr(dataText)) self.assertEqual( normalizeJSON(cd.calendar().getTextWithTimezones( True, format="application/calendar+json")), normalizeJSON(jsonText)) self.assertEqual(cd.content_type, "application/calendar+json") self.assertEqual(cd.toxml(), jsonXML) comp = Component.fromString(jsonText, format="application/calendar+json") cd = CalendarData.fromCalendar(comp) self.assertEqual( normalize_iCalStr(cd.calendar().getTextWithTimezones( True, format="text/calendar")), normalize_iCalStr(dataText)) self.assertEqual( normalizeJSON(cd.calendar().getTextWithTimezones( True, format="application/calendar+json")), normalizeJSON(jsonText)) self.assertEqual(cd.content_type, "text/calendar") self.assertEqual(cd.toxml(), dataXML) cd = CalendarData.fromCalendar(comp, format="application/calendar+json") self.assertEqual( normalize_iCalStr(cd.calendar().getTextWithTimezones( True, format="text/calendar")), normalize_iCalStr(dataText)) self.assertEqual( normalizeJSON(cd.calendar().getTextWithTimezones( True, format="application/calendar+json")), normalizeJSON(jsonText)) self.assertEqual(cd.content_type, "application/calendar+json") self.assertEqual(normalizeJSON(cd.toxml()), normalizeJSON(jsonXML))
def test_CalendarDataTextAndJSON(self): """ Text that we can both parse and generate CalendarData elements with both text and json formats. """ dataText = """BEGIN:VCALENDAR VERSION:2.0 PRODID:-//CALENDARSERVER.ORG//NONSGML Version 1//EN BEGIN:VEVENT UID:12345-67890 DTSTART:20080601T120000Z DTEND:20080601T130000Z ATTENDEE:mailto:[email protected] ATTENDEE:mailto:[email protected] DTSTAMP:20080601T120000Z EXDATE:20080602T120000Z EXDATE:20080603T120000Z ORGANIZER;CN=User 01:mailto:[email protected] RRULE:FREQ=DAILY SUMMARY:Test END:VEVENT END:VCALENDAR """.replace("\n", "\r\n") dataXML = """<?xml version='1.0' encoding='UTF-8'?> <calendar-data xmlns='urn:ietf:params:xml:ns:caldav'><![CDATA[%s]]></calendar-data>""" % (dataText,) jsonText = """[ "vcalendar", [ ["version", {}, "text", "2.0"], ["prodid", {}, "text", "-//CALENDARSERVER.ORG//NONSGML Version 1//EN"] ], [ ["vevent", [ ["uid", {}, "text", "12345-67890"], ["dtstart", {}, "date-time", "2008-06-01T12:00:00Z"], ["dtend", {}, "date-time", "2008-06-01T13:00:00Z"], ["attendee", {}, "cal-address", "mailto:[email protected]"], ["attendee", {}, "cal-address", "mailto:[email protected]"], ["dtstamp", {}, "date-time", "2008-06-01T12:00:00Z"], ["exdate", {}, "date-time", "2008-06-02T12:00:00Z"], ["exdate", {}, "date-time", "2008-06-03T12:00:00Z"], ["organizer", {"cn": "User 01"}, "cal-address", "mailto:[email protected]"], ["rrule", {}, "recur", {"freq": "DAILY"}], ["summary", {}, "text", "Test"] ], [ ] ] ] ] """ jsonXML = """<?xml version='1.0' encoding='UTF-8'?> <calendar-data content-type='application/calendar+json' xmlns='urn:ietf:params:xml:ns:caldav'><![CDATA[%s]]></calendar-data>""" % (jsonText,) cd = CalendarData.fromTextData(dataText) self.assertEqual(normalize_iCalStr(cd.calendar().getTextWithTimezones(True, format="text/calendar")), normalize_iCalStr(dataText)) self.assertEqual(normalizeJSON(cd.calendar().getTextWithTimezones(True, format="application/calendar+json")), normalizeJSON(jsonText)) self.assertEqual(cd.content_type, "text/calendar") self.assertEqual(cd.toxml(), dataXML) comp = Component.fromString(dataText) cd = CalendarData.fromCalendar(comp) self.assertEqual(normalize_iCalStr(cd.calendar().getTextWithTimezones(True, format="text/calendar")), normalize_iCalStr(dataText)) self.assertEqual(normalizeJSON(cd.calendar().getTextWithTimezones(True, format="application/calendar+json")), normalizeJSON(jsonText)) self.assertEqual(cd.content_type, "text/calendar") self.assertEqual(cd.toxml(), dataXML) cd = CalendarData.fromCalendar(comp, format="application/calendar+json") self.assertEqual(normalize_iCalStr(cd.calendar().getTextWithTimezones(True, format="text/calendar")), normalize_iCalStr(dataText)) self.assertEqual(normalizeJSON(cd.calendar().getTextWithTimezones(True, format="application/calendar+json")), normalizeJSON(jsonText)) self.assertEqual(cd.content_type, "application/calendar+json") self.assertEqual(normalizeJSON(cd.toxml()), normalizeJSON(jsonXML)) cd = CalendarData.fromTextData(jsonText, format="application/calendar+json") self.assertEqual(normalize_iCalStr(cd.calendar().getTextWithTimezones(True, format="text/calendar")), normalize_iCalStr(dataText)) self.assertEqual(normalizeJSON(cd.calendar().getTextWithTimezones(True, format="application/calendar+json")), normalizeJSON(jsonText)) self.assertEqual(cd.content_type, "application/calendar+json") self.assertEqual(cd.toxml(), jsonXML) comp = Component.fromString(jsonText, format="application/calendar+json") cd = CalendarData.fromCalendar(comp) self.assertEqual(normalize_iCalStr(cd.calendar().getTextWithTimezones(True, format="text/calendar")), normalize_iCalStr(dataText)) self.assertEqual(normalizeJSON(cd.calendar().getTextWithTimezones(True, format="application/calendar+json")), normalizeJSON(jsonText)) self.assertEqual(cd.content_type, "text/calendar") self.assertEqual(cd.toxml(), dataXML) cd = CalendarData.fromCalendar(comp, format="application/calendar+json") self.assertEqual(normalize_iCalStr(cd.calendar().getTextWithTimezones(True, format="text/calendar")), normalize_iCalStr(dataText)) self.assertEqual(normalizeJSON(cd.calendar().getTextWithTimezones(True, format="application/calendar+json")), normalizeJSON(jsonText)) self.assertEqual(cd.content_type, "application/calendar+json") self.assertEqual(normalizeJSON(cd.toxml()), normalizeJSON(jsonXML))