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