From e622198bab4771a9bb32aefdd16e390903ed8865 Mon Sep 17 00:00:00 2001 From: Akhil Narang Date: Wed, 10 Jan 2024 18:44:51 +0530 Subject: [PATCH] fix(response): fixup non-ASCII character filenames This was previously only handled for binary files Werkzeug crashes otherwise: 17:54:40 web.1 | Traceback (most recent call last): 17:54:40 web.1 | File "/home/akhil/frappe/frappe-bench/env/lib/python3.11/site-packages/werkzeug/serving.py", line 362, in run_wsgi 17:54:40 web.1 | execute(self.server.app) 17:54:40 web.1 | File "/home/akhil/frappe/frappe-bench/env/lib/python3.11/site-packages/werkzeug/serving.py", line 326, in execute 17:54:40 web.1 | write(data) 17:54:40 web.1 | File "/home/akhil/frappe/frappe-bench/env/lib/python3.11/site-packages/werkzeug/serving.py", line 266, in write 17:54:40 web.1 | self.send_header(key, value) 17:54:40 web.1 | File "/usr/lib/python3.11/http/server.py", line 526, in send_header 17:54:40 web.1 | ("%s: %s\r\n" % (keyword, value)).encode('latin-1', 'strict')) 17:54:40 web.1 | UnicodeEncodeError: 'latin-1' codec can't encode characters in position 43-52: ordinal not in range(256) Signed-off-by: Akhil Narang --- frappe/utils/response.py | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/frappe/utils/response.py b/frappe/utils/response.py index 927e3f3b38..96ae268921 100644 --- a/frappe/utils/response.py +++ b/frappe/utils/response.py @@ -7,7 +7,7 @@ import json import mimetypes import os import sys -from typing import TYPE_CHECKING, Literal +from typing import TYPE_CHECKING from urllib.parse import quote import werkzeug.utils @@ -92,6 +92,7 @@ def as_csv(): response = Response() response.mimetype = "text/csv" filename = f"{frappe.response['doctype']}.csv" + filename = filename.encode("utf-8").decode("unicode-escape", "ignore") response.headers.add("Content-Disposition", "attachment", filename=filename) response.data = frappe.response["result"] return response @@ -101,6 +102,7 @@ def as_txt(): response = Response() response.mimetype = "text" filename = f"{frappe.response['doctype']}.txt" + filename = filename.encode("utf-8").decode("unicode-escape", "ignore") response.headers.add("Content-Disposition", "attachment", filename=filename) response.data = frappe.response["result"] return response @@ -113,10 +115,11 @@ def as_raw(): or mimetypes.guess_type(frappe.response["filename"])[0] or "application/unknown" ) + filename = frappe.response["filename"].encode("utf-8").decode("unicode-escape", "ignore") response.headers.add( "Content-Disposition", frappe.response.get("display_content_as", "attachment"), - filename=frappe.response["filename"], + filename=filename, ) response.data = frappe.response["filecontent"] return response @@ -138,7 +141,8 @@ def as_json(): def as_pdf(): response = Response() response.mimetype = "application/pdf" - response.headers.add("Content-Disposition", None, filename=frappe.response["filename"]) + filename = frappe.response["filename"].encode("utf-8").decode("unicode-escape", "ignore") + response.headers.add("Content-Disposition", None, filename=filename) response.data = frappe.response["filecontent"] return response