summary refs log tree commit diff
path: root/src
diff options
context:
space:
mode:
authorNicolas Werner <nicolas.werner@hotmail.de>2024-05-31 18:17:35 +0200
committerNicolas Werner <nicolas.werner@hotmail.de>2024-05-31 18:28:16 +0200
commitec9af40fc50142f32924efd9235ca5912b6fe395 (patch)
treef81bba9db7cff61d5f967d7ded1fb192108de3e9 /src
parentDisable image loaded workaround for Qt 6.7 and up (diff)
downloadnheko-ec9af40fc50142f32924efd9235ca5912b6fe395.tar.xz
fix mentions with markdown and in edits
Diffstat (limited to 'src')
-rw-r--r--src/UsersModel.cpp6
-rw-r--r--src/Utils.cpp35
-rw-r--r--src/Utils.h3
-rw-r--r--src/timeline/InputBar.cpp4
-rw-r--r--src/timeline/TimelineModel.cpp16
5 files changed, 48 insertions, 16 deletions
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));
                     }
                 }