From db9623a201a96c4574e03c183bc2b9b38da5d72b Mon Sep 17 00:00:00 2001 From: Mark Liversedge Date: Fri, 6 Aug 2021 07:28:21 +0100 Subject: [PATCH] Alternate Json string protection .. when embedding json in xml element attributes the string decoding mangles results. this is because of the way \ and " are handled at each pass, causing combinations to be lost early when decoding. This is largely a non-issue, except the user chart config is stored in perspectives.xml and worse, when embedded on an overview is encoded as .gcchart (xml) before being encoded in the perspective.xml. To avoid this issue, specifically with user charts and to leave all other json string encoding and decoding unaffected (e.g. ridefile.json) a new protect/unprotect method has been introduced. Utils::jsonprotect2() and Utils::jsonunprotect2() will still decode older style encoding for backwards compatibility, but now will translate \ and " in the encoded string with :sl: and :qu: This means that, for example, the following substitutions will be made: newline (\n) -> :sl:n cr (\r) -> :sl:r \ -> :sl: " -> :qu: If users add text with these characters (e.g. :sl:) then it will be decoded to a slash, but it is highly unlikely they will do this, and if they do, it should be harmless as these text strings aren't part of the the datafilter syntax. Fixes #3996 --- src/Charts/UserChart.cpp | 32 +++++++++++++++--------------- src/Core/Utils.cpp | 42 +++++++++++++++++++++++++++++++++++++++- src/Core/Utils.h | 2 ++ 3 files changed, 59 insertions(+), 17 deletions(-) diff --git a/src/Charts/UserChart.cpp b/src/Charts/UserChart.cpp index ffba07df6..b8594772c 100644 --- a/src/Charts/UserChart.cpp +++ b/src/Charts/UserChart.cpp @@ -324,8 +324,8 @@ UserChart::settings() const out << "{ "; // chartinfo - out << "\"title\": \"" << Utils::jsonprotect(chartinfo.title) << "\",\n"; - out << "\"description\": \"" << Utils::jsonprotect(chartinfo.description) << "\",\n"; + out << "\"title\": \"" << Utils::jsonprotect2(chartinfo.title) << "\",\n"; + out << "\"description\": \"" << Utils::jsonprotect2(chartinfo.description) << "\",\n"; out << "\"type\": " << chartinfo.type << ",\n"; out << "\"animate\": " << (chartinfo.animate ? "true" : "false") << ",\n"; out << "\"legendpos\": " << chartinfo.legendpos << ",\n"; @@ -346,11 +346,11 @@ UserChart::settings() const // out as a json object in the "SERIES" array out << "{ "; - out << "\"name\": \"" << Utils::jsonprotect(series.name) << "\", "; - out << "\"group\": \"" << Utils::jsonprotect(series.group) << "\", "; - out << "\"xname\": \"" << Utils::jsonprotect(series.xname) << "\", "; - out << "\"yname\": \"" << Utils::jsonprotect(series.yname) << "\", "; - out << "\"program\": \"" << Utils::jsonprotect(series.string1) << "\", "; + out << "\"name\": \"" << Utils::jsonprotect2(series.name) << "\", "; + out << "\"group\": \"" << Utils::jsonprotect2(series.group) << "\", "; + out << "\"xname\": \"" << Utils::jsonprotect2(series.xname) << "\", "; + out << "\"yname\": \"" << Utils::jsonprotect2(series.yname) << "\", "; + out << "\"program\": \"" << Utils::jsonprotect2(series.string1) << "\", "; out << "\"line\": " << series.line << ", "; out << "\"symbol\": " << series.symbol << ", "; out << "\"size\": " << series.size << ", "; @@ -377,7 +377,7 @@ UserChart::settings() const // out as a json object in the AXES array out << "{ "; - out << "\"name\": \"" << Utils::jsonprotect(axis.name) << "\", "; + out << "\"name\": \"" << Utils::jsonprotect2(axis.name) << "\", "; out << "\"type\": " << axis.type << ", "; out << "\"orientation\": " << axis.orientation << ", "; out << "\"align\": " << axis.align << ", "; @@ -411,8 +411,8 @@ UserChart::applySettings(QString x) QJsonObject obj = doc.object(); // chartinfo - chartinfo.title = Utils::jsonunprotect(obj["title"].toString()); - chartinfo.description = Utils::jsonunprotect(obj["description"].toString()); + chartinfo.title = Utils::jsonunprotect2(obj["title"].toString()); + chartinfo.description = Utils::jsonunprotect2(obj["description"].toString()); chartinfo.type = obj["type"].toInt(); chartinfo.animate = obj["animate"].toBool(); chartinfo.legendpos = obj["legendpos"].toInt(); @@ -433,11 +433,11 @@ UserChart::applySettings(QString x) QJsonObject series=it.toObject(); GenericSeriesInfo add; - add.name = Utils::jsonunprotect(series["name"].toString()); - add.group = Utils::jsonunprotect(series["group"].toString()); - add.xname = Utils::jsonunprotect(series["xname"].toString()); - add.yname = Utils::jsonunprotect(series["yname"].toString()); - add.string1 = Utils::jsonunprotect(series["program"].toString()); + add.name = Utils::jsonunprotect2(series["name"].toString()); + add.group = Utils::jsonunprotect2(series["group"].toString()); + add.xname = Utils::jsonunprotect2(series["xname"].toString()); + add.yname = Utils::jsonunprotect2(series["yname"].toString()); + add.string1 = Utils::jsonunprotect2(series["program"].toString()); add.line = series["line"].toInt(); add.symbol = series["symbol"].toInt(); add.size = series["size"].toDouble(); @@ -463,7 +463,7 @@ UserChart::applySettings(QString x) QJsonObject axis=it.toObject(); GenericAxisInfo add; - add.name = Utils::jsonunprotect(axis["name"].toString()); + add.name = Utils::jsonunprotect2(axis["name"].toString()); add.type = static_cast(axis["type"].toInt()); add.orientation = static_cast(axis["orientation"].toInt()); add.align = static_cast(axis["align"].toInt()); diff --git a/src/Core/Utils.cpp b/src/Core/Utils.cpp index e5253181f..b7c597f6e 100644 --- a/src/Core/Utils.cpp +++ b/src/Core/Utils.cpp @@ -236,6 +236,47 @@ QString jsonunprotect(const QString &string) return s; } +// json protection with special handling for " and \ +// used when embedding in xml +QString jsonprotect2(const QString &string) +{ + QString s = string; + s.replace("\t", "\\t"); // tab + s.replace("\n", "\\n"); // newline + s.replace("\r", "\\r"); // carriage-return + s.replace("\b", "\\b"); // backspace + s.replace("\f", "\\f"); // formfeed + s.replace("/", "\\/"); // solidus + s.replace("\\", ":sl:"); // backslash + s.replace("\"", ":qu:"); // quote + + // add a trailing space to avoid conflicting with GC special tokens + s += " "; + + return s; +} + +QString jsonunprotect2(const QString &string) +{ + QString s = string; + // legacy- these should never be present + // but included to read older files + s.replace("\\\"", "\""); // quote + s.replace("\\\\", "\\"); // backslash + + s.replace(":qu:", "\""); // quote + s.replace(":sl:", "\\"); // backslash + s.replace("\\t", "\t"); // tab + s.replace("\\n", "\n"); // newline + s.replace("\\r", "\r"); // carriage-return + s.replace("\\b", "\b"); // backspace + s.replace("\\f", "\f"); // formfeed + s.replace("\\/", "/"); // solidus + + // those trailing spaces. + while (s.endsWith(" ")) s = s.mid(0,s.length()-1); + return s; +} QStringList searchPath(QString path, QString binary, bool isexec) { @@ -540,4 +581,3 @@ double myisinf(double x) { return isinf(x); } // math.h double myisnan(double x) { return isnan(x); } // math.h }; - diff --git a/src/Core/Utils.h b/src/Core/Utils.h index c0904b7dc..cb53d1887 100644 --- a/src/Core/Utils.h +++ b/src/Core/Utils.h @@ -42,6 +42,8 @@ namespace Utils QString unescape(const QString &string); // simple string unescaping, used in datafilters QString jsonprotect(const QString &buffer); QString jsonunprotect(const QString &buffer); + QString jsonprotect2(const QString &buffer); // internal use- encodes / and " as :sl: :qu: + QString jsonunprotect2(const QString &buffer); // internal use- encodes / and " as :sl: :qu: QStringList searchPath(QString path, QString binary, bool isexec=true); QString removeDP(QString); double number(QString);