diff --git a/src/UsersModel.cpp b/src/UsersModel.cpp
index a017aa84..28f6c6fa 100644
--- a/src/UsersModel.cpp
+++ b/src/UsersModel.cpp
@@ -11,6 +11,7 @@
#include "CompletionModelRoles.h"
#include "Logging.h"
#include "UserSettingsPage.h"
+#include "Utils.h"
UsersModel::UsersModel(const std::string &roomId, QObject *parent)
: QAbstractListModel(parent)
@@ -66,10 +67,7 @@ UsersModel::data(const QModelIndex &index, int role) const
case CompletionModel::CompletionRole:
if (UserSettings::instance()->markdown())
return QStringLiteral("[%1](https://matrix.to/#/%2)")
- .arg(QString(displayNames[index.row()])
- .replace("[", "\\[")
- .replace("]", "\\]")
- .toHtmlEscaped(),
+ .arg(utils::escapeMentionMarkdown(QString(displayNames[index.row()])),
QString(QUrl::toPercentEncoding(userids[index.row()])));
else
return displayNames[index.row()];
diff --git a/src/Utils.cpp b/src/Utils.cpp
index 8b8a11dc..3e7340f4 100644
--- a/src/Utils.cpp
+++ b/src/Utils.cpp
@@ -582,6 +582,34 @@ utils::linkifyMessage(const QString &body)
}
QString
+utils::escapeMentionMarkdown(QString input)
+{
+ input = input.toHtmlEscaped();
+
+ constexpr std::array<char, 10> markdownChars = {
+ '\\',
+ '`',
+ '*',
+ '_',
+ /*'{', '}',*/ '[',
+ ']',
+ '<',
+ '>',
+ /* '(', ')', '#', '-', '+', '.', '!', */ '~',
+ '|',
+ };
+
+ QByteArray replacement = "\\\\";
+
+ for (char c : markdownChars) {
+ replacement[1] = c;
+ input.replace(QChar::fromLatin1(c), QLatin1StringView(replacement));
+ }
+
+ return input;
+}
+
+QString
utils::escapeBlacklistedHtml(const QString &rawStr)
{
static const std::set<QByteArray> allowedTags = {
@@ -1139,18 +1167,19 @@ utils::getFormattedQuoteBody(const RelatedInfo &related, const QString &html)
return QStringLiteral("sent a video");
}
default: {
- return related.quoted_formatted_body;
+ return escapeBlacklistedHtml(related.quoted_formatted_body);
}
}
};
+
return QStringLiteral("<mx-reply><blockquote><a "
"href=\"https://matrix.to/#/%1/%2\">In reply "
"to</a> <a href=\"https://matrix.to/#/%3\">%4</a><br"
"/>%5</blockquote></mx-reply>")
.arg(related.room,
QString::fromStdString(related.related_event),
- related.quoted_user,
- related.quoted_user,
+ QUrl::toPercentEncoding(related.quoted_user),
+ related.quoted_user.toHtmlEscaped(),
getFormattedBody()) +
html;
}
diff --git a/src/Utils.h b/src/Utils.h
index 75438a7b..9c5e4713 100644
--- a/src/Utils.h
+++ b/src/Utils.h
@@ -140,6 +140,9 @@ linkifyMessage(const QString &body);
QString
markdownToHtml(const QString &text, bool rainbowify = false, bool noExtensions = false);
+QString
+escapeMentionMarkdown(QString input);
+
//! Escape every html tag, that was not whitelisted
QString
escapeBlacklistedHtml(const QString &data);
diff --git a/src/timeline/InputBar.cpp b/src/timeline/InputBar.cpp
index ac1d47a2..2d15d01a 100644
--- a/src/timeline/InputBar.cpp
+++ b/src/timeline/InputBar.cpp
@@ -462,8 +462,7 @@ replaceMatrixToMarkdownLink(QString input)
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("\\]", "]");
+ name.remove(QChar(u'\\'), Qt::CaseSensitive);
input.replace(startOfName, endOfLink - startOfName + 1, name);
replaced = true;
}
@@ -522,6 +521,7 @@ InputBar::generateMentions()
// this->containsAtRoom_ = false;
// this->mentions_.clear();
// this->mentionTexts_.clear();
+
return mention;
}
diff --git a/src/timeline/TimelineModel.cpp b/src/timeline/TimelineModel.cpp
index 03606d90..4aa9af00 100644
--- a/src/timeline/TimelineModel.cpp
+++ b/src/timeline/TimelineModel.cpp
@@ -3080,8 +3080,6 @@ TimelineModel::setEdit(const QString &newEdit)
input()->storeForEdit();
}
- 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()) {
@@ -3100,10 +3098,12 @@ TimelineModel::setEdit(const QString &newEdit)
for (const auto &user : mentionsList->user_ids) {
auto userid = QString::fromStdString(user);
mentions.append(userid);
- mentionTexts.append(
- QStringLiteral("[%1](https://matrix.to/#/%2)")
- .arg(displayName(userid).replace("[", "\\[").replace("]", "\\]"),
- QString(QUrl::toPercentEncoding(userid))));
+ mentionTexts.append(QStringLiteral("[%1](https://matrix.to/#/%2)")
+ .arg(utils::escapeMentionMarkdown(
+ // not using TimelineModel::displayName here,
+ // because it would double html escape
+ cache::displayName(room_id_, userid)),
+ QString(QUrl::toPercentEncoding(userid))));
}
}
@@ -3127,7 +3127,9 @@ 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(quoted(user), link));
+ editText.replace(
+ user,
+ QStringLiteral("[%1](%2)").arg(utils::escapeMentionMarkdown(user), link));
}
}
|