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"