[#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.
This commit is contained in:
Calum Lind 2019-11-09 10:42:33 +00:00
parent 89d62eb509
commit 23b3f144fc
3 changed files with 102 additions and 3 deletions

View File

@ -25,7 +25,7 @@ from twisted.internet.task import LoopingCall
import deluge.component as component import deluge.component as component
from deluge._libtorrent import lt 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.configmanager import ConfigManager, get_config_dir
from deluge.core.authmanager import AUTH_LEVEL_ADMIN from deluge.core.authmanager import AUTH_LEVEL_ADMIN
from deluge.core.torrent import Torrent, TorrentOptions, sanitize_filepath from deluge.core.torrent import Torrent, TorrentOptions, sanitize_filepath
@ -809,7 +809,10 @@ class TorrentManager(component.Component):
try: try:
with open(filepath, 'rb') as _file: 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: except (IOError, EOFError, pickle.UnpicklingError) as ex:
message = 'Unable to load {}: {}'.format(filepath, ex) message = 'Unable to load {}: {}'.format(filepath, ex)
log.error(message) log.error(message)

View File

@ -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.

View File

@ -7,6 +7,8 @@
from __future__ import unicode_literals from __future__ import unicode_literals
import os
import shutil
import warnings import warnings
from base64 import b64encode from base64 import b64encode
@ -28,7 +30,7 @@ warnings.resetwarnings()
class TorrentmanagerTestCase(BaseTestCase): class TorrentmanagerTestCase(BaseTestCase):
def set_up(self): def set_up(self):
common.set_tmp_config_dir() self.config_dir = common.set_tmp_config_dir()
self.rpcserver = RPCServer(listen=False) self.rpcserver = RPCServer(listen=False)
self.core = Core() self.core = Core()
self.core.config.config['lsd'] = False self.core.config.config['lsd'] = False
@ -118,3 +120,12 @@ class TorrentmanagerTestCase(BaseTestCase):
self.assertRaises( self.assertRaises(
InvalidTorrentError, self.tm.remove, 'torrentidthatdoesntexist' 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)