diff --git a/CMakeLists.txt b/CMakeLists.txt index 5054be05..667d7b88 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -606,7 +606,7 @@ if(USE_BUNDLED_MTXCLIENT) FetchContent_Declare( MatrixClient GIT_REPOSITORY https://github.com/Nheko-Reborn/mtxclient.git - GIT_TAG 8b3c9a34770df147fbd78134dc71a9b27471d153 + GIT_TAG 604a2ec95b03fbf8c856bc481e7582f5c6a64748 ) set(BUILD_LIB_EXAMPLES OFF CACHE INTERNAL "") set(BUILD_LIB_TESTS OFF CACHE INTERNAL "") diff --git a/im.nheko.Nheko.yaml b/im.nheko.Nheko.yaml index 8150b292..ca5c3a98 100644 --- a/im.nheko.Nheko.yaml +++ b/im.nheko.Nheko.yaml @@ -223,7 +223,7 @@ modules: buildsystem: cmake-ninja name: mtxclient sources: - - commit: 8b3c9a34770df147fbd78134dc71a9b27471d153 + - commit: 604a2ec95b03fbf8c856bc481e7582f5c6a64748 #tag: v0.9.2 type: git url: https://github.com/Nheko-Reborn/mtxclient.git 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 @@ -581,6 +581,34 @@ utils::linkifyMessage(const QString &body) return doc; } +QString +utils::escapeMentionMarkdown(QString input) +{ + input = input.toHtmlEscaped(); + + constexpr std::array 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) { @@ -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("
In reply " "to %4%5
") .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)); } }