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

0001 /*
0002     SPDX-FileCopyrightText: 2010 Klarälvdalens Datakonsult AB, a KDAB Group company <info@kdab.com>
0003     SPDX-FileContributor: Kevin Ottens <kevin@kdab.com>
0004 
0005     SPDX-FileCopyrightText: 2014 Christian Mollekopf <mollekopf@kolabsys.com>
0006 
0007     SPDX-License-Identifier: LGPL-2.0-or-later
0008 */
0009 
0010 #include "kolabretrievecollectionstask.h"
0011 #include "kolabhelpers.h"
0012 #include "kolabresource_debug.h"
0013 #include "kolabresource_trace.h"
0014 
0015 #include "collectionmetadatahelper.h"
0016 #include "imapaclattribute.h"
0017 #include "noinferiorsattribute.h"
0018 #include "noselectattribute.h"
0019 #include <Akonadi/CollectionAnnotationsAttribute>
0020 #include <KIMAP/GetMetaDataJob>
0021 #include <KIMAP/MyRightsJob>
0022 
0023 #include <Akonadi/MessageParts>
0024 
0025 #include <Akonadi/BlockAlarmsAttribute>
0026 #include <Akonadi/CachePolicy>
0027 #include <Akonadi/CollectionColorAttribute>
0028 #include <Akonadi/CollectionIdentificationAttribute>
0029 #include <Akonadi/EntityDisplayAttribute>
0030 #include <Akonadi/SpecialCollectionAttribute>
0031 #include <Akonadi/VectorHelper>
0032 
0033 #include <KMime/Message>
0034 
0035 #include <KLocalizedString>
0036 
0037 #include <QColor>
0038 
0039 static bool isNamespaceFolder(const QString &path, const QList<KIMAP::MailBoxDescriptor> &namespaces, bool matchCompletePath = false)
0040 {
0041     for (const KIMAP::MailBoxDescriptor &desc : namespaces) {
0042         if (path.startsWith(desc.name.left(desc.name.size() - 1))) { // Namespace ends with path separator and path doesn't
0043             if (!matchCompletePath || path.size() - desc.name.size() <= 1) { // We want to match only for the complete path
0044                 return true;
0045             }
0046         }
0047     }
0048     return false;
0049 }
0050 
0051 RetrieveMetadataJob::RetrieveMetadataJob(KIMAP::Session *session,
0052                                          const QStringList &mailboxes,
0053                                          const QStringList &serverCapabilities,
0054                                          const QSet<QByteArray> &requestedMetadata,
0055                                          const QString &separator,
0056                                          const QList<KIMAP::MailBoxDescriptor> &sharedNamespace,
0057                                          const QList<KIMAP::MailBoxDescriptor> &userNamespace,
0058                                          QObject *parent)
0059     : KJob(parent)
0060     , mRequestedMetadata(requestedMetadata)
0061     , mServerCapabilities(serverCapabilities)
0062     , mMailboxes(mailboxes)
0063     , mSession(session)
0064     , mSeparator(separator)
0065     , mSharedNamespace(sharedNamespace)
0066     , mUserNamespace(userNamespace)
0067 {
0068 }
0069 
0070 void RetrieveMetadataJob::start()
0071 {
0072     qCDebug(KOLABRESOURCE_TRACE);
0073     // Fill the map with empty entries so we set the mimetype to mail if no metadata is retrieved
0074     for (const QString &mailbox : std::as_const(mMailboxes)) {
0075         mMetadata.insert(mailbox, QMap<QByteArray, QByteArray>());
0076     }
0077 
0078     if (mServerCapabilities.contains(QLatin1StringView("METADATA")) || mServerCapabilities.contains(QLatin1StringView("ANNOTATEMORE"))) {
0079         QSet<QString> toplevelMailboxes;
0080         for (const QString &mailbox : std::as_const(mMailboxes)) {
0081             const QStringList parts = mailbox.split(mSeparator);
0082             if (!parts.isEmpty()) {
0083                 if (isNamespaceFolder(mailbox, mUserNamespace) && parts.length() >= 2) {
0084                     // Other Users can be too big to request with a single command so we request Other Users/<user>/*
0085                     toplevelMailboxes << parts.at(0) + mSeparator + parts.at(1) + mSeparator;
0086                 } else if (!isNamespaceFolder(mailbox, mSharedNamespace)) {
0087                     toplevelMailboxes << parts.first();
0088                 }
0089             }
0090         }
0091         for (const KIMAP::MailBoxDescriptor &desc : std::as_const(mSharedNamespace)) {
0092             toplevelMailboxes << desc.name;
0093         }
0094         // TODO perhaps exclude the shared and other users namespaces by listing only toplevel (with %), and then only getting metadata of the toplevel folders.
0095         for (const QString &mailbox : std::as_const(toplevelMailboxes)) {
0096             auto meta = new KIMAP::GetMetaDataJob(mSession);
0097             meta->setMailBox(mailbox + QLatin1StringView("*"));
0098             if (mServerCapabilities.contains(QLatin1StringView("METADATA"))) {
0099                 meta->setServerCapability(KIMAP::MetaDataJobBase::Metadata);
0100             } else {
0101                 meta->setServerCapability(KIMAP::MetaDataJobBase::Annotatemore);
0102             }
0103             meta->setDepth(KIMAP::GetMetaDataJob::AllLevels);
0104             for (const QByteArray &requestedEntry : std::as_const(mRequestedMetadata)) {
0105                 meta->addRequestedEntry(requestedEntry);
0106             }
0107             connect(meta, &KJob::result, this, &RetrieveMetadataJob::onGetMetaDataDone);
0108             mJobs++;
0109             meta->start();
0110         }
0111     }
0112 
0113     // Get the ACLs from the mailbox if it's supported
0114     if (mServerCapabilities.contains(QLatin1StringView("ACL"))) {
0115         for (const QString &mailbox : std::as_const(mMailboxes)) {
0116             // "Shared Folders" is not a valid mailbox, so we have to skip the ACL request for this folder
0117             if (isNamespaceFolder(mailbox, mSharedNamespace, true)) {
0118                 continue;
0119             }
0120             auto rights = new KIMAP::MyRightsJob(mSession);
0121             rights->setMailBox(mailbox);
0122             connect(rights, &KJob::result, this, &RetrieveMetadataJob::onRightsReceived);
0123             mJobs++;
0124             rights->start();
0125         }
0126     }
0127     checkDone();
0128 }
0129 
0130 void RetrieveMetadataJob::onGetMetaDataDone(KJob *job)
0131 {
0132     mJobs--;
0133     auto meta = static_cast<KIMAP::GetMetaDataJob *>(job);
0134     if (job->error()) {
0135         qCDebug(KOLABRESOURCE_LOG) << "No metadata for for mailbox: " << meta->mailBox();
0136         if (!isNamespaceFolder(meta->mailBox(), mSharedNamespace)) {
0137             qCWarning(KOLABRESOURCE_LOG) << "Get metadata failed: " << job->errorString();
0138             // We ignore the error to avoid failing the complete sync. We can run into this when trying to retrieve rights for non-existing mailboxes.
0139         }
0140         checkDone();
0141         return;
0142     }
0143 
0144     const QHash<QString, QMap<QByteArray, QByteArray>> metadata = meta->allMetaDataForMailboxes();
0145     const QStringList lstKeys = metadata.keys();
0146     for (const QString &folder : lstKeys) {
0147         mMetadata.insert(folder, metadata.value(folder));
0148     }
0149     checkDone();
0150 }
0151 
0152 void RetrieveMetadataJob::onRightsReceived(KJob *job)
0153 {
0154     mJobs--;
0155     auto rights = static_cast<KIMAP::MyRightsJob *>(job);
0156     if (job->error()) {
0157         qCDebug(KOLABRESOURCE_LOG) << "No rights for mailbox: " << rights->mailBox();
0158         if (!isNamespaceFolder(rights->mailBox(), mSharedNamespace)) {
0159             qCWarning(KOLABRESOURCE_LOG) << "MyRights for mailbox" << rights->mailBox() << "failed:" << job->errorString();
0160             // We ignore the error to avoid failing the complete sync. We can run into this when trying to retrieve rights for non-existing mailboxes.
0161         }
0162         checkDone();
0163         return;
0164     }
0165 
0166     const KIMAP::Acl::Rights imapRights = rights->rights();
0167     mRights.insert(rights->mailBox(), imapRights);
0168     checkDone();
0169 }
0170 
0171 void RetrieveMetadataJob::checkDone()
0172 {
0173     if (!mJobs) {
0174         qCDebug(KOLABRESOURCE_TRACE) << "done";
0175         qCDebug(KOLABRESOURCE_LOG) << "done";
0176         emitResult();
0177     }
0178 }
0179 
0180 KolabRetrieveCollectionsTask::KolabRetrieveCollectionsTask(const ResourceStateInterface::Ptr &resource, QObject *parent)
0181     : ResourceTask(CancelIfNoSession, resource, parent)
0182     , cContentMimeTypes("CONTENTMIMETYPES")
0183     , cAccessRights("AccessRights")
0184     , cImapAcl("imapacl")
0185     , cCollectionAnnotations("collectionannotations")
0186     , cDefaultKeepLocalChanges(QSet<QByteArray>() << cContentMimeTypes << cAccessRights << cImapAcl << cCollectionAnnotations)
0187     , cDefaultMimeTypes(QStringList() << Akonadi::Collection::mimeType() << QStringLiteral("application/x-kolab-objects"))
0188     , cCollectionOnlyContentMimeTypes(QStringList() << Akonadi::Collection::mimeType())
0189 {
0190     mRequestedMetadata << "/shared/vendor/kolab/folder-type";
0191     mRequestedMetadata << "/private/vendor/kolab/folder-type";
0192     mRequestedMetadata << "/shared" KOLAB_COLOR_ANNOTATION << "/private" KOLAB_COLOR_ANNOTATION;
0193 }
0194 
0195 KolabRetrieveCollectionsTask::~KolabRetrieveCollectionsTask() = default;
0196 
0197 void KolabRetrieveCollectionsTask::doStart(KIMAP::Session *session)
0198 {
0199     qCDebug(KOLABRESOURCE_LOG) << "Starting collection retrieval";
0200     mTime.start();
0201     mSession = session;
0202 
0203     Akonadi::Collection root;
0204     root.setName(resourceName());
0205     root.setRemoteId(rootRemoteId());
0206     root.setContentMimeTypes(QStringList(Akonadi::Collection::mimeType()));
0207     root.setParentCollection(Akonadi::Collection::root());
0208     root.addAttribute(new NoSelectAttribute(true));
0209     root.attribute<Akonadi::EntityDisplayAttribute>(Akonadi::Collection::AddIfMissing)->setIconName(QStringLiteral("kolab"));
0210 
0211     Akonadi::CachePolicy policy;
0212     policy.setInheritFromParent(false);
0213     policy.setSyncOnDemand(true);
0214 
0215     QStringList localParts;
0216     localParts << QLatin1StringView(Akonadi::MessagePart::Envelope) << QLatin1StringView(Akonadi::MessagePart::Header);
0217     int cacheTimeout = 60;
0218 
0219     if (isDisconnectedModeEnabled()) {
0220         // For disconnected mode we also cache the body
0221         // and we keep all data indefinitely
0222         localParts << QLatin1StringView(Akonadi::MessagePart::Body);
0223         cacheTimeout = -1;
0224     }
0225 
0226     policy.setLocalParts(localParts);
0227     policy.setCacheTimeout(cacheTimeout);
0228     policy.setIntervalCheckTime(intervalCheckTime());
0229 
0230     root.setCachePolicy(policy);
0231 
0232     mMailCollections.insert(QString(), root);
0233 
0234     qCDebug(KOLABRESOURCE_TRACE) << "subscription enabled: " << isSubscriptionEnabled();
0235     // jobs are serialized by the session
0236     if (isSubscriptionEnabled()) {
0237         auto fullListJob = new KIMAP::ListJob(session);
0238         fullListJob->setOption(KIMAP::ListJob::NoOption);
0239         fullListJob->setQueriedNamespaces(serverNamespaces());
0240         connect(fullListJob, &KIMAP::ListJob::mailBoxesReceived, this, &KolabRetrieveCollectionsTask::onFullMailBoxesReceived);
0241         connect(fullListJob, &KJob::result, this, &KolabRetrieveCollectionsTask::onFullMailBoxesReceiveDone);
0242         mJobs++;
0243         fullListJob->start();
0244     }
0245 
0246     auto listJob = new KIMAP::ListJob(session);
0247     listJob->setOption(KIMAP::ListJob::IncludeUnsubscribed);
0248     listJob->setQueriedNamespaces(serverNamespaces());
0249     connect(listJob, &KIMAP::ListJob::mailBoxesReceived, this, &KolabRetrieveCollectionsTask::onMailBoxesReceived);
0250     connect(listJob, &KJob::result, this, &KolabRetrieveCollectionsTask::onMailBoxesReceiveDone);
0251     mJobs++;
0252     listJob->start();
0253 }
0254 
0255 void KolabRetrieveCollectionsTask::onMailBoxesReceived(const QList<KIMAP::MailBoxDescriptor> &descriptors, const QList<QList<QByteArray>> &flags)
0256 {
0257     const int nbDescriptors = descriptors.size();
0258     for (int i = 0; i < nbDescriptors; ++i) {
0259         const KIMAP::MailBoxDescriptor descriptor = descriptors[i];
0260         createCollection(descriptor.name, flags.at(i), !isSubscriptionEnabled() || mSubscribedMailboxes.contains(descriptor.name));
0261     }
0262     checkDone();
0263 }
0264 
0265 Akonadi::Collection KolabRetrieveCollectionsTask::getOrCreateParent(const QString &path)
0266 {
0267     if (mMailCollections.contains(path)) {
0268         return mMailCollections.value(path);
0269     }
0270     // create a dummy collection
0271     const QString separator = separatorCharacter();
0272     const QStringList pathParts = path.split(separator);
0273     const QString pathPart = pathParts.last();
0274     Akonadi::Collection c;
0275     c.setName(pathPart);
0276     c.setRemoteId(separator + pathPart);
0277     const QStringList parentPath = pathParts.mid(0, pathParts.size() - 1);
0278     const Akonadi::Collection parentCollection = getOrCreateParent(parentPath.join(separator));
0279     c.setParentCollection(parentCollection);
0280 
0281     c.addAttribute(new NoSelectAttribute(true));
0282     c.setContentMimeTypes(QStringList() << Akonadi::Collection::mimeType());
0283     c.setRights(Akonadi::Collection::ReadOnly);
0284     c.setEnabled(false);
0285     setAttributes(c, pathParts, path);
0286 
0287     mMailCollections.insert(path, c);
0288     return c;
0289 }
0290 
0291 void KolabRetrieveCollectionsTask::setAttributes(Akonadi::Collection &c, const QStringList &pathParts, const QString &path)
0292 {
0293     auto attr = c.attribute<Akonadi::CollectionIdentificationAttribute>(Akonadi::Collection::AddIfMissing);
0294     attr->setIdentifier(path.toLatin1());
0295 
0296     // If the folder is a other users folder block all alarms from default
0297     if (isNamespaceFolder(path, resourceState()->userNamespaces())) {
0298         auto attr = c.attribute<Akonadi::BlockAlarmsAttribute>(Akonadi::Collection::AddIfMissing);
0299         attr->blockEverything(true);
0300     }
0301 
0302     // If the folder is a other users top-level folder mark it accordingly
0303     if (pathParts.size() == 1 && isNamespaceFolder(path, resourceState()->userNamespaces())) {
0304         auto attr = c.attribute<Akonadi::EntityDisplayAttribute>(Akonadi::Collection::AddIfMissing);
0305         attr->setDisplayName(i18n("Other Users"));
0306         attr->setIconName(QStringLiteral("x-mail-distribution-list"));
0307     }
0308 
0309     // Mark user folders for searching
0310     if (pathParts.size() >= 2 && isNamespaceFolder(path, resourceState()->userNamespaces())) {
0311         auto attr = c.attribute<Akonadi::CollectionIdentificationAttribute>(Akonadi::Collection::AddIfMissing);
0312         if (pathParts.size() == 2) {
0313             attr->setCollectionNamespace("usertoplevel");
0314         } else {
0315             attr->setCollectionNamespace("user");
0316         }
0317     }
0318 
0319     // If the folder is a shared folders top-level folder mark it accordingly
0320     if (pathParts.size() == 1 && isNamespaceFolder(path, resourceState()->sharedNamespaces())) {
0321         auto attr = c.attribute<Akonadi::EntityDisplayAttribute>(Akonadi::Collection::AddIfMissing);
0322         attr->setDisplayName(i18n("Shared Folders"));
0323         attr->setIconName(QStringLiteral("x-mail-distribution-list"));
0324     }
0325 
0326     // Mark shared folders for searching
0327     if (pathParts.size() >= 2 && isNamespaceFolder(path, resourceState()->sharedNamespaces())) {
0328         auto attr = c.attribute<Akonadi::CollectionIdentificationAttribute>(Akonadi::Collection::AddIfMissing);
0329         attr->setCollectionNamespace("shared");
0330     }
0331 }
0332 
0333 void KolabRetrieveCollectionsTask::createCollection(const QString &mailbox, const QList<QByteArray> &currentFlags, bool isSubscribed)
0334 {
0335     const QString separator = separatorCharacter();
0336     Q_ASSERT(separator.size() == 1);
0337     const QString boxName = mailbox.endsWith(separator) ? mailbox.left(mailbox.size() - 1) : mailbox;
0338     const QStringList pathParts = boxName.split(separator);
0339     const QString pathPart = pathParts.last();
0340 
0341     Akonadi::Collection c;
0342     // If we had a dummy collection we need to replace it
0343     if (mMailCollections.contains(mailbox)) {
0344         c = mMailCollections.value(mailbox);
0345     }
0346     c.setName(pathPart);
0347     c.setRemoteId(separator + pathPart);
0348     const QStringList parentPath = pathParts.mid(0, pathParts.size() - 1);
0349     const Akonadi::Collection parentCollection = getOrCreateParent(parentPath.join(separator));
0350     c.setParentCollection(parentCollection);
0351     // TODO get from ResourceState, and add KMime::Message::mimeType() for the normal imap resource by default
0352     // We add a dummy mimetype, otherwise the itemsync doesn't even work (action is disabled and resourcebase aborts the operation)
0353     c.setContentMimeTypes(cDefaultMimeTypes);
0354     c.setKeepLocalChanges(cDefaultKeepLocalChanges);
0355 
0356     // assume LRS, until myrights is executed
0357     if (serverCapabilities().contains(QLatin1StringView("ACL"))) {
0358         c.setRights(Akonadi::Collection::ReadOnly);
0359     } else {
0360         c.setRights(Akonadi::Collection::AllRights);
0361     }
0362 
0363     setAttributes(c, pathParts, mailbox);
0364 
0365     // If the folder is the Inbox, make some special settings.
0366     if (pathParts.size() == 1 && pathPart.compare(QLatin1StringView("inbox"), Qt::CaseInsensitive) == 0) {
0367         auto attr = c.attribute<Akonadi::EntityDisplayAttribute>(Akonadi::Collection::AddIfMissing);
0368         attr->setDisplayName(i18n("Inbox"));
0369         attr->setIconName(QStringLiteral("mail-folder-inbox"));
0370         c.attribute<Akonadi::SpecialCollectionAttribute>(Akonadi::Collection::AddIfMissing)->setCollectionType("inbox");
0371         setIdleCollection(c);
0372     }
0373 
0374     // If this folder is a noselect folder, make some special settings.
0375     if (currentFlags.contains("\\noselect")) {
0376         c.addAttribute(new NoSelectAttribute(true));
0377         c.setContentMimeTypes(cCollectionOnlyContentMimeTypes);
0378         c.setRights(Akonadi::Collection::ReadOnly);
0379     } else {
0380         // remove the noselect attribute explicitly, in case we had set it before (eg. for non-subscribed non-leaf folders)
0381         c.removeAttribute<NoSelectAttribute>();
0382     }
0383 
0384     // If this folder is a noinferiors folder, it is not allowed to create subfolders inside.
0385     if (currentFlags.contains("\\noinferiors")) {
0386         // qCDebug(KOLABRESOURCE_LOG) << "Noinferiors: " << currentPath;
0387         c.addAttribute(new NoInferiorsAttribute(true));
0388         c.setRights(c.rights() & ~Akonadi::Collection::CanCreateCollection);
0389     }
0390     c.setEnabled(isSubscribed);
0391 
0392     // qCDebug(KOLABRESOURCE_LOG) << "creating collection " << mailbox << " with parent " << parentPath;
0393     mMailCollections.insert(mailbox, c);
0394 }
0395 
0396 void KolabRetrieveCollectionsTask::onMailBoxesReceiveDone(KJob *job)
0397 {
0398     qCDebug(KOLABRESOURCE_LOG) << "All mailboxes received: " << mTime.elapsed();
0399     qCDebug(KOLABRESOURCE_LOG) << "in total: " << mMailCollections.size();
0400     mJobs--;
0401     if (job->error()) {
0402         qCWarning(KOLABRESOURCE_LOG) << QStringLiteral("Failed to retrieve mailboxes: ") + job->errorString();
0403         cancelTask(i18n("Collection retrieval failed"));
0404     } else {
0405         QSet<QString> mailboxes;
0406         const QStringList lstKeys = mMailCollections.keys();
0407         for (const QString &mailbox : lstKeys) {
0408             if (!mailbox.isEmpty() && !isNamespaceFolder(mailbox, resourceState()->userNamespaces() + resourceState()->sharedNamespaces())) {
0409                 mailboxes << mailbox;
0410             }
0411         }
0412 
0413         // Only request metadata for subscribed Other Users Folders
0414         const QStringList metadataMailboxes = mailboxes.unite(mSubscribedMailboxes).values();
0415 
0416         auto metadata = new RetrieveMetadataJob(mSession,
0417                                                 metadataMailboxes,
0418                                                 serverCapabilities(),
0419                                                 mRequestedMetadata,
0420                                                 separatorCharacter(),
0421                                                 resourceState()->sharedNamespaces(),
0422                                                 resourceState()->userNamespaces(),
0423                                                 this);
0424         connect(metadata, &KJob::result, this, &KolabRetrieveCollectionsTask::onMetadataRetrieved);
0425         mJobs++;
0426         metadata->start();
0427     }
0428 }
0429 
0430 void KolabRetrieveCollectionsTask::applyRights(const QHash<QString, KIMAP::Acl::Rights> &rights)
0431 {
0432     // qCDebug(KOLABRESOURCE_LOG) << rights;
0433     const QStringList lstKeys = rights.keys();
0434     for (const QString &mailbox : lstKeys) {
0435         if (mMailCollections.contains(mailbox)) {
0436             const KIMAP::Acl::Rights imapRights = rights.value(mailbox);
0437             QStringList parts = mailbox.split(separatorCharacter());
0438             parts.removeLast();
0439             QString parentMailbox = parts.join(separatorCharacter());
0440 
0441             KIMAP::Acl::Rights parentImapRights;
0442             // If the parent folder is not existing we can't rename
0443             if (!parentMailbox.isEmpty() && rights.contains(parentMailbox)) {
0444                 parentImapRights = rights.value(parentMailbox);
0445             }
0446             // qCDebug(KOLABRESOURCE_LOG) << mailbox << parentMailbox << imapRights << parentImapRights;
0447 
0448             Akonadi::Collection &collection = mMailCollections[mailbox];
0449             CollectionMetadataHelper::applyRights(collection, imapRights, parentImapRights);
0450 
0451             // Store the mailbox ACLs
0452             auto aclAttribute = collection.attribute<Akonadi::ImapAclAttribute>(Akonadi::Collection::AddIfMissing);
0453             const KIMAP::Acl::Rights oldRights = aclAttribute->myRights();
0454             if (oldRights != imapRights) {
0455                 aclAttribute->setMyRights(imapRights);
0456             }
0457         } else {
0458             qCWarning(KOLABRESOURCE_LOG) << "Can't find mailbox " << mailbox;
0459         }
0460     }
0461 }
0462 
0463 void KolabRetrieveCollectionsTask::applyMetadata(const QHash<QString, QMap<QByteArray, QByteArray>> &metadataMap)
0464 {
0465     // qCDebug(KOLABRESOURCE_LOG) << metadataMap;
0466     const auto keys{metadataMap.keys()};
0467     for (const QString &mailbox : keys) {
0468         const QMap<QByteArray, QByteArray> metadata = metadataMap.value(mailbox);
0469         if (mMailCollections.contains(mailbox)) {
0470             Akonadi::Collection &collection = mMailCollections[mailbox];
0471             // qCDebug(KOLABRESOURCE_LOG) << mailbox << metadata << type << folderType << KolabHelpers::getContentMimeTypes(folderType);
0472             collection.attribute<Akonadi::CollectionAnnotationsAttribute>(Akonadi::Collection::AddIfMissing)->setAnnotations(metadata);
0473             const QByteArray type = KolabHelpers::getFolderTypeAnnotation(metadata);
0474             const Kolab::FolderType folderType = KolabHelpers::folderTypeFromString(type);
0475             collection.setContentMimeTypes(KolabHelpers::getContentMimeTypes(folderType));
0476             const QColor color = KolabHelpers::getFolderColor(metadata);
0477             if (color.isValid()) {
0478                 collection.attribute<Akonadi::CollectionColorAttribute>(Akonadi::Collection::AddIfMissing)->setColor(color);
0479             }
0480             QSet<QByteArray> keepLocalChanges = collection.keepLocalChanges();
0481             keepLocalChanges.remove(cContentMimeTypes);
0482             collection.setKeepLocalChanges(keepLocalChanges);
0483         }
0484     }
0485 }
0486 
0487 void KolabRetrieveCollectionsTask::onMetadataRetrieved(KJob *job)
0488 {
0489     qCDebug(KOLABRESOURCE_LOG) << mTime.elapsed();
0490     mJobs--;
0491     if (job->error()) {
0492         qCWarning(KOLABRESOURCE_LOG) << "Error while retrieving metadata, aborting collection retrieval: " << job->errorString();
0493         cancelTask(i18n("Collection retrieval failed"));
0494     } else {
0495         auto metadata = static_cast<RetrieveMetadataJob *>(job);
0496         applyRights(metadata->mRights);
0497         applyMetadata(metadata->mMetadata);
0498         checkDone();
0499     }
0500 }
0501 
0502 void KolabRetrieveCollectionsTask::checkDone()
0503 {
0504     if (!mJobs) {
0505         collectionsRetrieved(Akonadi::valuesToVector(mMailCollections));
0506         qCDebug(KOLABRESOURCE_LOG) << "done " << mTime.elapsed();
0507     }
0508 }
0509 
0510 void KolabRetrieveCollectionsTask::onFullMailBoxesReceived(const QList<KIMAP::MailBoxDescriptor> &descriptors, const QList<QList<QByteArray>> &flags)
0511 {
0512     Q_UNUSED(flags)
0513     for (const KIMAP::MailBoxDescriptor &descriptor : descriptors) {
0514         mSubscribedMailboxes.insert(descriptor.name);
0515     }
0516 }
0517 
0518 void KolabRetrieveCollectionsTask::onFullMailBoxesReceiveDone(KJob *job)
0519 {
0520     qCDebug(KOLABRESOURCE_LOG) << "received subscribed collections " << mTime.elapsed();
0521     mJobs--;
0522     if (job->error()) {
0523         qCWarning(KOLABRESOURCE_LOG) << QStringLiteral("Failed to retrieve subscribed collections: ") + job->errorString();
0524         cancelTask(i18n("Collection retrieval failed"));
0525     } else {
0526         checkDone();
0527     }
0528 }
0529 
0530 #include "moc_kolabretrievecollectionstask.cpp"