File indexing completed on 2024-11-24 04:44:18

0001 /*
0002     SPDX-FileCopyrightText: 2014 Christian Mollekopf <mollekopf@kolabsys.com>
0003 
0004     SPDX-License-Identifier: LGPL-2.0-or-later
0005 */
0006 
0007 #include "kolabretrievetagstask.h"
0008 #include "kolabresource_debug.h"
0009 #include "kolabresource_trace.h"
0010 #include "tagchangehelper.h"
0011 
0012 #include "imapflags.h"
0013 #include "pimkolab/kolabformat/kolabobject.h"
0014 #include <KIMAP/FetchJob>
0015 #include <KIMAP/SelectJob>
0016 
0017 KolabRetrieveTagTask::KolabRetrieveTagTask(const ResourceStateInterface::Ptr &resource, RetrieveType type, QObject *parent)
0018     : KolabRelationResourceTask(resource, parent)
0019     , mRetrieveType(type)
0020 {
0021 }
0022 
0023 void KolabRetrieveTagTask::startRelationTask(KIMAP::Session *session)
0024 {
0025     mSession = session;
0026     const QString mailBox = mailBoxForCollection(relationCollection());
0027 
0028     auto select = new KIMAP::SelectJob(session);
0029     select->setMailBox(mailBox);
0030     connect(select, &KJob::result, this, &KolabRetrieveTagTask::onFinalSelectDone);
0031     select->start();
0032 }
0033 
0034 void KolabRetrieveTagTask::onFinalSelectDone(KJob *job)
0035 {
0036     if (job->error()) {
0037         qCWarning(KOLABRESOURCE_LOG) << job->errorString();
0038         cancelTask(job->errorString());
0039         return;
0040     }
0041 
0042     auto select = static_cast<KIMAP::SelectJob *>(job);
0043     auto fetch = new KIMAP::FetchJob(select->session());
0044 
0045     if (select->messageCount() == 0) {
0046         taskComplete();
0047         return;
0048     }
0049 
0050     KIMAP::ImapSet set;
0051     set.add(KIMAP::ImapInterval(1, 0));
0052     fetch->setSequenceSet(set);
0053     fetch->setUidBased(false);
0054 
0055     KIMAP::FetchJob::FetchScope scope;
0056     scope.parts.clear();
0057     scope.mode = KIMAP::FetchJob::FetchScope::Full;
0058     fetch->setScope(scope);
0059     connect(fetch, &KIMAP::FetchJob::messagesAvailable, this, &KolabRetrieveTagTask::onMessagesAvailable);
0060     connect(fetch, &KJob::result, this, &KolabRetrieveTagTask::onHeadersFetchDone);
0061     fetch->start();
0062 }
0063 
0064 void KolabRetrieveTagTask::onMessagesAvailable(const QMap<qint64, KIMAP::Message> &messages)
0065 {
0066     auto fetch = static_cast<KIMAP::FetchJob *>(sender());
0067     Q_ASSERT(fetch);
0068 
0069     for (auto it = messages.cbegin(), end = messages.cend(); it != end; ++it) {
0070         if (it->flags.contains(ImapFlags::Deleted)) {
0071             continue;
0072         }
0073         const KMime::Message::Ptr msg = it->message;
0074         const Kolab::KolabObjectReader reader(msg);
0075         switch (reader.getType()) {
0076         case Kolab::RelationConfigurationObject:
0077             if (mRetrieveType == RetrieveTags && reader.isTag()) {
0078                 extractTag(reader, it->uid);
0079             } else if (mRetrieveType == RetrieveRelations && reader.isRelation()) {
0080                 extractRelation(reader, it->uid);
0081             }
0082             break;
0083 
0084         default:
0085             break;
0086         }
0087     }
0088 }
0089 
0090 Akonadi::Item KolabRetrieveTagTask::extractMember(const Kolab::RelationMember &member)
0091 {
0092     // TODO should we create a dummy item if it isn't yet available?
0093     Akonadi::Item i;
0094     if (!member.gid.isEmpty()) {
0095         // Reference by GID
0096         i.setGid(member.gid);
0097     } else {
0098         // Reference by imap uid
0099         if (member.uid < 0) {
0100             return {};
0101         }
0102         i.setRemoteId(QString::number(member.uid));
0103         qCDebug(KOLABRESOURCE_LOG) << "got member: " << member.uid << member.mailbox;
0104         Akonadi::Collection parent;
0105         {
0106             // The root collection is not part of the mailbox path
0107             Akonadi::Collection col;
0108             col.setRemoteId(rootRemoteId());
0109             col.setParentCollection(Akonadi::Collection::root());
0110             parent = col;
0111         }
0112         for (const QByteArray &part : std::as_const(member.mailbox)) {
0113             Akonadi::Collection col;
0114             col.setRemoteId(separatorCharacter() + QString::fromLatin1(part));
0115             col.setParentCollection(parent);
0116             parent = col;
0117         }
0118         i.setParentCollection(parent);
0119     }
0120     return i;
0121 }
0122 
0123 void KolabRetrieveTagTask::extractTag(const Kolab::KolabObjectReader &reader, qint64 remoteUid)
0124 {
0125     Akonadi::Tag tag = reader.getTag();
0126     tag.setRemoteId(QByteArray::number(remoteUid));
0127     mTags << tag;
0128 
0129     qCDebug(KOLABRESOURCE_TRACE) << "Extracted tag: " << tag.gid() << " remoteId: " << remoteUid << tag.remoteId();
0130 
0131     Akonadi::Item::List members;
0132     const QStringList lstMemberUrl = reader.getTagMembers();
0133     for (const QString &memberUrl : lstMemberUrl) {
0134         Kolab::RelationMember member = Kolab::parseMemberUrl(memberUrl);
0135         const Akonadi::Item i = extractMember(member);
0136         // TODO implement fallback to search if uid is not available
0137         if (!i.remoteId().isEmpty() || !i.gid().isEmpty()) {
0138             members << i;
0139         } else {
0140             qCWarning(KOLABRESOURCE_LOG) << "Failed to parse member: " << memberUrl;
0141         }
0142     }
0143     mTagMembers.insert(QString::fromLatin1(tag.remoteId()), members);
0144 }
0145 
0146 void KolabRetrieveTagTask::extractRelation(const Kolab::KolabObjectReader &reader, qint64 remoteUid)
0147 {
0148     Akonadi::Item::List members;
0149     const QStringList lstMemberUrl = reader.getTagMembers();
0150     for (const QString &memberUrl : lstMemberUrl) {
0151         Kolab::RelationMember member = Kolab::parseMemberUrl(memberUrl);
0152         const Akonadi::Item i = extractMember(member);
0153         // TODO implement fallback to search if uid is not available
0154         if (!i.remoteId().isEmpty() || !i.gid().isEmpty()) {
0155             members << i;
0156         } else {
0157             qCWarning(KOLABRESOURCE_LOG) << "Failed to parse member: " << memberUrl;
0158         }
0159     }
0160     if (members.size() != 2) {
0161         qCWarning(KOLABRESOURCE_LOG) << "Wrong numbers of members for a relation, expected 2: " << members.size();
0162         return;
0163     }
0164 
0165     Akonadi::Relation relation = reader.getRelation();
0166     relation.setType(Akonadi::Relation::GENERIC);
0167     relation.setRemoteId(QByteArray::number(remoteUid));
0168     relation.setLeft(members.at(0));
0169     relation.setRight(members.at(1));
0170     mRelations << relation;
0171 }
0172 
0173 void KolabRetrieveTagTask::onHeadersFetchDone(KJob *job)
0174 {
0175     if (job->error()) {
0176         qCWarning(KOLABRESOURCE_LOG) << "Fetch job failed " << job->errorString();
0177         cancelTask(job->errorString());
0178         return;
0179     }
0180 
0181     taskComplete();
0182 }
0183 
0184 void KolabRetrieveTagTask::taskComplete()
0185 {
0186     if (mRetrieveType == RetrieveTags) {
0187         qCDebug(KOLABRESOURCE_LOG) << "Fetched tags: " << mTags.size() << mTagMembers.size();
0188         resourceState()->tagsRetrieved(mTags, mTagMembers);
0189     } else if (mRetrieveType == RetrieveRelations) {
0190         qCDebug(KOLABRESOURCE_LOG) << "Fetched relations:" << mRelations.size();
0191         resourceState()->relationsRetrieved(mRelations);
0192     }
0193 
0194     deleteLater();
0195 }
0196 
0197 #include "moc_kolabretrievetagstask.cpp"