mirror of
https://github.com/Nheko-Reborn/nheko.git
synced 2024-11-22 11:00:48 +03:00
Fix matrix.to link escaping
Properly escapes in replies and with unmatched []. fixes #854
This commit is contained in:
parent
a5dafec147
commit
0ad74590d4
5 changed files with 59 additions and 12 deletions
|
@ -30,9 +30,7 @@ const QRegularExpression url_regex(
|
||||||
// vvvv atomic match url -> fail if there is a " before or after vvv
|
// vvvv atomic match url -> fail if there is a " before or after vvv
|
||||||
QStringLiteral(
|
QStringLiteral(
|
||||||
R"((?<!["'])(?>((www\.(?!\.)|[a-z][a-z0-9+.-]*://)[^\s<>'"]+[^!,\.\s<>'"\]\)\:]))(?!["']))"));
|
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.
|
// A matrix link to be converted back to markdown
|
||||||
static const QRegularExpression
|
|
||||||
matrixToMarkdownLink(QStringLiteral(R"(\[(.*?)(?<!\\)\]\((https://matrix.to/#/.*?\)))"));
|
|
||||||
static const QRegularExpression
|
static const QRegularExpression
|
||||||
matrixToLink(QStringLiteral(R"(<a href=\"(https://matrix.to/#/.*?)\">(.*?)</a>)"));
|
matrixToLink(QStringLiteral(R"(<a href=\"(https://matrix.to/#/.*?)\">(.*?)</a>)"));
|
||||||
}
|
}
|
||||||
|
|
|
@ -61,7 +61,11 @@ RoomsModel::data(const QModelIndex &index, int role) const
|
||||||
if (UserSettings::instance()->markdown()) {
|
if (UserSettings::instance()->markdown()) {
|
||||||
QString percentEncoding = QUrl::toPercentEncoding(roomAliases[index.row()]);
|
QString percentEncoding = QUrl::toPercentEncoding(roomAliases[index.row()]);
|
||||||
return QStringLiteral("[%1](https://matrix.to/#/%2)")
|
return QStringLiteral("[%1](https://matrix.to/#/%2)")
|
||||||
.arg(roomAliases[index.row()], percentEncoding);
|
.arg(QString(roomAliases[index.row()])
|
||||||
|
.replace("[", "\\[")
|
||||||
|
.replace("]", "\\]")
|
||||||
|
.toHtmlEscaped(),
|
||||||
|
percentEncoding);
|
||||||
} else {
|
} else {
|
||||||
return roomAliases[index.row()];
|
return roomAliases[index.row()];
|
||||||
}
|
}
|
||||||
|
|
|
@ -43,7 +43,10 @@ UsersModel::data(const QModelIndex &index, int role) const
|
||||||
case CompletionModel::CompletionRole:
|
case CompletionModel::CompletionRole:
|
||||||
if (UserSettings::instance()->markdown())
|
if (UserSettings::instance()->markdown())
|
||||||
return QStringLiteral("[%1](https://matrix.to/#/%2)")
|
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()])));
|
QString(QUrl::toPercentEncoding(userids[index.row()])));
|
||||||
else
|
else
|
||||||
return displayNames[index.row()];
|
return displayNames[index.row()];
|
||||||
|
|
|
@ -343,6 +343,47 @@ InputBar::openFileSelection()
|
||||||
startUploadFromPath(fileName);
|
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
|
void
|
||||||
InputBar::message(const QString &msg, MarkdownOverride useMarkdown, bool rainbowify)
|
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) {
|
useMarkdown == MarkdownOverride::ON) {
|
||||||
text.formatted_body = utils::markdownToHtml(msg, rainbowify).toStdString();
|
text.formatted_body = utils::markdownToHtml(msg, rainbowify).toStdString();
|
||||||
// Remove markdown links by completer
|
// 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
|
// Don't send formatted_body, when we don't need to
|
||||||
if (text.formatted_body.find('<') == std::string::npos)
|
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!
|
// NOTE(Nico): rich replies always need a formatted_body!
|
||||||
text.format = "org.matrix.custom.html";
|
text.format = "org.matrix.custom.html";
|
||||||
|
@ -426,8 +468,7 @@ InputBar::emote(const QString &msg, bool rainbowify)
|
||||||
emote.formatted_body = html.toStdString();
|
emote.formatted_body = html.toStdString();
|
||||||
emote.format = "org.matrix.custom.html";
|
emote.format = "org.matrix.custom.html";
|
||||||
// Remove markdown links by completer
|
// Remove markdown links by completer
|
||||||
emote.body =
|
emote.body = replaceMatrixToMarkdownLink(msg.trimmed()).toStdString();
|
||||||
msg.trimmed().replace(conf::strings::matrixToMarkdownLink, "\\1").toStdString();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!room->reply().isEmpty()) {
|
if (!room->reply().isEmpty()) {
|
||||||
|
@ -454,8 +495,7 @@ InputBar::notice(const QString &msg, bool rainbowify)
|
||||||
notice.formatted_body = html.toStdString();
|
notice.formatted_body = html.toStdString();
|
||||||
notice.format = "org.matrix.custom.html";
|
notice.format = "org.matrix.custom.html";
|
||||||
// Remove markdown links by completer
|
// Remove markdown links by completer
|
||||||
notice.body =
|
notice.body = replaceMatrixToMarkdownLink(msg.trimmed()).toStdString();
|
||||||
msg.trimmed().replace(conf::strings::matrixToMarkdownLink, "\\1").toStdString();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!room->reply().isEmpty()) {
|
if (!room->reply().isEmpty()) {
|
||||||
|
|
|
@ -2625,6 +2625,8 @@ TimelineModel::setEdit(const QString &newEdit)
|
||||||
nhlog::ui()->debug("Stored: {}", textBeforeEdit.toStdString());
|
nhlog::ui()->debug("Stored: {}", textBeforeEdit.toStdString());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
auto quoted = [](QString in) { return in.replace("[", "\\[").replace("]", "\\]"); };
|
||||||
|
|
||||||
if (edit_ != newEdit) {
|
if (edit_ != newEdit) {
|
||||||
auto ev = events.get(newEdit.toStdString(), "");
|
auto ev = events.get(newEdit.toStdString(), "");
|
||||||
if (ev && mtx::accessors::sender(*ev) == http::client()->user_id().to_string()) {
|
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) {
|
for (const auto &[user, link] : reverseNameMapping) {
|
||||||
// TODO(Nico): html unescape the user name
|
// 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));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue