[Scummvm-git-logs] scummvm master -> bdd7b6baedea7455fdc1754f7c7b49fef201cc2a

bluegr bluegr at gmail.com
Sat Sep 21 21:15:32 CEST 2019


This automated email contains information about 2 new commits which have been
pushed to the 'scummvm' repo located at https://github.com/scummvm/scummvm .

Summary:
caf0968078 NETWORKING: Convert translation results to UTF-8 for local webserver pages
bdd7b6baed COMMON: Fix escaping and parsing of UTF-8 strings in JASON parser


Commit: caf096807895d30983c649e4218033fa3abccbfa
    https://github.com/scummvm/scummvm/commit/caf096807895d30983c649e4218033fa3abccbfa
Author: Thierry Crozat (criezy at scummvm.org)
Date: 2019-09-21T22:15:26+03:00

Commit Message:
NETWORKING: Convert translation results to UTF-8 for local webserver pages

Changed paths:
    backends/networking/sdl_net/getclienthandler.cpp
    backends/networking/sdl_net/handlers/createdirectoryhandler.cpp
    backends/networking/sdl_net/handlers/downloadfilehandler.cpp
    backends/networking/sdl_net/handlers/filesajaxpagehandler.cpp
    backends/networking/sdl_net/handlers/filespagehandler.cpp
    backends/networking/sdl_net/handlers/indexpagehandler.cpp
    backends/networking/sdl_net/handlers/listajaxhandler.cpp
    backends/networking/sdl_net/handlers/uploadfilehandler.cpp
    backends/networking/sdl_net/handlerutils.cpp
    backends/networking/sdl_net/handlerutils.h
    backends/networking/sdl_net/uploadfileclienthandler.cpp


