[GTK] Fix unable to prefetch magnet in thinclient

A UnicodeDecodeError is raised in transfer module when attempting to
prefetch a magnet.

This is result of passing a Python dict containing text bytes and raw
bytes that cannot be decoded as utf-8 in rencode when recieving the
message. This could be handled in rencode by returning raw bytes if
decoding fails (perhaps with a strict mode?) however better to follow
convention of encoding raw bytes in base64 in API calls.

Fixed by retaining bencoding and encoding with base64 when sending
result.

Resolves: https://github.com/deluge-torrent/deluge/pull/334
This commit is contained in:
Calum Lind 2022-01-08 18:54:15 +00:00
parent 79b7e6093f
commit e50927f575
No known key found for this signature in database
GPG Key ID: 90597A687B836BA3
5 changed files with 45 additions and 35 deletions

View File

@ -432,12 +432,14 @@ class Core(component.Component):
Used by UIs to get magnet files for selection before adding to session. Used by UIs to get magnet files for selection before adding to session.
The metadata is bencoded and for transfer base64 encoded.
Args: Args:
magnet (str): The magnet URI. magnet (str): The magnet URI.
timeout (int): Number of seconds to wait before canceling request. timeout (int): Number of seconds to wait before canceling request.
Returns: Returns:
Deferred: A tuple of (torrent_id (str), metadata (dict)) for the magnet. Deferred: A tuple of (torrent_id (str), metadata (str)) for the magnet.
""" """

View File

@ -13,6 +13,7 @@ import operator
import os import os
import pickle import pickle
import time import time
from base64 import b64encode
from collections import namedtuple from collections import namedtuple
from tempfile import gettempdir from tempfile import gettempdir
@ -396,12 +397,12 @@ class TorrentManager(component.Component):
else: else:
self.session.remove_torrent(torrent_handle, 1) self.session.remove_torrent(torrent_handle, 1)
metadata = None metadata = b''
if isinstance(torrent_info, lt.torrent_info): if isinstance(torrent_info, lt.torrent_info):
log.debug('prefetch metadata received') log.debug('prefetch metadata received')
metadata = lt.bdecode(torrent_info.metadata()) metadata = torrent_info.metadata()
return torrent_id, metadata return torrent_id, b64encode(metadata)
def _build_torrent_options(self, options): def _build_torrent_options(self, options):
"""Load default options and update if needed.""" """Load default options and update if needed."""

View File

@ -317,7 +317,7 @@ class CoreTestCase(BaseTestCase):
def test_prefetch_metadata_existing(self): def test_prefetch_metadata_existing(self):
"""Check another call with same magnet returns existing deferred.""" """Check another call with same magnet returns existing deferred."""
magnet = 'magnet:?xt=urn:btih:ab570cdd5a17ea1b61e970bb72047de141bce173' magnet = 'magnet:?xt=urn:btih:ab570cdd5a17ea1b61e970bb72047de141bce173'
expected = ('ab570cdd5a17ea1b61e970bb72047de141bce173', None) expected = ('ab570cdd5a17ea1b61e970bb72047de141bce173', b'')
def on_result(result): def on_result(result):
self.assertEqual(result, expected) self.assertEqual(result, expected)

View File

