Page Menu
Home
GnuPG
Search
Configure Global Search
Log In
Files
F36623083
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Award Token
Size
34 KB
Subscribers
None
View Options
diff --git a/src/core/errors.h b/src/core/errors.h
index 9614cd2..6a873e6 100644
--- a/src/core/errors.h
+++ b/src/core/errors.h
@@ -1,328 +1,328 @@
// SPDX-FileCopyrightText: 2018 Christian Mollekopf <mollekopf@kolabsys.com>
// SPDX-License-Identifier: LGPL-2.0-or-later
#pragma once
#include <memory>
#include <type_traits>
#include <utility>
// A somewhat implementation of the expected monad, proposed here:
// https://isocpp.org/files/papers/n4015.pdf
// A class used to differentiate errors and values when they are of the same type.
template<typename Error>
class Unexpected
{
static_assert(!std::is_same<Error, void>::value, "Cannot have an Unexpected void");
public:
Unexpected() = delete;
constexpr explicit Unexpected(const Error &error)
: mValue(error)
{
}
constexpr explicit Unexpected(Error &&error)
: mValue(std::move(error))
{
}
// For implicit conversions when doing makeUnexpected(other)
template<typename Other>
constexpr explicit Unexpected(const Unexpected<Other> &error)
: mValue(error.value())
{
}
template<typename Other>
constexpr explicit Unexpected(Unexpected<Other> &&error)
: mValue(std::move(error.value()))
{
}
constexpr const Error &value() const &
{
return mValue;
}
Error &value() &
{
return mValue;
}
constexpr const Error &&value() const &&
{
return std::move(mValue);
}
Error &&value() &&
{
return std::move(mValue);
}
private:
Error mValue;
};
template<class Error>
Unexpected<typename std::decay<Error>::type> makeUnexpected(Error &&e)
{
return Unexpected<typename std::decay<Error>::type>(std::forward<Error>(e));
}
template<typename Error>
bool operator==(const Unexpected<Error> &lhs, const Unexpected<Error> &rhs)
{
return lhs.value() == rhs.value();
}
template<typename Error>
bool operator!=(const Unexpected<Error> &lhs, const Unexpected<Error> &rhs)
{
return lhs.value() != rhs.value();
}
namespace detail
{
namespace tags
{
struct Expected {
};
struct Unexpected {
};
} // namespace tags
// Write functions here when storage related and when Type != void
template<typename Error, typename Type>
struct StorageBase {
protected:
// To be able to define a copy constructor in a child class
StorageBase()
{
}
// Rule of 5 (copy constructors defined in StorageCopyConstructor) {{{
StorageBase(StorageBase &&other)
: mIsValue(other.mIsValue)
{
// This is a constructor, you have to construct object, not assign them
// (hence the placement new)
//
// Here's the problem:
//
// Object that are part of a union are not initialized (which is
// normal). If we replaced the placement new by a line like this:
//
// ```
// mValue = other.mValue;
// ```
//
// If overloaded, this will call `mValue.operator=(other.mValue);`, but
// since we're in the constructor, mValue is not initialized. This can
// cause big issues if `Type` / `Error` is not trivially (move)
// assignable.
//
// And so, the placement new allows us to call the constructor of
// `Type` or `Error` instead of its assignment operator.
if (mIsValue) {
new (std::addressof(mValue)) Type(std::move(other.mValue));
} else {
new (std::addressof(mError)) Unexpected<Error>(std::move(other.mError));
}
}
StorageBase &operator=(StorageBase &&other)
{
this->~StorageBase();
mIsValue = other.mIsValue;
if (mIsValue) {
mValue = std::move(other.mValue);
} else {
mError = std::move(other.mError);
}
return *this;
}
~StorageBase()
{
if (mIsValue) {
mValue.~Type();
} else {
mError.~Unexpected<Error>();
}
}
// }}}
template<typename... Args>
constexpr StorageBase(tags::Expected, Args &&...args)
: mValue(std::forward<Args>(args)...)
, mIsValue(true)
{
}
template<typename... Args>
constexpr StorageBase(tags::Unexpected, Args &&...args)
: mError(std::forward<Args>(args)...)
, mIsValue(false)
{
}
union {
Unexpected<Error> mError;
Type mValue;
};
bool mIsValue;
};
// Write functions here when storage related and when Type == void
template<typename Error>
struct StorageBase<Error, void> {
protected:
constexpr StorageBase(tags::Expected)
: mIsValue(true)
{
}
template<typename... Args>
constexpr StorageBase(tags::Unexpected, Args &&...args)
: mError(std::forward<Args>(args)...)
, mIsValue(false)
{
}
Unexpected<Error> mError;
bool mIsValue;
};
// Struct used to add the copy constructor / assignment only if both types are copy constructible
-template<typename Error, typename Type, bool both_copy_constructible = std::is_copy_constructible<Error>::value &&std::is_copy_constructible<Type>::value>
+template<typename Error, typename Type, bool both_copy_constructible = std::is_copy_constructible<Error>::value && std::is_copy_constructible<Type>::value>
struct StorageCopyConstructor;
template<typename Error, typename Type>
struct StorageCopyConstructor<Error, Type, true> : StorageBase<Error, Type> {
protected:
using StorageBase<Error, Type>::StorageBase;
StorageCopyConstructor(const StorageCopyConstructor &other)
: StorageBase<Error, Type>()
{
// If you're thinking WTF, see the comment in the move constructor above.
this->mIsValue = other.mIsValue;
if (this->mIsValue) {
new (std::addressof(this->mValue)) Type(other.mValue);
} else {
new (std::addressof(this->mError)) Unexpected<Error>(other.mError);
}
}
StorageCopyConstructor &operator=(const StorageCopyConstructor &other)
{
this->mIsValue = other.mIsValue;
if (this->mIsValue) {
this->mValue = other.mValue;
} else {
this->mError = other.mError;
}
return *this;
}
};
template<typename Error, typename Type>
struct StorageCopyConstructor<Error, Type, false> : StorageBase<Error, Type> {
protected:
using StorageBase<Error, Type>::StorageBase;
};
// Write functions here when storage related, whether Type is void or not
template<typename Error, typename Type>
struct Storage : StorageCopyConstructor<Error, Type> {
protected:
// Forward the construction to StorageBase
using StorageCopyConstructor<Error, Type>::StorageCopyConstructor;
};
// Write functions here when dev API related and when Type != void
template<typename Error, typename Type>
struct ExpectedBase : detail::Storage<Error, Type> {
constexpr ExpectedBase()
: detail::Storage<Error, Type>(detail::tags::Expected{})
{
}
template<typename OtherError>
constexpr ExpectedBase(const Unexpected<OtherError> &error)
: detail::Storage<Error, Type>(detail::tags::Unexpected{}, error)
{
}
template<typename OtherError>
constexpr ExpectedBase(Unexpected<OtherError> &&error)
: detail::Storage<Error, Type>(detail::tags::Unexpected{}, std::move(error))
{
}
constexpr ExpectedBase(const Type &value)
: detail::Storage<Error, Type>(detail::tags::Expected{}, value)
{
}
constexpr ExpectedBase(Type &&value)
: detail::Storage<Error, Type>(detail::tags::Expected{}, std::move(value))
{
}
// Warning: will crash if this is an error. You should always check this is
// an expected value before calling `.value()`
constexpr const Type &value() const &
{
// FIXME: Q_ASSERT cannot be used in a constexpr with qt 5.9. See also:
// https://git.qt.io/consulting-usa/qtbase-xcb-rendering/commit/8ea27bb1c669e21100a6a042b0378b3346bdf671 Q_ASSERT(this->mIsValue);
return this->mValue;
}
Type &&value() &&
{
Q_ASSERT(this->mIsValue);
return std::move(this->mValue);
}
};
// Write functions here when dev API related and when Type == void
template<typename Error>
struct ExpectedBase<Error, void> : Storage<Error, void> {
// Rewrite constructors for unexpected because Expected doesn't have direct access to it.
template<typename OtherError>
constexpr ExpectedBase(const Unexpected<OtherError> &error)
: Storage<Error, void>(tags::Unexpected{}, error)
{
}
template<typename OtherError>
constexpr ExpectedBase(Unexpected<OtherError> &&error)
: Storage<Error, void>(tags::Unexpected{}, std::move(error))
{
}
};
} // namespace detail
// Write functions here when dev API related, whether Type is void or not
template<typename Error, typename Type = void>
class Expected : public detail::ExpectedBase<Error, Type>
{
static_assert(!std::is_same<Error, void>::value, "Expected with void Error is not implemented");
public:
using detail::ExpectedBase<Error, Type>::ExpectedBase;
constexpr const Error &error() const &
{
return this->mError.value();
}
constexpr bool isValue() const
{
return this->mIsValue;
}
constexpr explicit operator bool() const
{
return this->mIsValue;
}
};
diff --git a/src/core/partmodel.cpp b/src/core/partmodel.cpp
index 5c10c76..9295861 100644
--- a/src/core/partmodel.cpp
+++ b/src/core/partmodel.cpp
@@ -1,656 +1,654 @@
// SPDX-FileCopyrightText: 2016 Sandro Knauß <knauss@kolabsys.com>
// SPDX-License-Identifier: LGPL-2.0-or-later
#include "partmodel.h"
#include "enums.h"
#include "htmlutils.h"
#include "messagepart.h"
#include "mimetreeparser_core_debug.h"
#include "objecttreeparser.h"
#include "utils.h"
#include <KLocalizedString>
#include <Libkleo/Compliance>
#include <Libkleo/Formatting>
#include <Libkleo/KeyCache>
#include <QDebug>
#include <QGpgME/Protocol>
#include <QRegularExpression>
#include <QStringLiteral>
#include <QTextDocument>
#include <verificationresult.h>
static std::optional<GpgME::Signature> signatureFromMessagePart(MimeTreeParser::MessagePart *messagePart)
{
const auto signatureState = messagePart->signatureState();
const bool messageIsSigned = signatureState == MimeTreeParser::KMMsgPartiallySigned || signatureState == MimeTreeParser::KMMsgFullySigned;
if (!messageIsSigned) {
return std::nullopt;
}
const auto signatureParts = messagePart->signatures();
Q_ASSERT(!signatureParts.isEmpty());
if (signatureParts.empty()) {
return std::nullopt;
}
const auto signaturePart = signatureParts.front(); // TODO add support for multiple signature
const auto signatures = signaturePart->partMetaData()->verificationResult.signatures();
Q_ASSERT(!signatures.empty());
if (signatures.empty()) {
return std::nullopt;
}
const auto signature = signatures.front(); // TODO add support for multiple signature
return signature;
}
// We return a pair containing the trimmed string, as well as a boolean indicating whether the string was trimmed or not
std::pair<QString, bool> PartModel::trim(const QString &text)
{
// The delimiters have <p>.? prefixed including the .? because sometimes we get a byte order mark <feff> (seen with user-agent:
// Microsoft-MacOutlook/10.1d.0.190908) We match both regulard withspace with \s and non-breaking spaces with \u00A0
const QList<QRegularExpression> delimiters{
// English
QRegularExpression{QStringLiteral("<p>.?-+Original(\\s|\u00A0)Message-+"), QRegularExpression::CaseInsensitiveOption},
// The remainder is not quoted
QRegularExpression{QStringLiteral("<p>.?On.*wrote:"), QRegularExpression::CaseInsensitiveOption},
// The remainder is quoted
QRegularExpression{QStringLiteral("> On.*wrote:"), QRegularExpression::CaseInsensitiveOption},
// German
// Forwarded
QRegularExpression{QStringLiteral("<p>.?Von:.*</p>"), QRegularExpression::CaseInsensitiveOption},
// Reply
QRegularExpression{QStringLiteral("<p>.?Am.*schrieb.*:</p>"), QRegularExpression::CaseInsensitiveOption},
// Signature
QRegularExpression{QStringLiteral("<p>.?--(\\s|\u00A0)<br>"), QRegularExpression::CaseInsensitiveOption},
};
for (const auto &expression : delimiters) {
auto i = expression.globalMatchView(text);
while (i.hasNext()) {
const auto match = i.next();
const int startOffset = match.capturedStart(0);
// This is a very simplistic detection for an inline reply where we would have the patterns before the actual message content.
// We simply ignore anything we find within the first few lines.
if (startOffset >= 5) {
return {text.mid(0, startOffset), true};
} else {
// Search for the next delimiter
continue;
}
}
}
return {text, false};
}
static QString addCss(const QString &s)
{
// Get the default font from QApplication
static const QString fontFamily = QFont{}.family();
// overflow:hidden ensures no scrollbars are ever shown.
static const QString css = QStringLiteral("<style>\n")
+ QStringLiteral(
"body {\n"
" overflow:hidden;\n"
" font-family: \"%1\" ! important;\n"
" color: #31363b ! important;\n"
" background-color: #fcfcfc ! important\n"
"}\n")
.arg(fontFamily)
+ QStringLiteral("blockquote { \n"
" border-left: 2px solid #bdc3c7 ! important;\n"
"}\n")
+ QStringLiteral("</style>");
const QString header = QLatin1StringView(
"<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\" \"http://www.w3.org/TR/html4/loose.dtd\">\n"
"<html><head><title></title>")
+ css + QLatin1StringView("</head>\n<body>\n");
return header + s + QStringLiteral("</body></html>");
}
class PartModelPrivate
{
public:
PartModelPrivate(PartModel *q_ptr, const std::shared_ptr<MimeTreeParser::ObjectTreeParser> &parser)
: q(q_ptr)
, mParser(parser)
{
collectContents();
}
~PartModelPrivate() = default;
void checkPart(const MimeTreeParser::MessagePart::Ptr part)
{
mMimeTypeCache[part.data()] = part->mimeType();
// Extract the content of the part and
mContents.insert(part.data(), extractContent(part.data()));
}
// Recursively find encapsulated messages
void findEncapsulated(const MimeTreeParser::EncapsulatedRfc822MessagePart::Ptr &e)
{
mEncapsulatedParts[e.data()] = mParser->collectContentParts(e);
for (const auto &subPart : std::as_const(mEncapsulatedParts[e.data()])) {
checkPart(subPart);
mParents[subPart.data()] = e.data();
if (auto encapsulatedSub = subPart.dynamicCast<MimeTreeParser::EncapsulatedRfc822MessagePart>()) {
findEncapsulated(encapsulatedSub);
}
}
}
QVariant extractContent(MimeTreeParser::MessagePart *messagePart)
{
if (auto alternativePart = dynamic_cast<MimeTreeParser::AlternativeMessagePart *>(messagePart)) {
if (alternativePart->availableModes().contains(MimeTreeParser::AlternativeMessagePart::MultipartIcal)) {
return alternativePart->icalContent();
}
}
auto preprocessPlaintext = [&](const QString &text) {
// Reduce consecutive new lines to never exceed 2
auto cleaned = text;
cleaned.replace(QRegularExpression(QStringLiteral("[\n\r]{2,}")), QStringLiteral("\n\n"));
// We always do rich text (so we get highlighted links and stuff).
const auto html = Qt::convertFromPlainText(cleaned, Qt::WhiteSpaceNormal);
if (trimMail) {
const auto result = PartModel::trim(html);
isTrimmed = result.second;
Q_EMIT q->trimMailChanged();
return MimeTreeParser::linkify(result.first);
}
return MimeTreeParser::linkify(html);
};
if (messagePart->isHtml()) {
if (dynamic_cast<MimeTreeParser::AlternativeMessagePart *>(messagePart)) {
containsHtmlAndPlain = true;
Q_EMIT q->containsHtmlChanged();
if (!showHtml) {
return preprocessPlaintext(messagePart->plaintextContent());
}
}
return addCss(mParser->resolveCidLinks(messagePart->htmlContent()));
}
if (auto attachmentPart = dynamic_cast<MimeTreeParser::AttachmentMessagePart *>(messagePart)) {
auto node = attachmentPart->node();
if (node && mMimeTypeCache[attachmentPart] == QByteArrayLiteral("text/calendar")) {
return messagePart->text();
}
}
return preprocessPlaintext(messagePart->text());
}
QVariant contentForPart(MimeTreeParser::MessagePart *messagePart) const
{
return mContents.value(messagePart);
}
void collectContents()
{
mEncapsulatedParts.clear();
mParents.clear();
mContents.clear();
containsHtmlAndPlain = false;
isTrimmed = false;
const auto parts = mParser->collectContentParts();
MimeTreeParser::MessagePart::List filteredParts;
for (const auto &part : parts) {
if (part->node()) {
const auto contentType = part->node()->contentType();
if (contentType && contentType->hasParameter("protected-headers")) {
const auto contentDisposition = part->node()->contentDisposition();
if (contentDisposition && contentDisposition->disposition() == KMime::Headers::CDinline) {
continue;
}
}
}
filteredParts << part;
}
for (const auto &part : std::as_const(filteredParts)) {
checkPart(part);
if (auto encapsulatedPart = part.dynamicCast<MimeTreeParser::EncapsulatedRfc822MessagePart>()) {
findEncapsulated(encapsulatedPart);
}
}
for (const auto &part : std::as_const(filteredParts)) {
if (mMimeTypeCache[part.data()] == QByteArrayLiteral("text/calendar")) {
mParts.prepend(part);
} else {
mParts.append(part);
}
}
}
PartModel *q;
MimeTreeParser::MessagePart::List mParts;
QHash<MimeTreeParser::MessagePart *, QByteArray> mMimeTypeCache;
QHash<MimeTreeParser::MessagePart *, MimeTreeParser::MessagePart::List> mEncapsulatedParts;
QHash<MimeTreeParser::MessagePart *, MimeTreeParser::MessagePart *> mParents;
QMap<MimeTreeParser::MessagePart *, QVariant> mContents;
std::shared_ptr<MimeTreeParser::ObjectTreeParser> mParser;
bool showHtml{false};
bool containsHtmlAndPlain{false};
bool trimMail{false};
bool isTrimmed{false};
};
PartModel::PartModel(std::shared_ptr<MimeTreeParser::ObjectTreeParser> parser)
: d(std::unique_ptr<PartModelPrivate>(new PartModelPrivate(this, parser)))
{
}
PartModel::~PartModel() = default;
void PartModel::setShowHtml(bool html)
{
if (d->showHtml != html) {
beginResetModel();
d->showHtml = html;
d->collectContents();
endResetModel();
Q_EMIT showHtmlChanged();
}
}
bool PartModel::showHtml() const
{
return d->showHtml;
}
void PartModel::setTrimMail(bool trim)
{
if (d->trimMail != trim) {
beginResetModel();
d->trimMail = trim;
d->collectContents();
endResetModel();
Q_EMIT trimMailChanged();
}
}
bool PartModel::trimMail() const
{
return d->trimMail;
}
bool PartModel::isTrimmed() const
{
return d->isTrimmed;
}
bool PartModel::containsHtml() const
{
return d->containsHtmlAndPlain;
}
QHash<int, QByteArray> PartModel::roleNames() const
{
return {
{TypeRole, QByteArrayLiteral("type")},
{ContentRole, QByteArrayLiteral("content")},
{IsEmbeddedRole, QByteArrayLiteral("isEmbedded")},
{SidebarSecurityLevelRole, QByteArrayLiteral("sidebarSecurityLevel")},
{EncryptionSecurityLevelRole, QByteArrayLiteral("encryptionSecurityLevel")},
{SignatureSecurityLevelRole, QByteArrayLiteral("signatureSecurityLevel")},
{EncryptionSecurityLevelRole, QByteArrayLiteral("encryptionSecurityLevel")},
{ErrorType, QByteArrayLiteral("errorType")},
{ErrorString, QByteArrayLiteral("errorString")},
{IsErrorRole, QByteArrayLiteral("error")},
{SenderRole, QByteArrayLiteral("sender")},
{SignatureDetailsRole, QByteArrayLiteral("signatureDetails")},
{SignatureIconNameRole, QByteArrayLiteral("signatureIconName")},
{EncryptionDetails, QByteArrayLiteral("encryptionDetails")},
{EncryptionIconNameRole, QByteArrayLiteral("encryptionIconName")},
{DateRole, QByteArrayLiteral("date")},
};
}
QModelIndex PartModel::index(int row, int column, const QModelIndex &parent) const
{
if (row < 0 || column != 0) {
return QModelIndex();
}
if (parent.isValid()) {
const auto part = static_cast<MimeTreeParser::MessagePart *>(parent.internalPointer());
auto encapsulatedPart = dynamic_cast<MimeTreeParser::EncapsulatedRfc822MessagePart *>(part);
if (encapsulatedPart) {
const auto parts = d->mEncapsulatedParts[encapsulatedPart];
if (row < parts.size()) {
return createIndex(row, column, parts.at(row).data());
}
}
return QModelIndex();
}
if (row < d->mParts.size()) {
return createIndex(row, column, d->mParts.at(row).data());
}
return QModelIndex();
}
SignatureInfo encryptionInfo(MimeTreeParser::MessagePart *messagePart)
{
SignatureInfo signatureInfo;
const auto encryptions = messagePart->encryptions();
if (encryptions.size() > 1) {
qWarning() << "Can't deal with more than one encryption";
}
for (const auto &encryptionPart : encryptions) {
signatureInfo.keyId = encryptionPart->partMetaData()->keyId;
signatureInfo.cryptoProto = encryptionPart->cryptoProto();
signatureInfo.decryptRecipients = encryptionPart->decryptRecipients();
}
return signatureInfo;
};
template<typename T>
const T *findHeader(KMime::Content *content)
{
if (!content) {
return {};
}
auto header = content->header<T>();
if (header || !content->parent()) {
return header;
}
return findHeader<T>(content->parent());
}
PartModel::SecurityLevel PartModel::signatureSecurityLevel(MimeTreeParser::MessagePart *messagePart)
{
auto signature = signatureFromMessagePart(messagePart);
if (!signature) {
return SecurityLevel::Unknow;
}
const auto summary = signature->summary();
if (summary & GpgME::Signature::Summary::Red) {
return SecurityLevel::Bad;
}
if (summary & GpgME::Signature::Summary::Valid) {
return SecurityLevel::Good;
}
return SecurityLevel::NotSoGood;
}
QString PartModel::signatureDetails(MimeTreeParser::MessagePart *messagePart)
{
auto signature = signatureFromMessagePart(messagePart);
if (!signature) {
return QString{};
}
// guess sender from mime node or parent node
auto from = findHeader<KMime::Headers::From>(messagePart->node());
if (from) {
const auto mailboxes = from->mailboxes();
if (!mailboxes.isEmpty()) {
auto mailBox = mailboxes.front();
if (mailBox.hasAddress()) {
return Kleo::Formatting::prettySignature(*signature, QString::fromUtf8(mailboxes.front().address()));
}
}
}
return Kleo::Formatting::prettySignature(*signature, {});
}
QVariant PartModel::data(const QModelIndex &index, int role) const
{
if (!index.isValid()) {
return QVariant();
}
if (index.internalPointer()) {
const auto messagePart = static_cast<MimeTreeParser::MessagePart *>(index.internalPointer());
// qWarning() << "Found message part " << messagePart->metaObject()->className() << messagePart->partMetaData()->status << messagePart->error();
Q_ASSERT(messagePart);
switch (role) {
case Qt::DisplayRole:
return QStringLiteral("Content%1");
case SenderRole: {
if (auto e = dynamic_cast<MimeTreeParser::EncapsulatedRfc822MessagePart *>(messagePart)) {
return e->from();
}
return {};
}
case DateRole: {
if (auto e = dynamic_cast<MimeTreeParser::EncapsulatedRfc822MessagePart *>(messagePart)) {
return e->date();
}
return {};
}
case TypeRole: {
if (messagePart->error()) {
return QVariant::fromValue(Types::Error);
}
if (dynamic_cast<MimeTreeParser::EncapsulatedRfc822MessagePart *>(messagePart)) {
return QVariant::fromValue(Types::Encapsulated);
}
if (auto alternativePart = dynamic_cast<MimeTreeParser::AlternativeMessagePart *>(messagePart)) {
if (alternativePart->availableModes().contains(MimeTreeParser::AlternativeMessagePart::MultipartIcal)) {
return QVariant::fromValue(Types::Ical);
}
}
if (auto attachmentPart = dynamic_cast<MimeTreeParser::AttachmentMessagePart *>(messagePart)) {
auto node = attachmentPart->node();
if (!node) {
qWarning() << "no content for attachment";
return {};
}
if (d->mMimeTypeCache[attachmentPart] == QByteArrayLiteral("text/calendar")) {
return QVariant::fromValue(Types::Ical);
}
}
if (!d->showHtml && d->containsHtmlAndPlain) {
return QVariant::fromValue(Types::Plain);
}
// For simple html we don't need a browser
auto complexHtml = [&] {
if (messagePart->isHtml()) {
const auto text = messagePart->htmlContent();
if (text.contains(QStringLiteral("<!DOCTYPE html PUBLIC"))) {
// We can probably deal with this if it adheres to the strict dtd
//(that's what our composer produces as well)
if (!text.contains(QStringLiteral("http://www.w3.org/TR/REC-html40/strict.dtd"))) {
return true;
}
}
// Blockquotes don't support any styling which would be necessary so they become readable.
if (text.contains(QStringLiteral("blockquote"))) {
return true;
}
// Media queries are too advanced
if (text.contains(QStringLiteral("@media"))) {
return true;
}
// auto css properties are not supported e.g margin-left: auto;
if (text.contains(QStringLiteral(": auto;"))) {
return true;
}
return false;
} else {
return false;
}
}();
if (complexHtml) {
return QVariant::fromValue(Types::Html);
}
return QVariant::fromValue(Types::Plain);
}
case IsEmbeddedRole:
return false;
case IsErrorRole:
return messagePart->error();
case ContentRole:
return d->contentForPart(messagePart);
case SidebarSecurityLevelRole: {
const auto signature = index.data(SignatureSecurityLevelRole).value<SecurityLevel>();
const auto encryption = index.data(EncryptionSecurityLevelRole).value<SecurityLevel>();
if (signature == SecurityLevel::Bad || encryption == SecurityLevel::Bad) {
return SecurityLevel::Bad;
}
if (signature == SecurityLevel::NotSoGood || encryption == SecurityLevel::NotSoGood) {
return SecurityLevel::NotSoGood;
}
if (signature == SecurityLevel::Good || encryption == SecurityLevel::Good) {
return SecurityLevel::Good;
}
return SecurityLevel::Unknow;
}
case SignatureSecurityLevelRole:
// Color displayed for the signature info box
return signatureSecurityLevel(messagePart);
case EncryptionSecurityLevelRole: {
// Color displayed for the encryption info box
const auto encryption = messagePart->encryptionState();
const bool messageIsEncrypted = encryption == MimeTreeParser::KMMsgPartiallyEncrypted || encryption == MimeTreeParser::KMMsgFullyEncrypted;
if (messagePart->error()) {
return SecurityLevel::Bad;
}
return messageIsEncrypted ? SecurityLevel::Good : SecurityLevel::Unknow;
}
case EncryptionIconNameRole: {
const auto encryption = messagePart->encryptionState();
const bool messageIsEncrypted = encryption == MimeTreeParser::KMMsgPartiallyEncrypted || encryption == MimeTreeParser::KMMsgFullyEncrypted;
if (messagePart->error()) {
return QStringLiteral("data-error");
}
return messageIsEncrypted ? QStringLiteral("mail-encrypted") : QString();
}
case SignatureIconNameRole: {
auto signature = signatureFromMessagePart(messagePart);
if (!signature) {
return QString{};
}
const auto summary = signature->summary();
if (summary & GpgME::Signature::Valid) {
return QStringLiteral("mail-signed");
} else if (summary & GpgME::Signature::Red) {
return QStringLiteral("data-error");
} else {
return QStringLiteral("data-warning");
}
}
case SignatureDetailsRole:
return signatureDetails(messagePart);
case EncryptionDetails:
return QVariant::fromValue(encryptionInfo(messagePart));
case ErrorType:
return messagePart->error();
case ErrorString: {
switch (messagePart->error()) {
case MimeTreeParser::MessagePart::NoKeyError: {
if (auto encryptedMessagePart = dynamic_cast<MimeTreeParser::EncryptedMessagePart *>(messagePart)) {
if (encryptedMessagePart->isNoSecKey()) {
QString errorMessage;
if (encryptedMessagePart->cryptoProto() == QGpgME::smime()) {
- errorMessage +=
- i18ndc("mimetreeparser", "@info:status", "You cannot decrypt this message.");
+ errorMessage += i18ndc("mimetreeparser", "@info:status", "You cannot decrypt this message.");
} else {
- errorMessage +=
- i18ndc("mimetreeparser", "@info:status", "You cannot decrypt this message.");
+ errorMessage += i18ndc("mimetreeparser", "@info:status", "You cannot decrypt this message.");
}
if (!encryptedMessagePart->decryptRecipients().empty()) {
errorMessage += QLatin1Char(' ')
+ i18ndcp("mimetreeparser",
"@info:status",
"The message is encrypted for the following recipient:",
"The message is encrypted for the following recipients:",
encryptedMessagePart->decryptRecipients().size());
errorMessage +=
MimeTreeParser::decryptRecipientsToHtml(encryptedMessagePart->decryptRecipients(), encryptedMessagePart->cryptoProto());
}
return errorMessage;
}
}
}
return messagePart->errorString();
case MimeTreeParser::MessagePart::PassphraseError:
return i18ndc("mimetreeparser", "@info:status", "Wrong passphrase.");
case MimeTreeParser::MessagePart::UnknownError:
break;
default:
break;
}
return messagePart->errorString();
}
}
}
return QVariant();
}
QModelIndex PartModel::parent(const QModelIndex &index) const
{
if (index.isValid()) {
if (auto indexPart = static_cast<MimeTreeParser::MessagePart *>(index.internalPointer())) {
for (const auto &part : std::as_const(d->mParts)) {
if (part.data() == indexPart) {
return QModelIndex();
}
}
const auto parentPart = d->mParents[indexPart];
Q_ASSERT(parentPart);
int row = 0;
const auto parts = d->mEncapsulatedParts[parentPart];
for (const auto &part : parts) {
if (part.data() == indexPart) {
break;
}
row++;
}
return createIndex(row, 0, parentPart);
}
return {};
}
return {};
}
int PartModel::rowCount(const QModelIndex &parent) const
{
if (parent.isValid()) {
const auto part = static_cast<MimeTreeParser::MessagePart *>(parent.internalPointer());
auto encapsulatedPart = dynamic_cast<MimeTreeParser::EncapsulatedRfc822MessagePart *>(part);
if (encapsulatedPart) {
const auto parts = d->mEncapsulatedParts[encapsulatedPart];
return parts.size();
}
return 0;
}
return d->mParts.count();
}
int PartModel::columnCount(const QModelIndex &) const
{
return 1;
}
#include "moc_partmodel.cpp"
File Metadata
Details
Attached
Mime Type
text/x-diff
Expires
Thu, Feb 26, 6:40 PM (14 h, 52 m)
Storage Engine
local-disk
Storage Format
Raw Data
Storage Handle
d5/58/0845033f93833e0a7851b446ccef
Attached To
rMTP MIME Tree Parser
Event Timeline
Log In to Comment