예제 #1
0
 def test_wrong_signature_exception_on_channel_update(self):
     # Test wrong signature exception
     old_payload = ChannelMetadataPayload.from_file(CHANNEL_METADATA)
     payload = ChannelMetadataPayload.from_file(CHANNEL_METADATA_UPDATED)
     payload.signature = old_payload.signature
     self.assertRaises(InvalidSignatureException,
                       self.session.lm.update_channel, payload)
예제 #2
0
    def test_channel_update_and_download(self):
        """
        Test whether we can successfully update a channel and download the new version
        """
        # First we have to manually add the old version
        old_payload = ChannelMetadataPayload.from_file(CHANNEL_METADATA)
        with db_session:
            old_channel = self.session.lm.mds.ChannelMetadata.from_payload(
                old_payload)
            chan_dir = os.path.join(CHANNEL_DIR, old_channel.dir_name)

        self.session.lm.mds.process_channel_dir(chan_dir,
                                                old_payload.public_key)

        channel_tdef = TorrentDef.load(CHANNEL_TORRENT_UPDATED)
        libtorrent_port = get_random_port()
        yield self.setup_seeder(channel_tdef, CHANNEL_DIR, libtorrent_port)

        payload = ChannelMetadataPayload.from_file(CHANNEL_METADATA_UPDATED)

        # Download the channel in our session
        download, finished_deferred = self.session.lm.update_channel(payload)
        download.add_peer(
            ("127.0.0.1", self.seeder_session.config.get_libtorrent_port()))
        yield finished_deferred

        with db_session:
            # There should be 4 torrents + 1 channel torrent
            channel = self.session.lm.mds.ChannelMetadata.get_channel_with_id(
                payload.public_key)
            self.assertEqual(
                5, len(list(self.session.lm.mds.TorrentMetadata.select())))
            self.assertEqual(4, channel.local_version)
예제 #3
0
    def test_channel_update_and_download(self):
        """
        Test whether we can successfully update a channel and download the new version
        """
        # First we have to manually add the old version
        old_payload = ChannelMetadataPayload.from_file(CHANNEL_METADATA)
        with db_session:
            old_channel = self.session.lm.mds.ChannelMetadata.from_payload(old_payload)
            chan_dir = os.path.join(CHANNEL_DIR, old_channel.dir_name)

        self.session.lm.mds.process_channel_dir(chan_dir, old_payload.public_key)

        channel_tdef = TorrentDef.load(CHANNEL_TORRENT_UPDATED)
        libtorrent_port = get_random_port()
        yield self.setup_seeder(channel_tdef, CHANNEL_DIR, libtorrent_port)

        payload = ChannelMetadataPayload.from_file(CHANNEL_METADATA_UPDATED)

        # Download the channel in our session
        download, finished_deferred = self.session.lm.update_channel(payload)
        download.add_peer(("127.0.0.1", self.seeder_session.config.get_libtorrent_port()))
        yield finished_deferred

        with db_session:
            # There should be 4 torrents + 1 channel torrent
            channel = self.session.lm.mds.ChannelMetadata.get_channel_with_id(payload.public_key)
            self.assertEqual(5, len(list(self.session.lm.mds.TorrentMetadata.select())))
            self.assertEqual(4, channel.local_version)
예제 #4
0
    def update_channel(self, payload):
        """
        We received some channel metadata, possibly over the network.
        Validate the signature, update the local metadata store and start downloading this channel if needed.
        :param payload: The channel metadata, in serialized form.
        """
        if not payload.has_valid_signature():
            raise InvalidSignatureException("The signature of the channel metadata is invalid.")

        channel = self.mds.ChannelMetadata.get_channel_with_id(payload.public_key)
        if channel:
            if float2time(payload.timestamp) > channel.timestamp:
                # Update the channel that is already there.
                self._logger.info("Updating channel metadata %s ts %s->%s", str(channel.public_key).encode("hex"),
                                  str(channel.timestamp), str(float2time(payload.timestamp)))
                channel.set(**ChannelMetadataPayload.to_dict(payload))
        else:
            # Add new channel object to DB
            channel = self.mds.ChannelMetadata.from_payload(payload)
            channel.subscribed = True

        if channel.version > channel.local_version:
            self._logger.info("Downloading new channel version %s ver %i->%i", str(channel.public_key).encode("hex"),
                              channel.local_version, channel.version)
        #TODO: handle the case where the local version is the same as the new one and is not seeded
        return self.download_channel(channel)