diff --git a/backends/networking/sdl_net/getclienthandler.cpp b/backends/networking/sdl_net/getclienthandler.cpp
index ef085c6..8e940a1 100644
--- a/backends/networking/sdl_net/getclienthandler.cpp
+++ b/backends/networking/sdl_net/getclienthandler.cpp
@@ -107,7 +107,7 @@ const char *GetClientHandler::responseMessage(long responseCode) {
 
 void GetClientHandler::prepareHeaders() {
 	if (!_specialHeaders.contains("Content-Type"))
-		setHeader("Content-Type", "text/html");
+		setHeader("Content-Type", "text/html; charset=UTF-8");
 
 	if (!_specialHeaders.contains("Content-Length") && _stream)
 		setHeader("Content-Length", Common::String::format("%u", _stream->size()));
diff --git a/backends/networking/sdl_net/handlers/createdirectoryhandler.cpp b/backends/networking/sdl_net/handlers/createdirectoryhandler.cpp
index ad90c44..a1d1d9b 100644
--- a/backends/networking/sdl_net/handlers/createdirectoryhandler.cpp
+++ b/backends/networking/sdl_net/handlers/createdirectoryhandler.cpp
@@ -57,35 +57,35 @@ void CreateDirectoryHandler::handle(Client &client) {
 
 	// check that <path> is not an absolute root
 	if (path == "" || path == "/") {
-		handleError(client, _("Can't create directory here!"));
+		handleError(client, HandlerUtils::toUtf8(_("Can't create directory here!")));
 		return;
 	}
 
 	// check that <path> contains no '../'
 	if (HandlerUtils::hasForbiddenCombinations(path)) {
-		handleError(client, _("Invalid path!"));
+		handleError(client, HandlerUtils::toUtf8(_("Invalid path!")));
 		return;
 	}
 
 	// transform virtual path to actual file system one
 	Common::String prefixToRemove = "", prefixToAdd = "";
 	if (!transformPath(path, prefixToRemove, prefixToAdd) || path.empty()) {
-		handleError(client, _("Invalid path!"));
+		handleError(client, HandlerUtils::toUtf8(_("Invalid path!")));
 		return;
 	}
 
 	// check that <path> exists, is directory and isn't forbidden
 	AbstractFSNode *node = g_system->getFilesystemFactory()->makeFileNodePath(path);
 	if (!HandlerUtils::permittedPath(node->getPath())) {
-		handleError(client, _("Invalid path!"));
+		handleError(client, HandlerUtils::toUtf8(_("Invalid path!")));
 		return;
 	}
 	if (!node->exists()) {
-		handleError(client, _("Parent directory doesn't exists!"));
+		handleError(client, HandlerUtils::toUtf8(_("Parent directory doesn't exists!")));
 		return;
 	}
 	if (!node->isDirectory()) {
-		handleError(client, _("Can't create a directory within a file!"));
+		handleError(client, HandlerUtils::toUtf8(_("Can't create a directory within a file!")));
 		return;
 	}
 
@@ -95,20 +95,20 @@ void CreateDirectoryHandler::handle(Client &client) {
 	node = g_system->getFilesystemFactory()->makeFileNodePath(path + name);
 	if (node->exists()) {
 		if (!node->isDirectory()) {
-			handleError(client, _("There is a file with that name in the parent directory!"));
+			handleError(client, HandlerUtils::toUtf8(_("There is a file with that name in the parent directory!")));
 			return;
 		}
 	} else {
 		// create the <directory_name> in <path>
 		if (!node->createDirectory()) {
-			handleError(client, _("Failed to create the directory!"));
+			handleError(client, HandlerUtils::toUtf8(_("Failed to create the directory!")));
 			return;
 		}
 	}
 
 	// if json requested, respond with it
 	if (client.queryParameter("answer_json") == "true") {
-		setJsonResponseHandler(client, "success", _("Directory created successfully!"));
+		setJsonResponseHandler(client, "success", HandlerUtils::toUtf8(_("Directory created successfully!")));
 		return;
 	}
 
@@ -117,9 +117,9 @@ void CreateDirectoryHandler::handle(Client &client) {
 		client,
 		Common::String::format(
 			"%s<br/><a href=\"files?path=%s\">%s</a>",
-			_("Directory created successfully!"),
+			HandlerUtils::toUtf8(_("Directory created successfully!")).c_str(),
 			client.queryParameter("path").c_str(),
-			_("Back to parent directory")
+			HandlerUtils::toUtf8(_("Back to parent directory")).c_str()
 		),
 		(client.queryParameter("ajax") == "true" ? "/filesAJAX?path=" : "/files?path=") +
 		LocalWebserver::urlEncodeQueryParameterValue(client.queryParameter("path"))
diff --git a/backends/networking/sdl_net/handlers/downloadfilehandler.cpp b/backends/networking/sdl_net/handlers/downloadfilehandler.cpp
index 281be2d..8f434ed 100644
--- a/backends/networking/sdl_net/handlers/downloadfilehandler.cpp
+++ b/backends/networking/sdl_net/handlers/downloadfilehandler.cpp
@@ -40,40 +40,40 @@ void DownloadFileHandler::handle(Client &client) {
 
 	// check that <path> is not an absolute root
 	if (path == "" || path == "/") {
-		HandlerUtils::setFilesManagerErrorMessageHandler(client, _("Invalid path!"));
+		HandlerUtils::setFilesManagerErrorMessageHandler(client, HandlerUtils::toUtf8(_("Invalid path!")));
 		return;
 	}
 
 	// check that <path> contains no '../'
 	if (HandlerUtils::hasForbiddenCombinations(path)) {
-		HandlerUtils::setFilesManagerErrorMessageHandler(client, _("Invalid path!"));
+		HandlerUtils::setFilesManagerErrorMessageHandler(client, HandlerUtils::toUtf8(_("Invalid path!")));
 		return;
 	}
 
 	// transform virtual path to actual file system one
 	Common::String prefixToRemove = "", prefixToAdd = "";
 	if (!transformPath(path, prefixToRemove, prefixToAdd, false) || path.empty()) {
-		HandlerUtils::setFilesManagerErrorMessageHandler(client, _("Invalid path!"));
+		HandlerUtils::setFilesManagerErrorMessageHandler(client, HandlerUtils::toUtf8(_("Invalid path!")));
 		return;
 	}
 
 	// check that <path> exists, is directory and isn't forbidden
 	AbstractFSNode *node = g_system->getFilesystemFactory()->makeFileNodePath(path);
 	if (!HandlerUtils::permittedPath(node->getPath())) {
-		HandlerUtils::setFilesManagerErrorMessageHandler(client, _("Invalid path!"));
+		HandlerUtils::setFilesManagerErrorMessageHandler(client, HandlerUtils::toUtf8(_("Invalid path!")));
 		return;
 	}
 	if (!node->exists()) {
-		HandlerUtils::setFilesManagerErrorMessageHandler(client, _("The file doesn't exist!"));
+		HandlerUtils::setFilesManagerErrorMessageHandler(client, HandlerUtils::toUtf8(_("The file doesn't exist!")));
 		return;
 	}
 	if (node->isDirectory()) {
-		HandlerUtils::setFilesManagerErrorMessageHandler(client, _("Can't download a directory!"));
+		HandlerUtils::setFilesManagerErrorMessageHandler(client, HandlerUtils::toUtf8(_("Can't download a directory!")));
 		return;
 	}
 	Common::SeekableReadStream *stream = node->createReadStream();
 	if (stream == nullptr) {
-		HandlerUtils::setFilesManagerErrorMessageHandler(client, _("Failed to read the file!"));
+		HandlerUtils::setFilesManagerErrorMessageHandler(client, HandlerUtils::toUtf8(_("Failed to read the file!")));
 		return;
 	}
 
diff --git a/backends/networking/sdl_net/handlers/filesajaxpagehandler.cpp b/backends/networking/sdl_net/handlers/filesajaxpagehandler.cpp
index 94cc741..eda5d13 100644
--- a/backends/networking/sdl_net/handlers/filesajaxpagehandler.cpp
+++ b/backends/networking/sdl_net/handlers/filesajaxpagehandler.cpp
@@ -56,7 +56,7 @@ void FilesAjaxPageHandler::handle(Client &client) {
 	// load stylish response page from the archive
 	Common::SeekableReadStream *const stream = HandlerUtils::getArchiveFile(FILES_PAGE_NAME);
 	if (stream == nullptr) {
-		HandlerUtils::setFilesManagerErrorMessageHandler(client, _("The page is not available without the resources. Make sure file wwwroot.zip from ScummVM distribution is available in 'themepath'."));
+		HandlerUtils::setFilesManagerErrorMessageHandler(client, HandlerUtils::toUtf8(_("The page is not available without the resources. Make sure file wwwroot.zip from ScummVM distribution is available in 'themepath'.")));
 		return;
 	}
 
@@ -64,16 +64,16 @@ void FilesAjaxPageHandler::handle(Client &client) {
 	Common::String path = client.queryParameter("path");
 
 	//these occur twice:
-	replace(response, "{create_directory_button}", _("Create directory"));
-	replace(response, "{create_directory_button}", _("Create directory"));
-	replace(response, "{upload_files_button}", _("Upload files")); //tab
-	replace(response, "{upload_file_button}", _("Upload files")); //button in the tab
-	replace(response, "{create_directory_desc}", _("Type new directory name:"));
-	replace(response, "{upload_file_desc}", _("Select a file to upload:"));
-	replace(response, "{or_upload_directory_desc}", _("Or select a directory (works in Chrome only):"));
-	replace(response, "{index_of}", _("Index of "));
-	replace(response, "{loading}", _("Loading..."));
-	replace(response, "{error}", _("Error occurred"));
+	replace(response, "{create_directory_button}", HandlerUtils::toUtf8(_("Create directory")));
+	replace(response, "{create_directory_button}", HandlerUtils::toUtf8(_("Create directory")));
+	replace(response, "{upload_files_button}", HandlerUtils::toUtf8(_("Upload files"))); //tab
+	replace(response, "{upload_file_button}", HandlerUtils::toUtf8(_("Upload files"))); //button in the tab
+	replace(response, "{create_directory_desc}", HandlerUtils::toUtf8(_("Type new directory name:")));
+	replace(response, "{upload_file_desc}", HandlerUtils::toUtf8(_("Select a file to upload:")));
+	replace(response, "{or_upload_directory_desc}", HandlerUtils::toUtf8(_("Or select a directory (works in Chrome only):")));
+	replace(response, "{index_of}", HandlerUtils::toUtf8(_("Index of ")));
+	replace(response, "{loading}", HandlerUtils::toUtf8(("Loading...")));
+	replace(response, "{error}", HandlerUtils::toUtf8(_("Error occurred")));
 	replace(response, "{start_path}", encodeDoubleQuotesAndSlashes(path));
 	LocalWebserver::setClientGetHandler(client, response);
 }
diff --git a/backends/networking/sdl_net/handlers/filespagehandler.cpp b/backends/networking/sdl_net/handlers/filespagehandler.cpp
index 22afa83..72e2300 100644
--- a/backends/networking/sdl_net/handlers/filespagehandler.cpp
+++ b/backends/networking/sdl_net/handlers/filespagehandler.cpp
@@ -78,8 +78,8 @@ Common::String getDisplayPath(Common::String s) {
 bool FilesPageHandler::listDirectory(Common::String path, Common::String &content, const Common::String &itemTemplate) {
 	if (path == "" || path == "/") {
 		if (ConfMan.hasKey("rootpath", "cloud"))
-			addItem(content, itemTemplate, IT_DIRECTORY, "/root/", _("File system root"));
-		addItem(content, itemTemplate, IT_DIRECTORY, "/saves/", _("Saved games"));
+			addItem(content, itemTemplate, IT_DIRECTORY, "/root/", HandlerUtils::toUtf8(_("File system root")));
+		addItem(content, itemTemplate, IT_DIRECTORY, "/saves/", HandlerUtils::toUtf8(_("Saved games")));
 		return true;
 	}
 
@@ -116,7 +116,7 @@ bool FilesPageHandler::listDirectory(Common::String path, Common::String &conten
 			filePath = "/";
 		else
 			filePath = parentPath(prefixToAdd + filePath);
-		addItem(content, itemTemplate, IT_PARENT_DIRECTORY, filePath, _("Parent directory"));
+		addItem(content, itemTemplate, IT_PARENT_DIRECTORY, filePath, HandlerUtils::toUtf8(_("Parent directory")));
 	}
 
 	// fill the content
@@ -182,7 +182,7 @@ void FilesPageHandler::addItem(Common::String &content, const Common::String &it
 void FilesPageHandler::handle(Client &client) {
 	Common::String response =
 		"<html>" \
-		"<head><title>ScummVM</title></head>" \
+		"<head><title>ScummVM</title><meta charset=\"utf-8\"/></head>" \
 		"<body>" \
 		"<p>{create_directory_desc}</p>" \
 		"<form action=\"create\">" \
@@ -215,21 +215,21 @@ void FilesPageHandler::handle(Client &client) {
 
 	// show an error message if failed to list directory
 	if (!listDirectory(path, content, itemTemplate)) {
-		HandlerUtils::setFilesManagerErrorMessageHandler(client, _("ScummVM couldn't list the directory you specified."));
+		HandlerUtils::setFilesManagerErrorMessageHandler(client, HandlerUtils::toUtf8(_("ScummVM couldn't list the directory you specified.")));
 		return;
 	}
 
 	//these occur twice:
-	replace(response, "{create_directory_button}", _("Create directory"));
-	replace(response, "{create_directory_button}", _("Create directory"));
+	replace(response, "{create_directory_button}", HandlerUtils::toUtf8(_("Create directory")));
+	replace(response, "{create_directory_button}", HandlerUtils::toUtf8(_("Create directory")));
 	replace(response, "{path}", encodeDoubleQuotes(client.queryParameter("path")));
 	replace(response, "{path}", encodeDoubleQuotes(client.queryParameter("path")));
-	replace(response, "{upload_files_button}", _("Upload files")); //tab
-	replace(response, "{upload_file_button}", _("Upload files")); //button in the tab
-	replace(response, "{create_directory_desc}", _("Type new directory name:"));
-	replace(response, "{upload_file_desc}", _("Select a file to upload:"));
-	replace(response, "{or_upload_directory_desc}", _("Or select a directory (works in Chrome only):"));
-	replace(response, "{index_of_directory}", Common::String::format(_("Index of %s"), encodeHtmlEntities(getDisplayPath(client.queryParameter("path"))).c_str()));
+	replace(response, "{upload_files_button}", HandlerUtils::toUtf8(_("Upload files"))); //tab
+	replace(response, "{upload_file_button}", HandlerUtils::toUtf8(_("Upload files"))); //button in the tab
+	replace(response, "{create_directory_desc}", HandlerUtils::toUtf8(_("Type new directory name:")));
+	replace(response, "{upload_file_desc}", HandlerUtils::toUtf8(_("Select a file to upload:")));
+	replace(response, "{or_upload_directory_desc}", HandlerUtils::toUtf8(_("Or select a directory (works in Chrome only):")));
+	replace(response, "{index_of_directory}", Common::String::format("%s %s", HandlerUtils::toUtf8(_("Index of")).c_str(), encodeHtmlEntities(getDisplayPath(client.queryParameter("path"))).c_str()));
 	replace(response, "{content}", content);
 	LocalWebserver::setClientGetHandler(client, response);
 }
diff --git a/backends/networking/sdl_net/handlers/indexpagehandler.cpp b/backends/networking/sdl_net/handlers/indexpagehandler.cpp
index 876bdde..f872810 100644
--- a/backends/networking/sdl_net/handlers/indexpagehandler.cpp
+++ b/backends/networking/sdl_net/handlers/indexpagehandler.cpp
@@ -39,8 +39,8 @@ void IndexPageHandler::handle(Client &client) {
 		client,
 		Common::String::format(
 			"%s<br/><a href=\"files\">%s</a>",
-			_("This is a local webserver index page."),
-			_("Open Files manager")
+			HandlerUtils::toUtf8(_("This is a local webserver index page.")).c_str(),
+			HandlerUtils::toUtf8(_("Open Files manager")).c_str()
 		),
 		"/filesAJAX"
 	);
diff --git a/backends/networking/sdl_net/handlers/listajaxhandler.cpp b/backends/networking/sdl_net/handlers/listajaxhandler.cpp
index 7c28015..e364712 100644
--- a/backends/networking/sdl_net/handlers/listajaxhandler.cpp
+++ b/backends/networking/sdl_net/handlers/listajaxhandler.cpp
@@ -42,8 +42,8 @@ Common::JSONObject ListAjaxHandler::listDirectory(Common::String path) {
 
 	if (path == "" || path == "/") {
 		if (ConfMan.hasKey("rootpath", "cloud"))
-			addItem(itemsList, IT_DIRECTORY, "/root/", _("File system root"));
-		addItem(itemsList, IT_DIRECTORY, "/saves/", _("Saved games"));
+			addItem(itemsList, IT_DIRECTORY, "/root/", HandlerUtils::toUtf8(_("File system root")));
+		addItem(itemsList, IT_DIRECTORY, "/saves/", HandlerUtils::toUtf8(_("Saved games")));
 		successResult.setVal("items", new Common::JSONValue(itemsList));
 		return successResult;
 	}
@@ -81,7 +81,7 @@ Common::JSONObject ListAjaxHandler::listDirectory(Common::String path) {
 			filePath = "/";
 		else
 			filePath = parentPath(prefixToAdd + filePath);
-		addItem(itemsList, IT_PARENT_DIRECTORY, filePath, _("Parent directory"));
+		addItem(itemsList, IT_PARENT_DIRECTORY, filePath, HandlerUtils::toUtf8(_("Parent directory")));
 	}
 
 	// fill the content
diff --git a/backends/networking/sdl_net/handlers/uploadfilehandler.cpp b/backends/networking/sdl_net/handlers/uploadfilehandler.cpp
index 6b8be15..c3454a9 100644
--- a/backends/networking/sdl_net/handlers/uploadfilehandler.cpp
+++ b/backends/networking/sdl_net/handlers/uploadfilehandler.cpp
@@ -40,35 +40,35 @@ void UploadFileHandler::handle(Client &client) {
 
 	// check that <path> is not an absolute root
 	if (path == "" || path == "/" || path == "\\") {
-		HandlerUtils::setFilesManagerErrorMessageHandler(client, _("Invalid path!"));
+		HandlerUtils::setFilesManagerErrorMessageHandler(client, HandlerUtils::toUtf8(_("Invalid path!")));
 		return;
 	}
 
 	// check that <path> contains no '../'
 	if (HandlerUtils::hasForbiddenCombinations(path)) {
-		HandlerUtils::setFilesManagerErrorMessageHandler(client, _("Invalid path!"));
+		HandlerUtils::setFilesManagerErrorMessageHandler(client, HandlerUtils::toUtf8(_("Invalid path!")));
 		return;
 	}
 
 	// transform virtual path to actual file system one
 	Common::String prefixToRemove = "", prefixToAdd = "";
 	if (!transformPath(path, prefixToRemove, prefixToAdd, false) || path.empty()) {
-		HandlerUtils::setFilesManagerErrorMessageHandler(client, _("Invalid path!"));
+		HandlerUtils::setFilesManagerErrorMessageHandler(client, HandlerUtils::toUtf8(_("Invalid path!")));
 		return;
 	}
 
 	// check that <path> exists, is directory and isn't forbidden
 	AbstractFSNode *node = g_system->getFilesystemFactory()->makeFileNodePath(path);
 	if (!HandlerUtils::permittedPath(node->getPath())) {
-		HandlerUtils::setFilesManagerErrorMessageHandler(client, _("Invalid path!"));
+		HandlerUtils::setFilesManagerErrorMessageHandler(client, HandlerUtils::toUtf8(_("Invalid path!")));
 		return;
 	}
 	if (!node->exists()) {
-		HandlerUtils::setFilesManagerErrorMessageHandler(client, _("The parent directory doesn't exist!"));
+		HandlerUtils::setFilesManagerErrorMessageHandler(client, HandlerUtils::toUtf8(_("The parent directory doesn't exist!")));
 		return;
 	}
 	if (!node->isDirectory()) {
-		HandlerUtils::setFilesManagerErrorMessageHandler(client, _("Can't upload into a file!"));
+		HandlerUtils::setFilesManagerErrorMessageHandler(client, HandlerUtils::toUtf8(_("Can't upload into a file!")));
 		return;
 	}
 
diff --git a/backends/networking/sdl_net/handlerutils.cpp b/backends/networking/sdl_net/handlerutils.cpp
index 73ddf9e..36f615f 100644
--- a/backends/networking/sdl_net/handlerutils.cpp
+++ b/backends/networking/sdl_net/handlerutils.cpp
@@ -28,6 +28,7 @@
 #include "common/file.h"
 #include "common/translation.h"
 #include "common/unzip.h"
+#include "common/encoding.h"
 
 namespace Networking {
 
@@ -171,8 +172,23 @@ bool HandlerUtils::permittedPath(const Common::String path) {
 	return hasPermittedPrefix(path) && !isBlacklisted(path);
 }
 
+Common::String HandlerUtils::toUtf8(const char *text) {
+#ifdef USE_TRANSLATION
+	Common::String guiEncoding = TransMan.getCurrentCharset();
+	if (guiEncoding != "ASCII") {
+		char *utf8Text = Common::Encoding::convert("utf-8", guiEncoding, text, strlen(text));
+		if (utf8Text != nullptr) {
+			Common::String str(utf8Text);
+			delete [] utf8Text;
+			return str;
+		}
+	}
+#endif
+	return Common::String(text);
+}
+
 void HandlerUtils::setMessageHandler(Client &client, Common::String message, Common::String redirectTo) {
-	Common::String response = "<html><head><title>ScummVM</title></head><body>{message}</body></html>";
+	Common::String response = "<html><head><title>ScummVM</title><meta charset=\"utf-8\"/></head><body>{message}</body></html>";
 
 	// load stylish response page from the archive
 	Common::SeekableReadStream *const stream = getArchiveFile(INDEX_PAGE_NAME);
@@ -194,7 +210,7 @@ void HandlerUtils::setFilesManagerErrorMessageHandler(Client &client, Common::St
 			message.c_str(),
 			client.queryParameter("ajax") == "true" ? "AJAX" : "",
 			"%2F", //that's encoded "/"
-			_("Back to the files manager")
+			toUtf8(_("Back to the files manager")).c_str()
 		),
 		redirectTo
 	);
diff --git a/backends/networking/sdl_net/handlerutils.h b/backends/networking/sdl_net/handlerutils.h
index 0958294..10ef2dc 100644
--- a/backends/networking/sdl_net/handlerutils.h
+++ b/backends/networking/sdl_net/handlerutils.h
@@ -41,6 +41,8 @@ public:
 	static bool hasPermittedPrefix(const Common::String &path);
 	static bool permittedPath(const Common::String path);
 
+	static Common::String toUtf8(const char*);
+
 	static void setMessageHandler(Client &client, Common::String message, Common::String redirectTo = "");
 	static void setFilesManagerErrorMessageHandler(Client &client, Common::String message, Common::String redirectTo = "");
 };
diff --git a/backends/networking/sdl_net/uploadfileclienthandler.cpp b/backends/networking/sdl_net/uploadfileclienthandler.cpp
index 8486b6e..fb00b6b 100644
--- a/backends/networking/sdl_net/uploadfileclienthandler.cpp
+++ b/backends/networking/sdl_net/uploadfileclienthandler.cpp
@@ -66,7 +66,7 @@ void UploadFileClientHandler::handle(Client *client) {
 
 			// fail on suspicious headers
 			if (_headersStream->size() > Reader::SUSPICIOUS_HEADERS_SIZE) {
-				setErrorMessageHandler(*client, _("Invalid request: headers are too long!"));
+				setErrorMessageHandler(*client, HandlerUtils::toUtf8(_("Invalid request: headers are too long!")));
 			}
 			break;
 
@@ -108,7 +108,7 @@ void UploadFileClientHandler::handleBlockHeaders(Client *client) {
 
 	// fail on suspicious headers
 	if (_headersStream->size() > Reader::SUSPICIOUS_HEADERS_SIZE) {
-		setErrorMessageHandler(*client, _("Invalid request: headers are too long!"));
+		setErrorMessageHandler(*client, HandlerUtils::toUtf8(_("Invalid request: headers are too long!")));
 	}
 
 	// search for "upload_file" field
@@ -134,11 +134,11 @@ void UploadFileClientHandler::handleBlockHeaders(Client *client) {
 		path += '/';
 	AbstractFSNode *originalNode = g_system->getFilesystemFactory()->makeFileNodePath(path + filename);
 	if (!HandlerUtils::permittedPath(originalNode->getPath())) {
-		setErrorMessageHandler(*client, _("Invalid path!"));
+		setErrorMessageHandler(*client, HandlerUtils::toUtf8(_("Invalid path!")));
 		return;
 	}
 	if (originalNode->exists()) {
-		setErrorMessageHandler(*client, _("There is a file with that name in the parent directory!"));
+		setErrorMessageHandler(*client, HandlerUtils::toUtf8(_("There is a file with that name in the parent directory!")));
 		return;
 	}
 
@@ -152,7 +152,7 @@ void UploadFileClientHandler::handleBlockHeaders(Client *client) {
 	Common::DumpFile *f = new Common::DumpFile();
 	if (!f->open(originalNode->getPath(), true)) {
 		delete f;
-		setErrorMessageHandler(*client, _("Failed to upload the file!"));
+		setErrorMessageHandler(*client, HandlerUtils::toUtf8(_("Failed to upload the file!")));
 		return;
 	}
 
@@ -181,7 +181,7 @@ void UploadFileClientHandler::handleBlockContent(Client *client) {
 	if (client->noMoreContent()) {
 		// if no file field was found - failure
 		if (_uploadedFiles == 0) {
-			setErrorMessageHandler(*client, _("No file was passed!"));
+			setErrorMessageHandler(*client, HandlerUtils::toUtf8(_("No file was passed!")));
 		} else {
 			setSuccessHandler(*client);
 		}
@@ -199,9 +199,9 @@ void UploadFileClientHandler::setSuccessHandler(Client &client) {
 		client,
 		Common::String::format(
 			"%s<br/><a href=\"files?path=%s\">%s</a>",
-			_("Uploaded successfully!"),
+			HandlerUtils::toUtf8(_("Uploaded successfully!")).c_str(),
 			client.queryParameter("path").c_str(),
-			_("Back to parent directory")
+			HandlerUtils::toUtf8(_("Back to parent directory")).c_str()
 			),
 		(client.queryParameter("ajax") == "true" ? "/filesAJAX?path=" : "/files?path=") +
 		LocalWebserver::urlEncodeQueryParameterValue(client.queryParameter("path"))


Commit: bdd7b6baedea7455fdc1754f7c7b49fef201cc2a
    https://github.com/scummvm/scummvm/commit/bdd7b6baedea7455fdc1754f7c7b49fef201cc2a
Author: Thierry Crozat (criezy at scummvm.org)
Date: 2019-09-21T22:15:26+03:00

Commit Message:
COMMON: Fix escaping and parsing of UTF-8 strings in JASON parser

Changed paths:
    common/json.cpp
    common/json.h


diff --git a/common/json.cpp b/common/json.cpp
index 89f780b..4c63768 100644
--- a/common/json.cpp
+++ b/common/json.cpp
@@ -142,6 +142,7 @@ bool JSON::extractString(const char **data, String &str) {
 	while (**data != 0) {
 		// Save the char so we can change it if need be
 		char next_char = **data;
+		uint32 next_uchar = 0;
 
 		// Escaping something?
 		if (next_char == '\\') {
@@ -167,31 +168,24 @@ bool JSON::extractString(const char **data, String &str) {
 			case 't': next_char = '\t';
 				break;
 			case 'u': {
-				// We need 5 chars (4 hex + the 'u') or its not valid
-				if (!simplejson_wcsnlen(*data, 5))
-					return false;
-
-				// Deal with the chars
 				next_char = 0;
-				for (int i = 0; i < 4; i++) {
-					// Do it first to move off the 'u' and leave us on the
-					// final hex digit as we move on by one later on
+				next_uchar = parseUnicode(data);
+				// If the codepoint is a high surrogate, we should have a low surrogate now
+				if (next_uchar >= 0xD800 && next_uchar <= 0xDBFF) {
 					(*data)++;
-
-					next_char <<= 4;
-
-					// Parse the hex digit
-					if (**data >= '0' && **data <= '9')
-						next_char |= (**data - '0');
-					else if (**data >= 'A' && **data <= 'F')
-						next_char |= (10 + (**data - 'A'));
-					else if (**data >= 'a' && **data <= 'f')
-						next_char |= (10 + (**data - 'a'));
-					else {
-						// Invalid hex digit = invalid JSON
+					if (**data != '\\')
 						return false;
-					}
-				}
+					(*data)++;
+					uint32 low_surrogate = parseUnicode(data);
+					if (low_surrogate < 0xDC00 || low_surrogate > 0xDFFF)
+						return false;
+					//next_uchar = 0x10000 + (next_uchar - 0xD800) * 0x400 + (low_surrogate - 0xDC00);
+					next_uchar = (next_uchar << 10) + low_surrogate - 0x35FDC00u;
+				} else if (next_uchar >= 0xDC00 && next_uchar <= 0xDFFF)
+					return false; // low surrogate, which should only follow a high surrogate
+				// Check this is a valid code point
+				if (next_uchar > 0x10FFFF)
+					return false;
 				break;
 			}
 
@@ -215,7 +209,29 @@ bool JSON::extractString(const char **data, String &str) {
 		}
 
 		// Add the next char
-		str += next_char;
+		if (next_char != 0)
+			str += next_char;
+		else {
+			if (next_uchar < 0x80)
+				// 1-byte character (ASCII)
+				str += (char)next_uchar;
+			else if (next_uchar <= 0x7FF) {
+				// 2-byte characters: 110xxxxx 10xxxxxx
+				str += (char)(0xC0 | (next_uchar >> 6));
+				str += (char)(0x80 | (next_uchar & 0x3F));
+			} else if (next_uchar <= 0xFFFF) {
+				// 3-byte characters: 1110xxxx 10xxxxxx 10xxxxxx
+				str += (char)(0xE0 | (next_uchar >> 12));
+				str += (char)(0x80 | ((next_uchar >> 6) & 0x3F));
+				str += (char)(0x80 | (next_uchar & 0x3F));
+			} else {
+				// 4-byte characters: 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx
+				str += (char)(0xF0 | (next_uchar >> 18));
+				str += (char)(0x80 | ((next_uchar >> 12) & 0x3F));
+				str += (char)(0x80 | ((next_uchar >> 6) & 0x3F));
+				str += (char)(0x80 | (next_uchar & 0x3F));
+			}
+		}
 
 		// Move on
 		(*data)++;
@@ -226,6 +242,48 @@ bool JSON::extractString(const char **data, String &str) {
 }
 
 /**
+* Parses some text as though it is a unicode hexadecimal sequence.
+* It assumes that the data is currently pointing on the 'u' part of '\uXXXX`.
+*
+* @access protected
+*
+* @param char** data Pointer to a char* that contains the JSON text
+* @param String& str Reference to a String to receive the extracted string
+*
+* @return uint32 Returns the unicode code point value or 0xFFFFFFFF in case of error.
+*/
+uint32 JSON::parseUnicode(const char **data) {
+	if (**data != 'u')
+		return 0xFFFFFFFF;
+	// We need 5 chars (4 hex + the 'u') or its not valid
+	if (!simplejson_wcsnlen(*data, 5))
+		return 0xFFFFFFFF;
+
+	// Deal with the chars
+	uint32 codepoint = 0;
+	for (int i = 0; i < 4; i++) {
+		// Do it first to move off the 'u' and leave us on the
+		// final hex digit as we move on by one later on
+		(*data)++;
+
+		codepoint <<= 4;
+
+		// Parse the hex digit
+		if (**data >= '0' && **data <= '9')
+			codepoint |= (**data - '0');
+		else if (**data >= 'A' && **data <= 'F')
+			codepoint |= (10 + (**data - 'A'));
+		else if (**data >= 'a' && **data <= 'f')
+			codepoint |= (10 + (**data - 'a'));
+		else {
+			// Invalid hex digit
+			return 0xFFFFFFFF;
+		}
+	}
+	return codepoint;
+}
+
+/**
 * Parses some text as though it is an integer
 *
 * @access protected
@@ -1039,33 +1097,30 @@ String JSONValue::stringifyString(const String &str) {
 
 	String::const_iterator iter = str.begin();
 	while (iter != str.end()) {
-		char chr = *iter;
+		uint32 uchr = decodeUtf8Char(iter, str.end());
+		if (uchr == 0xFFFFFFFF)
+			break; // error - truncate the result
 
-		if (chr == '"' || chr == '\\' || chr == '/') {
+		if (uchr == '"' || uchr == '\\' || uchr == '/') {
 			str_out += '\\';
-			str_out += chr;
-		} else if (chr == '\b') {
+			str_out += (char)uchr;
+		} else if (uchr == '\b') {
 			str_out += "\\b";
-		} else if (chr == '\f') {
+		} else if (uchr == '\f') {
 			str_out += "\\f";
-		} else if (chr == '\n') {
+		} else if (uchr == '\n') {
 			str_out += "\\n";
-		} else if (chr == '\r') {
+		} else if (uchr == '\r') {
 			str_out += "\\r";
-		} else if (chr == '\t') {
+		} else if (uchr == '\t') {
 			str_out += "\\t";
-		} else if (chr < ' ' || chr > 126) {
-			str_out += "\\u";
-			for (int i = 0; i < 4; i++) {
-				int value = (chr >> 12) & 0xf;
-				if (value >= 0 && value <= 9)
-					str_out += (char)('0' + value);
-				else if (value >= 10 && value <= 15)
-					str_out += (char)('A' + (value - 10));
-				chr <<= 4;
-			}
+		} else if (uchr >= ' ' && uchr <= 126 ) {
+			str_out += (char)uchr;
 		} else {
-			str_out += chr;
+			if (uchr <= 0xFFFF)
+				str_out += String::format("\\u%04x", uchr);
+			else
+				str_out += String::format("\\u%04x\\u%04x", 0xD7C0 + (uchr >> 10), 0xDC00 + (uchr & 0x3FF));
 		}
 
 		iter++;
@@ -1076,6 +1131,86 @@ String JSONValue::stringifyString(const String &str) {
 }
 
 /**
+* Decode the next utf-8 character in the String pointed to by begin.
+*
+* @param String::const_iterator &iter Iterator pointing to the start of the character to decode.
+*
+* @param const String::const_iterator &end Iterator pointing past the end of the string being decoded.
+*
+* @return The codepoint value for the next utf-8 character starting at the current iterator position,
+* or 0xFFFFFFFF in case of error.
+*/
+uint32 JSONValue::decodeUtf8Char(String::const_iterator &iter, const String::const_iterator &end) {
+	uint8 state = 0;
+	uint32 codepoint = 0;
+	int nbRead = 0;
+	do {
+		uint8 byte = uint8(*iter);
+		state = decodeUtf8Byte(state, codepoint, byte);
+		++nbRead;
+		if (state == 0)
+			return codepoint;
+	} while (state != 1 && ++iter != end);
+	if (state == 1) {
+		// We failed to read this as a UTF-8 character. The string might be encoded differently, which
+		// would be invalid (since the json standard indicate the string has to be in utf-8) but rather
+		// that return 0FFFFFFFF and truncate, try to recover from it by rewinding and returning the
+		// raw byte.
+		while (--nbRead > 0) { --iter; }
+		uint8 byte = uint8(*iter);
+		warning("Invalid UTF-8 character 0x%x in JSON string.", byte);
+		return byte;
+	}
+	return 0xFFFFFFFF;
+}
+
+/**
+* Decode one byte from a UTF-8 string.
+*
+* The function must initially (for the first byte) be called with a state of 0, and then
+* with the state from the previous byte until it returns 0 (success) or 1 (failure).
+*
+* Copyright (c) 2008-2009 Bjoern Hoehrmann <bjoern at hoehrmann.de>
+* See http://bjoern.hoehrmann.de/utf-8/decoder/dfa/ for details.
+*
+* @access private
+*
+* @param uint8 state The state from the previous byte, or 0 when decoding the first byte.
+*
+* @param uint32 &codepoint The codepoint value. Unless the returned state is 0, the codepoint is
+* a partial reasult and the function needs to be called again with the next byte.
+*
+* @param uint8 byte The byte to decode.
+*
+* @return The state of the utf8 decoder: 0 if a character has been decoded, 1 in case of
+* error, and any other value for decoding in progress.
+*/
+uint8 JSONValue::decodeUtf8Byte(uint8 state, uint32 &codepoint, uint8 byte) {
+	static const uint8 utf8d[] = {
+		0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 00..1F
+		0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 20..3F
+		0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 40..5F
+		0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 60..7F
+		1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, // 80..9F
+		7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, // A0..BF
+		8, 8, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, // C0..DF
+		0xA, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x4, 0x3, 0x3, // E0..EF
+		0xB, 0x6, 0x6, 0x6, 0x5, 0x8, 0x8, 0x8, 0x8, 0x8, 0x8, 0x8, 0x8, 0x8, 0x8, 0x8, // F0..FF
+		0x0, 0x1, 0x2, 0x3, 0x5, 0x8, 0x7, 0x1, 0x1, 0x1, 0x4, 0x6, 0x1, 0x1, 0x1, 0x1, // s0..s0
+		1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 0, 1, 0, 1, 1, 1, 1, 1, 1, // s1..s2
+		1, 2, 1, 1, 1, 1, 1, 2, 1, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 1, 1, 1, 1, 1, 1, 1, 1, // s3..s4
+		1, 2, 1, 1, 1, 1, 1, 1, 1, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 3, 1, 3, 1, 1, 1, 1, 1, 1, // s5..s6
+		1, 3, 1, 1, 1, 1, 1, 3, 1, 3, 1, 1, 1, 1, 1, 1, 1, 3, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 // s7..s8
+	};
+
+	const uint8 type = utf8d[byte];
+	codepoint = state != 0 ?
+		(codepoint << 6) | (byte & 0x3f) :
+		(0xFF >> type) & byte;
+	return utf8d[256 + state * 16 + type];
+}
+
+/**
 * Creates the indentation string for the depth given
 *
 * @access private
diff --git a/common/json.h b/common/json.h
index a911196..c1e630c 100644
--- a/common/json.h
+++ b/common/json.h
@@ -130,6 +130,8 @@ protected:
 
 private:
 	static String stringifyString(const String &str);
+	static uint32 decodeUtf8Char(String::const_iterator &begin, const String::const_iterator &end);
+	static uint8 decodeUtf8Byte(uint8 state, uint32 &codepoint, uint8 byte);
 	String stringifyImpl(size_t const indentDepth) const;
 	static String indent(size_t depth);
 
@@ -155,6 +157,7 @@ public:
 protected:
 	static bool skipWhitespace(const char **data);
 	static bool extractString(const char **data, String &str);
+	static uint32 parseUnicode(const char **data);
 	static double parseInt(const char **data);
 	static double parseDecimal(const char **data);
 private:





More information about the Scummvm-git-logs mailing list