Page Menu
Home
GnuPG
Search
Configure Global Search
Log In
Files
F34102364
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Award Token
Size
27 KB
Subscribers
None
View Options
diff --git a/src/core/autotests/fileopenertest.cpp b/src/core/autotests/fileopenertest.cpp
index 4e1b315..0be9ab8 100644
--- a/src/core/autotests/fileopenertest.cpp
+++ b/src/core/autotests/fileopenertest.cpp
@@ -1,57 +1,58 @@
-// SPDX-FileCopyrightText: 2023 Carl Schwan <carl@carlschwan.eu>
+// SPDX-FileCopyrightText: 2023 g10 Code GmbH
+// SPDX-FileContributor: Carl Schwan <carl@carlschwan.eu>
// SPDX-License-Identifier: LGPL-2.0-or-later
#include <MimeTreeParserCore/FileOpener>
#include <QDebug>
#include <QTest>
using namespace MimeTreeParser::Core;
class FileOpenerTest : public QObject
{
Q_OBJECT
private Q_SLOTS:
void openSingleMboxTest()
{
const auto messages = FileOpener::openFile(QLatin1String(MAIL_DATA_DIR) + QLatin1Char('/') + QLatin1String("smime-opaque-enc+sign.mbox"));
QCOMPARE(messages.count(), 1);
}
void openSingleCombinedTest()
{
const auto messages = FileOpener::openFile(QLatin1String(MAIL_DATA_DIR) + QLatin1Char('/') + QLatin1String("combined.mbox"));
QCOMPARE(messages.count(), 3);
}
void openAscTest()
{
const auto messages = FileOpener::openFile(QLatin1String(MAIL_DATA_DIR) + QLatin1Char('/') + QLatin1String("msg.asc"));
QCOMPARE(messages.count(), 1);
auto message = messages[0];
QCOMPARE(message->contentType()->mimeType(), "multipart/encrypted");
QCOMPARE(message->contents().count(), 2);
auto pgpPart = message->contents()[0];
QCOMPARE(pgpPart->contentType()->mimeType(), "application/pgp-encrypted");
auto octetStreamPart = message->contents()[1];
QCOMPARE(octetStreamPart->contentType()->mimeType(), "application/octet-stream");
}
void openSmimeTest()
{
const auto messages = FileOpener::openFile(QLatin1String(MAIL_DATA_DIR) + QLatin1Char('/') + QLatin1String("smime.p7m"));
QCOMPARE(messages.count(), 1);
auto message = messages[0];
QCOMPARE(message->contentType()->mimeType(), "application/pkcs7-mime");
QCOMPARE(message->contentType()->parameter(QStringLiteral("smime-type")), QStringLiteral("enveloped-data"));
QCOMPARE(message->contentDisposition()->filename(), QStringLiteral("smime.p7m"));
}
};
QTEST_GUILESS_MAIN(FileOpenerTest)
#include "fileopenertest.moc"
diff --git a/src/core/fileopener.cpp b/src/core/fileopener.cpp
index 0ac8b26..574022c 100644
--- a/src/core/fileopener.cpp
+++ b/src/core/fileopener.cpp
@@ -1,128 +1,129 @@
-// SPDX-FileCopyrigthText: 2023 Carl Schwan <carl@carlschwan.eu>
+// SPDX-FileCopyrightText: 2023 g10 Code GmbH
+// SPDX-FileContributor: Carl Schwan <carl.schwan@gnupg.com>
// SPDX-License-Identifier: LGPL-2.0-or-later
#include "fileopener.h"
#include "mimetreeparser_core_debug.h"
#include <QFile>
#include <QMimeDatabase>
#include <QMimeType>
using namespace MimeTreeParser::Core;
QVector<KMime::Message::Ptr> FileOpener::openFile(const QString &fileName)
{
QMimeDatabase db;
QMimeType mime = db.mimeTypeForFile(fileName);
KMime::Message::Ptr message(new KMime::Message);
QFile file(fileName);
file.open(QIODevice::ReadOnly);
if (!file.isOpen()) {
qCWarning(MIMETREEPARSER_CORE_LOG) << "Could not open file";
return {};
}
const auto content = file.readAll();
if (content.length() == 0) {
qCWarning(MIMETREEPARSER_CORE_LOG) << "File is empty";
return {};
}
if (mime.inherits(QStringLiteral("application/pkcs7-mime")) || fileName.endsWith(QStringLiteral("smime.p7m"))) {
auto contentType = message->contentType();
contentType->setMimeType("application/pkcs7-mime");
contentType->setParameter(QStringLiteral("smime-type"), QStringLiteral("enveloped-data"));
auto contentDisposition = new KMime::Headers::ContentDisposition;
contentDisposition->setDisposition(KMime::Headers::CDattachment);
contentDisposition->setFilename(QStringLiteral("smime.p7m"));
message->appendHeader(contentDisposition);
auto cte = message->contentTransferEncoding();
cte->setEncoding(KMime::Headers::CE7Bit);
cte->setDecoded(true);
message->setBody(content);
message->assemble();
} else if (mime.inherits(QStringLiteral("application/pgp-encrypted")) || fileName.endsWith(QStringLiteral(".asc"))) {
auto contentType = message->contentType();
contentType->setMimeType("multipart/encrypted");
contentType->setBoundary(KMime::multiPartBoundary());
contentType->setParameter(QStringLiteral("protocol"), QStringLiteral("application/pgp-encrypted"));
contentType->setCategory(KMime::Headers::CCcontainer);
auto cte = message->contentTransferEncoding();
cte->setEncoding(KMime::Headers::CE7Bit);
cte->setDecoded(true);
auto pgpEncrypted = new KMime::Content;
pgpEncrypted->contentType()->setMimeType("application/pgp-encrypted");
auto contentDisposition = new KMime::Headers::ContentDisposition;
contentDisposition->setDisposition(KMime::Headers::CDattachment);
pgpEncrypted->appendHeader(contentDisposition);
pgpEncrypted->setBody("Version: 1");
message->addContent(pgpEncrypted);
auto encryptedContent = new KMime::Content;
encryptedContent->contentType()->setMimeType("application/octet-stream");
contentDisposition = new KMime::Headers::ContentDisposition;
contentDisposition->setDisposition(KMime::Headers::CDinline);
contentDisposition->setFilename(QStringLiteral("msg.asc"));
encryptedContent->appendHeader(contentDisposition);
encryptedContent->setBody(content);
message->addContent(encryptedContent);
message->assemble();
} else {
int startOfMessage = 0;
if (content.startsWith("From ")) {
startOfMessage = content.indexOf('\n');
if (startOfMessage == -1) {
return {};
}
startOfMessage += 1; // the message starts after the '\n'
}
QVector<KMime::Message::Ptr> listMessages;
// check for multiple messages in the file
int endOfMessage = content.indexOf("\nFrom ", startOfMessage);
while (endOfMessage != -1) {
if (content.indexOf("From ", startOfMessage) == startOfMessage) {
startOfMessage = content.indexOf('\n', startOfMessage) + 1;
}
auto msg = new KMime::Message;
msg->setContent(KMime::CRLFtoLF(content.mid(startOfMessage, endOfMessage - startOfMessage)));
msg->parse();
if (!msg->hasContent()) {
delete msg;
msg = nullptr;
return {};
}
KMime::Message::Ptr mMsg(msg);
listMessages << mMsg;
startOfMessage = endOfMessage + 1;
endOfMessage = content.indexOf("\nFrom ", startOfMessage);
}
if (endOfMessage == -1) {
if (content.indexOf("From ", startOfMessage) == startOfMessage) {
startOfMessage = content.indexOf('\n', startOfMessage) + 1;
}
endOfMessage = content.length();
auto msg = new KMime::Message;
msg->setContent(KMime::CRLFtoLF(content.mid(startOfMessage, endOfMessage - startOfMessage)));
msg->parse();
if (!msg->hasContent()) {
delete msg;
msg = nullptr;
return {};
}
KMime::Message::Ptr mMsg(msg);
listMessages << mMsg;
}
return listMessages;
}
return {message};
}
diff --git a/src/core/fileopener.h b/src/core/fileopener.h
index a222173..2972588 100644
--- a/src/core/fileopener.h
+++ b/src/core/fileopener.h
@@ -1,20 +1,21 @@
-// SPDX-FileCopyrigthText: 2023 Carl Schwan <carl@carlschwan.eu>
+// SPDX-FileCopyrightText: 2023 g10 Code GmbH
+// SPDX-FileContributor: Carl Schwan <carl.schwan@gnupg.com>
// SPDX-License-Identifier: LGPL-2.0-or-later
#pragma once
#include "mimetreeparser_core_export.h"
#include <KMime/Message>
namespace MimeTreeParser
{
namespace Core
{
namespace FileOpener
{
/// Open messages from file
QVector<KMime::Message::Ptr> MIMETREEPARSER_CORE_EXPORT openFile(const QString &fileName);
}
}
}
diff --git a/src/widgets/CMakeLists.txt b/src/widgets/CMakeLists.txt
index 1bbf460..2fb4974 100644
--- a/src/widgets/CMakeLists.txt
+++ b/src/widgets/CMakeLists.txt
@@ -1,155 +1,158 @@
-# SPDX-FileCopyrightText: 2023 Carl Schwan <carl.schwan@gnupg.com>
+# SPDX-FileCopyrightText: 2023 g10 Code GmbH
+# SPDX-FileContributor: Carl Schwan <carlschwan@kde.org>
# SPDX-License-Identifier: BSD-3-Clause
ecm_setup_version(PROJECT
VARIABLE_PREFIX MIMETREEPARSER_WIDGETS
VERSION_HEADER "${CMAKE_CURRENT_BINARY_DIR}/mimetreeparser_widgets_version.h"
PACKAGE_VERSION_FILE "${CMAKE_CURRENT_BINARY_DIR}/KPim${KF_MAJOR_VERSION}MimeTreeParserWidgetsConfigVersion.cmake"
SOVERSION 5
)
add_library(KPim${KF_MAJOR_VERSION}MimeTreeParserWidgets)
add_library(KPim${KF_MAJOR_VERSION}::MimeTreeParserWidgets
ALIAS KPim${KF_MAJOR_VERSION}MimeTreeParserWidgets)
target_sources(KPim${KF_MAJOR_VERSION}MimeTreeParserWidgets PRIVATE
attachmentview_p.h
attachmentview.cpp
messagecontainerwidget_p.h
messagecontainerwidget.cpp
messageviewer.h
messageviewer.cpp
messageviewerdialog.h
messageviewerdialog.cpp
urlhandler_p.h
urlhandler.cpp
)
ecm_qt_declare_logging_category(KPim${KF_MAJOR_VERSION}MimeTreeParserWidgets
HEADER mimetreeparser_widgets_debug.h
IDENTIFIER MIMETREEPARSER_WIDGET_LOG
CATEGORY_NAME org.kde.pim.mimetreeparser.widgets
DESCRIPTION "mimetreeparser (pim lib)"
EXPORT MIMETREEPARSER
)
if (COMPILE_WITH_UNITY_CMAKE_SUPPORT)
set_target_properties(KPim${KF_MAJOR_VERSION}MimeTreeParserWidgets PROPERTIES UNITY_BUILD ON)
endif()
generate_export_header(KPim${KF_MAJOR_VERSION}MimeTreeParserWidgets BASE_NAME mimetreeparser_widgets)
target_include_directories(KPim${KF_MAJOR_VERSION}MimeTreeParserWidgets INTERFACE "$<INSTALL_INTERFACE:${KDE_INSTALL_INCLUDEDIR}/KPim${KF_MAJOR_VERSION}/MimeTreeParserWidgets>")
target_link_libraries(KPim${KF_MAJOR_VERSION}MimeTreeParserWidgets
PUBLIC
Qt${KF_MAJOR_VERSION}::Widgets
KPim${KF_MAJOR_VERSION}::MimeTreeParserCore
PRIVATE
Qt${KF_MAJOR_VERSION}::PrintSupport
KF${KF_MAJOR_VERSION}::Codecs
KF${KF_MAJOR_VERSION}::I18n
KF${KF_MAJOR_VERSION}::CalendarCore
KF${KF_MAJOR_VERSION}::WidgetsAddons
KPim${KF_MAJOR_VERSION}::Libkleo
Gpgme::Gpgme
)
set_target_properties(KPim${KF_MAJOR_VERSION}MimeTreeParserWidgets PROPERTIES
VERSION ${MIMETREEPARSERNG_VERSION}
SOVERSION ${MIMETREEPARSERNG_SOVERSION}
EXPORT_NAME MimeTreeParserWidgets
)
ecm_generate_pri_file(BASE_NAME MimeTreeParserWidgets
LIB_NAME KPim${KF_MAJOR_VERSION}MimeTreeParserWidgets
DEPS "MimeTreeParserWidgets"
FILENAME_VAR PRI_FILENAME
)
install(FILES ${PRI_FILENAME} DESTINATION ${ECM_MKSPECS_INSTALL_DIR})
install(TARGETS
KPim${KF_MAJOR_VERSION}MimeTreeParserWidgets
EXPORT KPim${KF_MAJOR_VERSION}MimeTreeParserWidgetsTargets ${KDE_INSTALL_TARGETS_DEFAULT_ARGS}
)
ecm_generate_headers(MimeTreeParserWidgets_CamelCase_HEADERS
HEADER_NAMES
MessageViewer
MessageViewerDialog
REQUIRED_HEADERS MimeTreeParserWidgets_HEADERS
PREFIX MimeTreeParserWidgets
)
install(FILES
${MimeTreeParserWidgets_CamelCase_HEADERS}
DESTINATION ${KDE_INSTALL_INCLUDEDIR}/KPim${KF_MAJOR_VERSION}/MimeTreeParserWidgets/MimeTreeParserWidgets
COMPONENT Devel
)
install(FILES
${CMAKE_CURRENT_BINARY_DIR}/mimetreeparser_widgets_export.h
${MimeTreeParserWidgets_HEADERS}
DESTINATION ${KDE_INSTALL_INCLUDEDIR}/KPim${KF_MAJOR_VERSION}/MimeTreeParserWidgets/mimetreeparserwidgets
COMPONENT Devel
)
if (BUILD_QCH)
ecm_add_qch(
KPim${KF_MAJOR_VERSION}MimeTreeParserWidgets_QCH
NAME MimeTreeParserWidgets
BASE_NAME KPim${KF_MAJOR_VERSION}MimeTreeParserWidgets
VERSION ${PIM_VERSION}
ORG_DOMAIN org.kde
# using only public headers, to cover only public API
SOURCES ${MimeTreeParserWidgets_HEADERS}
MD_MAINPAGE "${CMAKE_SOURCE_DIR}/README.md"
LINK_QCHS
Qt${QT_MAJOR_VERSION}Core_QCH
INCLUDE_DIRS
${CMAKE_CURRENT_BINARY_DIR}
BLANK_MACROS
MIMETREEPARSER_WIDGETS_EXPORT
TAGFILE_INSTALL_DESTINATION ${KDE_INSTALL_QTQCHDIR}
QCH_INSTALL_DESTINATION ${KDE_INSTALL_QTQCHDIR}
COMPONENT Devel
)
endif()
########### CMake Config Files ###########
set(CMAKECONFIG_INSTALL_DIR "${KDE_INSTALL_CMAKEPACKAGEDIR}/KPim${KF_MAJOR_VERSION}MimeTreeParserWidgets")
if (BUILD_QCH)
ecm_install_qch_export(
TARGETS KPim${KF_MAJOR_VERSION}MimeTreeParserWidgets_QCH
FILE KPim${KF_MAJOR_VERSION}MimeTreeParserWidgetsQchTargets.cmake
DESTINATION "${CMAKECONFIG_INSTALL_DIR}"
COMPONENT Devel
)
set(PACKAGE_INCLUDE_QCHTARGETS "include(\"\${CMAKE_CURRENT_LIST_DIR}/KPim${KF_MAJOR_VERSION}MimeTreeParserWidgetsQchTargets.cmake\")")
endif()
configure_package_config_file(
"${CMAKE_CURRENT_SOURCE_DIR}/KPimMimeTreeParserWidgetsConfig.cmake.in"
"${CMAKE_CURRENT_BINARY_DIR}/KPim${KF_MAJOR_VERSION}MimeTreeParserWidgetsConfig.cmake"
INSTALL_DESTINATION ${CMAKECONFIG_INSTALL_DIR}
)
install(FILES
"${CMAKE_CURRENT_BINARY_DIR}/KPim${KF_MAJOR_VERSION}MimeTreeParserWidgetsConfig.cmake"
"${CMAKE_CURRENT_BINARY_DIR}/KPim${KF_MAJOR_VERSION}MimeTreeParserWidgetsConfigVersion.cmake"
DESTINATION "${CMAKECONFIG_INSTALL_DIR}"
COMPONENT Devel
)
install(EXPORT KPim${KF_MAJOR_VERSION}MimeTreeParserWidgetsTargets
DESTINATION "${CMAKECONFIG_INSTALL_DIR}"
FILE KPim${KF_MAJOR_VERSION}MimeTreeParserWidgetsTargets.cmake
NAMESPACE KPim${KF_MAJOR_VERSION}::
)
install(FILES
${CMAKE_CURRENT_BINARY_DIR}/mimetreeparser_widgets_version.h
DESTINATION ${KDE_INSTALL_INCLUDEDIR}/KPim${KF_MAJOR_VERSION}/MimeTreeParserWidgets
COMPONENT Devel
)
+
+add_subdirectory(autotests)
diff --git a/src/widgets/autotests/CMakeLists.txt b/src/widgets/autotests/CMakeLists.txt
new file mode 100644
index 0000000..95d9c44
--- /dev/null
+++ b/src/widgets/autotests/CMakeLists.txt
@@ -0,0 +1,10 @@
+# SPDX-FileCopyrightText: 2023 g10 Code GmbH
+# SPDX-FileContributor: Carl Schwan <carlschwan@kde.org>
+# SPDX-License-Identifier: BSD-3-Clause
+
+add_definitions(-DMAIL_DATA_DIR="${CMAKE_CURRENT_SOURCE_DIR}/../../core/testdata")
+
+ecm_add_test(messageviewerdialogtest.cpp
+ LINK_LIBRARIES KPim${KF_MAJOR_VERSION}MimeTreeParserWidgets Qt::Test
+ NAME_PREFIX "widget-"
+)
diff --git a/src/widgets/autotests/messageviewerdialogtest.cpp b/src/widgets/autotests/messageviewerdialogtest.cpp
new file mode 100644
index 0000000..3202ff8
--- /dev/null
+++ b/src/widgets/autotests/messageviewerdialogtest.cpp
@@ -0,0 +1,41 @@
+// SPDX-FileCopyrightText: 2023 g10 Code GmbH
+// SPDX-FileContributor: Carl Schwan <carl@carlschwan.eu>
+// SPDX-License-Identifier: LGPL-2.0-or-later
+
+#include <MimeTreeParserWidgets/MessageViewerDialog>
+#include <QLayout>
+#include <QTest>
+
+using namespace MimeTreeParser::Widgets;
+
+class MessageViewerDialogTest : public QObject
+{
+ Q_OBJECT
+
+private Q_SLOTS:
+ void messageViewerDialogCreationMultipleTest()
+ {
+ MessageViewerDialog dialog(QLatin1String(MAIL_DATA_DIR) + QLatin1Char('/') + QLatin1String("combined.mbox"));
+ QCOMPARE(dialog.messages().count(), 3);
+
+ QCOMPARE(dialog.layout()->count(), 2);
+ QVERIFY(dialog.layout()->itemAt(1)->layout());
+ QCOMPARE(dialog.layout()->itemAt(1)->layout()->count(), 2);
+
+ const auto actions = dialog.layout()->menuBar()->actions();
+ QCOMPARE(actions.count(), 2);
+ }
+
+ void messageViewerDialogCreationTest()
+ {
+ MessageViewerDialog dialog(QLatin1String(MAIL_DATA_DIR) + QLatin1Char('/') + QLatin1String("plaintext.mbox"));
+ QCOMPARE(dialog.messages().count(), 1);
+
+ QCOMPARE(dialog.layout()->count(), 1); // no toolbar here
+ QVERIFY(dialog.layout()->itemAt(0)->layout());
+ QCOMPARE(dialog.layout()->itemAt(0)->layout()->count(), 2);
+ }
+};
+
+QTEST_MAIN(MessageViewerDialogTest)
+#include "messageviewerdialogtest.moc"
diff --git a/src/widgets/messageviewerdialog.cpp b/src/widgets/messageviewerdialog.cpp
index 3b2f509..12825cc 100644
--- a/src/widgets/messageviewerdialog.cpp
+++ b/src/widgets/messageviewerdialog.cpp
@@ -1,255 +1,260 @@
// SPDX-FileCopyrightText: 2023 g10 Code GmbH
// SPDX-FileContributor: Carl Schwan <carl.schwan@gnupg.com>
// SPDX-License-Identifier: GPL-2.0-or-later
#include "messageviewerdialog.h"
#include "messageviewer.h"
#include "mimetreeparser_widgets_debug.h"
#include <MimeTreeParserCore/CryptoHelper>
#include <MimeTreeParserCore/FileOpener>
#include <KLocalizedString>
#include <KMessageBox>
#include <KMessageWidget>
#include <KMime/Message>
#include <QDialogButtonBox>
#include <QFileDialog>
#include <QMenuBar>
#include <QPainter>
#include <QPrintDialog>
#include <QPrintPreviewDialog>
#include <QPrinter>
#include <QPushButton>
#include <QSaveFile>
#include <QStandardPaths>
#include <QStyle>
#include <QToolBar>
#include <QVBoxLayout>
#include <memory>
using namespace MimeTreeParser::Widgets;
class MessageViewerDialog::Private
{
public:
int currentIndex = 0;
QVector<KMime::Message::Ptr> messages;
MimeTreeParser::Widgets::MessageViewer *messageViewer = nullptr;
QAction *nextAction = nullptr;
QAction *previousAction = nullptr;
void setCurrentIndex(int currentIndex);
QMenuBar *createMenuBar(QWidget *parent);
private:
void save(QWidget *parent);
void saveDecrypted(QWidget *parent);
void print(QWidget *parent);
void printPreview(QWidget *parent);
void printInternal(QPrinter *printer);
};
void MessageViewerDialog::Private::setCurrentIndex(int index)
{
Q_ASSERT(index >= 0);
Q_ASSERT(index < messages.count());
currentIndex = index;
messageViewer->setMessage(messages[currentIndex]);
previousAction->setEnabled(currentIndex != 0);
nextAction->setEnabled(currentIndex != messages.count() - 1);
}
QMenuBar *MessageViewerDialog::Private::createMenuBar(QWidget *parent)
{
const auto menuBar = new QMenuBar(parent);
// File menu
const auto fileMenu = menuBar->addMenu(i18nc("@action:inmenu", "&File"));
const auto saveAction = new QAction(QIcon::fromTheme(QStringLiteral("document-save")), i18nc("@action:inmenu", "&Save"));
QObject::connect(saveAction, &QAction::triggered, parent, [parent, this] {
save(parent);
});
fileMenu->addAction(saveAction);
const auto saveDecryptedAction = new QAction(QIcon::fromTheme(QStringLiteral("document-save")), i18nc("@action:inmenu", "Save Decrypted"));
QObject::connect(saveDecryptedAction, &QAction::triggered, parent, [parent, this] {
saveDecrypted(parent);
});
fileMenu->addAction(saveDecryptedAction);
const auto printPreviewAction = new QAction(QIcon::fromTheme(QStringLiteral("document-print-preview")), i18nc("@action:inmenu", "Print Preview"));
QObject::connect(printPreviewAction, &QAction::triggered, parent, [parent, this] {
printPreview(parent);
});
fileMenu->addAction(printPreviewAction);
const auto printAction = new QAction(QIcon::fromTheme(QStringLiteral("document-print")), i18nc("@action:inmenu", "&Print"));
QObject::connect(printAction, &QAction::triggered, parent, [parent, this] {
print(parent);
});
fileMenu->addAction(printAction);
// Navigation menu
const auto navigationMenu = menuBar->addMenu(i18nc("@action:inmenu", "&Navigation"));
previousAction = new QAction(QIcon::fromTheme(QStringLiteral("go-previous")), i18nc("@action:button Previous email", "Previous Message"), parent);
previousAction->setEnabled(false);
navigationMenu->addAction(previousAction);
nextAction = new QAction(QIcon::fromTheme(QStringLiteral("go-next")), i18nc("@action:button Next email", "Next Message"), parent);
nextAction->setEnabled(false);
navigationMenu->addAction(nextAction);
return menuBar;
}
void MessageViewerDialog::Private::save(QWidget *parent)
{
const QString fileName = QFileDialog::getSaveFileName(parent,
i18nc("@title:window", "Save File"),
QStandardPaths::writableLocation(QStandardPaths::DocumentsLocation),
i18nc("File dialog accepted files", "Email files (*.mbox)"));
QSaveFile file(fileName);
if (!file.open(QIODevice::WriteOnly)) {
KMessageBox::error(parent, i18n("File %1 could not be created.", fileName), i18n("Error saving message"));
return;
}
file.write(messages[currentIndex]->encodedContent());
file.commit();
}
void MessageViewerDialog::Private::saveDecrypted(QWidget *parent)
{
const QString fileName = QFileDialog::getSaveFileName(parent,
i18nc("@title:window", "Save Decrypted File"),
QStandardPaths::writableLocation(QStandardPaths::DocumentsLocation),
i18nc("File dialog accepted files", "Email files (*.mbox)"));
QSaveFile file(fileName);
if (!file.open(QIODevice::WriteOnly)) {
KMessageBox::error(parent, i18nc("Error message", "File %1 could not be created.", fileName), i18n("Error saving message"));
return;
}
auto message = messages[currentIndex];
bool wasEncrypted = false;
auto decryptedMessage = CryptoUtils::decryptMessage(message, wasEncrypted);
if (!wasEncrypted) {
decryptedMessage = message;
}
file.write(decryptedMessage->encodedContent());
file.commit();
}
void MessageViewerDialog::Private::print(QWidget *parent)
{
QPrinter printer;
QPrintDialog dialog(&printer, parent);
dialog.setWindowTitle(i18nc("@title:window", "Print Document"));
if (dialog.exec() != QDialog::Accepted)
return;
printInternal(&printer);
}
void MessageViewerDialog::Private::printPreview(QWidget *parent)
{
auto dialog = new QPrintPreviewDialog(parent);
dialog->setAttribute(Qt::WA_DeleteOnClose);
dialog->resize(800, 750);
dialog->setWindowTitle(i18nc("@title:window", "Print Document"));
QObject::connect(dialog, &QPrintPreviewDialog::paintRequested, parent, [this](QPrinter *printer) {
printInternal(printer);
});
dialog->open();
}
void MessageViewerDialog::Private::printInternal(QPrinter *printer)
{
QPainter painter;
painter.begin(printer);
const auto pageLayout = printer->pageLayout();
const auto pageRect = pageLayout.paintRectPixels(printer->resolution());
const auto paperRect = pageLayout.fullRectPixels(printer->resolution());
const double xscale = pageRect.width() / double(messageViewer->width());
const double yscale = pageRect.height() / double(messageViewer->height());
const double scale = qMin(qMin(xscale, yscale), 1.);
painter.translate(pageRect.x(), pageRect.y());
painter.scale(scale, scale);
messageViewer->print(&painter, pageRect.width());
}
MessageViewerDialog::MessageViewerDialog(const QString &fileName, QWidget *parent)
: QDialog(parent)
, d(std::make_unique<Private>())
{
const auto mainLayout = new QVBoxLayout(this);
mainLayout->setContentsMargins({});
const auto layout = new QVBoxLayout;
layout->setContentsMargins(style()->pixelMetric(QStyle::PM_LayoutLeftMargin, nullptr, this),
style()->pixelMetric(QStyle::PM_LayoutTopMargin, nullptr, this),
style()->pixelMetric(QStyle::PM_LayoutRightMargin, nullptr, this),
style()->pixelMetric(QStyle::PM_LayoutBottomMargin, nullptr, this));
const auto menuBar = d->createMenuBar(this);
mainLayout->setMenuBar(menuBar);
d->messages += MimeTreeParser::Core::FileOpener::openFile(fileName);
if (d->messages.isEmpty()) {
auto errorMessage = new KMessageWidget(this);
errorMessage->setMessageType(KMessageWidget::Error);
errorMessage->setText(i18nc("@info", "Unable to read file"));
layout->addWidget(errorMessage);
return;
}
const bool multipleMessages = d->messages.length() > 1;
if (multipleMessages) {
const auto toolBar = new QToolBar(this);
#ifdef Q_OS_UNIX
toolBar->setToolButtonStyle(Qt::ToolButtonFollowStyle);
#else
// on other platforms the default is IconOnly which is bad for
// accessibility and can't be changed by the user.
toolBar->setToolButtonStyle(Qt::ToolButtonTextBesideIcon);
#endif
toolBar->addAction(d->previousAction);
connect(d->previousAction, &QAction::triggered, this, [this](int index) {
d->setCurrentIndex(d->currentIndex - 1);
});
const auto spacer = new QWidget(this);
spacer->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
toolBar->addWidget(spacer);
toolBar->addAction(d->nextAction);
connect(d->nextAction, &QAction::triggered, this, [this](int index) {
d->setCurrentIndex(d->currentIndex + 1);
});
d->nextAction->setEnabled(true);
mainLayout->addWidget(toolBar);
}
mainLayout->addLayout(layout);
d->messageViewer = new MimeTreeParser::Widgets::MessageViewer(this);
d->messageViewer->setMessage(d->messages[0]);
layout->addWidget(d->messageViewer);
auto buttonBox = new QDialogButtonBox(this);
auto closeButton = buttonBox->addButton(QDialogButtonBox::Close);
connect(closeButton, &QPushButton::pressed, this, &QDialog::accept);
layout->addWidget(buttonBox);
}
MessageViewerDialog::~MessageViewerDialog() = default;
+
+QVector<KMime::Message::Ptr> MessageViewerDialog::messages() const
+{
+ return d->messages;
+}
diff --git a/src/widgets/messageviewerdialog.h b/src/widgets/messageviewerdialog.h
index cfc2da5..b2d7075 100644
--- a/src/widgets/messageviewerdialog.h
+++ b/src/widgets/messageviewerdialog.h
@@ -1,36 +1,39 @@
// SPDX-FileCopyrightText: 2023 g10 Code GmbH
// SPDX-FileContributor: Carl Schwan <carl.schwan@gnupg.com>
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include "mimetreeparser_widgets_export.h"
+#include <KMime/Message>
#include <QDialog>
#include <memory>
namespace MimeTreeParser
{
namespace Widgets
{
/// MessageViewerDialog that displays the given email stored in the
/// file.
///
/// \author Carl Schwan <carl.schwan@gnupg.com>
class MIMETREEPARSER_WIDGETS_EXPORT MessageViewerDialog : public QDialog
{
Q_OBJECT
public:
MessageViewerDialog(const QString &fileName, QWidget *parent = nullptr);
~MessageViewerDialog() override;
+ QVector<KMime::Message::Ptr> messages() const;
+
private:
class Private;
std::unique_ptr<Private> const d;
};
} // end namespace Widgets
} // end namespace MimeTreeParser
File Metadata
Details
Attached
Mime Type
text/x-diff
Expires
Thu, Dec 4, 1:03 PM (1 d, 14 h)
Storage Engine
local-disk
Storage Format
Raw Data
Storage Handle
a8/d6/62b92cb153e57a634b562289f293
Attached To
rMTP MIME Tree Parser
Event Timeline
Log In to Comment