diff --git a/messageviewer/src/settings/messageviewer.kcfg.cmake b/messageviewer/src/settings/messageviewer.kcfg.cmake
index c18dd210..c47b17be 100644
--- a/messageviewer/src/settings/messageviewer.kcfg.cmake
+++ b/messageviewer/src/settings/messageviewer.kcfg.cmake
@@ -1,250 +1,246 @@
QFontDatabase
false
false
true
QFontDatabase::systemFont(QFontDatabase::FixedFont)
8
QFontDatabase::systemFont(QFontDatabase::GeneralFont)
QFontDatabase::systemFont(QFontDatabase::GeneralFont)
false
false
true
false
-
- true
-
-
false
Enable this option to show different levels of quoted text. Disable to hide the levels of quoted text.
3
0
10
false
Enable this option to show quoted text with a smaller font.
false
false
Never
100
180
defaultgrantlee
fancy
rich
false
false
Smart
false
true
true
true
0
0
true
0
Microsoft Outlook has a number of shortcomings in its implementation of the iCalendar standard; this option works around one of them. If you have problems with Outlook users not being able to get your replies, try setting this option.
${LEGACY_MANGLE_FROM_TO_HEADERS}
Microsoft Outlook has a number of shortcomings in its implementation of the iCalendar standard; this option works around one of them. If you have problems with Outlook users not being able to get your invitations, try setting this option.
${LEGACY_BODY_INVITES}
Microsoft Outlook, when used in combination with a Microsoft Exchange server, has a problem understanding standards-compliant groupware email. Turn this option on to send groupware invitations in a way that Microsoft Exchange understands.
${EXCHANGE_COMPATIBLE_INVITATIONS}
When this is checked, you will not see the mail composer window. Instead, all invitation mails are sent automatically. If you want to see the mail before sending it, you can uncheck this option. However, be aware that the text in the composer window is in iCalendar syntax, and you should not try modifying it by hand.
true
When this is checked, received invitation emails that have been replied to will be moved to the Trash folder, once the reply has been successfully sent.
true
AskForAllButAcceptance
false
The most recent selected folder using for Todo.
-1
The most recent selected folder using for Event.
-1
The most recent selected folder using for Notes.
-1
diff --git a/messageviewer/src/ui/settings.ui b/messageviewer/src/ui/settings.ui
index e35909a8..63dff324 100644
--- a/messageviewer/src/ui/settings.ui
+++ b/messageviewer/src/ui/settings.ui
@@ -1,269 +1,261 @@
/* Copyright (C) 2009 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.net
Copyright (c) 2009 Andras Mantia <andras@kdab.net>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License along
with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
Settings
0
0
482
456
Viewer settings
-
Qt::Vertical
QSizePolicy::Fixed
20
24
-
0
0
O&verride character encoding:
overrideCharacterEncoding
-
0
0
-
2
30
-
-
Enable access key
- -
-
-
- Show &HTML Side Bar
-
-
-
-
Reduce font size for "ed text
-
Show &expand/collapse quote marks
-
-
Qt::Horizontal
QSizePolicy::Fixed
24
20
-
false
Automatic collapse &level:
kcfg_CollapseQuoteLevelSpin
-
false
1
3
3
-
Qt::Horizontal
40
20
-
Qt::Horizontal
40
20
-
Qt::Vertical
20
40
-
&Minimum font size:
kcfg_MinimumFontSize
KComboBox
QComboBox
kcfg_AccessKeyEnabled
- kcfg_showColorBar
kcfg_ShrinkQuotes
kcfg_ShowExpandQuotesMark
kcfg_CollapseQuoteLevelSpin
overrideCharacterEncoding
kcfg_MinimumFontSize
kcfg_ShowExpandQuotesMark
toggled(bool)
collapseQuoteLevelLabel
setEnabled(bool)
94
125
138
153
kcfg_ShowExpandQuotesMark
toggled(bool)
kcfg_CollapseQuoteLevelSpin
setEnabled(bool)
179
125
228
153
diff --git a/messageviewer/src/viewer/viewer_p.cpp b/messageviewer/src/viewer/viewer_p.cpp
index 7e1382a7..686d823b 100644
--- a/messageviewer/src/viewer/viewer_p.cpp
+++ b/messageviewer/src/viewer/viewer_p.cpp
@@ -1,3277 +1,3273 @@
/*
Copyright (c) 1997 Markus Wuebben
Copyright (C) 2009 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.net
Copyright (c) 2009 Andras Mantia
Copyright (c) 2010 Torgny Nyblom
Copyright (C) 2011-2018 Laurent Montel
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License along
with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include "viewer_p.h"
#include "viewer.h"
#include "messageviewer_debug.h"
#include "utils/mimetype.h"
#include "viewer/objecttreeemptysource.h"
#include "viewer/objecttreeviewersource.h"
#include "messagedisplayformatattribute.h"
#include "utils/iconnamecache.h"
#include "scamdetection/scamdetectionwarningwidget.h"
#include "scamdetection/scamattribute.h"
#include "viewer/mimeparttree/mimeparttreeview.h"
#include "widgets/openattachmentfolderwidget.h"
#include "messageviewer/headerstyle.h"
#include "messageviewer/headerstrategy.h"
#include "kpimtextedit/slidecontainer.h"
#include
#include
#include "job/attachmenteditjob.h"
#include "job/modifymessagedisplayformatjob.h"
#include "config-messageviewer.h"
#include "webengine/mailwebenginescript.h"
#include "viewerplugins/viewerplugintoolmanager.h"
#include
#include "htmlwriter/webengineembedpart.h"
#include
#include // link()
//KDE includes
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
//Qt includes
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
//libkdepim
#include "Libkdepim/BroadcastStatus"
#include
#include
#include
#include
#include
#include
#include
#include
//own includes
#include "widgets/attachmentdialog.h"
#include "csshelper.h"
#include "settings/messageviewersettings.h"
#include "widgets/htmlstatusbar.h"
#include "viewer/attachmentstrategy.h"
#include "viewer/mimeparttree/mimetreemodel.h"
#include "viewer/urlhandlermanager.h"
#include "messageviewer/messageviewerutil.h"
#include "utils/messageviewerutil_p.h"
#include "widgets/vcardviewer.h"
#include
#include "viewer/webengine/mailwebengineview.h"
#include "htmlwriter/webengineparthtmlwriter.h"
#include
#include
#include "header/headerstylemenumanager.h"
#include "widgets/submittedformwarningwidget.h"
#include
#include
#include "interfaces/htmlwriter.h"
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
using namespace boost;
using namespace MailTransport;
using namespace MessageViewer;
using namespace MessageCore;
static QAtomicInt _k_attributeInitialized;
template
struct InvokeWrapper {
R *receiver;
void (C::*memberFun)(Arg);
void operator()(Arg result)
{
(receiver->*memberFun)(result);
}
};
template
InvokeWrapper invoke(R *receiver, void (C::*memberFun)(Arg))
{
InvokeWrapper wrapper = {receiver, memberFun};
return wrapper;
}
ViewerPrivate::ViewerPrivate(Viewer *aParent, QWidget *mainWindow, KActionCollection *actionCollection)
: QObject(aParent)
, mNodeHelper(new MimeTreeParser::NodeHelper)
, mViewer(nullptr)
, mFindBar(nullptr)
, mAttachmentStrategy(nullptr)
, mUpdateReaderWinTimer(nullptr)
, mResizeTimer(nullptr)
, mOldGlobalOverrideEncoding(QStringLiteral("---"))
, mMsgDisplay(true)
, // init with dummy value
mCSSHelper(nullptr)
, mMainWindow(mainWindow)
, mActionCollection(actionCollection)
, mCopyAction(nullptr)
, mCopyURLAction(nullptr)
, mUrlOpenAction(nullptr)
, mSelectAllAction(nullptr)
, mScrollUpAction(nullptr)
, mScrollDownAction(nullptr)
, mScrollUpMoreAction(nullptr)
, mScrollDownMoreAction(nullptr)
, mHeaderOnlyAttachmentsAction(nullptr)
, mSelectEncodingAction(nullptr)
, mToggleFixFontAction(nullptr)
, mToggleDisplayModeAction(nullptr)
, mToggleMimePartTreeAction(nullptr)
, mSpeakTextAction(nullptr)
, mCanStartDrag(false)
, mHtmlWriter(nullptr)
, mDecrytMessageOverwrite(false)
, mShowSignatureDetails(false)
, mShowAttachmentQuicklist(true)
, mForceEmoticons(true)
, mRecursionCountForDisplayMessage(0)
, mCurrentContent(nullptr)
, mMessagePartNode(nullptr)
, q(aParent)
, mSession(new Akonadi::Session("MessageViewer-"
+ QByteArray::number(reinterpret_cast(this)), this))
, mPreviouslyViewedItem(-1)
, mScamDetectionWarning(nullptr)
, mOpenAttachmentFolderWidget(nullptr)
, mSliderContainer(nullptr)
, mShareServiceManager(nullptr)
, mHeaderStylePlugin(nullptr)
, mHeaderStyleMenuManager(nullptr)
, mViewerPluginToolManager(nullptr)
, mZoomActionMenu(nullptr)
, mCurrentPrinter(nullptr)
, mPhishingDatabase(nullptr)
{
mMimePartTree = nullptr;
if (!mainWindow) {
mMainWindow = aParent;
}
if (_k_attributeInitialized.testAndSetAcquire(0, 1)) {
Akonadi::AttributeFactory::registerAttribute();
Akonadi::AttributeFactory::registerAttribute();
}
mPhishingDatabase = new WebEngineViewer::LocalDataBaseManager(this);
mPhishingDatabase->initialize();
connect(mPhishingDatabase, &WebEngineViewer::LocalDataBaseManager::checkUrlFinished,
this, &ViewerPrivate::slotCheckedUrlFinished);
mShareServiceManager = new PimCommon::ShareServiceUrlManager(this);
mDisplayFormatMessageOverwrite = MessageViewer::Viewer::UseGlobalSetting;
mHtmlLoadExtOverride = false;
mHtmlLoadExternalGlobalSetting = false;
mHtmlMailGlobalSetting = false;
mUpdateReaderWinTimer.setObjectName(QStringLiteral("mUpdateReaderWinTimer"));
mResizeTimer.setObjectName(QStringLiteral("mResizeTimer"));
mPrinting = false;
createWidgets();
createActions();
initHtmlWidget();
readConfig();
mLevelQuote = MessageViewer::MessageViewerSettings::self()->collapseQuoteLevelSpin() - 1;
mResizeTimer.setSingleShot(true);
connect(&mResizeTimer, &QTimer::timeout,
this, &ViewerPrivate::slotDelayedResize);
mUpdateReaderWinTimer.setSingleShot(true);
connect(&mUpdateReaderWinTimer, &QTimer::timeout,
this, &ViewerPrivate::updateReaderWin);
connect(mNodeHelper, &MimeTreeParser::NodeHelper::update,
this, &ViewerPrivate::update);
connect(mColorBar, &HtmlStatusBar::clicked,
this, &ViewerPrivate::slotToggleHtmlMode);
// FIXME: Don't use the full payload here when attachment loading on demand is used, just
// like in KMMainWidget::slotMessageActivated().
mMonitor.setObjectName(QStringLiteral("MessageViewerMonitor"));
mMonitor.setSession(mSession);
Akonadi::ItemFetchScope fs;
fs.fetchFullPayload();
fs.fetchAttribute();
fs.fetchAttribute();
fs.fetchAttribute();
mMonitor.setItemFetchScope(fs);
connect(&mMonitor, &Akonadi::Monitor::itemChanged,
this, &ViewerPrivate::slotItemChanged);
connect(&mMonitor, &Akonadi::Monitor::itemRemoved,
this, &ViewerPrivate::slotClear);
connect(&mMonitor, &Akonadi::Monitor::itemMoved,
this, &ViewerPrivate::slotItemMoved);
}
ViewerPrivate::~ViewerPrivate()
{
MessageViewer::MessageViewerSettings::self()->save();
delete mHtmlWriter;
mHtmlWriter = nullptr;
delete mViewer;
mViewer = nullptr;
delete mCSSHelper;
mNodeHelper->forceCleanTempFiles();
qDeleteAll(mListMailSourceViewer);
delete mNodeHelper;
}
//-----------------------------------------------------------------------------
KMime::Content *ViewerPrivate::nodeFromUrl(const QUrl &url) const
{
return mNodeHelper->fromHREF(mMessage, url);
}
void ViewerPrivate::openAttachment(KMime::Content *node, const QUrl &url)
{
if (!node) {
return;
}
if (node->contentType(false)) {
if (node->contentType()->mimeType() == "text/x-moz-deleted") {
return;
}
if (node->contentType()->mimeType() == "message/external-body") {
if (node->contentType()->hasParameter(QStringLiteral("url"))) {
KRun::RunFlags flags;
flags |= KRun::RunExecutables;
const QString url = node->contentType()->parameter(QStringLiteral("url"));
KRun::runUrl(QUrl(url), QStringLiteral("text/html"), q, flags);
return;
}
}
}
const bool isEncapsulatedMessage = node->parent() && node->parent()->bodyIsMessage();
if (isEncapsulatedMessage) {
// the viewer/urlhandlermanager expects that the message (mMessage) it is passed is the root when doing index calculation
// in urls. Simply passing the result of bodyAsMessage() does not cut it as the resulting pointer is a child in its tree.
KMime::Message::Ptr m = KMime::Message::Ptr(new KMime::Message);
m->setContent(node->parent()->bodyAsMessage()->encodedContent());
m->parse();
atmViewMsg(m);
return;
}
// determine the MIME type of the attachment
// prefer the value of the Content-Type header
QMimeDatabase mimeDb;
auto mimetype
= mimeDb.mimeTypeForName(QString::fromLatin1(node->contentType()->mimeType().toLower()));
if (mimetype.isValid() && mimetype.inherits(KContacts::Addressee::mimeType())) {
showVCard(node);
return;
}
// special case treatment on mac and windows
QUrl atmUrl = url;
if (url.isEmpty()) {
atmUrl = mNodeHelper->tempFileUrlFromNode(node);
}
if (Util::handleUrlWithQDesktopServices(atmUrl)) {
return;
}
if (!mimetype.isValid() || mimetype.name() == QLatin1String("application/octet-stream")) {
mimetype = MimeTreeParser::Util::mimetype(
url.isLocalFile() ? url.toLocalFile() : url.fileName());
}
KService::Ptr offer
= KMimeTypeTrader::self()->preferredService(mimetype.name(), QStringLiteral("Application"));
const QString filenameText = MimeTreeParser::NodeHelper::fileName(node);
QPointer dialog = new AttachmentDialog(mMainWindow, filenameText,
offer,
QLatin1String(
"askSave_") + mimetype.name());
const int choice = dialog->exec();
delete dialog;
if (choice == AttachmentDialog::Save) {
QList urlList;
if (Util::saveContents(mMainWindow, KMime::Content::List() << node, urlList)) {
showOpenAttachmentFolderWidget(urlList);
}
} else if (choice == AttachmentDialog::Open) { // Open
if (offer) {
attachmentOpenWith(node, offer);
} else {
attachmentOpen(node);
}
} else if (choice == AttachmentDialog::OpenWith) {
attachmentOpenWith(node);
} else { // Cancel
qCDebug(MESSAGEVIEWER_LOG) << "Canceled opening attachment";
}
}
bool ViewerPrivate::deleteAttachment(KMime::Content *node, bool showWarning)
{
if (!node) {
return true;
}
KMime::Content *parent = node->parent();
if (!parent) {
return true;
}
QList extraNodes = mNodeHelper->extraContents(mMessage.data());
if (extraNodes.contains(node->topLevel())) {
KMessageBox::error(mMainWindow,
i18n(
"Deleting an attachment from an encrypted or old-style mailman message is not supported."),
i18n("Delete Attachment"));
return true; //cancelled
}
if (showWarning && KMessageBox::warningContinueCancel(mMainWindow,
i18n(
"Deleting an attachment might invalidate any digital signature on this message."),
i18n("Delete Attachment"),
KStandardGuiItem::del(),
KStandardGuiItem::cancel(),
QStringLiteral(
"DeleteAttachmentSignatureWarning"))
!= KMessageBox::Continue) {
return false; //cancelled
}
//don't confuse the model
#ifndef QT_NO_TREEVIEW
mMimePartTree->clearModel();
#endif
QString filename;
QString name;
QByteArray mimetype;
if (node->contentDisposition(false)) {
filename = node->contentDisposition()->filename();
}
if (node->contentType(false)) {
name = node->contentType()->name();
mimetype = node->contentType()->mimeType();
}
// text/plain part:
KMime::Content *deletePart = new KMime::Content(parent);
deletePart->contentType()->setMimeType("text/x-moz-deleted");
deletePart->contentType()->setName(QStringLiteral("Deleted: %1").arg(name), "utf8");
deletePart->contentDisposition()->setDisposition(KMime::Headers::CDattachment);
deletePart->contentDisposition()->setFilename(QStringLiteral("Deleted: %1").arg(name));
deletePart->contentType()->setCharset("utf-8");
deletePart->contentTransferEncoding()->setEncoding(KMime::Headers::CE7Bit);
QByteArray bodyMessage = QByteArrayLiteral(
"\nYou deleted an attachment from this message. The original MIME headers for the attachment were:");
bodyMessage += ("\nContent-Type: ") + mimetype;
bodyMessage += ("\nname=\"") + name.toUtf8() + "\"";
bodyMessage += ("\nfilename=\"") + filename.toUtf8() + "\"";
deletePart->setBody(bodyMessage);
parent->replaceContent(node, deletePart);
parent->assemble();
KMime::Message *modifiedMessage = mNodeHelper->messageWithExtraContent(mMessage.data());
#ifndef QT_NO_TREEVIEW
mMimePartTree->mimePartModel()->setRoot(modifiedMessage);
#endif
mMessageItem.setPayloadFromData(modifiedMessage->encodedContent());
Akonadi::ItemModifyJob *job = new Akonadi::ItemModifyJob(mMessageItem, mSession);
job->disableRevisionCheck();
connect(job, &KJob::result, this, &ViewerPrivate::itemModifiedResult);
return true;
}
void ViewerPrivate::itemModifiedResult(KJob *job)
{
if (job->error()) {
qCDebug(MESSAGEVIEWER_LOG) << "Item update failed:" << job->errorString();
} else {
setMessageItem(mMessageItem, MimeTreeParser::Force);
}
}
void ViewerPrivate::editAttachment(KMime::Content *node, bool showWarning)
{
MessageViewer::AttachmentEditJob *job = new MessageViewer::AttachmentEditJob(mSession, this);
connect(job, &AttachmentEditJob::refreshMessage, this, &ViewerPrivate::slotRefreshMessage);
job->setMainWindow(mMainWindow);
job->setMessageItem(mMessageItem);
job->setMessage(mMessage);
job->addAttachment(node, showWarning);
job->canDeleteJob();
}
void ViewerPrivate::scrollToAnchor(const QString &anchor)
{
mViewer->scrollToAnchor(anchor);
}
void ViewerPrivate::createOpenWithMenu(QMenu *topMenu, const QString &contentTypeStr, bool fromCurrentContent)
{
const KService::List offers = KFileItemActions::associatedApplications(
QStringList() << contentTypeStr, QString());
if (!offers.isEmpty()) {
QMenu *menu = topMenu;
QActionGroup *actionGroup = new QActionGroup(menu);
if (fromCurrentContent) {
connect(actionGroup, &QActionGroup::triggered, this,
&ViewerPrivate::slotOpenWithActionCurrentContent);
} else {
connect(actionGroup, &QActionGroup::triggered, this,
&ViewerPrivate::slotOpenWithAction);
}
if (offers.count() > 1) { // submenu 'open with'
menu = new QMenu(i18nc("@title:menu", "&Open With"), topMenu);
menu->menuAction()->setObjectName(QStringLiteral("openWith_submenu")); // for the unittest
topMenu->addMenu(menu);
}
//qCDebug(MESSAGEVIEWER_LOG) << offers.count() << "offers" << topMenu << menu;
KService::List::ConstIterator it = offers.constBegin();
KService::List::ConstIterator end = offers.constEnd();
for (; it != end; ++it) {
QAction *act = MessageViewer::Util::createAppAction(*it,
// no submenu -> prefix single offer
menu == topMenu, actionGroup, menu);
menu->addAction(act);
}
QString openWithActionName;
if (menu != topMenu) { // submenu
menu->addSeparator();
openWithActionName = i18nc("@action:inmenu Open With", "&Other...");
} else {
openWithActionName = i18nc("@title:menu", "&Open With...");
}
QAction *openWithAct = new QAction(menu);
openWithAct->setText(openWithActionName);
if (fromCurrentContent) {
connect(openWithAct, &QAction::triggered, this,
&ViewerPrivate::slotOpenWithDialogCurrentContent);
} else {
connect(openWithAct, &QAction::triggered, this, &ViewerPrivate::slotOpenWithDialog);
}
menu->addAction(openWithAct);
} else { // no app offers -> Open With...
QAction *act = new QAction(topMenu);
act->setText(i18nc("@title:menu", "&Open With..."));
if (fromCurrentContent) {
connect(act, &QAction::triggered, this,
&ViewerPrivate::slotOpenWithDialogCurrentContent);
} else {
connect(act, &QAction::triggered, this, &ViewerPrivate::slotOpenWithDialog);
}
topMenu->addAction(act);
}
}
void ViewerPrivate::slotOpenWithDialogCurrentContent()
{
if (!mCurrentContent) {
return;
}
attachmentOpenWith(mCurrentContent);
}
void ViewerPrivate::slotOpenWithDialog()
{
auto contents = selectedContents();
if (contents.count() == 1) {
attachmentOpenWith(contents.first());
}
}
void ViewerPrivate::slotOpenWithActionCurrentContent(QAction *act)
{
if (!mCurrentContent) {
return;
}
KService::Ptr app = act->data().value();
attachmentOpenWith(mCurrentContent, app);
}
void ViewerPrivate::slotOpenWithAction(QAction *act)
{
KService::Ptr app = act->data().value();
auto contents = selectedContents();
if (contents.count() == 1) {
attachmentOpenWith(contents.first(), app);
}
}
void ViewerPrivate::showAttachmentPopup(KMime::Content *node, const QString &name, const QPoint &globalPos)
{
Q_UNUSED(name);
prepareHandleAttachment(node);
QMenu menu;
bool deletedAttachment = false;
if (node->contentType(false)) {
deletedAttachment = (node->contentType()->mimeType() == "text/x-moz-deleted");
}
const QString contentTypeStr = QLatin1String(node->contentType()->mimeType());
QAction *action
= menu.addAction(QIcon::fromTheme(QStringLiteral("document-open")), i18nc("to open",
"Open"));
action->setEnabled(!deletedAttachment);
connect(action, &QAction::triggered, this, [this]() {
slotHandleAttachment(Viewer::Open);
});
if (!deletedAttachment) {
createOpenWithMenu(&menu, contentTypeStr, true);
}
QMimeDatabase mimeDb;
auto mimetype = mimeDb.mimeTypeForName(contentTypeStr);
if (mimetype.isValid()) {
const QStringList parentMimeType = mimetype.parentMimeTypes();
if ((contentTypeStr == QLatin1String("text/plain"))
|| (contentTypeStr == QLatin1String("image/png"))
|| (contentTypeStr == QLatin1String("image/jpeg"))
|| parentMimeType.contains(QLatin1String("text/plain"))
|| parentMimeType.contains(QLatin1String("image/png"))
|| parentMimeType.contains(QLatin1String("image/jpeg"))
) {
action = menu.addAction(i18nc("to view something", "View"));
action->setEnabled(!deletedAttachment);
connect(action, &QAction::triggered, this, [this]() {
slotHandleAttachment(Viewer::View);
});
}
}
#if 0 //Reimplement in the future
const bool attachmentInHeader = mViewer->isAttachmentInjectionPoint(globalPos);
const bool hasScrollbar = mViewer->hasVerticalScrollBar();
if (attachmentInHeader && hasScrollbar) {
action = menu->addAction(i18n("Scroll To"));
connect(action, &QAction::triggered, this, [this]() {
slotHandleAttachment(Viewer::ScrollTo);
});
}
#endif
action = menu.addAction(QIcon::fromTheme(QStringLiteral("document-save-as")), i18n(
"Save As..."));
action->setEnabled(!deletedAttachment);
connect(action, &QAction::triggered, this, [this]() {
slotHandleAttachment(Viewer::Save);
});
action = menu.addAction(QIcon::fromTheme(QStringLiteral("edit-copy")), i18n("Copy"));
action->setEnabled(!deletedAttachment);
connect(action, &QAction::triggered, this, [this]() {
slotHandleAttachment(Viewer::Copy);
});
const bool isEncapsulatedMessage = node->parent() && node->parent()->bodyIsMessage();
const bool canChange = mMessageItem.isValid() && mMessageItem.parentCollection().isValid()
&& (mMessageItem.parentCollection().rights()
!= Akonadi::Collection::ReadOnly)
&& !isEncapsulatedMessage;
if (MessageViewer::MessageViewerSettings::self()->allowAttachmentEditing()) {
action
= menu.addAction(QIcon::fromTheme(QStringLiteral("document-properties")), i18n(
"Edit Attachment"));
connect(action, &QAction::triggered, this, [this]() {
slotHandleAttachment(Viewer::Edit);
});
action->setEnabled(canChange);
}
action
= menu.addAction(QIcon::fromTheme(QStringLiteral("edit-delete")),
i18n("Delete Attachment"));
connect(action, &QAction::triggered, this, [this]() {
slotHandleAttachment(Viewer::Delete);
});
action->setEnabled(canChange && !deletedAttachment);
#if 0
menu->addSeparator();
action
= menu->addAction(QIcon::fromTheme(QStringLiteral("mail-reply-sender")),
i18n("Reply To Author"));
connect(action, &QAction::triggered, this, [this]() {
slotHandleAttachment(Viewer::ReplyMessageToAuthor);
});
menu->addSeparator();
action = menu->addAction(QIcon::fromTheme(QStringLiteral("mail-reply-all")), i18n(
"Reply To All"));
connect(action, &QAction::triggered, this, [this]() {
slotHandleAttachment(Viewer::ReplyMessageToAll);
});
#endif
menu.addSeparator();
action = menu.addAction(i18n("Properties"));
connect(action, &QAction::triggered, this, [this]() {
slotHandleAttachment(Viewer::Properties);
});
menu.exec(globalPos);
}
void ViewerPrivate::prepareHandleAttachment(KMime::Content *node)
{
mCurrentContent = node;
}
QString ViewerPrivate::createAtmFileLink(const QString &atmFileName) const
{
QFileInfo atmFileInfo(atmFileName);
// tempfile name is /TMP/attachmentsRANDOM/atmFileInfo.fileName()"
const QString tmpPath = QDir::tempPath() + QLatin1String("/attachments");
QDir().mkpath(tmpPath);
QTemporaryDir *linkDir = new QTemporaryDir(tmpPath);
QString linkPath = linkDir->path() + QLatin1Char('/') + atmFileInfo.fileName();
QFile *linkFile = new QFile(linkPath);
linkFile->open(QIODevice::ReadWrite);
const QString linkName = linkFile->fileName();
delete linkFile;
delete linkDir;
if (::link(QFile::encodeName(atmFileName).constData(),
QFile::encodeName(linkName).constData()) == 0) {
return linkName; // success
}
return QString();
}
KService::Ptr ViewerPrivate::getServiceOffer(KMime::Content *content)
{
const QString fileName = mNodeHelper->writeNodeToTempFile(content);
const QString contentTypeStr = QLatin1String(content->contentType()->mimeType());
// determine the MIME type of the attachment
// prefer the value of the Content-Type header
QMimeDatabase mimeDb;
auto mimetype = mimeDb.mimeTypeForName(contentTypeStr);
if (mimetype.isValid() && mimetype.inherits(KContacts::Addressee::mimeType())) {
attachmentView(content);
return KService::Ptr(nullptr);
}
if (!mimetype.isValid() || mimetype.name() == QLatin1String("application/octet-stream")) {
/*TODO(Andris) port when on-demand loading is done && msgPart.isComplete() */
mimetype = MimeTreeParser::Util::mimetype(fileName);
}
return KMimeTypeTrader::self()->preferredService(mimetype.name(),
QStringLiteral("Application"));
}
KMime::Content::List ViewerPrivate::selectedContents()
{
return mMimePartTree->selectedContents();
}
void ViewerPrivate::attachmentOpenWith(KMime::Content *node, const KService::Ptr &offer)
{
QString name = mNodeHelper->writeNodeToTempFile(node);
// Make sure that it will not deleted when we switch from message.
QTemporaryDir *tmpDir
= new QTemporaryDir(QDir::tempPath() + QLatin1String("/messageviewer_attachment_XXXXXX"));
if (tmpDir->isValid()) {
tmpDir->setAutoRemove(false);
const QString path = tmpDir->path();
delete tmpDir;
QFile f(name);
const QUrl tmpFileName = QUrl::fromLocalFile(name);
const QString newPath = path + QLatin1Char('/') + tmpFileName.fileName();
if (!f.copy(newPath)) {
qCDebug(MESSAGEVIEWER_LOG) << " File was not able to copy: filename: " << name
<< " to " << path;
} else {
name = newPath;
}
f.close();
} else {
delete tmpDir;
}
QList lst;
const QFileDevice::Permissions perms = QFile::permissions(name);
QFile::setPermissions(name, perms | QFileDevice::ReadUser | QFileDevice::WriteUser);
const QUrl url = QUrl::fromLocalFile(name);
lst.append(url);
if (offer) {
if ((!KRun::runService(*offer, lst, nullptr, true))) {
QFile::remove(url.toLocalFile());
}
} else {
if ((!KRun::displayOpenWithDialog(lst, mMainWindow, true))) {
QFile::remove(url.toLocalFile());
}
}
}
void ViewerPrivate::attachmentOpen(KMime::Content *node)
{
KService::Ptr offer = getServiceOffer(node);
if (!offer) {
qCDebug(MESSAGEVIEWER_LOG) << "got no offer";
return;
}
attachmentOpenWith(node, offer);
}
bool ViewerPrivate::showEmoticons() const
{
return mForceEmoticons;
}
HtmlWriter *ViewerPrivate::htmlWriter() const
{
return mHtmlWriter;
}
CSSHelper *ViewerPrivate::cssHelper() const
{
return mCSSHelper;
}
MimeTreeParser::NodeHelper *ViewerPrivate::nodeHelper() const
{
return mNodeHelper;
}
Viewer *ViewerPrivate::viewer() const
{
return q;
}
Akonadi::Item ViewerPrivate::messageItem() const
{
return mMessageItem;
}
KMime::Message::Ptr ViewerPrivate::message() const
{
return mMessage;
}
bool ViewerPrivate::decryptMessage() const
{
if (!MessageViewer::MessageViewerSettings::self()->alwaysDecrypt()) {
return mDecrytMessageOverwrite;
} else {
return true;
}
}
void ViewerPrivate::displaySplashPage(const QString &message)
{
displaySplashPage(QStringLiteral("status.html"), {
{ QStringLiteral("icon"), QStringLiteral("kmail") },
{ QStringLiteral("name"), i18n("KMail") },
{ QStringLiteral("subtitle"), i18n("The KDE Mail Client") },
{ QStringLiteral("message"), message }
});
}
void ViewerPrivate::displaySplashPage(const QString &templateName, const QVariantHash &data, const QByteArray &domain)
{
mMsgDisplay = false;
adjustLayout();
GrantleeTheme::ThemeManager manager(QStringLiteral("splashPage"),
QStringLiteral("splash.theme"),
nullptr,
QStringLiteral("messageviewer/about/"));
GrantleeTheme::Theme theme = manager.theme(QStringLiteral("default"));
if (theme.isValid()) {
mViewer->setHtml(theme.render(templateName, data, domain),
QUrl::fromLocalFile(theme.absolutePath() + QLatin1Char('/')));
} else {
qCDebug(MESSAGEVIEWER_LOG) << "Theme error: failed to find splash theme";
}
mViewer->show();
}
void ViewerPrivate::enableMessageDisplay()
{
if (mMsgDisplay) {
return;
}
mMsgDisplay = true;
adjustLayout();
}
void ViewerPrivate::displayMessage()
{
showHideMimeTree();
mNodeHelper->setOverrideCodec(mMessage.data(), overrideCodec());
if (mMessageItem.hasAttribute()) {
const MessageViewer::MessageDisplayFormatAttribute *const attr
= mMessageItem.attribute();
setHtmlLoadExtOverride(attr->remoteContent());
setDisplayFormatMessageOverwrite(attr->messageFormat());
}
htmlWriter()->begin();
htmlWriter()->write(mCSSHelper->htmlHead(mUseFixedFont));
if (!mMainWindow) {
q->setWindowTitle(mMessage->subject()->asUnicodeString());
}
// Don't update here, parseMsg() can overwrite the HTML mode, which would lead to flicker.
// It is updated right after parseMsg() instead.
mColorBar->setMode(MimeTreeParser::Util::Normal, HtmlStatusBar::NoUpdate);
if (mMessageItem.hasAttribute()) {
//TODO: Insert link to clear error so that message might be resent
const ErrorAttribute *const attr = mMessageItem.attribute();
Q_ASSERT(attr);
if (!mForegroundError.isValid()) {
const KColorScheme scheme = KColorScheme(QPalette::Active, KColorScheme::View);
mForegroundError = scheme.foreground(KColorScheme::NegativeText).color();
mBackgroundError = scheme.background(KColorScheme::NegativeBackground).color();
}
htmlWriter()->write(QStringLiteral(
"%4
").arg(
mBackgroundError.
name(),
mForegroundError
.
name(),
mForegroundError
.
name(), attr->message().toHtmlEscaped()));
htmlWriter()->write(QStringLiteral(""));
}
parseContent(mMessage.data());
#ifndef QT_NO_TREEVIEW
mMimePartTree->setRoot(mNodeHelper->messageWithExtraContent(mMessage.data()));
#endif
mColorBar->update();
htmlWriter()->write(QStringLiteral("