1 /*
2 SPDX-FileCopyrightText: 2014 Christian Mollekopf <mollekopf@kolabsys.com>
3
4 SPDX-License-Identifier: LGPL-2.0-or-later
5 */
6
7 #include "kolabretrievetagstask.h"
8 #include "kolabresource_debug.h"
9 #include "kolabresource_trace.h"
10 #include "tagchangehelper.h"
11
12 #include "pimkolab/kolabformat/kolabobject.h"
13 #include <KIMAP/FetchJob>
14 #include <KIMAP/SelectJob>
15 #include <imapflags.h>
16
KolabRetrieveTagTask(const ResourceStateInterface::Ptr & resource,RetrieveType type,QObject * parent)17 KolabRetrieveTagTask::KolabRetrieveTagTask(const ResourceStateInterface::Ptr &resource, RetrieveType type, QObject *parent)
18 : KolabRelationResourceTask(resource, parent)
19 , mRetrieveType(type)
20 {
21 }
22
startRelationTask(KIMAP::Session * session)23 void KolabRetrieveTagTask::startRelationTask(KIMAP::Session *session)
24 {
25 mSession = session;
26 const QString mailBox = mailBoxForCollection(relationCollection());
27
28 auto select = new KIMAP::SelectJob(session);
29 select->setMailBox(mailBox);
30 connect(select, &KJob::result, this, &KolabRetrieveTagTask::onFinalSelectDone);
31 select->start();
32 }
33
onFinalSelectDone(KJob * job)34 void KolabRetrieveTagTask::onFinalSelectDone(KJob *job)
35 {
36 if (job->error()) {
37 qCWarning(KOLABRESOURCE_LOG) << job->errorString();
38 cancelTask(job->errorString());
39 return;
40 }
41
42 auto select = static_cast<KIMAP::SelectJob *>(job);
43 auto fetch = new KIMAP::FetchJob(select->session());
44
45 if (select->messageCount() == 0) {
46 taskComplete();
47 return;
48 }
49
50 KIMAP::ImapSet set;
51 set.add(KIMAP::ImapInterval(1, 0));
52 fetch->setSequenceSet(set);
53 fetch->setUidBased(false);
54
55 KIMAP::FetchJob::FetchScope scope;
56 scope.parts.clear();
57 scope.mode = KIMAP::FetchJob::FetchScope::Full;
58 fetch->setScope(scope);
59 connect(fetch, &KIMAP::FetchJob::messagesAvailable, this, &KolabRetrieveTagTask::onMessagesAvailable);
60 connect(fetch, &KJob::result, this, &KolabRetrieveTagTask::onHeadersFetchDone);
61 fetch->start();
62 }
63
onMessagesAvailable(const QMap<qint64,KIMAP::Message> & messages)64 void KolabRetrieveTagTask::onMessagesAvailable(const QMap<qint64, KIMAP::Message> &messages)
65 {
66 auto fetch = static_cast<KIMAP::FetchJob *>(sender());
67 Q_ASSERT(fetch);
68
69 for (auto it = messages.cbegin(), end = messages.cend(); it != end; ++it) {
70 if (it->flags.contains(ImapFlags::Deleted)) {
71 continue;
72 }
73 const KMime::Message::Ptr msg = it->message;
74 const Kolab::KolabObjectReader reader(msg);
75 switch (reader.getType()) {
76 case Kolab::RelationConfigurationObject:
77 if (mRetrieveType == RetrieveTags && reader.isTag()) {
78 extractTag(reader, it->uid);
79 } else if (mRetrieveType == RetrieveRelations && reader.isRelation()) {
80 extractRelation(reader, it->uid);
81 }
82 break;
83
84 default:
85 break;
86 }
87 }
88 }
89
extractMember(const Kolab::RelationMember & member)90 Akonadi::Item KolabRetrieveTagTask::extractMember(const Kolab::RelationMember &member)
91 {
92 // TODO should we create a dummy item if it isn't yet available?
93 Akonadi::Item i;
94 if (!member.gid.isEmpty()) {
95 // Reference by GID
96 i.setGid(member.gid);
97 } else {
98 // Reference by imap uid
99 if (member.uid < 0) {
100 return Akonadi::Item();
101 }
102 i.setRemoteId(QString::number(member.uid));
103 qCDebug(KOLABRESOURCE_LOG) << "got member: " << member.uid << member.mailbox;
104 Akonadi::Collection parent;
105 {
106 // The root collection is not part of the mailbox path
107 Akonadi::Collection col;
108 col.setRemoteId(rootRemoteId());
109 col.setParentCollection(Akonadi::Collection::root());
110 parent = col;
111 }
112 for (const QByteArray &part : std::as_const(member.mailbox)) {
113 Akonadi::Collection col;
114 col.setRemoteId(separatorCharacter() + QString::fromLatin1(part));
115 col.setParentCollection(parent);
116 parent = col;
117 }
118 i.setParentCollection(parent);
119 }
120 return i;
121 }
122
extractTag(const Kolab::KolabObjectReader & reader,qint64 remoteUid)123 void KolabRetrieveTagTask::extractTag(const Kolab::KolabObjectReader &reader, qint64 remoteUid)
124 {
125 Akonadi::Tag tag = reader.getTag();
126 tag.setRemoteId(QByteArray::number(remoteUid));
127 mTags << tag;
128
129 qCDebug(KOLABRESOURCE_TRACE) << "Extracted tag: " << tag.gid() << " remoteId: " << remoteUid << tag.remoteId();
130
131 Akonadi::Item::List members;
132 const QStringList lstMemberUrl = reader.getTagMembers();
133 for (const QString &memberUrl : lstMemberUrl) {
134 Kolab::RelationMember member = Kolab::parseMemberUrl(memberUrl);
135 const Akonadi::Item i = extractMember(member);
136 // TODO implement fallback to search if uid is not available
137 if (!i.remoteId().isEmpty() || !i.gid().isEmpty()) {
138 members << i;
139 } else {
140 qCWarning(KOLABRESOURCE_LOG) << "Failed to parse member: " << memberUrl;
141 }
142 }
143 mTagMembers.insert(QString::fromLatin1(tag.remoteId()), members);
144 }
145
extractRelation(const Kolab::KolabObjectReader & reader,qint64 remoteUid)146 void KolabRetrieveTagTask::extractRelation(const Kolab::KolabObjectReader &reader, qint64 remoteUid)
147 {
148 Akonadi::Item::List members;
149 const QStringList lstMemberUrl = reader.getTagMembers();
150 for (const QString &memberUrl : lstMemberUrl) {
151 Kolab::RelationMember member = Kolab::parseMemberUrl(memberUrl);
152 const Akonadi::Item i = extractMember(member);
153 // TODO implement fallback to search if uid is not available
154 if (!i.remoteId().isEmpty() || !i.gid().isEmpty()) {
155 members << i;
156 } else {
157 qCWarning(KOLABRESOURCE_LOG) << "Failed to parse member: " << memberUrl;
158 }
159 }
160 if (members.size() != 2) {
161 qCWarning(KOLABRESOURCE_LOG) << "Wrong numbers of members for a relation, expected 2: " << members.size();
162 return;
163 }
164
165 Akonadi::Relation relation = reader.getRelation();
166 relation.setType(Akonadi::Relation::GENERIC);
167 relation.setRemoteId(QByteArray::number(remoteUid));
168 relation.setLeft(members.at(0));
169 relation.setRight(members.at(1));
170 mRelations << relation;
171 }
172
onHeadersFetchDone(KJob * job)173 void KolabRetrieveTagTask::onHeadersFetchDone(KJob *job)
174 {
175 if (job->error()) {
176 qCWarning(KOLABRESOURCE_LOG) << "Fetch job failed " << job->errorString();
177 cancelTask(job->errorString());
178 return;
179 }
180
181 taskComplete();
182 }
183
taskComplete()184 void KolabRetrieveTagTask::taskComplete()
185 {
186 if (mRetrieveType == RetrieveTags) {
187 qCDebug(KOLABRESOURCE_LOG) << "Fetched tags: " << mTags.size() << mTagMembers.size();
188 resourceState()->tagsRetrieved(mTags, mTagMembers);
189 } else if (mRetrieveType == RetrieveRelations) {
190 qCDebug(KOLABRESOURCE_LOG) << "Fetched relations:" << mRelations.size();
191 resourceState()->relationsRetrieved(mRelations);
192 }
193
194 deleteLater();
195 }
196