diff --git a/resources/ews/ewssubscriptionmanager.cpp b/resources/ews/ewssubscriptionmanager.cpp index e449f8c8b..e839b9f44 100644 --- a/resources/ews/ewssubscriptionmanager.cpp +++ b/resources/ews/ewssubscriptionmanager.cpp @@ -1,326 +1,326 @@ /* Copyright (C) 2015-2017 Krzysztof Nowicki This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "ewssubscriptionmanager.h" #include "ewsresource_debug.h" #include "ewsgeteventsrequest.h" #include "ewsgetfolderrequest.h" #include "ewsgetstreamingeventsrequest.h" #include "ewssubscribedfoldersjob.h" #include "ewssubscriberequest.h" #include "ewsunsubscriberequest.h" #include "ewssettings.h" #include // TODO: Allow customization static Q_CONSTEXPR uint pollInterval = 10; /* seconds */ static Q_CONSTEXPR uint streamingTimeout = 30; /* minutes */ static Q_CONSTEXPR uint streamingConnTimeout = 60; /* seconds */ EwsSubscriptionManager::EwsSubscriptionManager(EwsClient &client, const EwsId &rootId, EwsSettings *settings, QObject *parent) : QObject(parent) , mEwsClient(client) , mPollTimer(this) , mMsgRootId(rootId) , mFolderTreeChanged(false) , mEventReq(nullptr) , mSettings(settings) { mStreamingEvents = mEwsClient.serverVersion().supports(EwsServerVersion::StreamingSubscription); mStreamingTimer.setInterval(streamingConnTimeout * 1000); mStreamingTimer.setSingleShot(true); connect(&mStreamingTimer, &QTimer::timeout, this, &EwsSubscriptionManager::streamingConnectionTimeout); } EwsSubscriptionManager::~EwsSubscriptionManager() { cancelSubscription(); } void EwsSubscriptionManager::start() { // Set-up change notification subscription (if needed) if (mSettings->eventSubscriptionId().isEmpty()) { setupSubscription(); } else { reset(); } if (!mStreamingEvents) { mPollTimer.setInterval(pollInterval * 1000); mPollTimer.setSingleShot(false); connect(&mPollTimer, &QTimer::timeout, this, &EwsSubscriptionManager::getEvents); } } void EwsSubscriptionManager::cancelSubscription() { if (!mSettings->eventSubscriptionId().isEmpty()) { QPointer req = new EwsUnsubscribeRequest(mEwsClient, this); req->setSubscriptionId(mSettings->eventSubscriptionId()); req->exec(); mSettings->setEventSubscriptionId(QString()); mSettings->setEventSubscriptionWatermark(QString()); mSettings->save(); } } void EwsSubscriptionManager::setupSubscription() { EwsSubscribedFoldersJob *job = new EwsSubscribedFoldersJob(mEwsClient, mSettings, this); connect(job, &EwsRequest::result, this, &EwsSubscriptionManager::verifySubFoldersRequestFinished); job->start(); } void EwsSubscriptionManager::verifySubFoldersRequestFinished(KJob *job) { if (!job->error()) { EwsSubscribedFoldersJob *folderJob = qobject_cast(job); Q_ASSERT(folderJob); setupSubscriptionReq(folderJob->folders()); } else { Q_EMIT connectionError(); } } void EwsSubscriptionManager::setupSubscriptionReq(const EwsId::List &ids) { EwsSubscribeRequest *req = new EwsSubscribeRequest(mEwsClient, this); //req->setAllFolders(true); QList events; events << EwsNewMailEvent; events << EwsMovedEvent; events << EwsCopiedEvent; events << EwsModifiedEvent; events << EwsDeletedEvent; events << EwsCreatedEvent; req->setEventTypes(events); if (mStreamingEvents) { req->setType(EwsSubscribeRequest::StreamingSubscription); } else { req->setType(EwsSubscribeRequest::PullSubscription); } req->setFolderIds(ids); req->setAllFolders(false); connect(req, &EwsRequest::result, this, &EwsSubscriptionManager::subscribeRequestFinished); req->start(); } void EwsSubscriptionManager::reset() { mPollTimer.stop(); getEvents(); if (!mStreamingEvents) { mPollTimer.start(); } } void EwsSubscriptionManager::resetSubscription() { mPollTimer.stop(); cancelSubscription(); setupSubscription(); } void EwsSubscriptionManager::subscribeRequestFinished(KJob *job) { if (!job->error()) { EwsSubscribeRequest *req = qobject_cast(job); if (req) { mSettings->setEventSubscriptionId(req->response().subscriptionId()); if (mStreamingEvents) { getEvents(); } else { mSettings->setEventSubscriptionWatermark(req->response().watermark()); getEvents(); mPollTimer.start(); } mSettings->save(); } } else { Q_EMIT connectionError(); } } void EwsSubscriptionManager::getEvents() { if (mStreamingEvents) { EwsGetStreamingEventsRequest *req = new EwsGetStreamingEventsRequest(mEwsClient, this); req->setSubscriptionId(mSettings->eventSubscriptionId()); req->setTimeout(streamingTimeout); connect(req, &EwsRequest::result, this, &EwsSubscriptionManager::getEventsRequestFinished); connect(req, &EwsGetStreamingEventsRequest::eventsReceived, this, &EwsSubscriptionManager::streamingEventsReceived); req->start(); mEventReq = req; mStreamingTimer.start(); } else { EwsGetEventsRequest *req = new EwsGetEventsRequest(mEwsClient, this); req->setSubscriptionId(mSettings->eventSubscriptionId()); req->setWatermark(mSettings->eventSubscriptionWatermark()); connect(req, &EwsRequest::result, this, &EwsSubscriptionManager::getEventsRequestFinished); req->start(); mEventReq = req; } } void EwsSubscriptionManager::getEventsRequestFinished(KJob *job) { mStreamingTimer.stop(); mEventReq->deleteLater(); mEventReq = nullptr; EwsEventRequestBase *req = qobject_cast(job); if (!req) { qCWarningNC(EWSRES_LOG) << QStringLiteral("Invalid EwsEventRequestBase job object."); reset(); return; } if ((!req->responses().isEmpty()) && ((req->responses()[0].responseCode() == QLatin1String("ErrorInvalidSubscription")) || (req->responses()[0].responseCode() == QLatin1String("ErrorSubscriptionNotFound")))) { mSettings->setEventSubscriptionId(QString()); mSettings->setEventSubscriptionWatermark(QString()); mSettings->save(); resetSubscription(); return; } if (!job->error()) { processEvents(req, true); if (mStreamingEvents) { getEvents(); } } else { reset(); } } void EwsSubscriptionManager::streamingEventsReceived(KJob *job) { mStreamingTimer.stop(); EwsEventRequestBase *req = qobject_cast(job); if (!req) { qCWarningNC(EWSRES_LOG) << QStringLiteral("Invalid EwsEventRequestBase job object."); reset(); return; } if (!job->error()) { processEvents(req, false); mStreamingTimer.start(); } } void EwsSubscriptionManager::streamingConnectionTimeout() { if (mEventReq) { qCWarningNC(EWSRES_LOG) << QStringLiteral("Streaming request timeout - restarting"); mEventReq->deleteLater(); mEventReq = nullptr; getEvents(); } } void EwsSubscriptionManager::processEvents(EwsEventRequestBase *req, bool finished) { bool moreEvents = false; Q_FOREACH (const EwsGetEventsRequest::Response &resp, req->responses()) { Q_FOREACH (const EwsGetEventsRequest::Notification &nfy, resp.notifications()) { Q_FOREACH (const EwsGetEventsRequest::Event &event, nfy.events()) { bool skip = false; EwsId id = event.itemId(); for (auto it = mQueuedUpdates.find(id.id()); it != mQueuedUpdates.end(); ++it) { if (it->type == event.type() && (it->type == EwsDeletedEvent || it->changeKey == id.changeKey())) { qCDebugNC(EWSRES_LOG) << QStringLiteral("Skipped queued update type %1 for item %2"); skip = true; mQueuedUpdates.erase(it); break; } } mSettings->setEventSubscriptionWatermark(event.watermark()); if (!skip) { switch (event.type()) { case EwsCopiedEvent: case EwsMovedEvent: if (!event.itemIsFolder()) { mUpdatedFolderIds.insert(event.oldParentFolderId()); } /* fall through */ case EwsCreatedEvent: case EwsDeletedEvent: case EwsModifiedEvent: case EwsNewMailEvent: if (event.itemIsFolder()) { mFolderTreeChanged = true; } else { mUpdatedFolderIds.insert(event.parentFolderId()); } break; case EwsStatusEvent: // Do nothing break; default: break; } } } if (nfy.hasMoreEvents()) { moreEvents = true; } } if (mStreamingEvents) { EwsGetStreamingEventsRequest *req2 = qobject_cast(req); if (req2) { req2->eventsProcessed(resp); } } } if (moreEvents && finished) { getEvents(); } else { if (mFolderTreeChanged) { qCDebugNC(EWSRES_LOG) << QStringLiteral("Found modified folder tree"); Q_EMIT folderTreeModified(); mFolderTreeChanged = false; } if (!mUpdatedFolderIds.isEmpty()) { qCDebugNC(EWSRES_LOG) << QStringLiteral("Found %1 modified folders") .arg(mUpdatedFolderIds.size()); - Q_EMIT foldersModified(mUpdatedFolderIds.toList()); + Q_EMIT foldersModified(mUpdatedFolderIds.values()); mUpdatedFolderIds.clear(); } } } void EwsSubscriptionManager::queueUpdate(EwsEventType type, const QString &id, const QString &changeKey) { mQueuedUpdates.insert(id, {type, changeKey}); } diff --git a/resources/imap/additemtask.cpp b/resources/imap/additemtask.cpp index e224cf8b9..d4fae83e9 100644 --- a/resources/imap/additemtask.cpp +++ b/resources/imap/additemtask.cpp @@ -1,206 +1,206 @@ /* Copyright (c) 2010 Klarälvdalens Datakonsult AB, a KDAB Group company Author: Kevin Ottens This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "additemtask.h" #include #include "imapresource_debug.h" #include #include #include #include #include #include #include #include "uidnextattribute.h" AddItemTask::AddItemTask(const ResourceStateInterface::Ptr &resource, QObject *parent) : ResourceTask(DeferIfNoSession, resource, parent) { } AddItemTask::~AddItemTask() { } void AddItemTask::doStart(KIMAP::Session *session) { if (!item().hasPayload()) { changeProcessed(); return; } const QString mailBox = mailBoxForCollection(collection()); if (mailBox.isEmpty()) { qCWarning(IMAPRESOURCE_LOG) << "Trying to append message to invalid mailbox, this will fail. Id: " << parentCollection().id(); } qCDebug(IMAPRESOURCE_LOG) << "Got notification about item added for local id " << item().id() << " and remote id " << item().remoteId(); // save message to the server. KMime::Message::Ptr msg = item().payload(); m_messageId = msg->messageID()->asUnicodeString().toUtf8(); KIMAP::AppendJob *job = new KIMAP::AppendJob(session); job->setMailBox(mailBox); job->setContent(msg->encodedContent(true)); - job->setFlags(fromAkonadiToSupportedImapFlags(item().flags().toList(), collection())); + job->setFlags(fromAkonadiToSupportedImapFlags(item().flags().values(), collection())); job->setInternalDate(msg->date()->dateTime()); connect(job, &KIMAP::AppendJob::result, this, &AddItemTask::onAppendMessageDone); job->start(); } void AddItemTask::onAppendMessageDone(KJob *job) { KIMAP::AppendJob *append = qobject_cast(job); if (append->error()) { qCWarning(IMAPRESOURCE_LOG) << append->errorString(); cancelTask(append->errorString()); return; } qint64 uid = append->uid(); if (uid > 0) { // We got it directly if UIDPLUS is supported... applyFoundUid(uid); } else { // ... otherwise prepare searching for the message KIMAP::Session *session = append->session(); const QString mailBox = append->mailBox(); if (session->selectedMailBox() != mailBox) { KIMAP::SelectJob *select = new KIMAP::SelectJob(session); select->setMailBox(mailBox); connect(select, &KJob::result, this, &AddItemTask::onPreSearchSelectDone); select->start(); } else { triggerSearchJob(session); } } } void AddItemTask::onPreSearchSelectDone(KJob *job) { if (job->error()) { qCWarning(IMAPRESOURCE_LOG) << job->errorString(); cancelTask(job->errorString()); } else { KIMAP::SelectJob *select = static_cast(job); triggerSearchJob(select->session()); } } void AddItemTask::triggerSearchJob(KIMAP::Session *session) { KIMAP::SearchJob *search = new KIMAP::SearchJob(session); search->setUidBased(true); if (!m_messageId.isEmpty()) { search->setTerm(KIMAP::Term(QStringLiteral("Message-ID"), QString::fromLatin1(m_messageId))); } else { Akonadi::Collection c = collection(); UidNextAttribute *uidNext = c.attribute(); if (!uidNext) { cancelTask(i18n("Could not determine the UID for the newly created message on the server")); search->deleteLater(); return; } search->setTerm(KIMAP::Term(KIMAP::Term::And, { KIMAP::Term(KIMAP::Term::New), KIMAP::Term(KIMAP::Term::Uid, KIMAP::ImapSet(uidNext->uidNext(), 0)) })); } connect(search, &KJob::result, this, &AddItemTask::onSearchDone); search->start(); } void AddItemTask::onSearchDone(KJob *job) { if (job->error()) { qCWarning(IMAPRESOURCE_LOG) << job->errorString(); cancelTask(job->errorString()); return; } KIMAP::SearchJob *search = static_cast(job); qint64 uid = 0; if (search->results().count() == 1) { uid = search->results().at(0); } applyFoundUid(uid); } void AddItemTask::applyFoundUid(qint64 uid) { Akonadi::Item i = item(); // if we didn't manage to get a valid UID from the server, use a random RID instead // this will make ItemSync clean up the mess during the next sync (while empty RIDs are protected as not yet existing on the server) if (uid > 0) { i.setRemoteId(QString::number(uid)); } else { i.setRemoteId(QUuid::createUuid().toString()); } qCDebug(IMAPRESOURCE_LOG) << "Setting remote ID to " << i.remoteId() << " for item with local id " << i.id(); changeCommitted(i); Akonadi::Collection c = collection(); // Get the current uid next value and store it UidNextAttribute *uidAttr = nullptr; int oldNextUid = 0; if (c.hasAttribute("uidnext")) { uidAttr = static_cast(c.attribute("uidnext")); oldNextUid = uidAttr->uidNext(); } // If the uid we just got back is the expected next one of the box // then update the property to the probable next uid to keep the cache in sync. // If not something happened in our back, so we don't update and a refetch will // happen at some point. if (uid == oldNextUid) { if (uidAttr == nullptr) { uidAttr = new UidNextAttribute(uid + 1); c.addAttribute(uidAttr); } else { uidAttr->setUidNext(uid + 1); } applyCollectionChanges(c); } } diff --git a/resources/imap/changeitemsflagstask.cpp b/resources/imap/changeitemsflagstask.cpp index 3ab3c7a9b..d7e866a72 100644 --- a/resources/imap/changeitemsflagstask.cpp +++ b/resources/imap/changeitemsflagstask.cpp @@ -1,164 +1,164 @@ /* Copyright (c) 2013 Daniel Vrátil This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "changeitemsflagstask.h" #include #include #include #include "imapresource_debug.h" ChangeItemsFlagsTask::ChangeItemsFlagsTask(const ResourceStateInterface::Ptr &resource, QObject *parent) : ResourceTask(ResourceTask::DeferIfNoSession, resource, parent) { } ChangeItemsFlagsTask::~ChangeItemsFlagsTask() { } void ChangeItemsFlagsTask::doStart(KIMAP::Session *session) { const QString mailBox = mailBoxForCollection(items().at(0).parentCollection()); qCDebug(IMAPRESOURCE_LOG) << mailBox; if (session->selectedMailBox() != mailBox) { KIMAP::SelectJob *select = new KIMAP::SelectJob(session); select->setMailBox(mailBox); connect(select, &KJob::result, this, &ChangeItemsFlagsTask::onSelectDone); select->start(); } else { if (!addedFlags().isEmpty()) { triggerAppendFlagsJob(session); } else if (!removedFlags().isEmpty()) { triggerRemoveFlagsJob(session); } else { qCDebug(IMAPRESOURCE_LOG) << "nothing to do"; changeProcessed(); } } } void ChangeItemsFlagsTask::onSelectDone(KJob *job) { if (job->error()) { qCWarning(IMAPRESOURCE_LOG) << "Select failed: " << job->errorString(); cancelTask(job->errorString()); } else { KIMAP::SelectJob *select = static_cast(job); qCDebug(IMAPRESOURCE_LOG) << addedFlags(); if (!addedFlags().isEmpty()) { triggerAppendFlagsJob(select->session()); } else if (!removedFlags().isEmpty()) { triggerRemoveFlagsJob(select->session()); } else { qCDebug(IMAPRESOURCE_LOG) << "nothing to do"; changeProcessed(); } } } KIMAP::StoreJob *ChangeItemsFlagsTask::prepareJob(KIMAP::Session *session) { KIMAP::ImapSet set; const Akonadi::Item::List &allItems = items(); // Split the request to multiple smaller requests of 2000 UIDs each - various IMAP // servers have various limits on maximum size of a request // 2000 is a random number that sounds like a good compromise between performance // and functionality (i.e. 2000 UIDs should be supported by any server out there) for (int i = 0, count = qMin(2000, allItems.count() - m_processedItems); i < count; ++i) { set.add(allItems[m_processedItems].remoteId().toLong()); ++m_processedItems; } KIMAP::StoreJob *store = new KIMAP::StoreJob(session); store->setUidBased(true); store->setSequenceSet(set); return store; } void ChangeItemsFlagsTask::triggerAppendFlagsJob(KIMAP::Session *session) { - const auto supportedFlags = fromAkonadiToSupportedImapFlags(addedFlags().toList(), items().at(0).parentCollection()); + const auto supportedFlags = fromAkonadiToSupportedImapFlags(addedFlags().values(), items().at(0).parentCollection()); if (supportedFlags.isEmpty()) { if (!removedFlags().isEmpty()) { m_processedItems = 0; triggerRemoveFlagsJob(session); } else { changeProcessed(); } } else { KIMAP::StoreJob *store = prepareJob(session); store->setFlags(supportedFlags); store->setMode(KIMAP::StoreJob::AppendFlags); connect(store, &KIMAP::StoreJob::result, this, &ChangeItemsFlagsTask::onAppendFlagsDone); store->start(); } } void ChangeItemsFlagsTask::triggerRemoveFlagsJob(KIMAP::Session *session) { - const auto supportedFlags = fromAkonadiToSupportedImapFlags(removedFlags().toList(), items().at(0).parentCollection()); + const auto supportedFlags = fromAkonadiToSupportedImapFlags(removedFlags().values(), items().at(0).parentCollection()); if (supportedFlags.isEmpty()) { changeProcessed(); } else { KIMAP::StoreJob *store = prepareJob(session); store->setFlags(supportedFlags); store->setMode(KIMAP::StoreJob::RemoveFlags); connect(store, &KIMAP::StoreJob::result, this, &ChangeItemsFlagsTask::onRemoveFlagsDone); store->start(); } } void ChangeItemsFlagsTask::onAppendFlagsDone(KJob *job) { if (job->error()) { qCWarning(IMAPRESOURCE_LOG) << "Flag append failed: " << job->errorString(); cancelTask(job->errorString()); } else { KIMAP::Session *session = qobject_cast(job)->session(); if (m_processedItems < items().count()) { triggerAppendFlagsJob(session); } else if (removedFlags().isEmpty()) { changeProcessed(); } else { qCDebug(IMAPRESOURCE_LOG) << removedFlags(); m_processedItems = 0; triggerRemoveFlagsJob(session); } } } void ChangeItemsFlagsTask::onRemoveFlagsDone(KJob *job) { if (job->error()) { qCWarning(IMAPRESOURCE_LOG) << "Flag remove failed: " << job->errorString(); cancelTask(job->errorString()); } else { if (m_processedItems < items().count()) { triggerRemoveFlagsJob(qobject_cast(job)->session()); } else { changeProcessed(); } } } diff --git a/resources/imap/changeitemtask.cpp b/resources/imap/changeitemtask.cpp index 447880f13..589287839 100644 --- a/resources/imap/changeitemtask.cpp +++ b/resources/imap/changeitemtask.cpp @@ -1,285 +1,285 @@ /* Copyright (c) 2010 Klarälvdalens Datakonsult AB, a KDAB Group company Author: Kevin Ottens This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "changeitemtask.h" #include "imapresource_debug.h" #include #include #include #include #include #include #include #include "imapflags.h" #include "uidnextattribute.h" ChangeItemTask::ChangeItemTask(const ResourceStateInterface::Ptr &resource, QObject *parent) : ResourceTask(DeferIfNoSession, resource, parent) { } ChangeItemTask::~ChangeItemTask() { } void ChangeItemTask::doStart(KIMAP::Session *session) { m_session = session; const QString mailBox = mailBoxForCollection(item().parentCollection()); m_oldUid = item().remoteId().toLongLong(); qCDebug(IMAPRESOURCE_LOG) << mailBox << m_oldUid << parts(); if (parts().contains("PLD:RFC822")) { if (!item().hasPayload()) { qCWarning(IMAPRESOURCE_LOG) << "Payload changed, no payload available."; changeProcessed(); return; } // save message to the server. KMime::Message::Ptr msg = item().payload(); m_messageId = msg->messageID()->asUnicodeString().toUtf8(); KIMAP::AppendJob *job = new KIMAP::AppendJob(session); job->setMailBox(mailBox); job->setContent(msg->encodedContent(true)); - const QList flags = fromAkonadiToSupportedImapFlags(item().flags().toList(), item().parentCollection()); + const QList flags = fromAkonadiToSupportedImapFlags(item().flags().values(), item().parentCollection()); job->setFlags(flags); qCDebug(IMAPRESOURCE_LOG) << "Appending new message: " << flags; connect(job, &KIMAP::AppendJob::result, this, &ChangeItemTask::onAppendMessageDone); job->start(); } else if (parts().contains("FLAGS")) { if (session->selectedMailBox() != mailBox) { KIMAP::SelectJob *select = new KIMAP::SelectJob(session); select->setMailBox(mailBox); connect(select, &KIMAP::SelectJob::result, this, &ChangeItemTask::onPreStoreSelectDone); select->start(); } else { triggerStoreJob(); } } else { qCDebug(IMAPRESOURCE_LOG) << "Nothing to do"; changeProcessed(); } } void ChangeItemTask::onPreStoreSelectDone(KJob *job) { if (job->error()) { qCWarning(IMAPRESOURCE_LOG) << "Select failed: " << job->errorString(); cancelTask(job->errorString()); } else { triggerStoreJob(); } } void ChangeItemTask::triggerStoreJob() { - const QList flags = fromAkonadiToSupportedImapFlags(item().flags().toList(), item().parentCollection()); + const QList flags = fromAkonadiToSupportedImapFlags(item().flags().values(), item().parentCollection()); qCDebug(IMAPRESOURCE_LOG) << flags; KIMAP::StoreJob *store = new KIMAP::StoreJob(m_session); store->setUidBased(true); store->setSequenceSet(KIMAP::ImapSet(m_oldUid)); store->setFlags(flags); store->setMode(KIMAP::StoreJob::SetFlags); connect(store, &KIMAP::StoreJob::result, this, &ChangeItemTask::onStoreFlagsDone); store->start(); } void ChangeItemTask::onStoreFlagsDone(KJob *job) { if (job->error()) { qCWarning(IMAPRESOURCE_LOG) << "Flag store failed: " << job->errorString(); cancelTask(job->errorString()); } else { changeProcessed(); } } void ChangeItemTask::onAppendMessageDone(KJob *job) { if (job->error()) { qCWarning(IMAPRESOURCE_LOG) << "Append failed: " << job->errorString(); cancelTask(job->errorString()); return; } KIMAP::AppendJob *append = qobject_cast(job); m_newUid = append->uid(); // OK it's a content change, so we've to mark the old version as deleted // remember, you can't modify messages in IMAP mailboxes so that's really // add+remove all the time. // APPEND does not require a SELECT, so we could be anywhere right now if (m_session->selectedMailBox() != append->mailBox()) { KIMAP::SelectJob *select = new KIMAP::SelectJob(m_session); select->setMailBox(append->mailBox()); connect(select, &KIMAP::SelectJob::result, this, &ChangeItemTask::onPreDeleteSelectDone); select->start(); } else { if (m_newUid > 0) { triggerDeleteJob(); } else { triggerSearchJob(); } } } void ChangeItemTask::onPreDeleteSelectDone(KJob *job) { if (job->error()) { qCWarning(IMAPRESOURCE_LOG) << "PreDelete select failed: " << job->errorString(); if (m_newUid > 0) { recordNewUid(); } else { cancelTask(job->errorString()); } } else { if (m_newUid > 0) { triggerDeleteJob(); } else { triggerSearchJob(); } } } void ChangeItemTask::triggerSearchJob() { KIMAP::SearchJob *search = new KIMAP::SearchJob(m_session); search->setUidBased(true); if (!m_messageId.isEmpty()) { search->setTerm(KIMAP::Term(QStringLiteral("Message-ID"), QString::fromLatin1(m_messageId))); } else { UidNextAttribute *uidNext = item().parentCollection().attribute(); if (!uidNext) { qCWarning(IMAPRESOURCE_LOG) << "Failed to determine new uid."; cancelTask(i18n("Could not determine the UID for the newly created message on the server")); search->deleteLater(); return; } search->setTerm(KIMAP::Term(KIMAP::Term::And, { KIMAP::Term(KIMAP::Term::New), KIMAP::Term(KIMAP::Term::Uid, KIMAP::ImapSet(uidNext->uidNext(), 0)) })); } connect(search, &KIMAP::SearchJob::result, this, &ChangeItemTask::onSearchDone); search->start(); } void ChangeItemTask::onSearchDone(KJob *job) { if (job->error()) { qCWarning(IMAPRESOURCE_LOG) << "Search failed: " << job->errorString(); cancelTask(job->errorString()); return; } KIMAP::SearchJob *search = static_cast(job); if (search->results().count() != 1) { qCWarning(IMAPRESOURCE_LOG) << "Failed to determine new uid."; cancelTask(i18n("Could not determine the UID for the newly created message on the server")); return; } m_newUid = search->results().at(0); triggerDeleteJob(); } void ChangeItemTask::triggerDeleteJob() { KIMAP::StoreJob *store = new KIMAP::StoreJob(m_session); store->setUidBased(true); store->setSequenceSet(KIMAP::ImapSet(m_oldUid)); store->setFlags(QList() << ImapFlags::Deleted); store->setMode(KIMAP::StoreJob::AppendFlags); connect(store, &KIMAP::StoreJob::result, this, &ChangeItemTask::onDeleteDone); store->start(); } void ChangeItemTask::onDeleteDone(KJob */*job*/) { recordNewUid(); } void ChangeItemTask::recordNewUid() { Q_ASSERT(m_newUid > 0); Akonadi::Item i = item(); Akonadi::Collection c = i.parentCollection(); // Get the current uid next value and store it UidNextAttribute *uidAttr = nullptr; int oldNextUid = 0; if (c.hasAttribute("uidnext")) { uidAttr = static_cast(c.attribute("uidnext")); oldNextUid = uidAttr->uidNext(); } // If the uid we just got back is the expected next one of the box // then update the property to the probable next uid to keep the cache in sync. // If not something happened in our back, so we don't update and a refetch will // happen at some point. if (m_newUid == oldNextUid) { if (uidAttr == nullptr) { uidAttr = new UidNextAttribute(m_newUid + 1); c.addAttribute(uidAttr); } else { uidAttr->setUidNext(m_newUid + 1); } applyCollectionChanges(c); } const QString remoteId = QString::number(m_newUid); qCDebug(IMAPRESOURCE_LOG) << "Setting remote ID to " << remoteId << " for item with local id " << i.id(); i.setRemoteId(remoteId); changeCommitted(i); } diff --git a/resources/kolab/kolabretrievecollectionstask.cpp b/resources/kolab/kolabretrievecollectionstask.cpp index dd3620e3f..338a5c232 100644 --- a/resources/kolab/kolabretrievecollectionstask.cpp +++ b/resources/kolab/kolabretrievecollectionstask.cpp @@ -1,536 +1,536 @@ /* Copyright (c) 2010 Klarälvdalens Datakonsult AB, a KDAB Group company Author: Kevin Ottens Copyright (c) 2014 Christian Mollekopf This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "kolabretrievecollectionstask.h" #include "kolabhelpers.h" #include "kolabresource_debug.h" #include "kolabresource_trace.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include static bool isNamespaceFolder(const QString &path, const QList &namespaces, bool matchCompletePath = false) { for (const KIMAP::MailBoxDescriptor &desc : namespaces) { if (path.startsWith(desc.name.left(desc.name.size() - 1))) { //Namespace ends with path separator and path doesn't if (!matchCompletePath || path.size() - desc.name.size() <= 1) { //We want to match only for the complete path return true; } } } return false; } RetrieveMetadataJob::RetrieveMetadataJob(KIMAP::Session *session, const QStringList &mailboxes, const QStringList &serverCapabilities, const QSet &requestedMetadata, const QString &separator, const QList &sharedNamespace, const QList &userNamespace, QObject *parent) : KJob(parent) , mJobs(0) , mRequestedMetadata(requestedMetadata) , mServerCapabilities(serverCapabilities) , mMailboxes(mailboxes) , mSession(session) , mSeparator(separator) , mSharedNamespace(sharedNamespace) , mUserNamespace(userNamespace) { } void RetrieveMetadataJob::start() { qCDebug(KOLABRESOURCE_TRACE); //Fill the map with empty entries so we set the mimetype to mail if no metadata is retrieved for (const QString &mailbox : qAsConst(mMailboxes)) { mMetadata.insert(mailbox, QMap()); } if (mServerCapabilities.contains(QLatin1String("METADATA")) || mServerCapabilities.contains(QLatin1String("ANNOTATEMORE"))) { QSet toplevelMailboxes; for (const QString &mailbox : qAsConst(mMailboxes)) { const QStringList parts = mailbox.split(mSeparator); if (!parts.isEmpty()) { if (isNamespaceFolder(mailbox, mUserNamespace) && parts.length() >= 2) { // Other Users can be too big to request with a single command so we request Other Users//* toplevelMailboxes << parts.at(0) + mSeparator + parts.at(1) + mSeparator; } else if (!isNamespaceFolder(mailbox, mSharedNamespace)) { toplevelMailboxes << parts.first(); } } } for (const KIMAP::MailBoxDescriptor &desc : qAsConst(mSharedNamespace)) { toplevelMailboxes << desc.name; } //TODO perhaps exclude the shared and other users namespaces by listing only toplevel (with %), and then only getting metadata of the toplevel folders. for (const QString &mailbox : qAsConst(toplevelMailboxes)) { KIMAP::GetMetaDataJob *meta = new KIMAP::GetMetaDataJob(mSession); meta->setMailBox(mailbox + QLatin1String("*")); if (mServerCapabilities.contains(QLatin1String("METADATA"))) { meta->setServerCapability(KIMAP::MetaDataJobBase::Metadata); } else { meta->setServerCapability(KIMAP::MetaDataJobBase::Annotatemore); } meta->setDepth(KIMAP::GetMetaDataJob::AllLevels); for (const QByteArray &requestedEntry : qAsConst(mRequestedMetadata)) { meta->addRequestedEntry(requestedEntry); } connect(meta, &KJob::result, this, &RetrieveMetadataJob::onGetMetaDataDone); mJobs++; meta->start(); } } // Get the ACLs from the mailbox if it's supported if (mServerCapabilities.contains(QLatin1String("ACL"))) { for (const QString &mailbox : qAsConst(mMailboxes)) { // "Shared Folders" is not a valid mailbox, so we have to skip the ACL request for this folder if (isNamespaceFolder(mailbox, mSharedNamespace, true)) { continue; } KIMAP::MyRightsJob *rights = new KIMAP::MyRightsJob(mSession); rights->setMailBox(mailbox); connect(rights, &KJob::result, this, &RetrieveMetadataJob::onRightsReceived); mJobs++; rights->start(); } } checkDone(); } void RetrieveMetadataJob::onGetMetaDataDone(KJob *job) { mJobs--; KIMAP::GetMetaDataJob *meta = static_cast(job); if (job->error()) { qCDebug(KOLABRESOURCE_LOG) << "No metadata for for mailbox: " << meta->mailBox(); if (!isNamespaceFolder(meta->mailBox(), mSharedNamespace)) { qCWarning(KOLABRESOURCE_LOG) << "Get metadata failed: " << job->errorString(); //We ignore the error to avoid failing the complete sync. We can run into this when trying to retrieve rights for non-existing mailboxes. } checkDone(); return; } const QHash > metadata = meta->allMetaDataForMailboxes(); const QStringList lstKeys = metadata.keys(); for (const QString &folder : lstKeys) { mMetadata.insert(folder, metadata.value(folder)); } checkDone(); } void RetrieveMetadataJob::onRightsReceived(KJob *job) { mJobs--; KIMAP::MyRightsJob *rights = static_cast(job); if (job->error()) { qCDebug(KOLABRESOURCE_LOG) << "No rights for mailbox: " << rights->mailBox(); if (!isNamespaceFolder(rights->mailBox(), mSharedNamespace)) { qCWarning(KOLABRESOURCE_LOG) << "MyRights for mailbox" << rights->mailBox() << "failed:" << job->errorString(); //We ignore the error to avoid failing the complete sync. We can run into this when trying to retrieve rights for non-existing mailboxes. } checkDone(); return; } const KIMAP::Acl::Rights imapRights = rights->rights(); mRights.insert(rights->mailBox(), imapRights); checkDone(); } void RetrieveMetadataJob::checkDone() { if (!mJobs) { qCDebug(KOLABRESOURCE_TRACE) << "done"; qCDebug(KOLABRESOURCE_LOG) << "done"; emitResult(); } } KolabRetrieveCollectionsTask::KolabRetrieveCollectionsTask(const ResourceStateInterface::Ptr &resource, QObject *parent) : ResourceTask(CancelIfNoSession, resource, parent) , mJobs(0) , mSession(nullptr) , cContentMimeTypes("CONTENTMIMETYPES") , cAccessRights("AccessRights") , cImapAcl("imapacl") , cCollectionAnnotations("collectionannotations") , cDefaultKeepLocalChanges(QSet() << cContentMimeTypes << cAccessRights << cImapAcl << cCollectionAnnotations) , cDefaultMimeTypes(QStringList() << Akonadi::Collection::mimeType() << QStringLiteral("application/x-kolab-objects")) , cCollectionOnlyContentMimeTypes(QStringList() << Akonadi::Collection::mimeType()) { mRequestedMetadata << "/shared/vendor/kolab/folder-type"; mRequestedMetadata << "/private/vendor/kolab/folder-type"; mRequestedMetadata << "/shared" KOLAB_COLOR_ANNOTATION << "/private" KOLAB_COLOR_ANNOTATION; } KolabRetrieveCollectionsTask::~KolabRetrieveCollectionsTask() { } void KolabRetrieveCollectionsTask::doStart(KIMAP::Session *session) { qCDebug(KOLABRESOURCE_LOG) << "Starting collection retrieval"; mTime.start(); mSession = session; Akonadi::Collection root; root.setName(resourceName()); root.setRemoteId(rootRemoteId()); root.setContentMimeTypes(QStringList(Akonadi::Collection::mimeType())); root.setParentCollection(Akonadi::Collection::root()); root.addAttribute(new NoSelectAttribute(true)); root.attribute(Akonadi::Collection::AddIfMissing)->setIconName(QStringLiteral("kolab")); Akonadi::CachePolicy policy; policy.setInheritFromParent(false); policy.setSyncOnDemand(true); QStringList localParts; localParts << QLatin1String(Akonadi::MessagePart::Envelope) << QLatin1String(Akonadi::MessagePart::Header); int cacheTimeout = 60; if (isDisconnectedModeEnabled()) { // For disconnected mode we also cache the body // and we keep all data indefinitely localParts << QLatin1String(Akonadi::MessagePart::Body); cacheTimeout = -1; } policy.setLocalParts(localParts); policy.setCacheTimeout(cacheTimeout); policy.setIntervalCheckTime(intervalCheckTime()); root.setCachePolicy(policy); mMailCollections.insert(QString(), root); qCDebug(KOLABRESOURCE_TRACE) << "subscription enabled: " << isSubscriptionEnabled(); //jobs are serialized by the session if (isSubscriptionEnabled()) { KIMAP::ListJob *fullListJob = new KIMAP::ListJob(session); fullListJob->setOption(KIMAP::ListJob::NoOption); fullListJob->setQueriedNamespaces(serverNamespaces()); connect(fullListJob, &KIMAP::ListJob::mailBoxesReceived, this, &KolabRetrieveCollectionsTask::onFullMailBoxesReceived); connect(fullListJob, &KJob::result, this, &KolabRetrieveCollectionsTask::onFullMailBoxesReceiveDone); mJobs++; fullListJob->start(); } KIMAP::ListJob *listJob = new KIMAP::ListJob(session); listJob->setOption(KIMAP::ListJob::IncludeUnsubscribed); listJob->setQueriedNamespaces(serverNamespaces()); connect(listJob, &KIMAP::ListJob::mailBoxesReceived, this, &KolabRetrieveCollectionsTask::onMailBoxesReceived); connect(listJob, &KJob::result, this, &KolabRetrieveCollectionsTask::onMailBoxesReceiveDone); mJobs++; listJob->start(); } void KolabRetrieveCollectionsTask::onMailBoxesReceived(const QList< KIMAP::MailBoxDescriptor > &descriptors, const QList< QList > &flags) { const int nbDescriptors = descriptors.size(); for (int i = 0; i < nbDescriptors; ++i) { const KIMAP::MailBoxDescriptor descriptor = descriptors[i]; createCollection(descriptor.name, flags.at(i), !isSubscriptionEnabled() || mSubscribedMailboxes.contains(descriptor.name)); } checkDone(); } Akonadi::Collection KolabRetrieveCollectionsTask::getOrCreateParent(const QString &path) { if (mMailCollections.contains(path)) { return mMailCollections.value(path); } //create a dummy collection const QString separator = separatorCharacter(); const QStringList pathParts = path.split(separator); const QString pathPart = pathParts.last(); Akonadi::Collection c; c.setName(pathPart); c.setRemoteId(separator + pathPart); const QStringList parentPath = pathParts.mid(0, pathParts.size() - 1); const Akonadi::Collection parentCollection = getOrCreateParent(parentPath.join(separator)); c.setParentCollection(parentCollection); c.addAttribute(new NoSelectAttribute(true)); c.setContentMimeTypes(QStringList() << Akonadi::Collection::mimeType()); c.setRights(Akonadi::Collection::ReadOnly); c.setEnabled(false); setAttributes(c, pathParts, path); mMailCollections.insert(path, c); return c; } void KolabRetrieveCollectionsTask::setAttributes(Akonadi::Collection &c, const QStringList &pathParts, const QString &path) { Akonadi::CollectionIdentificationAttribute *attr = c.attribute(Akonadi::Collection::AddIfMissing); attr->setIdentifier(path.toLatin1()); // If the folder is a other users folder block all alarms from default if (isNamespaceFolder(path, resourceState()->userNamespaces())) { Akonadi::BlockAlarmsAttribute *attr = c.attribute(Akonadi::Collection::AddIfMissing); attr->blockEverything(true); } // If the folder is a other users top-level folder mark it accordingly if (pathParts.size() == 1 && isNamespaceFolder(path, resourceState()->userNamespaces())) { Akonadi::EntityDisplayAttribute *attr = c.attribute(Akonadi::Collection::AddIfMissing); attr->setDisplayName(i18n("Other Users")); attr->setIconName(QStringLiteral("x-mail-distribution-list")); } //Mark user folders for searching if (pathParts.size() >= 2 && isNamespaceFolder(path, resourceState()->userNamespaces())) { Akonadi::CollectionIdentificationAttribute *attr = c.attribute(Akonadi::Collection::AddIfMissing); if (pathParts.size() == 2) { attr->setCollectionNamespace("usertoplevel"); } else { attr->setCollectionNamespace("user"); } } // If the folder is a shared folders top-level folder mark it accordingly if (pathParts.size() == 1 && isNamespaceFolder(path, resourceState()->sharedNamespaces())) { Akonadi::EntityDisplayAttribute *attr = c.attribute(Akonadi::Collection::AddIfMissing); attr->setDisplayName(i18n("Shared Folders")); attr->setIconName(QStringLiteral("x-mail-distribution-list")); } //Mark shared folders for searching if (pathParts.size() >= 2 && isNamespaceFolder(path, resourceState()->sharedNamespaces())) { Akonadi::CollectionIdentificationAttribute *attr = c.attribute(Akonadi::Collection::AddIfMissing); attr->setCollectionNamespace("shared"); } } void KolabRetrieveCollectionsTask::createCollection(const QString &mailbox, const QList ¤tFlags, bool isSubscribed) { const QString separator = separatorCharacter(); Q_ASSERT(separator.size() == 1); const QString boxName = mailbox.endsWith(separator) ? mailbox.left(mailbox.size() - 1) : mailbox; const QStringList pathParts = boxName.split(separator); const QString pathPart = pathParts.last(); Akonadi::Collection c; //If we had a dummy collection we need to replace it if (mMailCollections.contains(mailbox)) { c = mMailCollections.value(mailbox); } c.setName(pathPart); c.setRemoteId(separator + pathPart); const QStringList parentPath = pathParts.mid(0, pathParts.size() - 1); const Akonadi::Collection parentCollection = getOrCreateParent(parentPath.join(separator)); c.setParentCollection(parentCollection); //TODO get from ResourceState, and add KMime::Message::mimeType() for the normal imap resource by default //We add a dummy mimetype, otherwise the itemsync doesn't even work (action is disabled and resourcebase aborts the operation) c.setContentMimeTypes(cDefaultMimeTypes); c.setKeepLocalChanges(cDefaultKeepLocalChanges); //assume LRS, until myrights is executed if (serverCapabilities().contains(QLatin1String("ACL"))) { c.setRights(Akonadi::Collection::ReadOnly); } else { c.setRights(Akonadi::Collection::AllRights); } setAttributes(c, pathParts, mailbox); // If the folder is the Inbox, make some special settings. if (pathParts.size() == 1 && pathPart.compare(QLatin1String("inbox"), Qt::CaseInsensitive) == 0) { Akonadi::EntityDisplayAttribute *attr = c.attribute(Akonadi::Collection::AddIfMissing); attr->setDisplayName(i18n("Inbox")); attr->setIconName(QStringLiteral("mail-folder-inbox")); c.attribute(Akonadi::Collection::AddIfMissing)->setCollectionType("inbox"); setIdleCollection(c); } // If this folder is a noselect folder, make some special settings. if (currentFlags.contains("\\noselect")) { c.addAttribute(new NoSelectAttribute(true)); c.setContentMimeTypes(cCollectionOnlyContentMimeTypes); c.setRights(Akonadi::Collection::ReadOnly); } else { // remove the noselect attribute explicitly, in case we had set it before (eg. for non-subscribed non-leaf folders) c.removeAttribute(); } // If this folder is a noinferiors folder, it is not allowed to create subfolders inside. if (currentFlags.contains("\\noinferiors")) { //qCDebug(KOLABRESOURCE_LOG) << "Noinferiors: " << currentPath; c.addAttribute(new NoInferiorsAttribute(true)); c.setRights(c.rights() & ~Akonadi::Collection::CanCreateCollection); } c.setEnabled(isSubscribed); // qCDebug(KOLABRESOURCE_LOG) << "creating collection " << mailbox << " with parent " << parentPath; mMailCollections.insert(mailbox, c); } void KolabRetrieveCollectionsTask::onMailBoxesReceiveDone(KJob *job) { qCDebug(KOLABRESOURCE_LOG) << "All mailboxes received: " << mTime.elapsed(); qCDebug(KOLABRESOURCE_LOG) << "in total: " << mMailCollections.size(); mJobs--; if (job->error()) { qCWarning(KOLABRESOURCE_LOG) << QStringLiteral("Failed to retrieve mailboxes: ") + job->errorString(); cancelTask(i18n("Collection retrieval failed")); } else { QSet mailboxes; const QStringList lstKeys = mMailCollections.keys(); for (const QString &mailbox : lstKeys) { if (!mailbox.isEmpty() && !isNamespaceFolder(mailbox, resourceState()->userNamespaces() + resourceState()->sharedNamespaces())) { mailboxes << mailbox; } } //Only request metadata for subscribed Other Users Folders - const QStringList metadataMailboxes = mailboxes.unite(mSubscribedMailboxes.toList().toSet()).toList(); + const QStringList metadataMailboxes = mailboxes.unite(mSubscribedMailboxes.values().toSet()).values(); RetrieveMetadataJob *metadata = new RetrieveMetadataJob(mSession, metadataMailboxes, serverCapabilities(), mRequestedMetadata, separatorCharacter(), resourceState()->sharedNamespaces(), resourceState()->userNamespaces(), this); connect(metadata, &KJob::result, this, &KolabRetrieveCollectionsTask::onMetadataRetrieved); mJobs++; metadata->start(); } } void KolabRetrieveCollectionsTask::applyRights(const QHash &rights) { // qCDebug(KOLABRESOURCE_LOG) << rights; const QStringList lstKeys = rights.keys(); for (const QString &mailbox : lstKeys) { if (mMailCollections.contains(mailbox)) { const KIMAP::Acl::Rights imapRights = rights.value(mailbox); QStringList parts = mailbox.split(separatorCharacter()); parts.removeLast(); QString parentMailbox = parts.join(separatorCharacter()); KIMAP::Acl::Rights parentImapRights; //If the parent folder is not existing we cant rename if (!parentMailbox.isEmpty() && rights.contains(parentMailbox)) { parentImapRights = rights.value(parentMailbox); } // qCDebug(KOLABRESOURCE_LOG) << mailbox << parentMailbox << imapRights << parentImapRights; Akonadi::Collection &collection = mMailCollections[mailbox]; CollectionMetadataHelper::applyRights(collection, imapRights, parentImapRights); // Store the mailbox ACLs Akonadi::ImapAclAttribute *aclAttribute = collection.attribute(Akonadi::Collection::AddIfMissing); const KIMAP::Acl::Rights oldRights = aclAttribute->myRights(); if (oldRights != imapRights) { aclAttribute->setMyRights(imapRights); } } else { qCWarning(KOLABRESOURCE_LOG) << "Can't find mailbox " << mailbox; } } } void KolabRetrieveCollectionsTask::applyMetadata(const QHash > &metadataMap) { // qCDebug(KOLABRESOURCE_LOG) << metadataMap; Q_FOREACH (const QString &mailbox, metadataMap.keys()) { const QMap metadata = metadataMap.value(mailbox); if (mMailCollections.contains(mailbox)) { Akonadi::Collection &collection = mMailCollections[mailbox]; // qCDebug(KOLABRESOURCE_LOG) << mailbox << metadata << type << folderType << KolabHelpers::getContentMimeTypes(folderType); collection.attribute(Akonadi::Collection::AddIfMissing)->setAnnotations(metadata); const QByteArray type = KolabHelpers::getFolderTypeAnnotation(metadata); const Kolab::FolderType folderType = KolabHelpers::folderTypeFromString(type); collection.setContentMimeTypes(KolabHelpers::getContentMimeTypes(folderType)); const QColor color = KolabHelpers::getFolderColor(metadata); if (color.isValid()) { collection.attribute(Akonadi::Collection::AddIfMissing)->setColor(color); } QSet keepLocalChanges = collection.keepLocalChanges(); keepLocalChanges.remove(cContentMimeTypes); collection.setKeepLocalChanges(keepLocalChanges); } } } void KolabRetrieveCollectionsTask::onMetadataRetrieved(KJob *job) { qCDebug(KOLABRESOURCE_LOG) << mTime.elapsed(); mJobs--; if (job->error()) { qCWarning(KOLABRESOURCE_LOG) << "Error while retrieving metadata, aborting collection retrieval: " << job->errorString(); cancelTask(i18n("Collection retrieval failed")); } else { RetrieveMetadataJob *metadata = static_cast(job); applyRights(metadata->mRights); applyMetadata(metadata->mMetadata); checkDone(); } } void KolabRetrieveCollectionsTask::checkDone() { if (!mJobs) { collectionsRetrieved(Akonadi::valuesToVector(mMailCollections)); qCDebug(KOLABRESOURCE_LOG) << "done " << mTime.elapsed(); } } void KolabRetrieveCollectionsTask::onFullMailBoxesReceived(const QList< KIMAP::MailBoxDescriptor > &descriptors, const QList< QList< QByteArray > > &flags) { Q_UNUSED(flags); for (const KIMAP::MailBoxDescriptor &descriptor : descriptors) { mSubscribedMailboxes.insert(descriptor.name); } } void KolabRetrieveCollectionsTask::onFullMailBoxesReceiveDone(KJob *job) { qCDebug(KOLABRESOURCE_LOG) << "received subscribed collections " << mTime.elapsed(); mJobs--; if (job->error()) { qCWarning(KOLABRESOURCE_LOG) << QStringLiteral("Failed to retrieve subscribed collections: ") + job->errorString(); cancelTask(i18n("Collection retrieval failed")); } else { checkDone(); } }