1 /*
2 SPDX-FileCopyrightText: 2013 Daniel Vrátil <dvratil@redhat.com>
3
4 SPDX-License-Identifier: LGPL-2.0-or-later
5 */
6
7 #include "changeitemsflagstask.h"
8
9 #include "imapresource_debug.h"
10 #include <KIMAP/SelectJob>
11 #include <KIMAP/Session>
12 #include <KIMAP/StoreJob>
13
ChangeItemsFlagsTask(const ResourceStateInterface::Ptr & resource,QObject * parent)14 ChangeItemsFlagsTask::ChangeItemsFlagsTask(const ResourceStateInterface::Ptr &resource, QObject *parent)
15 : ResourceTask(ResourceTask::DeferIfNoSession, resource, parent)
16 {
17 }
18
~ChangeItemsFlagsTask()19 ChangeItemsFlagsTask::~ChangeItemsFlagsTask()
20 {
21 }
22
doStart(KIMAP::Session * session)23 void ChangeItemsFlagsTask::doStart(KIMAP::Session *session)
24 {
25 const QString mailBox = mailBoxForCollection(items().at(0).parentCollection());
26 qCDebug(IMAPRESOURCE_LOG) << mailBox;
27
28 if (session->selectedMailBox() != mailBox) {
29 auto select = new KIMAP::SelectJob(session);
30 select->setMailBox(mailBox);
31
32 connect(select, &KJob::result, this, &ChangeItemsFlagsTask::onSelectDone);
33
34 select->start();
35 } else {
36 if (!addedFlags().isEmpty()) {
37 triggerAppendFlagsJob(session);
38 } else if (!removedFlags().isEmpty()) {
39 triggerRemoveFlagsJob(session);
40 } else {
41 qCDebug(IMAPRESOURCE_LOG) << "nothing to do";
42 changeProcessed();
43 }
44 }
45 }
46
onSelectDone(KJob * job)47 void ChangeItemsFlagsTask::onSelectDone(KJob *job)
48 {
49 if (job->error()) {
50 qCWarning(IMAPRESOURCE_LOG) << "Select failed: " << job->errorString();
51 cancelTask(job->errorString());
52 } else {
53 auto select = static_cast<KIMAP::SelectJob *>(job);
54 qCDebug(IMAPRESOURCE_LOG) << addedFlags();
55 if (!addedFlags().isEmpty()) {
56 triggerAppendFlagsJob(select->session());
57 } else if (!removedFlags().isEmpty()) {
58 triggerRemoveFlagsJob(select->session());
59 } else {
60 qCDebug(IMAPRESOURCE_LOG) << "nothing to do";
61 changeProcessed();
62 }
63 }
64 }
65
prepareJob(KIMAP::Session * session)66 KIMAP::StoreJob *ChangeItemsFlagsTask::prepareJob(KIMAP::Session *session)
67 {
68 KIMAP::ImapSet set;
69 const Akonadi::Item::List &allItems = items();
70 // Split the request to multiple smaller requests of 2000 UIDs each - various IMAP
71 // servers have various limits on maximum size of a request
72 // 2000 is a random number that sounds like a good compromise between performance
73 // and functionality (i.e. 2000 UIDs should be supported by any server out there)
74 for (int i = 0, count = qMin(2000, allItems.count() - m_processedItems); i < count; ++i) {
75 set.add(allItems[m_processedItems].remoteId().toLong());
76 ++m_processedItems;
77 }
78
79 auto store = new KIMAP::StoreJob(session);
80 store->setUidBased(true);
81 store->setSequenceSet(set);
82
83 return store;
84 }
85
triggerAppendFlagsJob(KIMAP::Session * session)86 void ChangeItemsFlagsTask::triggerAppendFlagsJob(KIMAP::Session *session)
87 {
88 const auto supportedFlags = fromAkonadiToSupportedImapFlags(addedFlags().values(), items().at(0).parentCollection());
89 if (supportedFlags.isEmpty()) {
90 if (!removedFlags().isEmpty()) {
91 m_processedItems = 0;
92 triggerRemoveFlagsJob(session);
93 } else {
94 changeProcessed();
95 }
96 } else {
97 KIMAP::StoreJob *store = prepareJob(session);
98 store->setFlags(supportedFlags);
99 store->setMode(KIMAP::StoreJob::AppendFlags);
100 connect(store, &KIMAP::StoreJob::result, this, &ChangeItemsFlagsTask::onAppendFlagsDone);
101 store->start();
102 }
103 }
104
triggerRemoveFlagsJob(KIMAP::Session * session)105 void ChangeItemsFlagsTask::triggerRemoveFlagsJob(KIMAP::Session *session)
106 {
107 const auto supportedFlags = fromAkonadiToSupportedImapFlags(removedFlags().values(), items().at(0).parentCollection());
108 if (supportedFlags.isEmpty()) {
109 changeProcessed();
110 } else {
111 KIMAP::StoreJob *store = prepareJob(session);
112 store->setFlags(supportedFlags);
113 store->setMode(KIMAP::StoreJob::RemoveFlags);
114 connect(store, &KIMAP::StoreJob::result, this, &ChangeItemsFlagsTask::onRemoveFlagsDone);
115 store->start();
116 }
117 }
118
onAppendFlagsDone(KJob * job)119 void ChangeItemsFlagsTask::onAppendFlagsDone(KJob *job)
120 {
121 if (job->error()) {
122 qCWarning(IMAPRESOURCE_LOG) << "Flag append failed: " << job->errorString();
123 cancelTask(job->errorString());
124 } else {
125 KIMAP::Session *session = qobject_cast<KIMAP::Job *>(job)->session();
126 if (m_processedItems < items().count()) {
127 triggerAppendFlagsJob(session);
128 } else if (removedFlags().isEmpty()) {
129 changeProcessed();
130 } else {
131 qCDebug(IMAPRESOURCE_LOG) << removedFlags();
132 m_processedItems = 0;
133 triggerRemoveFlagsJob(session);
134 }
135 }
136 }
137
onRemoveFlagsDone(KJob * job)138 void ChangeItemsFlagsTask::onRemoveFlagsDone(KJob *job)
139 {
140 if (job->error()) {
141 qCWarning(IMAPRESOURCE_LOG) << "Flag remove failed: " << job->errorString();
142 cancelTask(job->errorString());
143 } else {
144 if (m_processedItems < items().count()) {
145 triggerRemoveFlagsJob(qobject_cast<KIMAP::Job *>(job)->session());
146 } else {
147 changeProcessed();
148 }
149 }
150 }
151