1 //
2 
3 #pragma once
4 
5 #include "kmail_private_export.h"
6 
7 #include <MailCommon/SearchPattern>
8 #include <MessageComposer/MessageFactoryNG>
9 #include <MessageList/View>
10 #include <MessageViewer/Viewer>
11 
12 #include <Akonadi/KMime/MessageStatus>
13 #include <KIO/Job>
14 #include <KMime/KMimeMessage>
15 
16 #include <Akonadi/Collection>
17 #include <Akonadi/Item>
18 #include <Akonadi/ItemFetchScope>
19 #include <QList>
20 #include <QPointer>
21 #include <QUrl>
22 namespace Akonadi
23 {
24 class Tag;
25 }
26 
27 namespace KPIM
28 {
29 class ProgressItem;
30 }
31 
32 using Akonadi::MessageStatus;
33 
34 class QProgressDialog;
35 class KMMainWidget;
36 class KMReaderMainWin;
37 
38 template<typename T> class QSharedPointer;
39 
40 namespace MessageViewer
41 {
42 class HeaderStyle;
43 class AttachmentStrategy;
44 }
45 
46 namespace KIO
47 {
48 class Job;
49 }
50 namespace KMail
51 {
52 class Composer;
53 }
54 using PartNodeMessageMap = QMap<KMime::Content *, Akonadi::Item>;
55 /// Small helper structure which encapsulates the KMMessage created when creating a reply, and
56 
57 class KMAILTESTS_TESTS_EXPORT KMCommand : public QObject
58 {
59     Q_OBJECT
60 
61 public:
62     enum Result {
63         Undefined,
64         OK,
65         Canceled,
66         Failed,
67     };
68 
69     // Trivial constructor, don't retrieve any messages
70     explicit KMCommand(QWidget *parent = nullptr);
71     KMCommand(QWidget *parent, const Akonadi::Item &);
72     // Retrieve all messages in msgList when start is called.
73     KMCommand(QWidget *parent, const Akonadi::Item::List &msgList);
74     // Retrieve the single message msgBase when start is called.
75     ~KMCommand() override;
76 
77     /** Returns the result of the command. Only call this method from the slot
78       connected to completed().
79     */
80     Q_REQUIRED_RESULT Result result() const;
81 
82 public Q_SLOTS:
83     // Retrieve messages then calls execute
84     void start();
85 
86 Q_SIGNALS:
87 
88     /// @param result The status of the command.
89     void messagesTransfered(KMCommand::Result result);
90 
91     /// Emitted when the command has completed.
92     void completed(KMCommand *command);
93 
94 protected:
95     virtual Akonadi::ItemFetchJob *createFetchJob(const Akonadi::Item::List &items);
96 
97     /** Allows to configure how much data should be retrieved of the messages. */
fetchScope()98     Q_REQUIRED_RESULT Akonadi::ItemFetchScope &fetchScope()
99     {
100         return mFetchScope;
101     }
102 
103     // Returns list of messages retrieved
104     Q_REQUIRED_RESULT const Akonadi::Item::List retrievedMsgs() const;
105     // Returns the single message retrieved
106     Q_REQUIRED_RESULT Akonadi::Item retrievedMessage() const;
107     // Returns the parent widget
108     QWidget *parentWidget() const;
109 
110     Q_REQUIRED_RESULT bool deletesItself() const;
111     /** Specify whether the subclass takes care of the deletion of the object.
112       By default the base class will delete the object.
113       @param deletesItself true if the subclass takes care of deletion, false
114                            if the base class should take care of deletion
115     */
116     void setDeletesItself(bool deletesItself);
117 
118     Q_REQUIRED_RESULT bool emitsCompletedItself() const;
119     /** Specify whether the subclass takes care of emitting the completed()
120       signal. By default the base class will Q_EMIT this signal.
121       @param emitsCompletedItself true if the subclass emits the completed
122                                   signal, false if the base class should Q_EMIT
123                                   the signal
124     */
125     void setEmitsCompletedItself(bool emitsCompletedItself);
126 
127     /** Use this to set the result of the command.
128       @param result The result of the command.
129     */
130     void setResult(Result result);
131 
132 private:
133     Q_DISABLE_COPY(KMCommand)
134     // execute should be implemented by derived classes
135     virtual Result execute() = 0;
136 
137     /** transfers the list of (imap)-messages
138      *  this is a necessary preparation for e.g. forwarding */
139     void transferSelectedMsgs();
140 
141 private Q_SLOTS:
142     void slotPostTransfer(KMCommand::Result result);
143     /** the msg has been transferred */
144     void slotMsgTransfered(const Akonadi::Item::List &msgs);
145     /** the KMImapJob is finished */
146     void slotJobFinished();
147     /** the transfer was canceled */
148     void slotTransferCancelled();
149 
150 protected:
151     Akonadi::Item::List mRetrievedMsgs;
152 
153 private:
154     // ProgressDialog for transferring messages
155     QPointer<QProgressDialog> mProgressDialog;
156     // Currently only one async command allowed at a time
157     static int mCountJobs;
158     int mCountMsgs = 0;
159     Result mResult = Undefined;
160     bool mDeletesItself : 1;
161     bool mEmitsCompletedItself : 1;
162 
163     QWidget *const mParent;
164     Akonadi::Item::List mMsgList;
165     Akonadi::ItemFetchScope mFetchScope;
166 };
167 
168 class KMAILTESTS_TESTS_EXPORT KMMailtoComposeCommand : public KMCommand
169 {
170     Q_OBJECT
171 
172 public:
173     explicit KMMailtoComposeCommand(const QUrl &url, const Akonadi::Item &msg = Akonadi::Item());
174 
175 private:
176     Q_REQUIRED_RESULT Result execute() override;
177 
178     QUrl mUrl;
179     Akonadi::Item mMessage;
180 };
181 
182 class KMAILTESTS_TESTS_EXPORT KMMailtoReplyCommand : public KMCommand
183 {
184     Q_OBJECT
185 
186 public:
187     KMMailtoReplyCommand(QWidget *parent, const QUrl &url, const Akonadi::Item &msg, const QString &selection);
188 
189     Q_REQUIRED_RESULT bool replyAsHtml() const;
190     void setReplyAsHtml(bool replyAsHtml);
191 
192 private:
193     Q_REQUIRED_RESULT Result execute() override;
194 
195     QUrl mUrl;
196     QString mSelection;
197     bool mReplyAsHtml = false;
198 };
199 
200 class KMAILTESTS_TESTS_EXPORT KMMailtoForwardCommand : public KMCommand
201 {
202     Q_OBJECT
203 
204 public:
205     KMMailtoForwardCommand(QWidget *parent, const QUrl &url, const Akonadi::Item &msg);
206 
207 private:
208     Q_REQUIRED_RESULT Result execute() override;
209     QUrl mUrl;
210 };
211 
212 class KMAILTESTS_TESTS_EXPORT KMAddBookmarksCommand : public KMCommand
213 {
214     Q_OBJECT
215 
216 public:
217     KMAddBookmarksCommand(const QUrl &url, QWidget *parent);
218 
219 private:
220     Q_REQUIRED_RESULT Result execute() override;
221 
222     QUrl mUrl;
223 };
224 
225 class KMAILTESTS_TESTS_EXPORT KMUrlSaveCommand : public KMCommand
226 {
227     Q_OBJECT
228 
229 public:
230     KMUrlSaveCommand(const QUrl &url, QWidget *parent);
231 
232 private Q_SLOTS:
233     void slotUrlSaveResult(KJob *job);
234 
235 private:
236     Q_REQUIRED_RESULT Result execute() override;
237 
238     QUrl mUrl;
239 };
240 
241 class KMAILTESTS_TESTS_EXPORT KMEditItemCommand : public KMCommand
242 {
243     Q_OBJECT
244 
245 public:
246     explicit KMEditItemCommand(QWidget *parent, const Akonadi::Item &msg, bool deleteFromSource = true);
247     ~KMEditItemCommand() override;
248 private Q_SLOTS:
249     void slotDeleteItem(KJob *job);
250 
251 private:
252     Q_REQUIRED_RESULT Result execute() override;
253     bool mDeleteFromSource = false;
254 };
255 
256 class KMAILTESTS_TESTS_EXPORT KMEditMessageCommand : public KMCommand
257 {
258     Q_OBJECT
259 
260 public:
261     explicit KMEditMessageCommand(QWidget *parent, const KMime::Message::Ptr &msg);
262 
263 private:
264     Q_REQUIRED_RESULT Result execute() override;
265     KMime::Message::Ptr mMessage;
266 };
267 
268 class KMAILTESTS_TESTS_EXPORT KMUseTemplateCommand : public KMCommand
269 {
270     Q_OBJECT
271 
272 public:
273     KMUseTemplateCommand(QWidget *parent, const Akonadi::Item &msg);
274 
275 private:
276     Q_REQUIRED_RESULT Result execute() override;
277 };
278 
279 class KMAILTESTS_TESTS_EXPORT KMSaveMsgCommand : public KMCommand
280 {
281     Q_OBJECT
282 
283 public:
284     KMSaveMsgCommand(QWidget *parent, const Akonadi::Item::List &msgList);
285 
286 private:
287     Q_REQUIRED_RESULT Result execute() override;
288 };
289 
290 class KMAILTESTS_TESTS_EXPORT KMOpenMsgCommand : public KMCommand
291 {
292     Q_OBJECT
293 
294 public:
295     explicit KMOpenMsgCommand(QWidget *parent, const QUrl &url = QUrl(), const QString &encoding = QString(), KMMainWidget *main = nullptr);
296 
297 private:
298     Q_REQUIRED_RESULT Result execute() override;
299 
300 private Q_SLOTS:
301     void slotDataArrived(KIO::Job *job, const QByteArray &data);
302     void slotResult(KJob *job);
303 
304 private:
305     void doesNotContainMessage();
306     static const int MAX_CHUNK_SIZE = 64 * 1024;
307     QUrl mUrl;
308     QString mMsgString;
309     KIO::TransferJob *mJob = nullptr;
310     const QString mEncoding;
311     KMMainWidget *mMainWidget = nullptr;
312 };
313 
314 class KMAILTESTS_TESTS_EXPORT KMSaveAttachmentsCommand : public KMCommand
315 {
316     Q_OBJECT
317 public:
318     /** Use this to save all attachments of the given message.
319       @param parent  The parent widget of the command used for message boxes.
320       @param msg     The message of which the attachments should be saved.
321       @param viewer  The message viewer.
322     */
323     KMSaveAttachmentsCommand(QWidget *parent, const Akonadi::Item &msg, MessageViewer::Viewer *viewer);
324     /** Use this to save all attachments of the given messages.
325       @param parent  The parent widget of the command used for message boxes.
326       @param msgs    The messages of which the attachments should be saved.
327     */
328     KMSaveAttachmentsCommand(QWidget *parent, const Akonadi::Item::List &msgs, MessageViewer::Viewer *viewer);
329 
330 private:
331     Q_REQUIRED_RESULT Result execute() override;
332     MessageViewer::Viewer *mViewer = nullptr;
333 };
334 
335 class KMAILTESTS_TESTS_EXPORT KMReplyCommand : public KMCommand
336 {
337     Q_OBJECT
338 public:
339     KMReplyCommand(QWidget *parent,
340                    const Akonadi::Item &msg,
341                    MessageComposer::ReplyStrategy replyStrategy,
342                    const QString &selection = QString(),
343                    bool noquote = false,
344                    const QString &templateName = QString());
345     Q_REQUIRED_RESULT bool replyAsHtml() const;
346     void setReplyAsHtml(bool replyAsHtml);
347 
348 private:
349     Q_REQUIRED_RESULT Result execute() override;
350 
351 private:
352     QString mSelection;
353     QString mTemplate;
354     MessageComposer::ReplyStrategy m_replyStrategy;
355     bool mNoQuote = false;
356     bool mReplyAsHtml = false;
357 };
358 
359 class KMAILTESTS_TESTS_EXPORT KMForwardCommand : public KMCommand
360 {
361     Q_OBJECT
362 
363 public:
364     KMForwardCommand(QWidget *parent,
365                      const Akonadi::Item::List &msgList,
366                      uint identity = 0,
367                      const QString &templateName = QString(),
368                      const QString &selection = QString());
369     KMForwardCommand(QWidget *parent,
370                      const Akonadi::Item &msg,
371                      uint identity = 0,
372                      const QString &templateName = QString(),
373                      const QString &selection = QString());
374 
375 private:
376     Q_REQUIRED_RESULT KMCommand::Result createComposer(const Akonadi::Item &item);
377     Result execute() override;
378 
379 private:
380     uint mIdentity;
381     QString mTemplate;
382     QString mSelection;
383 };
384 
385 class KMAILTESTS_TESTS_EXPORT KMForwardAttachedCommand : public KMCommand
386 {
387     Q_OBJECT
388 
389 public:
390     KMForwardAttachedCommand(QWidget *parent, const Akonadi::Item::List &msgList, uint identity = 0, KMail::Composer *win = nullptr);
391     KMForwardAttachedCommand(QWidget *parent, const Akonadi::Item &msg, uint identity = 0, KMail::Composer *win = nullptr);
392 
393 private:
394     Result execute() override;
395 
396     uint mIdentity;
397     QPointer<KMail::Composer> mWin;
398 };
399 
400 class KMAILTESTS_TESTS_EXPORT KMRedirectCommand : public KMCommand
401 {
402     Q_OBJECT
403 
404 public:
405     KMRedirectCommand(QWidget *parent, const Akonadi::Item &msg);
406     KMRedirectCommand(QWidget *parent, const Akonadi::Item::List &msgList);
407 
408 private:
409     Q_REQUIRED_RESULT Result execute() override;
410 };
411 
412 struct KMAILTESTS_TESTS_EXPORT KMPrintCommandInfo {
413     Akonadi::Item mMsg;
414     QFont mOverrideFont;
415     QString mEncoding;
416     MessageViewer::Viewer::DisplayFormatMessage mFormat = MessageViewer::Viewer::UseGlobalSetting;
417     const MessageViewer::AttachmentStrategy *mAttachmentStrategy = nullptr;
418     MessageViewer::HeaderStylePlugin *mHeaderStylePlugin = nullptr;
419     bool mHtmlLoadExtOverride = false;
420     bool mUseFixedFont = false;
421     bool mPrintPreview = false;
422     bool mShowSignatureDetails = false;
423     bool mShowEncryptionDetails = false;
424 };
425 
426 QDebug operator<<(QDebug d, const KMPrintCommandInfo &t);
427 
428 class KMAILTESTS_TESTS_EXPORT KMPrintCommand : public KMCommand
429 {
430     Q_OBJECT
431 
432 public:
433     KMPrintCommand(QWidget *parent, const KMPrintCommandInfo &commandInfo);
434 
435 private:
436     Q_REQUIRED_RESULT Result execute() override;
437 
438     KMPrintCommandInfo mPrintCommandInfo;
439 };
440 
441 class KMAILTESTS_TESTS_EXPORT KMSetStatusCommand : public KMCommand
442 {
443     Q_OBJECT
444 
445 public:
446     // Serial numbers
447     KMSetStatusCommand(const MessageStatus &status, const Akonadi::Item::List &items, bool invert = false);
448 
449 protected Q_SLOTS:
450     void slotModifyItemDone(KJob *job);
451 
452 private:
453     Q_REQUIRED_RESULT Result execute() override;
454     MessageStatus mStatus;
455     bool mInvertMark = false;
456 };
457 
458 /** This command is used to set or toggle a tag for a list of messages. If toggle is
459     true then the tag is deleted if it is already applied.
460  */
461 class KMAILTESTS_TESTS_EXPORT KMSetTagCommand : public KMCommand
462 {
463     Q_OBJECT
464 
465 public:
466     enum SetTagMode {
467         AddIfNotExisting,
468         Toggle,
469         CleanExistingAndAddNew,
470     };
471 
472     KMSetTagCommand(const Akonadi::Tag::List &tags, const Akonadi::Item::List &item, SetTagMode mode = AddIfNotExisting);
473 
474 protected Q_SLOTS:
475     void slotModifyItemDone(KJob *job);
476 
477 private:
478     Q_REQUIRED_RESULT Result execute() override;
479     void setTags();
480 
481     Akonadi::Tag::List mTags;
482     Akonadi::Tag::List mCreatedTags;
483     Akonadi::Item::List mItem;
484     SetTagMode mMode;
485 };
486 
487 /* This command is used to apply a single filter (AKA ad-hoc filter)
488     to a set of messages */
489 class KMAILTESTS_TESTS_EXPORT KMFilterActionCommand : public KMCommand
490 {
491     Q_OBJECT
492 
493 public:
494     KMFilterActionCommand(QWidget *parent, const QVector<qlonglong> &msgListId, const QString &filterId);
495 
496 private:
497     Q_REQUIRED_RESULT Result execute() override;
498     QVector<qlonglong> mMsgListId;
499     QString mFilterId;
500 };
501 
502 class KMAILTESTS_TESTS_EXPORT KMMetaFilterActionCommand : public QObject
503 {
504     Q_OBJECT
505 
506 public:
507     KMMetaFilterActionCommand(const QString &filterId, KMMainWidget *main);
508 
509 public Q_SLOTS:
510     void start();
511 
512 private:
513     QString mFilterId;
514     KMMainWidget *mMainWidget = nullptr;
515 };
516 
517 class KMAILTESTS_TESTS_EXPORT KMMailingListFilterCommand : public KMCommand
518 {
519     Q_OBJECT
520 
521 public:
522     KMMailingListFilterCommand(QWidget *parent, const Akonadi::Item &msg);
523 
524 private:
525     Q_REQUIRED_RESULT Result execute() override;
526 };
527 
528 class KMAILTESTS_TESTS_EXPORT KMCopyCommand : public KMCommand
529 {
530     Q_OBJECT
531 
532 public:
533     KMCopyCommand(const Akonadi::Collection &destFolder, const Akonadi::Item::List &msgList);
534     KMCopyCommand(const Akonadi::Collection &destFolder, const Akonadi::Item &msg);
535 
536 protected Q_SLOTS:
537     void slotCopyResult(KJob *job);
538 
539 private:
540     Q_REQUIRED_RESULT Result execute() override;
541 
542     Akonadi::Collection mDestFolder;
543 };
544 
545 class KMAILTESTS_TESTS_EXPORT KMCopyDecryptedCommand : public KMCommand
546 {
547     Q_OBJECT
548 public:
549     KMCopyDecryptedCommand(const Akonadi::Collection &destFolder, const Akonadi::Item::List &msgList);
550     KMCopyDecryptedCommand(const Akonadi::Collection &destFolder, const Akonadi::Item &msg);
551 
552 protected Q_SLOTS:
553     void slotAppendResult(KJob *job);
554 
555 private:
556     Q_REQUIRED_RESULT Result execute() override;
557 
558     Akonadi::Collection mDestFolder;
559     QList<KJob *> mPendingJobs;
560 };
561 
562 class KMAILTESTS_TESTS_EXPORT KMMoveCommand : public KMCommand
563 {
564     Q_OBJECT
565 
566 public:
567     KMMoveCommand(const Akonadi::Collection &destFolder, const Akonadi::Item::List &msgList, MessageList::Core::MessageItemSetReference ref);
568     KMMoveCommand(const Akonadi::Collection &destFolder,
569                   const Akonadi::Item &msg,
570                   MessageList::Core::MessageItemSetReference ref = MessageList::Core::MessageItemSetReference());
destFolder()571     Q_REQUIRED_RESULT Akonadi::Collection destFolder() const
572     {
573         return mDestFolder;
574     }
575 
refSet()576     Q_REQUIRED_RESULT MessageList::Core::MessageItemSetReference refSet() const
577     {
578         return mRef;
579     }
580 
581 public Q_SLOTS:
582     void slotMoveCanceled();
583     void slotMoveResult(KJob *job);
584 
585 protected:
setDestFolder(const Akonadi::Collection & folder)586     void setDestFolder(const Akonadi::Collection &folder)
587     {
588         mDestFolder = folder;
589     }
590 
591 Q_SIGNALS:
592     void moveDone(KMMoveCommand *);
593 
594 private:
595     Q_REQUIRED_RESULT Result execute() override;
596     void completeMove(Result result);
597 
598     Akonadi::Collection mDestFolder;
599     KPIM::ProgressItem *mProgressItem = nullptr;
600     MessageList::Core::MessageItemSetReference mRef;
601 };
602 
603 class KMAILTESTS_TESTS_EXPORT KMTrashMsgCommand final : public KMCommand
604 {
605     Q_OBJECT
606 
607 public:
608     enum TrashOperation {
609         Unknown,
610         MoveToTrash,
611         Delete,
612         Both,
613     };
614 
615     KMTrashMsgCommand(const Akonadi::Collection &srcFolder, const Akonadi::Item::List &msgList, MessageList::Core::MessageItemSetReference ref);
616     KMTrashMsgCommand(const Akonadi::Collection &srcFolder, const Akonadi::Item &msg, MessageList::Core::MessageItemSetReference ref);
refSet()617     Q_REQUIRED_RESULT MessageList::Core::MessageItemSetReference refSet() const
618     {
619         return mRef;
620     }
621 
622     TrashOperation operation() const;
623 
624 public Q_SLOTS:
625     void slotMoveCanceled();
626 
627 private Q_SLOTS:
628     void slotMoveResult(KJob *job);
629     void slotDeleteResult(KJob *job);
630 Q_SIGNALS:
631     void moveDone(KMTrashMsgCommand *);
632 
633 private:
634     Q_REQUIRED_RESULT Result execute() override;
635     void completeMove(Result result);
636 
637     Q_REQUIRED_RESULT static Akonadi::Collection findTrashFolder(const Akonadi::Collection &srcFolder);
638 
639     QMap<Akonadi::Collection, Akonadi::Item::List> mTrashFolders;
640     KPIM::ProgressItem *mMoveProgress = nullptr;
641     KPIM::ProgressItem *mDeleteProgress = nullptr;
642     MessageList::Core::MessageItemSetReference mRef;
643     QList<KJob *> mPendingMoves;
644     QList<KJob *> mPendingDeletes;
645 };
646 
647 class KMAILTESTS_TESTS_EXPORT KMResendMessageCommand : public KMCommand
648 {
649     Q_OBJECT
650 
651 public:
652     explicit KMResendMessageCommand(QWidget *parent, const Akonadi::Item &msg = Akonadi::Item());
653 
654 private:
655     Q_REQUIRED_RESULT Result execute() override;
656 };
657 
658 class KMAILTESTS_TESTS_EXPORT KMShareImageCommand : public KMCommand
659 {
660     Q_OBJECT
661 
662 public:
663     explicit KMShareImageCommand(const QUrl &url, QWidget *parent);
664 
665 private:
666     Q_REQUIRED_RESULT Result execute() override;
667     QUrl mUrl;
668 };
669 
670 class KMAILTESTS_TESTS_EXPORT KMFetchMessageCommand : public KMCommand
671 {
672     Q_OBJECT
673 public:
674     explicit KMFetchMessageCommand(QWidget *parent, const Akonadi::Item &item, MessageViewer::Viewer *viewer, KMReaderMainWin *win = nullptr);
675 
676     Q_REQUIRED_RESULT Akonadi::Item item() const;
677 
678     KMReaderMainWin *readerMainWin() const;
679 
680 private:
681     Akonadi::ItemFetchJob *createFetchJob(const Akonadi::Item::List &items) override;
682     Q_REQUIRED_RESULT Result execute() override;
683 
684     Akonadi::Item mItem;
685     MessageViewer::Viewer *mViewer = nullptr;
686     KMReaderMainWin *mReaderMainWin = nullptr;
687 };
688 
689