Page Menu
Home
GnuPG
Search
Configure Global Search
Log In
Files
F35134484
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Award Token
Size
28 KB
Subscribers
None
View Options
diff --git a/autotests/core/attachmentmodeltest.cpp b/autotests/core/attachmentmodeltest.cpp
index a6f02a2..65658d6 100644
--- a/autotests/core/attachmentmodeltest.cpp
+++ b/autotests/core/attachmentmodeltest.cpp
@@ -1,112 +1,97 @@
// SPDX-FileCopyrightText: 2023 g10 Code GmbH
// SPDX-FileContributor: Carl Schwan <carl.schwan@gnupg.com>
// SPDX-License-Identifier: LGPL-2.0-or-later
#include <QTest>
#include "attachmentmodel.h"
#include "messageparser.h"
#include <QAbstractItemModelTester>
#include <QSignalSpy>
#include <QTemporaryFile>
KMime::Message::Ptr readMailFromFile(const QString &mailFile)
{
QFile file(QLatin1String(MAIL_DATA_DIR) + QLatin1Char('/') + mailFile);
file.open(QIODevice::ReadOnly);
Q_ASSERT(file.isOpen());
auto mailData = KMime::CRLFtoLF(file.readAll());
KMime::Message::Ptr message(new KMime::Message);
message->setContent(mailData);
message->parse();
return message;
}
class AttachmentModelTest : public QObject
{
Q_OBJECT
private Q_SLOTS:
void openMailWithOneAttachementTest()
{
MessageParser messageParser;
messageParser.setMessage(readMailFromFile(QLatin1String("attachment.mbox")));
auto attachmentModel = messageParser.attachments();
new QAbstractItemModelTester(attachmentModel);
QCOMPARE(attachmentModel->rowCount(), 1);
QCOMPARE(attachmentModel->data(attachmentModel->index(0, 0), AttachmentModel::TypeRole).toString(), QStringLiteral("image/jpeg"));
QCOMPARE(attachmentModel->data(attachmentModel->index(0, 0), AttachmentModel::NameRole).toString(), QStringLiteral("aqnaozisxya.jpeg"));
QCOMPARE(attachmentModel->data(attachmentModel->index(0, 0), AttachmentModel::SizeRole).toString(), QStringLiteral("100.22 KB"));
QCOMPARE(attachmentModel->data(attachmentModel->index(0, 0), AttachmentModel::IsEncryptedRole).toBool(), false);
QCOMPARE(attachmentModel->data(attachmentModel->index(0, 0), AttachmentModel::IsSignedRole).toBool(), false);
QCOMPARE(attachmentModel->data(attachmentModel->index(0, AttachmentModel::IsEncryptedColumn), Qt::CheckStateRole).value<Qt::CheckState>(),
Qt::Unchecked);
QCOMPARE(attachmentModel->data(attachmentModel->index(0, AttachmentModel::IsSignedColumn), Qt::CheckStateRole).value<Qt::CheckState>(), Qt::Unchecked);
QCOMPARE(attachmentModel->data(attachmentModel->index(0, AttachmentModel::SizeColumn), Qt::DisplayRole).toString(), QStringLiteral("100.22 KB"));
}
void saveTest()
{
MessageParser messageParser;
messageParser.setMessage(readMailFromFile(QLatin1String("attachment.mbox")));
auto attachmentModel = messageParser.attachments();
QTemporaryFile file;
QVERIFY(file.open());
const auto fileName = attachmentModel->saveAttachmentToPath(0, file.fileName());
QFile file2(fileName);
QVERIFY(file2.open(QIODevice::ReadOnly | QIODevice::Text));
QVERIFY(!file2.readAll().isEmpty());
}
void openTest()
{
MessageParser messageParser;
messageParser.setMessage(readMailFromFile(QLatin1String("attachment.mbox")));
auto attachmentModel = messageParser.attachments();
QSignalSpy spy(attachmentModel, &AttachmentModel::errorOccurred);
QVERIFY(spy.isValid());
attachmentModel->openAttachment(0);
// Check no error occurred
QCOMPARE(spy.count(), 0);
}
- void saveReadonlyTest()
- {
- MessageParser messageParser;
- messageParser.setMessage(readMailFromFile(QLatin1String("attachment.mbox")));
-
- auto attachmentModel = messageParser.attachments();
- QTemporaryFile file;
- QVERIFY(file.open());
- const auto fileName = attachmentModel->saveAttachmentToPath(0, file.fileName(), true);
- QFile file2(fileName);
- QVERIFY(file2.open(QIODevice::ReadOnly | QIODevice::Text));
- QVERIFY(!file2.readAll().isEmpty());
- QVERIFY(file.permissions() & QFileDevice::ReadUser);
- }
-
void saveInvalidPathTest()
{
MessageParser messageParser;
messageParser.setMessage(readMailFromFile(QLatin1String("attachment.mbox")));
auto attachmentModel = messageParser.attachments();
QSignalSpy spy(attachmentModel, &AttachmentModel::errorOccurred);
QVERIFY(spy.isValid());
const auto fileName = attachmentModel->saveAttachmentToPath(0, QStringLiteral("/does/not/exist"));
QList<QVariant> arguments = spy.takeFirst();
QVERIFY(arguments.at(0).userType() == QMetaType::QString);
}
};
QTEST_MAIN(AttachmentModelTest)
#include "attachmentmodeltest.moc"
diff --git a/examples/widgets/main.cpp b/examples/widgets/main.cpp
index a8abada..fbf6700 100644
--- a/examples/widgets/main.cpp
+++ b/examples/widgets/main.cpp
@@ -1,29 +1,42 @@
// SPDX-FileCopyrightText: 2023 Carl Schwan <carl.schwan@gnupg.com>
// SPDX-License-Identifier: LGPL-2.0-or-later
#include <KLocalizedString>
#include <MimeTreeParserWidgets/MessageViewer>
#include <MimeTreeParserWidgets/MessageViewerDialog>
#include <QApplication>
#include <QCommandLineParser>
#include <QDir>
#include <QMainWindow>
#include <QUrl>
+#ifdef Q_OS_WIN
+#include <Windows.h>
+#endif
+
int main(int argc, char *argv[])
{
QApplication app(argc, argv);
+#ifdef Q_OS_WIN
+ if (AttachConsole(ATTACH_PARENT_PROCESS)) {
+ freopen("CONOUT$", "w", stdout);
+ freopen("CONOUT$", "w", stderr);
+ }
+#endif
+
KLocalizedString::setApplicationDomain("mimetreeparser");
QCommandLineParser parser;
parser.setApplicationDescription(i18n("View mbox file"));
parser.addPositionalArgument(QStringLiteral("file"), i18n("mbox file"));
parser.process(app);
const QStringList args = parser.positionalArguments();
const auto file = QUrl::fromUserInput(args.at(args.count() - 1), QDir::currentPath());
const auto messageViewer = new MimeTreeParser::Widgets::MessageViewerDialog(file.toLocalFile(), nullptr);
- return messageViewer->exec();
+ messageViewer->show();
+ messageViewer->setAttribute(Qt::WA_DeleteOnClose);
+ return app.exec();
}
diff --git a/src/core/attachmentmodel.cpp b/src/core/attachmentmodel.cpp
index 070626d..f169542 100644
--- a/src/core/attachmentmodel.cpp
+++ b/src/core/attachmentmodel.cpp
@@ -1,348 +1,403 @@
// SPDX-FileCopyrightText: 2016 Sandro Knauß <knauss@kolabsys.com>
// SPDX-FileCopyCopyright: 2017 Christian Mollekopf <mollekopf@kolabsys.com>
// SPDX-License-Identifier: LGPL-2.0-or-later
#include "attachmentmodel.h"
#include "mimetreeparser_core_debug.h"
#include "objecttreeparser.h"
#include <QGpgME/ImportJob>
#include <QGpgME/Protocol>
#include <KLocalizedString>
#include <KMime/Content>
#include <QDesktopServices>
#include <QDir>
#include <QFile>
#include <QGuiApplication>
#include <QIcon>
#include <QMimeDatabase>
#include <QMimeType>
#include <QRegularExpression>
#include <QStandardPaths>
#include <QTemporaryDir>
#include <QUrl>
+#ifdef Q_OS_WIN
+#include <stdio.h>
+#include <tchar.h>
+#include <windows.h>
+#endif
+
namespace
{
QString sizeHuman(float size)
{
QStringList list;
list << QStringLiteral("KB") << QStringLiteral("MB") << QStringLiteral("GB") << QStringLiteral("TB");
QStringListIterator i(list);
QString unit = QStringLiteral("Bytes");
while (size >= 1024.0 && i.hasNext()) {
unit = i.next();
size /= 1024.0;
}
if (unit == QStringLiteral("Bytes")) {
return QString().setNum(size) + QStringLiteral(" ") + unit;
} else {
return QString().setNum(size, 'f', 2) + QStringLiteral(" ") + unit;
}
}
// SPDX-SnippetBegin
// Copyright (C) 2016 The Qt Company Ltd.
// SPDX-License-Identifier: GPL-3.0-only
#define WINDOWS_DEVICES_PATTERN "(CON|AUX|PRN|NUL|COM[1-9]|LPT[1-9])(\\..*)?"
// Naming a file like a device name will break on Windows, even if it is
// "com1.txt". Since we are cross-platform, we generally disallow such file
// names.
const QRegularExpression &windowsDeviceNoSubDirPattern()
{
static const QRegularExpression rc(QStringLiteral("^" WINDOWS_DEVICES_PATTERN "$"), QRegularExpression::CaseInsensitiveOption);
Q_ASSERT(rc.isValid());
return rc;
}
const QRegularExpression &windowsDeviceSubDirPattern()
{
static const QRegularExpression rc(QStringLiteral("^.*[/\\\\]" WINDOWS_DEVICES_PATTERN "$"), QRegularExpression::CaseInsensitiveOption);
Q_ASSERT(rc.isValid());
return rc;
}
/* Validate a file base name, check for forbidden characters/strings. */
#define SLASHES "/\\"
static const char notAllowedCharsSubDir[] = ",^@={}[]~!?:&*\"|#%<>$\"'();`' ";
static const char notAllowedCharsNoSubDir[] = ",^@={}[]~!?:&*\"|#%<>$\"'();`' " SLASHES;
static const char *notAllowedSubStrings[] = {".."};
bool validateFileName(const QString &name, bool allowDirectories)
{
if (name.isEmpty()) {
return false;
}
// Characters
const char *notAllowedChars = allowDirectories ? notAllowedCharsSubDir : notAllowedCharsNoSubDir;
for (const char *c = notAllowedChars; *c; c++) {
if (name.contains(QLatin1Char(*c))) {
return false;
}
}
// Substrings
const int notAllowedSubStringCount = sizeof(notAllowedSubStrings) / sizeof(const char *);
for (int s = 0; s < notAllowedSubStringCount; s++) {
const QLatin1String notAllowedSubString(notAllowedSubStrings[s]);
if (name.contains(notAllowedSubString)) {
return false;
}
}
// Windows devices
bool matchesWinDevice = name.contains(windowsDeviceNoSubDirPattern());
if (!matchesWinDevice && allowDirectories) {
matchesWinDevice = name.contains(windowsDeviceSubDirPattern());
}
return !matchesWinDevice;
}
// SPDX-SnippetEnd
}
+#ifdef Q_OS_WIN
+struct WindowFile {
+ HANDLE handle;
+ wchar_t *fileName;
+ qsizetype fileNameSize;
+};
+#endif
+
+
class AttachmentModelPrivate
{
public:
AttachmentModelPrivate(AttachmentModel *q_ptr, const std::shared_ptr<MimeTreeParser::ObjectTreeParser> &parser);
AttachmentModel *q;
QMimeDatabase mimeDb;
std::shared_ptr<MimeTreeParser::ObjectTreeParser> mParser;
MimeTreeParser::MessagePart::List mAttachments;
+
+#ifdef Q_OS_WIN
+ QList<WindowFile> mOpenFiles;
+#endif
};
AttachmentModelPrivate::AttachmentModelPrivate(AttachmentModel *q_ptr, const std::shared_ptr<MimeTreeParser::ObjectTreeParser> &parser)
: q(q_ptr)
, mParser(parser)
{
mAttachments = mParser->collectAttachmentParts();
}
AttachmentModel::AttachmentModel(std::shared_ptr<MimeTreeParser::ObjectTreeParser> parser)
: QAbstractTableModel()
, d(std::unique_ptr<AttachmentModelPrivate>(new AttachmentModelPrivate(this, parser)))
{
}
AttachmentModel::~AttachmentModel()
{
+#ifdef Q_OS_WIN
+ for (const auto &file : std::as_const(d->mOpenFiles)) {
+ //CloseHandle(file.handle);
+ auto result = DeleteFileW(file.fileName);
+ if (!result) {
+ qWarning() << "Unable to destruct" << QString::fromWCharArray(file.fileName, file.fileNameSize) << result;
+ }
+
+ delete file.fileName;
+ }
+#endif
}
QHash<int, QByteArray> AttachmentModel::roleNames() const
{
return {
{TypeRole, QByteArrayLiteral("type")},
{NameRole, QByteArrayLiteral("name")},
{SizeRole, QByteArrayLiteral("size")},
{IconRole, QByteArrayLiteral("iconName")},
{IsEncryptedRole, QByteArrayLiteral("encrypted")},
{IsSignedRole, QByteArrayLiteral("signed")},
};
}
QVariant AttachmentModel::headerData(int section, Qt::Orientation orientation, int role) const
{
if (orientation == Qt::Horizontal && role == Qt::DisplayRole) {
switch (section) {
case NameColumn:
return i18ndc("mimetreeparser", "@title:column", "Name");
case SizeColumn:
return i18ndc("mimetreeparser", "@title:column", "Size");
case IsEncryptedColumn:
return i18ndc("mimetreeparser", "@title:column", "Encrypted");
case IsSignedColumn:
return i18ndc("mimetreeparser", "@title:column", "Signed");
}
}
return {};
}
QVariant AttachmentModel::data(const QModelIndex &index, int role) const
{
const auto row = index.row();
const auto column = index.column();
const auto part = d->mAttachments.at(row);
Q_ASSERT(part);
auto node = part->node();
if (!node) {
qWarning() << "no content for attachment";
return {};
}
const auto mimetype = d->mimeDb.mimeTypeForName(QString::fromLatin1(part->mimeType()));
const auto content = node->encodedContent();
switch (column) {
case NameColumn:
switch (role) {
case TypeRole:
return mimetype.name();
case Qt::DisplayRole:
case NameRole:
return part->filename();
case IconRole:
return mimetype.iconName();
case Qt::DecorationRole:
return QIcon::fromTheme(mimetype.iconName());
case SizeRole:
return sizeHuman(content.size());
case IsEncryptedRole:
return part->encryptions().size() > 0;
case IsSignedRole:
return part->signatures().size() > 0;
case AttachmentPartRole:
return QVariant::fromValue(part);
default:
return {};
}
case SizeColumn:
switch (role) {
case Qt::DisplayRole:
return sizeHuman(content.size());
default:
return {};
}
case IsEncryptedColumn:
switch (role) {
case Qt::CheckStateRole:
return part->encryptions().size() > 0 ? Qt::Checked : Qt::Unchecked;
default:
return {};
}
case IsSignedColumn:
switch (role) {
case Qt::CheckStateRole:
return part->signatures().size() > 0 ? Qt::Checked : Qt::Unchecked;
default:
return {};
}
default:
return {};
}
}
-QString AttachmentModel::saveAttachmentToPath(const int row, const QString &path, bool readonly)
+QString AttachmentModel::saveAttachmentToPath(const int row, const QString &path)
{
const auto part = d->mAttachments.at(row);
- return saveAttachmentToPath(part, path, readonly);
+ return saveAttachmentToPath(part, path);
}
-QString AttachmentModel::saveAttachmentToPath(const MimeTreeParser::MessagePart::Ptr &part, const QString &path, bool readonly)
+QString AttachmentModel::saveAttachmentToPath(const MimeTreeParser::MessagePart::Ptr &part, const QString &path)
{
Q_ASSERT(part);
auto node = part->node();
auto data = node->decodedContent();
// This is necessary to store messages embedded messages (EncapsulatedRfc822MessagePart)
if (data.isEmpty()) {
data = node->encodedContent();
}
if (part->isText()) {
// convert CRLF to LF before writing text attachments to disk
data = KMime::CRLFtoLF(data);
}
QFile f(path);
if (!f.open(QIODevice::ReadWrite)) {
qCWarning(MIMETREEPARSER_CORE_LOG) << "Failed to write attachment to file:" << path << " Error: " << f.errorString();
Q_EMIT errorOccurred(i18ndc("mimetreeparser", "@info", "Failed to save attachment."));
return {};
}
f.write(data);
- if (readonly) {
- // make file read-only so that nobody gets the impression that he migh edit attached files
- f.setPermissions(QFileDevice::ReadUser);
- }
f.close();
qCInfo(MIMETREEPARSER_CORE_LOG) << "Wrote attachment to file: " << path;
return path;
}
bool AttachmentModel::openAttachment(const int row)
{
const auto part = d->mAttachments.at(row);
return openAttachment(part);
}
bool AttachmentModel::openAttachment(const MimeTreeParser::MessagePart::Ptr &message)
{
QString fileName = message->filename();
QTemporaryDir tempDir(QDir::tempPath() + QLatin1Char('/') + qGuiApp->applicationName() + QStringLiteral(".XXXXXX"));
// TODO: We need some cleanup here. Otherwise the files will stay forever on Windows.
tempDir.setAutoRemove(false);
if (message->filename().isEmpty() || !validateFileName(fileName, false)) {
const auto mimetype = d->mimeDb.mimeTypeForName(QString::fromLatin1(message->mimeType()));
fileName = tempDir.filePath(i18n("attachment") + QLatin1Char('.') + mimetype.preferredSuffix());
} else {
fileName = tempDir.filePath(message->filename());
}
- const auto filePath = saveAttachmentToPath(message, fileName, true);
+ const auto filePath = saveAttachmentToPath(message, fileName);
if (filePath.isEmpty()) {
Q_EMIT errorOccurred(i18ndc("mimetreeparser", "@info", "Failed to write attachment for opening."));
return false;
}
+#ifdef Q_OS_WIN
+ wchar_t *fileNameStr = static_cast<wchar_t *>(calloc(filePath.count() + 1, sizeof(wchar_t)));
+ const auto fileNameSize = filePath.toWCharArray(fileNameStr);
+ fileNameStr[fileNameSize] = L'\0';
+
+ HANDLE hFile = CreateFileW(fileNameStr,
+ GENERIC_READ,
+ FILE_SHARE_READ | FILE_SHARE_DELETE, // allow other processes to delete it
+ NULL,
+ OPEN_EXISTING,
+ FILE_ATTRIBUTE_NORMAL,
+ NULL // no template
+ );
+
+ if (hFile == INVALID_HANDLE_VALUE) {
+ Q_EMIT errorOccurred(i18ndc("mimetreeparser", "@info", "Failed to open attachment."));
+ QFile file(fileName);
+ file.remove();
+ return false;
+ }
+
+ d->mOpenFiles << WindowFile{
+ hFile,
+ fileNameStr,
+ fileNameSize,
+ };
+#endif
+
if (!QDesktopServices::openUrl(QUrl::fromLocalFile(filePath))) {
Q_EMIT errorOccurred(i18ndc("mimetreeparser", "@info", "Failed to open attachment."));
return false;
}
+
return true;
}
bool AttachmentModel::importPublicKey(const int row)
{
const auto part = d->mAttachments.at(row);
return importPublicKey(part);
}
bool AttachmentModel::importPublicKey(const MimeTreeParser::MessagePart::Ptr &part)
{
Q_ASSERT(part);
const QByteArray certData = part->node()->decodedContent();
QGpgME::ImportJob *importJob = QGpgME::openpgp()->importJob();
connect(importJob, &QGpgME::AbstractImportJob::result, this, [this](const GpgME::ImportResult &result) {
if (result.numConsidered() == 0) {
Q_EMIT errorOccurred(i18ndc("mimetreeparser", "@info", "No keys were found in this attachment"));
return;
} else {
QString message = i18ndcp("mimetreeparser", "@info", "one key imported", "%1 keys imported", result.numImported());
if (result.numUnchanged() != 0) {
message += QStringLiteral("\n")
+ i18ndcp("mimetreeparser", "@info", "one key was already imported", "%1 keys were already imported", result.numUnchanged());
}
Q_EMIT info(message);
}
});
GpgME::Error err = importJob->start(certData);
return !err;
}
int AttachmentModel::rowCount(const QModelIndex &parent) const
{
if (!parent.isValid()) {
return d->mAttachments.size();
}
return 0;
}
int AttachmentModel::columnCount(const QModelIndex &parent) const
{
if (!parent.isValid()) {
return ColumnCount;
}
return 0;
}
diff --git a/src/core/attachmentmodel.h b/src/core/attachmentmodel.h
index c35bde4..8f24659 100644
--- a/src/core/attachmentmodel.h
+++ b/src/core/attachmentmodel.h
@@ -1,67 +1,67 @@
// SPDX-FileCopyrightText: 2016 Sandro Knauß <knauss@kolabsys.com>
// SPDX-FileCopyCopyright: 2017 Christian Mollekopf <mollekopf@kolabsys.com>
// SPDX-License-Identifier: LGPL-2.0-or-later
#pragma once
#include "mimetreeparser_core_export.h"
#include "objecttreeparser.h"
#include <QAbstractTableModel>
#include <QModelIndex>
#include <memory>
namespace MimeTreeParser
{
class ObjectTreeParser;
}
class AttachmentModelPrivate;
class MIMETREEPARSER_CORE_EXPORT AttachmentModel : public QAbstractTableModel
{
Q_OBJECT
public:
AttachmentModel(std::shared_ptr<MimeTreeParser::ObjectTreeParser> parser);
~AttachmentModel();
public:
enum Roles {
TypeRole = Qt::UserRole + 1,
IconRole,
NameRole,
SizeRole,
IsEncryptedRole,
IsSignedRole,
AttachmentPartRole,
};
enum Columns {
NameColumn = 0,
SizeColumn,
IsEncryptedColumn,
IsSignedColumn,
ColumnCount,
};
QHash<int, QByteArray> roleNames() const override;
QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;
QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const override;
int rowCount(const QModelIndex &parent = QModelIndex()) const override;
int columnCount(const QModelIndex &parent = QModelIndex()) const override;
Q_INVOKABLE bool openAttachment(const int row);
Q_INVOKABLE bool importPublicKey(const int row);
bool openAttachment(const MimeTreeParser::MessagePart::Ptr &message);
bool importPublicKey(const MimeTreeParser::MessagePart::Ptr &message);
- Q_INVOKABLE QString saveAttachmentToPath(const int row, const QString &path, bool readonly = false);
- QString saveAttachmentToPath(const MimeTreeParser::MessagePart::Ptr &part, const QString &path, bool readonly = false);
+ Q_INVOKABLE QString saveAttachmentToPath(const int row, const QString &path);
+ QString saveAttachmentToPath(const MimeTreeParser::MessagePart::Ptr &part, const QString &path);
Q_SIGNALS:
void info(const QString &message);
void errorOccurred(const QString &message);
private:
std::unique_ptr<AttachmentModelPrivate> d;
};
diff --git a/src/core/messageparser.cpp b/src/core/messageparser.cpp
index 4839fd9..fef6c6b 100644
--- a/src/core/messageparser.cpp
+++ b/src/core/messageparser.cpp
@@ -1,222 +1,224 @@
// SPDX-FileCopyrightText: 2016 Christian Mollekopf <mollekopf@kolabsys.com>
// SPDX-License-Identifier: LGPL-2.0-or-later
#include "messageparser.h"
#include "attachmentmodel.h"
#include "mimetreeparser_core_debug.h"
#include "objecttreeparser.h"
#include "partmodel.h"
#include <KLocalizedString>
#include <QElapsedTimer>
namespace
{
template<typename T>
const T *findHeader(KMime::Content *content, KMime::Content *protectedHeaderNode)
{
if (protectedHeaderNode) {
auto header = protectedHeaderNode->header<T>();
if (header) {
return header;
}
}
auto header = content->header<T>();
if (header || !content->parent()) {
return header;
}
return findHeader<T>(content->parent(), nullptr);
}
const KMime::Headers::Base *findHeader(KMime::Content *content, const char *headerType)
{
const auto header = content->headerByType(headerType);
if (header || !content->parent()) {
return header;
}
return findHeader(content->parent(), headerType);
}
}
class MessagePartPrivate
{
public:
std::shared_ptr<MimeTreeParser::ObjectTreeParser> mParser;
KMime::Message::Ptr mMessage;
KMime::Content *protectedHeaderNode = nullptr;
std::unique_ptr<KMime::Content> ownedContent;
};
MessageParser::MessageParser(QObject *parent)
: QObject(parent)
, d(std::unique_ptr<MessagePartPrivate>(new MessagePartPrivate))
{
}
MessageParser::~MessageParser()
{
}
KMime::Message::Ptr MessageParser::message() const
{
return d->mMessage;
}
void MessageParser::setMessage(const KMime::Message::Ptr message)
{
if (message == d->mMessage) {
return;
}
if (!message) {
qCWarning(MIMETREEPARSER_CORE_LOG) << Q_FUNC_INFO << "Empty message given";
return;
}
d->mMessage = message;
QElapsedTimer time;
time.start();
auto parser = std::make_shared<MimeTreeParser::ObjectTreeParser>();
parser->parseObjectTree(message.data());
qCDebug(MIMETREEPARSER_CORE_LOG) << "Message parsing took: " << time.elapsed();
parser->decryptParts();
qCDebug(MIMETREEPARSER_CORE_LOG) << "Message parsing and decryption/verification: " << time.elapsed();
d->mParser = parser;
const auto contentParts = parser->collectContentParts();
for (const auto &part : contentParts) {
const auto contentType = part->node()->contentType();
if (contentType && contentType->hasParameter(QStringLiteral("protected-headers"))) {
const auto contentDisposition = part->node()->contentDisposition();
// Check for legacy format for protected-headers
if (contentDisposition && contentDisposition->disposition() == KMime::Headers::CDinline) {
d->ownedContent = std::make_unique<KMime::Content>();
// we put the decoded content as encoded content of the new node
// as the header are inline in part->node()
d->ownedContent->setContent(part->node()->decodedContent());
d->ownedContent->parse();
d->protectedHeaderNode = d->ownedContent.get();
break;
}
d->protectedHeaderNode = part->node();
break;
}
}
Q_EMIT htmlChanged();
}
bool MessageParser::loaded() const
{
return bool{d->mParser};
}
QString MessageParser::structureAsString() const
{
if (!d->mParser) {
return {};
}
return d->mParser->structureAsString();
}
PartModel *MessageParser::parts() const
{
if (!d->mParser) {
return nullptr;
}
const auto model = new PartModel(d->mParser);
return model;
}
AttachmentModel *MessageParser::attachments() const
{
if (!d->mParser) {
return nullptr;
}
- return new AttachmentModel(d->mParser);
+ auto attachmentModel = new AttachmentModel(d->mParser);
+ attachmentModel->setParent(const_cast<MessageParser *>(this));
+ return attachmentModel;
}
QString MessageParser::subject() const
{
if (d->mMessage) {
const auto header = findHeader<KMime::Headers::Subject>(d->mMessage.get(), d->protectedHeaderNode);
if (header) {
return header->asUnicodeString();
}
}
return QString();
}
QString MessageParser::from() const
{
if (d->mMessage) {
const auto header = findHeader<KMime::Headers::From>(d->mMessage.get(), d->protectedHeaderNode);
if (header) {
return header->displayString();
}
}
return QString();
}
QString MessageParser::sender() const
{
if (d->mMessage) {
const auto header = findHeader<KMime::Headers::Sender>(d->mMessage.get(), d->protectedHeaderNode);
if (header) {
return header->displayString();
}
}
return QString();
}
QString MessageParser::to() const
{
if (d->mMessage) {
const auto header = findHeader<KMime::Headers::To>(d->mMessage.get(), d->protectedHeaderNode);
if (!header) {
return {};
}
return header->displayString();
}
return QString();
}
QString MessageParser::cc() const
{
if (d->mMessage) {
const auto header = findHeader<KMime::Headers::Cc>(d->mMessage.get(), d->protectedHeaderNode);
if (!header) {
return {};
}
return header->displayString();
}
return QString();
}
QString MessageParser::bcc() const
{
if (d->mMessage) {
const auto header = findHeader<KMime::Headers::Bcc>(d->mMessage.get(), d->protectedHeaderNode);
if (!header) {
return {};
}
return header->displayString();
}
return QString();
}
QDateTime MessageParser::date() const
{
if (d->mMessage) {
const auto header = findHeader<KMime::Headers::Date>(d->mMessage.get(), d->protectedHeaderNode);
if (header) {
return header->dateTime();
}
}
return QDateTime();
}
File Metadata
Details
Attached
Mime Type
text/x-diff
Expires
Sun, Feb 1, 7:28 PM (5 h, 38 m)
Storage Engine
local-disk
Storage Format
Raw Data
Storage Handle
57/fe/a0615d87e7b56e4ea8095fab5522
Attached To
rMTP MIME Tree Parser
Event Timeline
Log In to Comment