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_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 #3
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 #4
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 _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 #6
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)
    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)
Exemple #8
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 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 #10
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 #11
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 #12
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)
    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 #14
0
    def render(self, request):
        output = """<html>
<head>
<title>{rtype} Inbox Resource</title>
</head>
<body>
<h1>{rtype} Inbox Resource.</h1>
</body
</html>""".format(rtype="Podding" if self._podding else "iSchedule")

        response = Response(200, {}, output)
        response.headers.setHeader("content-type", MimeType("text", "html"))
        return response
    def 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
Exemple #16
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 testStupidFilename(self):
        return self.doTest(
            '----------0xKhTmLbOuNdArY', """------------0xKhTmLbOuNdArY\r
Content-Disposition: form-data; name="file"; filename="foo"; name="foobar.txt"\r
Content-Type: text/plain\r
\r
Contents of a file
blah
blah\r
------------0xKhTmLbOuNdArY--\r
""", {}, {
                'file': [('foo"; name="foobar.txt', MimeType(
                    'text', 'plain'), "Contents of a file\nblah\nblah")]
            })
Exemple #18
0
    def __init__(self, schedule_response_element, xml_responses, location=None):
        """
        @param xml_responses: an iterable of davxml.Response objects.
        @param location:      the value of the location header to return in the response,
            or None.
        """

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

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

        if location is not None:
            self.headers.setHeader("location", location)
Exemple #19
0
    def render(self, request):
        output = """<html>
<head>
<title>DomainKey Resource</title>
</head>
<body>
<h1>DomainKey Resource.</h1>
<a href="%s">Domain: %s<br>
Selector: %s</a>
</body
</html>""" % (joinURL(request.uri, self.domain, self.selector), self.domain, self.selector,)

        response = Response(200, {}, output)
        response.headers.setHeader("content-type", MimeType("text", "html"))
        return response
    def _addManagedAttachment(self, home, calendar, event, name):

        txn = self._sqlCalendarStore.newTransaction()

        # Create an event with an attachment
        home = (yield txn.calendarHomeWithUID(home))
        calendar = (yield home.calendarWithName(calendar))
        event = (yield calendar.calendarObjectWithName(event))
        attachment = (yield event.createManagedAttachment())
        t = attachment.store(MimeType("text", "x-fixture"), name)
        t.write("%s/%s/%s/%s" % (home, calendar, event, name,))
        t.write(" managed attachment")
        (yield t.loseConnection())

        (yield txn.commit())

        returnValue(attachment)
    def testNormalUpload(self):
        return self.doTest(
            '---------------------------155781040421463194511908194298',
            """-----------------------------155781040421463194511908194298\r
Content-Disposition: form-data; name="foo"\r
\r
Foo Bar\r
-----------------------------155781040421463194511908194298\r
Content-Disposition: form-data; name="file"; filename="filename"\r
Content-Type: text/html\r
\r
Contents of a file
blah
blah\r
-----------------------------155781040421463194511908194298--\r
""", {'foo': ['Foo Bar']}, {
                'file': [('filename', MimeType(
                    'text', 'html'), "Contents of a file\nblah\nblah")]
            })
