Page Menu
Home
GnuPG
Search
Configure Global Search
Log In
Files
F26446333
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Award Token
Size
13 KB
Subscribers
None
View Options
diff --git a/src/widgets/messageviewerdialog.cpp b/src/widgets/messageviewerdialog.cpp
index b429c18..ee89ce7 100644
--- a/src/widgets/messageviewerdialog.cpp
+++ b/src/widgets/messageviewerdialog.cpp
@@ -1,366 +1,365 @@
// 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 <QDialogButtonBox>
#include <QFileDialog>
#include <QMenuBar>
#include <QPainter>
#include <QPrintDialog>
#include <QPrintPreviewDialog>
#include <QPrinter>
#include <QPushButton>
#include <QRegularExpression>
#include <QSaveFile>
#include <QStandardPaths>
#include <QStyle>
#include <QToolBar>
#include <QVBoxLayout>
using namespace MimeTreeParser::Widgets;
namespace
{
inline QString changeExtension(const QString &fileName, const QString &extension)
{
auto renamedFileName = fileName;
renamedFileName.replace(QRegularExpression(QStringLiteral("\\.(mbox|p7m|asc)$")), extension);
// In case the file name didn't contain any of the expected extension: mbox, p7m, asc and eml
// or doesn't contains an extension at all.
if (!renamedFileName.endsWith(extension)) {
renamedFileName += extension;
}
return renamedFileName;
}
#define SLASHES "/\\"
inline QString changeFileName(const QString &fileName, const QString &subject)
{
if (subject.isEmpty()) {
return fileName;
}
if (fileName.isEmpty()) {
return subject;
}
auto cleanedSubject = subject;
static const char notAllowedChars[] = ",^@={}[]~!?:&*\"|#%<>$\"'();`'/\\.";
static const char *notAllowedSubStrings[] = {"..", " "};
for (const char *c = notAllowedChars; *c; c++) {
cleanedSubject.replace(QLatin1Char(*c), QStringLiteral(" "));
}
const int notAllowedSubStringCount = sizeof(notAllowedSubStrings) / sizeof(const char *);
for (int s = 0; s < notAllowedSubStringCount; s++) {
const QLatin1StringView notAllowedSubString(notAllowedSubStrings[s]);
cleanedSubject.replace(notAllowedSubString, QStringLiteral(" "));
}
QStringList splitedFileName = fileName.split(QLatin1Char('/'));
splitedFileName[splitedFileName.count() - 1] = cleanedSubject;
return splitedFileName.join(QLatin1Char('/'));
}
}
class MessageViewerDialog::Private
{
public:
Private(MessageViewerDialog *dialog)
: q(dialog)
{
}
MessageViewerDialog *const q;
int currentIndex = 0;
QList<KMime::Message::Ptr> messages;
QString fileName;
MimeTreeParser::Widgets::MessageViewer *messageViewer = nullptr;
QAction *nextAction = nullptr;
QAction *previousAction = nullptr;
QToolBar *toolBar = 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]);
q->setWindowTitle(messageViewer->subject());
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)
{
QString extension;
QString alternatives;
auto message = messages[currentIndex];
bool wasEncrypted = false;
GpgME::Protocol protocol;
auto decryptedMessage = CryptoUtils::decryptMessage(message, wasEncrypted, protocol);
Q_UNUSED(decryptedMessage); // we save the message without modifying it
if (wasEncrypted) {
extension = QStringLiteral(".mime");
if (protocol == GpgME::OpenPGP) {
alternatives = i18nc("File dialog accepted files", "Email files (*.eml *.mbox *.mime)");
} else {
alternatives = i18nc("File dialog accepted files", "Encrypted S/MIME files (*.p7m)");
}
} else {
extension = QStringLiteral(".eml");
alternatives = i18nc("File dialog accepted files", "Email files (*.eml *.mbox *.mime)");
}
const QString location = QFileDialog::getSaveFileName(parent,
i18nc("@title:window", "Save File"),
changeExtension(changeFileName(fileName, messageViewer->subject()), extension),
alternatives);
QSaveFile file(location);
if (!file.open(QIODevice::WriteOnly)) {
KMessageBox::error(parent, i18n("File %1 could not be created.", location), i18n("Error saving message"));
return;
}
file.write(messages[currentIndex]->encodedContent());
file.commit();
}
void MessageViewerDialog::Private::saveDecrypted(QWidget *parent)
{
const QString location = QFileDialog::getSaveFileName(parent,
i18nc("@title:window", "Save Decrypted File"),
changeExtension(changeFileName(fileName, messageViewer->subject()), QStringLiteral(".eml")),
i18nc("File dialog accepted files", "Email files (*.eml *.mbox *.mime)"));
QSaveFile file(location);
if (!file.open(QIODevice::WriteOnly)) {
KMessageBox::error(parent, i18nc("Error message", "File %1 could not be created.", location), i18n("Error saving message"));
return;
}
auto message = messages[currentIndex];
bool wasEncrypted = false;
GpgME::Protocol protocol;
auto decryptedMessage = CryptoUtils::decryptMessage(message, wasEncrypted, protocol);
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 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 QList<KMime::Message::Ptr> &messages, QWidget *parent)
: QDialog(parent)
, d(std::make_unique<Private>(this))
{
d->messages += messages;
initGUI();
}
MessageViewerDialog::MessageViewerDialog(const QString &fileName, QWidget *parent)
: QDialog(parent)
, d(std::make_unique<Private>(this))
{
d->fileName = fileName;
d->messages += MimeTreeParser::Core::FileOpener::openFile(fileName);
initGUI();
}
void MessageViewerDialog::initGUI()
{
const auto mainLayout = new QVBoxLayout(this);
mainLayout->setContentsMargins({});
mainLayout->setSpacing(0);
const auto layout = new QVBoxLayout;
const auto menuBar = d->createMenuBar(this);
mainLayout->setMenuBar(menuBar);
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;
d->toolBar = new QToolBar(this);
if (multipleMessages) {
#ifdef Q_OS_UNIX
d->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.
d->toolBar->setToolButtonStyle(Qt::ToolButtonTextBesideIcon);
#endif
d->toolBar->addAction(d->previousAction);
connect(d->previousAction, &QAction::triggered, this, [this] {
d->setCurrentIndex(d->currentIndex - 1);
});
const auto spacer = new QWidget(this);
spacer->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
d->toolBar->addWidget(spacer);
d->toolBar->addAction(d->nextAction);
connect(d->nextAction, &QAction::triggered, this, [this] {
d->setCurrentIndex(d->currentIndex + 1);
});
d->nextAction->setEnabled(true);
mainLayout->addWidget(d->toolBar);
} else {
mainLayout->addWidget(d->toolBar);
d->toolBar->hide();
}
mainLayout->addLayout(layout);
d->messageViewer = new MimeTreeParser::Widgets::MessageViewer(this);
d->messageViewer->setMessage(d->messages[0]);
setWindowTitle(d->messageViewer->subject());
layout->addWidget(d->messageViewer);
auto buttonBox = new QDialogButtonBox(this);
buttonBox->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));
auto closeButton = buttonBox->addButton(QDialogButtonBox::Close);
connect(closeButton, &QPushButton::pressed, this, &QDialog::accept);
layout->addWidget(buttonBox);
setMinimumSize(300, 300);
resize(600, 600);
}
MessageViewerDialog::~MessageViewerDialog() = default;
QToolBar *MessageViewerDialog::toolBar() const
{
return d->toolBar;
}
QList<KMime::Message::Ptr> MessageViewerDialog::messages() const
{
return d->messages;
}
#include "moc_messageviewerdialog.cpp"
File Metadata
Details
Attached
Mime Type
text/x-diff
Expires
Thu, Jul 17, 12:51 AM (1 d, 6 h)
Storage Engine
local-disk
Storage Format
Raw Data
Storage Handle
97/a3/620a6aa8c5387b4b7fd2c25852f8
Attached To
rMTP MIME Tree Parser
Event Timeline
Log In to Comment