예제 #5
0
 def test_process_channel_dir(self):
     """
     Test processing a directory containing metadata blobs
     """
     payload = ChannelMetadataPayload.from_file(self.CHANNEL_METADATA)
     channel = self.mds.ChannelMetadata.process_channel_metadata_payload(payload)
     self.assertFalse(channel.contents_list)
     self.mds.process_channel_dir(self.CHANNEL_DIR, channel.public_key)
     self.assertEqual(len(channel.contents_list), 3)
     self.assertEqual(channel.local_version, 3)
예제 #6
0
 def test_process_channel_dir(self):
     """
     Test processing a directory containing metadata blobs
     """
     payload = ChannelMetadataPayload.from_file(self.CHANNEL_METADATA)
     channel = self.mds.ChannelMetadata.process_channel_metadata_payload(
         payload)
     self.assertFalse(channel.contents_list)
     self.mds.process_channel_dir(self.CHANNEL_DIR, channel.public_key)
     self.assertEqual(len(channel.contents_list), 3)
     self.assertEqual(channel.local_version, 3)
    def test_export_channel_mdblob_notfound(self):
        """
        Test if export of a channel .mdblob through the endpoint works correctly
        """
        with open(os.path.join(TESTS_DATA_DIR, 'channel.mdblob'), 'rb') as f:
            mdblob = f.read()
        payload = ChannelMetadataPayload.from_signed_blob(mdblob)

        self.should_check_equality = False
        return self.do_request('channels/discovered/%s/mdblob' % str(payload.public_key).encode('hex'),
                               expected_code=404, request_type='GET')
예제 #8
0
    def test_export_channel_mdblob_notfound(self):
        """
        Test if export of a channel .mdblob through the endpoint works correctly
        """
        with open(os.path.join(TESTS_DATA_DIR, 'channel.mdblob'), 'rb') as f:
            mdblob = f.read()
        payload = ChannelMetadataPayload.from_signed_blob(mdblob)

        self.should_check_equality = False
        return self.do_request('channels/discovered/%s/mdblob' %
                               str(payload.public_key).encode('hex'),
                               expected_code=404,
                               request_type='GET')
    def test_export_channel_mdblob(self):
        """
        Test if export of a channel .mdblob through the endpoint works correctly
        """
        with open(os.path.join(TESTS_DATA_DIR, 'channel.mdblob'), 'rb') as f:
            mdblob = f.read()
        payload = ChannelMetadataPayload.from_signed_blob(mdblob)
        with db_session:
            self.session.lm.mds.ChannelMetadata.from_payload(payload)

        def verify_exported_data(result):
            self.assertEqual(mdblob, result)

        self.should_check_equality = False
        return self.do_request('channels/discovered/%s/mdblob' % str(payload.public_key).encode('hex'),
                               expected_code=200, request_type='GET').addCallback(verify_exported_data)
예제 #10
0
    def test_process_channel_metadata_payload(self):
        """
        Test whether a channel metadata payload is processed correctly
        """
        payload = ChannelMetadataPayload.from_file(self.CHANNEL_METADATA)
        channel_metadata = self.mds.ChannelMetadata.process_channel_metadata_payload(payload)
        self.assertTrue(channel_metadata)

        # Check that we do not add it again
        self.mds.ChannelMetadata.process_channel_metadata_payload(payload)
        self.assertEqual(len(self.mds.ChannelMetadata.select()), 1)

        # Check that we always take the latest version
        channel_metadata.timestamp -= 1
        self.assertEqual(channel_metadata.timestamp, 1551110113006)
        channel_metadata = self.mds.ChannelMetadata.process_channel_metadata_payload(payload)
        self.assertEqual(channel_metadata.timestamp, 1551110113007)
        self.assertEqual(len(self.mds.ChannelMetadata.select()), 1)
예제 #11
0
    def test_export_channel_mdblob(self):
        """
        Test if export of a channel .mdblob through the endpoint works correctly
        """
        with open(os.path.join(TESTS_DATA_DIR, 'channel.mdblob'), 'rb') as f:
            mdblob = f.read()
        payload = ChannelMetadataPayload.from_signed_blob(mdblob)
        with db_session:
            self.session.lm.mds.ChannelMetadata.from_payload(payload)

        def verify_exported_data(result):
            self.assertEqual(mdblob, result)

        self.should_check_equality = False
        return self.do_request(
            'channels/discovered/%s/mdblob' %
            str(payload.public_key).encode('hex'),
            expected_code=200,
            request_type='GET').addCallback(verify_exported_data)
예제 #12
0
    def test_process_channel_metadata_payload(self):
        """
        Test whether a channel metadata payload is processed correctly
        """
        payload = ChannelMetadataPayload.from_file(self.CHANNEL_METADATA)
        channel_metadata = self.mds.ChannelMetadata.process_channel_metadata_payload(
            payload)
        self.assertTrue(channel_metadata)

        # Check that we do not add it again
        self.mds.ChannelMetadata.process_channel_metadata_payload(payload)
        self.assertEqual(len(self.mds.ChannelMetadata.select()), 1)

        # Check that we always take the latest version
        channel_metadata.timestamp -= 1
        self.assertEqual(channel_metadata.timestamp, 1551110113006)
        channel_metadata = self.mds.ChannelMetadata.process_channel_metadata_payload(
            payload)
        self.assertEqual(channel_metadata.timestamp, 1551110113007)
        self.assertEqual(len(self.mds.ChannelMetadata.select()), 1)
예제 #13
0
    def render_PUT(self, request):
        """
        .. http:put:: /downloads

        A PUT request to this endpoint will start a download from a provided URI. This URI can either represent a file
        location, a magnet link or a HTTP(S) url.
        - anon_hops: the number of hops for the anonymous download. 0 hops is equivalent to a plain download
        - safe_seeding: whether the seeding of the download should be anonymous or not (0 = off, 1 = on)
        - destination: the download destination path of the torrent
        - torrent: the URI of the torrent file that should be downloaded. This parameter is required.

            **Example request**:

                .. sourcecode:: none

                    curl -X PUT http://localhost:8085/downloads
                    --data "anon_hops=2&safe_seeding=1&destination=/my/dest/on/disk/&uri=file:/home/me/test.torrent

            **Example response**:

                .. sourcecode:: javascript

                    {"started": True, "infohash": "4344503b7e797ebf31582327a5baae35b11bda01"}
        """
        parameters = http.parse_qs(request.content.read(), 1)

        if 'uri' not in parameters or len(parameters['uri']) == 0:
            request.setResponseCode(http.BAD_REQUEST)
            return json.dumps({"error": "uri parameter missing"})

        download_config, error = DownloadsEndpoint.create_dconfig_from_params(
            parameters)
        if error:
            request.setResponseCode(http.BAD_REQUEST)
            return json.dumps({"error": error})

        def download_added(download):
            request.write(
                json.dumps({
                    "started":
                    True,
                    "infohash":
                    download.get_def().get_infohash().encode('hex')
                }))
            request.finish()

        def on_error(error):
            request.setResponseCode(http.INTERNAL_SERVER_ERROR)
            request.write(json.dumps({"error": error.getErrorMessage()}))
            request.finish()

        uri = parameters['uri'][0]
        if uri.startswith("file:"):
            if uri.endswith(".mdblob"):
                filename = url2pathname(uri[5:].encode('utf-8') if isinstance(
                    uri, unicode) else uri[5:])
                try:
                    payload = ChannelMetadataPayload.from_file(filename)
                except IOError:
                    request.setResponseCode(http.BAD_REQUEST)
                    return json.dumps({"error": "file not found"})
                except InvalidSignatureException:
                    request.setResponseCode(http.BAD_REQUEST)
                    return json.dumps(
                        {"error": "Metadata has invalid signature"})

                download, _ = self.session.lm.update_channel(payload)
                return json.dumps({
                    "started":
                    True,
                    "infohash":
                    str(download.get_def().get_infohash()).encode('hex')
                })
            else:
                download_uri = u"file:%s" % url2pathname(
                    unicode(uri[5:], 'utf-8'))
        else:
            download_uri = unquote_plus(unicode(uri, 'utf-8'))
        download_deferred = self.session.start_download_from_uri(
            download_uri, download_config)
        download_deferred.addCallback(download_added)
        download_deferred.addErrback(on_error)

        return NOT_DONE_YET
