From 0ad74590d4b7d91189a08d2278aaa6093d0a7e22 Mon Sep 17 00:00:00 2001 From: Nicolas Werner Date: Wed, 20 Jul 2022 13:52:13 +0200 Subject: [PATCH] Fix matrix.to link escaping Properly escapes in replies and with unmatched []. fixes #854 --- src/Config.h | 4 +-- src/RoomsModel.cpp | 6 +++- src/UsersModel.cpp | 5 +++- src/timeline/InputBar.cpp | 52 ++++++++++++++++++++++++++++++---- src/timeline/TimelineModel.cpp | 4 ++- 5 files changed, 59 insertions(+), 12 deletions(-) diff --git a/src/Config.h b/src/Config.h index 72521f20..5a742337 100644 --- a/src/Config.h +++ b/src/Config.h @@ -30,9 +30,7 @@ const QRegularExpression url_regex( // vvvv atomic match url -> fail if there is a " before or after vvv QStringLiteral( R"((?((www\.(?!\.)|[a-z][a-z0-9+.-]*://)[^\s<>'"]+[^!,\.\s<>'"\]\)\:]))(?!["']))")); -// match any markdown matrix.to link. Capture group 1 is the link name, group 2 is the target. -static const QRegularExpression - matrixToMarkdownLink(QStringLiteral(R"(\[(.*?)(?(.*?))")); } diff --git a/src/RoomsModel.cpp b/src/RoomsModel.cpp index 68cfaf1b..8abcb32e 100644 --- a/src/RoomsModel.cpp +++ b/src/RoomsModel.cpp @@ -61,7 +61,11 @@ RoomsModel::data(const QModelIndex &index, int role) const if (UserSettings::instance()->markdown()) { QString percentEncoding = QUrl::toPercentEncoding(roomAliases[index.row()]); return QStringLiteral("[%1](https://matrix.to/#/%2)") - .arg(roomAliases[index.row()], percentEncoding); + .arg(QString(roomAliases[index.row()]) + .replace("[", "\\[") + .replace("]", "\\]") + .toHtmlEscaped(), + percentEncoding); } else { return roomAliases[index.row()]; } diff --git a/src/UsersModel.cpp b/src/UsersModel.cpp index ecd76cf4..5d7dd5b7 100644 --- a/src/UsersModel.cpp +++ b/src/UsersModel.cpp @@ -43,7 +43,10 @@ UsersModel::data(const QModelIndex &index, int role) const case CompletionModel::CompletionRole: if (UserSettings::instance()->markdown()) return QStringLiteral("[%1](https://matrix.to/#/%2)") - .arg(displayNames[index.row()].toHtmlEscaped(), + .arg(QString(displayNames[index.row()]) + .replace("[", "\\[") + .replace("]", "\\]") + .toHtmlEscaped(), QString(QUrl::toPercentEncoding(userids[index.row()]))); else return displayNames[index.row()]; diff --git a/src/timeline/InputBar.cpp b/src/timeline/InputBar.cpp index 66bc8ef9..47efa867 100644 --- a/src/timeline/InputBar.cpp +++ b/src/timeline/InputBar.cpp @@ -343,6 +343,47 @@ InputBar::openFileSelection() startUploadFromPath(fileName); } +QString +replaceMatrixToMarkdownLink(QString input) +{ + bool replaced = false; + do { + replaced = false; + + int endOfName = input.indexOf("](https://matrix.to/#/"); + int startOfName; + int nestingCount = 0; + for (startOfName = endOfName - 1; startOfName > 0; startOfName--) { + // skip escaped chars + if (startOfName > 0 && input[startOfName - 1] == '\\') + continue; + + if (input[startOfName] == '[') { + if (nestingCount <= 0) + break; + else + nestingCount--; + } + if (input[startOfName] == ']') + nestingCount++; + } + if (startOfName < 0 || nestingCount > 0) + break; + + int endOfLink = input.indexOf(')', endOfName); + int newline = input.indexOf('\n', endOfName); + if (endOfLink > endOfName && (newline == -1 || endOfLink < newline)) { + auto name = input.mid(startOfName + 1, endOfName - startOfName - 1); + name.replace("\\[", "["); + name.replace("\\]", "]"); + input.replace(startOfName, endOfLink - startOfName + 1, name); + replaced = true; + } + } while (replaced); + + return input; +} + void InputBar::message(const QString &msg, MarkdownOverride useMarkdown, bool rainbowify) { @@ -354,7 +395,7 @@ InputBar::message(const QString &msg, MarkdownOverride useMarkdown, bool rainbow useMarkdown == MarkdownOverride::ON) { text.formatted_body = utils::markdownToHtml(msg, rainbowify).toStdString(); // Remove markdown links by completer - text.body = msg.trimmed().replace(conf::strings::matrixToMarkdownLink, "\\1").toStdString(); + text.body = replaceMatrixToMarkdownLink(msg.trimmed()).toStdString(); // Don't send formatted_body, when we don't need to if (text.formatted_body.find('<') == std::string::npos) @@ -392,7 +433,8 @@ InputBar::message(const QString &msg, MarkdownOverride useMarkdown, bool rainbow } } - text.body = QStringLiteral("%1\n%2").arg(body, msg).toStdString(); + text.body = + QStringLiteral("%1\n%2").arg(body, QString::fromStdString(text.body)).toStdString(); // NOTE(Nico): rich replies always need a formatted_body! text.format = "org.matrix.custom.html"; @@ -426,8 +468,7 @@ InputBar::emote(const QString &msg, bool rainbowify) emote.formatted_body = html.toStdString(); emote.format = "org.matrix.custom.html"; // Remove markdown links by completer - emote.body = - msg.trimmed().replace(conf::strings::matrixToMarkdownLink, "\\1").toStdString(); + emote.body = replaceMatrixToMarkdownLink(msg.trimmed()).toStdString(); } if (!room->reply().isEmpty()) { @@ -454,8 +495,7 @@ InputBar::notice(const QString &msg, bool rainbowify) notice.formatted_body = html.toStdString(); notice.format = "org.matrix.custom.html"; // Remove markdown links by completer - notice.body = - msg.trimmed().replace(conf::strings::matrixToMarkdownLink, "\\1").toStdString(); + notice.body = replaceMatrixToMarkdownLink(msg.trimmed()).toStdString(); } if (!room->reply().isEmpty()) { diff --git a/src/timeline/TimelineModel.cpp b/src/timeline/TimelineModel.cpp index c60940a7..b2798e26 100644 --- a/src/timeline/TimelineModel.cpp +++ b/src/timeline/TimelineModel.cpp @@ -2625,6 +2625,8 @@ TimelineModel::setEdit(const QString &newEdit) nhlog::ui()->debug("Stored: {}", textBeforeEdit.toStdString()); } + auto quoted = [](QString in) { return in.replace("[", "\\[").replace("]", "\\]"); }; + if (edit_ != newEdit) { auto ev = events.get(newEdit.toStdString(), ""); if (ev && mtx::accessors::sender(*ev) == http::client()->user_id().to_string()) { @@ -2649,7 +2651,7 @@ TimelineModel::setEdit(const QString &newEdit) for (const auto &[user, link] : reverseNameMapping) { // TODO(Nico): html unescape the user name - editText.replace(user, QStringLiteral("[%1](%2)").arg(user, link)); + editText.replace(user, QStringLiteral("[%1](%2)").arg(quoted(user), link)); } }