From d0bd1a0a1c91c924dd8f57edca3061738ce6ec86 Mon Sep 17 00:00:00 2001 From: Robert Adam Date: Sun, 16 May 2021 15:01:18 +0200 Subject: [PATCH] FIX(client): Don't escape characters in URL targets If one was to send a link like "http://localhost/?var1=foo&var2=bar&var3=baz" either as-is or as a markdown-style link ("[name]()"), the "&" character in the link would get escaped as "&". For displaying purposes this is fine, as the Mumble client re-interprets these escape sequences before rendering them to the user. The same escaping was done in the surrounding HTML element's href specification. This lead to invalid URLs being used as link targets. Therefore this commit makes sure that the href content of an HTML element will be un-escaped before inserting into the HTML structure (thus preserving the original characters in the href specification). Fixes #4999 --- src/mumble/Markdown.cpp | 22 +++++++++++++++++----- 1 file changed, 17 insertions(+), 5 deletions(-) diff --git a/src/mumble/Markdown.cpp b/src/mumble/Markdown.cpp index 053ed3963..1bf902eeb 100644 --- a/src/mumble/Markdown.cpp +++ b/src/mumble/Markdown.cpp @@ -7,6 +7,7 @@ #include #include +#include namespace Markdown { // Placeholder constant @@ -67,6 +68,18 @@ bool processMarkdownHeader(QString &str, int &offset) { return false; } +/// Reverts the effect of QString::toHtmlEscaped. It is intended to be used for URLs that are part of the +/// actual href specification. In there we don't want to escape HTML characters as they are indeed part of +/// the underlaying link and thus we must not escape e.g. "&" by "&" in there. +/// +/// @param url The html-escaped URL +/// @return The un-escaped version of the given URL +QString unescapeURL(const QString &url) { + QTextDocument doc; + doc.setHtml(url); + return doc.toPlainText(); +} + /// Tries to match and replace a markdown link at exactly the given offset in the string /// /// @param str A reference to the String to work on @@ -91,7 +104,7 @@ bool processMarkdownLink(QString &str, int &offset) { url = QLatin1String("http://") + url; } - QString replacement = QString::fromLatin1("%2").arg(url).arg(match.captured(1)); + QString replacement = QString::fromLatin1("%2").arg(unescapeURL(url)).arg(match.captured(1)); str.replace(match.capturedStart(), match.capturedEnd() - match.capturedStart(), replacement); offset += replacement.size(); @@ -302,16 +315,15 @@ bool processPlainLink(QString &str, int &offset) { s_regex.match(str, offset, QRegularExpression::NormalMatch, QRegularExpression::AnchoredMatchOption); if (match.hasMatch()) { - QString url = match.captured(0); - QString urlText = url; + QString url = match.captured(0); if (url.startsWith(QLatin1String("www"), Qt::CaseInsensitive)) { // Link is missing a protocol specification. // Use http as the default - url = QLatin1String("http://") + url; + url = QStringLiteral("http://") + url; } - QString replacement = QString::fromLatin1("%2").arg(url).arg(urlText); + QString replacement = QString::fromLatin1("%2").arg(unescapeURL(url)).arg(url); str.replace(match.capturedStart(), match.capturedEnd() - match.capturedStart(), replacement); offset += replacement.size();