Exemple #22
0
    def _processRequest(self):
        """
        Process the request by sending it to the relevant server.

        @return: the HTTP response.
        @rtype: L{Response}
        """
        ssl, host, port, _ignore_path = self.server.details()
        path = "/" + config.Servers.ConduitName

        headers = Headers()
        headers.setHeader("Host", utf8String(host + ":{}".format(port)))
        if self.streamType:
            # For attachments we put the base64-encoded JSON data into a header
            headers.setHeader("Content-Type", self.streamType)
            headers.addRawHeader("XPOD", base64.b64encode(self.data))
        else:
            headers.setHeader(
                "Content-Type",
                MimeType("application", "json", params={
                    "charset": "utf-8",
                }))
        headers.setHeader("User-Agent", "CalendarServer/{}".format(version))
        headers.addRawHeader(*self.server.secretHeader())

        from twisted.internet import reactor
        f = Factory()
        f.protocol = HTTPClientProtocol
        ep = GAIEndpoint(
            reactor, host, port,
            _configuredClientContextFactory(host) if ssl else None)
        proto = (yield ep.connect(f))

        request = ClientRequest(
            "POST", path, headers,
            self.stream if self.stream is not None else self.data)

        if accountingEnabledForCategory("xPod"):
            self.loggedRequest = yield self.logRequest(request)

        response = (yield proto.submitRequest(request))

        returnValue(response)
    def setUp(self):
        yield super(PurgePrincipalTests, self).setUp()

        txn = self._sqlCalendarStore.newTransaction()

        # Add attachment to attachment.ics
        self._sqlCalendarStore._dropbox_ok = True
        home = yield txn.calendarHomeWithUID(self.uid)
        calendar = yield home.calendarWithName("calendar1")
        event = yield calendar.calendarObjectWithName("attachment.ics")
        attachment = yield event.createAttachmentWithName("attachment.txt")
        t = attachment.store(MimeType("text", "x-fixture"))
        t.write("attachment")
        t.write(" text")
        yield t.loseConnection()
        self._sqlCalendarStore._dropbox_ok = False

        # Share calendars each way
        home2 = yield txn.calendarHomeWithUID(self.uid2)
        calendar2 = yield home2.calendarWithName("calendar2")
        self.sharedName = yield calendar2.shareWith(home, _BIND_MODE_WRITE)
        self.sharedName2 = yield calendar.shareWith(home2, _BIND_MODE_WRITE)

        yield txn.commit()

        txn = self._sqlCalendarStore.newTransaction()
        home = yield txn.calendarHomeWithUID(self.uid)
        calendar2 = yield home.childWithName(self.sharedName)
        self.assertNotEquals(calendar2, None)
        home2 = yield txn.calendarHomeWithUID(self.uid2)
        calendar1 = yield home2.childWithName(self.sharedName2)
        self.assertNotEquals(calendar1, None)
        yield txn.commit()

        # Now remove user01
        yield self.directory.removeRecords((self.uid,))
        self.patch(config.Scheduling.Options.WorkQueues, "Enabled", False)
        self.patch(config.AutomaticPurging, "Enabled", True)
        self.patch(config.AutomaticPurging, "PollingIntervalSeconds", -1)
        self.patch(config.AutomaticPurging, "CheckStaggerSeconds", 1)
        self.patch(config.AutomaticPurging, "PurgeIntervalSeconds", 3)
        self.patch(config.AutomaticPurging, "HomePurgeDelaySeconds", 1)
Exemple #24
0
    def _doPOST(self, body, resultcode=responsecode.OK):
        authPrincipal = yield self.actualRoot.findPrincipalForAuthID("user01")
        request = SimpleStoreRequest(self,
                                     "POST",
                                     "/calendars/__uids__/user01/calendar/",
                                     content=body,
                                     authPrincipal=authPrincipal)
        request.headers.setHeader("content-type", MimeType("text", "xml"))
        response = yield self.send(request)
        response = IResponse(response)
        self.assertEqual(response.code, resultcode)

        # Reload resource
        self.resource = yield self._getResource()

        if response.stream:
            data = yield allDataFromStream(response.stream)
            returnValue(data)
        else:
            returnValue(None)
Exemple #25
0
    def _doPOSTSharerAccept(self,
                            body,
                            resultcode=responsecode.OK,
                            sharer="user02"):
        authPrincipal = yield self.actualRoot.findPrincipalForAuthID(sharer)
        request = SimpleStoreRequest(self,
                                     "POST",
                                     "/calendars/__uids__/{}/".format(sharer),
                                     content=body,
                                     authPrincipal=authPrincipal)
        request.headers.setHeader("content-type", MimeType("text", "xml"))
        response = yield self.send(request)
        response = IResponse(response)
        self.assertEqual(response.code, resultcode)

        if response.stream:
            xmldata = yield allDataFromStream(response.stream)
            doc = WebDAVDocument.fromString(xmldata)
            returnValue(doc)
        else:
            returnValue(None)
Exemple #26
0
 def contentType(self):
     return MimeType("httpd", "unix-directory")
Exemple #27
0
 def renderResponse(self, code, body=None):
     response = Response(code, {}, body)
     response.headers.setHeader("content-type", MimeType("text", "html"))
     return response
 def contentType(self):
     return MimeType("text", "calendar", charset="utf-8")
 def contentType(self):
     return MimeType("httpd",
                     "unix-directory") if self.isCollection() else None