From 635f6d970dd83de53ccece2837f8eae72dfc832f Mon Sep 17 00:00:00 2001 From: Calum Lind Date: Fri, 13 Dec 2019 11:21:51 +0000 Subject: [PATCH] [Config] Fix loading config with double-quotes in string If a password or other string contained a double-quote then the config would fail to be loaded on startup and reset. This occurred due to fixing a similar issue with curly braces for #3079 in commit 33e9545cd44 and the checking for double-quotes had unforseen consequences. To resolve both these issues the code to check for json objects in config files was simplified and utilises the json module raw_decode method to ensure the extracted string indexes are json objects. --- deluge/config.py | 41 ++++++++++++++++--------------------- deluge/tests/test_config.py | 28 +++++++++++++++++++++---- 2 files changed, 42 insertions(+), 27 deletions(-) diff --git a/deluge/config.py b/deluge/config.py index c85299682..90944a38f 100644 --- a/deluge/config.py +++ b/deluge/config.py @@ -74,38 +74,33 @@ def prop(func): return property(doc=func.__doc__, **func()) -def find_json_objects(s): - """Find json objects in a string. +def find_json_objects(text, decoder=json.JSONDecoder()): + """Find json objects in text. Args: - s (str): the string to find json objects in + text (str): The text to find json objects within. Returns: list: A list of tuples containing start and end locations of json - objects in string `s`. e.g. [(start, end), ...] + objects in the text. e.g. [(start, end), ...] + """ objects = [] - opens = 0 - start = s.find('{') - offset = start + offset = 0 + while True: + try: + start = text.index('{', offset) + except ValueError: + break - if start < 0: - return [] - - quoted = False - for index, c in enumerate(s[offset:]): - if c == '"': - quoted = not quoted - elif quoted: - continue - elif c == '{': - opens += 1 - elif c == '}': - opens -= 1 - if opens == 0: - objects.append((start, index + offset + 1)) - start = index + offset + 1 + try: + __, index = decoder.raw_decode(text[start:]) + except json.decoder.JSONDecodeError: + offset = start + 1 + else: + offset = start + index + objects.append((start, offset)) return objects diff --git a/deluge/tests/test_config.py b/deluge/tests/test_config.py index 270cc5a5b..0f6df3bb5 100644 --- a/deluge/tests/test_config.py +++ b/deluge/tests/test_config.py @@ -25,6 +25,7 @@ DEFAULTS = { 'float': 0.435, 'bool': True, 'unicode': 'foobar', + 'password': 'abc123*\\[!]?/<>#{@}=|"+$%(^)~', } @@ -95,6 +96,7 @@ class ConfigTestCase(unittest.TestCase): self.assertEqual(config['string'], 'foobar') self.assertEqual(config['float'], 0.435) + self.assertEqual(config['password'], 'abc123*\\[!]?/<>#{@}=|"+$%(^)~') # Test opening a previous 1.2 config file of just a json object import json @@ -107,8 +109,8 @@ class ConfigTestCase(unittest.TestCase): # Test opening a previous 1.2 config file of having the format versions # as ints with open(os.path.join(self.config_dir, 'test.conf'), 'wb') as _file: - _file.write(bytes(1) + b'\n') - _file.write(bytes(1) + b'\n') + _file.write(b'1\n') + _file.write(b'1\n') json.dump(DEFAULTS, getwriter('utf8')(_file), **JSON_FORMAT) check_config() @@ -184,9 +186,27 @@ class ConfigTestCase(unittest.TestCase): }{ "ssl": true, "enabled": false, - "port": 8115 + "port": 8115, "password": "abc{def" -}\n""" +}""" + + from deluge.config import find_json_objects + + objects = find_json_objects(s) + self.assertEqual(len(objects), 2) + + def test_find_json_objects_double_quote(self): + """Test with string containing double quote""" + s = r"""{ + "file": 1, + "format": 1 +}{ + "ssl": true, + "enabled": false, + "port": 8115, + "password": "abc\"def" +} +""" from deluge.config import find_json_objects