File indexing completed on 2024-05-12 05:20:44

0001 /*
0002   This file is part of KMail, the KDE mail client.
0003   SPDX-FileCopyrightText: 1997 Markus Wuebben <markus.wuebben@kde.org>
0004   SPDX-FileCopyrightText: 2009-2024 Laurent Montel <montel@kde.org>
0005 
0006   SPDX-License-Identifier: GPL-2.0-or-later
0007 */
0008 
0009 // define this to copy all html that is written to the readerwindow to
0010 // filehtmlwriter.out in the current working directory
0011 #include "kmreaderwin.h"
0012 
0013 #include "dialog/addemailtoexistingcontactdialog.h"
0014 #include "job/addemailtoexistingcontactjob.h"
0015 #include "kmmainwidget.h"
0016 #include "kmreadermainwin.h"
0017 #include <MailCommon/MailKernel>
0018 
0019 #include "kmail-version.h"
0020 #include "kmcommands.h"
0021 #include <Akonadi/AddEmailAddressJob>
0022 #include <Akonadi/AddEmailDisplayJob>
0023 #include <Akonadi/OpenEmailAddressJob>
0024 #include <KEmailAddress>
0025 #include <MailCommon/MailUtil>
0026 #include <MessageViewer/CSSHelper>
0027 #include <MessageViewer/HeaderStrategy>
0028 #include <MessageViewer/MarkMessageReadHandler>
0029 #include <MessageViewer/MessageViewerSettings>
0030 #include <PimCommon/BroadcastStatus>
0031 #include <QVBoxLayout>
0032 using MessageViewer::CSSHelper;
0033 #include "util.h"
0034 #include <Akonadi/MessageFlags>
0035 #include <KMime/MDN>
0036 #include <MessageCore/StringUtil>
0037 #include <QCryptographicHash>
0038 
0039 using namespace MessageViewer;
0040 #include <MessageCore/MessageCoreSettings>
0041 
0042 #include <MessageComposer/Composer>
0043 #include <MessageComposer/InfoPart>
0044 #include <MessageComposer/MDNWarningWidgetJob>
0045 #include <MessageComposer/MessageSender>
0046 #include <MessageComposer/TextPart>
0047 #include <MessageViewer/AttachmentStrategy>
0048 
0049 #include <KIO/JobUiDelegate>
0050 
0051 #include <Akonadi/ChangeRecorder>
0052 #include <Akonadi/ContactEditorDialog>
0053 
0054 #include "kmail_debug.h"
0055 #include <KActionCollection>
0056 #include <KLocalizedString>
0057 #include <KMessageBox>
0058 #include <KToggleAction>
0059 #include <QAction>
0060 #include <QDesktopServices>
0061 #include <QMenu>
0062 
0063 #include <QClipboard>
0064 
0065 // X headers...
0066 #undef Never
0067 #undef Always
0068 
0069 #include <MailCommon/MDNWarningJob>
0070 #include <MailCommon/MailUtil>
0071 
0072 #include <KLazyLocalizedString>
0073 
0074 using namespace KMail;
0075 using namespace MailCommon;
0076 
0077 KMReaderWin::KMReaderWin(QWidget *aParent, QWidget *mainWindow, KActionCollection *actionCollection)
0078     : QWidget(aParent)
0079     , mMainWindow(mainWindow)
0080     , mActionCollection(actionCollection)
0081 {
0082     createActions();
0083     auto vlay = new QVBoxLayout(this);
0084     vlay->setContentsMargins(0, 0, 0, 0);
0085     mViewer = new Viewer(this, mainWindow, mActionCollection);
0086     mViewer->setIdentityManager(kmkernel->identityManager());
0087     connect(mViewer, qOverload<const Akonadi::Item &, const QUrl &>(&Viewer::urlClicked), this, &KMReaderWin::slotUrlClicked);
0088     connect(mViewer,
0089             &Viewer::requestConfigSync,
0090             kmkernel,
0091             &KMKernel::slotRequestConfigSync,
0092             Qt::QueuedConnection); // happens anyway on shutdown, so we can skip it there with using a queued connection
0093     connect(mViewer, &Viewer::makeResourceOnline, kmkernel, &KMKernel::makeResourceOnline);
0094     connect(mViewer, &MessageViewer::Viewer::showReader, this, &KMReaderWin::slotShowReader);
0095     connect(mViewer, &MessageViewer::Viewer::showMessage, this, &KMReaderWin::slotShowMessage);
0096     connect(mViewer, &MessageViewer::Viewer::showStatusBarMessage, this, &KMReaderWin::showStatusBarMessage);
0097     connect(mViewer, &MessageViewer::Viewer::printingFinished, this, &KMReaderWin::slotPrintingFinished);
0098     connect(mViewer, &MessageViewer::Viewer::zoomChanged, this, &KMReaderWin::zoomChanged);
0099     connect(mViewer, qOverload<const Akonadi::Item &>(&Viewer::deleteMessage), this, &KMReaderWin::slotDeleteMessage);
0100     connect(mViewer, &MessageViewer::Viewer::showNextMessage, this, &KMReaderWin::showNextMessage);
0101     connect(mViewer, &MessageViewer::Viewer::showPreviousMessage, this, &KMReaderWin::showPreviousMessage);
0102     connect(mViewer, &MessageViewer::Viewer::sendResponse, this, &KMReaderWin::slotSendMdnResponse);
0103     connect(kmkernel->folderCollectionMonitor(), &Akonadi::Monitor::itemChanged, this, &KMReaderWin::slotItemModified);
0104 
0105     mViewer->addMessageLoadedHandler(new MessageViewer::MarkMessageReadHandler(this));
0106 
0107     vlay->addWidget(mViewer);
0108     readConfig();
0109 }
0110 
0111 void KMReaderWin::createActions()
0112 {
0113     KActionCollection *ac = mActionCollection;
0114     if (!ac) {
0115         return;
0116     }
0117     //
0118     // Message Menu
0119     //
0120     // new message to
0121     mMailToComposeAction = new QAction(QIcon::fromTheme(QStringLiteral("mail-message-new")), i18n("New Message To..."), this);
0122     ac->addAction(QStringLiteral("mail_new"), mMailToComposeAction);
0123     ac->setShortcutsConfigurable(mMailToComposeAction, false);
0124     connect(mMailToComposeAction, &QAction::triggered, this, &KMReaderWin::slotMailtoCompose);
0125 
0126     // reply to
0127     mMailToReplyAction = new QAction(QIcon::fromTheme(QStringLiteral("mail-reply-sender")), i18n("Reply To..."), this);
0128     ac->addAction(QStringLiteral("mailto_reply"), mMailToReplyAction);
0129     ac->setShortcutsConfigurable(mMailToReplyAction, false);
0130     connect(mMailToReplyAction, &QAction::triggered, this, &KMReaderWin::slotMailtoReply);
0131 
0132     // forward to
0133     mMailToForwardAction = new QAction(QIcon::fromTheme(QStringLiteral("mail-forward")), i18n("Forward To..."), this);
0134     ac->setShortcutsConfigurable(mMailToForwardAction, false);
0135     ac->addAction(QStringLiteral("mailto_forward"), mMailToForwardAction);
0136     connect(mMailToForwardAction, &QAction::triggered, this, &KMReaderWin::slotMailtoForward);
0137 
0138     // add to addressbook
0139     mAddAddrBookAction = new QAction(QIcon::fromTheme(QStringLiteral("contact-new")), i18n("Add to Address Book"), this);
0140     ac->setShortcutsConfigurable(mAddAddrBookAction, false);
0141     ac->addAction(QStringLiteral("add_addr_book"), mAddAddrBookAction);
0142     connect(mAddAddrBookAction, &QAction::triggered, this, &KMReaderWin::slotMailtoAddAddrBook);
0143 
0144     mAddEmailToExistingContactAction = new QAction(QIcon::fromTheme(QStringLiteral("contact-new")), i18n("Add to Existing Contact"), this);
0145     ac->setShortcutsConfigurable(mAddEmailToExistingContactAction, false);
0146     ac->addAction(QStringLiteral("add_to_existing_contact"), mAddAddrBookAction);
0147     connect(mAddEmailToExistingContactAction, &QAction::triggered, this, &KMReaderWin::slotMailToAddToExistingContact);
0148 
0149     // open in addressbook
0150     mOpenAddrBookAction = new QAction(QIcon::fromTheme(QStringLiteral("view-pim-contacts")), i18n("Open in Address Book"), this);
0151     ac->setShortcutsConfigurable(mOpenAddrBookAction, false);
0152     ac->addAction(QStringLiteral("openin_addr_book"), mOpenAddrBookAction);
0153     connect(mOpenAddrBookAction, &QAction::triggered, this, &KMReaderWin::slotMailtoOpenAddrBook);
0154     // bookmark message
0155     mAddUrlToBookmarkAction = new QAction(QIcon::fromTheme(QStringLiteral("bookmark-new")), i18n("Bookmark This Link"), this);
0156     ac->setShortcutsConfigurable(mAddUrlToBookmarkAction, false);
0157     ac->addAction(QStringLiteral("add_bookmarks"), mAddUrlToBookmarkAction);
0158     connect(mAddUrlToBookmarkAction, &QAction::triggered, this, &KMReaderWin::slotAddUrlToBookmark);
0159 
0160     mEditContactAction = new QAction(QIcon::fromTheme(QStringLiteral("view-pim-contacts")), i18n("Edit contact..."), this);
0161     ac->setShortcutsConfigurable(mEditContactAction, false);
0162     ac->addAction(QStringLiteral("edit_contact"), mOpenAddrBookAction);
0163     connect(mEditContactAction, &QAction::triggered, this, &KMReaderWin::slotEditContact);
0164 
0165     // save URL as
0166     mUrlSaveAsAction = new QAction(i18n("Save Link As..."), this);
0167     ac->addAction(QStringLiteral("saveas_url"), mUrlSaveAsAction);
0168     ac->setShortcutsConfigurable(mUrlSaveAsAction, false);
0169     connect(mUrlSaveAsAction, &QAction::triggered, this, &KMReaderWin::slotUrlSave);
0170 
0171     // find text
0172     auto action = new QAction(QIcon::fromTheme(QStringLiteral("edit-find")), i18n("&Find in Message..."), this);
0173     ac->addAction(QStringLiteral("find_in_messages"), action);
0174     connect(action, &QAction::triggered, this, &KMReaderWin::slotFind);
0175     ac->setDefaultShortcut(action, KStandardShortcut::find().first());
0176 
0177     // save Image On Disk
0178     mImageUrlSaveAsAction = new QAction(i18n("Save Image On Disk..."), this);
0179     ac->addAction(QStringLiteral("saveas_imageurl"), mImageUrlSaveAsAction);
0180     ac->setShortcutsConfigurable(mImageUrlSaveAsAction, false);
0181     connect(mImageUrlSaveAsAction, &QAction::triggered, this, &KMReaderWin::slotSaveImageOnDisk);
0182 
0183     // save Image On Disk
0184     mOpenImageAction = new QAction(i18n("Open Image..."), this);
0185     ac->addAction(QStringLiteral("open_image"), mOpenImageAction);
0186     ac->setShortcutsConfigurable(mOpenImageAction, false);
0187     connect(mOpenImageAction, &QAction::triggered, this, &KMReaderWin::slotOpenImage);
0188 
0189     // View html options
0190     mViewHtmlOptions = new QMenu(i18n("Show HTML Format"), this);
0191     mViewAsHtml = new QAction(i18n("Show HTML format when mail comes from this contact"), mViewHtmlOptions);
0192     ac->setShortcutsConfigurable(mViewAsHtml, false);
0193     connect(mViewAsHtml, &QAction::triggered, this, &KMReaderWin::slotContactHtmlOptions);
0194     mViewAsHtml->setCheckable(true);
0195     mViewHtmlOptions->addAction(mViewAsHtml);
0196 
0197     mLoadExternalReference = new QAction(i18n("Load external reference when mail comes for this contact"), mViewHtmlOptions);
0198     ac->setShortcutsConfigurable(mLoadExternalReference, false);
0199     connect(mLoadExternalReference, &QAction::triggered, this, &KMReaderWin::slotContactHtmlOptions);
0200     mLoadExternalReference->setCheckable(true);
0201     mViewHtmlOptions->addAction(mLoadExternalReference);
0202 
0203     mShareImage = new QAction(i18n("Share image..."), this);
0204     ac->addAction(QStringLiteral("share_imageurl"), mShareImage);
0205     ac->setShortcutsConfigurable(mShareImage, false);
0206     connect(mShareImage, &QAction::triggered, this, &KMReaderWin::slotShareImage);
0207 }
0208 
0209 void KMReaderWin::setUseFixedFont(bool useFixedFont)
0210 {
0211     mViewer->setUseFixedFont(useFixedFont);
0212 }
0213 
0214 Viewer *KMReaderWin::viewer() const
0215 {
0216     return mViewer;
0217 }
0218 
0219 bool KMReaderWin::isFixedFont() const
0220 {
0221     return mViewer->isFixedFont();
0222 }
0223 
0224 KMReaderWin::~KMReaderWin() = default;
0225 
0226 void KMReaderWin::readConfig()
0227 {
0228     mViewer->readConfig();
0229 }
0230 
0231 void KMReaderWin::setAttachmentStrategy(const MessageViewer::AttachmentStrategy *strategy)
0232 {
0233     mViewer->setAttachmentStrategy(strategy);
0234 }
0235 
0236 void KMReaderWin::setOverrideEncoding(const QString &encoding)
0237 {
0238     mViewer->setOverrideEncoding(encoding);
0239 }
0240 
0241 void KMReaderWin::clearCache()
0242 {
0243     clear();
0244 }
0245 
0246 void KMReaderWin::updateShowMultiMessagesButton(bool enablePreviousButton, bool enableNextButton)
0247 {
0248     mViewer->updateShowMultiMessagesButton(enablePreviousButton, enableNextButton);
0249 }
0250 
0251 void KMReaderWin::hasMultiMessages(bool multi)
0252 {
0253     mViewer->hasMultiMessages(multi);
0254 }
0255 
0256 // enter items for the "Important changes" list here:
0257 static const KLazyLocalizedString kmailChanges[] = {
0258     kli18n("KMail is now based on the Akonadi Personal Information Management framework, which brings many "
0259            "changes all around.")};
0260 static const int numKMailChanges = sizeof kmailChanges / sizeof *kmailChanges;
0261 
0262 // enter items for the "new features" list here, so the main body of
0263 // the welcome page can be left untouched (probably much easier for
0264 // the translators). Note that the <li>...</li> tags are added
0265 // automatically below:
0266 static const KLazyLocalizedString kmailNewFeatures[] = {
0267 
0268     kli18n("Push email (IMAP IDLE)"),
0269     kli18n("Improved searches"),
0270     kli18n("Support for adding notes (annotations) to mails"),
0271     kli18n("Less GUI freezes, mail checks happen in the background"),
0272     kli18n("Plugins support"),
0273     kli18n("New HTML renderer (QtWebEngine)"),
0274     kli18n("Added Check for Phishing URL"),
0275     kli18n("Added Check DKIM"),
0276     kli18n("Added Markdown Editing Support"),
0277     kli18n("Added Grammar Plugin Support"),
0278     kli18n("Added etesync resource support"),
0279     kli18n("Added Microsoft Exchange Web Services resource support"),
0280 };
0281 static const int numKMailNewFeatures = sizeof kmailNewFeatures / sizeof *kmailNewFeatures;
0282 
0283 // static
0284 QString KMReaderWin::newFeaturesMD5()
0285 {
0286     QByteArray str;
0287     for (int i = 0; i < numKMailChanges; ++i) {
0288         str += kmailChanges[i].untranslatedText();
0289     }
0290     for (int i = 0; i < numKMailNewFeatures; ++i) {
0291         str += kmailNewFeatures[i].untranslatedText();
0292     }
0293     QCryptographicHash md5(QCryptographicHash::Md5);
0294     md5.addData(str);
0295     return QLatin1StringView(md5.result().toBase64());
0296 }
0297 
0298 void KMReaderWin::displaySplashPage(const QString &templateName, const QVariantHash &_data)
0299 {
0300     QVariantHash data = _data;
0301     if (!data.contains(QStringLiteral("icon"))) {
0302         data[QStringLiteral("icon")] = QStringLiteral("kmail");
0303     }
0304     if (!data.contains(QStringLiteral("name"))) {
0305         data[QStringLiteral("name")] = i18n("KMail");
0306     }
0307     if (!data.contains(QStringLiteral("subtitle"))) {
0308         data[QStringLiteral("subtitle")] = i18n("The KDE Mail Client");
0309     }
0310 
0311     mViewer->displaySplashPage(templateName, data, QByteArrayLiteral("kmail"));
0312 }
0313 
0314 void KMReaderWin::displayBusyPage()
0315 {
0316     displaySplashPage(QStringLiteral("status.html"),
0317                       {{QStringLiteral("title"), i18n("Retrieving Folder Contents")}, {QStringLiteral("subtext"), i18n("Please wait . . .")}});
0318 }
0319 
0320 void KMReaderWin::displayOfflinePage()
0321 {
0322     displaySplashPage(QStringLiteral("status.html"),
0323                       {{QStringLiteral("title"), i18n("Offline")},
0324                        {QStringLiteral("subtext"),
0325                         i18n("KMail is currently in offline mode. "
0326                              "Click <a href=\"kmail:goOnline\">here</a> to go online . . .</p>")}});
0327 }
0328 
0329 void KMReaderWin::displayResourceOfflinePage()
0330 {
0331     displaySplashPage(QStringLiteral("status.html"),
0332                       {{QStringLiteral("title"), i18n("Offline")},
0333                        {QStringLiteral("subtext"),
0334                         i18n("Account is currently in offline mode. "
0335                              "Click <a href=\"kmail:goResourceOnline\">here</a> to go online . . .</p>")}});
0336 }
0337 
0338 void KMReaderWin::displayAboutPage()
0339 {
0340     QVariantHash data;
0341     data[QStringLiteral("version")] = QStringLiteral(KDEPIM_VERSION);
0342     data[QStringLiteral("firstStart")] = kmkernel->firstStart();
0343 
0344     QVariantList features;
0345     features.reserve(numKMailNewFeatures);
0346     for (int i = 0; i < numKMailNewFeatures; ++i) {
0347         features.push_back(kmailNewFeatures[i].toString());
0348     }
0349     data[QStringLiteral("newFeatures")] = features;
0350 
0351     QVariantList changes;
0352     changes.reserve(numKMailChanges);
0353     for (int i = 0; i < numKMailChanges; ++i) {
0354         features.push_back(kmailChanges[i].toString());
0355     }
0356     data[QStringLiteral("importantChanges")] = changes;
0357 
0358     displaySplashPage(QStringLiteral(":/about/introduction_kmail.html"), data);
0359 }
0360 
0361 void KMReaderWin::slotFind()
0362 {
0363     mViewer->slotFind();
0364 }
0365 
0366 void KMReaderWin::slotCopySelectedText()
0367 {
0368     QString selection = mViewer->selectedText();
0369     selection.replace(QChar::Nbsp, QLatin1Char(' '));
0370     QApplication::clipboard()->setText(selection);
0371 }
0372 
0373 void KMReaderWin::setMsgPart(KMime::Content *aMsgPart)
0374 {
0375     mViewer->setMessagePart(aMsgPart);
0376 }
0377 
0378 QString KMReaderWin::copyText() const
0379 {
0380     return mViewer->selectedText();
0381 }
0382 
0383 MessageViewer::Viewer::DisplayFormatMessage KMReaderWin::displayFormatMessageOverwrite() const
0384 {
0385     return mViewer->displayFormatMessageOverwrite();
0386 }
0387 
0388 void KMReaderWin::setPrintElementBackground(bool printElementBackground)
0389 {
0390     mViewer->setPrintElementBackground(printElementBackground);
0391 }
0392 
0393 void KMReaderWin::setDisplayFormatMessageOverwrite(MessageViewer::Viewer::DisplayFormatMessage format)
0394 {
0395     mViewer->setDisplayFormatMessageOverwrite(format);
0396 }
0397 
0398 void KMReaderWin::setHtmlLoadExtDefault(bool loadExtDefault)
0399 {
0400     mViewer->setHtmlLoadExtDefault(loadExtDefault);
0401 }
0402 
0403 void KMReaderWin::setHtmlLoadExtOverride(bool loadExtOverride)
0404 {
0405     mViewer->setHtmlLoadExtOverride(loadExtOverride);
0406 }
0407 
0408 bool KMReaderWin::htmlMail() const
0409 {
0410     return mViewer->htmlMail();
0411 }
0412 
0413 bool KMReaderWin::htmlLoadExternal()
0414 {
0415     return mViewer->htmlLoadExternal();
0416 }
0417 
0418 Akonadi::Item KMReaderWin::messageItem() const
0419 {
0420     return mViewer->messageItem();
0421 }
0422 
0423 QWidget *KMReaderWin::mainWindow() const
0424 {
0425     return mMainWindow;
0426 }
0427 
0428 void KMReaderWin::slotMailtoCompose()
0429 {
0430     KMCommand *command = new KMMailtoComposeCommand(urlClicked(), messageItem());
0431     command->start();
0432 }
0433 
0434 void KMReaderWin::slotMailtoForward()
0435 {
0436     KMCommand *command = new KMMailtoForwardCommand(mMainWindow, urlClicked(), messageItem());
0437     command->start();
0438 }
0439 
0440 void KMReaderWin::slotMailtoAddAddrBook()
0441 {
0442     const QUrl url = urlClicked();
0443     if (url.isEmpty()) {
0444         return;
0445     }
0446     const QString emailString = KEmailAddress::decodeMailtoUrl(url);
0447 
0448     auto job = new Akonadi::AddEmailAddressJob(emailString, mMainWindow, this);
0449     job->setInteractive(true);
0450     connect(job, &Akonadi::AddEmailAddressJob::successMessage, this, [](const QString &message) {
0451         PimCommon::BroadcastStatus::instance()->setStatusMsg(message);
0452     });
0453     job->start();
0454 }
0455 
0456 void KMReaderWin::slotMailToAddToExistingContact()
0457 {
0458     const QUrl url = urlClicked();
0459     if (url.isEmpty()) {
0460         return;
0461     }
0462     const QString emailString = KEmailAddress::decodeMailtoUrl(url);
0463     QPointer<AddEmailToExistingContactDialog> dlg = new AddEmailToExistingContactDialog(this);
0464     if (dlg->exec()) {
0465         Akonadi::Item item = dlg->selectedContact();
0466         if (item.isValid()) {
0467             auto job = new AddEmailToExistingContactJob(item, emailString, this);
0468             job->start();
0469         }
0470     }
0471     delete dlg;
0472 }
0473 
0474 void KMReaderWin::slotMailtoOpenAddrBook()
0475 {
0476     const QUrl url = urlClicked();
0477     if (url.isEmpty()) {
0478         return;
0479     }
0480     const QString emailString = KEmailAddress::decodeMailtoUrl(url).toLower();
0481 
0482     auto job = new Akonadi::OpenEmailAddressJob(emailString, mMainWindow, this);
0483     job->start();
0484 }
0485 
0486 void KMReaderWin::slotAddUrlToBookmark()
0487 {
0488     const QUrl url = urlClicked();
0489     if (url.isEmpty()) {
0490         return;
0491     }
0492     KMCommand *command = new KMAddBookmarksCommand(url, this);
0493     command->start();
0494 }
0495 
0496 void KMReaderWin::slotUrlSave()
0497 {
0498     const QUrl url = urlClicked();
0499     if (url.isEmpty()) {
0500         return;
0501     }
0502     KMCommand *command = new KMUrlSaveCommand(url, mMainWindow);
0503     command->start();
0504 }
0505 
0506 void KMReaderWin::slotOpenImage()
0507 {
0508     const QUrl url = imageUrlClicked();
0509     if (url.isEmpty()) {
0510         return;
0511     }
0512     QDesktopServices::openUrl(url);
0513 }
0514 
0515 void KMReaderWin::slotSaveImageOnDisk()
0516 {
0517     const QUrl url = imageUrlClicked();
0518     if (url.isEmpty()) {
0519         return;
0520     }
0521     KMCommand *command = new KMUrlSaveCommand(url, mMainWindow);
0522     command->start();
0523 }
0524 
0525 void KMReaderWin::slotMailtoReply()
0526 {
0527     auto command = new KMMailtoReplyCommand(mMainWindow, urlClicked(), messageItem(), copyText());
0528     command->setReplyAsHtml(htmlMail());
0529     command->start();
0530 }
0531 
0532 CSSHelper *KMReaderWin::cssHelper() const
0533 {
0534     return mViewer->cssHelper();
0535 }
0536 
0537 bool KMReaderWin::htmlLoadExtOverride() const
0538 {
0539     return mViewer->htmlLoadExtOverride();
0540 }
0541 
0542 void KMReaderWin::setDecryptMessageOverwrite(bool overwrite)
0543 {
0544     mViewer->setDecryptMessageOverwrite(overwrite);
0545 }
0546 
0547 const MessageViewer::AttachmentStrategy *KMReaderWin::attachmentStrategy() const
0548 {
0549     return mViewer->attachmentStrategy();
0550 }
0551 
0552 QString KMReaderWin::overrideEncoding() const
0553 {
0554     return mViewer->overrideEncoding();
0555 }
0556 
0557 KToggleAction *KMReaderWin::toggleFixFontAction() const
0558 {
0559     return mViewer->toggleFixFontAction();
0560 }
0561 
0562 QAction *KMReaderWin::mailToComposeAction() const
0563 {
0564     return mMailToComposeAction;
0565 }
0566 
0567 QAction *KMReaderWin::mailToReplyAction() const
0568 {
0569     return mMailToReplyAction;
0570 }
0571 
0572 QAction *KMReaderWin::mailToForwardAction() const
0573 {
0574     return mMailToForwardAction;
0575 }
0576 
0577 QAction *KMReaderWin::addAddrBookAction() const
0578 {
0579     return mAddAddrBookAction;
0580 }
0581 
0582 QAction *KMReaderWin::openAddrBookAction() const
0583 {
0584     return mOpenAddrBookAction;
0585 }
0586 
0587 bool KMReaderWin::mimePartTreeIsEmpty() const
0588 {
0589     return mViewer->mimePartTreeIsEmpty();
0590 }
0591 
0592 QAction *KMReaderWin::toggleMimePartTreeAction() const
0593 {
0594     return mViewer->toggleMimePartTreeAction();
0595 }
0596 
0597 KActionMenu *KMReaderWin::shareServiceUrlMenu() const
0598 {
0599     return mViewer->shareServiceUrlMenu();
0600 }
0601 
0602 DKIMViewerMenu *KMReaderWin::dkimViewerMenu() const
0603 {
0604     return mViewer->dkimViewerMenu();
0605 }
0606 
0607 RemoteContentMenu *KMReaderWin::remoteContentMenu() const
0608 {
0609     return mViewer->remoteContentMenu();
0610 }
0611 
0612 QList<QAction *> KMReaderWin::viewerPluginActionList(ViewerPluginInterface::SpecificFeatureTypes features)
0613 {
0614     return mViewer->viewerPluginActionList(features);
0615 }
0616 
0617 QAction *KMReaderWin::selectAllAction() const
0618 {
0619     return mViewer->selectAllAction();
0620 }
0621 
0622 QAction *KMReaderWin::copyURLAction() const
0623 {
0624     return mViewer->copyURLAction();
0625 }
0626 
0627 QAction *KMReaderWin::copyImageLocation() const
0628 {
0629     return mViewer->copyImageLocation();
0630 }
0631 
0632 QAction *KMReaderWin::copyAction() const
0633 {
0634     return mViewer->copyAction();
0635 }
0636 
0637 QAction *KMReaderWin::viewSourceAction() const
0638 {
0639     return mViewer->viewSourceAction();
0640 }
0641 
0642 QAction *KMReaderWin::saveAsAction() const
0643 {
0644     return mViewer->saveAsAction();
0645 }
0646 
0647 QAction *KMReaderWin::findInMessageAction() const
0648 {
0649     return mViewer->findInMessageAction();
0650 }
0651 
0652 QAction *KMReaderWin::urlOpenAction() const
0653 {
0654     return mViewer->urlOpenAction();
0655 }
0656 
0657 QAction *KMReaderWin::urlSaveAsAction() const
0658 {
0659     return mUrlSaveAsAction;
0660 }
0661 
0662 QAction *KMReaderWin::addUrlToBookmarkAction() const
0663 {
0664     return mAddUrlToBookmarkAction;
0665 }
0666 
0667 void KMReaderWin::setPrinting(bool enable)
0668 {
0669     mViewer->setPrinting(enable);
0670 }
0671 
0672 QAction *KMReaderWin::speakTextAction() const
0673 {
0674     return mViewer->speakTextAction();
0675 }
0676 
0677 QAction *KMReaderWin::shareTextAction() const
0678 {
0679     return mViewer->shareTextAction();
0680 }
0681 
0682 QAction *KMReaderWin::downloadImageToDiskAction() const
0683 {
0684     return mImageUrlSaveAsAction;
0685 }
0686 
0687 QAction *KMReaderWin::openImageAction() const
0688 {
0689     return mOpenImageAction;
0690 }
0691 
0692 void KMReaderWin::clear(bool force)
0693 {
0694     mViewer->clear(force ? MimeTreeParser::Force : MimeTreeParser::Delayed);
0695 }
0696 
0697 void KMReaderWin::setMessage(const Akonadi::Item &item, MimeTreeParser::UpdateMode updateMode)
0698 {
0699     qCDebug(KMAIL_LOG) << Q_FUNC_INFO << parentWidget();
0700     mViewer->setFolderIdentity(MailCommon::Util::folderIdentity(item));
0701     mViewer->setMessageItem(item, updateMode);
0702     if (!item.hasAttribute<Akonadi::MDNStateAttribute>()
0703         || (item.hasAttribute<Akonadi::MDNStateAttribute>()
0704             && item.attribute<Akonadi::MDNStateAttribute>()->mdnState() == Akonadi::MDNStateAttribute::MDNStateUnknown)) {
0705         sendMdnInfo(item);
0706     } else {
0707         mViewer->mdnWarningAnimatedHide();
0708     }
0709 }
0710 
0711 void KMReaderWin::setMessage(const KMime::Message::Ptr &message)
0712 {
0713     mViewer->setFolderIdentity(0);
0714     mViewer->setMessage(message);
0715 }
0716 
0717 QUrl KMReaderWin::urlClicked() const
0718 {
0719     return mViewer->urlClicked();
0720 }
0721 
0722 QUrl KMReaderWin::imageUrlClicked() const
0723 {
0724     return mViewer->imageUrlClicked();
0725 }
0726 
0727 void KMReaderWin::update(bool force)
0728 {
0729     mViewer->update(force ? MimeTreeParser::Force : MimeTreeParser::Delayed);
0730 }
0731 
0732 void KMReaderWin::slotUrlClicked(const Akonadi::Item &item, const QUrl &url)
0733 {
0734     if (item.isValid() && item.parentCollection().isValid()) {
0735         const auto col = CommonKernel->collectionFromId(item.parentCollection().id());
0736         QSharedPointer<FolderSettings> fd = FolderSettings::forCollection(col, false);
0737         KMail::Util::handleClickedURL(url, fd, item.parentCollection());
0738         return;
0739     }
0740     // No folder so we can't have identity and template.
0741     KMail::Util::handleClickedURL(url);
0742 }
0743 
0744 void KMReaderWin::slotShowReader(KMime::Content *msgPart, bool html, const QString &encoding)
0745 {
0746     const MessageViewer::Viewer::DisplayFormatMessage format = html ? MessageViewer::Viewer::Html : MessageViewer::Viewer::Text;
0747     auto win = new KMReaderMainWin(msgPart, format, encoding);
0748     win->show();
0749 }
0750 
0751 void KMReaderWin::slotShowMessage(const KMime::Message::Ptr &message, const QString &encoding)
0752 {
0753     auto win = new KMReaderMainWin();
0754     win->showMessage(encoding, message);
0755     win->show();
0756 }
0757 
0758 void KMReaderWin::slotDeleteMessage(const Akonadi::Item &item)
0759 {
0760     if (!item.isValid()) {
0761         return;
0762     }
0763     auto command = new KMTrashMsgCommand(item.parentCollection(), item, -1);
0764     command->start();
0765 }
0766 
0767 bool KMReaderWin::printSelectedText(bool preview)
0768 {
0769     const QString str = mViewer->selectedText();
0770     if (str.isEmpty()) {
0771         return false;
0772     }
0773     auto composer = new ::MessageComposer::Composer;
0774     composer->textPart()->setCleanPlainText(str);
0775     composer->textPart()->setWrappedPlainText(str);
0776     auto messagePtr = messageItem().payload<KMime::Message::Ptr>();
0777     composer->infoPart()->setFrom(messagePtr->from()->asUnicodeString());
0778     composer->infoPart()->setTo(QStringList() << messagePtr->to()->asUnicodeString());
0779     composer->infoPart()->setCc(QStringList() << messagePtr->cc()->asUnicodeString());
0780     if (auto subject = messagePtr->subject(false)) {
0781         composer->infoPart()->setSubject(subject->asUnicodeString());
0782     }
0783     composer->setProperty("preview", preview);
0784     connect(composer, &::MessageComposer::Composer::result, this, &KMReaderWin::slotPrintComposeResult);
0785     composer->start();
0786     return true;
0787 }
0788 
0789 void KMReaderWin::slotPrintComposeResult(KJob *job)
0790 {
0791     const bool preview = job->property("preview").toBool();
0792     auto composer = qobject_cast<::MessageComposer::Composer *>(job);
0793     Q_ASSERT(composer);
0794     if (composer->error() == ::MessageComposer::Composer::NoError) {
0795         Q_ASSERT(composer->resultMessages().size() == 1);
0796         Akonadi::Item printItem;
0797         printItem.setPayload<KMime::Message::Ptr>(composer->resultMessages().constFirst());
0798         Akonadi::MessageFlags::copyMessageFlags(*(composer->resultMessages().constFirst()), printItem);
0799         const bool useFixedFont = MessageViewer::MessageViewerSettings::self()->useFixedFont();
0800         const QString overrideEncoding = MessageCore::MessageCoreSettings::self()->overrideCharacterEncoding();
0801 
0802         KMPrintCommandInfo commandInfo;
0803         commandInfo.mMsg = printItem;
0804         commandInfo.mHeaderStylePlugin = mViewer->headerStylePlugin();
0805         commandInfo.mFormat = mViewer->displayFormatMessageOverwrite();
0806         commandInfo.mHtmlLoadExtOverride = mViewer->htmlLoadExternal();
0807         commandInfo.mPrintPreview = preview;
0808         commandInfo.mUseFixedFont = useFixedFont;
0809         commandInfo.mOverrideFont = overrideEncoding;
0810         commandInfo.mShowSignatureDetails =
0811             mViewer->showSignatureDetails() || MessageViewer::MessageViewerSettings::self()->alwaysShowEncryptionSignatureDetails();
0812         commandInfo.mShowEncryptionDetails =
0813             mViewer->showEncryptionDetails() || MessageViewer::MessageViewerSettings::self()->alwaysShowEncryptionSignatureDetails();
0814 
0815         auto command = new KMPrintCommand(this, commandInfo);
0816         command->start();
0817     } else {
0818         if (static_cast<KIO::Job *>(job)->uiDelegate()) {
0819             static_cast<KIO::Job *>(job)->uiDelegate()->showErrorMessage();
0820         } else {
0821             qCWarning(KMAIL_LOG) << "Composer for printing failed:" << composer->errorString();
0822         }
0823     }
0824 }
0825 
0826 void KMReaderWin::clearContactItem()
0827 {
0828     mSearchedContact = Akonadi::Item();
0829     mSearchedAddress = KContacts::Addressee();
0830     mLoadExternalReference->setChecked(false);
0831     mViewAsHtml->setChecked(false);
0832 }
0833 
0834 void KMReaderWin::setContactItem(const Akonadi::Item &contact, const KContacts::Addressee &address)
0835 {
0836     mSearchedContact = contact;
0837     mSearchedAddress = address;
0838     updateHtmlActions();
0839 }
0840 
0841 void KMReaderWin::updateHtmlActions()
0842 {
0843     if (!mSearchedContact.isValid()) {
0844         mLoadExternalReference->setChecked(false);
0845         mViewAsHtml->setChecked(false);
0846     } else {
0847         const QStringList customs = mSearchedAddress.customs();
0848         for (const QString &custom : customs) {
0849             if (custom.contains(QLatin1StringView("MailPreferedFormatting"))) {
0850                 const QString value = mSearchedAddress.custom(QStringLiteral("KADDRESSBOOK"), QStringLiteral("MailPreferedFormatting"));
0851                 mViewAsHtml->setChecked(value == QLatin1StringView("HTML"));
0852             } else if (custom.contains(QLatin1StringView("MailAllowToRemoteContent"))) {
0853                 const QString value = mSearchedAddress.custom(QStringLiteral("KADDRESSBOOK"), QStringLiteral("MailAllowToRemoteContent"));
0854                 mLoadExternalReference->setChecked((value == QLatin1StringView("TRUE")));
0855             }
0856         }
0857     }
0858 }
0859 
0860 void KMReaderWin::slotContactHtmlOptions()
0861 {
0862     const QUrl url = urlClicked();
0863     if (url.isEmpty()) {
0864         return;
0865     }
0866     const QString emailString = KEmailAddress::decodeMailtoUrl(url).toLower();
0867 
0868     auto job = new Akonadi::AddEmailDisplayJob(emailString, mMainWindow, this);
0869     job->setMessageId(mViewer->messageItem().id());
0870     connect(job, &Akonadi::AddEmailDisplayJob::contactUpdated, this, &KMReaderWin::slotContactHtmlPreferencesUpdated);
0871     job->setRemoteContent(mLoadExternalReference->isChecked());
0872     job->setShowAsHTML(mViewAsHtml->isChecked());
0873     job->setContact(mSearchedContact);
0874     job->start();
0875 }
0876 
0877 void KMReaderWin::slotContactHtmlPreferencesUpdated(const Akonadi::Item &contact, Akonadi::Item::Id id, bool showAsHTML, bool remoteContent)
0878 {
0879     Q_UNUSED(contact)
0880     if (mViewer->messageItem().id() == id) {
0881         mViewer->slotChangeDisplayMail(showAsHTML ? Viewer::Html : Viewer::Text, remoteContent);
0882     }
0883 }
0884 
0885 void KMReaderWin::slotEditContact()
0886 {
0887     if (mSearchedContact.isValid()) {
0888         QPointer<Akonadi::ContactEditorDialog> dlg = new Akonadi::ContactEditorDialog(Akonadi::ContactEditorDialog::EditMode, this);
0889         connect(dlg.data(), &Akonadi::ContactEditorDialog::contactStored, this, &KMReaderWin::contactStored);
0890         connect(dlg.data(), &Akonadi::ContactEditorDialog::error, this, &KMReaderWin::slotContactEditorError);
0891         dlg->setContact(mSearchedContact);
0892         dlg->exec();
0893         delete dlg;
0894     }
0895 }
0896 
0897 void KMReaderWin::slotContactEditorError(const QString &error)
0898 {
0899     KMessageBox::error(this, i18n("Contact cannot be stored: %1", error), i18nc("@title:window", "Failed to store contact"));
0900 }
0901 
0902 void KMReaderWin::contactStored(const Akonadi::Item &item)
0903 {
0904     if (item.hasPayload<KContacts::Addressee>()) {
0905         const auto contact = item.payload<KContacts::Addressee>();
0906         setContactItem(item, contact);
0907         mViewer->slotChangeDisplayMail(mViewAsHtml->isChecked() ? Viewer::Html : Viewer::Text, mLoadExternalReference->isChecked());
0908     }
0909     PimCommon::BroadcastStatus::instance()->setStatusMsg(i18n("Contact modified successfully"));
0910 }
0911 
0912 QAction *KMReaderWin::saveMessageDisplayFormatAction() const
0913 {
0914     return mViewer->saveMessageDisplayFormatAction();
0915 }
0916 
0917 QAction *KMReaderWin::resetMessageDisplayFormatAction() const
0918 {
0919     return mViewer->resetMessageDisplayFormatAction();
0920 }
0921 
0922 QAction *KMReaderWin::editContactAction() const
0923 {
0924     return mEditContactAction;
0925 }
0926 
0927 QAction *KMReaderWin::developmentToolsAction() const
0928 {
0929     return mViewer->developmentToolsAction();
0930 }
0931 
0932 QMenu *KMReaderWin::viewHtmlOption() const
0933 {
0934     return mViewHtmlOptions;
0935 }
0936 
0937 QAction *KMReaderWin::shareImage() const
0938 {
0939     return mShareImage;
0940 }
0941 
0942 QAction *KMReaderWin::addToExistingContactAction() const
0943 {
0944     return mAddEmailToExistingContactAction;
0945 }
0946 
0947 void KMReaderWin::slotShareImage()
0948 {
0949     KMCommand *command = new KMShareImageCommand(imageUrlClicked(), this);
0950     command->start();
0951 }
0952 
0953 QList<QAction *> KMReaderWin::interceptorUrlActions(const WebEngineViewer::WebHitTestResult &result) const
0954 {
0955     return mViewer->interceptorUrlActions(result);
0956 }
0957 
0958 void KMReaderWin::slotPrintingFinished()
0959 {
0960     if (mViewer->printingMode()) {
0961         deleteLater();
0962     }
0963 }
0964 
0965 void KMReaderWin::sendMdnInfo(const Akonadi::Item &item)
0966 {
0967     auto job = new MessageComposer::MDNWarningWidgetJob(this);
0968     job->setItem(item);
0969     connect(job, &MessageComposer::MDNWarningWidgetJob::showMdnInfo, this, &KMReaderWin::slotShowMdnInfo);
0970     if (!job->start()) {
0971         qCWarning(KMAIL_LOG) << "Impossible to start MDNWarningWidgetJob";
0972     }
0973 }
0974 
0975 void KMReaderWin::slotShowMdnInfo(const QPair<QString, bool> &mdnInfo)
0976 {
0977     mViewer->showMdnInformations(mdnInfo);
0978 }
0979 
0980 void KMReaderWin::slotSendMdnResponse(MessageViewer::MDNWarningWidget::ResponseType type, KMime::MDN::SendingMode sendingMode)
0981 {
0982     MailCommon::MDNWarningJob::ResponseMDN response = MailCommon::MDNWarningJob::ResponseMDN::Unknown;
0983     switch (type) {
0984     case MessageViewer::MDNWarningWidget::ResponseType::Ignore:
0985         response = MailCommon::MDNWarningJob::ResponseMDN::MDNIgnore;
0986         break;
0987     case MessageViewer::MDNWarningWidget::ResponseType::Send:
0988         response = MailCommon::MDNWarningJob::ResponseMDN::Send;
0989         break;
0990     case MessageViewer::MDNWarningWidget::ResponseType::SendDeny:
0991         response = MailCommon::MDNWarningJob::ResponseMDN::Denied;
0992         break;
0993     }
0994 
0995     auto job = new MailCommon::MDNWarningJob(KMKernel::self(), this);
0996     job->setItem(mViewer->messageItem());
0997     job->setResponse(response);
0998     job->setSendingMode(sendingMode);
0999     job->start();
1000     connect(job, &MDNWarningJob::finished, this, [this]() {
1001         mViewer->mdnWarningAnimatedHide();
1002     });
1003 }
1004 
1005 void KMReaderWin::slotItemModified(const Akonadi::Item &item, const QSet<QByteArray> &partIdentifiers)
1006 {
1007     if (mViewer->messageItem().id() == item.id()) {
1008         if (partIdentifiers.contains("MDNStateAttribute")) {
1009             mViewer->mdnWarningAnimatedHide();
1010         }
1011     }
1012 }
1013 
1014 void KMReaderWin::addImageMenuActions(QMenu *menu)
1015 {
1016     menu->addSeparator();
1017     menu->addAction(copyImageLocation());
1018     menu->addAction(downloadImageToDiskAction());
1019     menu->addAction(shareImage());
1020     menu->addAction(openImageAction());
1021 }
1022 
1023 #include "moc_kmreaderwin.cpp"