From 473654ccb72b7f04cc1774339ef2c1826db3775a Mon Sep 17 00:00:00 2001 From: Itamar Turner-Trauring Date: Tue, 15 Jun 2021 13:26:50 -0400 Subject: [PATCH 1/5] Test demonstrating the problem. --- src/allmydata/test/cli/test_cp.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/allmydata/test/cli/test_cp.py b/src/allmydata/test/cli/test_cp.py index d55bc01bd..fff50f331 100644 --- a/src/allmydata/test/cli/test_cp.py +++ b/src/allmydata/test/cli/test_cp.py @@ -55,6 +55,11 @@ class Cp(GridTestMixin, CLITestMixin, unittest.TestCase): d.addCallback(lambda res: self.do_cli("get", "tahoe:" + artonwall_arg)) d.addCallback(lambda rc_out_err: self.assertEqual(rc_out_err[1], DATA1)) + # Version where destination filename is explicitly Unicode too. + d.addCallback(lambda res: self.do_cli("cp", fn1, "tahoe:" + artonwall_arg + "-2")) + d.addCallback(lambda res: self.do_cli("get", "tahoe:" + artonwall_arg + "-2")) + d.addCallback(lambda rc_out_err: self.assertEqual(rc_out_err[1], DATA1)) + d.addCallback(lambda res: self.do_cli("cp", fn2, "tahoe:")) d.addCallback(lambda res: self.do_cli("get", "tahoe:Metallica")) @@ -74,7 +79,7 @@ class Cp(GridTestMixin, CLITestMixin, unittest.TestCase): self.failUnlessReallyEqual(rc, 0) if PY2: out = out.decode(get_io_encoding()) - self.failUnlessReallyEqual(out, u"Metallica\n\u00C4rtonwall\n") + self.failUnlessReallyEqual(out, u"Metallica\n\u00C4rtonwall\n\u00C4rtonwall-2\n") self.assertEqual(len(err), 0, err) d.addCallback(_check) From d74ef0798d16581827f3e6740e4246655197330e Mon Sep 17 00:00:00 2001 From: Itamar Turner-Trauring Date: Tue, 15 Jun 2021 13:46:22 -0400 Subject: [PATCH 2/5] Fix quoting to work on Python 2. --- src/allmydata/scripts/common_http.py | 6 ++++-- src/allmydata/web/common.py | 3 ++- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/src/allmydata/scripts/common_http.py b/src/allmydata/scripts/common_http.py index bf47f2dd6..cbc6e2969 100644 --- a/src/allmydata/scripts/common_http.py +++ b/src/allmydata/scripts/common_http.py @@ -97,8 +97,10 @@ def format_http_success(resp): return "%s %s" % (resp.status, quote_output(resp.reason, quotemarks=False)) def format_http_error(msg, resp): - return "%s: %s %s\n%s" % (msg, resp.status, quote_output(resp.reason, quotemarks=False), - quote_output(resp.read(), quotemarks=False)) + return quote_output( + "%s: %s %s\n%s" % (msg, resp.status, str(resp.reason, "utf-8"), + str(resp.read(), "utf-8")), + quotemarks=False) def check_http_error(resp, stderr): if resp.status < 200 or resp.status >= 300: diff --git a/src/allmydata/web/common.py b/src/allmydata/web/common.py index bb84a2e70..bf89044a3 100644 --- a/src/allmydata/web/common.py +++ b/src/allmydata/web/common.py @@ -90,6 +90,7 @@ from allmydata.util.time_format import ( ) from allmydata.util.encodingutil import ( quote_output, + quote_output_u, to_bytes, ) from allmydata.util import abbreviate @@ -324,7 +325,7 @@ def humanize_exception(exc): return ("There was already a child by that name, and you asked me " "to not replace it.", http.CONFLICT) if isinstance(exc, NoSuchChildError): - quoted_name = quote_output(exc.args[0], encoding="utf-8", quotemarks=False) + quoted_name = quote_output_u(exc.args[0], quotemarks=False) return ("No such child: %s" % quoted_name, http.NOT_FOUND) if isinstance(exc, NotEnoughSharesError): t = ("NotEnoughSharesError: This indicates that some " From 23a71db38d7fb3f77a923911b5c6c8f4a00e022e Mon Sep 17 00:00:00 2001 From: Itamar Turner-Trauring Date: Tue, 15 Jun 2021 13:46:57 -0400 Subject: [PATCH 3/5] News file. --- newsfragments/3738.bugfix | 1 + 1 file changed, 1 insertion(+) create mode 100644 newsfragments/3738.bugfix diff --git a/newsfragments/3738.bugfix b/newsfragments/3738.bugfix new file mode 100644 index 000000000..6a4bc1cd9 --- /dev/null +++ b/newsfragments/3738.bugfix @@ -0,0 +1 @@ +Fix regression where uploading files with non-ASCII names failed. \ No newline at end of file From e8308043e3181b65756b80c926c653a78c1950be Mon Sep 17 00:00:00 2001 From: Itamar Turner-Trauring Date: Tue, 15 Jun 2021 14:03:58 -0400 Subject: [PATCH 4/5] Method that doesn't break on Python 3. --- src/allmydata/scripts/common_http.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/allmydata/scripts/common_http.py b/src/allmydata/scripts/common_http.py index cbc6e2969..a842f93ae 100644 --- a/src/allmydata/scripts/common_http.py +++ b/src/allmydata/scripts/common_http.py @@ -97,9 +97,10 @@ def format_http_success(resp): return "%s %s" % (resp.status, quote_output(resp.reason, quotemarks=False)) def format_http_error(msg, resp): + # ensure_text() shouldn't be necessary when Python 2 is dropped. return quote_output( - "%s: %s %s\n%s" % (msg, resp.status, str(resp.reason, "utf-8"), - str(resp.read(), "utf-8")), + "%s: %s %s\n%s" % (msg, resp.status, six.ensure_text(resp.reason), + six.ensure_text(resp.read())), quotemarks=False) def check_http_error(resp, stderr): From f778d25c44c7d2feb59332a43f89ceeafd7a05c7 Mon Sep 17 00:00:00 2001 From: Itamar Turner-Trauring Date: Tue, 15 Jun 2021 14:05:49 -0400 Subject: [PATCH 5/5] Apply same fix to success path. --- src/allmydata/scripts/common_http.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/allmydata/scripts/common_http.py b/src/allmydata/scripts/common_http.py index a842f93ae..95099a2eb 100644 --- a/src/allmydata/scripts/common_http.py +++ b/src/allmydata/scripts/common_http.py @@ -94,7 +94,10 @@ def do_http(method, url, body=b""): def format_http_success(resp): - return "%s %s" % (resp.status, quote_output(resp.reason, quotemarks=False)) + # ensure_text() shouldn't be necessary when Python 2 is dropped. + return quote_output( + "%s %s" % (resp.status, six.ensure_text(resp.reason)), + quotemarks=False) def format_http_error(msg, resp): # ensure_text() shouldn't be necessary when Python 2 is dropped.