From 7dbbfedd3b765f9c0ac95424c8864f2ea4feee5f Mon Sep 17 00:00:00 2001 From: Florian Bruhin Date: Sat, 3 Jan 2015 17:50:59 +0100 Subject: [PATCH] Replace unencodable chars in download filenames. Fixes #427. --- qutebrowser/browser/downloads.py | 13 ++++++++++++- qutebrowser/test/utils/test_utils.py | 20 ++++++++++++++++++++ qutebrowser/utils/utils.py | 8 ++++++++ 3 files changed, 40 insertions(+), 1 deletion(-) diff --git a/qutebrowser/browser/downloads.py b/qutebrowser/browser/downloads.py index d7b5f7c35..f9aaa022e 100644 --- a/qutebrowser/browser/downloads.py +++ b/qutebrowser/browser/downloads.py @@ -21,6 +21,7 @@ import io import os +import sys import os.path import shutil import functools @@ -385,6 +386,10 @@ class DownloadItem(QObject): "existing: {}, fileobj {}".format( filename, self._filename, self.fileobj)) filename = os.path.expanduser(filename) + # Remove chars which can't be encoded in the filename encoding. + # See https://github.com/The-Compiler/qutebrowser/issues/427 + encoding = sys.getfilesystemencoding() + filename = utils.force_encoding(filename, encoding) if os.path.isabs(filename) and os.path.isdir(filename): # We got an absolute directory from the user, so we save it under # the default filename in that directory. @@ -639,7 +644,10 @@ class DownloadManager(QAbstractListModel): return self.fetch_request(request, filename, fileobj, page, auto_remove) q = self._prepare_question() - q.default = urlutils.filename_from_url(request.url()) + filename = urlutils.filename_from_url(request.url()) + encoding = sys.getfilesystemencoding() + filename = utils.force_encoding(filename, encoding) + q.default = filename message_bridge = objreg.get('message-bridge', scope='window', window=self._win_id) q.answered.connect( @@ -719,6 +727,9 @@ class DownloadManager(QAbstractListModel): download.autoclose = False else: q = self._prepare_question() + encoding = sys.getfilesystemencoding() + suggested_filename = utils.force_encoding(suggested_filename, + encoding) q.default = suggested_filename q.answered.connect(download.set_filename) q.cancelled.connect(download.cancel) diff --git a/qutebrowser/test/utils/test_utils.py b/qutebrowser/test/utils/test_utils.py index 027e0d516..a0db50b51 100644 --- a/qutebrowser/test/utils/test_utils.py +++ b/qutebrowser/test/utils/test_utils.py @@ -383,5 +383,25 @@ class RaisesTests(unittest.TestCase): utils.raises(ValueError, self.do_raise) +class ForceEncodingTests(unittest.TestCase): + + """Test force_encoding.""" + + def test_fitting_ascii(self): + """Test with a text fitting into ascii.""" + text = 'hello world' + self.assertEqual(utils.force_encoding(text, 'ascii'), text) + + def test_fitting_utf8(self): + """Test with a text fitting into utf-8.""" + text = 'hellö wörld' + self.assertEqual(utils.force_encoding(text, 'utf-8'), text) + + def test_not_fitting_ascii(self): + """Test with a text not fitting into ascii.""" + text = 'hellö wörld' + self.assertEqual(utils.force_encoding(text, 'ascii'), 'hell? w?rld') + + if __name__ == '__main__': unittest.main() diff --git a/qutebrowser/utils/utils.py b/qutebrowser/utils/utils.py index 567c84e9c..3d264a335 100644 --- a/qutebrowser/utils/utils.py +++ b/qutebrowser/utils/utils.py @@ -542,3 +542,11 @@ def raises(exc, func, *args): return True else: return False + + +def force_encoding(text, encoding): + """Make sure a given text is encodable with the given encoding. + + This replaces all chars not encodable with question marks. + """ + return text.encode(encoding, errors='replace').decode(encoding)