1 /*
2   This file is part of KMail, the KDE mail client.
3   SPDX-FileCopyrightText: 1997 Markus Wuebben <markus.wuebben@kde.org>
4   SPDX-FileCopyrightText: 2009-2021 Laurent Montel <montel@kde.org>
5 
6   SPDX-License-Identifier: GPL-2.0-or-later
7 */
8 
9 // define this to copy all html that is written to the readerwindow to
10 // filehtmlwriter.out in the current working directory
11 #include "kmreaderwin.h"
12 
13 #include "dialog/addemailtoexistingcontactdialog.h"
14 #include "job/addemailtoexistingcontactjob.h"
15 #include "kmmainwidget.h"
16 #include "kmreadermainwin.h"
17 #include "settings/kmailsettings.h"
18 #include <MailCommon/MailKernel>
19 
20 #include "kmail-version.h"
21 #include "kmcommands.h"
22 #include <Akonadi/Contact/AddEmailAddressJob>
23 #include <Akonadi/Contact/AddEmailDisplayJob>
24 #include <Akonadi/Contact/OpenEmailAddressJob>
25 #include <KEmailAddress>
26 #include <MailCommon/MailUtil>
27 #include <MailCommon/SendMdnHandler>
28 #include <MessageViewer/CSSHelper>
29 #include <MessageViewer/HeaderStrategy>
30 #include <MessageViewer/MarkMessageReadHandler>
31 #include <MessageViewer/MessageViewerSettings>
32 #include <PimCommon/BroadcastStatus>
33 #include <QVBoxLayout>
34 using MessageViewer::CSSHelper;
35 #include "util.h"
36 #include <KMime/MDN>
37 #include <MessageCore/StringUtil>
38 #include <QCryptographicHash>
39 #include <akonadi/kmime/messageflags.h>
40 
41 using namespace MessageViewer;
42 #include <MessageCore/MessageCoreSettings>
43 
44 #include <MessageComposer/Composer>
45 #include <MessageComposer/InfoPart>
46 #include <MessageComposer/MessageSender>
47 #include <MessageComposer/TextPart>
48 #include <MessageViewer/AttachmentStrategy>
49 
50 #include <KIO/JobUiDelegate>
51 
52 #include <Akonadi/Contact/ContactEditorDialog>
53 
54 #include "kmail_debug.h"
55 #include <KActionCollection>
56 #include <KLocalizedString>
57 #include <KMessageBox>
58 #include <KToggleAction>
59 #include <QAction>
60 #include <QMenu>
61 
62 #include <QClipboard>
63 
64 // X headers...
65 #undef Never
66 #undef Always
67 
68 #include <MailCommon/MailUtil>
69 
70 using namespace KMail;
71 using namespace MailCommon;
72 
KMReaderWin(QWidget * aParent,QWidget * mainWindow,KActionCollection * actionCollection)73 KMReaderWin::KMReaderWin(QWidget *aParent, QWidget *mainWindow, KActionCollection *actionCollection)
74     : QWidget(aParent)
75     , mMainWindow(mainWindow)
76     , mActionCollection(actionCollection)
77 {
78     createActions();
79     auto vlay = new QVBoxLayout(this);
80     vlay->setContentsMargins(0, 4, 0, 0);
81     mViewer = new Viewer(this, mainWindow, mActionCollection);
82     mViewer->setIdentityManager(kmkernel->identityManager());
83     connect(mViewer, qOverload<const Akonadi::Item &, const QUrl &>(&Viewer::urlClicked), this, &KMReaderWin::slotUrlClicked);
84     connect(mViewer,
85             &Viewer::requestConfigSync,
86             kmkernel,
87             &KMKernel::slotRequestConfigSync,
88             Qt::QueuedConnection); // happens anyway on shutdown, so we can skip it there with using a queued connection
89     connect(mViewer, &Viewer::makeResourceOnline, kmkernel, &KMKernel::makeResourceOnline);
90     connect(mViewer, &MessageViewer::Viewer::showReader, this, &KMReaderWin::slotShowReader);
91     connect(mViewer, &MessageViewer::Viewer::showMessage, this, &KMReaderWin::slotShowMessage);
92     connect(mViewer, &MessageViewer::Viewer::showStatusBarMessage, this, &KMReaderWin::showStatusBarMessage);
93     connect(mViewer, &MessageViewer::Viewer::printingFinished, this, &KMReaderWin::slotPrintingFinished);
94     connect(mViewer, &MessageViewer::Viewer::zoomChanged, this, &KMReaderWin::zoomChanged);
95     connect(mViewer, qOverload<const Akonadi::Item &>(&Viewer::deleteMessage), this, &KMReaderWin::slotDeleteMessage);
96     connect(mViewer, &MessageViewer::Viewer::showNextMessage, this, &KMReaderWin::showNextMessage);
97     connect(mViewer, &MessageViewer::Viewer::showPreviousMessage, this, &KMReaderWin::showPreviousMessage);
98 
99     mViewer->addMessageLoadedHandler(new MessageViewer::MarkMessageReadHandler(this));
100     mViewer->addMessageLoadedHandler(new MailCommon::SendMdnHandler(kmkernel, this));
101 
102     vlay->addWidget(mViewer);
103     readConfig();
104 }
105 
createActions()106 void KMReaderWin::createActions()
107 {
108     KActionCollection *ac = mActionCollection;
109     if (!ac) {
110         return;
111     }
112     //
113     // Message Menu
114     //
115     // new message to
116     mMailToComposeAction = new QAction(QIcon::fromTheme(QStringLiteral("mail-message-new")), i18n("New Message To..."), this);
117     ac->addAction(QStringLiteral("mail_new"), mMailToComposeAction);
118     ac->setShortcutsConfigurable(mMailToComposeAction, false);
119     connect(mMailToComposeAction, &QAction::triggered, this, &KMReaderWin::slotMailtoCompose);
120 
121     // reply to
122     mMailToReplyAction = new QAction(QIcon::fromTheme(QStringLiteral("mail-reply-sender")), i18n("Reply To..."), this);
123     ac->addAction(QStringLiteral("mailto_reply"), mMailToReplyAction);
124     ac->setShortcutsConfigurable(mMailToReplyAction, false);
125     connect(mMailToReplyAction, &QAction::triggered, this, &KMReaderWin::slotMailtoReply);
126 
127     // forward to
128     mMailToForwardAction = new QAction(QIcon::fromTheme(QStringLiteral("mail-forward")), i18n("Forward To..."), this);
129     ac->setShortcutsConfigurable(mMailToForwardAction, false);
130     ac->addAction(QStringLiteral("mailto_forward"), mMailToForwardAction);
131     connect(mMailToForwardAction, &QAction::triggered, this, &KMReaderWin::slotMailtoForward);
132 
133     // add to addressbook
134     mAddAddrBookAction = new QAction(QIcon::fromTheme(QStringLiteral("contact-new")), i18n("Add to Address Book"), this);
135     ac->setShortcutsConfigurable(mAddAddrBookAction, false);
136     ac->addAction(QStringLiteral("add_addr_book"), mAddAddrBookAction);
137     connect(mAddAddrBookAction, &QAction::triggered, this, &KMReaderWin::slotMailtoAddAddrBook);
138 
139     mAddEmailToExistingContactAction = new QAction(QIcon::fromTheme(QStringLiteral("contact-new")), i18n("Add to Existing Contact"), this);
140     ac->setShortcutsConfigurable(mAddEmailToExistingContactAction, false);
141     ac->addAction(QStringLiteral("add_to_existing_contact"), mAddAddrBookAction);
142     connect(mAddEmailToExistingContactAction, &QAction::triggered, this, &KMReaderWin::slotMailToAddToExistingContact);
143 
144     // open in addressbook
145     mOpenAddrBookAction = new QAction(QIcon::fromTheme(QStringLiteral("view-pim-contacts")), i18n("Open in Address Book"), this);
146     ac->setShortcutsConfigurable(mOpenAddrBookAction, false);
147     ac->addAction(QStringLiteral("openin_addr_book"), mOpenAddrBookAction);
148     connect(mOpenAddrBookAction, &QAction::triggered, this, &KMReaderWin::slotMailtoOpenAddrBook);
149     // bookmark message
150     mAddBookmarksAction = new QAction(QIcon::fromTheme(QStringLiteral("bookmark-new")), i18n("Bookmark This Link"), this);
151     ac->setShortcutsConfigurable(mAddBookmarksAction, false);
152     ac->addAction(QStringLiteral("add_bookmarks"), mAddBookmarksAction);
153     connect(mAddBookmarksAction, &QAction::triggered, this, &KMReaderWin::slotAddBookmarks);
154 
155     mEditContactAction = new QAction(QIcon::fromTheme(QStringLiteral("view-pim-contacts")), i18n("Edit contact..."), this);
156     ac->setShortcutsConfigurable(mEditContactAction, false);
157     ac->addAction(QStringLiteral("edit_contact"), mOpenAddrBookAction);
158     connect(mEditContactAction, &QAction::triggered, this, &KMReaderWin::slotEditContact);
159 
160     // save URL as
161     mUrlSaveAsAction = new QAction(i18n("Save Link As..."), this);
162     ac->addAction(QStringLiteral("saveas_url"), mUrlSaveAsAction);
163     ac->setShortcutsConfigurable(mUrlSaveAsAction, false);
164     connect(mUrlSaveAsAction, &QAction::triggered, this, &KMReaderWin::slotUrlSave);
165 
166     // find text
167     auto action = new QAction(QIcon::fromTheme(QStringLiteral("edit-find")), i18n("&Find in Message..."), this);
168     ac->addAction(QStringLiteral("find_in_messages"), action);
169     connect(action, &QAction::triggered, this, &KMReaderWin::slotFind);
170     ac->setDefaultShortcut(action, KStandardShortcut::find().first());
171 
172     // save Image On Disk
173     mImageUrlSaveAsAction = new QAction(i18n("Save Image On Disk..."), this);
174     ac->addAction(QStringLiteral("saveas_imageurl"), mImageUrlSaveAsAction);
175     ac->setShortcutsConfigurable(mImageUrlSaveAsAction, false);
176     connect(mImageUrlSaveAsAction, &QAction::triggered, this, &KMReaderWin::slotSaveImageOnDisk);
177 
178     // View html options
179     mViewHtmlOptions = new QMenu(i18n("Show HTML Format"), this);
180     mViewAsHtml = new QAction(i18n("Show HTML format when mail comes from this contact"), mViewHtmlOptions);
181     ac->setShortcutsConfigurable(mViewAsHtml, false);
182     connect(mViewAsHtml, &QAction::triggered, this, &KMReaderWin::slotContactHtmlOptions);
183     mViewAsHtml->setCheckable(true);
184     mViewHtmlOptions->addAction(mViewAsHtml);
185 
186     mLoadExternalReference = new QAction(i18n("Load external reference when mail comes for this contact"), mViewHtmlOptions);
187     ac->setShortcutsConfigurable(mLoadExternalReference, false);
188     connect(mLoadExternalReference, &QAction::triggered, this, &KMReaderWin::slotContactHtmlOptions);
189     mLoadExternalReference->setCheckable(true);
190     mViewHtmlOptions->addAction(mLoadExternalReference);
191 
192     mShareImage = new QAction(i18n("Share image..."), this);
193     ac->addAction(QStringLiteral("share_imageurl"), mShareImage);
194     ac->setShortcutsConfigurable(mShareImage, false);
195     connect(mShareImage, &QAction::triggered, this, &KMReaderWin::slotShareImage);
196 }
197 
setUseFixedFont(bool useFixedFont)198 void KMReaderWin::setUseFixedFont(bool useFixedFont)
199 {
200     mViewer->setUseFixedFont(useFixedFont);
201 }
202 
viewer() const203 Viewer *KMReaderWin::viewer() const
204 {
205     return mViewer;
206 }
207 
isFixedFont() const208 bool KMReaderWin::isFixedFont() const
209 {
210     return mViewer->isFixedFont();
211 }
212 
213 KMReaderWin::~KMReaderWin() = default;
214 
readConfig()215 void KMReaderWin::readConfig()
216 {
217     mViewer->readConfig();
218 }
219 
setAttachmentStrategy(const MessageViewer::AttachmentStrategy * strategy)220 void KMReaderWin::setAttachmentStrategy(const MessageViewer::AttachmentStrategy *strategy)
221 {
222     mViewer->setAttachmentStrategy(strategy);
223 }
224 
setOverrideEncoding(const QString & encoding)225 void KMReaderWin::setOverrideEncoding(const QString &encoding)
226 {
227     mViewer->setOverrideEncoding(encoding);
228 }
229 
clearCache()230 void KMReaderWin::clearCache()
231 {
232     clear();
233 }
234 
updateShowMultiMessagesButton(bool enablePreviousButton,bool enableNextButton)235 void KMReaderWin::updateShowMultiMessagesButton(bool enablePreviousButton, bool enableNextButton)
236 {
237     mViewer->updateShowMultiMessagesButton(enablePreviousButton, enableNextButton);
238 }
239 
hasMultiMessages(bool multi)240 void KMReaderWin::hasMultiMessages(bool multi)
241 {
242     mViewer->hasMultiMessages(multi);
243 }
244 
245 // enter items for the "Important changes" list here:
246 static const char *const kmailChanges[] = {
247     I18N_NOOP("KMail is now based on the Akonadi Personal Information Management framework, which brings many "
248               "changes all around.")};
249 static const int numKMailChanges = sizeof kmailChanges / sizeof *kmailChanges;
250 
251 // enter items for the "new features" list here, so the main body of
252 // the welcome page can be left untouched (probably much easier for
253 // the translators). Note that the <li>...</li> tags are added
254 // automatically below:
255 static const char *const kmailNewFeatures[] = {
256     I18N_NOOP("Push email (IMAP IDLE)"),
257     I18N_NOOP("Improved searches"),
258     I18N_NOOP("Support for adding notes (annotations) to mails"),
259     I18N_NOOP("Less GUI freezes, mail checks happen in the background"),
260     I18N_NOOP("Plugins support"),
261     I18N_NOOP("New HTML renderer (QtWebEngine)"),
262     I18N_NOOP("Added Check for Phishing URL"),
263     I18N_NOOP("Added Check DKIM"),
264     I18N_NOOP("Added Markdown Editing Support"),
265     I18N_NOOP("Added Grammar Plugin Support"),
266     I18N_NOOP("Added etesync resource support"),
267     I18N_NOOP("Added Microsoft Exchange Web Services resource support"),
268 };
269 static const int numKMailNewFeatures = sizeof kmailNewFeatures / sizeof *kmailNewFeatures;
270 
271 // static
newFeaturesMD5()272 QString KMReaderWin::newFeaturesMD5()
273 {
274     QByteArray str;
275     for (int i = 0; i < numKMailChanges; ++i) {
276         str += kmailChanges[i];
277     }
278     for (int i = 0; i < numKMailNewFeatures; ++i) {
279         str += kmailNewFeatures[i];
280     }
281     QCryptographicHash md5(QCryptographicHash::Md5);
282     md5.addData(str);
283     return QLatin1String(md5.result().toBase64());
284 }
285 
displaySplashPage(const QString & templateName,const QVariantHash & _data)286 void KMReaderWin::displaySplashPage(const QString &templateName, const QVariantHash &_data)
287 {
288     QVariantHash data = _data;
289     if (!data.contains(QStringLiteral("icon"))) {
290         data[QStringLiteral("icon")] = QStringLiteral("kmail");
291     }
292     if (!data.contains(QStringLiteral("name"))) {
293         data[QStringLiteral("name")] = i18n("KMail");
294     }
295     if (!data.contains(QStringLiteral("subtitle"))) {
296         data[QStringLiteral("subtitle")] = i18n("The KDE Mail Client");
297     }
298 
299     mViewer->displaySplashPage(templateName, data, QByteArrayLiteral("kmail"));
300 }
301 
displayBusyPage()302 void KMReaderWin::displayBusyPage()
303 {
304     displaySplashPage(QStringLiteral("status.html"),
305                       {{QStringLiteral("title"), i18n("Retrieving Folder Contents")}, {QStringLiteral("subtext"), i18n("Please wait . . .")}});
306 }
307 
displayOfflinePage()308 void KMReaderWin::displayOfflinePage()
309 {
310     displaySplashPage(QStringLiteral("status.html"),
311                       {{QStringLiteral("title"), i18n("Offline")},
312                        {QStringLiteral("subtext"),
313                         i18n("KMail is currently in offline mode. "
314                              "Click <a href=\"kmail:goOnline\">here</a> to go online . . .</p>")}});
315 }
316 
displayResourceOfflinePage()317 void KMReaderWin::displayResourceOfflinePage()
318 {
319     displaySplashPage(QStringLiteral("status.html"),
320                       {{QStringLiteral("title"), i18n("Offline")},
321                        {QStringLiteral("subtext"),
322                         i18n("Account is currently in offline mode. "
323                              "Click <a href=\"kmail:goResourceOnline\">here</a> to go online . . .</p>")}});
324 }
325 
displayAboutPage()326 void KMReaderWin::displayAboutPage()
327 {
328     QVariantHash data;
329     data[QStringLiteral("version")] = QStringLiteral(KDEPIM_VERSION);
330     data[QStringLiteral("firstStart")] = kmkernel->firstStart();
331 
332     QVariantList features;
333     features.reserve(numKMailNewFeatures);
334     for (int i = 0; i < numKMailNewFeatures; ++i) {
335         features.push_back(i18n(kmailNewFeatures[i]));
336     }
337     data[QStringLiteral("newFeatures")] = features;
338 
339     QVariantList changes;
340     changes.reserve(numKMailChanges);
341     for (int i = 0; i < numKMailChanges; ++i) {
342         features.push_back(i18n(kmailChanges[i]));
343     }
344     data[QStringLiteral("importantChanges")] = changes;
345 
346     displaySplashPage(QStringLiteral(":/about/introduction_kmail.html"), data);
347 }
348 
slotFind()349 void KMReaderWin::slotFind()
350 {
351     mViewer->slotFind();
352 }
353 
slotCopySelectedText()354 void KMReaderWin::slotCopySelectedText()
355 {
356     QString selection = mViewer->selectedText();
357     selection.replace(QChar::Nbsp, QLatin1Char(' '));
358     QApplication::clipboard()->setText(selection);
359 }
360 
setMsgPart(KMime::Content * aMsgPart)361 void KMReaderWin::setMsgPart(KMime::Content *aMsgPart)
362 {
363     mViewer->setMessagePart(aMsgPart);
364 }
365 
copyText() const366 QString KMReaderWin::copyText() const
367 {
368     return mViewer->selectedText();
369 }
370 
displayFormatMessageOverwrite() const371 MessageViewer::Viewer::DisplayFormatMessage KMReaderWin::displayFormatMessageOverwrite() const
372 {
373     return mViewer->displayFormatMessageOverwrite();
374 }
375 
setPrintElementBackground(bool printElementBackground)376 void KMReaderWin::setPrintElementBackground(bool printElementBackground)
377 {
378     mViewer->setPrintElementBackground(printElementBackground);
379 }
380 
setDisplayFormatMessageOverwrite(MessageViewer::Viewer::DisplayFormatMessage format)381 void KMReaderWin::setDisplayFormatMessageOverwrite(MessageViewer::Viewer::DisplayFormatMessage format)
382 {
383     mViewer->setDisplayFormatMessageOverwrite(format);
384 }
385 
setHtmlLoadExtDefault(bool loadExtDefault)386 void KMReaderWin::setHtmlLoadExtDefault(bool loadExtDefault)
387 {
388     mViewer->setHtmlLoadExtDefault(loadExtDefault);
389 }
390 
setHtmlLoadExtOverride(bool loadExtOverride)391 void KMReaderWin::setHtmlLoadExtOverride(bool loadExtOverride)
392 {
393     mViewer->setHtmlLoadExtOverride(loadExtOverride);
394 }
395 
htmlMail() const396 bool KMReaderWin::htmlMail() const
397 {
398     return mViewer->htmlMail();
399 }
400 
htmlLoadExternal()401 bool KMReaderWin::htmlLoadExternal()
402 {
403     return mViewer->htmlLoadExternal();
404 }
405 
messageItem() const406 Akonadi::Item KMReaderWin::messageItem() const
407 {
408     return mViewer->messageItem();
409 }
410 
mainWindow() const411 QWidget *KMReaderWin::mainWindow() const
412 {
413     return mMainWindow;
414 }
415 
slotMailtoCompose()416 void KMReaderWin::slotMailtoCompose()
417 {
418     KMCommand *command = new KMMailtoComposeCommand(urlClicked(), messageItem());
419     command->start();
420 }
421 
slotMailtoForward()422 void KMReaderWin::slotMailtoForward()
423 {
424     KMCommand *command = new KMMailtoForwardCommand(mMainWindow, urlClicked(), messageItem());
425     command->start();
426 }
427 
slotMailtoAddAddrBook()428 void KMReaderWin::slotMailtoAddAddrBook()
429 {
430     const QUrl url = urlClicked();
431     if (url.isEmpty()) {
432         return;
433     }
434     const QString emailString = KEmailAddress::decodeMailtoUrl(url);
435 
436     auto job = new Akonadi::AddEmailAddressJob(emailString, mMainWindow, this);
437     job->setInteractive(true);
438     connect(job, &Akonadi::AddEmailAddressJob::successMessage, this, [](const QString &message) {
439         PimCommon::BroadcastStatus::instance()->setStatusMsg(message);
440     });
441     job->start();
442 }
443 
slotMailToAddToExistingContact()444 void KMReaderWin::slotMailToAddToExistingContact()
445 {
446     const QUrl url = urlClicked();
447     if (url.isEmpty()) {
448         return;
449     }
450     const QString emailString = KEmailAddress::decodeMailtoUrl(url);
451     QPointer<AddEmailToExistingContactDialog> dlg = new AddEmailToExistingContactDialog(this);
452     if (dlg->exec()) {
453         Akonadi::Item item = dlg->selectedContact();
454         if (item.isValid()) {
455             auto job = new AddEmailToExistingContactJob(item, emailString, this);
456             job->start();
457         }
458     }
459     delete dlg;
460 }
461 
slotMailtoOpenAddrBook()462 void KMReaderWin::slotMailtoOpenAddrBook()
463 {
464     const QUrl url = urlClicked();
465     if (url.isEmpty()) {
466         return;
467     }
468     const QString emailString = KEmailAddress::decodeMailtoUrl(url).toLower();
469 
470     auto job = new Akonadi::OpenEmailAddressJob(emailString, mMainWindow, this);
471     job->start();
472 }
473 
slotAddBookmarks()474 void KMReaderWin::slotAddBookmarks()
475 {
476     const QUrl url = urlClicked();
477     if (url.isEmpty()) {
478         return;
479     }
480     KMCommand *command = new KMAddBookmarksCommand(url, this);
481     command->start();
482 }
483 
slotUrlSave()484 void KMReaderWin::slotUrlSave()
485 {
486     const QUrl url = urlClicked();
487     if (url.isEmpty()) {
488         return;
489     }
490     KMCommand *command = new KMUrlSaveCommand(url, mMainWindow);
491     command->start();
492 }
493 
slotSaveImageOnDisk()494 void KMReaderWin::slotSaveImageOnDisk()
495 {
496     const QUrl url = imageUrlClicked();
497     if (url.isEmpty()) {
498         return;
499     }
500     KMCommand *command = new KMUrlSaveCommand(url, mMainWindow);
501     command->start();
502 }
503 
slotMailtoReply()504 void KMReaderWin::slotMailtoReply()
505 {
506     auto command = new KMMailtoReplyCommand(mMainWindow, urlClicked(), messageItem(), copyText());
507     command->setReplyAsHtml(htmlMail());
508     command->start();
509 }
510 
cssHelper() const511 CSSHelper *KMReaderWin::cssHelper() const
512 {
513     return mViewer->cssHelper();
514 }
515 
htmlLoadExtOverride() const516 bool KMReaderWin::htmlLoadExtOverride() const
517 {
518     return mViewer->htmlLoadExtOverride();
519 }
520 
setDecryptMessageOverwrite(bool overwrite)521 void KMReaderWin::setDecryptMessageOverwrite(bool overwrite)
522 {
523     mViewer->setDecryptMessageOverwrite(overwrite);
524 }
525 
attachmentStrategy() const526 const MessageViewer::AttachmentStrategy *KMReaderWin::attachmentStrategy() const
527 {
528     return mViewer->attachmentStrategy();
529 }
530 
overrideEncoding() const531 QString KMReaderWin::overrideEncoding() const
532 {
533     return mViewer->overrideEncoding();
534 }
535 
toggleFixFontAction() const536 KToggleAction *KMReaderWin::toggleFixFontAction() const
537 {
538     return mViewer->toggleFixFontAction();
539 }
540 
mailToComposeAction() const541 QAction *KMReaderWin::mailToComposeAction() const
542 {
543     return mMailToComposeAction;
544 }
545 
mailToReplyAction() const546 QAction *KMReaderWin::mailToReplyAction() const
547 {
548     return mMailToReplyAction;
549 }
550 
mailToForwardAction() const551 QAction *KMReaderWin::mailToForwardAction() const
552 {
553     return mMailToForwardAction;
554 }
555 
addAddrBookAction() const556 QAction *KMReaderWin::addAddrBookAction() const
557 {
558     return mAddAddrBookAction;
559 }
560 
openAddrBookAction() const561 QAction *KMReaderWin::openAddrBookAction() const
562 {
563     return mOpenAddrBookAction;
564 }
565 
mimePartTreeIsEmpty() const566 bool KMReaderWin::mimePartTreeIsEmpty() const
567 {
568     return mViewer->mimePartTreeIsEmpty();
569 }
570 
toggleMimePartTreeAction() const571 QAction *KMReaderWin::toggleMimePartTreeAction() const
572 {
573     return mViewer->toggleMimePartTreeAction();
574 }
575 
shareServiceUrlMenu() const576 KActionMenu *KMReaderWin::shareServiceUrlMenu() const
577 {
578     return mViewer->shareServiceUrlMenu();
579 }
580 
dkimViewerMenu() const581 DKIMViewerMenu *KMReaderWin::dkimViewerMenu() const
582 {
583     return mViewer->dkimViewerMenu();
584 }
585 
remoteContentMenu() const586 RemoteContentMenu *KMReaderWin::remoteContentMenu() const
587 {
588     return mViewer->remoteContentMenu();
589 }
590 
viewerPluginActionList(ViewerPluginInterface::SpecificFeatureTypes features)591 QList<QAction *> KMReaderWin::viewerPluginActionList(ViewerPluginInterface::SpecificFeatureTypes features)
592 {
593     return mViewer->viewerPluginActionList(features);
594 }
595 
selectAllAction() const596 QAction *KMReaderWin::selectAllAction() const
597 {
598     return mViewer->selectAllAction();
599 }
600 
copyURLAction() const601 QAction *KMReaderWin::copyURLAction() const
602 {
603     return mViewer->copyURLAction();
604 }
605 
copyImageLocation() const606 QAction *KMReaderWin::copyImageLocation() const
607 {
608     return mViewer->copyImageLocation();
609 }
610 
copyAction() const611 QAction *KMReaderWin::copyAction() const
612 {
613     return mViewer->copyAction();
614 }
615 
viewSourceAction() const616 QAction *KMReaderWin::viewSourceAction() const
617 {
618     return mViewer->viewSourceAction();
619 }
620 
saveAsAction() const621 QAction *KMReaderWin::saveAsAction() const
622 {
623     return mViewer->saveAsAction();
624 }
625 
findInMessageAction() const626 QAction *KMReaderWin::findInMessageAction() const
627 {
628     return mViewer->findInMessageAction();
629 }
630 
urlOpenAction() const631 QAction *KMReaderWin::urlOpenAction() const
632 {
633     return mViewer->urlOpenAction();
634 }
635 
urlSaveAsAction() const636 QAction *KMReaderWin::urlSaveAsAction() const
637 {
638     return mUrlSaveAsAction;
639 }
640 
addBookmarksAction() const641 QAction *KMReaderWin::addBookmarksAction() const
642 {
643     return mAddBookmarksAction;
644 }
645 
setPrinting(bool enable)646 void KMReaderWin::setPrinting(bool enable)
647 {
648     mViewer->setPrinting(enable);
649 }
650 
speakTextAction() const651 QAction *KMReaderWin::speakTextAction() const
652 {
653     return mViewer->speakTextAction();
654 }
655 
shareTextAction() const656 QAction *KMReaderWin::shareTextAction() const
657 {
658     return mViewer->shareTextAction();
659 }
660 
downloadImageToDiskAction() const661 QAction *KMReaderWin::downloadImageToDiskAction() const
662 {
663     return mImageUrlSaveAsAction;
664 }
665 
clear(bool force)666 void KMReaderWin::clear(bool force)
667 {
668     mViewer->clear(force ? MimeTreeParser::Force : MimeTreeParser::Delayed);
669 }
670 
setMessage(const Akonadi::Item & item,MimeTreeParser::UpdateMode updateMode)671 void KMReaderWin::setMessage(const Akonadi::Item &item, MimeTreeParser::UpdateMode updateMode)
672 {
673     qCDebug(KMAIL_LOG) << Q_FUNC_INFO << parentWidget();
674     mViewer->setFolderIdentity(MailCommon::Util::folderIdentity(item));
675     mViewer->setMessageItem(item, updateMode);
676 }
677 
setMessage(const KMime::Message::Ptr & message)678 void KMReaderWin::setMessage(const KMime::Message::Ptr &message)
679 {
680     mViewer->setFolderIdentity(0);
681     mViewer->setMessage(message);
682 }
683 
urlClicked() const684 QUrl KMReaderWin::urlClicked() const
685 {
686     return mViewer->urlClicked();
687 }
688 
imageUrlClicked() const689 QUrl KMReaderWin::imageUrlClicked() const
690 {
691     return mViewer->imageUrlClicked();
692 }
693 
update(bool force)694 void KMReaderWin::update(bool force)
695 {
696     mViewer->update(force ? MimeTreeParser::Force : MimeTreeParser::Delayed);
697 }
698 
slotUrlClicked(const Akonadi::Item & item,const QUrl & url)699 void KMReaderWin::slotUrlClicked(const Akonadi::Item &item, const QUrl &url)
700 {
701     if (item.isValid() && item.parentCollection().isValid()) {
702         const auto col = CommonKernel->collectionFromId(item.parentCollection().id());
703         QSharedPointer<FolderSettings> fd = FolderSettings::forCollection(col, false);
704         KMail::Util::handleClickedURL(url, fd, item.parentCollection());
705         return;
706     }
707     // No folder so we can't have identity and template.
708     KMail::Util::handleClickedURL(url);
709 }
710 
slotShowReader(KMime::Content * msgPart,bool html,const QString & encoding)711 void KMReaderWin::slotShowReader(KMime::Content *msgPart, bool html, const QString &encoding)
712 {
713     const MessageViewer::Viewer::DisplayFormatMessage format = html ? MessageViewer::Viewer::Html : MessageViewer::Viewer::Text;
714     auto win = new KMReaderMainWin(msgPart, format, encoding);
715     win->show();
716 }
717 
slotShowMessage(const KMime::Message::Ptr & message,const QString & encoding)718 void KMReaderWin::slotShowMessage(const KMime::Message::Ptr &message, const QString &encoding)
719 {
720     auto win = new KMReaderMainWin();
721     win->showMessage(encoding, message);
722     win->show();
723 }
724 
slotDeleteMessage(const Akonadi::Item & item)725 void KMReaderWin::slotDeleteMessage(const Akonadi::Item &item)
726 {
727     if (!item.isValid()) {
728         return;
729     }
730     auto command = new KMTrashMsgCommand(item.parentCollection(), item, -1);
731     command->start();
732 }
733 
printSelectedText(bool preview)734 bool KMReaderWin::printSelectedText(bool preview)
735 {
736     const QString str = mViewer->selectedText();
737     if (str.isEmpty()) {
738         return false;
739     }
740     auto composer = new ::MessageComposer::Composer;
741     composer->textPart()->setCleanPlainText(str);
742     composer->textPart()->setWrappedPlainText(str);
743     auto messagePtr = messageItem().payload<KMime::Message::Ptr>();
744     composer->infoPart()->setFrom(messagePtr->from()->asUnicodeString());
745     composer->infoPart()->setTo(QStringList() << messagePtr->to()->asUnicodeString());
746     composer->infoPart()->setCc(QStringList() << messagePtr->cc()->asUnicodeString());
747     composer->infoPart()->setSubject(messagePtr->subject()->asUnicodeString());
748     composer->setProperty("preview", preview);
749     connect(composer, &::MessageComposer::Composer::result, this, &KMReaderWin::slotPrintComposeResult);
750     composer->start();
751     return true;
752 }
753 
slotPrintComposeResult(KJob * job)754 void KMReaderWin::slotPrintComposeResult(KJob *job)
755 {
756     const bool preview = job->property("preview").toBool();
757     auto composer = qobject_cast<::MessageComposer::Composer *>(job);
758     Q_ASSERT(composer);
759     if (composer->error() == ::MessageComposer::Composer::NoError) {
760         Q_ASSERT(composer->resultMessages().size() == 1);
761         Akonadi::Item printItem;
762         printItem.setPayload<KMime::Message::Ptr>(composer->resultMessages().constFirst());
763         Akonadi::MessageFlags::copyMessageFlags(*(composer->resultMessages().constFirst()), printItem);
764         const bool useFixedFont = MessageViewer::MessageViewerSettings::self()->useFixedFont();
765         const QString overrideEncoding = MessageCore::MessageCoreSettings::self()->overrideCharacterEncoding();
766 
767         KMPrintCommandInfo commandInfo;
768         commandInfo.mMsg = printItem;
769         commandInfo.mHeaderStylePlugin = mViewer->headerStylePlugin();
770         commandInfo.mFormat = mViewer->displayFormatMessageOverwrite();
771         commandInfo.mHtmlLoadExtOverride = mViewer->htmlLoadExternal();
772         commandInfo.mPrintPreview = preview;
773         commandInfo.mUseFixedFont = useFixedFont;
774         commandInfo.mOverrideFont = overrideEncoding;
775         commandInfo.mShowSignatureDetails =
776             mViewer->showSignatureDetails() || MessageViewer::MessageViewerSettings::self()->alwaysShowEncryptionSignatureDetails();
777         commandInfo.mShowEncryptionDetails =
778             mViewer->showEncryptionDetails() || MessageViewer::MessageViewerSettings::self()->alwaysShowEncryptionSignatureDetails();
779 
780         auto command = new KMPrintCommand(this, commandInfo);
781         command->start();
782     } else {
783         if (static_cast<KIO::Job *>(job)->uiDelegate()) {
784             static_cast<KIO::Job *>(job)->uiDelegate()->showErrorMessage();
785         } else {
786             qCWarning(KMAIL_LOG) << "Composer for printing failed:" << composer->errorString();
787         }
788     }
789 }
790 
clearContactItem()791 void KMReaderWin::clearContactItem()
792 {
793     mSearchedContact = Akonadi::Item();
794     mSearchedAddress = KContacts::Addressee();
795     mLoadExternalReference->setChecked(false);
796     mViewAsHtml->setChecked(false);
797 }
798 
setContactItem(const Akonadi::Item & contact,const KContacts::Addressee & address)799 void KMReaderWin::setContactItem(const Akonadi::Item &contact, const KContacts::Addressee &address)
800 {
801     mSearchedContact = contact;
802     mSearchedAddress = address;
803     updateHtmlActions();
804 }
805 
updateHtmlActions()806 void KMReaderWin::updateHtmlActions()
807 {
808     if (!mSearchedContact.isValid()) {
809         mLoadExternalReference->setChecked(false);
810         mViewAsHtml->setChecked(false);
811     } else {
812         const QStringList customs = mSearchedAddress.customs();
813         for (const QString &custom : customs) {
814             if (custom.contains(QLatin1String("MailPreferedFormatting"))) {
815                 const QString value = mSearchedAddress.custom(QStringLiteral("KADDRESSBOOK"), QStringLiteral("MailPreferedFormatting"));
816                 mViewAsHtml->setChecked(value == QLatin1String("HTML"));
817             } else if (custom.contains(QLatin1String("MailAllowToRemoteContent"))) {
818                 const QString value = mSearchedAddress.custom(QStringLiteral("KADDRESSBOOK"), QStringLiteral("MailAllowToRemoteContent"));
819                 mLoadExternalReference->setChecked((value == QLatin1String("TRUE")));
820             }
821         }
822     }
823 }
824 
slotContactHtmlOptions()825 void KMReaderWin::slotContactHtmlOptions()
826 {
827     const QUrl url = urlClicked();
828     if (url.isEmpty()) {
829         return;
830     }
831     const QString emailString = KEmailAddress::decodeMailtoUrl(url).toLower();
832 
833     auto job = new Akonadi::AddEmailDisplayJob(emailString, mMainWindow, this);
834     job->setMessageId(mViewer->messageItem().id());
835     connect(job, &Akonadi::AddEmailDisplayJob::contactUpdated, this, &KMReaderWin::slotContactHtmlPreferencesUpdated);
836     job->setRemoteContent(mLoadExternalReference->isChecked());
837     job->setShowAsHTML(mViewAsHtml->isChecked());
838     job->setContact(mSearchedContact);
839     job->start();
840 }
841 
slotContactHtmlPreferencesUpdated(const Akonadi::Item & contact,Akonadi::Item::Id id,bool showAsHTML,bool remoteContent)842 void KMReaderWin::slotContactHtmlPreferencesUpdated(const Akonadi::Item &contact, Akonadi::Item::Id id, bool showAsHTML, bool remoteContent)
843 {
844     Q_UNUSED(contact)
845     if (mViewer->messageItem().id() == id) {
846         mViewer->slotChangeDisplayMail(showAsHTML ? Viewer::Html : Viewer::Text, remoteContent);
847     }
848 }
849 
slotEditContact()850 void KMReaderWin::slotEditContact()
851 {
852     if (mSearchedContact.isValid()) {
853         QPointer<Akonadi::ContactEditorDialog> dlg = new Akonadi::ContactEditorDialog(Akonadi::ContactEditorDialog::EditMode, this);
854         connect(dlg.data(), &Akonadi::ContactEditorDialog::contactStored, this, &KMReaderWin::contactStored);
855         connect(dlg.data(), &Akonadi::ContactEditorDialog::error, this, &KMReaderWin::slotContactEditorError);
856         dlg->setContact(mSearchedContact);
857         dlg->exec();
858         delete dlg;
859     }
860 }
861 
slotContactEditorError(const QString & error)862 void KMReaderWin::slotContactEditorError(const QString &error)
863 {
864     KMessageBox::error(this, i18n("Contact cannot be stored: %1", error), i18n("Failed to store contact"));
865 }
866 
contactStored(const Akonadi::Item & item)867 void KMReaderWin::contactStored(const Akonadi::Item &item)
868 {
869     if (item.hasPayload<KContacts::Addressee>()) {
870         const auto contact = item.payload<KContacts::Addressee>();
871         setContactItem(item, contact);
872         mViewer->slotChangeDisplayMail(mViewAsHtml->isChecked() ? Viewer::Html : Viewer::Text, mLoadExternalReference->isChecked());
873     }
874     PimCommon::BroadcastStatus::instance()->setStatusMsg(i18n("Contact modified successfully"));
875 }
876 
saveMessageDisplayFormatAction() const877 QAction *KMReaderWin::saveMessageDisplayFormatAction() const
878 {
879     return mViewer->saveMessageDisplayFormatAction();
880 }
881 
resetMessageDisplayFormatAction() const882 QAction *KMReaderWin::resetMessageDisplayFormatAction() const
883 {
884     return mViewer->resetMessageDisplayFormatAction();
885 }
886 
editContactAction() const887 QAction *KMReaderWin::editContactAction() const
888 {
889     return mEditContactAction;
890 }
891 
developmentToolsAction() const892 QAction *KMReaderWin::developmentToolsAction() const
893 {
894     return mViewer->developmentToolsAction();
895 }
896 
viewHtmlOption() const897 QMenu *KMReaderWin::viewHtmlOption() const
898 {
899     return mViewHtmlOptions;
900 }
901 
shareImage() const902 QAction *KMReaderWin::shareImage() const
903 {
904     return mShareImage;
905 }
906 
addToExistingContactAction() const907 QAction *KMReaderWin::addToExistingContactAction() const
908 {
909     return mAddEmailToExistingContactAction;
910 }
911 
slotShareImage()912 void KMReaderWin::slotShareImage()
913 {
914     KMCommand *command = new KMShareImageCommand(imageUrlClicked(), this);
915     command->start();
916 }
917 
interceptorUrlActions(const WebEngineViewer::WebHitTestResult & result) const918 QList<QAction *> KMReaderWin::interceptorUrlActions(const WebEngineViewer::WebHitTestResult &result) const
919 {
920     return mViewer->interceptorUrlActions(result);
921 }
922 
slotPrintingFinished()923 void KMReaderWin::slotPrintingFinished()
924 {
925     if (mViewer->printingMode()) {
926         deleteLater();
927     }
928 }
929