diff options
author | Calum Lind <calumlind+deluge@gmail.com> | 2019-11-09 10:42:33 +0000 |
---|---|---|
committer | Calum Lind <calumlind+deluge@gmail.com> | 2019-11-12 15:21:56 +0000 |
commit | 23b3f144fce3424ae874d54a659cb7b8dd624ade (patch) | |
tree | c294e7a3dbbddc72e5c208907397bbcf7c1ac297 | |
parent | 89d62eb509d498becd83d8854d56e5d6581a7cbf (diff) | |
download | deluge-23b3f144fce3424ae874d54a659cb7b8dd624ade.tar.gz deluge-23b3f144fce3424ae874d54a659cb7b8dd624ade.tar.bz2 deluge-23b3f144fce3424ae874d54a659cb7b8dd624ade.zip |
[#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.
-rw-r--r-- | deluge/core/torrentmanager.py | 7 | ||||
-rw-r--r-- | deluge/tests/data/utf8_filename_torrents.state | 85 | ||||
-rw-r--r-- | deluge/tests/test_torrentmanager.py | 13 |
3 files changed, 102 insertions, 3 deletions
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) |