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)
Exemple #2
0
    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")]
            })
Exemple #4
0
    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)
Exemple #5
0
    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))
        )
Exemple #7
0
    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)
Exemple #12
0
    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
Exemple #13
0
    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 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 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)
Exemple #16
0
    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 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, ))
Exemple #18
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_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)
Exemple #20
0
    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)
Exemple #21
0
    def __init__(self, code, error, description=None):
        """
        @param code: a response code.
        @param error: an L{WebDAVElement} identifying the error, or a
            tuple C{(namespace, name)} with which to create an empty element
            denoting the error.  (The latter is useful in the case of
            preconditions ans postconditions, not all of which have defined
            XML element classes.)
        @param description: an optional string that, if present, will get
            wrapped in a (twisted_dav_namespace, error-description) element.
        """
        if type(error) is tuple:
            xml_namespace, xml_name = error
            error = element.WebDAVUnknownElement()
            error.namespace = xml_namespace
            error.name = xml_name

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

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

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

        self.error = error
Exemple #22
0
    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 _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
Exemple #24
0
    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)
Exemple #25
0
    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()
Exemple #26
0
    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)
Exemple #27
0
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)
Exemple #28
0
 def gotBody(output):
     mime_params = {"charset": "utf-8"}
     response = Response(200, {}, output)
     response.headers.setHeader(
         "content-type",
         MimeType("text", "html", mime_params)
     )
     return response
    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)
Exemple #31
0
    def _processRequest(self):
        """
        Process the request by sending it to the relevant server.

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

        store = self.storeMap[self.server.details()]
        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)
Exemple #32
0
    def __init__(self, xml_responses):
        """
        @param xml_responses: an interable of element.Response objects.
        """
        Response.__init__(self,
                          code=responsecode.MULTI_STATUS,
                          stream=element.MultiStatus(*xml_responses).toxml())

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

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

        returnValue(response)
Exemple #34
0
    def _processRequest(self):
        """
        Process the request by sending it to the relevant server.

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

        store = self.storeMap[self.server.details()]
        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)
Exemple #35
0
    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)
Exemple #36
0
    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)
Exemple #37
0
    def render(self, request):
        # yield self.handleQueryArguments(request)

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

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

        returnValue(response)
Exemple #38
0
    def render(self, request):
        response = Response()
        response.stream = MemoryStream((yield self.iCalendarZipArchiveData()))

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

        returnValue(response)
Exemple #39
0
    def render(self, request):
        # yield self.handleQueryArguments(request)

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

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

        returnValue(response)
Exemple #40
0
    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")]
            })
Exemple #43
0
    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 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)
Exemple #52
0
 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)
Exemple #55
0
def http_GET(self, request):

    if self.exists():
        # Special sharing request on a calendar or address book
        if self.isCalendarCollection() or self.isAddressBookCollection():

            # Check for action=share
            if request.args:
                action = request.args.get("action", ("",))
                if len(action) != 1:
                    raise HTTPError(ErrorResponse(
                        responsecode.BAD_REQUEST,
                        (calendarserver_namespace, "valid-action"),
                        "Invalid action parameter: %s" % (action,),
                    ))
                action = action[0]

                dispatch = {
                    "share"   : self.directShare,
                }.get(action, None)

                if dispatch is None:
                    raise HTTPError(ErrorResponse(
                        responsecode.BAD_REQUEST,
                        (calendarserver_namespace, "supported-action"),
                        "Action not supported: %s" % (action,),
                    ))

                response = (yield dispatch(request))
                returnValue(response)

        else:
            # FIXME: this should be implemented in storebridge.CalendarObject.render

            # Look for calendar access restriction on existing resource.
            parentURL = parentForURL(request.uri)
            parent = (yield request.locateResource(parentURL))
            if isPseudoCalendarCollectionResource(parent):

                # Check authorization first
                yield self.authorize(request, (davxml.Read(),))

                # Accept header handling
                accepted_type = bestAcceptType(request.headers.getHeader("accept"), Component.allowedTypes())
                if accepted_type is None:
                    raise HTTPError(StatusResponse(responsecode.NOT_ACCEPTABLE, "Cannot generate requested data type"))

                caldata = (yield self.iCalendarForUser(request))

                # Filter any attendee hidden instances
                caldata = HiddenInstanceFilter().filter(caldata)

                if self.accessMode:

                    # Non DAV:owner's have limited access to the data
                    isowner = (yield self.isOwner(request))

                    # Now "filter" the resource calendar data
                    caldata = PrivateEventFilter(self.accessMode, isowner).filter(caldata)

                response = Response()
                response.stream = MemoryStream(caldata.getTextWithTimezones(includeTimezones=not config.EnableTimezonesByReference, format=accepted_type))
                response.headers.setHeader("content-type", MimeType.fromString("%s; charset=utf-8" % (accepted_type,)))

                # Add Schedule-Tag header if property is present
                if self.scheduleTag:
                    response.headers.setHeader("Schedule-Tag", self.scheduleTag)

                returnValue(response)

    # Do normal GET behavior
    response = (yield super(CalDAVResource, self).http_GET(request))
    returnValue(response)
    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()
Exemple #57
0
 def contentType(self):
     """
     The content type of Addressbook objects is text/x-vcard.
     """
     return MimeType.fromString("text/vcard; charset=utf-8")
Exemple #58
0
 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)
Exemple #60
0
 def contentType(self):
     """
     The content type of Calendar objects is text/calendar.
     """
     return MimeType.fromString("text/calendar; charset=utf-8")