def test_update_attachment(self): """ Test that action=update-attachment works. """ yield self.createShare("user01", "puser01") calendar1 = yield self.calendarUnderTest(txn=self.theTransactionUnderTest(0), home="user01", name="calendar") yield calendar1.createCalendarObjectWithName("1.ics", Component.fromString(self.caldata1)) yield self.commitTransaction(0) object1 = yield self.calendarObjectUnderTest(txn=self.theTransactionUnderTest(0), home="user01", calendar_name="calendar", name="1.ics") resourceID = object1.id() attachment, _ignore_location = yield object1.addAttachment(None, MimeType.fromString("text/plain"), "test.txt", MemoryStream("Here is some text.")) managedID = attachment.managedID() yield self.commitTransaction(0) shared_object = yield self.calendarObjectUnderTest(txn=self.theTransactionUnderTest(1), home="puser01", calendar_name="shared-calendar", name="1.ics") data = "Here is some more text." attachment, location = yield shared_object.updateAttachment(managedID, MimeType.fromString("text/plain"), "test.txt", MemoryStream(data)) managedID = attachment.managedID() from txdav.caldav.datastore.sql_external import ManagedAttachmentExternal self.assertTrue(isinstance(attachment, ManagedAttachmentExternal)) self.assertEqual(attachment.size(), len(data)) self.assertTrue("user01/dropbox/" in location) yield self.commitTransaction(1) cobjs = yield ManagedAttachment.referencesTo(self.theTransactionUnderTest(0), managedID) self.assertEqual(cobjs, set((resourceID,))) attachment = yield ManagedAttachment.load(self.transactionUnderTest(), resourceID, managedID) self.assertEqual(attachment.name(), "test.txt") data = yield self.attachmentToString(attachment) self.assertEqual(data, "Here is some more text.") yield self.commitTransaction(0)
def testMultipleUpload(self): return self.doTest( 'xyz', """--xyz\r Content-Disposition: form-data; name="foo"\r \r Foo Bar\r --xyz\r Content-Disposition: form-data; name="foo"\r \r Baz\r --xyz\r Content-Disposition: form-data; name="file"; filename="filename"\r Content-Type: text/html\r \r blah\r --xyz\r Content-Disposition: form-data; name="file"; filename="filename"\r Content-Type: text/plain\r \r bleh\r --xyz--\r """, {'foo': ['Foo Bar', 'Baz']}, { 'file': [('filename', MimeType('text', 'html'), "blah"), ('filename', MimeType('text', 'plain'), "bleh")] })
def test_MissingContentType(self): test_files = ( ("plain.txt", MimeType.fromString("text/plain"),), ("word.doc", MimeType.fromString("application/msword"),), ("markup.html", MimeType.fromString("text/html"),), ("octet", MimeType.fromString("application/octet-stream"),), ("bogus.bog", MimeType.fromString("application/octet-stream"),), ) class FakeAttachment(object): def __init__(self, name): self._name = name def name(self): return self._name for filename, result in test_files: item = StorageTransportBase(FakeAttachment(filename), None, None) self.assertEquals(item._contentType, result) self.assertEquals(item._dispositionName, None) item = StorageTransportBase(FakeAttachment(filename), result, filename) self.assertEquals(item._contentType, result) self.assertEquals(item._dispositionName, filename)
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 test_remove_attachment(self): """ Test that action=remove-attachment works. """ yield self.createShare("user01", "puser01") calendar1 = yield self.calendarUnderTest(txn=self.theTransactionUnderTest(0), home="user01", name="calendar") yield calendar1.createCalendarObjectWithName("1.ics", Component.fromString(self.caldata1)) yield self.commitTransaction(0) object1 = yield self.calendarObjectUnderTest(txn=self.theTransactionUnderTest(0), home="user01", calendar_name="calendar", name="1.ics") resourceID = object1.id() attachment, _ignore_location = yield object1.addAttachment(None, MimeType.fromString("text/plain"), "test.txt", MemoryStream("Here is some text.")) managedID = attachment.managedID() yield self.commitTransaction(0) shared_object = yield self.calendarObjectUnderTest(txn=self.theTransactionUnderTest(1), home="puser01", calendar_name="shared-calendar", name="1.ics") yield shared_object.removeAttachment(None, managedID) yield self.commitTransaction(1) cobjs = yield ManagedAttachment.referencesTo(self.theTransactionUnderTest(0), managedID) self.assertEqual(cobjs, set()) attachment = yield ManagedAttachment.load(self.theTransactionUnderTest(0), resourceID, managedID) self.assertTrue(attachment is None) yield self.commitTransaction(0)
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
def test_body_hash(self): data = "Hello World!" for algorithm, hash_method in ( ( "rsa-sha1", hashlib.sha1, ), ( "rsa-sha256", hashlib.sha256, ), ): stream = str(data) headers = Headers() headers.addRawHeader("Originator", "mailto:[email protected]") headers.addRawHeader("Recipient", "mailto:[email protected]") headers.setHeader( "Content-Type", MimeType("text", "calendar", **{ "component": "VEVENT", "charset": "utf-8" })) request = DKIMRequest("POST", "/", headers, stream, "example.com", "dkim", "/tmp/key", algorithm, ( "Originator", "Recipient", "Content-Type", ), True, True, True, 3600) hash = base64.b64encode( hash_method(DKIMUtils.canonicalizeBody(data)).digest()) result = (yield request.bodyHash()) self.assertEqual(result, hash)
def test_get_attachment_links(self): """ Test that action=get-attachment-links works. """ yield self.createShare("user01", "puser01") calendar1 = yield self.calendarUnderTest(txn=self.theTransactionUnderTest(0), home="user01", name="calendar") cobj1 = yield calendar1.createCalendarObjectWithName("1.ics", Component.fromString(self.caldata1)) calobjID = cobj1.id() yield self.commitTransaction(0) object1 = yield self.calendarObjectUnderTest(txn=self.theTransactionUnderTest(0), home="user01", calendar_name="calendar", name="1.ics") attachment, _ignore_location = yield object1.addAttachment(None, MimeType.fromString("text/plain"), "test.txt", MemoryStream("Here is some text.")) attID = attachment.id() managedID = attachment.managedID() yield self.commitTransaction(0) shared_object = yield self.calendarObjectUnderTest(txn=self.theTransactionUnderTest(1), home="puser01", calendar_name="shared-calendar", name="1.ics") links = yield shared_object.ownerHome().getAttachmentLinks() self.assertEqual(len(links), 1) self.assertTrue(isinstance(links[0], AttachmentLink)) self.assertEqual(links[0]._attachmentID, attID) self.assertEqual(links[0]._managedID, managedID) self.assertEqual(links[0]._calendarObjectID, calobjID) yield self.commitTransaction(1)
def makeClass(cls, txn, attachmentData): """ Given the various database rows, build the actual class. @param attachmentData: the standard set of attachment columns @type attachmentData: C{list} @return: the constructed child class @rtype: L{Attachment} """ att = cls._attachmentSchema dropbox_id = attachmentData[cls._allColumns().index(att.DROPBOX_ID)] c = ManagedAttachment if dropbox_id == "." else DropBoxAttachment child = c( txn, attachmentData[cls._allColumns().index(att.ATTACHMENT_ID)], attachmentData[cls._allColumns().index(att.DROPBOX_ID)], attachmentData[cls._allColumns().index(att.PATH)], ) for attr, value in zip(child._rowAttributes(), attachmentData): setattr(child, attr, value) child._created = parseSQLTimestamp(child._created) child._modified = parseSQLTimestamp(child._modified) child._contentType = MimeType.fromString(child._contentType) return child
def test_generateSignature(self): data = "Hello World!" for algorithm, hash_method in ( ("rsa-sha1", hashlib.sha1,), ("rsa-sha256", hashlib.sha256,), ): stream = MemoryStream(data) headers = Headers() headers.addRawHeader("Originator", "mailto:[email protected]") headers.addRawHeader("Recipient", "mailto:[email protected]") headers.setHeader("Content-Type", MimeType("text", "calendar", **{"component": "VEVENT", "charset": "utf-8"})) request = DKIMRequest("POST", "/", headers, stream, "example.com", "dkim", self.private_keyfile, algorithm, ("Originator", "Recipient", "Content-Type",), True, True, True, 3600) # Manually create what should be the correct thing to sign bodyhash = base64.b64encode(hash_method(data).digest()) sign_this = """originator:mailto:[email protected] recipient:mailto:[email protected] content-type:%s ischedule-version:1.0 dkim-signature:v=1; d=example.com; s=dkim; t=%s; x=%s; a=%s; q=dns/txt:http/well-known; c=ischedule-relaxed/simple; h=Originator:Recipient; bh=%s; b=""".replace("\n", "\r\n") % (headers.getRawHeaders("Content-Type")[0], str(int(time.time())), str(int(time.time() + 3600)), algorithm, bodyhash) result = request.generateSignature(sign_this) key = RSA.importKey(open(self.private_keyfile).read()) signature = DKIMUtils.sign(sign_this, key, DKIMUtils.hash_func(algorithm)) self.assertEqual(result, signature)
def initFromStore(self): """ Execute necessary SQL queries to retrieve attributes. @return: C{True} if this attachment exists, C{False} otherwise. """ att = self._attachmentSchema if self._dropboxID and self._dropboxID != ".": where = (att.DROPBOX_ID == self._dropboxID).And( att.PATH == self._name) else: where = (att.ATTACHMENT_ID == self._attachmentID) rows = (yield Select( self._allColumns(), From=att, Where=where ).on(self._txn)) if not rows: returnValue(None) for attr, value in zip(self._rowAttributes(), rows[0]): setattr(self, attr, value) self._created = parseSQLTimestamp(self._created) self._modified = parseSQLTimestamp(self._modified) self._contentType = MimeType.fromString(self._contentType) returnValue(self)
def test_fail_dot_file_put_in_calendar(self): """ Make (regular) collection in calendar """ calendar_uri = "/calendars/users/wsanchez/dot_file_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, )) stream = self.dataPath.child("Holidays").child( "C318AA54-1ED0-11D9-A5E0-000A958A3252.ics").open() try: calendar = str(Component.fromStream(stream)) finally: stream.close() event_uri = "/".join([calendar_uri, ".event.ics"]) request = SimpleStoreRequest(self, "PUT", event_uri, authPrincipal=principal) request.headers.setHeader("content-type", MimeType("text", "calendar")) request.stream = MemoryStream(calendar) response = yield self.send(request) response = IResponse(response) if response.code != responsecode.FORBIDDEN: self.fail("Incorrect response to dot file PUT: %s" % (response.code, ))
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
def test_timeoutOnPUT(self): """ PUT gets a 503 on a lock timeout. """ # Create a fake lock txn = self.transactionUnderTest() yield NamedLock.acquire(txn, "ImplicitUIDLock:%s" % (hashlib.md5("uid1").hexdigest(),)) # PUT fails principal = yield self.actualRoot.findPrincipalForAuthID("wsanchez") request = SimpleStoreRequest( self, "PUT", "/calendars/users/wsanchez/calendar/1.ics", headers=Headers({"content-type": MimeType.fromString("text/calendar")}), authPrincipal=principal ) request.stream = MemoryStream("""BEGIN:VCALENDAR CALSCALE:GREGORIAN PRODID:-//Apple Computer\, Inc//iCal 2.0//EN VERSION:2.0 BEGIN:VEVENT UID:uid1 DTSTART;VALUE=DATE:20020101 DTEND;VALUE=DATE:20020102 DTSTAMP:20020101T121212Z SUMMARY:New Year's Day END:VEVENT END:VCALENDAR """.replace("\n", "\r\n")) response = yield self.send(request) self.assertEqual(response.code, responsecode.SERVICE_UNAVAILABLE)
def test_sign(self): data = "Hello World!" for algorithm, hash_method in ( ("rsa-sha1", hashlib.sha1,), ("rsa-sha256", hashlib.sha256,), ): stream = MemoryStream(data) headers = Headers() headers.addRawHeader("Originator", "mailto:[email protected]") headers.addRawHeader("Recipient", "mailto:[email protected]") headers.setHeader("Content-Type", MimeType("text", "calendar", **{"component": "VEVENT", "charset": "utf-8"})) request = DKIMRequest("POST", "/", headers, stream, "example.com", "dkim", self.private_keyfile, algorithm, ("Originator", "Recipient", "Content-Type",), True, True, True, 3600) result = (yield request.sign()) # Manually create what should be the correct thing to sign and make sure signatures match bodyhash = base64.b64encode(hash_method(DKIMUtils.canonicalizeBody(data)).digest()) sign_this = """originator:mailto:[email protected] recipient:mailto:[email protected] content-type:%s ischedule-version:1.0 ischedule-message-id:%s dkim-signature:v=1; d=example.com; s=dkim; t=%s; x=%s; a=%s; q=private-exchange:http/well-known:dns/txt; c=ischedule-relaxed/simple; h=Originator:Recipient:Content-Type:iSchedule-Version:iSchedule-Message-ID; bh=%s; b=""".replace("\n", "\r\n") % (headers.getRawHeaders("Content-Type")[0], request.message_id, request.time, request.expire, algorithm, bodyhash) key = RSA.importKey(open(self.private_keyfile).read()) signature = DKIMUtils.sign(sign_this, key, DKIMUtils.hash_func(algorithm)) self.assertEqual(result, signature) # Make sure header is updated in the request updated_header = "v=1; d=example.com; s=dkim; t=%s; x=%s; a=%s; q=private-exchange:http/well-known:dns/txt; c=ischedule-relaxed/simple; h=Originator:Recipient:Content-Type:iSchedule-Version:iSchedule-Message-ID; bh=%s; b=%s" % (request.time, request.expire, algorithm, bodyhash, signature,) self.assertEqual(request.headers.getRawHeaders("DKIM-Signature")[0], updated_header) # Try to verify result using public key pubkey = RSA.importKey(open(self.public_keyfile).read()) self.assertEqual(DKIMUtils.verify(sign_this, result, pubkey, DKIMUtils.hash_func(algorithm)), None)
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 _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 initFromStore(self): """ Execute necessary SQL queries to retrieve attributes. @return: C{True} if this attachment exists, C{False} otherwise. """ att = self._attachmentSchema if self._dropboxID and self._dropboxID != ".": where = (att.DROPBOX_ID == self._dropboxID).And( att.PATH == self._name) else: where = (att.ATTACHMENT_ID == self._attachmentID) rows = (yield Select(self._allColumns(), From=att, Where=where).on(self._txn)) if not rows: returnValue(None) for attr, value in zip(self._rowAttributes(), rows[0]): setattr(self, attr, value) self._created = parseSQLTimestamp(self._created) self._modified = parseSQLTimestamp(self._modified) self._contentType = MimeType.fromString(self._contentType) returnValue(self)
def secondState(self): """ Setup the server with data changes appearing after the first sync """ txn = self.theTransactionUnderTest(0) obj = yield self.calendarObjectUnderTest(txn, name="01_1.ics", calendar_name="calendar", home="user01") yield obj.setComponent(self.data01_1_changed) obj = yield self.calendarObjectUnderTest(txn, name="02_2.ics", calendar_name="calendar", home="user02") attachment, _ignore_location = yield obj.addAttachment( None, MimeType.fromString("text/plain"), "test_02.txt", MemoryStream("Here is some text #02.")) self.stash["user02_attachment_id"] = attachment.id() self.stash["user02_attachment_md5"] = attachment.md5() self.stash["user02_attachment_mid"] = attachment.managedID() yield self.commitTransaction(0) yield self.waitAllEmpty()
class DomainKeyResource(SimpleResource): """ Domainkey well-known resource. """ def __init__(self, domain, selector, pubkeyfile): """ """ assert domain assert selector SimpleResource.__init__(self, principalCollections=None, isdir=True, defaultACL=SimpleResource.allReadACL) self.makeKeyData(domain, selector, pubkeyfile) self.domain = domain self.selector = selector def makeKeyData(self, domain, selector, pubkeyfile): """ Check that a valid key exists, create the TXT record format data and make the needed child resources. """ # Get data from file try: with open(pubkeyfile) as f: key_data = f.read() except IOError, e: log.error( "DKIM: Unable to open the public key file: %s because of %s" % ( pubkeyfile, e, )) raise # Make sure we can parse a valid public key try: RSA.importKey(key_data) except: log.error("DKIM: Invalid public key file: %s" % (pubkeyfile, )) raise # Make the TXT record key_data = "".join(key_data.strip().splitlines()[1:-1]) txt_data = "v=DKIM1; s=ischedule; p=%s\n" % (key_data, ) # Setup resource hierarchy domainResource = SimpleResource(principalCollections=None, isdir=True, defaultACL=SimpleResource.allReadACL) self.putChild(domain, domainResource) selectorResource = SimpleDataResource( principalCollections=None, content_type=MimeType.fromString("text/plain"), data=txt_data, defaultACL=SimpleResource.allReadACL) domainResource.putChild(selector, selectorResource)
def gotBody(output): mime_params = {"charset": "utf-8"} response = Response(200, {}, output) response.headers.setHeader( "content-type", MimeType("text", "html", mime_params) ) return response
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 readProperty(self, property, request): if type(property) is tuple: qname = property else: qname = property.qname() namespace, name = qname if namespace == dav_namespace: if name == "resourcetype": result = davxml.ResourceType.empty # @UndefinedVariable return result elif name == "getetag": result = davxml.GETETag( ETag(hashlib.md5(self.vCardText()).hexdigest()).generate()) return result elif name == "getcontenttype": mimeType = MimeType('text', 'vcard', {}) result = davxml.GETContentType(generateContentType(mimeType)) return result elif name == "getcontentlength": result = davxml.GETContentLength.fromString( str(len(self.vCardText()))) return result elif name == "getlastmodified": if self.vCard().hasProperty("REV"): modDatetime = parse_date(self.vCard().propertyValue("REV")) else: modDatetime = datetime.datetime.utcnow() # strip time zone because time zones are unimplemented in davxml.GETLastModified.fromDate d = modDatetime.date() t = modDatetime.time() modDatetimeNoTZ = datetime.datetime(d.year, d.month, d.day, t.hour, t.minute, t.second, t.microsecond, None) result = davxml.GETLastModified.fromDate(modDatetimeNoTZ) return result elif name == "creationdate": if self.vCard().hasProperty( "REV"): # use modification date property if it exists creationDatetime = parse_date( self.vCard().propertyValue("REV")) else: creationDatetime = datetime.datetime.utcnow() result = davxml.CreationDate.fromDate(creationDatetime) return result elif name == "displayname": # AddressBook.app uses N. Use FN or UID instead? result = davxml.DisplayName.fromString( self.vCard().propertyValue("N")) return result elif namespace == twisted_dav_namespace: return super(ABDirectoryQueryResult, self).readProperty(property, request) return self._directoryBackedAddressBook.readProperty(property, request)
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()] j = json.loads(self.data) if self.stream is not None: j["stream"] = self.stream j["streamType"] = self.streamType try: 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)
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"))
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)
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()] j = json.loads(self.data) if self.stream is not None: j["stream"] = self.stream j["streamType"] = self.streamType try: 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)
def doRequest(self, txn): # Generate an HTTP client request try: if "xpod" not in txn.logItems: txn.logItems["xpod"] = 0 txn.logItems["xpod"] += 1 response = (yield self._processRequest()) if accountingEnabledForCategory("xPod"): self.loggedResponse = yield self.logResponse(response) emitAccounting("xPod", "", self.loggedRequest + "\n" + self.loggedResponse, "POST") if response.code == responsecode.OK: if self.writeStream is None: data = (yield allDataFromStream(response.stream)) data = json.loads(data) else: yield readStream(response.stream, self.writeStream.write) content_type = response.headers.getHeader("content-type") if content_type is None: content_type = MimeType("application", "octet-stream") content_disposition = response.headers.getHeader( "content-disposition") if content_disposition is None or "filename" not in content_disposition.params: filename = "" else: filename = content_disposition.params["filename"] self.writeStream.resetDetails(content_type, filename) yield self.writeStream.loseConnection() data = { "result": "ok", "content-type": content_type, "name": filename, } elif response.code == responsecode.BAD_REQUEST: data = (yield allDataFromStream(response.stream)) data = json.loads(data) else: raise ValueError( "Incorrect cross-pod response status code: {}".format( response.code)) except Exception as e: # Request failed log.error("Could not do cross-pod request : {request} {ex}", request=self, ex=e) raise ValueError("Failed cross-pod request: {}".format(e)) returnValue(data)
def simpleSend(self, method, path="/", body="", mimetype="text", subtype="xml", resultcode=responsecode.OK, headers=()): """ Assemble and send a simple request using L{SimpleRequest}. This L{SimpleRequest} is associated with this L{TestCase}'s C{site} attribute. @param method: the HTTP method @type method: L{bytes} @param path: the absolute path portion of the HTTP URI @type path: L{bytes} @param body: the content body of the request @type body: L{bytes} @param mimetype: the main type of the mime type of the body of the request @type mimetype: L{bytes} @param subtype: the subtype of the mimetype of the body of the request @type subtype: L{bytes} @param resultcode: The expected result code for the response to the request. @type resultcode: L{int} @param headers: An iterable of 2-tuples of C{(header, value)}; headers to set on the outgoing request. @return: a L{Deferred} which fires with a L{bytes} if the request was successfully processed and fails with an L{HTTPError} if not; or, if the resultcode does not match the response's code, fails with L{FailTest}. """ request = SimpleRequest(self.site, method, path, content=body) if headers is not None: for k, v in headers: request.headers.setHeader(k, v) request.headers.setHeader("content-type", MimeType(mimetype, subtype)) def checkResult(response): self.assertEqual(response.code, resultcode) if response.stream is None: return None return allDataFromStream(response.stream) return self.send(request, None).addCallback(checkResult)
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)
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)
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)
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
def test_get_all_attachments(self): """ Test that action=get-all-attachments works. """ yield self.createShare("user01", "puser01") calendar1 = yield self.calendarUnderTest(txn=self.theTransactionUnderTest(0), home="user01", name="calendar") yield calendar1.createCalendarObjectWithName("1.ics", Component.fromString(self.caldata1)) yield self.commitTransaction(0) object1 = yield self.calendarObjectUnderTest(txn=self.theTransactionUnderTest(0), home="user01", calendar_name="calendar", name="1.ics") yield object1.addAttachment(None, MimeType.fromString("text/plain"), "test.txt", MemoryStream("Here is some text.")) yield self.commitTransaction(0) shared_object = yield self.calendarObjectUnderTest(txn=self.theTransactionUnderTest(1), home="puser01", calendar_name="shared-calendar", name="1.ics") attachments = yield shared_object.ownerHome().getAllAttachments() self.assertEqual(len(attachments), 1) self.assertTrue(isinstance(attachments[0], ManagedAttachment)) self.assertEqual(attachments[0].contentType(), MimeType.fromString("text/plain")) self.assertEqual(attachments[0].name(), "test.txt") yield self.commitTransaction(1)
def testEmptyFilename(self): return self.doTest( 'curlPYafCMnsamUw9kSkJJkSen41sAV', """--curlPYafCMnsamUw9kSkJJkSen41sAV\r cONTENT-tYPE: application/octet-stream\r cONTENT-dISPOSITION: FORM-DATA; NAME="foo"; FILENAME=""\r \r qwertyuiop\r --curlPYafCMnsamUw9kSkJJkSen41sAV--\r """, {}, { 'foo': [('', MimeType('application', 'octet-stream'), "qwertyuiop")] })
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
def test_get_attachment_data(self): """ Test that action=get-all-attachments works. """ yield self.createShare("user01", "puser01") calendar1 = yield self.calendarUnderTest(txn=self.theTransactionUnderTest(0), home="user01", name="calendar") yield calendar1.createCalendarObjectWithName("1.ics", Component.fromString(self.caldata1)) yield self.commitTransaction(0) object1 = yield self.calendarObjectUnderTest(txn=self.theTransactionUnderTest(0), home="user01", calendar_name="calendar", name="1.ics") attachment, _ignore_location = yield object1.addAttachment(None, MimeType.fromString("text/plain"), "test.txt", MemoryStream("Here is some text.")) remote_id = attachment.id() yield self.commitTransaction(0) home1 = yield self.homeUnderTest(txn=self.theTransactionUnderTest(1), name="puser01") shared_object = yield self.calendarObjectUnderTest(txn=self.theTransactionUnderTest(1), home="puser01", calendar_name="shared-calendar", name="1.ics") attachment = yield ManagedAttachment._create(self.theTransactionUnderTest(1), None, home1.id()) attachment._contentType = MimeType.fromString("text/plain") attachment._name = "test.txt" yield shared_object.ownerHome().readAttachmentData(remote_id, attachment) yield self.commitTransaction(1)
def finalState(self): """ Setup the server with data changes appearing before the final sync """ txn = self.theTransactionUnderTest(1) obj = yield self.calendarObjectUnderTest(txn, name="p02_2.ics", calendar_name="calendar", home="puser02") attachment, _ignore_location = yield obj.addAttachment(None, MimeType.fromString("text/plain"), "test_p02.txt", MemoryStream("Here is some text #p02.")) self.stash["puser02_attachment_id"] = attachment.id() self.stash["puser02_attachment_mid"] = attachment.managedID() self.stash["puser02_attachment_md5"] = attachment.md5() yield self.commitTransaction(1) yield self.waitAllEmpty()
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 secondState(self): """ Setup the server with data changes appearing after the first sync """ txn = self.theTransactionUnderTest(0) obj = yield self.calendarObjectUnderTest(txn, name="01_1.ics", calendar_name="calendar", home="user01") yield obj.setComponent(self.data01_1_changed) obj = yield self.calendarObjectUnderTest(txn, name="02_2.ics", calendar_name="calendar", home="user02") attachment, _ignore_location = yield obj.addAttachment(None, MimeType.fromString("text/plain"), "test_02.txt", MemoryStream("Here is some text #02.")) self.stash["user02_attachment_id"] = attachment.id() self.stash["user02_attachment_md5"] = attachment.md5() self.stash["user02_attachment_mid"] = attachment.managedID() yield self.commitTransaction(0) yield self.waitAllEmpty()
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
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 test_upgradeAttachments(self): """ L{UpgradeToDatabaseService.startService} upgrades calendar attachments as well. """ # Need to tweak config and settings to setup dropbox to work self.patch(config, "EnableDropBox", True) self.patch(config, "EnableManagedAttachments", False) self.sqlStore.enableManagedAttachments = False txn = self.sqlStore.newTransaction() cs = schema.CALENDARSERVER yield Delete( From=cs, Where=cs.NAME == "MANAGED-ATTACHMENTS" ).on(txn) yield txn.commit() txn = self.fileStore.newTransaction() committed = [] def maybeCommit(): if not committed: committed.append(True) return txn.commit() self.addCleanup(maybeCommit) @inlineCallbacks def getSampleObj(): home = (yield txn.calendarHomeWithUID("home1")) calendar = (yield home.calendarWithName("calendar_1")) object = (yield calendar.calendarObjectWithName("1.ics")) returnValue(object) inObject = yield getSampleObj() someAttachmentName = "some-attachment" someAttachmentType = MimeType.fromString("application/x-custom-type") attachment = yield inObject.createAttachmentWithName( someAttachmentName, ) transport = attachment.store(someAttachmentType) someAttachmentData = "Here is some data for your attachment, enjoy." transport.write(someAttachmentData) yield transport.loseConnection() yield maybeCommit() yield self.upgrader.stepWithResult(None) committed = [] txn = self.sqlStore.newTransaction() outObject = yield getSampleObj() outAttachment = yield outObject.attachmentWithName(someAttachmentName) allDone = Deferred() class SimpleProto(Protocol): data = '' def dataReceived(self, data): self.data += data def connectionLost(self, reason): allDone.callback(self.data) self.assertEquals(outAttachment.contentType(), someAttachmentType) outAttachment.retrieve(SimpleProto()) allData = yield allDone self.assertEquals(allData, someAttachmentData)
def contentType(self): return MimeType.fromString("text/html; charset=utf-8")
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)
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)
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)
def initialState(self): """ Setup the server with an initial set of data user01 - migrating user user02 - has a calendar shared with user01 user03 - shared to by user01 puser01 - user on other pod puser02 - has a calendar shared with user01 puser03 - shared to by user01 """ # Data for user01 home = yield self.homeUnderTest(txn=self.theTransactionUnderTest(0), name="user01", create=True) self.stash["user01_pod0_home_id"] = home.id() calendar = yield home.childWithName("calendar") yield calendar.createCalendarObjectWithName("01_1.ics", Component.fromString(self.data01_1)) yield calendar.createCalendarObjectWithName("01_2.ics", Component.fromString(self.data01_2)) obj3 = yield calendar.createCalendarObjectWithName("01_3.ics", Component.fromString(self.data01_3)) attachment, _ignore_location = yield obj3.addAttachment(None, MimeType.fromString("text/plain"), "test.txt", MemoryStream("Here is some text #1.")) self.stash["user01_attachment_id"] = attachment.id() self.stash["user01_attachment_md5"] = attachment.md5() self.stash["user01_attachment_mid"] = attachment.managedID() yield self.commitTransaction(0) # Data for user02 home = yield self.homeUnderTest(txn=self.theTransactionUnderTest(0), name="user02", create=True) calendar = yield home.childWithName("calendar") yield calendar.createCalendarObjectWithName("02_1.ics", Component.fromString(self.data02_1)) yield calendar.createCalendarObjectWithName("02_2.ics", Component.fromString(self.data02_2)) yield calendar.createCalendarObjectWithName("02_3.ics", Component.fromString(self.data02_3)) yield self.commitTransaction(0) # Data for puser02 home = yield self.homeUnderTest(txn=self.theTransactionUnderTest(1), name="puser02", create=True) calendar = yield home.childWithName("calendar") yield calendar.createCalendarObjectWithName("p02_1.ics", Component.fromString(self.datap02_1)) yield calendar.createCalendarObjectWithName("p02_2.ics", Component.fromString(self.datap02_2)) yield calendar.createCalendarObjectWithName("p02_3.ics", Component.fromString(self.datap02_3)) yield self.commitTransaction(1) # Share calendars self.stash["sharename_user01_to_user03"] = yield self._createShare("user01", "user03") self.stash["sharename_user01_to_puser03"] = yield self._createShare("user01", "puser03") self.stash["sharename_user02_to_user01"] = yield self._createShare("user02", "user01") self.stash["sharename_puser02_to_user01"] = yield self._createShare("puser02", "user01") # Add some delegates txn = self.theTransactionUnderTest(0) record01 = yield txn.directoryService().recordWithUID(u"user01") record02 = yield txn.directoryService().recordWithUID(u"user02") record03 = yield txn.directoryService().recordWithUID(u"user03") precord01 = yield txn.directoryService().recordWithUID(u"puser01") group02 = yield txn.directoryService().recordWithUID(u"group02") group03 = yield txn.directoryService().recordWithUID(u"group03") # Add user02 and user03 as individual delegates yield Delegates.addDelegate(txn, record01, record02, True) yield Delegates.addDelegate(txn, record01, record03, False) yield Delegates.addDelegate(txn, record01, precord01, False) # Add group delegates yield Delegates.addDelegate(txn, record01, group02, True) yield Delegates.addDelegate(txn, record01, group03, False) # Add external delegates yield txn.assignExternalDelegates(u"user01", None, None, u"external1", u"external2") yield self.commitTransaction(0) yield self.waitAllEmpty()
def contentType(self): """ The content type of Addressbook objects is text/x-vcard. """ return MimeType.fromString("text/vcard; charset=utf-8")
def _defer(data): response = Response() response.stream = MemoryStream(str(data)) response.headers.setHeader("content-type", MimeType.fromString("text/calendar")) return response
def addUnicodeChild(davFile): m = MetaDataMixin() m.contentType = lambda: MimeType.fromString('text/plain') m.resourceType = lambda: ResourceType() m.isCollection = lambda: False davFile.putChild(unicodeChildName, m)
def contentType(self): """ The content type of Calendar objects is text/calendar. """ return MimeType.fromString("text/calendar; charset=utf-8")