File indexing completed on 2024-11-24 04:44:17
0001 /* 0002 SPDX-FileCopyrightText: 2014 Klarälvdalens Datakonsult AB, a KDAB Group company <info@kdab.com> 0003 SPDX-FileContributor: Kevin Krammer <kevin.krammer@kdab.com> 0004 0005 SPDX-License-Identifier: LGPL-2.0-or-later 0006 */ 0007 0008 #include "kolabchangeitemsrelationstask.h" 0009 #include "imapflags.h" 0010 #include "kolabresource_debug.h" 0011 #include "kolabresource_trace.h" 0012 #include "pimkolab/kolabformat/kolabobject.h" 0013 0014 #include <KIMAP/AppendJob> 0015 #include <KIMAP/ImapSet> 0016 #include <KIMAP/SelectJob> 0017 #include <KIMAP/Session> 0018 #include <KIMAP/StoreJob> 0019 0020 #include <Akonadi/ItemFetchJob> 0021 #include <Akonadi/ItemFetchScope> 0022 #include <Akonadi/RelationFetchJob> 0023 0024 #include "kolabhelpers.h" 0025 0026 KolabChangeItemsRelationsTask::KolabChangeItemsRelationsTask(const ResourceStateInterface::Ptr &resource, QObject *parent) 0027 : KolabRelationResourceTask(resource, parent) 0028 { 0029 } 0030 0031 void KolabChangeItemsRelationsTask::startRelationTask(KIMAP::Session *session) 0032 { 0033 qCDebug(KOLABRESOURCE_TRACE); 0034 mSession = session; 0035 mAddedRelations = resourceState()->addedRelations(); 0036 mRemovedRelations = resourceState()->removedRelations(); 0037 0038 processNextRelation(); 0039 } 0040 0041 void KolabChangeItemsRelationsTask::processNextRelation() 0042 { 0043 qCDebug(KOLABRESOURCE_TRACE) << mAddedRelations.size() << mRemovedRelations.size(); 0044 Akonadi::Relation relation; 0045 if (!mAddedRelations.isEmpty()) { 0046 relation = mAddedRelations.takeFirst(); 0047 mAdding = true; 0048 } else if (!mRemovedRelations.isEmpty()) { 0049 relation = mRemovedRelations.takeFirst(); 0050 mAdding = false; 0051 } else { 0052 qCDebug(KOLABRESOURCE_TRACE) << "Processing done"; 0053 changeProcessed(); 0054 return; 0055 } 0056 qCDebug(KOLABRESOURCE_TRACE) << "Processing " << (mAdding ? " add " : " remove ") << relation; 0057 0058 // We have to fetch it again in case it changed since the notification was emitted (which is likely) 0059 // Otherwise we get an empty remoteid for new tags that were immediately applied on an item 0060 auto fetch = new Akonadi::RelationFetchJob(relation); 0061 connect(fetch, &KJob::result, this, &KolabChangeItemsRelationsTask::onRelationFetchDone); 0062 } 0063 0064 void KolabChangeItemsRelationsTask::onRelationFetchDone(KJob *job) 0065 { 0066 qCDebug(KOLABRESOURCE_TRACE); 0067 if (job->error()) { 0068 qCWarning(KOLABRESOURCE_LOG) << "RelatonFetch failed: " << job->errorString(); 0069 processNextRelation(); 0070 return; 0071 } 0072 0073 const Akonadi::Relation::List relations = static_cast<Akonadi::RelationFetchJob *>(job)->relations(); 0074 if (relations.size() != 1) { 0075 qCWarning(KOLABRESOURCE_LOG) << "Invalid number of relations retrieved: " << relations.size(); 0076 processNextRelation(); 0077 return; 0078 } 0079 0080 Akonadi::Relation relation = relations.first(); 0081 if (mAdding) { 0082 addRelation(relation); 0083 } else { 0084 removeRelation(relation); 0085 } 0086 } 0087 0088 void KolabChangeItemsRelationsTask::addRelation(const Akonadi::Relation &relation) 0089 { 0090 auto fetchJob = new Akonadi::ItemFetchJob(Akonadi::Item::List() << relation.left() << relation.right()); 0091 fetchJob->fetchScope().setCacheOnly(true); 0092 fetchJob->fetchScope().setAncestorRetrieval(Akonadi::ItemFetchScope::All); 0093 fetchJob->fetchScope().setFetchGid(true); 0094 fetchJob->fetchScope().fetchFullPayload(true); 0095 fetchJob->setProperty("relation", QVariant::fromValue(relation)); 0096 connect(fetchJob, &KJob::result, this, &KolabChangeItemsRelationsTask::onItemsFetched); 0097 } 0098 0099 void KolabChangeItemsRelationsTask::onItemsFetched(KJob *job) 0100 { 0101 if (job->error()) { 0102 qCWarning(KOLABRESOURCE_LOG) << "Failed to fetch items for relation: " << job->errorString(); 0103 processNextRelation(); 0104 return; 0105 } 0106 auto fetchJob = static_cast<Akonadi::ItemFetchJob *>(job); 0107 if (fetchJob->items().size() != 2) { 0108 qCWarning(KOLABRESOURCE_LOG) << "Invalid number of items retrieved: " << fetchJob->items().size(); 0109 processNextRelation(); 0110 return; 0111 } 0112 0113 const Akonadi::Item::List items = fetchJob->items(); 0114 const auto relation = job->property("relation").value<Akonadi::Relation>(); 0115 Akonadi::Item leftItem = items[0] == relation.left() ? items[0] : items[1]; 0116 Akonadi::Item rightItem = items[0] == relation.right() ? items[0] : items[1]; 0117 const QString left = KolabHelpers::createMemberUrl(leftItem, resourceState()->userName()); 0118 const QString right = KolabHelpers::createMemberUrl(rightItem, resourceState()->userName()); 0119 if (left.isEmpty() || right.isEmpty()) { 0120 qCWarning(KOLABRESOURCE_LOG) << "Failed to add relation, invalid member: " << left << " : " << right; 0121 processNextRelation(); 0122 return; 0123 } 0124 QStringList members; 0125 members.reserve(2); 0126 members << left << right; 0127 0128 const QLatin1StringView productId("Akonadi-Kolab-Resource"); 0129 const KMime::Message::Ptr message = Kolab::KolabObjectWriter::writeRelation(relation, members, Kolab::KolabV3, productId); 0130 0131 auto appendJob = new KIMAP::AppendJob(mSession); 0132 appendJob->setMailBox(mailBoxForCollection(relationCollection())); 0133 appendJob->setContent(message->encodedContent(true)); 0134 appendJob->setInternalDate(message->date()->dateTime()); 0135 connect(appendJob, &KJob::result, this, &KolabChangeItemsRelationsTask::onChangeCommitted); 0136 appendJob->start(); 0137 } 0138 0139 void KolabChangeItemsRelationsTask::removeRelation(const Akonadi::Relation &relation) 0140 { 0141 qCDebug(KOLABRESOURCE_TRACE); 0142 mCurrentRelation = relation; 0143 const QString mailBox = mailBoxForCollection(relationCollection()); 0144 0145 if (mSession->selectedMailBox() != mailBox) { 0146 auto select = new KIMAP::SelectJob(mSession); 0147 select->setMailBox(mailBox); 0148 0149 connect(select, &KJob::result, this, &KolabChangeItemsRelationsTask::onSelectDone); 0150 0151 select->start(); 0152 } else { 0153 triggerStoreJob(); 0154 } 0155 } 0156 0157 void KolabChangeItemsRelationsTask::onSelectDone(KJob *job) 0158 { 0159 if (job->error()) { 0160 qCWarning(KOLABRESOURCE_LOG) << "Failed to select mailbox: " << job->errorString(); 0161 cancelTask(job->errorString()); 0162 } else { 0163 triggerStoreJob(); 0164 } 0165 } 0166 0167 void KolabChangeItemsRelationsTask::triggerStoreJob() 0168 { 0169 KIMAP::ImapSet set; 0170 set.add(mCurrentRelation.remoteId().toLong()); 0171 0172 qCDebug(KOLABRESOURCE_TRACE) << set.toImapSequenceSet(); 0173 0174 auto store = new KIMAP::StoreJob(mSession); 0175 store->setUidBased(true); 0176 store->setSequenceSet(set); 0177 store->setFlags(QList<QByteArray>() << ImapFlags::Deleted); 0178 store->setMode(KIMAP::StoreJob::AppendFlags); 0179 connect(store, &KJob::result, this, &KolabChangeItemsRelationsTask::onChangeCommitted); 0180 store->start(); 0181 } 0182 0183 void KolabChangeItemsRelationsTask::onChangeCommitted(KJob *job) 0184 { 0185 if (job->error()) { 0186 qCWarning(KOLABRESOURCE_LOG) << "Error while storing change"; 0187 cancelTask(job->errorString()); 0188 } else { 0189 processNextRelation(); 0190 } 0191 } 0192 0193 #include "moc_kolabchangeitemsrelationstask.cpp"