Handle unencodable file paths in config types.

If an user e.g. has a download-directory of ~/föö, but has LC_ALL=C set, we'll
get an UnicodeEncodeError when trying to validate it. This is now handled
properly by raising a ValidationError.

Fixes #562.
This commit is contained in:
Florian Bruhin 2015-03-19 12:39:56 +01:00
parent 330e03d382
commit fb5fbd09da
3 changed files with 61 additions and 21 deletions

View File

@ -833,10 +833,14 @@ class File(BaseType):
else: else:
raise configexc.ValidationError(value, "may not be empty!") raise configexc.ValidationError(value, "may not be empty!")
value = os.path.expanduser(value) value = os.path.expanduser(value)
if not os.path.isfile(value): try:
raise configexc.ValidationError(value, "must be a valid file!") if not os.path.isfile(value):
if not os.path.isabs(value): raise configexc.ValidationError(value, "must be a valid file!")
raise configexc.ValidationError(value, "must be an absolute path!") if not os.path.isabs(value):
raise configexc.ValidationError(
value, "must be an absolute path!")
except UnicodeEncodeError as e:
raise configexc.ValidationError(value, e)
def transform(self, value): def transform(self, value):
if not value: if not value:
@ -858,11 +862,15 @@ class Directory(BaseType):
raise configexc.ValidationError(value, "may not be empty!") raise configexc.ValidationError(value, "may not be empty!")
value = os.path.expandvars(value) value = os.path.expandvars(value)
value = os.path.expanduser(value) value = os.path.expanduser(value)
if not os.path.isdir(value): try:
raise configexc.ValidationError(value, "must be a valid " if not os.path.isdir(value):
"directory!") raise configexc.ValidationError(
if not os.path.isabs(value): value, "must be a valid directory!")
raise configexc.ValidationError(value, "must be an absolute path!") if not os.path.isabs(value):
raise configexc.ValidationError(
value, "must be an absolute path!")
except UnicodeEncodeError as e:
raise configexc.ValidationError(value, e)
def transform(self, value): def transform(self, value):
if not value: if not value:
@ -1179,18 +1187,21 @@ class UserStyleSheet(File):
raise configexc.ValidationError(value, "may not be empty!") raise configexc.ValidationError(value, "may not be empty!")
value = os.path.expandvars(value) value = os.path.expandvars(value)
value = os.path.expanduser(value) value = os.path.expanduser(value)
if not os.path.isabs(value): try:
# probably a CSS, so we don't handle it as filename. if not os.path.isabs(value):
# FIXME We just try if it is encodable, maybe we should validate # probably a CSS, so we don't handle it as filename.
# CSS? # FIXME We just try if it is encodable, maybe we should
# https://github.com/The-Compiler/qutebrowser/issues/115 # validate CSS?
try: # https://github.com/The-Compiler/qutebrowser/issues/115
value.encode('utf-8') try:
except UnicodeEncodeError as e: value.encode('utf-8')
raise configexc.ValidationError(value, str(e)) except UnicodeEncodeError as e:
return raise configexc.ValidationError(value, str(e))
elif not os.path.isfile(value): return
raise configexc.ValidationError(value, "must be a valid file!") elif not os.path.isfile(value):
raise configexc.ValidationError(value, "must be a valid file!")
except UnicodeEncodeError as e:
raise configexc.ValidationError(value, e)
def transform(self, value): def transform(self, value):
path = os.path.expandvars(value) path = os.path.expandvars(value)

View File

@ -1374,6 +1374,13 @@ class FileTests(unittest.TestCase):
self.t.validate('~/foobar') self.t.validate('~/foobar')
os_path.expanduser.assert_called_once_with('~/foobar') os_path.expanduser.assert_called_once_with('~/foobar')
def test_validate_invalid_encoding(self, os_path):
"""Test validate with an invalid encoding, e.g. LC_ALL=C."""
os_path.isfile.side_effect = helpers.unicode_encode_err
os_path.isabs.side_effect = helpers.unicode_encode_err
with self.assertRaises(configexc.ValidationError):
self.t.validate('foobar')
def test_transform(self, os_path): def test_transform(self, os_path):
"""Test transform.""" """Test transform."""
os_path.expanduser.side_effect = lambda x: x.replace('~', '/home/foo') os_path.expanduser.side_effect = lambda x: x.replace('~', '/home/foo')
@ -1445,6 +1452,13 @@ class DirectoryTests(unittest.TestCase):
self.t.validate('$BAR/foobar') self.t.validate('$BAR/foobar')
os_path.expandvars.assert_called_once_with('$BAR/foobar') os_path.expandvars.assert_called_once_with('$BAR/foobar')
def test_validate_invalid_encoding(self, os_path):
"""Test validate with an invalid encoding, e.g. LC_ALL=C."""
os_path.isdir.side_effect = helpers.unicode_encode_err
os_path.isabs.side_effect = helpers.unicode_encode_err
with self.assertRaises(configexc.ValidationError):
self.t.validate('foobar')
def test_transform(self, os_path): def test_transform(self, os_path):
"""Test transform.""" """Test transform."""
os_path.expandvars.side_effect = lambda x: x os_path.expandvars.side_effect = lambda x: x
@ -1879,6 +1893,14 @@ class UserStyleSheetTests(unittest.TestCase):
def setUp(self): def setUp(self):
self.t = configtypes.UserStyleSheet() self.t = configtypes.UserStyleSheet()
@mock.patch('qutebrowser.config.configtypes.os.path', autospec=True)
def test_validate_invalid_encoding(self, os_path):
"""Test validate with an invalid encoding, e.g. LC_ALL=C."""
os_path.isfile.side_effect = helpers.unicode_encode_err
os_path.isabs.side_effect = helpers.unicode_encode_err
with self.assertRaises(configexc.ValidationError):
self.t.validate('foobar')
def test_transform_empty(self): def test_transform_empty(self):
"""Test transform with an empty value.""" """Test transform with an empty value."""
self.assertIsNone(self.t.transform('')) self.assertIsNone(self.t.transform(''))

View File

@ -29,6 +29,13 @@ from PyQt5.QtWebKitWidgets import QWebPage
from PyQt5.QtNetwork import QNetworkAccessManager from PyQt5.QtNetwork import QNetworkAccessManager
unicode_encode_err = UnicodeEncodeError('ascii', # codec
'', # object
0, # start
2, # end
'fake exception') # reason
@contextlib.contextmanager @contextlib.contextmanager
def environ_set_temp(env): def environ_set_temp(env):
"""Set temporary environment variables. """Set temporary environment variables.