|
|
@ -314,37 +314,67 @@ utils::linkifyMessage(const QString &body) |
|
|
|
return doc; |
|
|
|
return doc; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
QByteArray |
|
|
|
QString |
|
|
|
escapeRawHtml(const QByteArray &data) |
|
|
|
utils::escapeBlacklistedHtml(const QString &rawStr) |
|
|
|
{ |
|
|
|
{ |
|
|
|
|
|
|
|
static const std::vector<std::string_view> allowedTags = { |
|
|
|
|
|
|
|
"font", "/font", "del", "/del", "h1", "/h1", "h2", "/h2", |
|
|
|
|
|
|
|
"h3", "/h3", "h4", "/h4", "h5", "/h5", "h6", "/h6", |
|
|
|
|
|
|
|
"blockquote", "/blockquote", "p", "/p", "a", "/a", "ul", "/ul", |
|
|
|
|
|
|
|
"ol", "/ol", "sup", "/sup", "sub", "/sub", "li", "/li", |
|
|
|
|
|
|
|
"b", "/b", "i", "/i", "u", "/u", "strong", "/strong", |
|
|
|
|
|
|
|
"em", "/em", "strike", "/strike", "code", "/code", "hr", "/hr", |
|
|
|
|
|
|
|
"br", "br/", "div", "/div", "table", "/table", "thead", "/thead", |
|
|
|
|
|
|
|
"tbody", "/tbody", "tr", "/tr", "th", "/th", "td", "/td", |
|
|
|
|
|
|
|
"caption", "/caption", "pre", "/pre", "span", "/span", "img", "/img"}; |
|
|
|
|
|
|
|
QByteArray data = rawStr.toUtf8(); |
|
|
|
QByteArray buffer; |
|
|
|
QByteArray buffer; |
|
|
|
const size_t length = data.size(); |
|
|
|
const size_t length = data.size(); |
|
|
|
buffer.reserve(length); |
|
|
|
buffer.reserve(length); |
|
|
|
|
|
|
|
bool escapingTag = false; |
|
|
|
for (size_t pos = 0; pos != length; ++pos) { |
|
|
|
for (size_t pos = 0; pos != length; ++pos) { |
|
|
|
switch (data.at(pos)) { |
|
|
|
switch (data.at(pos)) { |
|
|
|
case '&': |
|
|
|
case '<': { |
|
|
|
buffer.append("&"); |
|
|
|
bool oneTagMatched = false; |
|
|
|
break; |
|
|
|
size_t endPos = std::min(static_cast<size_t>(data.indexOf('>', pos)), |
|
|
|
case '<': |
|
|
|
static_cast<size_t>(data.indexOf(' ', pos))); |
|
|
|
buffer.append("<"); |
|
|
|
|
|
|
|
|
|
|
|
auto mid = data.mid(pos + 1, endPos - pos - 1); |
|
|
|
|
|
|
|
for (const auto &tag : allowedTags) { |
|
|
|
|
|
|
|
// TODO: Check src and href attribute
|
|
|
|
|
|
|
|
if (mid.compare(tag.data(), Qt::CaseInsensitive) == 0) { |
|
|
|
|
|
|
|
oneTagMatched = true; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
if (oneTagMatched) |
|
|
|
|
|
|
|
buffer.append('<'); |
|
|
|
|
|
|
|
else { |
|
|
|
|
|
|
|
escapingTag = true; |
|
|
|
|
|
|
|
buffer.append("<"); |
|
|
|
|
|
|
|
} |
|
|
|
break; |
|
|
|
break; |
|
|
|
|
|
|
|
} |
|
|
|
case '>': |
|
|
|
case '>': |
|
|
|
buffer.append(">"); |
|
|
|
if (escapingTag) |
|
|
|
|
|
|
|
buffer.append(">"); |
|
|
|
|
|
|
|
else { |
|
|
|
|
|
|
|
escapingTag = false; |
|
|
|
|
|
|
|
buffer.append('>'); |
|
|
|
|
|
|
|
} |
|
|
|
break; |
|
|
|
break; |
|
|
|
default: |
|
|
|
default: |
|
|
|
buffer.append(data.at(pos)); |
|
|
|
buffer.append(data.at(pos)); |
|
|
|
break; |
|
|
|
break; |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
return buffer; |
|
|
|
return QString::fromUtf8(buffer); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
QString |
|
|
|
QString |
|
|
|
utils::markdownToHtml(const QString &text) |
|
|
|
utils::markdownToHtml(const QString &text) |
|
|
|
{ |
|
|
|
{ |
|
|
|
const auto str = escapeRawHtml(text.toUtf8()); |
|
|
|
const auto str = text.toUtf8(); |
|
|
|
const char *tmp_buf = |
|
|
|
const char *tmp_buf = cmark_markdown_to_html(str.constData(), str.size(), CMARK_OPT_UNSAFE); |
|
|
|
cmark_markdown_to_html(str.constData(), str.size(), CMARK_OPT_DEFAULT); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Copy the null terminated output buffer.
|
|
|
|
// Copy the null terminated output buffer.
|
|
|
|
std::string html(tmp_buf); |
|
|
|
std::string html(tmp_buf); |
|
|
@ -352,7 +382,7 @@ utils::markdownToHtml(const QString &text) |
|
|
|
// The buffer is no longer needed.
|
|
|
|
// The buffer is no longer needed.
|
|
|
|
free((char *)tmp_buf); |
|
|
|
free((char *)tmp_buf); |
|
|
|
|
|
|
|
|
|
|
|
auto result = QString::fromStdString(html).trimmed(); |
|
|
|
auto result = escapeBlacklistedHtml(QString::fromStdString(html)).trimmed(); |
|
|
|
|
|
|
|
|
|
|
|
return result; |
|
|
|
return result; |
|
|
|
} |
|
|
|
} |
|
|
|