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)
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)
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)
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)
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_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')
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)
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)
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)
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)
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
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