Page MenuHome GnuPG

No OneTemporary

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

Mime Type
text/x-diff
Expires
Sun, Feb 1, 7:42 PM (10 h, 57 m)
Storage Engine
local-disk
Storage Format
Raw Data
Storage Handle
57/fe/a0615d87e7b56e4ea8095fab5522

Event Timeline