예제 #14
0
    def render_PUT(self, request):
        """
        .. http:put:: /downloads

        A PUT request to this endpoint will start a download from a provided URI. This URI can either represent a file
        location, a magnet link or a HTTP(S) url.
        - anon_hops: the number of hops for the anonymous download. 0 hops is equivalent to a plain download
        - safe_seeding: whether the seeding of the download should be anonymous or not (0 = off, 1 = on)
        - destination: the download destination path of the torrent
        - torrent: the URI of the torrent file that should be downloaded. This parameter is required.

            **Example request**:

                .. sourcecode:: none

                    curl -X PUT http://localhost:8085/downloads
                    --data "anon_hops=2&safe_seeding=1&destination=/my/dest/on/disk/&uri=file:/home/me/test.torrent

            **Example response**:

                .. sourcecode:: javascript

                    {"started": True, "infohash": "4344503b7e797ebf31582327a5baae35b11bda01"}
        """
        parameters = http.parse_qs(request.content.read(), 1)

        if 'uri' not in parameters or len(parameters['uri']) == 0:
            request.setResponseCode(http.BAD_REQUEST)
            return json.dumps({"error": "uri parameter missing"})

        download_config, error = DownloadsEndpoint.create_dconfig_from_params(parameters)
        if error:
            request.setResponseCode(http.BAD_REQUEST)
            return json.dumps({"error": error})

        def download_added(download):
            request.write(json.dumps({"started": True,
                                      "infohash": hexlify(download.get_def().get_infohash())}))
            request.finish()

        def on_error(error):
            request.setResponseCode(http.INTERNAL_SERVER_ERROR)
            request.write(json.dumps({"error": unichar_string(error.getErrorMessage())}))
            request.finish()

        uri = parameters['uri'][0]
        if uri.startswith("file:"):
            if uri.endswith(".mdblob"):
                filename = url2pathname(uri[5:].encode('utf-8') if isinstance(uri, six.text_type) else uri[5:])
                try:
                    payload = ChannelMetadataPayload.from_file(filename)
                except IOError:
                    request.setResponseCode(http.BAD_REQUEST)
                    return json.dumps({"error": "file not found"})
                except InvalidSignatureException:
                    request.setResponseCode(http.BAD_REQUEST)
                    return json.dumps({"error": "Metadata has invalid signature"})

                with db_session:
                    result = self.session.lm.mds.process_payload(payload)
                    if result:
                        node, status = result[0]
                        if (status == UNKNOWN_CHANNEL or
                                (status == UPDATED_OUR_VERSION and node.metadata_type == CHANNEL_TORRENT)):
                            node.subscribed = True
                            return json.dumps(
                                {"started": True, "infohash": hexlify(node.infohash)})
                    return json.dumps({"error": "Already subscribed"})
            else:
                download_uri = u"file:%s" % url2pathname(uri[5:]).decode('utf-8')
        else:
            download_uri = unquote_plus(cast_to_unicode_utf8(uri))
        download_deferred = self.session.start_download_from_uri(download_uri, download_config)
        download_deferred.addCallback(download_added)
        download_deferred.addErrback(on_error)

        return NOT_DONE_YET
예제 #15
0
 def test_wrong_signature_exception_on_channel_update(self):
     # Test wrong signature exception
     old_payload = ChannelMetadataPayload.from_file(CHANNEL_METADATA)
     payload = ChannelMetadataPayload.from_file(CHANNEL_METADATA_UPDATED)
     payload.signature = old_payload.signature
     self.assertRaises(InvalidSignatureException, self.session.lm.update_channel, payload)