[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 33e9545cd4 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.
This commit is contained in:
Calum Lind 2019-12-13 11:21:51 +00:00
commit 635f6d970d
2 changed files with 42 additions and 27 deletions

View file

@ -74,38 +74,33 @@ def prop(func):
return property(doc=func.__doc__, **func()) return property(doc=func.__doc__, **func())
def find_json_objects(s): def find_json_objects(text, decoder=json.JSONDecoder()):
"""Find json objects in a string. """Find json objects in text.
Args: Args:
s (str): the string to find json objects in text (str): The text to find json objects within.
Returns: Returns:
list: A list of tuples containing start and end locations of json 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 = [] objects = []
opens = 0 offset = 0
start = s.find('{') while True:
offset = start try:
start = text.index('{', offset)
except ValueError:
break
if start < 0: try:
return [] __, index = decoder.raw_decode(text[start:])
except json.decoder.JSONDecodeError:
quoted = False offset = start + 1
for index, c in enumerate(s[offset:]): else:
if c == '"': offset = start + index
quoted = not quoted objects.append((start, offset))
elif quoted:
continue
elif c == '{':
opens += 1
elif c == '}':
opens -= 1
if opens == 0:
objects.append((start, index + offset + 1))
start = index + offset + 1
return objects return objects

View file

@ -25,6 +25,7 @@ DEFAULTS = {
'float': 0.435, 'float': 0.435,
'bool': True, 'bool': True,
'unicode': 'foobar', 'unicode': 'foobar',
'password': 'abc123*\\[!]?/<>#{@}=|"+$%(^)~',
} }
@ -95,6 +96,7 @@ class ConfigTestCase(unittest.TestCase):
self.assertEqual(config['string'], 'foobar') self.assertEqual(config['string'], 'foobar')
self.assertEqual(config['float'], 0.435) self.assertEqual(config['float'], 0.435)
self.assertEqual(config['password'], 'abc123*\\[!]?/<>#{@}=|"+$%(^)~')
# Test opening a previous 1.2 config file of just a json object # Test opening a previous 1.2 config file of just a json object
import json import json
@ -107,8 +109,8 @@ class ConfigTestCase(unittest.TestCase):
# Test opening a previous 1.2 config file of having the format versions # Test opening a previous 1.2 config file of having the format versions
# as ints # as ints
with open(os.path.join(self.config_dir, 'test.conf'), 'wb') as _file: with open(os.path.join(self.config_dir, 'test.conf'), 'wb') as _file:
_file.write(bytes(1) + b'\n') _file.write(b'1\n')
_file.write(bytes(1) + b'\n') _file.write(b'1\n')
json.dump(DEFAULTS, getwriter('utf8')(_file), **JSON_FORMAT) json.dump(DEFAULTS, getwriter('utf8')(_file), **JSON_FORMAT)
check_config() check_config()
@ -184,9 +186,27 @@ class ConfigTestCase(unittest.TestCase):
}{ }{
"ssl": true, "ssl": true,
"enabled": false, "enabled": false,
"port": 8115 "port": 8115,
"password": "abc{def" "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 from deluge.config import find_json_objects