2 This file is part of KMail, the KDE mail client.
3 Copyright (c) 2002 Don Sanders <sanders@kde.org>
4 Copyright (C) 2013-2015 Laurent Montel <montel@kde.org>
6 KMail is free software; you can redistribute it and/or modify it
7 under the terms of the GNU General Public License, version 2, as
8 published by the Free Software Foundation.
10 KMail is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 General Public License for more details.
15 You should have received a copy of the GNU General Public License
16 along with this program; if not, write to the Free Software
17 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
21 // This file implements various "command" classes. These command classes
22 // are based on the command design pattern.
24 // Historically various operations were implemented as slots of KMMainWin.
25 // This proved inadequate as KMail has multiple top level windows
26 // (KMMainWin, KMReaderMainWin, SearchWindow, KMComposeWin) that may
27 // benefit from using these operations. It is desirable that these
28 // classes can operate without depending on or altering the state of
29 // a KMMainWin, in fact it is possible no KMMainWin object even exists.
31 // Now these operations have been rewritten as KMCommand based classes,
32 // making them independent of KMMainWin.
34 // The base command class KMCommand is async, which is a difference
35 // from the conventional command pattern. As normal derived classes implement
36 // the execute method, but client classes call start() instead of
37 // calling execute() directly. start() initiates async operations,
38 // and on completion of these operations calls execute() and then deletes
39 // the command. (So the client must not construct commands on the stack).
41 // The type of async operation supported by KMCommand is retrieval
42 // of messages from an IMAP server.
44 #include "kmcommands.h"
46 #include "widgets/collectionpane.h"
47 #include "mailcommon/mailkernel.h"
48 #include "mailcommon/mailutil.h"
49 #include "messageviewer/headerstyleplugin.h"
51 #include <unistd.h> // link()
52 #include <QProgressDialog>
53 #include <KEmailAddress>
54 #include <kdbusservicestarter.h>
55 #include "kmail_debug.h"
56 #include <qfiledialog.h>
57 #include <KLocalizedString>
58 #include <kmessagebox.h>
59 #include <kbookmarkmanager.h>
60 #include <QFileDialog>
61 #include <KJobWidgets>
64 #include <kio/jobuidelegate.h>
65 #include <kio/statjob.h>
67 #include <kmime/kmime_message.h>
69 #include <KIdentityManagement/kidentitymanagement/identitymanager.h>
70 #include <AkonadiCore/itemmodifyjob.h>
71 #include <AkonadiCore/itemfetchjob.h>
72 #include <MailCommon/MDNStateAttribute>
74 #include "MailCommon/FolderCollection"
76 #include "MessageCore/MailingList"
77 #include "editor/composer.h"
78 #include "MailCommon/FilterAction"
79 #include "MailCommon/FilterManager"
80 #include "MailCommon/MailFilter"
81 #include "MailCommon/RedirectDialog"
82 #include "kmmainwidget.h"
83 #include "undostack.h"
85 #include "Libkdepim/KCursorSaver"
87 #include "MessageViewer/ObjectTreeParser"
88 #include "MessageViewer/CSSHelper"
89 #include "messagecomposer/util.h"
90 #include "kmreadermainwin.h"
91 #include "secondarywindow.h"
92 using KMail::SecondaryWindow
;
94 #include "libkdepim/broadcaststatus.h"
95 #include "settings/kmailsettings.h"
96 #include "MessageCore/StringUtil"
98 #include "messageviewer/messageviewersettings.h"
99 #include "MessageCore/MessageCoreSettings"
101 #include <AkonadiCore/itemmovejob.h>
102 #include <AkonadiCore/itemcopyjob.h>
103 #include <AkonadiCore/itemdeletejob.h>
104 #include <AkonadiCore/tag.h>
105 #include <AkonadiCore/tagcreatejob.h>
106 #include <MailTransport/mailtransport/transportattribute.h>
107 #include <MailTransport/mailtransport/sentbehaviourattribute.h>
109 #include <messagelist/pane.h>
111 #include <MailTransport/mailtransport/transportmanager.h>
112 using MailTransport::TransportManager
;
114 #include "messageviewer/nodehelper.h"
115 #include "MessageViewer/ObjectTreeEmptySource"
117 #include "MessageCore/StringUtil"
118 #include "messagecore/messagehelpers.h"
120 #include "MessageComposer/MessageSender"
121 #include "MessageComposer/MessageHelper"
122 #include "MessageComposer/MessageComposerSettings"
123 #include "MessageComposer/MessageFactory"
124 using MessageComposer::MessageFactory
;
126 #include "libkdepim/progressmanager.h"
127 using KPIM::ProgressManager
;
128 using KPIM::ProgressItem
;
129 #include <kmime/kmime_mdn.h>
130 using namespace KMime
;
132 #include "Libkleo/CryptoBackend"
133 #include "Libkleo/CryptoBackendFactory"
135 #include <gpgme++/error.h>
137 #include <QByteArray>
138 #include <QApplication>
141 #include <boost/bind.hpp>
144 #include <QStandardPaths>
145 #include <QFontDatabase>
147 using namespace MailCommon
;
149 /// Small helper function to get the composer context from a reply
150 static KMail::Composer::TemplateContext
replyContext(MessageFactory::MessageReply reply
)
152 if (reply
.replyAll
) {
153 return KMail::Composer::ReplyToAll
;
155 return KMail::Composer::Reply
;
159 /// Helper to sanely show an error message for a job
160 static void showJobError(KJob
*job
)
163 // we can be called from the KJob::kill, where we are no longer a KIO::Job
164 // so better safe than sorry
165 KIO::Job
*kiojob
= dynamic_cast<KIO::Job
*>(job
);
166 if (kiojob
&& kiojob
->ui()) {
167 kiojob
->ui()->showErrorMessage();
169 qCWarning(KMAIL_LOG
) << "There is no GUI delegate set for a kjob, and it failed with error:" << job
->errorString();
173 KMCommand::KMCommand(QWidget
*parent
)
174 : mCountMsgs(0), mResult(Undefined
), mDeletesItself(false),
175 mEmitsCompletedItself(false), mParent(parent
)
179 KMCommand::KMCommand(QWidget
*parent
, const Akonadi::Item
&msg
)
180 : mCountMsgs(0), mResult(Undefined
), mDeletesItself(false),
181 mEmitsCompletedItself(false), mParent(parent
)
183 if (msg
.isValid() || msg
.hasPayload
<KMime::Message::Ptr
>()) {
184 mMsgList
.append(msg
);
188 KMCommand::KMCommand(QWidget
*parent
, const Akonadi::Item::List
&msgList
)
189 : mCountMsgs(0), mResult(Undefined
), mDeletesItself(false),
190 mEmitsCompletedItself(false), mParent(parent
)
195 KMCommand::~KMCommand()
199 KMCommand::Result
KMCommand::result() const
201 if (mResult
== Undefined
) {
202 qCDebug(KMAIL_LOG
) << "mResult is Undefined";
207 const Akonadi::Item::List
KMCommand::retrievedMsgs() const
209 return mRetrievedMsgs
;
212 Akonadi::Item
KMCommand::retrievedMessage() const
214 if (mRetrievedMsgs
.isEmpty()) {
215 return Akonadi::Item();
217 return *(mRetrievedMsgs
.begin());
220 QWidget
*KMCommand::parentWidget() const
225 bool KMCommand::deletesItself() const
227 return mDeletesItself
;
230 void KMCommand::setDeletesItself(bool deletesItself
)
232 mDeletesItself
= deletesItself
;
235 bool KMCommand::emitsCompletedItself() const
237 return mEmitsCompletedItself
;
240 void KMCommand::setEmitsCompletedItself(bool emitsCompletedItself
)
242 mEmitsCompletedItself
= emitsCompletedItself
;
245 void KMCommand::setResult(KMCommand::Result result
)
250 int KMCommand::mCountJobs
= 0;
252 void KMCommand::start()
254 connect(this, &KMCommand::messagesTransfered
,
255 this, &KMCommand::slotPostTransfer
);
257 if (mMsgList
.isEmpty()) {
258 Q_EMIT
messagesTransfered(OK
);
262 // Special case of operating on message that isn't in a folder
263 const Akonadi::Item mb
= mMsgList
.first();
264 if ((mMsgList
.count() == 1) && MessageCore::Util::isStandaloneMessage(mb
)) {
265 mRetrievedMsgs
.append(mMsgList
.takeFirst());
266 Q_EMIT
messagesTransfered(OK
);
270 // we can only retrieve items with a valid id
271 foreach (const Akonadi::Item
&item
, mMsgList
) {
272 if (!item
.isValid()) {
273 Q_EMIT
messagesTransfered(Failed
);
278 // transfer the selected messages first
279 transferSelectedMsgs();
282 void KMCommand::slotPostTransfer(KMCommand::Result result
)
284 disconnect(this, &KMCommand::messagesTransfered
,
285 this, &KMCommand::slotPostTransfer
);
290 if (!emitsCompletedItself()) {
291 Q_EMIT
completed(this);
293 if (!deletesItself()) {
298 Akonadi::ItemFetchJob
*KMCommand::createFetchJob(const Akonadi::Item::List
&items
)
300 return new Akonadi::ItemFetchJob(items
, this);
303 void KMCommand::transferSelectedMsgs()
305 // make sure no other transfer is active
306 if (KMCommand::mCountJobs
> 0) {
307 Q_EMIT
messagesTransfered(Failed
);
311 bool complete
= true;
312 KMCommand::mCountJobs
= 0;
314 mRetrievedMsgs
.clear();
315 mCountMsgs
= mMsgList
.count();
317 // the QProgressDialog for the user-feedback. Only enable it if it's needed.
318 // For some commands like KMSetStatusCommand it's not needed. Note, that
319 // for some reason the QProgressDialog eats the MouseReleaseEvent (if a
320 // command is executed after the MousePressEvent), cf. bug #71761.
321 if (mCountMsgs
> 0) {
322 mProgressDialog
= new QProgressDialog(mParent
);
323 mProgressDialog
.data()->setWindowTitle(i18n("Please wait"));
325 mProgressDialog
.data()->setLabelText(i18np("Please wait while the message is transferred", "Please wait while the %1 messages are transferred", mMsgList
.count()));
326 mProgressDialog
.data()->setModal(true);
327 mProgressDialog
.data()->setMinimumDuration(1000);
330 // TODO once the message list is based on ETM and we get the more advanced caching we need to make that check a bit more clever
331 if (!mFetchScope
.isEmpty()) {
333 ++KMCommand::mCountJobs
;
334 Akonadi::ItemFetchJob
*fetch
= createFetchJob(mMsgList
);
335 mFetchScope
.fetchAttribute
< MailCommon::MDNStateAttribute
>();
336 fetch
->setFetchScope(mFetchScope
);
337 connect(fetch
, &Akonadi::ItemFetchJob::itemsReceived
, this, &KMCommand::slotMsgTransfered
);
338 connect(fetch
, &Akonadi::ItemFetchJob::result
, this, &KMCommand::slotJobFinished
);
340 // no need to fetch anything
341 if (!mMsgList
.isEmpty()) {
342 mRetrievedMsgs
= mMsgList
;
347 delete mProgressDialog
.data();
348 mProgressDialog
.clear();
349 Q_EMIT
messagesTransfered(OK
);
351 // wait for the transfer and tell the progressBar the necessary steps
352 if (mProgressDialog
.data()) {
353 connect(mProgressDialog
.data(), &QProgressDialog::canceled
,
354 this, &KMCommand::slotTransferCancelled
);
355 mProgressDialog
.data()->setMaximum(totalSize
);
360 void KMCommand::slotMsgTransfered(const Akonadi::Item::List
&msgs
)
362 if (mProgressDialog
.data() && mProgressDialog
.data()->wasCanceled()) {
363 Q_EMIT
messagesTransfered(Canceled
);
366 // save the complete messages
367 #if QT_VERSION >= QT_VERSION_CHECK(5, 5, 0)
368 mRetrievedMsgs
.append(msgs
);
370 Q_FOREACH (const Akonadi::Item
&item
, msgs
) {
371 mRetrievedMsgs
.append(item
);
375 //mRetrievedMsgs.append(msgs);
378 void KMCommand::slotJobFinished()
380 // the job is finished (with / without error)
381 KMCommand::mCountJobs
--;
383 if (mProgressDialog
.data() && mProgressDialog
.data()->wasCanceled()) {
387 if (mCountMsgs
> mRetrievedMsgs
.count()) {
388 // the message wasn't retrieved before => error
389 if (mProgressDialog
.data()) {
390 mProgressDialog
.data()->hide();
392 slotTransferCancelled();
395 // update the progressbar
396 if (mProgressDialog
.data()) {
397 mProgressDialog
.data()->setLabelText(i18np("Please wait while the message is transferred",
398 "Please wait while the %1 messages are transferred", KMCommand::mCountJobs
));
400 if (KMCommand::mCountJobs
== 0) {
402 delete mProgressDialog
.data();
403 mProgressDialog
.clear();
404 Q_EMIT
messagesTransfered(OK
);
408 void KMCommand::slotTransferCancelled()
410 KMCommand::mCountJobs
= 0;
412 mRetrievedMsgs
.clear();
413 Q_EMIT
messagesTransfered(Canceled
);
416 KMMailtoComposeCommand::KMMailtoComposeCommand(const QUrl
&url
,
417 const Akonadi::Item
&msg
)
418 : mUrl(url
), mMessage(msg
)
422 KMCommand::Result
KMMailtoComposeCommand::execute()
424 KMime::Message::Ptr
msg(new KMime::Message
);
427 if (mMessage
.isValid() && mMessage
.parentCollection().isValid()) {
428 QSharedPointer
<FolderCollection
> fd
= FolderCollection::forCollection(mMessage
.parentCollection(), false);
432 MessageHelper::initHeader(msg
, KMKernel::self()->identityManager(), id
);
433 msg
->contentType()->setCharset("utf-8");
434 msg
->to()->fromUnicodeString(KEmailAddress::decodeMailtoUrl(mUrl
), "utf-8");
436 KMail::Composer
*win
= KMail::makeComposer(msg
, false, false, KMail::Composer::New
, id
);
437 win
->setFocusToSubject();
442 KMMailtoReplyCommand::KMMailtoReplyCommand(QWidget
*parent
,
443 const QUrl
&url
, const Akonadi::Item
&msg
, const QString
&selection
)
444 : KMCommand(parent
, msg
), mUrl(url
), mSelection(selection
)
446 fetchScope().fetchFullPayload(true);
447 fetchScope().setAncestorRetrieval(Akonadi::ItemFetchScope::Parent
);
450 KMCommand::Result
KMMailtoReplyCommand::execute()
452 Akonadi::Item item
= retrievedMessage();
453 KMime::Message::Ptr msg
= MessageCore::Util::message(item
);
457 MessageFactory
factory(msg
, item
.id(), MailCommon::Util::updatedCollection(item
.parentCollection()));
458 factory
.setIdentityManager(KMKernel::self()->identityManager());
459 factory
.setFolderIdentity(MailCommon::Util::folderIdentity(item
));
460 factory
.setMailingListAddresses(KMail::Util::mailingListsFromMessage(item
));
461 factory
.putRepliesInSameFolder(KMail::Util::putRepliesInSameFolder(item
));
462 factory
.setReplyStrategy(MessageComposer::ReplyNone
);
463 factory
.setSelection(mSelection
);
464 KMime::Message::Ptr rmsg
= factory
.createReply().msg
;
465 rmsg
->to()->fromUnicodeString(KEmailAddress::decodeMailtoUrl(mUrl
), "utf-8"); //TODO Check the UTF-8
466 bool lastEncrypt
= false;
467 bool lastSign
= false;
468 KMail::Util::lastEncryptAndSignState(lastEncrypt
, lastSign
, msg
);
470 KMail::Composer
*win
= KMail::makeComposer(rmsg
, lastSign
, lastEncrypt
, KMail::Composer::Reply
, 0, mSelection
);
471 win
->setFocusToEditor();
477 KMMailtoForwardCommand::KMMailtoForwardCommand(QWidget
*parent
,
478 const QUrl
&url
, const Akonadi::Item
&msg
)
479 : KMCommand(parent
, msg
), mUrl(url
)
481 fetchScope().fetchFullPayload(true);
482 fetchScope().setAncestorRetrieval(Akonadi::ItemFetchScope::Parent
);
485 KMCommand::Result
KMMailtoForwardCommand::execute()
487 //TODO : consider factoring createForward into this method.
488 Akonadi::Item item
= retrievedMessage();
489 KMime::Message::Ptr msg
= MessageCore::Util::message(item
);
493 MessageFactory
factory(msg
, item
.id(), MailCommon::Util::updatedCollection(item
.parentCollection()));
494 factory
.setIdentityManager(KMKernel::self()->identityManager());
495 factory
.setFolderIdentity(MailCommon::Util::folderIdentity(item
));
496 KMime::Message::Ptr fmsg
= factory
.createForward();
497 fmsg
->to()->fromUnicodeString(KEmailAddress::decodeMailtoUrl(mUrl
).toLower(), "utf-8"); //TODO check the utf-8
498 bool lastEncrypt
= false;
499 bool lastSign
= false;
500 KMail::Util::lastEncryptAndSignState(lastEncrypt
, lastSign
, msg
);
502 KMail::Composer
*win
= KMail::makeComposer(fmsg
, lastSign
, lastEncrypt
, KMail::Composer::Forward
);
508 KMAddBookmarksCommand::KMAddBookmarksCommand(const QUrl
&url
, QWidget
*parent
)
509 : KMCommand(parent
), mUrl(url
)
513 KMCommand::Result
KMAddBookmarksCommand::execute()
515 const QString filename
= QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation
) + QLatin1Char('/') + QLatin1String("konqueror/bookmarks.xml");
516 QFileInfo
fileInfo(filename
);
517 QDir().mkpath(fileInfo
.absolutePath());
518 KBookmarkManager
*bookManager
= KBookmarkManager::managerForFile(filename
, QStringLiteral("konqueror"));
519 KBookmarkGroup group
= bookManager
->root();
520 group
.addBookmark(mUrl
.path(), QUrl(mUrl
), QString());
521 if (bookManager
->save()) {
522 bookManager
->emitChanged(group
);
528 KMUrlSaveCommand::KMUrlSaveCommand(const QUrl
&url
, QWidget
*parent
)
529 : KMCommand(parent
), mUrl(url
)
533 KMCommand::Result
KMUrlSaveCommand::execute()
535 if (mUrl
.isEmpty()) {
538 const QUrl saveUrl
= QFileDialog::getSaveFileUrl(parentWidget(), QString(), mUrl
.fileName());
539 if (saveUrl
.isEmpty()) {
543 bool fileExists
= false;
544 if (saveUrl
.isLocalFile()) {
545 fileExists
= QFile::exists(saveUrl
.toLocalFile());
547 auto job
= KIO::stat(saveUrl
, KIO::StatJob::DestinationSide
, 0);
548 KJobWidgets::setWindow(job
, parentWidget());
549 fileExists
= job
->exec();
553 if (KMessageBox::warningContinueCancel(Q_NULLPTR
,
554 xi18nc("@info", "File <filename>%1</filename> exists.<nl/>Do you want to replace it?",
555 saveUrl
.toDisplayString()), i18n("Save to File"), KGuiItem(i18n("&Replace")))
556 != KMessageBox::Continue
) {
560 KIO::Job
*job
= KIO::file_copy(mUrl
, saveUrl
, -1, KIO::Overwrite
);
561 connect(job
, &KIO::Job::result
, this, &KMUrlSaveCommand::slotUrlSaveResult
);
562 setEmitsCompletedItself(true);
566 void KMUrlSaveCommand::slotUrlSaveResult(KJob
*job
)
571 Q_EMIT
completed(this);
574 Q_EMIT
completed(this);
578 KMEditMessageCommand::KMEditMessageCommand(QWidget
*parent
, const KMime::Message::Ptr
&msg
)
579 : KMCommand(parent
), mMessage(msg
)
583 KMCommand::Result
KMEditMessageCommand::execute()
589 KMail::Composer
*win
= KMail::makeComposer();
590 bool lastEncrypt
= false;
591 bool lastSign
= false;
592 KMail::Util::lastEncryptAndSignState(lastEncrypt
, lastSign
, mMessage
);
593 win
->setMessage(mMessage
, lastSign
, lastEncrypt
, false, true);
595 win
->setModified(true);
599 KMEditItemCommand::KMEditItemCommand(QWidget
*parent
, const Akonadi::Item
&msg
, bool deleteFromSource
)
600 : KMCommand(parent
, msg
)
601 , mDeleteFromSource(deleteFromSource
)
603 fetchScope().fetchFullPayload(true);
604 fetchScope().fetchAttribute
<MailTransport::TransportAttribute
>();
605 fetchScope().fetchAttribute
<MailTransport::SentBehaviourAttribute
>();
606 fetchScope().setAncestorRetrieval(Akonadi::ItemFetchScope::Parent
);
609 KMEditItemCommand::~KMEditItemCommand()
613 KMCommand::Result
KMEditItemCommand::execute()
615 Akonadi::Item item
= retrievedMessage();
616 if (!item
.isValid() || !item
.parentCollection().isValid()) {
619 KMime::Message::Ptr msg
= MessageCore::Util::message(item
);
624 if (mDeleteFromSource
) {
625 setDeletesItself(true);
626 Akonadi::ItemDeleteJob
*job
= new Akonadi::ItemDeleteJob(item
);
627 connect(job
, &KIO::Job::result
, this, &KMEditItemCommand::slotDeleteItem
);
629 KMail::Composer
*win
= KMail::makeComposer();
630 bool lastEncrypt
= false;
631 bool lastSign
= false;
632 KMail::Util::lastEncryptAndSignState(lastEncrypt
, lastSign
, msg
);
633 win
->setMessage(msg
, lastSign
, lastEncrypt
, false, true);
635 win
->setFolder(item
.parentCollection());
637 const MailTransport::TransportAttribute
*transportAttribute
= item
.attribute
<MailTransport::TransportAttribute
>();
638 if (transportAttribute
) {
639 win
->setCurrentTransport(transportAttribute
->transportId());
641 int transportId
= msg
->headerByType("X-KMail-Transport") ? msg
->headerByType("X-KMail-Transport")->asUnicodeString().toInt() : -1;
642 if (transportId
!= -1) {
643 win
->setCurrentTransport(transportId
);
647 if (auto hdr
= msg
->replyTo(false)) {
648 const QString replyTo
= hdr
->asUnicodeString();
649 win
->setCurrentReplyTo(replyTo
);
652 const MailTransport::SentBehaviourAttribute
*sentAttribute
= item
.attribute
<MailTransport::SentBehaviourAttribute
>();
653 if (sentAttribute
&& (sentAttribute
->sentBehaviour() == MailTransport::SentBehaviourAttribute::MoveToCollection
)) {
654 win
->setFcc(QString::number(sentAttribute
->moveToCollection().id()));
657 if (mDeleteFromSource
) {
658 win
->setModified(true);
664 void KMEditItemCommand::slotDeleteItem(KJob
*job
)
669 Q_EMIT
completed(this);
672 Q_EMIT
completed(this);
677 KMUseTemplateCommand::KMUseTemplateCommand(QWidget
*parent
, const Akonadi::Item
&msg
)
678 : KMCommand(parent
, msg
)
680 fetchScope().fetchFullPayload(true);
681 fetchScope().setAncestorRetrieval(Akonadi::ItemFetchScope::Parent
);
684 KMCommand::Result
KMUseTemplateCommand::execute()
686 Akonadi::Item item
= retrievedMessage();
688 || !item
.parentCollection().isValid() ||
689 !CommonKernel
->folderIsTemplates(item
.parentCollection())
693 KMime::Message::Ptr msg
= MessageCore::Util::message(item
);
698 KMime::Message::Ptr
newMsg(new KMime::Message
);
699 newMsg
->setContent(msg
->encodedContent());
701 // these fields need to be regenerated for the new message
702 newMsg
->removeHeader
<KMime::Headers::Date
>();
703 newMsg
->removeHeader
<KMime::Headers::MessageID
>();
705 KMail::Composer
*win
= KMail::makeComposer();
707 win
->setMessage(newMsg
, false, false, false, true);
712 KMSaveMsgCommand::KMSaveMsgCommand(QWidget
*parent
, const Akonadi::Item::List
&msgList
)
713 : KMCommand(parent
, msgList
)
715 if (msgList
.empty()) {
719 fetchScope().fetchFullPayload(true); // ### unless we call the corresponding KMCommand ctor, this has no effect
722 KMCommand::Result
KMSaveMsgCommand::execute()
724 if (!MessageViewer::Util::saveMessageInMbox(retrievedMsgs(), parentWidget())) {
730 //-----------------------------------------------------------------------------
732 KMOpenMsgCommand::KMOpenMsgCommand(QWidget
*parent
, const QUrl
&url
,
733 const QString
&encoding
, KMMainWidget
*main
)
740 qCDebug(KMAIL_LOG
) << "url :" << url
;
743 KMCommand::Result
KMOpenMsgCommand::execute()
745 if (mUrl
.isEmpty()) {
746 mUrl
= QFileDialog::getOpenFileUrl(parentWidget(), i18n("Open Message"), QUrl(),
747 i18n("Message (*.mbox)")
750 if (mUrl
.isEmpty()) {
755 mMainWidget
->addRecentFile(mUrl
);
758 setDeletesItself(true);
759 mJob
= KIO::get(mUrl
, KIO::NoReload
, KIO::HideProgressInfo
);
760 connect(mJob
, &KIO::TransferJob::data
,
761 this, &KMOpenMsgCommand::slotDataArrived
);
762 connect(mJob
, &KJob::result
,
763 this, &KMOpenMsgCommand::slotResult
);
764 setEmitsCompletedItself(true);
768 void KMOpenMsgCommand::slotDataArrived(KIO::Job
*, const QByteArray
&data
)
770 if (data
.isEmpty()) {
774 mMsgString
.append(QString::fromLatin1(data
.data()));
777 void KMOpenMsgCommand::doesNotContainMessage()
779 KMessageBox::sorry(parentWidget(),
780 i18n("The file does not contain a message."));
782 Q_EMIT
completed(this);
783 // Emulate closing of a secondary window so that KMail exits in case it
784 // was started with the --view command line option. Otherwise an
785 // invisible KMail would keep running.
786 SecondaryWindow
*win
= new SecondaryWindow();
793 void KMOpenMsgCommand::slotResult(KJob
*job
)
799 Q_EMIT
completed(this);
801 if (mMsgString
.isEmpty()) {
802 qCDebug(KMAIL_LOG
) << " Message not found. There is a problem";
803 doesNotContainMessage();
806 int startOfMessage
= 0;
807 if (mMsgString
.startsWith(QStringLiteral("From "))) {
808 startOfMessage
= mMsgString
.indexOf(QLatin1Char('\n'));
809 if (startOfMessage
== -1) {
810 doesNotContainMessage();
813 startOfMessage
+= 1; // the message starts after the '\n'
815 // check for multiple messages in the file
816 bool multipleMessages
= true;
817 int endOfMessage
= mMsgString
.indexOf(QLatin1String("\nFrom "));
818 if (endOfMessage
== -1) {
819 endOfMessage
= mMsgString
.length();
820 multipleMessages
= false;
822 KMime::Message
*msg
= new KMime::Message
;
823 msg
->setContent(KMime::CRLFtoLF(mMsgString
.mid(startOfMessage
, endOfMessage
- startOfMessage
).toUtf8()));
825 if (!msg
->hasContent()) {
826 delete msg
; msg
= Q_NULLPTR
;
827 doesNotContainMessage();
830 KMReaderMainWin
*win
= new KMReaderMainWin();
831 KMime::Message::Ptr
mMsg(msg
);
832 win
->showMessage(mEncoding
, mMsg
);
834 if (multipleMessages
)
835 KMessageBox::information(win
,
836 i18n("The file contains multiple messages. "
837 "Only the first message is shown."));
839 Q_EMIT
completed(this);
844 //-----------------------------------------------------------------------------
845 KMReplyCommand::KMReplyCommand(QWidget
*parent
, const Akonadi::Item
&msg
, MessageComposer::ReplyStrategy replyStrategy
,
846 const QString
&selection
, bool noquote
, const QString
&templateName
)
847 : KMCommand(parent
, msg
),
848 mSelection(selection
),
849 mTemplate(templateName
),
850 m_replyStrategy(replyStrategy
),
855 fetchScope().fetchFullPayload(true);
858 fetchScope().setAncestorRetrieval(Akonadi::ItemFetchScope::Parent
);
861 KMCommand::Result
KMReplyCommand::execute()
864 KPIM::KCursorSaver
busy(KPIM::KBusyPtr::busy());
866 Akonadi::Item item
= retrievedMessage();
867 KMime::Message::Ptr msg
= MessageCore::Util::message(item
);
872 MessageFactory
factory(msg
, item
.id(), MailCommon::Util::updatedCollection(item
.parentCollection()));
873 factory
.setIdentityManager(KMKernel::self()->identityManager());
874 factory
.setFolderIdentity(MailCommon::Util::folderIdentity(item
));
875 factory
.setMailingListAddresses(KMail::Util::mailingListsFromMessage(item
));
876 factory
.putRepliesInSameFolder(KMail::Util::putRepliesInSameFolder(item
));
877 factory
.setReplyStrategy(m_replyStrategy
);
878 factory
.setSelection(mSelection
);
879 if (!mTemplate
.isEmpty()) {
880 factory
.setTemplate(mTemplate
);
883 factory
.setQuote(false);
885 bool lastEncrypt
= false;
886 bool lastSign
= false;
887 KMail::Util::lastEncryptAndSignState(lastEncrypt
, lastSign
, msg
);
889 MessageFactory::MessageReply reply
= factory
.createReply();
890 KMail::Composer
*win
= KMail::makeComposer(KMime::Message::Ptr(reply
.msg
), lastSign
, lastEncrypt
, replyContext(reply
), 0,
891 mSelection
, mTemplate
);
892 win
->setFocusToEditor();
898 KMForwardCommand::KMForwardCommand(QWidget
*parent
,
899 const Akonadi::Item::List
&msgList
, uint identity
, const QString
&templateName
)
900 : KMCommand(parent
, msgList
),
902 mTemplate(templateName
)
904 fetchScope().fetchFullPayload(true);
905 fetchScope().setAncestorRetrieval(Akonadi::ItemFetchScope::Parent
);
908 KMForwardCommand::KMForwardCommand(QWidget
*parent
, const Akonadi::Item
&msg
,
909 uint identity
, const QString
&templateName
)
910 : KMCommand(parent
, msg
),
912 mTemplate(templateName
)
914 fetchScope().fetchFullPayload(true);
915 fetchScope().setAncestorRetrieval(Akonadi::ItemFetchScope::Parent
);
918 KMCommand::Result
KMForwardCommand::createComposer(const Akonadi::Item
&item
)
920 KMime::Message::Ptr msg
= MessageCore::Util::message(item
);
925 KPIM::KCursorSaver
busy(KPIM::KBusyPtr::busy());
927 MessageFactory
factory(msg
, item
.id(), MailCommon::Util::updatedCollection(item
.parentCollection()));
928 factory
.setIdentityManager(KMKernel::self()->identityManager());
929 factory
.setFolderIdentity(MailCommon::Util::folderIdentity(item
));
930 if (!mTemplate
.isEmpty()) {
931 factory
.setTemplate(mTemplate
);
933 KMime::Message::Ptr fwdMsg
= factory
.createForward();
935 uint id
= msg
->headerByType("X-KMail-Identity") ? msg
->headerByType("X-KMail-Identity")->asUnicodeString().trimmed().toUInt() : 0;
936 qCDebug(KMAIL_LOG
) << "mail" << msg
->encodedContent();
937 bool lastEncrypt
= false;
938 bool lastSign
= false;
939 KMail::Util::lastEncryptAndSignState(lastEncrypt
, lastSign
, msg
);
945 KMail::Composer
*win
= KMail::makeComposer(fwdMsg
, lastSign
, lastEncrypt
, KMail::Composer::Forward
, id
, QString(), mTemplate
);
951 KMCommand::Result
KMForwardCommand::execute()
953 Akonadi::Item::List msgList
= retrievedMsgs();
955 if (msgList
.count() >= 2) {
956 // ask if they want a mime digest forward
958 int answer
= KMessageBox::questionYesNoCancel(
960 i18n("Do you want to forward the selected messages as "
961 "attachments in one message (as a MIME digest) or as "
962 "individual messages?"), QString(),
963 KGuiItem(i18n("Send As Digest")),
964 KGuiItem(i18n("Send Individually")));
966 if (answer
== KMessageBox::Yes
) {
967 Akonadi::Item
firstItem(msgList
.first());
968 MessageFactory
factory(KMime::Message::Ptr(new KMime::Message
), firstItem
.id(), MailCommon::Util::updatedCollection(firstItem
.parentCollection()));
969 factory
.setIdentityManager(KMKernel::self()->identityManager());
970 factory
.setFolderIdentity(MailCommon::Util::folderIdentity(firstItem
));
972 QPair
< KMime::Message::Ptr
, KMime::Content
* > fwdMsg
= factory
.createForwardDigestMIME(msgList
);
973 KMail::Composer
*win
= KMail::makeComposer(fwdMsg
.first
, false, false, KMail::Composer::Forward
, mIdentity
);
974 win
->addAttach(fwdMsg
.second
);
977 } else if (answer
== KMessageBox::No
) { // NO MIME DIGEST, Multiple forward
978 Akonadi::Item::List::const_iterator it
;
979 Akonadi::Item::List::const_iterator
end(msgList
.constEnd());
981 for (it
= msgList
.constBegin(); it
!= end
; ++it
) {
982 if (createComposer(*it
) == Failed
) {
993 // forward a single message at most.
994 Akonadi::Item item
= msgList
.first();
995 if (createComposer(item
) == Failed
) {
1001 KMForwardAttachedCommand::KMForwardAttachedCommand(QWidget
*parent
,
1002 const Akonadi::Item::List
&msgList
, uint identity
, KMail::Composer
*win
)
1003 : KMCommand(parent
, msgList
), mIdentity(identity
),
1004 mWin(QPointer
<KMail::Composer
>(win
))
1006 fetchScope().fetchFullPayload(true);
1007 fetchScope().setAncestorRetrieval(Akonadi::ItemFetchScope::Parent
);
1010 KMForwardAttachedCommand::KMForwardAttachedCommand(QWidget
*parent
,
1011 const Akonadi::Item
&msg
, uint identity
, KMail::Composer
*win
)
1012 : KMCommand(parent
, msg
), mIdentity(identity
),
1013 mWin(QPointer
< KMail::Composer
>(win
))
1015 fetchScope().fetchFullPayload(true);
1016 fetchScope().setAncestorRetrieval(Akonadi::ItemFetchScope::Parent
);
1019 KMCommand::Result
KMForwardAttachedCommand::execute()
1021 Akonadi::Item::List msgList
= retrievedMsgs();
1022 Akonadi::Item
firstItem(msgList
.first());
1023 MessageFactory
factory(KMime::Message::Ptr(new KMime::Message
), firstItem
.id(), MailCommon::Util::updatedCollection(firstItem
.parentCollection()));
1024 factory
.setIdentityManager(KMKernel::self()->identityManager());
1025 factory
.setFolderIdentity(MailCommon::Util::folderIdentity(firstItem
));
1027 QPair
< KMime::Message::Ptr
, QList
< KMime::Content
* > > fwdMsg
= factory
.createAttachedForward(msgList
);
1029 mWin
= KMail::makeComposer(fwdMsg
.first
, false, false, KMail::Composer::Forward
, mIdentity
);
1031 foreach (KMime::Content
*attach
, fwdMsg
.second
) {
1032 mWin
->addAttach(attach
);
1038 KMRedirectCommand::KMRedirectCommand(QWidget
*parent
,
1039 const Akonadi::Item::List
&msgList
)
1040 : KMCommand(parent
, msgList
)
1042 fetchScope().fetchFullPayload(true);
1043 fetchScope().fetchAttribute
<MailTransport::SentBehaviourAttribute
>();
1044 fetchScope().fetchAttribute
<MailTransport::TransportAttribute
>();
1046 fetchScope().setAncestorRetrieval(Akonadi::ItemFetchScope::Parent
);
1049 KMRedirectCommand::KMRedirectCommand(QWidget
*parent
,
1050 const Akonadi::Item
&msg
)
1051 : KMCommand(parent
, msg
)
1053 fetchScope().fetchFullPayload(true);
1054 fetchScope().fetchAttribute
<MailTransport::SentBehaviourAttribute
>();
1055 fetchScope().fetchAttribute
<MailTransport::TransportAttribute
>();
1057 fetchScope().setAncestorRetrieval(Akonadi::ItemFetchScope::Parent
);
1060 KMCommand::Result
KMRedirectCommand::execute()
1062 const MailCommon::RedirectDialog::SendMode sendMode
= MessageComposer::MessageComposerSettings::self()->sendImmediate()
1063 ? MailCommon::RedirectDialog::SendNow
1064 : MailCommon::RedirectDialog::SendLater
;
1066 QScopedPointer
<MailCommon::RedirectDialog
> dlg(new MailCommon::RedirectDialog(sendMode
, parentWidget()));
1067 dlg
->setObjectName(QStringLiteral("redirect"));
1068 if (dlg
->exec() == QDialog::Rejected
|| !dlg
) {
1071 if (!TransportManager::self()->showTransportCreationDialog(parentWidget(), TransportManager::IfNoTransportExists
)) {
1075 //TODO use sendlateragent here too.
1076 const MessageComposer::MessageSender::SendMethod method
= (dlg
->sendMode() == MailCommon::RedirectDialog::SendNow
)
1077 ? MessageComposer::MessageSender::SendImmediate
1078 : MessageComposer::MessageSender::SendLater
;
1080 const int identity
= dlg
->identity();
1081 int transportId
= dlg
->transportId();
1082 const QString to
= dlg
->to();
1083 const QString cc
= dlg
->cc();
1084 const QString bcc
= dlg
->bcc();
1085 foreach (const Akonadi::Item
&item
, retrievedMsgs()) {
1086 const KMime::Message::Ptr msg
= MessageCore::Util::message(item
);
1091 MessageFactory
factory(msg
, item
.id(), MailCommon::Util::updatedCollection(item
.parentCollection()));
1092 factory
.setIdentityManager(KMKernel::self()->identityManager());
1093 factory
.setFolderIdentity(MailCommon::Util::folderIdentity(item
));
1095 if (transportId
== -1) {
1096 const MailTransport::TransportAttribute
*transportAttribute
= item
.attribute
<MailTransport::TransportAttribute
>();
1097 if (transportAttribute
) {
1098 transportId
= transportAttribute
->transportId();
1099 const MailTransport::Transport
*transport
= MailTransport::TransportManager::self()->transportById(transportId
);
1106 const MailTransport::SentBehaviourAttribute
*sentAttribute
= item
.attribute
<MailTransport::SentBehaviourAttribute
>();
1108 if (sentAttribute
&& (sentAttribute
->sentBehaviour() == MailTransport::SentBehaviourAttribute::MoveToCollection
)) {
1109 fcc
= QString::number(sentAttribute
->moveToCollection().id());
1112 const KMime::Message::Ptr newMsg
= factory
.createRedirect(to
, cc
, bcc
, transportId
, fcc
, identity
);
1117 MessageStatus status
;
1118 status
.setStatusFromFlags(item
.flags());
1119 if (!status
.isRead()) {
1120 FilterAction::sendMDN(item
, KMime::MDN::Dispatched
);
1123 if (!kmkernel
->msgSender()->send(newMsg
, method
)) {
1124 qCDebug(KMAIL_LOG
) << "KMRedirectCommand: could not redirect message (sending failed)";
1125 return Failed
; // error: couldn't send
1132 KMPrintCommand::KMPrintCommand(QWidget
*parent
, const Akonadi::Item
&msg
,
1133 MessageViewer::HeaderStylePlugin
*plugin
,
1134 MessageViewer::Viewer::DisplayFormatMessage format
, bool htmlLoadExtOverride
,
1135 bool useFixedFont
, const QString
&encoding
)
1136 : KMCommand(parent
, msg
),
1137 mHeaderStylePlugin(plugin
),
1138 mAttachmentStrategy(Q_NULLPTR
),
1139 mEncoding(encoding
),
1141 mHtmlLoadExtOverride(htmlLoadExtOverride
),
1142 mUseFixedFont(useFixedFont
),
1143 mPrintPreview(false)
1145 fetchScope().fetchFullPayload(true);
1146 if (MessageCore::MessageCoreSettings::useDefaultFonts()) {
1147 mOverrideFont
= QFontDatabase::systemFont(QFontDatabase::GeneralFont
);
1149 mOverrideFont
= MessageViewer::MessageViewerSettings::self()->printFont();
1153 void KMPrintCommand::setOverrideFont(const QFont
&font
)
1155 mOverrideFont
= font
;
1158 void KMPrintCommand::setAttachmentStrategy(const MessageViewer::AttachmentStrategy
*strategy
)
1160 mAttachmentStrategy
= strategy
;
1163 void KMPrintCommand::setPrintPreview(bool preview
)
1165 mPrintPreview
= preview
;
1168 KMCommand::Result
KMPrintCommand::execute()
1170 // the window will be deleted after printout is performed, in KMReaderWin::slotPrintMsg()
1171 KMReaderWin
*printerWin
= new KMReaderWin(Q_NULLPTR
, kmkernel
->mainWin(), Q_NULLPTR
, Q_NULLPTR
);
1172 printerWin
->setPrinting(true);
1173 printerWin
->readConfig();
1174 if (mHeaderStylePlugin
) {
1175 printerWin
->viewer()->setPluginName(mHeaderStylePlugin
->name());
1177 printerWin
->setDisplayFormatMessageOverwrite(mFormat
);
1178 printerWin
->setHtmlLoadExtOverride(mHtmlLoadExtOverride
);
1179 printerWin
->setUseFixedFont(mUseFixedFont
);
1180 printerWin
->setOverrideEncoding(mEncoding
);
1181 printerWin
->cssHelper()->setPrintFont(mOverrideFont
);
1182 printerWin
->setDecryptMessageOverwrite(true);
1183 if (mAttachmentStrategy
!= Q_NULLPTR
) {
1184 printerWin
->setAttachmentStrategy(mAttachmentStrategy
);
1186 if (mPrintPreview
) {
1187 printerWin
->viewer()->printPreviewMessage(retrievedMessage());
1189 printerWin
->viewer()->printMessage(retrievedMessage());
1194 KMSetStatusCommand::KMSetStatusCommand(const MessageStatus
&status
,
1195 const Akonadi::Item::List
&items
, bool invert
)
1196 : KMCommand(Q_NULLPTR
, items
), mStatus(status
), mInvertMark(invert
)
1198 setDeletesItself(true);
1201 KMCommand::Result
KMSetStatusCommand::execute()
1203 bool parentStatus
= false;
1204 // Toggle actions on threads toggle the whole thread
1205 // depending on the state of the parent.
1207 const Akonadi::Item first
= retrievedMsgs().first();
1208 MessageStatus pStatus
;
1209 pStatus
.setStatusFromFlags(first
.flags());
1210 if (pStatus
& mStatus
) {
1211 parentStatus
= true;
1213 parentStatus
= false;
1217 Akonadi::Item::List itemsToModify
;
1218 foreach (const Akonadi::Item
&it
, retrievedMsgs()) {
1220 //qCDebug(KMAIL_LOG)<<" item ::"<<tmpItem;
1223 MessageStatus itemStatus
;
1224 itemStatus
.setStatusFromFlags(it
.flags());
1225 if (itemStatus
& mStatus
) {
1230 if (myStatus
!= parentStatus
) {
1235 Akonadi::Item
item(it
);
1236 const Akonadi::Item::Flag flag
= *(mStatus
.statusFlags().begin());
1238 if (item
.hasFlag(flag
)) {
1239 item
.clearFlag(flag
);
1240 itemsToModify
.push_back(item
);
1243 itemsToModify
.push_back(item
);
1246 if (!item
.hasFlag(flag
)) {
1248 itemsToModify
.push_back(item
);
1253 if (itemsToModify
.isEmpty()) {
1254 slotModifyItemDone(Q_NULLPTR
); // pretend we did something
1256 Akonadi::ItemModifyJob
*modifyJob
= new Akonadi::ItemModifyJob(itemsToModify
, this);
1257 modifyJob
->disableRevisionCheck();
1258 modifyJob
->setIgnorePayload(true);
1259 connect(modifyJob
, &Akonadi::ItemModifyJob::result
, this, &KMSetStatusCommand::slotModifyItemDone
);
1264 void KMSetStatusCommand::slotModifyItemDone(KJob
*job
)
1266 if (job
&& job
->error()) {
1267 qCWarning(KMAIL_LOG
) << " Error trying to set item status:" << job
->errorText();
1272 KMSetTagCommand::KMSetTagCommand(const Akonadi::Tag::List
&tags
, const Akonadi::Item::List
&item
,
1278 setDeletesItself(true);
1281 KMCommand::Result
KMSetTagCommand::execute()
1283 Q_FOREACH (const Akonadi::Tag
&tag
, mTags
) {
1284 if (!tag
.isValid()) {
1285 Akonadi::TagCreateJob
*createJob
= new Akonadi::TagCreateJob(tag
, this);
1286 connect(createJob
, &Akonadi::TagCreateJob::result
, this, &KMSetTagCommand::slotModifyItemDone
);
1288 mCreatedTags
<< tag
;
1292 if (mCreatedTags
.size() == mTags
.size()) {
1301 void KMSetTagCommand::setTags()
1303 Akonadi::Item::List itemsToModify
;
1304 itemsToModify
.reserve(mItem
.count());
1305 Q_FOREACH (const Akonadi::Item
&i
, mItem
) {
1306 Akonadi::Item
item(i
);
1307 if (mMode
== CleanExistingAndAddNew
) {
1308 //WorkAround. ClearTags doesn't work.
1309 Q_FOREACH (const Akonadi::Tag
&tag
, item
.tags()) {
1315 if (mMode
== KMSetTagCommand::Toggle
) {
1316 Q_FOREACH (const Akonadi::Tag
&tag
, mCreatedTags
) {
1317 if (item
.hasTag(tag
)) {
1324 if (!mCreatedTags
.isEmpty()) {
1325 item
.setTags(mCreatedTags
);
1328 itemsToModify
<< item
;
1330 Akonadi::ItemModifyJob
*modifyJob
= new Akonadi::ItemModifyJob(itemsToModify
, this);
1331 modifyJob
->disableRevisionCheck();
1332 modifyJob
->setIgnorePayload(true);
1333 connect(modifyJob
, &Akonadi::ItemModifyJob::result
, this, &KMSetTagCommand::slotModifyItemDone
);
1335 if (!mCreatedTags
.isEmpty()) {
1336 KConfigGroup
tag(KMKernel::self()->config(), "MessageListView");
1337 const QString oldTagList
= tag
.readEntry("TagSelected");
1338 QStringList lst
= oldTagList
.split(QStringLiteral(","));
1339 Q_FOREACH (const Akonadi::Tag
&tag
, mCreatedTags
) {
1340 const QString url
= tag
.url().url();
1341 if (!lst
.contains(url
)) {
1345 tag
.writeEntry("TagSelected", lst
);
1346 KMKernel::self()->updatePaneTagComboBox();
1350 void KMSetTagCommand::slotModifyItemDone(KJob
*job
)
1352 if (job
&& job
->error()) {
1353 qCWarning(KMAIL_LOG
) << " Error trying to set item status:" << job
->errorText();
1358 KMFilterActionCommand::KMFilterActionCommand(QWidget
*parent
,
1359 const QVector
<qlonglong
> &msgListId
,
1360 const QString
&filterId
)
1361 : KMCommand(parent
), mMsgListId(msgListId
), mFilterId(filterId
)
1365 KMCommand::Result
KMFilterActionCommand::execute()
1367 #ifndef QT_NO_CURSOR
1368 KPIM::KCursorSaver
busy(KPIM::KBusyPtr::busy());
1371 const int msgCountToFilter
= mMsgListId
.count();
1372 ProgressItem
*progressItem
=
1373 ProgressManager::createProgressItem(
1374 QLatin1String("filter") + ProgressManager::getUniqueID(),
1375 i18n("Filtering messages"), QString(), true, KPIM::ProgressItem::Unknown
);
1376 progressItem
->setTotalItems(msgCountToFilter
);
1378 foreach (const qlonglong
&id
, mMsgListId
) {
1379 int diff
= msgCountToFilter
- ++msgCount
;
1380 if (diff
< 10 || !(msgCount
% 10) || msgCount
<= 10) {
1381 progressItem
->updateProgress();
1382 const QString statusMsg
= i18n("Filtering message %1 of %2",
1383 msgCount
, msgCountToFilter
);
1384 KPIM::BroadcastStatus::instance()->setStatusMsg(statusMsg
);
1385 qApp
->processEvents(QEventLoop::ExcludeUserInputEvents
, 50);
1388 MailCommon::FilterManager::instance()->filter(Akonadi::Item(id
), mFilterId
, QString());
1389 progressItem
->incCompletedItems();
1392 progressItem
->setComplete();
1393 progressItem
= Q_NULLPTR
;
1397 KMMetaFilterActionCommand::KMMetaFilterActionCommand(const QString
&filterId
,
1400 mFilterId(filterId
), mMainWidget(main
)
1404 void KMMetaFilterActionCommand::start()
1406 KMCommand
*filterCommand
= new KMFilterActionCommand(
1407 mMainWidget
, mMainWidget
->messageListPane()->selectionAsMessageItemListId(), mFilterId
);
1408 filterCommand
->start();
1411 KMMailingListFilterCommand::KMMailingListFilterCommand(QWidget
*parent
,
1412 const Akonadi::Item
&msg
)
1413 : KMCommand(parent
, msg
)
1417 KMCommand::Result
KMMailingListFilterCommand::execute()
1421 Akonadi::Item item
= retrievedMessage();
1422 KMime::Message::Ptr msg
= MessageCore::Util::message(item
);
1426 if (!MailingList::name(msg
, name
, value
).isEmpty()) {
1427 FilterIf
->openFilterDialog(false);
1428 FilterIf
->createFilter(name
, value
);
1435 KMCopyCommand::KMCopyCommand(const Akonadi::Collection
&destFolder
,
1436 const Akonadi::Item::List
&msgList
)
1437 : KMCommand(Q_NULLPTR
, msgList
), mDestFolder(destFolder
)
1441 KMCopyCommand::KMCopyCommand(const Akonadi::Collection
&destFolder
, const Akonadi::Item
&msg
)
1442 : KMCommand(Q_NULLPTR
, msg
), mDestFolder(destFolder
)
1446 KMCommand::Result
KMCopyCommand::execute()
1448 setDeletesItself(true);
1450 Akonadi::Item::List listItem
= retrievedMsgs();
1451 Akonadi::ItemCopyJob
*job
= new Akonadi::ItemCopyJob(listItem
, Akonadi::Collection(mDestFolder
.id()), this);
1452 connect(job
, &KIO::Job::result
, this, &KMCopyCommand::slotCopyResult
);
1457 void KMCopyCommand::slotCopyResult(KJob
*job
)
1467 KMMoveCommand::KMMoveCommand(const Akonadi::Collection
&destFolder
,
1468 const Akonadi::Item::List
&msgList
,
1469 MessageList::Core::MessageItemSetReference ref
)
1470 : KMCommand(Q_NULLPTR
, msgList
), mDestFolder(destFolder
), mProgressItem(Q_NULLPTR
), mRef(ref
)
1472 fetchScope().setAncestorRetrieval(Akonadi::ItemFetchScope::Parent
);
1475 KMMoveCommand::KMMoveCommand(const Akonadi::Collection
&destFolder
,
1476 const Akonadi::Item
&msg
,
1477 MessageList::Core::MessageItemSetReference ref
)
1478 : KMCommand(Q_NULLPTR
, msg
), mDestFolder(destFolder
), mProgressItem(Q_NULLPTR
), mRef(ref
)
1480 fetchScope().setAncestorRetrieval(Akonadi::ItemFetchScope::Parent
);
1483 void KMMoveCommand::slotMoveResult(KJob
*job
)
1488 completeMove(Failed
);
1494 KMCommand::Result
KMMoveCommand::execute()
1496 #ifndef QT_NO_CURSOR
1497 KPIM::KCursorSaver
busy(KPIM::KBusyPtr::busy());
1499 setEmitsCompletedItself(true);
1500 setDeletesItself(true);
1501 Akonadi::Item::List retrievedList
= retrievedMsgs();
1502 if (!retrievedList
.isEmpty()) {
1503 if (mDestFolder
.isValid()) {
1504 Akonadi::ItemMoveJob
*job
= new Akonadi::ItemMoveJob(retrievedList
, mDestFolder
, this);
1505 connect(job
, &KIO::Job::result
, this, &KMMoveCommand::slotMoveResult
);
1507 // group by source folder for undo
1508 std::sort(retrievedList
.begin(), retrievedList
.end(), boost::bind(&Akonadi::Item::storageCollectionId
, _1
) <
1509 boost::bind(&Akonadi::Item::storageCollectionId
, _2
));
1510 Akonadi::Collection parent
;
1512 foreach (const Akonadi::Item
&item
, retrievedList
) {
1513 if (item
.storageCollectionId() <= 0) {
1516 if (parent
.id() != item
.storageCollectionId()) {
1517 parent
= Akonadi::Collection(item
.storageCollectionId());
1518 undoId
= kmkernel
->undoStack()->newUndoAction(parent
, mDestFolder
);
1520 kmkernel
->undoStack()->addMsgToAction(undoId
, item
);
1523 Akonadi::ItemDeleteJob
*job
= new Akonadi::ItemDeleteJob(retrievedList
, this);
1524 connect(job
, &KIO::Job::result
, this, &KMMoveCommand::slotMoveResult
);
1530 // TODO set SSL state according to source and destfolder connection?
1531 Q_ASSERT(!mProgressItem
);
1533 ProgressManager::createProgressItem(QLatin1String("move") + ProgressManager::getUniqueID(),
1534 mDestFolder
.isValid() ? i18n("Moving messages") : i18n("Deleting messages"), QString(), true, KPIM::ProgressItem::Unknown
);
1535 mProgressItem
->setUsesBusyIndicator(true);
1536 connect(mProgressItem
, &ProgressItem::progressItemCanceled
,
1537 this, &KMMoveCommand::slotMoveCanceled
);
1541 void KMMoveCommand::completeMove(Result result
)
1543 if (mProgressItem
) {
1544 mProgressItem
->setComplete();
1545 mProgressItem
= Q_NULLPTR
;
1548 Q_EMIT
moveDone(this);
1549 Q_EMIT
completed(this);
1553 void KMMoveCommand::slotMoveCanceled()
1555 completeMove(Canceled
);
1558 // srcFolder doesn't make much sense for searchFolders
1559 KMTrashMsgCommand::KMTrashMsgCommand(const Akonadi::Collection
&srcFolder
,
1560 const Akonadi::Item::List
&msgList
, MessageList::Core::MessageItemSetReference ref
)
1561 : KMMoveCommand(findTrashFolder(srcFolder
), msgList
, ref
)
1565 KMTrashMsgCommand::KMTrashMsgCommand(const Akonadi::Collection
&srcFolder
, const Akonadi::Item
&msg
, MessageList::Core::MessageItemSetReference ref
)
1566 : KMMoveCommand(findTrashFolder(srcFolder
), msg
, ref
)
1570 Akonadi::Collection
KMTrashMsgCommand::findTrashFolder(const Akonadi::Collection
&folder
)
1572 Akonadi::Collection col
= CommonKernel
->trashCollectionFromResource(folder
);
1573 if (!col
.isValid()) {
1574 col
= CommonKernel
->trashCollectionFolder();
1576 if (folder
!= col
) {
1579 return Akonadi::Collection();
1582 KMSaveAttachmentsCommand::KMSaveAttachmentsCommand(QWidget
*parent
, const Akonadi::Item
&msg
, MessageViewer::Viewer
*viewer
)
1583 : KMCommand(parent
, msg
),
1586 fetchScope().fetchFullPayload(true);
1589 KMSaveAttachmentsCommand::KMSaveAttachmentsCommand(QWidget
*parent
, const Akonadi::Item::List
&msgs
)
1590 : KMCommand(parent
, msgs
),
1593 fetchScope().fetchFullPayload(true);
1596 KMCommand::Result
KMSaveAttachmentsCommand::execute()
1598 KMime::Content::List contentsToSave
;
1599 foreach (const Akonadi::Item
&item
, retrievedMsgs()) {
1600 if (item
.hasPayload
<KMime::Message::Ptr
>()) {
1601 contentsToSave
+= item
.payload
<KMime::Message::Ptr
>()->attachments();
1603 qCWarning(KMAIL_LOG
) << "Retrieved item has no payload? Ignoring for saving the attachments";
1607 if (MessageViewer::Util::saveAttachments(contentsToSave
, parentWidget(), currentUrl
)) {
1609 mViewer
->showOpenAttachmentFolderWidget(currentUrl
);
1616 KMResendMessageCommand::KMResendMessageCommand(QWidget
*parent
,
1617 const Akonadi::Item
&msg
)
1618 : KMCommand(parent
, msg
)
1620 fetchScope().fetchFullPayload(true);
1621 fetchScope().setAncestorRetrieval(Akonadi::ItemFetchScope::Parent
);
1624 KMCommand::Result
KMResendMessageCommand::execute()
1626 Akonadi::Item item
= retrievedMessage();
1627 KMime::Message::Ptr msg
= MessageCore::Util::message(item
);
1632 MessageFactory
factory(msg
, item
.id(), MailCommon::Util::updatedCollection(item
.parentCollection()));
1633 factory
.setIdentityManager(KMKernel::self()->identityManager());
1634 factory
.setFolderIdentity(MailCommon::Util::folderIdentity(item
));
1635 KMime::Message::Ptr newMsg
= factory
.createResend();
1636 newMsg
->contentType()->setCharset(MessageViewer::NodeHelper::charset(msg
.data()));
1638 KMail::Composer
*win
= KMail::makeComposer();
1639 if (auto hdr
= msg
->replyTo(false)) {
1640 const QString replyTo
= hdr
->asUnicodeString();
1641 win
->setCurrentReplyTo(replyTo
);
1643 bool lastEncrypt
= false;
1644 bool lastSign
= false;
1645 KMail::Util::lastEncryptAndSignState(lastEncrypt
, lastSign
, msg
);
1646 win
->setMessage(newMsg
, lastSign
, lastEncrypt
, false, true);
1653 KMShareImageCommand::KMShareImageCommand(const QUrl
&url
, QWidget
*parent
)
1654 : KMCommand(parent
),
1659 KMCommand::Result
KMShareImageCommand::execute()
1661 KMime::Message::Ptr
msg(new KMime::Message
);
1664 MessageHelper::initHeader(msg
, KMKernel::self()->identityManager(), id
);
1665 msg
->contentType()->setCharset("utf-8");
1667 KMail::Composer
*win
= KMail::makeComposer(msg
, false, false, KMail::Composer::New
, id
);
1668 win
->setFocusToSubject();
1669 win
->addAttachment(mUrl
, i18n("Image"));
1674 KMFetchMessageCommand::KMFetchMessageCommand(QWidget
*parent
, const Akonadi::Item
&item
)
1675 : KMCommand(parent
, item
)
1677 // Workaround KMCommand::transferSelectedMsgs() expecting non-empty fetchscope
1678 fetchScope().fetchFullPayload(true);
1681 Akonadi::ItemFetchJob
*KMFetchMessageCommand::createFetchJob(const Akonadi::Item::List
&items
)
1683 Q_ASSERT(items
.size() == 1);
1684 Akonadi::ItemFetchJob
*fetch
= MessageViewer::Viewer::createFetchJob(items
.first());
1685 fetchScope() = fetch
->fetchScope();
1689 KMCommand::Result
KMFetchMessageCommand::execute()
1691 Akonadi::Item item
= retrievedMessage();
1692 if (!item
.isValid() || !item
.hasPayload
<KMime::Message::Ptr
>()) {
1700 Akonadi::Item
KMFetchMessageCommand::item() const