From 23b3f144fce3424ae874d54a659cb7b8dd624ade Mon Sep 17 00:00:00 2001 From: Calum Lind Date: Sat, 9 Nov 2019 10:42:33 +0000 Subject: [#3298|Core] Fix pickle loading non-ascii state error When trying to load a torrents.state from version 1.3 users were encountering the following error: UnicodeDecodeError: 'ascii' codec can't decode byte This was due to the way that Python 2 was pickling state with torrent filenames that contained non-ascii characters and Python 3 was unpickling the state using ascii encoding and failing. The fix is to specify utf-8 encoding when loading torrents.state. --- deluge/core/torrentmanager.py | 7 ++- deluge/tests/data/utf8_filename_torrents.state | 85 ++++++++++++++++++++++++++ deluge/tests/test_torrentmanager.py | 13 +++- 3 files changed, 102 insertions(+), 3 deletions(-) create mode 100644 deluge/tests/data/utf8_filename_torrents.state diff --git a/deluge/core/torrentmanager.py b/deluge/core/torrentmanager.py index 752f93da0..b94868776 100644 --- a/deluge/core/torrentmanager.py +++ b/deluge/core/torrentmanager.py @@ -25,7 +25,7 @@ from twisted.internet.task import LoopingCall import deluge.component as component from deluge._libtorrent import lt -from deluge.common import archive_files, decode_bytes, get_magnet_info, is_magnet +from deluge.common import PY2, archive_files, decode_bytes, get_magnet_info, is_magnet from deluge.configmanager import ConfigManager, get_config_dir from deluge.core.authmanager import AUTH_LEVEL_ADMIN from deluge.core.torrent import Torrent, TorrentOptions, sanitize_filepath @@ -809,7 +809,10 @@ class TorrentManager(component.Component): try: with open(filepath, 'rb') as _file: - state = pickle.load(_file) + if PY2: + state = pickle.load(_file) + else: + state = pickle.load(_file, encoding='utf8') except (IOError, EOFError, pickle.UnpicklingError) as ex: message = 'Unable to load {}: {}'.format(filepath, ex) log.error(message) diff --git a/deluge/tests/data/utf8_filename_torrents.state b/deluge/tests/data/utf8_filename_torrents.state new file mode 100644 index 000000000..0e9c33dae --- /dev/null +++ b/deluge/tests/data/utf8_filename_torrents.state @@ -0,0 +1,85 @@ +(ideluge.core.torrentmanager +TorrentManagerState +p1 +(dp2 +S'torrents' +p3 +(lp4 +(ideluge.core.torrentmanager +TorrentState +p5 +(dp6 +S'max_download_speed' +p7 +I-1 +sS'move_completed_path' +p8 +S'/home/calum/Downloads' +p9 +sS'paused' +p10 +I00 +sS'max_upload_slots' +p11 +I-1 +sS'prioritize_first_last' +p12 +I00 +sS'max_connections' +p13 +I-1 +sS'compact' +p14 +I00 +sS'queue' +p15 +I0 +sS'file_priorities' +p16 +(lp17 +I4 +asS'filename' +p18 +S'\xc2\xa2.torrent' +p19 +sS'max_upload_speed' +p20 +I-1 +sS'save_path' +p21 +S'/home/calum/Downloads' +p22 +sS'time_added' +p23 +F1573563097.749759 +sS'total_uploaded' +p24 +I0 +sS'torrent_id' +p25 +S'80d81d55ef3b85f3c1b634c362e014b35594dc71' +p26 +sS'auto_managed' +p27 +I01 +sS'stop_at_ratio' +p28 +I00 +sS'move_completed' +p29 +I00 +sS'trackers' +p30 +(lp31 +sS'magnet' +p32 +NsS'remove_at_ratio' +p33 +I00 +sS'stop_ratio' +p34 +F2 +sS'is_finished' +p35 +I00 +sbasb. diff --git a/deluge/tests/test_torrentmanager.py b/deluge/tests/test_torrentmanager.py index bf84f451b..74d37bdeb 100644 --- a/deluge/tests/test_torrentmanager.py +++ b/deluge/tests/test_torrentmanager.py @@ -7,6 +7,8 @@ from __future__ import unicode_literals +import os +import shutil import warnings from base64 import b64encode @@ -28,7 +30,7 @@ warnings.resetwarnings() class TorrentmanagerTestCase(BaseTestCase): def set_up(self): - common.set_tmp_config_dir() + self.config_dir = common.set_tmp_config_dir() self.rpcserver = RPCServer(listen=False) self.core = Core() self.core.config.config['lsd'] = False @@ -118,3 +120,12 @@ class TorrentmanagerTestCase(BaseTestCase): self.assertRaises( InvalidTorrentError, self.tm.remove, 'torrentidthatdoesntexist' ) + + def test_open_state_from_python2(self): + """Open a Python2 state with a UTF-8 encoded torrent filename.""" + shutil.copy( + common.get_test_data_file('utf8_filename_torrents.state'), + os.path.join(self.config_dir, 'state', 'torrents.state'), + ) + state = self.tm.open_state() + self.assertEqual(len(state.torrents), 1) -- cgit