def __init__(self, parent, proxyType): """ @param parent: the parent of this resource. @param proxyType: a C{str} containing the name of the resource. """ if self.isCollection(): slash = "/" else: slash = "" url = joinURL(parent.principalURL(), proxyType) + slash super(CalendarUserProxyPrincipalResource, self).__init__() DAVResourceWithChildrenMixin.__init__(self) self.parent = parent self.proxyType = proxyType self._url = url # FIXME: if this is supposed to be public, it needs a better name: self.pcollection = self.parent.parent.parent # Principal UID is parent's GUID plus the proxy type; this we can easily # map back to a principal. self.uid = "%s#%s" % (self.parent.principalUID(), proxyType) self._alternate_urls = tuple( joinURL(url, proxyType) + slash for url in parent.alternateURIs() if url.startswith("/") )
def __init__(self, parent, record): """ @param parent: the parent of this resource. @param record: the L{IDirectoryRecord} that this resource represents. """ super(DirectoryPrincipalResource, self).__init__() self.cacheNotifier = self.cacheNotifierFactory(self, cacheHandle="PrincipalToken") if self.isCollection(): slash = "/" else: slash = "" assert record is not None, "Principal must have a directory record" self.record = record self.parent = parent url = joinURL(parent.principalCollectionURL(), self.principalUID()) + slash self._url = url self._alternate_urls = tuple([ joinURL( parent.parent.principalCollectionURL(), record.service.recordTypeToOldName(record.recordType), quote(shortName.encode("utf-8")) ) + slash for shortName in getattr(record, "shortNames", []) ])
def __init__(self, parent, record): """ @param parent: the parent of this resource. @param record: the L{IDirectoryRecord} that this resource represents. """ super(DirectoryPrincipalResource, self).__init__() self.cacheNotifier = self.cacheNotifierFactory( self, cacheHandle="PrincipalToken") if self.isCollection(): slash = "/" else: slash = "" assert record is not None, "Principal must have a directory record" self.record = record self.parent = parent url = joinURL(parent.principalCollectionURL(), self.principalUID()) + slash self._url = url self._alternate_urls = tuple([ joinURL(parent.parent.principalCollectionURL(), record.service.recordTypeToOldName(record.recordType), quote(shortName.encode("utf-8"))) + slash for shortName in getattr(record, "shortNames", []) ])
def addressbook_query(self, addressbook_uri, query, got_xml, data, no_init): if not no_init: ''' FIXME: clear address book, possibly by removing mkcol = """<?xml version="1.0" encoding="utf-8" ?> <D:mkcol xmlns:D="DAV:" xmlns:C="urn:ietf:params:xml:ns:carddav"> <D:set> <D:prop> <D:resourcetype><D:collection/><C:addressbook/></D:resourcetype> </D:prop> </D:set> </D:mkcol> """ response = yield self.send(SimpleStoreRequest(self, "MKCOL", addressbook_uri, content=mkcol, authPrincipal=self.authPrincipal)) response = IResponse(response) if response.code != responsecode.CREATED: self.fail("MKCOL failed: %s" % (response.code,)) ''' if data: for filename, icaldata in data.iteritems(): request = SimpleStoreRequest( self, "PUT", joinURL(addressbook_uri, filename + ".vcf"), headers=Headers({"content-type": MimeType.fromString("text/vcard")}), authPrincipal=self.authPrincipal ) request.stream = MemoryStream(icaldata) yield self.send(request) else: # Add vcards to addressbook for child in FilePath(self.vcards_dir).children(): if os.path.splitext(child.basename())[1] != ".vcf": continue request = SimpleStoreRequest( self, "PUT", joinURL(addressbook_uri, child.basename()), headers=Headers({"content-type": MimeType.fromString("text/vcard")}), authPrincipal=self.authPrincipal ) request.stream = MemoryStream(child.getContent()) yield self.send(request) request = SimpleStoreRequest(self, "REPORT", addressbook_uri, authPrincipal=self.authPrincipal) request.stream = MemoryStream(query.toxml()) response = yield self.send(request) response = IResponse(response) if response.code != responsecode.MULTI_STATUS: self.fail("REPORT failed: %s" % (response.code,)) returnValue( (yield davXMLFromStream(response.stream).addCallback(got_xml)) )
def calendar_query(self, calendar_uri, query, got_xml, data, no_init): if not no_init: response = yield self.send( SimpleStoreRequest(self, "MKCALENDAR", calendar_uri, authPrincipal=self.authPrincipal)) response = IResponse(response) if response.code != responsecode.CREATED: self.fail("MKCALENDAR failed: %s" % (response.code, )) if data: for filename, icaldata in data.iteritems(): request = SimpleStoreRequest( self, "PUT", joinURL(calendar_uri, filename + ".ics"), headers=Headers({ "content-type": MimeType.fromString("text/calendar") }), authPrincipal=self.authPrincipal) request.stream = MemoryStream(icaldata) yield self.send(request) else: # Add holiday events to calendar for child in FilePath(self.holidays_dir).children(): if os.path.splitext(child.basename())[1] != ".ics": continue request = SimpleStoreRequest( self, "PUT", joinURL(calendar_uri, child.basename()), headers=Headers({ "content-type": MimeType.fromString("text/calendar") }), authPrincipal=self.authPrincipal) request.stream = MemoryStream(child.getContent()) yield self.send(request) request = SimpleStoreRequest(self, "REPORT", calendar_uri, authPrincipal=self.authPrincipal) request.stream = MemoryStream(query.toxml()) response = yield self.send(request) response = IResponse(response) if response.code != responsecode.MULTI_STATUS: self.fail("REPORT failed: %s" % (response.code, )) returnValue((yield davXMLFromStream(response.stream).addCallback(got_xml)))
def actionCapabilities(self, request): """ Return the capabilities of this server. """ if len(request.args) != 0: self.problemReport("invalid-action", "Invalid request-URI query parameters", responsecode.BAD_REQUEST) urlbase = request.path.rsplit("/", 1)[0] result = { "version": "1", "info": { "primary-source" if self.primary else "secondary_source": self.info_source, "formats": self.formats, "contacts": [], }, "actions": [ { "name": "capabilities", "uri-template": joinURL(urlbase, "capabilities"), "parameters": [], }, { "name": "list", "uri-template": joinURL(urlbase, "zones{?changedsince}"), "parameters": [ {"name": "changedsince", "required": False, "multi": False, }, ], }, { "name": "get", "uri-template": joinURL(urlbase, "zones{/tzid}{?start,end}"), "parameters": [ {"name": "start", "required": False, "multi": False}, {"name": "stop", "required": False, "multi": False, }, ], }, { "name": "expand", "uri-template": joinURL(urlbase, "zones{/tzid}/observances{?start,end}"), "parameters": [ {"name": "start", "required": True, "multi": False, }, {"name": "end", "required": True, "multi": False, }, ], }, { "name": "find", "uri-template": joinURL(urlbase, "zones{?pattern}"), "parameters": [ {"name": "pattern", "required": True, "multi": False, }, ], }, ] } return JSONResponse(responsecode.OK, result, pretty=config.TimezoneService.PrettyPrintJSON)
def actionCapabilities(self, request): """ Return the capabilities of this server. """ if len(request.args) != 0: self.problemReport("invalid-action", "Invalid request-URI query parameters", responsecode.BAD_REQUEST) urlbase = request.path.rsplit("/", 1)[0] result = { "version": "1", "info" : { "primary-source" if self.primary else "secondary_source": self.info_source, "formats": self.formats, "contacts" : [], }, "actions" : [ { "name": "capabilities", "uri-template": joinURL(urlbase, "capabilities"), "parameters": [], }, { "name": "list", "uri-template": joinURL(urlbase, "zones{?changedsince}"), "parameters": [ {"name": "changedsince", "required": False, "multi": False, }, ], }, { "name": "get", "uri-template": joinURL(urlbase, "zones{/tzid}{?start,end}"), "parameters": [ {"name": "start", "required": False, "multi": False}, {"name": "stop", "required": False, "multi": False, }, ], }, { "name": "expand", "uri-template": joinURL(urlbase, "zones{/tzid}/observances{?start,end}"), "parameters": [ {"name": "start", "required": True, "multi": False, }, {"name": "end", "required": True, "multi": False, }, ], }, { "name": "find", "uri-template": joinURL(urlbase, "zones{?pattern}"), "parameters": [ {"name": "pattern", "required": True, "multi": False, }, ], }, ] } return JSONResponse(responsecode.OK, result, pretty=config.TimezoneService.PrettyPrintJSON)
def acceptShare(self, request, inviteUID, summary): # Accept the share try: shareeView = yield self._newStoreHome.acceptShare( inviteUID, summary) except DirectoryRecordNotFoundError: # Missing sharer record => fail request raise HTTPError( ErrorResponse( responsecode.FORBIDDEN, (calendarserver_namespace, "invalid-share"), "Invite UID not valid", )) if shareeView is None: raise HTTPError( ErrorResponse( responsecode.FORBIDDEN, (calendarserver_namespace, "invalid-share"), "Invite UID not valid", )) # Return the URL of the shared collection sharedAsURL = joinURL(self.url(), shareeView.shareName()) returnValue( XMLResponse(code=responsecode.OK, element=customxml.SharedAs( element.HRef.fromString(sharedAsURL))))
def queryCalendarObjectResource(resource, uri, name, calendar, timezone, query_ok=False, isowner=True): """ Run a query on the specified calendar. @param resource: the L{CalDAVResource} for the calendar. @param uri: the uri of the resource. @param name: the name of the resource. @param calendar: the L{Component} calendar read from the resource. """ # Handle private events access restrictions if not isowner: access = resource.accessMode else: access = None if query_ok or filter.match(calendar, access): # Check size of results is within limit matchcount[0] += 1 if max_number_of_results[0] is not None and matchcount[0] > max_number_of_results[0]: raise NumberOfMatchesWithinLimits(max_number_of_results[0]) if name: href = davxml.HRef.fromString(joinURL(uri, name)) else: href = davxml.HRef.fromString(uri) try: yield report_common.responseForHref(request, responses, href, resource, propertiesForResource, props, isowner, calendar=calendar, timezone=timezone) except ConcurrentModification: # This can happen because of a race-condition between the # time we determine which resources exist and the deletion # of one of these resources in another request. In this # case, we ignore the now missing resource rather # than raise an error for the entire report. log.error("Missing resource during query: %s" % (href,))
def accessControlList(self, request, *args, **kwargs): """ Override this to give write proxies DAV:write-acl privilege so they can add attachments too. """ acl = (yield super(DropBoxHomeResource, self).accessControlList(request, *args, **kwargs)) if config.EnableProxyPrincipals: owner = (yield self.ownerPrincipal(request)) newaces = tuple(acl.children) newaces += ( # DAV:write-acl access for this principal's calendar-proxy-write users. davxml.ACE( davxml.Principal(davxml.HRef(joinURL(owner.principalURL(), "calendar-proxy-write/"))), davxml.Grant( davxml.Privilege(davxml.WriteACL()), ), davxml.Protected(), TwistedACLInheritable(), ), ) returnValue(davxml.ACL(*newaces)) else: returnValue(acl)
def test_collection_in_calendar(self): """ Make (regular) collection in calendar """ calendar_uri = "/calendars/users/wsanchez/collection_in_calendar/" principal = yield self.actualRoot.findPrincipalForAuthID("wsanchez") request = SimpleStoreRequest(self, "MKCALENDAR", calendar_uri, authPrincipal=principal) response = yield self.send(request) response = IResponse(response) if response.code != responsecode.CREATED: self.fail("MKCALENDAR failed: %s" % (response.code, )) nested_uri = joinURL(calendar_uri, "nested") request = SimpleStoreRequest(self, "MKCOL", nested_uri, authPrincipal=principal) response = yield self.send(request) response = IResponse(response) if response.code != responsecode.FORBIDDEN: self.fail("Incorrect response to nested MKCOL: %s" % (response.code, ))
def readProperty(self, property, request): if type(property) is tuple: qname = property else: qname = property.qname() if qname == caldavxml.CalendarFreeBusySet.qname(): # Synthesize value for calendar transparency state top = self.parent.url() values = [] for cal in (yield self.parent._newStoreHome.calendars()): if cal.isUsedForFreeBusy(): values.append(HRef(joinURL(top, cal.name()) + "/")) returnValue(CalendarFreeBusySet(*values)) elif qname == customxml.CalendarAvailability.qname(): availability = self.parent._newStoreHome.getAvailability() returnValue(customxml.CalendarAvailability.fromString(str(availability)) if availability else None) elif qname in (caldavxml.ScheduleDefaultCalendarURL.qname(), customxml.ScheduleDefaultTasksURL.qname()): result = (yield self.readDefaultCalendarProperty(request, qname)) returnValue(result) result = (yield super(ScheduleInboxResource, self).readProperty(property, request)) returnValue(result)
def doRequest(self): """ Execute the actual HTTP request. """ hrefs = [ joinURL(self.sessions[0].calendarHref, "%d.ics" % (i + 1, )) for i in range(self.count) ] props = ( davxml.getetag, caldavxml.calendar_data, caldavxml.schedule_tag, ) # Create CalDAV multiget request = Multiget(self.sessions[0], self.sessions[0].calendarHref, hrefs, props) result = ResponseDataString() request.setOutput(result) # Process it self.sessions[0].runSession(request) # If its a 207 we want to parse the XML if request.getStatusCode() == statuscodes.MultiStatus: pass else: raise RuntimeError("Muliget request failed: %s" % (request.getStatusCode(), ))
def queryAddressBookObjectResource(resource, uri, name, vcard, query_ok=False): """ Run a query on the specified vcard. @param resource: the L{CalDAVResource} for the vcard. @param uri: the uri of the resource. @param name: the name of the resource. @param vcard: the L{Component} vcard read from the resource. """ if query_ok or filter.match(vcard): # Check size of results is within limit checkMaxResults() if name: href = davxml.HRef.fromString(joinURL(uri, name)) else: href = davxml.HRef.fromString(uri) try: yield report_common.responseForHref(request, responses, href, resource, propertiesForResource, query, vcard=vcard) except ConcurrentModification: # This can happen because of a race-condition between the # time we determine which resources exist and the deletion # of one of these resources in another request. In this # case, we ignore the now missing resource rather # than raise an error for the entire report. log.error("Missing resource during sync: {href}", href=href)
def cleanup(self): """ Do some cleanup after the real request. """ # Remove created resources href = joinURL(self.sessions[0].calendarHref, "put.ics") self.sessions[0].deleteResource(URL(path=href))
def acceptShare(self, request, inviteUID, summary): # Accept the share try: shareeView = yield self._newStoreHome.acceptShare(inviteUID, summary) except DirectoryRecordNotFoundError: # Missing sharer record => fail request raise HTTPError(ErrorResponse( responsecode.FORBIDDEN, (calendarserver_namespace, "invalid-share"), "Invite UID not valid", )) if shareeView is None: raise HTTPError(ErrorResponse( responsecode.FORBIDDEN, (calendarserver_namespace, "invalid-share"), "Invite UID not valid", )) # Return the URL of the shared collection sharedAsURL = joinURL(self.url(), shareeView.shareName()) returnValue(XMLResponse( code=responsecode.OK, element=customxml.SharedAs( element.HRef.fromString(sharedAsURL) ) ))
def __init__(self, base_uri, errors): """ An error response which is due to unsufficient privileges, as determined by L{DAVResource.checkPrivileges}. @param base_uri: the base URI for the resources with errors (the URI of the resource on which C{checkPrivileges} was called). @param errors: a sequence of tuples, as returned by C{checkPrivileges}. """ denials = [] for subpath, privileges in errors: if subpath is None: uri = base_uri else: uri = joinURL(base_uri, subpath) for p in privileges: denials.append( element.Resource(element.HRef(uri), element.Privilege(p)) ) super(NeedPrivilegesResponse, self).__init__( responsecode.FORBIDDEN, element.NeedPrivileges(*denials) )
def readProperty(self, property, request): if type(property) is tuple: qname = property else: qname = property.qname() if qname == caldavxml.CalendarFreeBusySet.qname(): # Synthesize value for calendar transparency state top = self.parent.url() values = [] for cal in (yield self.parent._newStoreHome.calendars()): if cal.isUsedForFreeBusy(): values.append(HRef(joinURL(top, cal.name()) + "/")) returnValue(CalendarFreeBusySet(*values)) elif qname == customxml.CalendarAvailability.qname(): availability = self.parent._newStoreHome.getAvailability() returnValue( customxml.CalendarAvailability. fromString(str(availability)) if availability else None) elif qname in (caldavxml.ScheduleDefaultCalendarURL.qname(), customxml.ScheduleDefaultTasksURL.qname()): result = (yield self.readDefaultCalendarProperty(request, qname)) returnValue(result) result = (yield super(ScheduleInboxResource, self).readProperty(property, request)) returnValue(result)
def accessControlList(self, request, *args, **kwargs): """ Override this to give write proxies DAV:write-acl privilege so they can add attachments too. """ acl = (yield super(DropBoxHomeResource, self).accessControlList(request, *args, **kwargs)) if config.EnableProxyPrincipals: owner = (yield self.ownerPrincipal(request)) newaces = tuple(acl.children) newaces += ( # DAV:write-acl access for this principal's calendar-proxy-write users. davxml.ACE( davxml.Principal( davxml.HRef( joinURL(owner.principalURL(), "calendar-proxy-write/"))), davxml.Grant(davxml.Privilege(davxml.WriteACL()), ), davxml.Protected(), TwistedACLInheritable(), ), ) returnValue(davxml.ACL(*newaces)) else: returnValue(acl)
def _test_file_in_calendar(self, what, *work): """ Creates a calendar collection, then PUTs a resource into that collection with the data from given stream and verifies that the response code from the PUT request matches the given response_code. """ calendar_uri = "/calendars/users/wsanchez/testing_calendar/" principal = yield self.actualRoot.findPrincipalForAuthID("wsanchez") request = SimpleStoreRequest(self, "MKCALENDAR", calendar_uri, authPrincipal=principal) response = yield self.send(request) response = IResponse(response) if response.code != responsecode.CREATED: self.fail("MKCALENDAR failed: %s" % (response.code,)) c = 0 for stream, response_code in work: dst_uri = joinURL(calendar_uri, "dst%d.ics" % (c,)) request = SimpleStoreRequest(self, "PUT", dst_uri, authPrincipal=principal) request.headers.setHeader("if-none-match", "*") request.headers.setHeader("content-type", MimeType("text", "calendar")) request.stream = stream response = yield self.send(request) response = IResponse(response) if response.code != response_code: self.fail("Incorrect response to %s: %s (!= %s)" % (what, response.code, response_code)) c += 1
def cleanup(self): """ Do some cleanup after the real request. """ # Remove created resources for i in range(self.count): href = joinURL(self.sessions[0].calendarHref, "tr-query-%d.ics" % (i + 1,)) self.sessions[0].deleteResource(URL(path=href))
def doRequest(self): """ Execute the actual HTTP request. """ now = DateTime.getNowUTC() href = joinURL(self.sessions[0].calendarHref, "put.ics") self.sessions[0].writeData(URL(path=href), ICAL % (now.getYear() + 1,), "text/calendar")
def calendar_query(self, calendar_uri, query, got_xml, data, no_init): if not no_init: response = yield self.send(SimpleStoreRequest(self, "MKCALENDAR", calendar_uri, authid="wsanchez")) response = IResponse(response) if response.code != responsecode.CREATED: self.fail("MKCALENDAR failed: %s" % (response.code,)) if data: for filename, icaldata in data.iteritems(): request = SimpleStoreRequest( self, "PUT", joinURL(calendar_uri, filename + ".ics"), headers=Headers({"content-type": MimeType.fromString("text/calendar")}), authid="wsanchez" ) request.stream = MemoryStream(icaldata) yield self.send(request) else: # Add holiday events to calendar for child in FilePath(self.holidays_dir).children(): if os.path.splitext(child.basename())[1] != ".ics": continue request = SimpleStoreRequest( self, "PUT", joinURL(calendar_uri, child.basename()), headers=Headers({"content-type": MimeType.fromString("text/calendar")}), authid="wsanchez" ) request.stream = MemoryStream(child.getContent()) yield self.send(request) request = SimpleStoreRequest(self, "REPORT", calendar_uri, authid="wsanchez") request.stream = MemoryStream(query.toxml()) response = yield self.send(request) response = IResponse(response) if response.code != responsecode.MULTI_STATUS: self.fail("REPORT failed: %s" % (response.code,)) returnValue( (yield davXMLFromStream(response.stream).addCallback(got_xml)) )
def mkdtemp(self, prefix): """ Creates a new directory in the document root and returns its path and URI. """ path = mkdtemp(prefix=prefix + "_", dir=self.docroot) uri = joinURL("/", url_quote(os.path.basename(path))) + "/" return (os.path.abspath(path), uri)
def _addressBookHomeChildURL(self, name): if not hasattr(self, "addressBookHomeURL"): if self.parent.parent.addressBookCollection is None: return None self.addressBookHomeURL = joinURL( self.parent.parent.addressBookCollection.url(), uidsResourceName, self.record.uid) + "/" # Prefix with other server if needed if not self.thisServer(): self.addressBookHomeURL = joinURL(self.serverURI(), self.addressBookHomeURL) url = self.addressBookHomeURL if url is None: return None else: return joinURL(url, name) if name else url
def writeProperty(self, property, request): assert isinstance(property, davxml.WebDAVElement) # Strictly speaking CS:calendar-availability is a live property in the sense that the # server enforces what can be stored, however it need not actually # exist so we cannot list it in liveProperties on this resource, since its # its presence there means that hasProperty will always return True for it. if property.qname() == customxml.CalendarAvailability.qname(): if not property.valid(): raise HTTPError(ErrorResponse( responsecode.CONFLICT, (caldav_namespace, "valid-calendar-data"), description="Invalid property" )) yield self.parent._newStoreHome.setAvailability(property.calendar()) returnValue(None) elif property.qname() == caldavxml.CalendarFreeBusySet.qname(): # Verify that the calendars added in the PROPPATCH are valid. We do not check # whether existing items in the property are still valid - only new ones. property.children = [davxml.HRef(normalizeURL(str(href))) for href in property.children] new_calendars = set([str(href) for href in property.children]) old_calendars = set() for cal in (yield self.parent._newStoreHome.calendars()): if cal.isUsedForFreeBusy(): old_calendars.add(HRef(joinURL(self.parent.url(), cal.name()))) added_calendars = new_calendars.difference(old_calendars) for href in added_calendars: cal = (yield request.locateResource(str(href))) if cal is None or not cal.exists() or not isCalendarCollectionResource(cal): # Validate that href's point to a valid calendar. raise HTTPError(ErrorResponse( responsecode.CONFLICT, (caldav_namespace, "valid-calendar-url"), "Invalid URI", )) # Remove old ones for href in old_calendars.difference(new_calendars): cal = (yield request.locateResource(str(href))) if cal is not None and cal.exists() and isCalendarCollectionResource(cal) and cal._newStoreObject.isUsedForFreeBusy(): yield cal._newStoreObject.setUsedForFreeBusy(False) # Add new ones for href in new_calendars: cal = (yield request.locateResource(str(href))) if cal is not None and cal.exists() and isCalendarCollectionResource(cal) and not cal._newStoreObject.isUsedForFreeBusy(): yield cal._newStoreObject.setUsedForFreeBusy(True) returnValue(None) elif property.qname() in (caldavxml.ScheduleDefaultCalendarURL.qname(), customxml.ScheduleDefaultTasksURL.qname()): yield self.writeDefaultCalendarProperty(request, property) returnValue(None) yield super(ScheduleInboxResource, self).writeProperty(property, request)
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)
def _addressBookHomeChildURL(self, name): if not hasattr(self, "addressBookHomeURL"): if not hasattr(self.record.service, "addressBookHomesCollection"): return None self.addressBookHomeURL = joinURL( self.record.service.addressBookHomesCollection.url(), uidsResourceName, self.record.uid ) + "/" # Prefix with other server if needed if not self.thisServer(): self.addressBookHomeURL = joinURL(self.serverURI(), self.addressBookHomeURL) url = self.addressBookHomeURL if url is None: return None else: return joinURL(url, name) if name else url
def _homeChildURL(self, name): if not hasattr(self, "calendarHomeURL"): if not hasattr(self.record.service, "calendarHomesCollection"): return None self.calendarHomeURL = joinURL( self.record.service.calendarHomesCollection.url(), uidsResourceName, self.record.uid ) + "/" # Prefix with other server if needed if not self.thisServer(): self.calendarHomeURL = joinURL(self.serverURI(), self.calendarHomeURL) url = self.calendarHomeURL if url is None: return None else: return joinURL(url, name) if name else url
def _homeChildURL(self, name): if not hasattr(self, "calendarHomeURL"): if self.parent.parent.calendarCollection is None: return None self.calendarHomeURL = joinURL( self.parent.parent.calendarCollection.url(), uidsResourceName, self.record.uid ) + "/" # Prefix with other server if needed if not self.thisServer(): self.calendarHomeURL = joinURL(self.serverURI(), self.calendarHomeURL) url = self.calendarHomeURL if url is None: return None else: return joinURL(url, name) if name else url
def __init__(self, parent): """ @param directory: an L{IDirectoryService} to provision calendars from. @param recordType: the directory record type to provision. """ DirectoryProvisioningResource.__init__( self, joinURL(parent.principalCollectionURL(), uidsResourceName) + "/", parent.directory) self.parent = parent
def provisionShare(self, child, request=None): """ Set shared state and check access control. """ if child._newStoreObject is not None and not child._newStoreObject.owned(): ownerHomeURL = (yield self._otherPrincipalHomeURL(child._newStoreObject.ownerHome().uid())) ownerView = yield child._newStoreObject.ownerView() child.setShare(joinURL(ownerHomeURL, ownerView.name()) if ownerHomeURL else None) access = yield child._checkAccessControl() if access is None: returnValue(None) returnValue(child)
def __init__(self, parent): """ @param directory: an L{IDirectoryService} to provision calendars from. @param recordType: the directory record type to provision. """ DirectoryProvisioningResource.__init__( self, joinURL(parent.principalCollectionURL(), uidsResourceName) + "/", parent.directory ) self.parent = parent
def __init__(self, parent, name, recordType): """ @param parent: the parent L{DirectoryPrincipalProvisioningResource}. @param recordType: the directory record type to provision. """ DirectoryProvisioningResource.__init__( self, joinURL(parent.principalCollectionURL(), name) + "/", parent.directory) self.recordType = recordType self.parent = parent
def prepare(self): """ Do some setup prior to the real request. """ # Add resources to create required number of changes self.start = DateTime.getNowUTC() self.start.setHHMMSS(12, 0, 0) self.end = self.start.duplicate() self.end.offsetHours(1) for i in range(self.count): href = joinURL(self.sessions[0].calendarHref, "tr-query-%d.ics" % (i + 1,)) self.sessions[0].writeData(URL(path=href), ICAL % (self.start.getText(), i + 1,), "text/calendar")
def __init__(self, parent, name, recordType): """ @param parent: the parent L{DirectoryPrincipalProvisioningResource}. @param recordType: the directory record type to provision. """ DirectoryProvisioningResource.__init__( self, joinURL(parent.principalCollectionURL(), name) + "/", parent.directory ) self.recordType = recordType self.parent = parent
def ensureEvents(self, session, calendarhref, n): """ Make sure the required number of events are present in the calendar. @param n: number of events @type n: C{int} """ now = DateTime.getNowUTC() for i in range(n - self.currentCount): index = self.currentCount + i + 1 href = joinURL(calendarhref, "%d.ics" % (index,)) session.writeData(URL(path=href), ICAL % (now.getYear() + 1, index,), "text/calendar") self.currentCount = n
def defaultCalendar(self, request, componentType): """ Find the default calendar for the supplied iCalendar component type. If one does not exist, automatically provision it. """ # This property now comes direct from the calendar home new store object default = (yield self.parent._newStoreHome.defaultCalendar(componentType, create=False)) # Need L{DAVResource} object to return not new store object if default is not None: default = (yield request.locateResource(joinURL(self.parent.url(), default.name()))) returnValue(default)
def doRequest(self): """ Execute the actual HTTP request. """ # Invite as user02 now = DateTime.getNowUTC() href = joinURL(self.sessions[1].calendarHref, "organizer.ics") attendees = "\r\n".join(["ATTENDEE:mailto:[email protected]"] + [ATTENDEE % (ctr + 3,) for ctr in range(self.count - 1)]) self.sessions[1].writeData( URL(path=href), ICAL.format(year=now.getYear() + 1, count=self.count, attendees=attendees), "text/calendar", )
def prepare(self): """ Do some setup prior to the real request. """ if not self.full: # Get current sync token results, _ignore_bad = self.sessions[0].getProperties(URL(path=self.sessions[0].calendarHref), (davxml.sync_token,)) self.synctoken = results[davxml.sync_token] # Add resources to create required number of changes now = DateTime.getNowUTC() for i in range(self.count): href = joinURL(self.sessions[0].calendarHref, "sync-collection-%d.ics" % (i + 1,)) self.sessions[0].writeData(URL(path=href), ICAL % (now.getYear() + 1, i + 1,), "text/calendar")
def ensureEvents(self, session, calendarhref, n): """ Make sure the required number of events are present in the calendar. @param n: number of events @type n: C{int} """ now = DateTime.getNowUTC() for i in range(n - self.currentCount): index = self.currentCount + i + 1 href = joinURL(calendarhref, "%d.ics" % (index,)) session.writeData(URL(path=href), ICAL % (now.getYear() + 1, index), "text/calendar") self.currentCount = n
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
def readDefaultCalendarProperty(self, request, qname): """ Read either the default VEVENT or VTODO calendar property. Try to pick one if not present. """ tasks = qname == customxml.ScheduleDefaultTasksURL.qname() componentType = "VTODO" if tasks else "VEVENT" prop_to_set = customxml.ScheduleDefaultTasksURL if tasks else caldavxml.ScheduleDefaultCalendarURL # This property now comes direct from the calendar home new store object default = (yield self.parent._newStoreHome.defaultCalendar(componentType, create=False)) if default is None: returnValue(prop_to_set()) else: defaultURL = joinURL(self.parent.url(), default.name()) returnValue(prop_to_set(davxml.HRef(defaultURL)))
def addressbook_query(self, addressbook_uri, query, got_xml): ''' FIXME: clear address book, possibly by removing mkcol = """<?xml version="1.0" encoding="utf-8" ?> <D:mkcol xmlns:D="DAV:" xmlns:C="urn:ietf:params:xml:ns:carddav"> <D:set> <D:prop> <D:resourcetype><D:collection/><C:addressbook/></D:resourcetype> </D:prop> </D:set> </D:mkcol> """ response = yield self.send(SimpleStoreRequest(self, "MKCOL", addressbook_uri, content=mkcol, authid="wsanchez")) response = IResponse(response) if response.code != responsecode.CREATED: self.fail("MKCOL failed: %s" % (response.code,)) ''' principal = yield self.actualRoot.findPrincipalForAuthID("wsanchez") # Add vCards to addressbook for child in FilePath(self.vcards_dir).children(): if os.path.splitext(child.basename())[1] != ".vcf": continue request = SimpleStoreRequest(self, "PUT", joinURL(addressbook_uri, child.basename()), authPrincipal=principal) request.stream = MemoryStream(child.getContent()) yield self.send(request) request = SimpleStoreRequest(self, "REPORT", addressbook_uri, authPrincipal=principal) request.stream = MemoryStream(query.toxml()) response = yield self.send(request) response = IResponse(response) if response.code != responsecode.MULTI_STATUS: self.fail("REPORT failed: %s" % (response.code, )) returnValue((yield davXMLFromStream(response.stream).addCallback(got_xml)))
def cleanup(self): """ Do some cleanup after the real request. """ # Remove created resources href = joinURL(self.sessions[1].calendarHref, "organizer.ics") self.sessions[1].deleteResource(URL(path=href)) # Remove the attendee event and inbox items props = (davxml.resourcetype,) results = self.sessions[0].getPropertiesOnHierarchy(URL(path=self.sessions[0].calendarHref), props) for href in results.keys(): if len(href.split("/")[-1]) > 10: self.sessions[0].deleteResource(URL(path=href)) results = self.sessions[0].getPropertiesOnHierarchy(URL(path=self.sessions[0].inboxHref), props) for href in results.keys(): if href != self.sessions[0].inboxHref: self.sessions[0].deleteResource(URL(path=href))