@ -15,6 +15,7 @@ from twisted.internet import defer, task
from twisted.trial import unittest from twisted.trial import unittest
from deluge import component from deluge import component
from deluge.bencode import bencode
from deluge.common import windows_check from deluge.common import windows_check
from deluge.core.core import Core from deluge.core.core import Core
from deluge.core.rpcserver import RPCServer from deluge.core.rpcserver import RPCServer
@ -82,34 +83,38 @@ class TorrentmanagerTestCase(BaseTestCase):
expected = ( expected = (
'ab570cdd5a17ea1b61e970bb72047de141bce173', 'ab570cdd5a17ea1b61e970bb72047de141bce173',
{ b64encode(
b'piece length': 32768, bencode(
b'sha1': ( {
b'2\xce\xb6\xa8"\xd7\xf0\xd4\xbf\xdc^K\xba\x1bh' b'piece length': 32768,
b'\x9d\xc5\xb7\xac\xdd' b'sha1': (
), b'2\xce\xb6\xa8"\xd7\xf0\xd4\xbf\xdc^K\xba\x1bh'
b'name': b'azcvsupdater_2.6.2.jar', b'\x9d\xc5\xb7\xac\xdd'
b'private': 0, ),
b'pieces': ( b'name': b'azcvsupdater_2.6.2.jar',
b"\xdb\x04B\x05\xc3'\xdab\xb8su97\xa9u" b'private': 0,
b'\xca<w\\\x1ef\xd4\x9b\x16\xa9}\xc0\x9f:\xfd' b'pieces': (
b'\x97qv\x83\xa2"\xef\x9d7\x0by!\rl\xe5v\xb7' b"\xdb\x04B\x05\xc3'\xdab\xb8su97\xa9u"
b'\x18{\xf7/"P\xe9\x8d\x01D\x9e8\xbd\x16\xe3' b'\xca<w\\\x1ef\xd4\x9b\x16\xa9}\xc0\x9f:\xfd'
b'\xfb-\x9d\xaa\xbcM\x11\xba\x92\xfc\x13F\xf0' b'\x97qv\x83\xa2"\xef\x9d7\x0by!\rl\xe5v\xb7'
b'\x1c\x86x+\xc8\xd0S\xa9\x90`\xa1\xe4\x82\xe8' b'\x18{\xf7/"P\xe9\x8d\x01D\x9e8\xbd\x16\xe3'
b'\xfc\x08\xf7\xe3\xe5\xf6\x85\x1c%\xe7%\n\xed' b'\xfb-\x9d\xaa\xbcM\x11\xba\x92\xfc\x13F\xf0'
b'\xc0\x1f\xa1;\x9a\xea\xcf\x90\x0c/F>\xdf\xdagA' b'\x1c\x86x+\xc8\xd0S\xa9\x90`\xa1\xe4\x82\xe8'
b'\xc42|\xda\x82\xf5\xa6b\xa1\xb8#\x80wI\xd8f' b'\xfc\x08\xf7\xe3\xe5\xf6\x85\x1c%\xe7%\n\xed'
b'\xf8\xbd\xacW\xab\xc3s\xe0\xbbw\xf2K\xbe\xee' b'\xc0\x1f\xa1;\x9a\xea\xcf\x90\x0c/F>\xdf\xdagA'
b'\xa8rG\xe1W\xe8\xb7\xc2i\xf3\xd8\xaf\x9d\xdc' b'\xc42|\xda\x82\xf5\xa6b\xa1\xb8#\x80wI\xd8f'
b'\xd0#\xf4\xc1\x12u\xcd\x0bE?:\xe8\x9c\x1cu' b'\xf8\xbd\xacW\xab\xc3s\xe0\xbbw\xf2K\xbe\xee'
b'\xabb(oj\r^\xd5\xd5A\x83\x88\x9a\xa1J\x1c?' b'\xa8rG\xe1W\xe8\xb7\xc2i\xf3\xd8\xaf\x9d\xdc'
b'\xa1\xd6\x8c\x83\x9e&' b'\xd0#\xf4\xc1\x12u\xcd\x0bE?:\xe8\x9c\x1cu'
), b'\xabb(oj\r^\xd5\xd5A\x83\x88\x9a\xa1J\x1c?'
b'length': 307949, b'\xa1\xd6\x8c\x83\x9e&'
b'name.utf-8': b'azcvsupdater_2.6.2.jar', ),
b'ed2k': b'>p\xefl\xfa]\x95K\x1b^\xc2\\;;e\xb7', b'length': 307949,
}, b'name.utf-8': b'azcvsupdater_2.6.2.jar',
b'ed2k': b'>p\xefl\xfa]\x95K\x1b^\xc2\\;;e\xb7',
}
)
),
) )
self.assertEqual(expected, self.successResultOf(d)) self.assertEqual(expected, self.successResultOf(d))
@ -117,7 +122,7 @@ class TorrentmanagerTestCase(BaseTestCase):
magnet = 'magnet:?xt=urn:btih:ab570cdd5a17ea1b61e970bb72047de141bce173' magnet = 'magnet:?xt=urn:btih:ab570cdd5a17ea1b61e970bb72047de141bce173'
d = self.tm.prefetch_metadata(magnet, 30) d = self.tm.prefetch_metadata(magnet, 30)
self.clock.advance(30) self.clock.advance(30)
expected = ('ab570cdd5a17ea1b61e970bb72047de141bce173', None) expected = ('ab570cdd5a17ea1b61e970bb72047de141bce173', b'')
return d.addCallback(self.assertEqual, expected) return d.addCallback(self.assertEqual, expected)
@pytest.mark.todo @pytest.mark.todo

View File

@ -8,7 +8,7 @@
import logging import logging
import os import os
from base64 import b64encode from base64 import b64decode, b64encode
from xml.sax.saxutils import escape as xml_escape from xml.sax.saxutils import escape as xml_escape
from xml.sax.saxutils import unescape as xml_unescape from xml.sax.saxutils import unescape as xml_unescape
@ -16,6 +16,7 @@ from gi.repository import Gtk
from gi.repository.GObject import TYPE_INT64, TYPE_UINT64 from gi.repository.GObject import TYPE_INT64, TYPE_UINT64
import deluge.component as component import deluge.component as component
from deluge.bencode import bdecode
from deluge.common import ( from deluge.common import (
create_magnet_uri, create_magnet_uri,
decode_bytes, decode_bytes,
@ -268,6 +269,7 @@ class AddTorrentDialog(component.Component):
return return
if metadata: if metadata:
metadata = bdecode(b64decode(metadata))
info = TorrentInfo.from_metadata(metadata, [[t] for t in trackers]) info = TorrentInfo.from_metadata(metadata, [[t] for t in trackers])
self.files[info_hash] = info.files self.files[info_hash] = info.files
self.infos[info_hash] = info.filedata self.infos[info_hash] = info.filedata