diff --git a/src/agentbase/agentbase.cpp b/src/agentbase/agentbase.cpp index 4e45d36c5..189f91df2 100644 --- a/src/agentbase/agentbase.cpp +++ b/src/agentbase/agentbase.cpp @@ -1,1333 +1,1299 @@ /* Copyright (c) 2006 Till Adam Copyright (c) 2007 Volker Krause Copyright (c) 2007 Bruno Virlet Copyright (c) 2008 Kevin Krammer 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 "agentbase.h" #include "agentbase_p.h" #include "akonadifull-version.h" #include "agentmanager.h" #include "changerecorder.h" #include "controladaptor.h" #include "itemfetchjob.h" #include "monitor_p.h" #include "servermanager_p.h" #include "session.h" #include "session_p.h" #include "statusadaptor.h" #include "agentconfigurationdialog.h" #include "private/standarddirs_p.h" #include "akonadiagentbase_debug.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include +#include #if defined __GLIBC__ # include // for dumping memory information #endif #ifdef Q_OS_WIN #include #include #include #endif using namespace Akonadi; static AgentBase *sAgentBase = nullptr; AgentBase::Observer::Observer() { } AgentBase::Observer::~Observer() { } void AgentBase::Observer::itemAdded(const Item &item, const Collection &collection) { Q_UNUSED(item); Q_UNUSED(collection); if (sAgentBase) { sAgentBase->d_ptr->changeProcessed(); } } void AgentBase::Observer::itemChanged(const Item &item, const QSet &partIdentifiers) { Q_UNUSED(item); Q_UNUSED(partIdentifiers); if (sAgentBase) { sAgentBase->d_ptr->changeProcessed(); } } void AgentBase::Observer::itemRemoved(const Item &item) { Q_UNUSED(item); if (sAgentBase) { sAgentBase->d_ptr->changeProcessed(); } } void AgentBase::Observer::collectionAdded(const Akonadi::Collection &collection, const Akonadi::Collection &parent) { Q_UNUSED(collection); Q_UNUSED(parent); if (sAgentBase) { sAgentBase->d_ptr->changeProcessed(); } } void AgentBase::Observer::collectionChanged(const Collection &collection) { Q_UNUSED(collection); if (sAgentBase) { sAgentBase->d_ptr->changeProcessed(); } } void AgentBase::Observer::collectionRemoved(const Collection &collection) { Q_UNUSED(collection); if (sAgentBase) { sAgentBase->d_ptr->changeProcessed(); } } void AgentBase::ObserverV2::itemMoved(const Akonadi::Item &item, const Akonadi::Collection &source, const Akonadi::Collection &dest) { Q_UNUSED(item); Q_UNUSED(source); Q_UNUSED(dest); if (sAgentBase) { sAgentBase->d_ptr->changeProcessed(); } } void AgentBase::ObserverV2::itemLinked(const Akonadi::Item &item, const Akonadi::Collection &collection) { Q_UNUSED(item); Q_UNUSED(collection); if (sAgentBase) { // not implementation, let's disconnect the signal to enable optimizations in Monitor QObject::disconnect(sAgentBase->changeRecorder(), &Monitor::itemLinked, sAgentBase->d_ptr, &AgentBasePrivate::itemLinked); sAgentBase->d_ptr->changeProcessed(); } } void AgentBase::ObserverV2::itemUnlinked(const Akonadi::Item &item, const Akonadi::Collection &collection) { Q_UNUSED(item); Q_UNUSED(collection); if (sAgentBase) { // not implementation, let's disconnect the signal to enable optimizations in Monitor QObject::disconnect(sAgentBase->changeRecorder(), &Monitor::itemUnlinked, sAgentBase->d_ptr, &AgentBasePrivate::itemUnlinked); sAgentBase->d_ptr->changeProcessed(); } } void AgentBase::ObserverV2::collectionMoved(const Akonadi::Collection &collection, const Akonadi::Collection &source, const Akonadi::Collection &dest) { Q_UNUSED(collection); Q_UNUSED(source); Q_UNUSED(dest); if (sAgentBase) { sAgentBase->d_ptr->changeProcessed(); } } void AgentBase::ObserverV2::collectionChanged(const Akonadi::Collection &collection, const QSet &changedAttributes) { Q_UNUSED(changedAttributes); collectionChanged(collection); } void AgentBase::ObserverV3::itemsFlagsChanged(const Akonadi::Item::List &items, const QSet &addedFlags, const QSet &removedFlags) { Q_UNUSED(items); Q_UNUSED(addedFlags); Q_UNUSED(removedFlags); if (sAgentBase) { // not implementation, let's disconnect the signal to enable optimizations in Monitor QObject::disconnect(sAgentBase->changeRecorder(), &Monitor::itemsFlagsChanged, sAgentBase->d_ptr, &AgentBasePrivate::itemsFlagsChanged); sAgentBase->d_ptr->changeProcessed(); } } void AgentBase::ObserverV3::itemsMoved(const Akonadi::Item::List &items, const Collection &sourceCollection, const Collection &destinationCollection) { Q_UNUSED(items); Q_UNUSED(sourceCollection); Q_UNUSED(destinationCollection); if (sAgentBase) { // not implementation, let's disconnect the signal to enable optimizations in Monitor QObject::disconnect(sAgentBase->changeRecorder(), &Monitor::itemsMoved, sAgentBase->d_ptr, &AgentBasePrivate::itemsMoved); sAgentBase->d_ptr->changeProcessed(); } } void AgentBase::ObserverV3::itemsRemoved(const Akonadi::Item::List &items) { Q_UNUSED(items); if (sAgentBase) { // not implementation, let's disconnect the signal to enable optimizations in Monitor QObject::disconnect(sAgentBase->changeRecorder(), &Monitor::itemsRemoved, sAgentBase->d_ptr, &AgentBasePrivate::itemsRemoved); sAgentBase->d_ptr->changeProcessed(); } } void AgentBase::ObserverV3::itemsLinked(const Akonadi::Item::List &items, const Collection &collection) { Q_UNUSED(items); Q_UNUSED(collection); if (sAgentBase) { // not implementation, let's disconnect the signal to enable optimizations in Monitor QObject::disconnect(sAgentBase->changeRecorder(), &Monitor::itemsLinked, sAgentBase->d_ptr, &AgentBasePrivate::itemsLinked); sAgentBase->d_ptr->changeProcessed(); } } void AgentBase::ObserverV3::itemsUnlinked(const Akonadi::Item::List &items, const Collection &collection) { Q_UNUSED(items); Q_UNUSED(collection) if (sAgentBase) { // not implementation, let's disconnect the signal to enable optimizations in Monitor QObject::disconnect(sAgentBase->changeRecorder(), &Monitor::itemsUnlinked, sAgentBase->d_ptr, &AgentBasePrivate::itemsUnlinked); sAgentBase->d_ptr->changeProcessed(); } } void AgentBase::ObserverV4::tagAdded(const Tag &tag) { Q_UNUSED(tag); if (sAgentBase) { // not implementation, let's disconnect the signal to enable optimization in Monitor QObject::disconnect(sAgentBase->changeRecorder(), &Monitor::tagAdded, sAgentBase->d_ptr, &AgentBasePrivate::tagAdded); sAgentBase->d_ptr->changeProcessed(); } } void AgentBase::ObserverV4::tagChanged(const Tag &tag) { Q_UNUSED(tag); if (sAgentBase) { // not implementation, let's disconnect the signal to enable optimization in Monitor QObject::disconnect(sAgentBase->changeRecorder(), &Monitor::tagChanged, sAgentBase->d_ptr, &AgentBasePrivate::tagChanged); sAgentBase->d_ptr->changeProcessed(); } } void AgentBase::ObserverV4::tagRemoved(const Tag &tag) { Q_UNUSED(tag); if (sAgentBase) { // not implementation, let's disconnect the signal to enable optimization in Monitor QObject::disconnect(sAgentBase->changeRecorder(), &Monitor::tagRemoved, sAgentBase->d_ptr, &AgentBasePrivate::tagRemoved); sAgentBase->d_ptr->changeProcessed(); } } void AgentBase::ObserverV4::itemsTagsChanged(const Item::List &items, const QSet &addedTags, const QSet &removedTags) { Q_UNUSED(items); Q_UNUSED(addedTags); Q_UNUSED(removedTags); if (sAgentBase) { // not implementation, let's disconnect the signal to enable optimization in Monitor QObject::disconnect(sAgentBase->changeRecorder(), &Monitor::itemsTagsChanged, sAgentBase->d_ptr, &AgentBasePrivate::itemsTagsChanged); sAgentBase->d_ptr->changeProcessed(); } } void AgentBase::ObserverV4::relationAdded(const Akonadi::Relation &relation) { Q_UNUSED(relation) if (sAgentBase) { // not implementation, let's disconnect the signal to enable optimization in Monitor QObject::disconnect(sAgentBase->changeRecorder(), &Monitor::relationAdded, sAgentBase->d_ptr, &AgentBasePrivate::relationAdded); sAgentBase->d_ptr->changeProcessed(); } } void AgentBase::ObserverV4::relationRemoved(const Akonadi::Relation &relation) { Q_UNUSED(relation) if (sAgentBase) { // not implementation, let's disconnect the signal to enable optimization in Monitor QObject::disconnect(sAgentBase->changeRecorder(), &Monitor::relationRemoved, sAgentBase->d_ptr, &AgentBasePrivate::relationRemoved); sAgentBase->d_ptr->changeProcessed(); } } void AgentBase::ObserverV4::itemsRelationsChanged(const Akonadi::Item::List &items, const Akonadi::Relation::List &addedRelations, const Akonadi::Relation::List &removedRelations) { Q_UNUSED(items) Q_UNUSED(addedRelations) Q_UNUSED(removedRelations) if (sAgentBase) { // not implementation, let's disconnect the signal to enable optimization in Monitor - QObject::disconnect(sAgentBase->changeRecorder(), SIGNAL(itemsRelationsChanged(Akonadi::Item::List,Akonadi::Relation::List,Akonadi::Relation::List)), - sAgentBase, SLOT(itemsRelationsChanged(Akonadi::Item::List,Akonadi::Relation::List,Akonadi::Relation::List))); + disconnect(sAgentBase->changeRecorder(), &Monitor::itemsRelationsChanged, + sAgentBase->d_ptr, &AgentBasePrivate::itemsRelationsChanged); sAgentBase->d_ptr->changeProcessed(); } } //@cond PRIVATE AgentBasePrivate::AgentBasePrivate(AgentBase *parent) : q_ptr(parent) , mStatusCode(AgentBase::Idle) , mProgress(0) , mNeedsNetwork(false) , mOnline(false) , mDesiredOnlineState(false) , mSettings(nullptr) , mChangeRecorder(nullptr) , mTracer(nullptr) , mObserver(nullptr) , mPowerInterface(nullptr) , mTemporaryOfflineTimer(nullptr) , mEventLoopLocker(nullptr) , mNetworkManager(nullptr) { Internal::setClientType(Internal::Agent); } AgentBasePrivate::~AgentBasePrivate() { mChangeRecorder->setConfig(nullptr); delete mSettings; } void AgentBasePrivate::init() { Q_Q(AgentBase); Kdelibs4ConfigMigrator migrate(mId); migrate.setConfigFiles(QStringList() << QStringLiteral("%1rc").arg(mId)); migrate.migrate(); /** * Create a default session for this process. */ SessionPrivate::createDefaultSession(mId.toLatin1()); mTracer = new org::freedesktop::Akonadi::Tracer(ServerManager::serviceName(ServerManager::Server), QStringLiteral("/tracing"), QDBusConnection::sessionBus(), q); new Akonadi__ControlAdaptor(q); new Akonadi__StatusAdaptor(q); if (!QDBusConnection::sessionBus().registerObject(QStringLiteral("/"), q, QDBusConnection::ExportAdaptors)) { Q_EMIT q->error(i18n("Unable to register object at dbus: %1", QDBusConnection::sessionBus().lastError().message())); } mSettings = new QSettings(ServerManager::agentConfigFilePath(mId), QSettings::IniFormat); mChangeRecorder = new ChangeRecorder(q); mChangeRecorder->setObjectName(QStringLiteral("AgentBaseChangeRecorder")); mChangeRecorder->ignoreSession(Session::defaultSession()); mChangeRecorder->itemFetchScope().setCacheOnly(true); mChangeRecorder->setConfig(mSettings); mDesiredOnlineState = mSettings->value(QStringLiteral("Agent/DesiredOnlineState"), true).toBool(); mOnline = mDesiredOnlineState; // reinitialize the status message now that online state is available mStatusMessage = defaultReadyMessage(); mName = mSettings->value(QStringLiteral("Agent/Name")).toString(); if (mName.isEmpty()) { mName = mSettings->value(QStringLiteral("Resource/Name")).toString(); if (!mName.isEmpty()) { mSettings->remove(QStringLiteral("Resource/Name")); mSettings->setValue(QStringLiteral("Agent/Name"), mName); } } - connect(mChangeRecorder, &Monitor::itemAdded, - this, &AgentBasePrivate::itemAdded); - connect(mChangeRecorder, &Monitor::itemChanged, - this, &AgentBasePrivate::itemChanged); - connect(mChangeRecorder, &Monitor::collectionAdded, - this, &AgentBasePrivate::collectionAdded); - connect(mChangeRecorder, SIGNAL(collectionChanged(Akonadi::Collection)), - SLOT(collectionChanged(Akonadi::Collection))); - connect(mChangeRecorder, SIGNAL(collectionChanged(Akonadi::Collection,QSet)), - SLOT(collectionChanged(Akonadi::Collection,QSet))); - connect(mChangeRecorder, &Monitor::collectionMoved, - this, &AgentBasePrivate::collectionMoved); - connect(mChangeRecorder, &Monitor::collectionRemoved, - this, &AgentBasePrivate::collectionRemoved); - connect(mChangeRecorder, &Monitor::collectionSubscribed, - this, &AgentBasePrivate::collectionSubscribed); - connect(mChangeRecorder, &Monitor::collectionUnsubscribed, - this, &AgentBasePrivate::collectionUnsubscribed); - - connect(q, SIGNAL(status(int,QString)), q, SLOT(slotStatus(int,QString))); - connect(q, &AgentBase::percent, q, [this](int value) { slotPercent(value); }); - connect(q, &AgentBase::warning, q, [this](const QString &str) { slotWarning(str); }); - connect(q, &AgentBase::error, q, [this](const QString &str) { slotError(str); }); + connect(mChangeRecorder, &Monitor::itemAdded, this, &AgentBasePrivate::itemAdded); + connect(mChangeRecorder, &Monitor::itemChanged, this, &AgentBasePrivate::itemChanged); + connect(mChangeRecorder, &Monitor::collectionAdded, this, &AgentBasePrivate::collectionAdded); + connect(mChangeRecorder, qOverload(&ChangeRecorder::collectionChanged), + this, qOverload(&AgentBasePrivate::collectionChanged)); + connect(mChangeRecorder, qOverload &>(&ChangeRecorder::collectionChanged), + this, qOverload &>(&AgentBasePrivate::collectionChanged)); + connect(mChangeRecorder, &Monitor::collectionMoved, this, &AgentBasePrivate::collectionMoved); + connect(mChangeRecorder, &Monitor::collectionRemoved, this, &AgentBasePrivate::collectionRemoved); + connect(mChangeRecorder, &Monitor::collectionSubscribed, this, &AgentBasePrivate::collectionSubscribed); + connect(mChangeRecorder, &Monitor::collectionUnsubscribed, this, &AgentBasePrivate::collectionUnsubscribed); + + connect(q, qOverload(&AgentBase::status), this, &AgentBasePrivate::slotStatus); + connect(q, &AgentBase::percent, this, &AgentBasePrivate::slotPercent); + connect(q, &AgentBase::warning, this, &AgentBasePrivate::slotWarning); + connect(q, &AgentBase::error, this, &AgentBasePrivate::slotError); mPowerInterface = new QDBusInterface(QStringLiteral("org.kde.Solid.PowerManagement"), QStringLiteral("/org/kde/Solid/PowerManagement/Actions/SuspendSession"), QStringLiteral("org.kde.Solid.PowerManagement.Actions.SuspendSession"), QDBusConnection::sessionBus(), this); if (mPowerInterface->isValid()) { - connect(mPowerInterface, SIGNAL(resumingFromSuspend()), - q, SLOT(slotResumedFromSuspend())); + connect(mPowerInterface, SIGNAL(resumingFromSuspend()), SLOT(slotResumedFromSuspend())); // clazy:exclude=old-style-connect } else { delete mPowerInterface; mPowerInterface = nullptr; } // Use reference counting to allow agents to finish internal jobs when the // agent is stopped. mEventLoopLocker = new QEventLoopLocker(); mResourceTypeName = AgentManager::self()->instance(mId).type().name(); setProgramName(); QTimer::singleShot(0, q, [this]{ delayedInit();}); } void AgentBasePrivate::delayedInit() { Q_Q(AgentBase); const QString serviceId = ServerManager::agentServiceName(ServerManager::Agent, mId); if (!QDBusConnection::sessionBus().registerService(serviceId)) { qCCritical(AKONADIAGENTBASE_LOG) << "Unable to register service" << serviceId << "at dbus:" << QDBusConnection::sessionBus().lastError().message(); } q->setOnlineInternal(mDesiredOnlineState); QDBusConnection::sessionBus().registerObject(QStringLiteral("/Debug"), this, QDBusConnection::ExportScriptableSlots); } void AgentBasePrivate::setProgramName() { // ugly, really ugly, if you find another solution, change it and blame me for this code (Andras) QString programName = mResourceTypeName; if (!mName.isEmpty()) { programName = i18nc("Name and type of Akonadi resource", "%1 of type %2", mName, mResourceTypeName); } QGuiApplication::setApplicationDisplayName(programName); } void AgentBasePrivate::itemAdded(const Akonadi::Item &item, const Akonadi::Collection &collection) { if (mObserver) { mObserver->itemAdded(item, collection); } else { changeProcessed(); } } void AgentBasePrivate::itemChanged(const Akonadi::Item &item, const QSet &partIdentifiers) { if (mObserver) { mObserver->itemChanged(item, partIdentifiers); } else { changeProcessed(); } } void AgentBasePrivate::itemMoved(const Akonadi::Item &item, const Akonadi::Collection &source, const Akonadi::Collection &dest) { AgentBase::ObserverV2 *observer2 = dynamic_cast(mObserver); if (mObserver) { // inter-resource moves, requires we know which resources the source and destination are in though if (!source.resource().isEmpty() && !dest.resource().isEmpty()) { if (source.resource() != dest.resource()) { if (source.resource() == q_ptr->identifier()) { // moved away from us Akonadi::Item i(item); i.setParentCollection(source); mObserver->itemRemoved(i); } else if (dest.resource() == q_ptr->identifier()) { // moved to us mObserver->itemAdded(item, dest); } else if (observer2) { observer2->itemMoved(item, source, dest); } else { // not for us, not sure if we should get here at all changeProcessed(); } return; } } // intra-resource move if (observer2) { observer2->itemMoved(item, source, dest); } else { // ### we cannot just call itemRemoved here as this will already trigger changeProcessed() // so, just itemAdded() is good enough as no resource can have implemented intra-resource moves anyway // without using ObserverV2 mObserver->itemAdded(item, dest); // mObserver->itemRemoved( item ); } } } void AgentBasePrivate::itemRemoved(const Akonadi::Item &item) { if (mObserver) { mObserver->itemRemoved(item); } else { changeProcessed(); } } void AgentBasePrivate::itemLinked(const Akonadi::Item &item, const Akonadi::Collection &collection) { AgentBase::ObserverV2 *observer2 = dynamic_cast(mObserver); if (observer2) { observer2->itemLinked(item, collection); } else { changeProcessed(); } } void AgentBasePrivate::itemUnlinked(const Akonadi::Item &item, const Akonadi::Collection &collection) { AgentBase::ObserverV2 *observer2 = dynamic_cast(mObserver); if (observer2) { observer2->itemUnlinked(item, collection); } else { changeProcessed(); } } void AgentBasePrivate::itemsFlagsChanged(const Akonadi::Item::List &items, const QSet &addedFlags, const QSet &removedFlags) { AgentBase::ObserverV3 *observer3 = dynamic_cast(mObserver); if (observer3) { observer3->itemsFlagsChanged(items, addedFlags, removedFlags); } else { Q_ASSERT_X(false, Q_FUNC_INFO, "Batch slots must never be called when ObserverV3 is not available"); } } void AgentBasePrivate::itemsMoved(const Akonadi::Item::List &items, const Akonadi::Collection &source, const Akonadi::Collection &destination) { AgentBase::ObserverV3 *observer3 = dynamic_cast(mObserver); if (observer3) { observer3->itemsMoved(items, source, destination); } else { Q_ASSERT_X(false, Q_FUNC_INFO, "Batch slots must never be called when ObserverV3 is not available"); } } void AgentBasePrivate::itemsRemoved(const Akonadi::Item::List &items) { AgentBase::ObserverV3 *observer3 = dynamic_cast(mObserver); if (observer3) { observer3->itemsRemoved(items); } else { Q_ASSERT_X(false, Q_FUNC_INFO, "Batch slots must never be called when ObserverV3 is not available"); } } void AgentBasePrivate::itemsLinked(const Akonadi::Item::List &items, const Akonadi::Collection &collection) { if (!mObserver) { changeProcessed(); return; } AgentBase::ObserverV3 *observer3 = dynamic_cast(mObserver); if (observer3) { observer3->itemsLinked(items, collection); } else { Q_ASSERT_X(false, Q_FUNC_INFO, "Batch slots must never be called when ObserverV3 is not available"); } } void AgentBasePrivate::itemsUnlinked(const Akonadi::Item::List &items, const Akonadi::Collection &collection) { if (!mObserver) { changeProcessed(); return; } AgentBase::ObserverV3 *observer3 = dynamic_cast(mObserver); if (observer3) { observer3->itemsUnlinked(items, collection); } else { Q_ASSERT_X(false, Q_FUNC_INFO, "Batch slots must never be called when ObserverV3 is not available"); } } void AgentBasePrivate::tagAdded(const Akonadi::Tag &tag) { AgentBase::ObserverV4 *observer4 = dynamic_cast(mObserver); if (observer4) { observer4->tagAdded(tag); } else { changeProcessed(); } } void AgentBasePrivate::tagChanged(const Akonadi::Tag &tag) { AgentBase::ObserverV4 *observer4 = dynamic_cast(mObserver); if (observer4) { observer4->tagChanged(tag); } else { changeProcessed(); } } void AgentBasePrivate::tagRemoved(const Akonadi::Tag &tag) { AgentBase::ObserverV4 *observer4 = dynamic_cast(mObserver); if (observer4) { observer4->tagRemoved(tag); } else { changeProcessed(); } } void AgentBasePrivate::itemsTagsChanged(const Akonadi::Item::List &items, const QSet &addedTags, const QSet &removedTags) { AgentBase::ObserverV4 *observer4 = dynamic_cast(mObserver); if (observer4) { observer4->itemsTagsChanged(items, addedTags, removedTags); } else { changeProcessed(); } } void AgentBasePrivate::relationAdded(const Akonadi::Relation &relation) { AgentBase::ObserverV4 *observer4 = dynamic_cast(mObserver); if (observer4) { observer4->relationAdded(relation); } else { changeProcessed(); } } void AgentBasePrivate::relationRemoved(const Akonadi::Relation &relation) { AgentBase::ObserverV4 *observer4 = dynamic_cast(mObserver); if (observer4) { observer4->relationRemoved(relation); } else { changeProcessed(); } } void AgentBasePrivate::itemsRelationsChanged(const Akonadi::Item::List &items, const Akonadi::Relation::List &addedRelations, const Akonadi::Relation::List &removedRelations) { AgentBase::ObserverV4 *observer4 = dynamic_cast(mObserver); if (observer4) { observer4->itemsRelationsChanged(items, addedRelations, removedRelations); } else { changeProcessed(); } } void AgentBasePrivate::collectionAdded(const Akonadi::Collection &collection, const Akonadi::Collection &parent) { if (mObserver) { mObserver->collectionAdded(collection, parent); } else { changeProcessed(); } } void AgentBasePrivate::collectionChanged(const Akonadi::Collection &collection) { AgentBase::ObserverV2 *observer2 = dynamic_cast(mObserver); if (mObserver && observer2 == nullptr) { // For ObserverV2 we use the variant with the part identifiers mObserver->collectionChanged(collection); } else if (!mObserver) { changeProcessed(); } } void AgentBasePrivate::collectionChanged(const Akonadi::Collection &collection, const QSet &changedAttributes) { AgentBase::ObserverV2 *observer2 = dynamic_cast(mObserver); if (observer2) { observer2->collectionChanged(collection, changedAttributes); } else { changeProcessed(); } } void AgentBasePrivate::collectionMoved(const Akonadi::Collection &collection, const Akonadi::Collection &source, const Akonadi::Collection &dest) { AgentBase::ObserverV2 *observer2 = dynamic_cast(mObserver); if (observer2) { observer2->collectionMoved(collection, source, dest); } else if (mObserver) { // ### we cannot just call collectionRemoved here as this will already trigger changeProcessed() // so, just collectionAdded() is good enough as no resource can have implemented intra-resource moves anyway // without using ObserverV2 mObserver->collectionAdded(collection, dest); } else { changeProcessed(); } } void AgentBasePrivate::collectionRemoved(const Akonadi::Collection &collection) { if (mObserver) { mObserver->collectionRemoved(collection); } else { changeProcessed(); } } void AgentBasePrivate::collectionSubscribed(const Akonadi::Collection &collection, const Akonadi::Collection &parent) { Q_UNUSED(collection); Q_UNUSED(parent); changeProcessed(); } void AgentBasePrivate::collectionUnsubscribed(const Akonadi::Collection &collection) { Q_UNUSED(collection); changeProcessed(); } void AgentBasePrivate::changeProcessed() { mChangeRecorder->changeProcessed(); QTimer::singleShot(0, mChangeRecorder, &ChangeRecorder::replayNext); } void AgentBasePrivate::slotStatus(int status, const QString &message) { mStatusMessage = message; mStatusCode = 0; switch (status) { case AgentBase::Idle: if (mStatusMessage.isEmpty()) { mStatusMessage = defaultReadyMessage(); } mStatusCode = 0; break; case AgentBase::Running: if (mStatusMessage.isEmpty()) { mStatusMessage = defaultSyncingMessage(); } mStatusCode = 1; break; case AgentBase::Broken: if (mStatusMessage.isEmpty()) { mStatusMessage = defaultErrorMessage(); } mStatusCode = 2; break; case AgentBase::NotConfigured: if (mStatusMessage.isEmpty()) { mStatusMessage = defaultUnconfiguredMessage(); } mStatusCode = 3; break; default: Q_ASSERT(!"Unknown status passed"); break; } } void AgentBasePrivate::slotPercent(int progress) { mProgress = progress; } void AgentBasePrivate::slotWarning(const QString &message) { mTracer->warning(QStringLiteral("AgentBase(%1)").arg(mId), message); } void AgentBasePrivate::slotError(const QString &message) { mTracer->error(QStringLiteral("AgentBase(%1)").arg(mId), message); } void AgentBasePrivate::slotNetworkStatusChange(bool isOnline) { Q_UNUSED(isOnline); Q_Q(AgentBase); q->setOnlineInternal(mDesiredOnlineState); } void AgentBasePrivate::slotResumedFromSuspend() { if (mNeedsNetwork) { slotNetworkStatusChange(mNetworkManager->isOnline()); } } void AgentBasePrivate::slotTemporaryOfflineTimeout() { Q_Q(AgentBase); q->setOnlineInternal(true); } QString AgentBasePrivate::dumpNotificationListToString() const { return mChangeRecorder->dumpNotificationListToString(); } void AgentBasePrivate::dumpMemoryInfo() const { // Send it to stdout, so we can debug user problems. // since you have to explicitly call this // it won't flood users with release builds. QTextStream stream(stdout); stream << dumpMemoryInfoToString(); } QString AgentBasePrivate::dumpMemoryInfoToString() const { // man mallinfo for more info QString str; #if defined __GLIBC__ struct mallinfo mi; mi = mallinfo(); QTextStream stream(&str); stream << "Total non-mmapped bytes (arena): " << mi.arena << '\n' << "# of free chunks (ordblks): " << mi.ordblks << '\n' << "# of free fastbin blocks (smblks>: " << mi.smblks << '\n' << "# of mapped regions (hblks): " << mi.hblks << '\n' << "Bytes in mapped regions (hblkhd): " << mi.hblkhd << '\n' << "Max. total allocated space (usmblks): " << mi.usmblks << '\n' << "Free bytes held in fastbins (fsmblks):" << mi.fsmblks << '\n' << "Total allocated space (uordblks): " << mi.uordblks << '\n' << "Total free space (fordblks): " << mi.fordblks << '\n' << "Topmost releasable block (keepcost): " << mi.keepcost << '\n'; #else str = QLatin1String("mallinfo() not supported"); #endif return str; } AgentBase::AgentBase(const QString &id) : d_ptr(new AgentBasePrivate(this)) { sAgentBase = this; d_ptr->mId = id; d_ptr->init(); } AgentBase::AgentBase(AgentBasePrivate *d, const QString &id) : d_ptr(d) { sAgentBase = this; d_ptr->mId = id; d_ptr->init(); } AgentBase::~AgentBase() { delete d_ptr; } void AgentBase::debugAgent(int argc, char **argv) { Q_UNUSED(argc); #ifdef Q_OS_WIN if (qEnvironmentVariableIsSet("AKONADI_DEBUG_WAIT")) { if (QByteArray(argv[0]).endsWith(qgetenv("AKONADI_DEBUG_WAIT") + ".exe")) { while (!IsDebuggerPresent()) { std::this_thread::sleep_for(std::chrono::milliseconds(100)); } DebugBreak(); } } #else Q_UNUSED(argv); #endif } QString AgentBase::parseArguments(int argc, char **argv) { Q_UNUSED(argc); QCommandLineOption identifierOption(QStringLiteral("identifier"), i18n("Agent identifier"), QStringLiteral("argument")); QCommandLineParser parser; parser.addOption(identifierOption); parser.addHelpOption(); parser.addVersionOption(); parser.process(*qApp); parser.setApplicationDescription(i18n("Akonadi Agent")); if (!parser.isSet(identifierOption)) { qCDebug(AKONADIAGENTBASE_LOG) << "Identifier argument missing"; exit(1); } const QString identifier = parser.value(identifierOption); if (identifier.isEmpty()) { qCDebug(AKONADIAGENTBASE_LOG) << "Identifier argument is empty"; exit(1); } QCoreApplication::setApplicationName(ServerManager::addNamespace(identifier)); QCoreApplication::setApplicationVersion(QStringLiteral(AKONADI_FULL_VERSION)); const QFileInfo fi(QString::fromLocal8Bit(argv[0])); // strip off full path and possible .exe suffix const QString catalog = fi.baseName(); QTranslator *translator = new QTranslator(); translator->load(catalog); QCoreApplication::installTranslator(translator); return identifier; } // @endcond int AgentBase::init(AgentBase &r) { KLocalizedString::setApplicationDomain("libakonadi5"); KAboutData::setApplicationData(r.aboutData()); return qApp->exec(); } int AgentBase::status() const { Q_D(const AgentBase); return d->mStatusCode; } QString AgentBase::statusMessage() const { Q_D(const AgentBase); return d->mStatusMessage; } int AgentBase::progress() const { Q_D(const AgentBase); return d->mProgress; } QString AgentBase::progressMessage() const { Q_D(const AgentBase); return d->mProgressMessage; } bool AgentBase::isOnline() const { Q_D(const AgentBase); return d->mOnline; } void AgentBase::setNeedsNetwork(bool needsNetwork) { Q_D(AgentBase); if (d->mNeedsNetwork == needsNetwork) { return; } d->mNeedsNetwork = needsNetwork; if (d->mNeedsNetwork) { d->mNetworkManager = new QNetworkConfigurationManager(this); - connect(d->mNetworkManager, SIGNAL(onlineStateChanged(bool)), - this, SLOT(slotNetworkStatusChange(bool)), + connect(d->mNetworkManager, &QNetworkConfigurationManager::onlineStateChanged, d, &AgentBasePrivate::slotNetworkStatusChange, Qt::UniqueConnection); } else { delete d->mNetworkManager; d->mNetworkManager = nullptr; setOnlineInternal(d->mDesiredOnlineState); } } void AgentBase::setOnline(bool state) { Q_D(AgentBase); d->mDesiredOnlineState = state; d->mSettings->setValue(QStringLiteral("Agent/DesiredOnlineState"), state); setOnlineInternal(state); } void AgentBase::setTemporaryOffline(int makeOnlineInSeconds) { Q_D(AgentBase); // if not currently online, avoid bringing it online after the timeout if (!d->mOnline) { return; } setOnlineInternal(false); if (!d->mTemporaryOfflineTimer) { d->mTemporaryOfflineTimer = new QTimer(d); d->mTemporaryOfflineTimer->setSingleShot(true); - connect(d->mTemporaryOfflineTimer, &QTimer::timeout, this, [this]() { d_ptr->slotTemporaryOfflineTimeout(); }); + connect(d->mTemporaryOfflineTimer, &QTimer::timeout, d, &AgentBasePrivate::slotTemporaryOfflineTimeout); } - d->mTemporaryOfflineTimer->setInterval(makeOnlineInSeconds * 1000); + d->mTemporaryOfflineTimer->setInterval(std::chrono::seconds{makeOnlineInSeconds}); d->mTemporaryOfflineTimer->start(); } void AgentBase::setOnlineInternal(bool state) { Q_D(AgentBase); if (state && d->mNeedsNetwork) { if (!d->mNetworkManager->isOnline()) { //Don't go online if the resource needs network but there is none state = false; } } d->mOnline = state; if (d->mTemporaryOfflineTimer) { d->mTemporaryOfflineTimer->stop(); } const QString newMessage = d->defaultReadyMessage(); if (d->mStatusMessage != newMessage && d->mStatusCode != AgentBase::Broken) { Q_EMIT status(d->mStatusCode, newMessage); } doSetOnline(state); Q_EMIT onlineChanged(state); } void AgentBase::doSetOnline(bool online) { Q_UNUSED(online); } KAboutData AgentBase::aboutData() const { return KAboutData(qApp->applicationName(), agentName(), qApp->applicationVersion()); } void AgentBase::configure(WId windowId) { Q_UNUSED(windowId); // Fallback if the agent implements the new plugin-based configuration, // but someone calls the deprecated configure() method auto instance = Akonadi::AgentManager::self()->instance(identifier()); QPointer dialog = new AgentConfigurationDialog(instance, nullptr); if (dialog->exec()) { Q_EMIT configurationDialogAccepted(); } else { Q_EMIT configurationDialogRejected(); } delete dialog; } #ifdef Q_OS_WIN //krazy:exclude=cpp void AgentBase::configure(qlonglong windowId) { configure(static_cast(windowId)); } #endif WId AgentBase::winIdForDialogs() const { const bool registered = QDBusConnection::sessionBus().interface()->isServiceRegistered(QStringLiteral("org.freedesktop.akonaditray")); if (!registered) { return 0; } QDBusInterface dbus(QStringLiteral("org.freedesktop.akonaditray"), QStringLiteral("/Actions"), QStringLiteral("org.freedesktop.Akonadi.Tray")); const QDBusMessage reply = dbus.call(QStringLiteral("getWinId")); if (reply.type() == QDBusMessage::ErrorMessage) { return 0; } const WId winid = (WId)reply.arguments().at(0).toLongLong(); return winid; } void AgentBase::quit() { Q_D(AgentBase); aboutToQuit(); if (d->mSettings) { d->mChangeRecorder->setConfig(nullptr); d->mSettings->sync(); delete d->mSettings; d->mSettings = nullptr; } delete d->mEventLoopLocker; d->mEventLoopLocker = nullptr; } void AgentBase::aboutToQuit() { } void AgentBase::cleanup() { Q_D(AgentBase); // prevent the monitor from picking up deletion signals for our own data if we are a resource // and thus avoid that we kill our own data as last act before our own death d->mChangeRecorder->blockSignals(true); aboutToQuit(); const QString fileName = d->mSettings->fileName(); /* * First destroy the settings object... */ d->mChangeRecorder->setConfig(nullptr); delete d->mSettings; d->mSettings = nullptr; /* * ... then remove the file from hd. */ if (!QFile::remove(fileName)) { qCWarning(AKONADIAGENTBASE_LOG) << "Impossible to remove " << fileName; } /* * ... and remove the changes file from hd. */ const QString changeDataFileName = fileName + QStringLiteral("_changes.dat"); if (!QFile::remove(changeDataFileName)) { qCWarning(AKONADIAGENTBASE_LOG) << "Impossible to remove " << changeDataFileName; } /* * ... and also remove the agent configuration file if there is one. */ const QString configFile = QStandardPaths::writableLocation(QStandardPaths::ConfigLocation) + QLatin1Char('/') + config()->name(); if (!QFile::remove(configFile)) { qCWarning(AKONADIAGENTBASE_LOG) << "Impossible to remove config file " << configFile; } delete d->mEventLoopLocker; d->mEventLoopLocker = nullptr; } void AgentBase::registerObserver(Observer *observer) { // TODO in theory we should re-connect change recorder signals here that we disconnected previously d_ptr->mObserver = observer; const bool hasObserverV3 = (dynamic_cast(d_ptr->mObserver) != nullptr); const bool hasObserverV4 = (dynamic_cast(d_ptr->mObserver) != nullptr); - disconnect(d_ptr->mChangeRecorder, &Monitor::tagAdded, - d_ptr, &AgentBasePrivate::tagAdded); - disconnect(d_ptr->mChangeRecorder, &Monitor::tagChanged, - d_ptr, &AgentBasePrivate::tagChanged); - disconnect(d_ptr->mChangeRecorder, &Monitor::tagRemoved, - d_ptr, &AgentBasePrivate::tagRemoved); - disconnect(d_ptr->mChangeRecorder, &Monitor::itemsTagsChanged, - d_ptr, &AgentBasePrivate::itemsTagsChanged); - disconnect(d_ptr->mChangeRecorder, &Monitor::itemsFlagsChanged, - d_ptr, &AgentBasePrivate::itemsFlagsChanged); - disconnect(d_ptr->mChangeRecorder, &Monitor::itemsMoved, - d_ptr, &AgentBasePrivate::itemsMoved); - disconnect(d_ptr->mChangeRecorder, &Monitor::itemsRemoved, - d_ptr, &AgentBasePrivate::itemsRemoved); - disconnect(d_ptr->mChangeRecorder, &Monitor::itemsLinked, - d_ptr, &AgentBasePrivate::itemsLinked); - disconnect(d_ptr->mChangeRecorder, &Monitor::itemsUnlinked, - d_ptr, &AgentBasePrivate::itemsUnlinked); - disconnect(d_ptr->mChangeRecorder, &Monitor::itemMoved, - d_ptr, &AgentBasePrivate::itemMoved); - disconnect(d_ptr->mChangeRecorder, &Monitor::itemRemoved, - d_ptr, &AgentBasePrivate::itemRemoved); - disconnect(d_ptr->mChangeRecorder, &Monitor::itemLinked, - d_ptr, &AgentBasePrivate::itemLinked); - disconnect(d_ptr->mChangeRecorder, &Monitor::itemUnlinked, - d_ptr, &AgentBasePrivate::itemUnlinked); + disconnect(d_ptr->mChangeRecorder, &Monitor::tagAdded, d_ptr, &AgentBasePrivate::tagAdded); + disconnect(d_ptr->mChangeRecorder, &Monitor::tagChanged, d_ptr, &AgentBasePrivate::tagChanged); + disconnect(d_ptr->mChangeRecorder, &Monitor::tagRemoved, d_ptr, &AgentBasePrivate::tagRemoved); + disconnect(d_ptr->mChangeRecorder, &Monitor::itemsTagsChanged, d_ptr, &AgentBasePrivate::itemsTagsChanged); + disconnect(d_ptr->mChangeRecorder, &Monitor::itemsFlagsChanged, d_ptr, &AgentBasePrivate::itemsFlagsChanged); + disconnect(d_ptr->mChangeRecorder, &Monitor::itemsMoved, d_ptr, &AgentBasePrivate::itemsMoved); + disconnect(d_ptr->mChangeRecorder, &Monitor::itemsRemoved, d_ptr, &AgentBasePrivate::itemsRemoved); + disconnect(d_ptr->mChangeRecorder, &Monitor::itemsLinked, d_ptr, &AgentBasePrivate::itemsLinked); + disconnect(d_ptr->mChangeRecorder, &Monitor::itemsUnlinked, d_ptr, &AgentBasePrivate::itemsUnlinked); + disconnect(d_ptr->mChangeRecorder, &Monitor::itemMoved, d_ptr, &AgentBasePrivate::itemMoved); + disconnect(d_ptr->mChangeRecorder, &Monitor::itemRemoved, d_ptr, &AgentBasePrivate::itemRemoved); + disconnect(d_ptr->mChangeRecorder, &Monitor::itemLinked, d_ptr, &AgentBasePrivate::itemLinked); + disconnect(d_ptr->mChangeRecorder, &Monitor::itemUnlinked, d_ptr, &AgentBasePrivate::itemUnlinked); if (hasObserverV4) { - connect(d_ptr->mChangeRecorder, &Monitor::tagAdded, - d_ptr, &AgentBasePrivate::tagAdded); - connect(d_ptr->mChangeRecorder, &Monitor::tagChanged, - d_ptr, &AgentBasePrivate::tagChanged); - connect(d_ptr->mChangeRecorder, &Monitor::tagRemoved, - d_ptr, &AgentBasePrivate::tagRemoved); - connect(d_ptr->mChangeRecorder, &Monitor::itemsTagsChanged, - d_ptr, &AgentBasePrivate::itemsTagsChanged); + connect(d_ptr->mChangeRecorder, &Monitor::tagAdded, d_ptr, &AgentBasePrivate::tagAdded); + connect(d_ptr->mChangeRecorder, &Monitor::tagChanged, d_ptr, &AgentBasePrivate::tagChanged); + connect(d_ptr->mChangeRecorder, &Monitor::tagRemoved, d_ptr, &AgentBasePrivate::tagRemoved); + connect(d_ptr->mChangeRecorder, &Monitor::itemsTagsChanged, d_ptr, &AgentBasePrivate::itemsTagsChanged); } if (hasObserverV3) { - connect(d_ptr->mChangeRecorder, &Monitor::itemsFlagsChanged, - d_ptr, &AgentBasePrivate::itemsFlagsChanged); - connect(d_ptr->mChangeRecorder, &Monitor::itemsMoved, - d_ptr, &AgentBasePrivate::itemsMoved); - connect(d_ptr->mChangeRecorder, &Monitor::itemsRemoved, - d_ptr, &AgentBasePrivate::itemsRemoved); - connect(d_ptr->mChangeRecorder, &Monitor::itemsLinked, - d_ptr, &AgentBasePrivate::itemsLinked); - connect(d_ptr->mChangeRecorder, &Monitor::itemsUnlinked, - d_ptr, &AgentBasePrivate::itemsUnlinked); + connect(d_ptr->mChangeRecorder, &Monitor::itemsFlagsChanged, d_ptr, &AgentBasePrivate::itemsFlagsChanged); + connect(d_ptr->mChangeRecorder, &Monitor::itemsMoved, d_ptr, &AgentBasePrivate::itemsMoved); + connect(d_ptr->mChangeRecorder, &Monitor::itemsRemoved, d_ptr, &AgentBasePrivate::itemsRemoved); + connect(d_ptr->mChangeRecorder, &Monitor::itemsLinked, d_ptr, &AgentBasePrivate::itemsLinked); + connect(d_ptr->mChangeRecorder, &Monitor::itemsUnlinked, d_ptr, &AgentBasePrivate::itemsUnlinked); } else { // V2 - don't connect these if we have V3 - connect(d_ptr->mChangeRecorder, &Monitor::itemMoved, - d_ptr, &AgentBasePrivate::itemMoved); - connect(d_ptr->mChangeRecorder, &Monitor::itemRemoved, - d_ptr, &AgentBasePrivate::itemRemoved); - connect(d_ptr->mChangeRecorder, &Monitor::itemLinked, - d_ptr, &AgentBasePrivate::itemLinked); - connect(d_ptr->mChangeRecorder, &Monitor::itemUnlinked, - d_ptr, &AgentBasePrivate::itemUnlinked); + connect(d_ptr->mChangeRecorder, &Monitor::itemMoved, d_ptr, &AgentBasePrivate::itemMoved); + connect(d_ptr->mChangeRecorder, &Monitor::itemRemoved, d_ptr, &AgentBasePrivate::itemRemoved); + connect(d_ptr->mChangeRecorder, &Monitor::itemLinked, d_ptr, &AgentBasePrivate::itemLinked); + connect(d_ptr->mChangeRecorder, &Monitor::itemUnlinked, d_ptr, &AgentBasePrivate::itemUnlinked); } } QString AgentBase::identifier() const { return d_ptr->mId; } void AgentBase::setAgentName(const QString &name) { Q_D(AgentBase); if (name == d->mName) { return; } // TODO: rename collection d->mName = name; if (d->mName.isEmpty() || d->mName == d->mId) { d->mSettings->remove(QStringLiteral("Resource/Name")); d->mSettings->remove(QStringLiteral("Agent/Name")); } else { d->mSettings->setValue(QStringLiteral("Agent/Name"), d->mName); } d->mSettings->sync(); d->setProgramName(); Q_EMIT agentNameChanged(d->mName); } QString AgentBase::agentName() const { Q_D(const AgentBase); if (d->mName.isEmpty()) { return d->mId; } else { return d->mName; } } void AgentBase::changeProcessed() { Q_D(AgentBase); d->changeProcessed(); } ChangeRecorder *AgentBase::changeRecorder() const { return d_ptr->mChangeRecorder; } KSharedConfigPtr AgentBase::config() { return KSharedConfig::openConfig(); } void AgentBase::abort() { Q_EMIT abortRequested(); } void AgentBase::reconfigure() { Q_EMIT reloadConfiguration(); } #include "moc_agentbase.cpp" #include "moc_agentbase_p.cpp" diff --git a/src/agentbase/resourcebase.cpp b/src/agentbase/resourcebase.cpp index 0b0412391..dabaa91b8 100644 --- a/src/agentbase/resourcebase.cpp +++ b/src/agentbase/resourcebase.cpp @@ -1,1627 +1,1613 @@ /* Copyright (c) 2006 Till Adam Copyright (c) 2007 Volker Krause 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 "resourcebase.h" #include "agentbase_p.h" #include "resourceadaptor.h" #include "collectiondeletejob.h" #include "collectionsync_p.h" #include #include "itemsync.h" #include "akonadifull-version.h" #include "tagsync.h" #include "relationsync.h" #include "resourcescheduler_p.h" #include "tracerinterface.h" #include "changerecorder.h" #include "collectionfetchjob.h" #include "collectionfetchscope.h" #include "collectionmodifyjob.h" #include "invalidatecachejob_p.h" #include "itemfetchjob.h" #include "itemfetchscope.h" #include "itemmodifyjob.h" #include "itemmodifyjob_p.h" #include "itemcreatejob.h" #include "session.h" #include "resourceselectjob_p.h" #include "monitor_p.h" #include "servermanager_p.h" #include "recursivemover_p.h" #include "tagmodifyjob.h" #include "specialcollectionattribute.h" #include "favoritecollectionattribute.h" #include "akonadiagentbase_debug.h" +#include #include #include #include #include #include #include #include using namespace Akonadi; using namespace AkRanges; class Akonadi::ResourceBasePrivate : public AgentBasePrivate { Q_OBJECT Q_CLASSINFO("D-Bus Interface", "org.kde.dfaure") public: ResourceBasePrivate(ResourceBase *parent) : AgentBasePrivate(parent) , scheduler(nullptr) , mItemSyncer(nullptr) , mItemSyncFetchScope(nullptr) , mItemTransactionMode(ItemSync::SingleTransaction) , mItemMergeMode(ItemSync::RIDMerge) , mCollectionSyncer(nullptr) , mTagSyncer(nullptr) , mRelationSyncer(nullptr) , mHierarchicalRid(false) , mUnemittedProgress(0) , mAutomaticProgressReporting(true) , mDisableAutomaticItemDeliveryDone(false) , mItemSyncBatchSize(10) , mCurrentCollectionFetchJob(nullptr) , mScheduleAttributeSyncBeforeCollectionSync(false) { Internal::setClientType(Internal::Resource); mStatusMessage = defaultReadyMessage(); mProgressEmissionCompressor.setInterval(1000); mProgressEmissionCompressor.setSingleShot(true); // HACK: skip local changes of the EntityDisplayAttribute by default. Remove this for KDE5 and adjust resource implementations accordingly. mKeepLocalCollectionChanges << "ENTITYDISPLAY"; } ~ResourceBasePrivate() override { delete mItemSyncFetchScope; } Q_DECLARE_PUBLIC(ResourceBase) void delayedInit() override { const QString serviceId = ServerManager::agentServiceName(ServerManager::Resource, mId); if (!QDBusConnection::sessionBus().registerService(serviceId)) { QString reason = QDBusConnection::sessionBus().lastError().message(); if (reason.isEmpty()) { reason = QStringLiteral("this service is probably running already."); } qCCritical(AKONADIAGENTBASE_LOG) << "Unable to register service" << serviceId << "at D-Bus:" << reason; if (QThread::currentThread() == QCoreApplication::instance()->thread()) { QCoreApplication::instance()->exit(1); } } else { AgentBasePrivate::delayedInit(); } } void changeProcessed() override { if (m_recursiveMover) { m_recursiveMover->changeProcessed(); QTimer::singleShot(0, m_recursiveMover.data(), &RecursiveMover::replayNext); return; } mChangeRecorder->changeProcessed(); if (!mChangeRecorder->isEmpty()) { scheduler->scheduleChangeReplay(); } scheduler->taskDone(); } void slotAbortRequested(); void slotDeliveryDone(KJob *job); void slotCollectionSyncDone(KJob *job); void slotLocalListDone(KJob *job); void slotSynchronizeCollection(const Collection &col); void slotItemRetrievalCollectionFetchDone(KJob *job); void slotCollectionListDone(KJob *job); void slotSynchronizeCollectionAttributes(const Collection &col); void slotCollectionListForAttributesDone(KJob *job); void slotCollectionAttributesSyncDone(KJob *job); void slotSynchronizeTags(); void slotSynchronizeRelations(); void slotAttributeRetrievalCollectionFetchDone(KJob *job); void slotItemSyncDone(KJob *job); void slotPercent(KJob *job, unsigned long percent); void slotDelayedEmitProgress(); void slotDeleteResourceCollection(); void slotDeleteResourceCollectionDone(KJob *job); void slotCollectionDeletionDone(KJob *job); void slotInvalidateCache(const Akonadi::Collection &collection); void slotPrepareItemRetrieval(const Akonadi::Item &item); void slotPrepareItemRetrievalResult(KJob *job); void slotPrepareItemsRetrieval(const QVector &item); void slotPrepareItemsRetrievalResult(KJob *job); void changeCommittedResult(KJob *job); void slotRecursiveMoveReplay(RecursiveMover *mover); void slotRecursiveMoveReplayResult(KJob *job); void slotTagSyncDone(KJob *job); void slotRelationSyncDone(KJob *job); void slotSessionReconnected() { Q_Q(ResourceBase); new ResourceSelectJob(q->identifier()); } void createItemSyncInstanceIfMissing() { Q_Q(ResourceBase); Q_ASSERT_X(scheduler->currentTask().type == ResourceScheduler::SyncCollection, "createItemSyncInstance", "Calling items retrieval methods although no item retrieval is in progress"); if (!mItemSyncer) { mItemSyncer = new ItemSync(q->currentCollection()); mItemSyncer->setTransactionMode(mItemTransactionMode); mItemSyncer->setBatchSize(mItemSyncBatchSize); mItemSyncer->setMergeMode(mItemMergeMode); if (mItemSyncFetchScope) { mItemSyncer->setFetchScope(*mItemSyncFetchScope); } mItemSyncer->setDisableAutomaticDeliveryDone(mDisableAutomaticItemDeliveryDone); mItemSyncer->setProperty("collection", QVariant::fromValue(q->currentCollection())); - connect(mItemSyncer, SIGNAL(percent(KJob*,ulong)), q, SLOT(slotPercent(KJob*,ulong))); - connect(mItemSyncer, SIGNAL(result(KJob*)), q, SLOT(slotItemSyncDone(KJob*))); + connect(mItemSyncer, qOverload(&KJob::percent), this, &ResourceBasePrivate::slotPercent); + connect(mItemSyncer, &KJob::result, this, &ResourceBasePrivate::slotItemSyncDone); connect(mItemSyncer, &ItemSync::readyForNextBatch, q, &ResourceBase::retrieveNextItemSyncBatch); } Q_ASSERT(mItemSyncer); } public Q_SLOTS: // Dump the state of the scheduler Q_SCRIPTABLE QString dumpToString() const { Q_Q(const ResourceBase); return scheduler->dumpToString() + QLatin1Char('\n') + q->dumpResourceToString(); } Q_SCRIPTABLE void dump() { scheduler->dump(); } Q_SCRIPTABLE void clear() { scheduler->clear(); } protected Q_SLOTS: // reimplementations from AgentbBasePrivate, containing sanity checks that only apply to resources // such as making sure that RIDs are present as well as translations of cross-resource moves // TODO: we could possibly add recovery code for no-RID notifications by re-enquing those to the change recorder // as the corresponding Add notifications, although that contains a risk of endless fail/retry loops void itemAdded(const Akonadi::Item &item, const Akonadi::Collection &collection) override { if (collection.remoteId().isEmpty()) { changeProcessed(); return; } AgentBasePrivate::itemAdded(item, collection); } void itemChanged(const Akonadi::Item &item, const QSet &partIdentifiers) override { if (item.remoteId().isEmpty()) { changeProcessed(); return; } AgentBasePrivate::itemChanged(item, partIdentifiers); } - void itemsFlagsChanged(const Item::List &items, const QSet &addedFlags, + void itemsFlagsChanged(const Akonadi::Item::List &items, const QSet &addedFlags, const QSet &removedFlags) override { if (addedFlags.isEmpty() && removedFlags.isEmpty()) { changeProcessed(); return; } const Item::List validItems = filterValidItems(items); if (validItems.isEmpty()) { changeProcessed(); return; } AgentBasePrivate::itemsFlagsChanged(validItems, addedFlags, removedFlags); } - void itemsTagsChanged(const Item::List &items, const QSet &addedTags, const QSet &removedTags) override + void itemsTagsChanged(const Akonadi::Item::List &items, const QSet &addedTags, const QSet &removedTags) override { if (addedTags.isEmpty() && removedTags.isEmpty()) { changeProcessed(); return; } const Item::List validItems = filterValidItems(items); if (validItems.isEmpty()) { changeProcessed(); return; } AgentBasePrivate::itemsTagsChanged(validItems, addedTags, removedTags); } // TODO move the move translation code from AgentBasePrivate here, it's wrong for agents void itemMoved(const Akonadi::Item &item, const Akonadi::Collection &source, const Akonadi::Collection &destination) override { if (item.remoteId().isEmpty() || destination.remoteId().isEmpty() || destination == source) { changeProcessed(); return; } AgentBasePrivate::itemMoved(item, source, destination); } - void itemsMoved(const Item::List &items, const Collection &source, const Collection &destination) override + void itemsMoved(const Akonadi::Item::List &items, const Akonadi::Collection &source, const Akonadi::Collection &destination) override { if (destination.remoteId().isEmpty() || destination == source) { changeProcessed(); return; } const Item::List validItems = filterValidItems(items); if (validItems.isEmpty()) { changeProcessed(); return; } AgentBasePrivate::itemsMoved(validItems, source, destination); } void itemRemoved(const Akonadi::Item &item) override { if (item.remoteId().isEmpty()) { changeProcessed(); return; } AgentBasePrivate::itemRemoved(item); } - void itemsRemoved(const Item::List &items) override + void itemsRemoved(const Akonadi::Item::List &items) override { const Item::List validItems = filterValidItems(items); if (validItems.isEmpty()) { changeProcessed(); return; } AgentBasePrivate::itemsRemoved(validItems); } void collectionAdded(const Akonadi::Collection &collection, const Akonadi::Collection &parent) override { if (parent.remoteId().isEmpty()) { changeProcessed(); return; } AgentBasePrivate::collectionAdded(collection, parent); } void collectionChanged(const Akonadi::Collection &collection) override { if (collection.remoteId().isEmpty()) { changeProcessed(); return; } AgentBasePrivate::collectionChanged(collection); } void collectionChanged(const Akonadi::Collection &collection, const QSet &partIdentifiers) override { if (collection.remoteId().isEmpty()) { changeProcessed(); return; } AgentBasePrivate::collectionChanged(collection, partIdentifiers); } void collectionMoved(const Akonadi::Collection &collection, const Akonadi::Collection &source, const Akonadi::Collection &destination) override { // unknown destination or source == destination means we can't do/don't have to do anything if (destination.remoteId().isEmpty() || source == destination) { changeProcessed(); return; } // inter-resource moves, requires we know which resources the source and destination are in though if (!source.resource().isEmpty() && !destination.resource().isEmpty() && source.resource() != destination.resource()) { if (source.resource() == q_ptr->identifier()) { // moved away from us AgentBasePrivate::collectionRemoved(collection); } else if (destination.resource() == q_ptr->identifier()) { // moved to us scheduler->taskDone(); // stop change replay for now RecursiveMover *mover = new RecursiveMover(this); mover->setCollection(collection, destination); scheduler->scheduleMoveReplay(collection, mover); } return; } // intra-resource move, requires the moved collection to have a valid id though if (collection.remoteId().isEmpty()) { changeProcessed(); return; } // intra-resource move, ie. something we can handle internally AgentBasePrivate::collectionMoved(collection, source, destination); } void collectionRemoved(const Akonadi::Collection &collection) override { if (collection.remoteId().isEmpty()) { changeProcessed(); return; } AgentBasePrivate::collectionRemoved(collection); } void tagAdded(const Akonadi::Tag &tag) override { if (!tag.isValid()) { changeProcessed(); return; } AgentBasePrivate::tagAdded(tag); } void tagChanged(const Akonadi::Tag &tag) override { if (tag.remoteId().isEmpty()) { changeProcessed(); return; } AgentBasePrivate::tagChanged(tag); } void tagRemoved(const Akonadi::Tag &tag) override { if (tag.remoteId().isEmpty()) { changeProcessed(); return; } AgentBasePrivate::tagRemoved(tag); } private: static Item::List filterValidItems(Item::List items) { items.erase(std::remove_if(items.begin(), items.end(), [](const auto &item) { return item.remoteId().isEmpty(); }), items.end()); return items; } public: // synchronize states Collection currentCollection; ResourceScheduler *scheduler = nullptr; ItemSync *mItemSyncer = nullptr; ItemFetchScope *mItemSyncFetchScope = nullptr; ItemSync::TransactionMode mItemTransactionMode; ItemSync::MergeMode mItemMergeMode; CollectionSync *mCollectionSyncer = nullptr; TagSync *mTagSyncer = nullptr; RelationSync *mRelationSyncer = nullptr; bool mHierarchicalRid; QTimer mProgressEmissionCompressor; int mUnemittedProgress; QMap mUnemittedAdvancedStatus; bool mAutomaticProgressReporting; bool mDisableAutomaticItemDeliveryDone; QPointer m_recursiveMover; int mItemSyncBatchSize; QSet mKeepLocalCollectionChanges; KJob *mCurrentCollectionFetchJob = nullptr; bool mScheduleAttributeSyncBeforeCollectionSync; }; ResourceBase::ResourceBase(const QString &id) : AgentBase(new ResourceBasePrivate(this), id) { Q_D(ResourceBase); qDBusRegisterMetaType(); new Akonadi__ResourceAdaptor(this); d->scheduler = new ResourceScheduler(this); d->mChangeRecorder->setChangeRecordingEnabled(true); d->mChangeRecorder->setCollectionMoveTranslationEnabled(false); // we deal with this ourselves connect(d->mChangeRecorder, &ChangeRecorder::changesAdded, d->scheduler, &ResourceScheduler::scheduleChangeReplay); d->mChangeRecorder->setResourceMonitored(d->mId.toLatin1()); d->mChangeRecorder->fetchCollection(true); - connect(d->scheduler, &ResourceScheduler::executeFullSync, - this, &ResourceBase::retrieveCollections); - connect(d->scheduler, &ResourceScheduler::executeCollectionTreeSync, - this, &ResourceBase::retrieveCollections); - connect(d->scheduler, SIGNAL(executeCollectionSync(Akonadi::Collection)), - SLOT(slotSynchronizeCollection(Akonadi::Collection))); - connect(d->scheduler, SIGNAL(executeCollectionAttributesSync(Akonadi::Collection)), - SLOT(slotSynchronizeCollectionAttributes(Akonadi::Collection))); - connect(d->scheduler, SIGNAL(executeTagSync()), - SLOT(slotSynchronizeTags())); - connect(d->scheduler, SIGNAL(executeRelationSync()), - SLOT(slotSynchronizeRelations())); - connect(d->scheduler, SIGNAL(executeItemFetch(Akonadi::Item,QSet)), - SLOT(slotPrepareItemRetrieval(Akonadi::Item))); - connect(d->scheduler, SIGNAL(executeItemsFetch(QVector,QSet)), - SLOT(slotPrepareItemsRetrieval(QVector))); - connect(d->scheduler, SIGNAL(executeResourceCollectionDeletion()), - SLOT(slotDeleteResourceCollection())); - connect(d->scheduler, SIGNAL(executeCacheInvalidation(Akonadi::Collection)), - SLOT(slotInvalidateCache(Akonadi::Collection))); - connect(d->scheduler, SIGNAL(status(int,QString)), - SIGNAL(status(int,QString))); - connect(d->scheduler, &ResourceScheduler::executeChangeReplay, - d->mChangeRecorder, &ChangeRecorder::replayNext); - connect(d->scheduler, SIGNAL(executeRecursiveMoveReplay(RecursiveMover*)), - SLOT(slotRecursiveMoveReplay(RecursiveMover*))); + connect(d->scheduler, &ResourceScheduler::executeFullSync, this, &ResourceBase::retrieveCollections); + connect(d->scheduler, &ResourceScheduler::executeCollectionTreeSync, this, &ResourceBase::retrieveCollections); + connect(d->scheduler, &ResourceScheduler::executeCollectionSync, d, &ResourceBasePrivate::slotSynchronizeCollection); + connect(d->scheduler, &ResourceScheduler::executeCollectionAttributesSync, d, &ResourceBasePrivate::slotSynchronizeCollectionAttributes); + connect(d->scheduler, &ResourceScheduler::executeTagSync, d, &ResourceBasePrivate::slotSynchronizeTags); + connect(d->scheduler, &ResourceScheduler::executeRelationSync, d, &ResourceBasePrivate::slotSynchronizeRelations); + connect(d->scheduler, &ResourceScheduler::executeItemFetch, d, &ResourceBasePrivate::slotPrepareItemRetrieval); + connect(d->scheduler, &ResourceScheduler::executeItemsFetch, d, &ResourceBasePrivate::slotPrepareItemsRetrieval); + connect(d->scheduler, &ResourceScheduler::executeResourceCollectionDeletion, d, &ResourceBasePrivate::slotDeleteResourceCollection); + connect(d->scheduler, &ResourceScheduler::executeCacheInvalidation, d, &ResourceBasePrivate::slotInvalidateCache); + connect(d->scheduler, &ResourceScheduler::status, this, qOverload(&ResourceBase::status)); + connect(d->scheduler, &ResourceScheduler::executeChangeReplay, d->mChangeRecorder, &ChangeRecorder::replayNext); + connect(d->scheduler, &ResourceScheduler::executeRecursiveMoveReplay, d, &ResourceBasePrivate::slotRecursiveMoveReplay); connect(d->scheduler, &ResourceScheduler::fullSyncComplete, this, &ResourceBase::synchronized); connect(d->scheduler, &ResourceScheduler::collectionTreeSyncComplete, this, &ResourceBase::collectionTreeSynchronized); connect(d->mChangeRecorder, &ChangeRecorder::nothingToReplay, d->scheduler, &ResourceScheduler::taskDone); - connect(d->mChangeRecorder, &Monitor::collectionRemoved, - d->scheduler, &ResourceScheduler::collectionRemoved); - connect(this, SIGNAL(abortRequested()), this, SLOT(slotAbortRequested())); + connect(d->mChangeRecorder, &Monitor::collectionRemoved, d->scheduler, &ResourceScheduler::collectionRemoved); + connect(this, &ResourceBase::abortRequested, d, &ResourceBasePrivate::slotAbortRequested); connect(this, &ResourceBase::synchronized, d->scheduler, &ResourceScheduler::taskDone); connect(this, &ResourceBase::collectionTreeSynchronized, d->scheduler, &ResourceScheduler::taskDone); - connect(this, &AgentBase::agentNameChanged, - this, &ResourceBase::nameChanged); - - connect(&d->mProgressEmissionCompressor, SIGNAL(timeout()), - this, SLOT(slotDelayedEmitProgress())); + connect(this, &AgentBase::agentNameChanged, this, &ResourceBase::nameChanged); + connect(&d->mProgressEmissionCompressor, &QTimer::timeout, d, &ResourceBasePrivate::slotDelayedEmitProgress); d->scheduler->setOnline(d->mOnline); if (!d->mChangeRecorder->isEmpty()) { d->scheduler->scheduleChangeReplay(); } new ResourceSelectJob(identifier()); - connect(d->mChangeRecorder->session(), SIGNAL(reconnected()), SLOT(slotSessionReconnected())); + connect(d->mChangeRecorder->session(), &Session::reconnected, d, &ResourceBasePrivate::slotSessionReconnected); } ResourceBase::~ResourceBase() = default; void ResourceBase::synchronize() { d_func()->scheduler->scheduleFullSync(); } void ResourceBase::setName(const QString &name) { AgentBase::setAgentName(name); } QString ResourceBase::name() const { return AgentBase::agentName(); } QString ResourceBase::parseArguments(int argc, char **argv) { Q_UNUSED(argc); QCommandLineOption identifierOption(QStringLiteral("identifier"), i18nc("@label command line option", "Resource identifier"), QStringLiteral("argument")); QCommandLineParser parser; parser.addOption(identifierOption); parser.addHelpOption(); parser.addVersionOption(); parser.process(*qApp); parser.setApplicationDescription(i18n("Akonadi Resource")); if (!parser.isSet(identifierOption)) { qCDebug(AKONADIAGENTBASE_LOG) << "Identifier argument missing"; exit(1); } const QString identifier = parser.value(identifierOption); if (identifier.isEmpty()) { qCDebug(AKONADIAGENTBASE_LOG) << "Identifier is empty"; exit(1); } QCoreApplication::setApplicationName(ServerManager::addNamespace(identifier)); QCoreApplication::setApplicationVersion(QStringLiteral(AKONADI_FULL_VERSION)); const QFileInfo fi(QString::fromLocal8Bit(argv[0])); // strip off full path and possible .exe suffix const QString catalog = fi.baseName(); QTranslator *translator = new QTranslator(); translator->load(catalog); QCoreApplication::installTranslator(translator); return identifier; } int ResourceBase::init(ResourceBase &r) { KLocalizedString::setApplicationDomain("libakonadi5"); KAboutData::setApplicationData(r.aboutData()); return qApp->exec(); } void ResourceBasePrivate::slotAbortRequested() { Q_Q(ResourceBase); scheduler->cancelQueues(); q->abortActivity(); } void ResourceBase::itemRetrieved(const Item &item) { Q_D(ResourceBase); Q_ASSERT(d->scheduler->currentTask().type == ResourceScheduler::FetchItem); if (!item.isValid()) { d->scheduler->itemFetchDone(i18nc("@info", "Invalid item retrieved")); return; } Item i(item); const QSet requestedParts = d->scheduler->currentTask().itemParts; for (const QByteArray &part : requestedParts) { if (!item.loadedPayloadParts().contains(part)) { qCWarning(AKONADIAGENTBASE_LOG) << "Item does not provide part" << part; } } ItemModifyJob *job = new ItemModifyJob(i); job->d_func()->setSilent(true); // FIXME: remove once the item with which we call retrieveItem() has a revision number job->disableRevisionCheck(); - connect(job, SIGNAL(result(KJob*)), SLOT(slotDeliveryDone(KJob*))); + connect(job, &KJob::result, d, &ResourceBasePrivate::slotDeliveryDone); } void ResourceBasePrivate::slotDeliveryDone(KJob *job) { Q_Q(ResourceBase); Q_ASSERT(scheduler->currentTask().type == ResourceScheduler::FetchItem); if (job->error()) { Q_EMIT q->error(i18nc("@info", "Error while creating item: %1", job->errorString())); } scheduler->itemFetchDone(QString()); } void ResourceBase::collectionAttributesRetrieved(const Collection &collection) { Q_D(ResourceBase); Q_ASSERT(d->scheduler->currentTask().type == ResourceScheduler::SyncCollectionAttributes); if (!collection.isValid()) { Q_EMIT attributesSynchronized(d->scheduler->currentTask().collection.id()); d->scheduler->taskDone(); return; } CollectionModifyJob *job = new CollectionModifyJob(collection); - connect(job, SIGNAL(result(KJob*)), SLOT(slotCollectionAttributesSyncDone(KJob*))); + connect(job, &KJob::result, d, &ResourceBasePrivate::slotCollectionAttributesSyncDone); } void ResourceBasePrivate::slotCollectionAttributesSyncDone(KJob *job) { Q_Q(ResourceBase); Q_ASSERT(scheduler->currentTask().type == ResourceScheduler::SyncCollectionAttributes); if (job->error()) { Q_EMIT q->error(i18nc("@info", "Error while updating collection: %1", job->errorString())); } Q_EMIT q->attributesSynchronized(scheduler->currentTask().collection.id()); scheduler->taskDone(); } void ResourceBasePrivate::slotDeleteResourceCollection() { Q_Q(ResourceBase); CollectionFetchJob *job = new CollectionFetchJob(Collection::root(), CollectionFetchJob::FirstLevel); job->fetchScope().setResource(q->identifier()); - connect(job, SIGNAL(result(KJob*)), q, SLOT(slotDeleteResourceCollectionDone(KJob*))); + connect(job, &KJob::result, this, &ResourceBasePrivate::slotDeleteResourceCollectionDone); } void ResourceBasePrivate::slotDeleteResourceCollectionDone(KJob *job) { Q_Q(ResourceBase); if (job->error()) { Q_EMIT q->error(job->errorString()); scheduler->taskDone(); } else { const CollectionFetchJob *fetchJob = static_cast(job); if (!fetchJob->collections().isEmpty()) { CollectionDeleteJob *job = new CollectionDeleteJob(fetchJob->collections().at(0)); - connect(job, SIGNAL(result(KJob*)), q, SLOT(slotCollectionDeletionDone(KJob*))); + connect(job, &KJob::result, this, &ResourceBasePrivate::slotCollectionDeletionDone); } else { // there is no resource collection, so just ignore the request scheduler->taskDone(); } } } void ResourceBasePrivate::slotCollectionDeletionDone(KJob *job) { Q_Q(ResourceBase); if (job->error()) { Q_EMIT q->error(job->errorString()); } scheduler->taskDone(); } void ResourceBasePrivate::slotInvalidateCache(const Akonadi::Collection &collection) { Q_Q(ResourceBase); - InvalidateCacheJob *job = new InvalidateCacheJob(collection, q); - connect(job, &KJob::result, scheduler, &ResourceScheduler::taskDone); + new InvalidateCacheJob(collection, q); } void ResourceBase::changeCommitted(const Item &item) { changesCommitted(Item::List() << item); } void ResourceBase::changesCommitted(const Item::List &items) { + Q_D(ResourceBase); TransactionSequence *transaction = new TransactionSequence(this); - connect(transaction, SIGNAL(finished(KJob*)), - this, SLOT(changeCommittedResult(KJob*))); + connect(transaction, &KJob::finished, d, &ResourceBasePrivate::changeCommittedResult); // Modify the items one-by-one, because STORE does not support mass RID change for (const Item &item : items) { ItemModifyJob *job = new ItemModifyJob(item, transaction); job->d_func()->setClean(); job->disableRevisionCheck(); // TODO: remove, but where/how do we handle the error? job->setIgnorePayload(true); // we only want to reset the dirty flag and update the remote id } } void ResourceBase::changeCommitted(const Collection &collection) { + Q_D(ResourceBase); CollectionModifyJob *job = new CollectionModifyJob(collection); - connect(job, SIGNAL(result(KJob*)), SLOT(changeCommittedResult(KJob*))); + connect(job, &KJob::result, d, &ResourceBasePrivate::changeCommittedResult); } void ResourceBasePrivate::changeCommittedResult(KJob *job) { if (job->error()) { qCWarning(AKONADIAGENTBASE_LOG) << job->errorText(); } Q_Q(ResourceBase); if (qobject_cast(job)) { if (job->error()) { Q_EMIT q->error(i18nc("@info", "Updating local collection failed: %1.", job->errorText())); } mChangeRecorder->d_ptr->invalidateCache(static_cast(job)->collection()); } else { if (job->error()) { Q_EMIT q->error(i18nc("@info", "Updating local items failed: %1.", job->errorText())); } // Item and tag cache is invalidated by modify job } changeProcessed(); } void ResourceBase::changeCommitted(const Tag &tag) { + Q_D(ResourceBase); TagModifyJob *job = new TagModifyJob(tag); - connect(job, SIGNAL(result(KJob*)), SLOT(changeCommittedResult(KJob*))); + connect(job, &KJob::result, d, &ResourceBasePrivate::changeCommittedResult); } void ResourceBase::requestItemDelivery(const QVector &uids, const QByteArrayList &parts) { Q_D(ResourceBase); if (!isOnline()) { const QString errorMsg = i18nc("@info", "Cannot fetch item in offline mode."); sendErrorReply(QDBusError::Failed, errorMsg); Q_EMIT error(errorMsg); return; } setDelayedReply(true); const auto items = uids | Views::transform([](const auto uid) { return Item{uid}; }) | Actions::toQVector; d->scheduler->scheduleItemsFetch(items, QSet::fromList(parts), message()); } void ResourceBase::collectionsRetrieved(const Collection::List &collections) { Q_D(ResourceBase); Q_ASSERT_X(d->scheduler->currentTask().type == ResourceScheduler::SyncCollectionTree || d->scheduler->currentTask().type == ResourceScheduler::SyncAll, "ResourceBase::collectionsRetrieved()", "Calling collectionsRetrieved() although no collection retrieval is in progress"); if (!d->mCollectionSyncer) { d->mCollectionSyncer = new CollectionSync(identifier()); d->mCollectionSyncer->setHierarchicalRemoteIds(d->mHierarchicalRid); d->mCollectionSyncer->setKeepLocalChanges(d->mKeepLocalCollectionChanges); - connect(d->mCollectionSyncer, SIGNAL(percent(KJob*,ulong)), SLOT(slotPercent(KJob*,ulong))); - connect(d->mCollectionSyncer, SIGNAL(result(KJob*)), SLOT(slotCollectionSyncDone(KJob*))); + connect(d->mCollectionSyncer, qOverload(&KJob::percent), d, &ResourceBasePrivate::slotPercent); + connect(d->mCollectionSyncer, &KJob::result, d, &ResourceBasePrivate::slotCollectionSyncDone); } d->mCollectionSyncer->setRemoteCollections(collections); } void ResourceBase::collectionsRetrievedIncremental(const Collection::List &changedCollections, const Collection::List &removedCollections) { Q_D(ResourceBase); Q_ASSERT_X(d->scheduler->currentTask().type == ResourceScheduler::SyncCollectionTree || d->scheduler->currentTask().type == ResourceScheduler::SyncAll, "ResourceBase::collectionsRetrievedIncremental()", "Calling collectionsRetrievedIncremental() although no collection retrieval is in progress"); if (!d->mCollectionSyncer) { d->mCollectionSyncer = new CollectionSync(identifier()); d->mCollectionSyncer->setHierarchicalRemoteIds(d->mHierarchicalRid); d->mCollectionSyncer->setKeepLocalChanges(d->mKeepLocalCollectionChanges); - connect(d->mCollectionSyncer, SIGNAL(percent(KJob*,ulong)), SLOT(slotPercent(KJob*,ulong))); - connect(d->mCollectionSyncer, SIGNAL(result(KJob*)), SLOT(slotCollectionSyncDone(KJob*))); + connect(d->mCollectionSyncer, qOverload(&KJob::percent), d, &ResourceBasePrivate::slotPercent); + connect(d->mCollectionSyncer, &KJob::result, d, &ResourceBasePrivate::slotCollectionSyncDone); } d->mCollectionSyncer->setRemoteCollections(changedCollections, removedCollections); } void ResourceBase::setCollectionStreamingEnabled(bool enable) { Q_D(ResourceBase); Q_ASSERT_X(d->scheduler->currentTask().type == ResourceScheduler::SyncCollectionTree || d->scheduler->currentTask().type == ResourceScheduler::SyncAll, "ResourceBase::setCollectionStreamingEnabled()", "Calling setCollectionStreamingEnabled() although no collection retrieval is in progress"); if (!d->mCollectionSyncer) { d->mCollectionSyncer = new CollectionSync(identifier()); d->mCollectionSyncer->setHierarchicalRemoteIds(d->mHierarchicalRid); - connect(d->mCollectionSyncer, SIGNAL(percent(KJob*,ulong)), SLOT(slotPercent(KJob*,ulong))); - connect(d->mCollectionSyncer, SIGNAL(result(KJob*)), SLOT(slotCollectionSyncDone(KJob*))); + connect(d->mCollectionSyncer, qOverload(&KJob::percent), d, &ResourceBasePrivate::slotPercent); + connect(d->mCollectionSyncer, &KJob::result, d, &ResourceBasePrivate::slotCollectionSyncDone); } d->mCollectionSyncer->setStreamingEnabled(enable); } void ResourceBase::collectionsRetrievalDone() { Q_D(ResourceBase); Q_ASSERT_X(d->scheduler->currentTask().type == ResourceScheduler::SyncCollectionTree || d->scheduler->currentTask().type == ResourceScheduler::SyncAll, "ResourceBase::collectionsRetrievalDone()", "Calling collectionsRetrievalDone() although no collection retrieval is in progress"); // streaming enabled, so finalize the sync if (d->mCollectionSyncer) { d->mCollectionSyncer->retrievalDone(); } else { // user did the sync himself, we are done now // FIXME: we need the same special case for SyncAll as in slotCollectionSyncDone here! d->scheduler->taskDone(); } } void ResourceBase::setKeepLocalCollectionChanges(const QSet &parts) { Q_D(ResourceBase); d->mKeepLocalCollectionChanges = parts; } void ResourceBasePrivate::slotCollectionSyncDone(KJob *job) { Q_Q(ResourceBase); mCollectionSyncer = nullptr; if (job->error()) { if (job->error() != Job::UserCanceled) { Q_EMIT q->error(job->errorString()); } } else { if (scheduler->currentTask().type == ResourceScheduler::SyncAll) { CollectionFetchJob *list = new CollectionFetchJob(Collection::root(), CollectionFetchJob::Recursive); list->setFetchScope(q->changeRecorder()->collectionFetchScope()); list->fetchScope().fetchAttribute(); list->fetchScope().fetchAttribute(); list->fetchScope().setResource(mId); list->fetchScope().setListFilter(CollectionFetchScope::Sync); - q->connect(list, SIGNAL(result(KJob*)), q, SLOT(slotLocalListDone(KJob*))); + connect(list, &KJob::result, this, &ResourceBasePrivate::slotLocalListDone); return; } else if (scheduler->currentTask().type == ResourceScheduler::SyncCollectionTree) { scheduler->scheduleCollectionTreeSyncCompletion(); } } scheduler->taskDone(); } namespace { bool sortCollectionsForSync(const Collection &l, const Collection &r) { const auto lType = l.hasAttribute() ? l.attribute()->collectionType() : QByteArray(); const bool lInbox = (lType == "inbox") || (l.remoteId().midRef(1).compare(QLatin1String("inbox"), Qt::CaseInsensitive) == 0); const bool lFav = l.hasAttribute(); const auto rType = r.hasAttribute() ? r.attribute()->collectionType() : QByteArray(); const bool rInbox = (rType == "inbox") || (r.remoteId().midRef(1).compare(QLatin1String("inbox"), Qt::CaseInsensitive) == 0); const bool rFav = r.hasAttribute(); // inbox is always first if (lInbox) { return true; } else if (rInbox) { return false; } // favorites right after inbox if (lFav) { return !rInbox; } else if (rFav) { return lInbox; } // trash is always last (unless it's favorite) if (lType == "trash") { return false; } else if (rType == "trash") { return true; } // Fallback to sorting by id return l.id() < r.id(); } } void ResourceBasePrivate::slotLocalListDone(KJob *job) { Q_Q(ResourceBase); if (job->error()) { Q_EMIT q->error(job->errorString()); } else { Collection::List cols = static_cast(job)->collections(); std::sort(cols.begin(), cols.end(), sortCollectionsForSync); for (const Collection &col : qAsConst(cols)) { scheduler->scheduleSync(col); } scheduler->scheduleFullSyncCompletion(); } scheduler->taskDone(); } void ResourceBasePrivate::slotSynchronizeCollection(const Collection &col) { Q_Q(ResourceBase); currentCollection = col; // This can happen due to FetchHelper::triggerOnDemandFetch() in the akonadi server (not an error). if (!col.remoteId().isEmpty()) { // check if this collection actually can contain anything QStringList contentTypes = currentCollection.contentMimeTypes(); contentTypes.removeAll(Collection::mimeType()); contentTypes.removeAll(Collection::virtualMimeType()); if (!contentTypes.isEmpty() || col.isVirtual()) { if (mAutomaticProgressReporting) { Q_EMIT q->status(AgentBase::Running, i18nc("@info:status", "Syncing folder '%1'", currentCollection.displayName())); } qCDebug(AKONADIAGENTBASE_LOG) << "Preparing collection sync of collection" << currentCollection.id() << currentCollection.displayName(); Akonadi::CollectionFetchJob *fetchJob = new Akonadi::CollectionFetchJob(col, CollectionFetchJob::Base, this); fetchJob->setFetchScope(q->changeRecorder()->collectionFetchScope()); - connect(fetchJob, SIGNAL(result(KJob*)), q, SLOT(slotItemRetrievalCollectionFetchDone(KJob*))); + connect(fetchJob, &KJob::result, this, &ResourceBasePrivate::slotItemRetrievalCollectionFetchDone); mCurrentCollectionFetchJob = fetchJob; return; } } scheduler->taskDone(); } void ResourceBasePrivate::slotItemRetrievalCollectionFetchDone(KJob *job) { Q_Q(ResourceBase); mCurrentCollectionFetchJob = nullptr; if (job->error()) { qCWarning(AKONADIAGENTBASE_LOG) << "Failed to retrieve collection for sync: " << job->errorString(); q->cancelTask(i18n("Failed to retrieve collection for sync.")); return; } Akonadi::CollectionFetchJob *fetchJob = static_cast(job); const Collection::List collections = fetchJob->collections(); if (collections.isEmpty()) { qCWarning(AKONADIAGENTBASE_LOG) << "The fetch job returned empty collection set. This is unexpected."; q->cancelTask(i18n("Failed to retrieve collection for sync.")); return; } q->retrieveItems(collections.at(0)); } int ResourceBase::itemSyncBatchSize() const { Q_D(const ResourceBase); return d->mItemSyncBatchSize; } void ResourceBase::setItemSyncBatchSize(int batchSize) { Q_D(ResourceBase); d->mItemSyncBatchSize = batchSize; } void ResourceBase::setScheduleAttributeSyncBeforeItemSync(bool enable) { Q_D(ResourceBase); d->mScheduleAttributeSyncBeforeCollectionSync = enable; } void ResourceBasePrivate::slotSynchronizeCollectionAttributes(const Collection &col) { Q_Q(ResourceBase); Akonadi::CollectionFetchJob *fetchJob = new Akonadi::CollectionFetchJob(col, CollectionFetchJob::Base, this); fetchJob->setFetchScope(q->changeRecorder()->collectionFetchScope()); - connect(fetchJob, SIGNAL(result(KJob*)), q, SLOT(slotAttributeRetrievalCollectionFetchDone(KJob*))); + connect(fetchJob, &KJob::result, this, &ResourceBasePrivate::slotAttributeRetrievalCollectionFetchDone); Q_ASSERT(!mCurrentCollectionFetchJob); mCurrentCollectionFetchJob = fetchJob; } void ResourceBasePrivate::slotAttributeRetrievalCollectionFetchDone(KJob *job) { mCurrentCollectionFetchJob = nullptr; Q_Q(ResourceBase); if (job->error()) { qCWarning(AKONADIAGENTBASE_LOG) << "Failed to retrieve collection for attribute sync: " << job->errorString(); q->cancelTask(i18n("Failed to retrieve collection for attribute sync.")); return; } Akonadi::CollectionFetchJob *fetchJob = static_cast(job); + // FIXME: Why not call q-> directly? QMetaObject::invokeMethod(q, "retrieveCollectionAttributes", Q_ARG(Akonadi::Collection, fetchJob->collections().at(0))); } void ResourceBasePrivate::slotSynchronizeTags() { Q_Q(ResourceBase); QMetaObject::invokeMethod(this, [q] { q->retrieveTags(); }); } void ResourceBasePrivate::slotSynchronizeRelations() { Q_Q(ResourceBase); QMetaObject::invokeMethod(this, [q] { q->retrieveRelations(); }); } void ResourceBasePrivate::slotPrepareItemRetrieval(const Item &item) { Q_Q(ResourceBase); auto fetch = new ItemFetchJob(item, this); // we always need at least parent so we can use ItemCreateJob to merge fetch->fetchScope().setAncestorRetrieval(qMax(ItemFetchScope::Parent, q->changeRecorder()->itemFetchScope().ancestorRetrieval())); fetch->fetchScope().setCacheOnly(true); fetch->fetchScope().setFetchRemoteIdentification(true); // copy list of attributes to fetch const QSet attributes = q->changeRecorder()->itemFetchScope().attributes(); for (const auto &attribute : attributes) { fetch->fetchScope().fetchAttribute(attribute); } - q->connect(fetch, SIGNAL(result(KJob*)), SLOT(slotPrepareItemRetrievalResult(KJob*))); + connect(fetch, &KJob::result, this, &ResourceBasePrivate::slotPrepareItemRetrievalResult); } void ResourceBasePrivate::slotPrepareItemRetrievalResult(KJob *job) { Q_Q(ResourceBase); Q_ASSERT_X(scheduler->currentTask().type == ResourceScheduler::FetchItem, "ResourceBasePrivate::slotPrepareItemRetrievalResult()", "Preparing item retrieval although no item retrieval is in progress"); if (job->error()) { q->cancelTask(job->errorText()); return; } ItemFetchJob *fetch = qobject_cast(job); if (fetch->items().count() != 1) { q->cancelTask(i18n("The requested item no longer exists")); return; } const QSet parts = scheduler->currentTask().itemParts; if (!q->retrieveItem(fetch->items().at(0), parts)) { q->cancelTask(); } } void ResourceBasePrivate::slotPrepareItemsRetrieval(const QVector &items) { Q_Q(ResourceBase); ItemFetchJob *fetch = new ItemFetchJob(items, this); // we always need at least parent so we can use ItemCreateJob to merge fetch->fetchScope().setAncestorRetrieval(qMax(ItemFetchScope::Parent, q->changeRecorder()->itemFetchScope().ancestorRetrieval())); fetch->fetchScope().setCacheOnly(true); fetch->fetchScope().setFetchRemoteIdentification(true); // It's possible that one or more items were removed before this task was // executed, so ignore it and just handle the rest. fetch->fetchScope().setIgnoreRetrievalErrors(true); // copy list of attributes to fetch const QSet attributes = q->changeRecorder()->itemFetchScope().attributes(); for (const auto &attribute : attributes) { fetch->fetchScope().fetchAttribute(attribute); } - q->connect(fetch, SIGNAL(result(KJob*)), SLOT(slotPrepareItemsRetrievalResult(KJob*))); + connect(fetch, &KJob::result, this, &ResourceBasePrivate::slotPrepareItemsRetrievalResult); } void ResourceBasePrivate::slotPrepareItemsRetrievalResult(KJob *job) { Q_Q(ResourceBase); Q_ASSERT_X(scheduler->currentTask().type == ResourceScheduler::FetchItems, "ResourceBasePrivate::slotPrepareItemsRetrievalResult()", "Preparing items retrieval although no items retrieval is in progress"); if (job->error()) { q->cancelTask(job->errorText()); return; } ItemFetchJob *fetch = qobject_cast(job); const auto items = fetch->items(); if (items.isEmpty()) { q->cancelTask(); return; } const QSet parts = scheduler->currentTask().itemParts; Q_ASSERT(items.first().parentCollection().isValid()); if (!q->retrieveItems(items, parts)) { q->cancelTask(); } } void ResourceBasePrivate::slotRecursiveMoveReplay(RecursiveMover *mover) { - Q_Q(ResourceBase); Q_ASSERT(mover); Q_ASSERT(!m_recursiveMover); m_recursiveMover = mover; - connect(mover, SIGNAL(result(KJob*)), q, SLOT(slotRecursiveMoveReplayResult(KJob*))); + connect(mover, &KJob::result, this, &ResourceBasePrivate::slotRecursiveMoveReplayResult); mover->start(); } void ResourceBasePrivate::slotRecursiveMoveReplayResult(KJob *job) { Q_Q(ResourceBase); m_recursiveMover = nullptr; if (job->error()) { q->deferTask(); return; } changeProcessed(); } void ResourceBase::itemsRetrievalDone() { Q_D(ResourceBase); // streaming enabled, so finalize the sync if (d->mItemSyncer) { d->mItemSyncer->deliveryDone(); } else { if (d->scheduler->currentTask().type == ResourceScheduler::FetchItems) { d->scheduler->currentTask().sendDBusReplies(QString()); } // user did the sync himself, we are done now d->scheduler->taskDone(); } } void ResourceBase::clearCache() { Q_D(ResourceBase); d->scheduler->scheduleResourceCollectionDeletion(); } void ResourceBase::invalidateCache(const Collection &collection) { Q_D(ResourceBase); d->scheduler->scheduleCacheInvalidation(collection); } Collection ResourceBase::currentCollection() const { Q_D(const ResourceBase); Q_ASSERT_X(d->scheduler->currentTask().type == ResourceScheduler::SyncCollection, "ResourceBase::currentCollection()", "Trying to access current collection although no item retrieval is in progress"); return d->currentCollection; } Item ResourceBase::currentItem() const { Q_D(const ResourceBase); Q_ASSERT_X(d->scheduler->currentTask().type == ResourceScheduler::FetchItem, "ResourceBase::currentItem()", "Trying to access current item although no item retrieval is in progress"); return d->scheduler->currentTask().items[0]; } Item::List ResourceBase::currentItems() const { Q_D(const ResourceBase); Q_ASSERT_X(d->scheduler->currentTask().type == ResourceScheduler::FetchItems, "ResourceBase::currentItems()", "Trying to access current items although no items retrieval is in progress"); return d->scheduler->currentTask().items; } void ResourceBase::synchronizeCollectionTree() { d_func()->scheduler->scheduleCollectionTreeSync(); } void ResourceBase::synchronizeTags() { d_func()->scheduler->scheduleTagSync(); } void ResourceBase::synchronizeRelations() { d_func()->scheduler->scheduleRelationSync(); } void ResourceBase::cancelTask() { Q_D(ResourceBase); if (d->mCurrentCollectionFetchJob) { d->mCurrentCollectionFetchJob->kill(); d->mCurrentCollectionFetchJob = nullptr; } switch (d->scheduler->currentTask().type) { case ResourceScheduler::FetchItem: itemRetrieved(Item()); // sends the error reply and break; case ResourceScheduler::FetchItems: itemsRetrieved(Item::List()); break; case ResourceScheduler::ChangeReplay: d->changeProcessed(); break; case ResourceScheduler::SyncCollectionTree: case ResourceScheduler::SyncAll: if (d->mCollectionSyncer) { d->mCollectionSyncer->rollback(); } else { d->scheduler->taskDone(); } break; case ResourceScheduler::SyncCollection: if (d->mItemSyncer) { d->mItemSyncer->rollback(); } else { d->scheduler->taskDone(); } break; default: d->scheduler->taskDone(); } } void ResourceBase::cancelTask(const QString &msg) { cancelTask(); Q_EMIT error(msg); } void ResourceBase::deferTask() { Q_D(ResourceBase); qCDebug(AKONADIAGENTBASE_LOG) << "Deferring task" << d->scheduler->currentTask(); // Deferring a CollectionSync is just not implemented. // We'd need to d->mItemSyncer->rollback() but also to NOT call taskDone in slotItemSyncDone() here... Q_ASSERT(!d->mItemSyncer); d->scheduler->deferTask(); } void ResourceBase::doSetOnline(bool state) { d_func()->scheduler->setOnline(state); } void ResourceBase::synchronizeCollection(qint64 collectionId) { synchronizeCollection(collectionId, false); } void ResourceBase::synchronizeCollection(qint64 collectionId, bool recursive) { + Q_D(ResourceBase); CollectionFetchJob *job = new CollectionFetchJob(Collection(collectionId), recursive ? CollectionFetchJob::Recursive : CollectionFetchJob::Base); job->setFetchScope(changeRecorder()->collectionFetchScope()); job->fetchScope().setResource(identifier()); job->fetchScope().setListFilter(CollectionFetchScope::Sync); - connect(job, SIGNAL(result(KJob*)), SLOT(slotCollectionListDone(KJob*))); + connect(job, &KJob::result, d, &ResourceBasePrivate::slotCollectionListDone); } void ResourceBasePrivate::slotCollectionListDone(KJob *job) { if (!job->error()) { const Collection::List list = static_cast(job)->collections(); for (const Collection &collection : list) { //We also get collections that should not be synced but are part of the tree. if (collection.shouldList(Collection::ListSync)) { if (mScheduleAttributeSyncBeforeCollectionSync) { scheduler->scheduleAttributesSync(collection); } scheduler->scheduleSync(collection); } } } else { qCWarning(AKONADIAGENTBASE_LOG) << "Failed to fetch collection for collection sync: " << job->errorString(); } } void ResourceBase::synchronizeCollectionAttributes(const Akonadi::Collection &col) { Q_D(ResourceBase); d->scheduler->scheduleAttributesSync(col); } void ResourceBase::synchronizeCollectionAttributes(qint64 collectionId) { + Q_D(ResourceBase); CollectionFetchJob *job = new CollectionFetchJob(Collection(collectionId), CollectionFetchJob::Base); job->setFetchScope(changeRecorder()->collectionFetchScope()); job->fetchScope().setResource(identifier()); - connect(job, SIGNAL(result(KJob*)), SLOT(slotCollectionListForAttributesDone(KJob*))); + connect(job, &KJob::result, d, &ResourceBasePrivate::slotCollectionListForAttributesDone); } void ResourceBasePrivate::slotCollectionListForAttributesDone(KJob *job) { if (!job->error()) { const Collection::List list = static_cast(job)->collections(); if (!list.isEmpty()) { const Collection col = list.first(); scheduler->scheduleAttributesSync(col); } } // TODO: error handling } void ResourceBase::setTotalItems(int amount) { qCDebug(AKONADIAGENTBASE_LOG) << amount; Q_D(ResourceBase); setItemStreamingEnabled(true); if (d->mItemSyncer) { d->mItemSyncer->setTotalItems(amount); } } void ResourceBase::setDisableAutomaticItemDeliveryDone(bool disable) { Q_D(ResourceBase); if (d->mItemSyncer) { d->mItemSyncer->setDisableAutomaticDeliveryDone(disable); } d->mDisableAutomaticItemDeliveryDone = disable; } void ResourceBase::setItemStreamingEnabled(bool enable) { Q_D(ResourceBase); d->createItemSyncInstanceIfMissing(); if (d->mItemSyncer) { d->mItemSyncer->setStreamingEnabled(enable); } } void ResourceBase::itemsRetrieved(const Item::List &items) { Q_D(ResourceBase); if (d->scheduler->currentTask().type == ResourceScheduler::FetchItems) { auto trx = new TransactionSequence(this); - connect(trx, SIGNAL(result(KJob*)), - this, SLOT(slotItemSyncDone(KJob*))); + connect(trx, &KJob::result, d, &ResourceBasePrivate::slotItemSyncDone); for (const Item &item : items) { Q_ASSERT(item.parentCollection().isValid()); if (item.isValid()) { new ItemModifyJob(item, trx); } else if (!item.remoteId().isEmpty()) { auto job = new ItemCreateJob(item, item.parentCollection(), trx); job->setMerge(ItemCreateJob::RID); } else { // This should not happen, but just to be sure... new ItemModifyJob(item, trx); } } trx->commit(); } else { d->createItemSyncInstanceIfMissing(); if (d->mItemSyncer) { d->mItemSyncer->setFullSyncItems(items); } } } void ResourceBase::itemsRetrievedIncremental(const Item::List &changedItems, const Item::List &removedItems) { Q_D(ResourceBase); d->createItemSyncInstanceIfMissing(); if (d->mItemSyncer) { d->mItemSyncer->setIncrementalSyncItems(changedItems, removedItems); } } void ResourceBasePrivate::slotItemSyncDone(KJob *job) { mItemSyncer = nullptr; Q_Q(ResourceBase); if (job->error() && job->error() != Job::UserCanceled) { Q_EMIT q->error(job->errorString()); } if (scheduler->currentTask().type == ResourceScheduler::FetchItems) { scheduler->currentTask().sendDBusReplies((job->error() && job->error() != Job::UserCanceled) ? job->errorString() : QString()); } scheduler->taskDone(); } void ResourceBasePrivate::slotDelayedEmitProgress() { Q_Q(ResourceBase); if (mAutomaticProgressReporting) { Q_EMIT q->percent(mUnemittedProgress); for (const QVariantMap &statusMap : qAsConst(mUnemittedAdvancedStatus)) { Q_EMIT q->advancedStatus(statusMap); } } mUnemittedProgress = 0; mUnemittedAdvancedStatus.clear(); } void ResourceBasePrivate::slotPercent(KJob *job, unsigned long percent) { mUnemittedProgress = percent; const Collection collection = job->property("collection").value(); if (collection.isValid()) { QVariantMap statusMap; statusMap.insert(QStringLiteral("key"), QStringLiteral("collectionSyncProgress")); statusMap.insert(QStringLiteral("collectionId"), collection.id()); statusMap.insert(QStringLiteral("percent"), static_cast(percent)); mUnemittedAdvancedStatus[collection.id()] = statusMap; } // deliver completion right away, intermediate progress at 1s intervals if (percent == 100) { mProgressEmissionCompressor.stop(); slotDelayedEmitProgress(); } else if (!mProgressEmissionCompressor.isActive()) { mProgressEmissionCompressor.start(); } } void ResourceBase::setHierarchicalRemoteIdentifiersEnabled(bool enable) { Q_D(ResourceBase); d->mHierarchicalRid = enable; } void ResourceBase::scheduleCustomTask(QObject *receiver, const char *method, const QVariant &argument, SchedulePriority priority) { Q_D(ResourceBase); d->scheduler->scheduleCustomTask(receiver, method, argument, priority); } void ResourceBase::taskDone() { Q_D(ResourceBase); d->scheduler->taskDone(); } void ResourceBase::retrieveCollectionAttributes(const Collection &collection) { collectionAttributesRetrieved(collection); } void ResourceBase::retrieveTags() { Q_D(ResourceBase); d->scheduler->taskDone(); } void ResourceBase::retrieveRelations() { Q_D(ResourceBase); d->scheduler->taskDone(); } bool ResourceBase::retrieveItem(const Akonadi::Item &item, const QSet &parts) { Q_UNUSED(item); Q_UNUSED(parts); // retrieveItem() can no longer be pure virtual, because then we could not mark // it as deprecated (i.e. implementations would still be forced to implement it), // so instead we assert here. // NOTE: Don't change to Q_ASSERT_X here: while the macro can be disabled at // compile time, we want to hit this assert *ALWAYS*. qt_assert_x("Akonadi::ResourceBase::retrieveItem()", "The base implementation of retrieveItem() must never be reached. " "You must implement either retrieveItem() or retrieveItems(Akonadi::Item::List, QSet) overload " "to handle item retrieval requests.", __FILE__, __LINE__); return false; } bool ResourceBase::retrieveItems(const Akonadi::Item::List &items, const QSet &parts) { Q_D(ResourceBase); // If we reach this implementation of retrieveItems() then it means that the // resource is still using the deprecated retrieveItem() method, so we explode // this to a myriad of tasks in scheduler and let them be processed one by one const qint64 id = d->scheduler->currentTask().serial; for (const auto &item : items) { d->scheduler->scheduleItemFetch(item, parts, d->scheduler->currentTask().dbusMsgs, id); } taskDone(); return true; } void Akonadi::ResourceBase::abortActivity() { } void ResourceBase::setItemTransactionMode(ItemSync::TransactionMode mode) { Q_D(ResourceBase); d->mItemTransactionMode = mode; } void ResourceBase::setItemSynchronizationFetchScope(const ItemFetchScope &fetchScope) { Q_D(ResourceBase); if (!d->mItemSyncFetchScope) { d->mItemSyncFetchScope = new ItemFetchScope; } *(d->mItemSyncFetchScope) = fetchScope; } void ResourceBase::setItemMergingMode(ItemSync::MergeMode mode) { Q_D(ResourceBase); d->mItemMergeMode = mode; } void ResourceBase::setAutomaticProgressReporting(bool enabled) { Q_D(ResourceBase); d->mAutomaticProgressReporting = enabled; } QString ResourceBase::dumpNotificationListToString() const { Q_D(const ResourceBase); return d->dumpNotificationListToString(); } QString ResourceBase::dumpSchedulerToString() const { Q_D(const ResourceBase); return d->dumpToString(); } void ResourceBase::dumpMemoryInfo() const { Q_D(const ResourceBase); - return d->dumpMemoryInfo(); + d->dumpMemoryInfo(); } QString ResourceBase::dumpMemoryInfoToString() const { Q_D(const ResourceBase); return d->dumpMemoryInfoToString(); } void ResourceBase::tagsRetrieved(const Tag::List &tags, const QHash &tagMembers) { Q_D(ResourceBase); Q_ASSERT_X(d->scheduler->currentTask().type == ResourceScheduler::SyncTags || d->scheduler->currentTask().type == ResourceScheduler::SyncAll || d->scheduler->currentTask().type == ResourceScheduler::Custom, "ResourceBase::tagsRetrieved()", "Calling tagsRetrieved() although no tag retrieval is in progress"); if (!d->mTagSyncer) { d->mTagSyncer = new TagSync(this); - connect(d->mTagSyncer, SIGNAL(percent(KJob*,ulong)), SLOT(slotPercent(KJob*,ulong))); - connect(d->mTagSyncer, SIGNAL(result(KJob*)), SLOT(slotTagSyncDone(KJob*))); + connect(d->mTagSyncer, qOverload(&KJob::percent), d, &ResourceBasePrivate::slotPercent); + connect(d->mTagSyncer, &KJob::result, d, &ResourceBasePrivate::slotTagSyncDone); } d->mTagSyncer->setFullTagList(tags); d->mTagSyncer->setTagMembers(tagMembers); } void ResourceBasePrivate::slotTagSyncDone(KJob *job) { Q_Q(ResourceBase); mTagSyncer = nullptr; if (job->error()) { if (job->error() != Job::UserCanceled) { qCWarning(AKONADIAGENTBASE_LOG) << "TagSync failed: " << job->errorString(); Q_EMIT q->error(job->errorString()); } } scheduler->taskDone(); } void ResourceBase::relationsRetrieved(const Relation::List &relations) { Q_D(ResourceBase); Q_ASSERT_X(d->scheduler->currentTask().type == ResourceScheduler::SyncRelations || d->scheduler->currentTask().type == ResourceScheduler::SyncAll || d->scheduler->currentTask().type == ResourceScheduler::Custom, "ResourceBase::relationsRetrieved()", "Calling relationsRetrieved() although no relation retrieval is in progress"); if (!d->mRelationSyncer) { d->mRelationSyncer = new RelationSync(this); - connect(d->mRelationSyncer, SIGNAL(percent(KJob*,ulong)), SLOT(slotPercent(KJob*,ulong))); - connect(d->mRelationSyncer, SIGNAL(result(KJob*)), SLOT(slotRelationSyncDone(KJob*))); + connect(d->mRelationSyncer, qOverload(&KJob::percent), d, &ResourceBasePrivate::slotPercent); + connect(d->mRelationSyncer, &KJob::result, d, &ResourceBasePrivate::slotRelationSyncDone); } d->mRelationSyncer->setRemoteRelations(relations); } void ResourceBasePrivate::slotRelationSyncDone(KJob *job) { Q_Q(ResourceBase); mRelationSyncer = nullptr; if (job->error()) { if (job->error() != Job::UserCanceled) { qCWarning(AKONADIAGENTBASE_LOG) << "RelationSync failed: " << job->errorString(); Q_EMIT q->error(job->errorString()); } } scheduler->taskDone(); } #include "resourcebase.moc" #include "moc_resourcebase.cpp"