File indexing completed on 2025-01-05 04:46:24

0001 /*
0002     SPDX-FileCopyrightText: 2006 Till Adam <adam@kde.org>
0003     SPDX-FileCopyrightText: 2007 Volker Krause <vkrause@kde.org>
0004     SPDX-FileCopyrightText: 2007 Bruno Virlet <bruno.virlet@gmail.com>
0005     SPDX-FileCopyrightText: 2008 Kevin Krammer <kevin.krammer@gmx.at>
0006 
0007     SPDX-License-Identifier: LGPL-2.0-or-later
0008 */
0009 
0010 #include "agentbase.h"
0011 #include "agentbase_p.h"
0012 
0013 #include "agentconfigurationdialog.h"
0014 #include "agentmanager.h"
0015 #include "akonadifull-version.h"
0016 #include "changerecorder.h"
0017 #include "controladaptor.h"
0018 #include "itemfetchjob.h"
0019 #include "monitor_p.h"
0020 #include "private/standarddirs_p.h"
0021 #include "servermanager_p.h"
0022 #include "session.h"
0023 #include "session_p.h"
0024 #include "statusadaptor.h"
0025 
0026 #include "akonadiagentbase_debug.h"
0027 
0028 #include <KLocalizedString>
0029 
0030 #include <KAboutData>
0031 
0032 #include <QCommandLineParser>
0033 #include <QNetworkInformation>
0034 #include <QPointer>
0035 #include <QSettings>
0036 #include <QTimer>
0037 
0038 #include <QStandardPaths>
0039 #include <signal.h>
0040 #include <stdlib.h>
0041 #if defined __GLIBC__
0042 #include <malloc.h> // for dumping memory information
0043 #endif
0044 
0045 #ifdef Q_OS_WIN
0046 #include <Windows.h>
0047 #endif
0048 
0049 #include <chrono>
0050 #include <thread>
0051 
0052 using namespace Akonadi;
0053 using namespace std::chrono_literals;
0054 static AgentBase *sAgentBase = nullptr;
0055 
0056 AgentBase::Observer::Observer()
0057 {
0058 }
0059 
0060 AgentBase::Observer::~Observer()
0061 {
0062 }
0063 
0064 void AgentBase::Observer::itemAdded(const Item &item, const Collection &collection)
0065 {
0066     Q_UNUSED(item)
0067     Q_UNUSED(collection)
0068     if (sAgentBase) {
0069         sAgentBase->d_ptr->changeProcessed();
0070     }
0071 }
0072 
0073 void AgentBase::Observer::itemChanged(const Item &item, const QSet<QByteArray> &partIdentifiers)
0074 {
0075     Q_UNUSED(item)
0076     Q_UNUSED(partIdentifiers)
0077     if (sAgentBase) {
0078         sAgentBase->d_ptr->changeProcessed();
0079     }
0080 }
0081 
0082 void AgentBase::Observer::itemRemoved(const Item &item)
0083 {
0084     Q_UNUSED(item)
0085     if (sAgentBase) {
0086         sAgentBase->d_ptr->changeProcessed();
0087     }
0088 }
0089 
0090 void AgentBase::Observer::collectionAdded(const Akonadi::Collection &collection, const Akonadi::Collection &parent)
0091 {
0092     Q_UNUSED(collection)
0093     Q_UNUSED(parent)
0094     if (sAgentBase) {
0095         sAgentBase->d_ptr->changeProcessed();
0096     }
0097 }
0098 
0099 void AgentBase::Observer::collectionChanged(const Collection &collection)
0100 {
0101     Q_UNUSED(collection)
0102     if (sAgentBase) {
0103         sAgentBase->d_ptr->changeProcessed();
0104     }
0105 }
0106 
0107 void AgentBase::Observer::collectionRemoved(const Collection &collection)
0108 {
0109     Q_UNUSED(collection)
0110     if (sAgentBase) {
0111         sAgentBase->d_ptr->changeProcessed();
0112     }
0113 }
0114 
0115 void AgentBase::ObserverV2::itemMoved(const Akonadi::Item &item, const Akonadi::Collection &source, const Akonadi::Collection &dest)
0116 {
0117     Q_UNUSED(item)
0118     Q_UNUSED(source)
0119     Q_UNUSED(dest)
0120     if (sAgentBase) {
0121         sAgentBase->d_ptr->changeProcessed();
0122     }
0123 }
0124 
0125 void AgentBase::ObserverV2::itemLinked(const Akonadi::Item &item, const Akonadi::Collection &collection)
0126 {
0127     Q_UNUSED(item)
0128     Q_UNUSED(collection)
0129     if (sAgentBase) {
0130         // not implementation, let's disconnect the signal to enable optimizations in Monitor
0131         QObject::disconnect(sAgentBase->changeRecorder(), &Monitor::itemLinked, sAgentBase->d_ptr.get(), &AgentBasePrivate::itemLinked);
0132         sAgentBase->d_ptr->changeProcessed();
0133     }
0134 }
0135 
0136 void AgentBase::ObserverV2::itemUnlinked(const Akonadi::Item &item, const Akonadi::Collection &collection)
0137 {
0138     Q_UNUSED(item)
0139     Q_UNUSED(collection)
0140     if (sAgentBase) {
0141         // not implementation, let's disconnect the signal to enable optimizations in Monitor
0142         QObject::disconnect(sAgentBase->changeRecorder(), &Monitor::itemUnlinked, sAgentBase->d_ptr.get(), &AgentBasePrivate::itemUnlinked);
0143         sAgentBase->d_ptr->changeProcessed();
0144     }
0145 }
0146 
0147 void AgentBase::ObserverV2::collectionMoved(const Akonadi::Collection &collection, const Akonadi::Collection &source, const Akonadi::Collection &dest)
0148 {
0149     Q_UNUSED(collection)
0150     Q_UNUSED(source)
0151     Q_UNUSED(dest)
0152     if (sAgentBase) {
0153         sAgentBase->d_ptr->changeProcessed();
0154     }
0155 }
0156 
0157 void AgentBase::ObserverV2::collectionChanged(const Akonadi::Collection &collection, const QSet<QByteArray> &changedAttributes)
0158 {
0159     Q_UNUSED(changedAttributes)
0160     collectionChanged(collection);
0161 }
0162 
0163 void AgentBase::ObserverV3::itemsFlagsChanged(const Akonadi::Item::List &items, const QSet<QByteArray> &addedFlags, const QSet<QByteArray> &removedFlags)
0164 {
0165     Q_UNUSED(items)
0166     Q_UNUSED(addedFlags)
0167     Q_UNUSED(removedFlags)
0168 
0169     if (sAgentBase) {
0170         // not implementation, let's disconnect the signal to enable optimizations in Monitor
0171         QObject::disconnect(sAgentBase->changeRecorder(), &Monitor::itemsFlagsChanged, sAgentBase->d_ptr.get(), &AgentBasePrivate::itemsFlagsChanged);
0172         sAgentBase->d_ptr->changeProcessed();
0173     }
0174 }
0175 
0176 void AgentBase::ObserverV3::itemsMoved(const Akonadi::Item::List &items, const Collection &sourceCollection, const Collection &destinationCollection)
0177 {
0178     Q_UNUSED(items)
0179     Q_UNUSED(sourceCollection)
0180     Q_UNUSED(destinationCollection)
0181 
0182     if (sAgentBase) {
0183         // not implementation, let's disconnect the signal to enable optimizations in Monitor
0184         QObject::disconnect(sAgentBase->changeRecorder(), &Monitor::itemsMoved, sAgentBase->d_ptr.get(), &AgentBasePrivate::itemsMoved);
0185         sAgentBase->d_ptr->changeProcessed();
0186     }
0187 }
0188 
0189 void AgentBase::ObserverV3::itemsRemoved(const Akonadi::Item::List &items)
0190 {
0191     Q_UNUSED(items)
0192 
0193     if (sAgentBase) {
0194         // not implementation, let's disconnect the signal to enable optimizations in Monitor
0195         QObject::disconnect(sAgentBase->changeRecorder(), &Monitor::itemsRemoved, sAgentBase->d_ptr.get(), &AgentBasePrivate::itemsRemoved);
0196         sAgentBase->d_ptr->changeProcessed();
0197     }
0198 }
0199 
0200 void AgentBase::ObserverV3::itemsLinked(const Akonadi::Item::List &items, const Collection &collection)
0201 {
0202     Q_UNUSED(items)
0203     Q_UNUSED(collection)
0204 
0205     if (sAgentBase) {
0206         // not implementation, let's disconnect the signal to enable optimizations in Monitor
0207         QObject::disconnect(sAgentBase->changeRecorder(), &Monitor::itemsLinked, sAgentBase->d_ptr.get(), &AgentBasePrivate::itemsLinked);
0208         sAgentBase->d_ptr->changeProcessed();
0209     }
0210 }
0211 
0212 void AgentBase::ObserverV3::itemsUnlinked(const Akonadi::Item::List &items, const Collection &collection)
0213 {
0214     Q_UNUSED(items)
0215     Q_UNUSED(collection)
0216 
0217     if (sAgentBase) {
0218         // not implementation, let's disconnect the signal to enable optimizations in Monitor
0219         QObject::disconnect(sAgentBase->changeRecorder(), &Monitor::itemsUnlinked, sAgentBase->d_ptr.get(), &AgentBasePrivate::itemsUnlinked);
0220         sAgentBase->d_ptr->changeProcessed();
0221     }
0222 }
0223 
0224 void AgentBase::ObserverV4::tagAdded(const Tag &tag)
0225 {
0226     Q_UNUSED(tag)
0227 
0228     if (sAgentBase) {
0229         // not implementation, let's disconnect the signal to enable optimization in Monitor
0230         QObject::disconnect(sAgentBase->changeRecorder(), &Monitor::tagAdded, sAgentBase->d_ptr.get(), &AgentBasePrivate::tagAdded);
0231         sAgentBase->d_ptr->changeProcessed();
0232     }
0233 }
0234 
0235 void AgentBase::ObserverV4::tagChanged(const Tag &tag)
0236 {
0237     Q_UNUSED(tag)
0238 
0239     if (sAgentBase) {
0240         // not implementation, let's disconnect the signal to enable optimization in Monitor
0241         QObject::disconnect(sAgentBase->changeRecorder(), &Monitor::tagChanged, sAgentBase->d_ptr.get(), &AgentBasePrivate::tagChanged);
0242         sAgentBase->d_ptr->changeProcessed();
0243     }
0244 }
0245 
0246 void AgentBase::ObserverV4::tagRemoved(const Tag &tag)
0247 {
0248     Q_UNUSED(tag)
0249 
0250     if (sAgentBase) {
0251         // not implementation, let's disconnect the signal to enable optimization in Monitor
0252         QObject::disconnect(sAgentBase->changeRecorder(), &Monitor::tagRemoved, sAgentBase->d_ptr.get(), &AgentBasePrivate::tagRemoved);
0253         sAgentBase->d_ptr->changeProcessed();
0254     }
0255 }
0256 
0257 void AgentBase::ObserverV4::itemsTagsChanged(const Item::List &items, const QSet<Tag> &addedTags, const QSet<Tag> &removedTags)
0258 {
0259     Q_UNUSED(items)
0260     Q_UNUSED(addedTags)
0261     Q_UNUSED(removedTags)
0262 
0263     if (sAgentBase) {
0264         // not implementation, let's disconnect the signal to enable optimization in Monitor
0265         QObject::disconnect(sAgentBase->changeRecorder(), &Monitor::itemsTagsChanged, sAgentBase->d_ptr.get(), &AgentBasePrivate::itemsTagsChanged);
0266         sAgentBase->d_ptr->changeProcessed();
0267     }
0268 }
0269 
0270 void AgentBase::ObserverV4::relationAdded(const Akonadi::Relation &relation)
0271 {
0272     Q_UNUSED(relation)
0273 
0274     if (sAgentBase) {
0275         // not implementation, let's disconnect the signal to enable optimization in Monitor
0276         QObject::disconnect(sAgentBase->changeRecorder(), &Monitor::relationAdded, sAgentBase->d_ptr.get(), &AgentBasePrivate::relationAdded);
0277         sAgentBase->d_ptr->changeProcessed();
0278     }
0279 }
0280 
0281 void AgentBase::ObserverV4::relationRemoved(const Akonadi::Relation &relation)
0282 {
0283     Q_UNUSED(relation)
0284 
0285     if (sAgentBase) {
0286         // not implementation, let's disconnect the signal to enable optimization in Monitor
0287         QObject::disconnect(sAgentBase->changeRecorder(), &Monitor::relationRemoved, sAgentBase->d_ptr.get(), &AgentBasePrivate::relationRemoved);
0288         sAgentBase->d_ptr->changeProcessed();
0289     }
0290 }
0291 
0292 void AgentBase::ObserverV4::itemsRelationsChanged(const Akonadi::Item::List &items,
0293                                                   const Akonadi::Relation::List &addedRelations,
0294                                                   const Akonadi::Relation::List &removedRelations)
0295 {
0296     Q_UNUSED(items)
0297     Q_UNUSED(addedRelations)
0298     Q_UNUSED(removedRelations)
0299 
0300     if (sAgentBase) {
0301         // not implementation, let's disconnect the signal to enable optimization in Monitor
0302         disconnect(sAgentBase->changeRecorder(), &Monitor::itemsRelationsChanged, sAgentBase->d_ptr.get(), &AgentBasePrivate::itemsRelationsChanged);
0303         sAgentBase->d_ptr->changeProcessed();
0304     }
0305 }
0306 
0307 /// @cond PRIVATE
0308 
0309 AgentBasePrivate::AgentBasePrivate(AgentBase *parent)
0310     : q_ptr(parent)
0311     , mStatusCode(AgentBase::Idle)
0312     , mProgress(0)
0313     , mNeedsNetwork(false)
0314     , mOnline(false)
0315     , mDesiredOnlineState(false)
0316     , mPendingQuit(false)
0317     , mSettings(nullptr)
0318     , mChangeRecorder(nullptr)
0319     , mTracer(nullptr)
0320     , mObserver(nullptr)
0321     , mPowerInterface(nullptr)
0322     , mTemporaryOfflineTimer(nullptr)
0323     , mEventLoopLocker(nullptr)
0324 {
0325     Internal::setClientType(Internal::Agent);
0326 }
0327 
0328 AgentBasePrivate::~AgentBasePrivate()
0329 {
0330     mChangeRecorder->setConfig(nullptr);
0331     delete mSettings;
0332 }
0333 
0334 void AgentBasePrivate::init()
0335 {
0336     Q_Q(AgentBase);
0337     /**
0338      * Create a default session for this process.
0339      */
0340     SessionPrivate::createDefaultSession(mId.toLatin1());
0341 
0342     mTracer =
0343         new org::freedesktop::Akonadi::Tracer(ServerManager::serviceName(ServerManager::Server), QStringLiteral("/tracing"), QDBusConnection::sessionBus(), q);
0344 
0345     new Akonadi__ControlAdaptor(q);
0346     new Akonadi__StatusAdaptor(q);
0347     if (!QDBusConnection::sessionBus().registerObject(QStringLiteral("/"), q, QDBusConnection::ExportAdaptors)) {
0348         Q_EMIT q->error(i18n("Unable to register object at dbus: %1", QDBusConnection::sessionBus().lastError().message()));
0349     }
0350 
0351     mSettings = new QSettings(ServerManager::agentConfigFilePath(mId), QSettings::IniFormat);
0352 
0353     mChangeRecorder = new ChangeRecorder(q);
0354     mChangeRecorder->setObjectName(QLatin1StringView("AgentBaseChangeRecorder"));
0355     mChangeRecorder->ignoreSession(Session::defaultSession());
0356     mChangeRecorder->itemFetchScope().setCacheOnly(true);
0357     mChangeRecorder->setConfig(mSettings);
0358 
0359     mDesiredOnlineState = mSettings->value(QStringLiteral("Agent/DesiredOnlineState"), true).toBool();
0360     mOnline = mDesiredOnlineState;
0361 
0362     // reinitialize the status message now that online state is available
0363     mStatusMessage = defaultReadyMessage();
0364 
0365     mName = mSettings->value(QStringLiteral("Agent/Name")).toString();
0366     if (mName.isEmpty()) {
0367         mName = mSettings->value(QStringLiteral("Resource/Name")).toString();
0368         if (!mName.isEmpty()) {
0369             mSettings->remove(QStringLiteral("Resource/Name"));
0370             mSettings->setValue(QStringLiteral("Agent/Name"), mName);
0371         }
0372     }
0373 
0374     connect(mChangeRecorder, &Monitor::itemAdded, this, &AgentBasePrivate::itemAdded);
0375     connect(mChangeRecorder, &Monitor::itemChanged, this, &AgentBasePrivate::itemChanged);
0376     connect(mChangeRecorder, &Monitor::collectionAdded, this, &AgentBasePrivate::collectionAdded);
0377     connect(mChangeRecorder,
0378             qOverload<const Collection &>(&ChangeRecorder::collectionChanged),
0379             this,
0380             qOverload<const Collection &>(&AgentBasePrivate::collectionChanged));
0381     connect(mChangeRecorder,
0382             qOverload<const Collection &, const QSet<QByteArray> &>(&ChangeRecorder::collectionChanged),
0383             this,
0384             qOverload<const Collection &, const QSet<QByteArray> &>(&AgentBasePrivate::collectionChanged));
0385     connect(mChangeRecorder, &Monitor::collectionMoved, this, &AgentBasePrivate::collectionMoved);
0386     connect(mChangeRecorder, &Monitor::collectionRemoved, this, &AgentBasePrivate::collectionRemoved);
0387     connect(mChangeRecorder, &Monitor::collectionSubscribed, this, &AgentBasePrivate::collectionSubscribed);
0388     connect(mChangeRecorder, &Monitor::collectionUnsubscribed, this, &AgentBasePrivate::collectionUnsubscribed);
0389 
0390     connect(q, qOverload<int, const QString &>(&AgentBase::status), this, &AgentBasePrivate::slotStatus);
0391     connect(q, &AgentBase::percent, this, &AgentBasePrivate::slotPercent);
0392     connect(q, &AgentBase::warning, this, &AgentBasePrivate::slotWarning);
0393     connect(q, &AgentBase::error, this, &AgentBasePrivate::slotError);
0394 
0395     mPowerInterface = new QDBusInterface(QStringLiteral("org.kde.Solid.PowerManagement"),
0396                                          QStringLiteral("/org/kde/Solid/PowerManagement/Actions/SuspendSession"),
0397                                          QStringLiteral("org.kde.Solid.PowerManagement.Actions.SuspendSession"),
0398                                          QDBusConnection::sessionBus(),
0399                                          this);
0400     if (mPowerInterface->isValid()) {
0401         connect(mPowerInterface, SIGNAL(resumingFromSuspend()), this, SLOT(slotResumedFromSuspend())); // clazy:exclude=old-style-connect
0402     } else {
0403         delete mPowerInterface;
0404         mPowerInterface = nullptr;
0405     }
0406 
0407     // Use reference counting to allow agents to finish internal jobs when the
0408     // agent is stopped.
0409     mEventLoopLocker = new QEventLoopLocker();
0410 
0411     mResourceTypeName = AgentManager::self()->instance(mId).type().name();
0412     setProgramName();
0413 
0414     QTimer::singleShot(0s, q, [this] {
0415         delayedInit();
0416     });
0417 }
0418 
0419 void AgentBasePrivate::delayedInit()
0420 {
0421     Q_Q(AgentBase);
0422 
0423     const QString serviceId = ServerManager::agentServiceName(ServerManager::Agent, mId);
0424     if (!QDBusConnection::sessionBus().registerService(serviceId)) {
0425         qCCritical(AKONADIAGENTBASE_LOG) << "Unable to register service" << serviceId << "at dbus:" << QDBusConnection::sessionBus().lastError().message();
0426     }
0427     q->setOnlineInternal(mDesiredOnlineState);
0428 
0429     QDBusConnection::sessionBus().registerObject(QStringLiteral("/Debug"), this, QDBusConnection::ExportScriptableSlots);
0430 }
0431 
0432 void AgentBasePrivate::setProgramName()
0433 {
0434     // ugly, really ugly, if you find another solution, change it and blame me for this code (Andras)
0435     QString programName = mResourceTypeName;
0436     if (!mName.isEmpty()) {
0437         programName = i18nc("Name and type of Akonadi resource", "%1 of type %2", mName, mResourceTypeName);
0438     }
0439 
0440     QGuiApplication::setApplicationDisplayName(programName);
0441 }
0442 
0443 void AgentBasePrivate::itemAdded(const Akonadi::Item &item, const Akonadi::Collection &collection)
0444 {
0445     if (mObserver) {
0446         mObserver->itemAdded(item, collection);
0447     } else {
0448         changeProcessed();
0449     }
0450 }
0451 
0452 void AgentBasePrivate::itemChanged(const Akonadi::Item &item, const QSet<QByteArray> &partIdentifiers)
0453 {
0454     if (mObserver) {
0455         mObserver->itemChanged(item, partIdentifiers);
0456     } else {
0457         changeProcessed();
0458     }
0459 }
0460 
0461 void AgentBasePrivate::itemMoved(const Akonadi::Item &item, const Akonadi::Collection &source, const Akonadi::Collection &dest)
0462 {
0463     auto observer2 = dynamic_cast<AgentBase::ObserverV2 *>(mObserver);
0464     if (mObserver) {
0465         // inter-resource moves, requires we know which resources the source and destination are in though
0466         if (!source.resource().isEmpty() && !dest.resource().isEmpty()) {
0467             if (source.resource() != dest.resource()) {
0468                 if (source.resource() == q_ptr->identifier()) { // moved away from us
0469                     Akonadi::Item i(item);
0470                     i.setParentCollection(source);
0471                     mObserver->itemRemoved(i);
0472                 } else if (dest.resource() == q_ptr->identifier()) { // moved to us
0473                     mObserver->itemAdded(item, dest);
0474                 } else if (observer2) {
0475                     observer2->itemMoved(item, source, dest);
0476                 } else {
0477                     // not for us, not sure if we should get here at all
0478                     changeProcessed();
0479                 }
0480                 return;
0481             }
0482         }
0483         // intra-resource move
0484         if (observer2) {
0485             observer2->itemMoved(item, source, dest);
0486         } else {
0487             // ### we cannot just call itemRemoved here as this will already trigger changeProcessed()
0488             // so, just itemAdded() is good enough as no resource can have implemented intra-resource moves anyway
0489             // without using ObserverV2
0490             mObserver->itemAdded(item, dest);
0491             // mObserver->itemRemoved( item );
0492         }
0493     }
0494 }
0495 
0496 void AgentBasePrivate::itemRemoved(const Akonadi::Item &item)
0497 {
0498     if (mObserver) {
0499         mObserver->itemRemoved(item);
0500     } else {
0501         changeProcessed();
0502     }
0503 }
0504 
0505 void AgentBasePrivate::itemLinked(const Akonadi::Item &item, const Akonadi::Collection &collection)
0506 {
0507     auto observer2 = dynamic_cast<AgentBase::ObserverV2 *>(mObserver);
0508     if (observer2) {
0509         observer2->itemLinked(item, collection);
0510     } else {
0511         changeProcessed();
0512     }
0513 }
0514 
0515 void AgentBasePrivate::itemUnlinked(const Akonadi::Item &item, const Akonadi::Collection &collection)
0516 {
0517     auto observer2 = dynamic_cast<AgentBase::ObserverV2 *>(mObserver);
0518     if (observer2) {
0519         observer2->itemUnlinked(item, collection);
0520     } else {
0521         changeProcessed();
0522     }
0523 }
0524 
0525 void AgentBasePrivate::itemsFlagsChanged(const Akonadi::Item::List &items, const QSet<QByteArray> &addedFlags, const QSet<QByteArray> &removedFlags)
0526 {
0527     auto observer3 = dynamic_cast<AgentBase::ObserverV3 *>(mObserver);
0528     if (observer3) {
0529         observer3->itemsFlagsChanged(items, addedFlags, removedFlags);
0530     } else {
0531         Q_ASSERT_X(false, Q_FUNC_INFO, "Batch slots must never be called when ObserverV3 is not available");
0532     }
0533 }
0534 
0535 void AgentBasePrivate::itemsMoved(const Akonadi::Item::List &items, const Akonadi::Collection &source, const Akonadi::Collection &destination)
0536 {
0537     auto observer3 = dynamic_cast<AgentBase::ObserverV3 *>(mObserver);
0538     if (observer3) {
0539         observer3->itemsMoved(items, source, destination);
0540     } else {
0541         Q_ASSERT_X(false, Q_FUNC_INFO, "Batch slots must never be called when ObserverV3 is not available");
0542     }
0543 }
0544 
0545 void AgentBasePrivate::itemsRemoved(const Akonadi::Item::List &items)
0546 {
0547     auto observer3 = dynamic_cast<AgentBase::ObserverV3 *>(mObserver);
0548     if (observer3) {
0549         observer3->itemsRemoved(items);
0550     } else {
0551         Q_ASSERT_X(false, Q_FUNC_INFO, "Batch slots must never be called when ObserverV3 is not available");
0552     }
0553 }
0554 
0555 void AgentBasePrivate::itemsLinked(const Akonadi::Item::List &items, const Akonadi::Collection &collection)
0556 {
0557     if (!mObserver) {
0558         changeProcessed();
0559         return;
0560     }
0561 
0562     auto observer3 = dynamic_cast<AgentBase::ObserverV3 *>(mObserver);
0563     if (observer3) {
0564         observer3->itemsLinked(items, collection);
0565     } else {
0566         Q_ASSERT_X(false, Q_FUNC_INFO, "Batch slots must never be called when ObserverV3 is not available");
0567     }
0568 }
0569 
0570 void AgentBasePrivate::itemsUnlinked(const Akonadi::Item::List &items, const Akonadi::Collection &collection)
0571 {
0572     if (!mObserver) {
0573         changeProcessed();
0574         return;
0575     }
0576 
0577     auto observer3 = dynamic_cast<AgentBase::ObserverV3 *>(mObserver);
0578     if (observer3) {
0579         observer3->itemsUnlinked(items, collection);
0580     } else {
0581         Q_ASSERT_X(false, Q_FUNC_INFO, "Batch slots must never be called when ObserverV3 is not available");
0582     }
0583 }
0584 
0585 void AgentBasePrivate::tagAdded(const Akonadi::Tag &tag)
0586 {
0587     auto observer4 = dynamic_cast<AgentBase::ObserverV4 *>(mObserver);
0588     if (observer4) {
0589         observer4->tagAdded(tag);
0590     } else {
0591         changeProcessed();
0592     }
0593 }
0594 
0595 void AgentBasePrivate::tagChanged(const Akonadi::Tag &tag)
0596 {
0597     auto observer4 = dynamic_cast<AgentBase::ObserverV4 *>(mObserver);
0598     if (observer4) {
0599         observer4->tagChanged(tag);
0600     } else {
0601         changeProcessed();
0602     }
0603 }
0604 
0605 void AgentBasePrivate::tagRemoved(const Akonadi::Tag &tag)
0606 {
0607     auto observer4 = dynamic_cast<AgentBase::ObserverV4 *>(mObserver);
0608     if (observer4) {
0609         observer4->tagRemoved(tag);
0610     } else {
0611         changeProcessed();
0612     }
0613 }
0614 
0615 void AgentBasePrivate::itemsTagsChanged(const Akonadi::Item::List &items, const QSet<Akonadi::Tag> &addedTags, const QSet<Akonadi::Tag> &removedTags)
0616 {
0617     auto observer4 = dynamic_cast<AgentBase::ObserverV4 *>(mObserver);
0618     if (observer4) {
0619         observer4->itemsTagsChanged(items, addedTags, removedTags);
0620     } else {
0621         changeProcessed();
0622     }
0623 }
0624 
0625 void AgentBasePrivate::relationAdded(const Akonadi::Relation &relation)
0626 {
0627     auto observer4 = dynamic_cast<AgentBase::ObserverV4 *>(mObserver);
0628     if (observer4) {
0629         observer4->relationAdded(relation);
0630     } else {
0631         changeProcessed();
0632     }
0633 }
0634 
0635 void AgentBasePrivate::relationRemoved(const Akonadi::Relation &relation)
0636 {
0637     auto observer4 = dynamic_cast<AgentBase::ObserverV4 *>(mObserver);
0638     if (observer4) {
0639         observer4->relationRemoved(relation);
0640     } else {
0641         changeProcessed();
0642     }
0643 }
0644 
0645 void AgentBasePrivate::itemsRelationsChanged(const Akonadi::Item::List &items,
0646                                              const Akonadi::Relation::List &addedRelations,
0647                                              const Akonadi::Relation::List &removedRelations)
0648 {
0649     auto observer4 = dynamic_cast<AgentBase::ObserverV4 *>(mObserver);
0650     if (observer4) {
0651         observer4->itemsRelationsChanged(items, addedRelations, removedRelations);
0652     } else {
0653         changeProcessed();
0654     }
0655 }
0656 
0657 void AgentBasePrivate::collectionAdded(const Akonadi::Collection &collection, const Akonadi::Collection &parent)
0658 {
0659     if (mObserver) {
0660         mObserver->collectionAdded(collection, parent);
0661     } else {
0662         changeProcessed();
0663     }
0664 }
0665 
0666 void AgentBasePrivate::collectionChanged(const Akonadi::Collection &collection)
0667 {
0668     auto observer2 = dynamic_cast<AgentBase::ObserverV2 *>(mObserver);
0669     if (mObserver && observer2 == nullptr) { // For ObserverV2 we use the variant with the part identifiers
0670         mObserver->collectionChanged(collection);
0671     } else if (!mObserver) {
0672         changeProcessed();
0673     }
0674 }
0675 
0676 void AgentBasePrivate::collectionChanged(const Akonadi::Collection &collection, const QSet<QByteArray> &changedAttributes)
0677 {
0678     auto observer2 = dynamic_cast<AgentBase::ObserverV2 *>(mObserver);
0679     if (observer2) {
0680         observer2->collectionChanged(collection, changedAttributes);
0681     } else {
0682         changeProcessed();
0683     }
0684 }
0685 
0686 void AgentBasePrivate::collectionMoved(const Akonadi::Collection &collection, const Akonadi::Collection &source, const Akonadi::Collection &dest)
0687 {
0688     auto observer2 = dynamic_cast<AgentBase::ObserverV2 *>(mObserver);
0689     if (observer2) {
0690         observer2->collectionMoved(collection, source, dest);
0691     } else if (mObserver) {
0692         // ### we cannot just call collectionRemoved here as this will already trigger changeProcessed()
0693         // so, just collectionAdded() is good enough as no resource can have implemented intra-resource moves anyway
0694         // without using ObserverV2
0695         mObserver->collectionAdded(collection, dest);
0696     } else {
0697         changeProcessed();
0698     }
0699 }
0700 
0701 void AgentBasePrivate::collectionRemoved(const Akonadi::Collection &collection)
0702 {
0703     if (mObserver) {
0704         mObserver->collectionRemoved(collection);
0705     } else {
0706         changeProcessed();
0707     }
0708 }
0709 
0710 void AgentBasePrivate::collectionSubscribed(const Akonadi::Collection &collection, const Akonadi::Collection &parent)
0711 {
0712     Q_UNUSED(collection)
0713     Q_UNUSED(parent)
0714     changeProcessed();
0715 }
0716 
0717 void AgentBasePrivate::collectionUnsubscribed(const Akonadi::Collection &collection)
0718 {
0719     Q_UNUSED(collection)
0720     changeProcessed();
0721 }
0722 
0723 void AgentBasePrivate::changeProcessed()
0724 {
0725     mChangeRecorder->changeProcessed();
0726     QTimer::singleShot(0s, mChangeRecorder, &ChangeRecorder::replayNext);
0727 }
0728 
0729 void AgentBasePrivate::slotStatus(int status, const QString &message)
0730 {
0731     mStatusMessage = message;
0732     mStatusCode = 0;
0733 
0734     switch (status) {
0735     case AgentBase::Idle:
0736         if (mStatusMessage.isEmpty()) {
0737             mStatusMessage = defaultReadyMessage();
0738         }
0739 
0740         mStatusCode = 0;
0741         break;
0742     case AgentBase::Running:
0743         if (mStatusMessage.isEmpty()) {
0744             mStatusMessage = defaultSyncingMessage();
0745         }
0746 
0747         mStatusCode = 1;
0748         break;
0749     case AgentBase::Broken:
0750         if (mStatusMessage.isEmpty()) {
0751             mStatusMessage = defaultErrorMessage();
0752         }
0753 
0754         mStatusCode = 2;
0755         break;
0756 
0757     case AgentBase::NotConfigured:
0758         if (mStatusMessage.isEmpty()) {
0759             mStatusMessage = defaultUnconfiguredMessage();
0760         }
0761 
0762         mStatusCode = 3;
0763         break;
0764 
0765     default:
0766         Q_ASSERT(!"Unknown status passed");
0767         break;
0768     }
0769 }
0770 
0771 void AgentBasePrivate::slotPercent(int progress)
0772 {
0773     mProgress = progress;
0774 }
0775 
0776 void AgentBasePrivate::slotWarning(const QString &message)
0777 {
0778     mTracer->warning(QStringLiteral("AgentBase(%1)").arg(mId), message);
0779 }
0780 
0781 void AgentBasePrivate::slotError(const QString &message)
0782 {
0783     mTracer->error(QStringLiteral("AgentBase(%1)").arg(mId), message);
0784 }
0785 
0786 void AgentBasePrivate::slotNetworkStatusChange(bool isOnline)
0787 {
0788     Q_UNUSED(isOnline)
0789     Q_Q(AgentBase);
0790     q->setOnlineInternal(mDesiredOnlineState);
0791 }
0792 
0793 void AgentBasePrivate::slotResumedFromSuspend()
0794 {
0795     if (mNeedsNetwork) {
0796         slotNetworkStatusChange(QNetworkInformation::instance()->reachability() != QNetworkInformation::Reachability::Online);
0797     }
0798 }
0799 
0800 void AgentBasePrivate::slotTemporaryOfflineTimeout()
0801 {
0802     Q_Q(AgentBase);
0803     q->setOnlineInternal(true);
0804 }
0805 
0806 QString AgentBasePrivate::dumpNotificationListToString() const
0807 {
0808     return mChangeRecorder->dumpNotificationListToString();
0809 }
0810 
0811 void AgentBasePrivate::dumpMemoryInfo() const
0812 {
0813     // Send it to stdout, so we can debug user problems.
0814     // since you have to explicitly call this
0815     // it won't flood users with release builds.
0816     QTextStream stream(stdout);
0817     stream << dumpMemoryInfoToString();
0818 }
0819 
0820 QString AgentBasePrivate::dumpMemoryInfoToString() const
0821 {
0822     // man mallinfo for more info
0823     QString str;
0824 #if defined __GLIBC__
0825     struct mallinfo mi;
0826     mi = mallinfo();
0827     QTextStream stream(&str);
0828     stream << "Total non-mmapped bytes (arena):      " << mi.arena << '\n'
0829            << "# of free chunks (ordblks):           " << mi.ordblks << '\n'
0830            << "# of free fastbin blocks (smblks>:    " << mi.smblks << '\n'
0831            << "# of mapped regions (hblks):          " << mi.hblks << '\n'
0832            << "Bytes in mapped regions (hblkhd):     " << mi.hblkhd << '\n'
0833            << "Max. total allocated space (usmblks): " << mi.usmblks << '\n'
0834            << "Free bytes held in fastbins (fsmblks):" << mi.fsmblks << '\n'
0835            << "Total allocated space (uordblks):     " << mi.uordblks << '\n'
0836            << "Total free space (fordblks):          " << mi.fordblks << '\n'
0837            << "Topmost releasable block (keepcost):  " << mi.keepcost << '\n';
0838 #else
0839     str = QLatin1StringView("mallinfo() not supported");
0840 #endif
0841     return str;
0842 }
0843 
0844 AgentBase::AgentBase(const QString &id)
0845     : d_ptr(new AgentBasePrivate(this))
0846 {
0847     sAgentBase = this;
0848     d_ptr->mId = id;
0849     d_ptr->init();
0850 }
0851 
0852 AgentBase::AgentBase(AgentBasePrivate *d, const QString &id)
0853     : d_ptr(d)
0854 {
0855     sAgentBase = this;
0856     d_ptr->mId = id;
0857     d_ptr->init();
0858 }
0859 
0860 AgentBase::~AgentBase() = default;
0861 
0862 void AgentBase::debugAgent(int argc, char **argv)
0863 {
0864     Q_UNUSED(argc)
0865 #ifdef Q_OS_WIN
0866     if (qEnvironmentVariableIsSet("AKONADI_DEBUG_WAIT")) {
0867         if (QByteArray(argv[0]).endsWith(QByteArray(qgetenv("AKONADI_DEBUG_WAIT") + QByteArrayLiteral(".exe")))) {
0868             while (!IsDebuggerPresent()) {
0869                 std::this_thread::sleep_for(std::chrono::milliseconds(100));
0870             }
0871             DebugBreak();
0872         }
0873     }
0874 #else
0875     Q_UNUSED(argv)
0876 #endif
0877 }
0878 
0879 QString AgentBase::parseArguments(int argc, char **argv)
0880 {
0881     Q_UNUSED(argc)
0882 
0883     QCommandLineOption identifierOption(QStringLiteral("identifier"), i18n("Agent identifier"), QStringLiteral("argument"));
0884     QCommandLineParser parser;
0885     parser.addOption(identifierOption);
0886     parser.addHelpOption();
0887     parser.addVersionOption();
0888     parser.process(*qApp);
0889     parser.setApplicationDescription(i18n("Akonadi Agent"));
0890 
0891     if (!parser.isSet(identifierOption)) {
0892         qCDebug(AKONADIAGENTBASE_LOG) << "Identifier argument missing";
0893         exit(1);
0894     }
0895 
0896     const QString identifier = parser.value(identifierOption);
0897     if (identifier.isEmpty()) {
0898         qCDebug(AKONADIAGENTBASE_LOG) << "Identifier argument is empty";
0899         exit(1);
0900     }
0901 
0902     QCoreApplication::setApplicationName(ServerManager::addNamespace(identifier));
0903     QCoreApplication::setApplicationVersion(QStringLiteral(AKONADI_FULL_VERSION));
0904 
0905     const QFileInfo fi(QString::fromLocal8Bit(argv[0]));
0906     // strip off full path and possible .exe suffix
0907     const QString catalog = fi.baseName();
0908 
0909     auto translator = new QTranslator(qApp);
0910     translator->load(catalog);
0911     QCoreApplication::installTranslator(translator);
0912 
0913     return identifier;
0914 }
0915 
0916 /// @endcond
0917 
0918 int AgentBase::init(AgentBase &r)
0919 {
0920     KLocalizedString::setApplicationDomain(QByteArrayLiteral("libakonadi6"));
0921     KAboutData::setApplicationData(r.aboutData());
0922     return qApp->exec();
0923 }
0924 
0925 int AgentBase::status() const
0926 {
0927     Q_D(const AgentBase);
0928 
0929     return d->mStatusCode;
0930 }
0931 
0932 QString AgentBase::statusMessage() const
0933 {
0934     Q_D(const AgentBase);
0935 
0936     return d->mStatusMessage;
0937 }
0938 
0939 int AgentBase::progress() const
0940 {
0941     Q_D(const AgentBase);
0942 
0943     return d->mProgress;
0944 }
0945 
0946 QString AgentBase::progressMessage() const
0947 {
0948     Q_D(const AgentBase);
0949 
0950     return d->mProgressMessage;
0951 }
0952 
0953 bool AgentBase::isOnline() const
0954 {
0955     Q_D(const AgentBase);
0956 
0957     return d->mOnline;
0958 }
0959 
0960 void AgentBase::setNeedsNetwork(bool needsNetwork)
0961 {
0962     Q_D(AgentBase);
0963     if (d->mNeedsNetwork == needsNetwork) {
0964         return;
0965     }
0966 
0967     d->mNeedsNetwork = needsNetwork;
0968     QNetworkInformation::load(QNetworkInformation::Feature::Reachability);
0969     connect(QNetworkInformation::instance(), &QNetworkInformation::reachabilityChanged, this, [this, d](auto reachability) {
0970         d->slotNetworkStatusChange(reachability == QNetworkInformation::Reachability::Online);
0971     });
0972 }
0973 
0974 void AgentBase::setOnline(bool state)
0975 {
0976     Q_D(AgentBase);
0977 
0978     if (d->mPendingQuit) {
0979         return;
0980     }
0981 
0982     d->mDesiredOnlineState = state;
0983     if (!d->mSettings) {
0984         d->mSettings = new QSettings(ServerManager::agentConfigFilePath(identifier()), QSettings::IniFormat);
0985         d->mSettings->setValue(QStringLiteral("Agent/Name"), agentName());
0986     }
0987     d->mSettings->setValue(QStringLiteral("Agent/DesiredOnlineState"), state);
0988     setOnlineInternal(state);
0989 }
0990 
0991 void AgentBase::setTemporaryOffline(int makeOnlineInSeconds)
0992 {
0993     Q_D(AgentBase);
0994 
0995     // if not currently online, avoid bringing it online after the timeout
0996     if (!d->mOnline) {
0997         return;
0998     }
0999 
1000     setOnlineInternal(false);
1001 
1002     if (!d->mTemporaryOfflineTimer) {
1003         d->mTemporaryOfflineTimer = new QTimer(d);
1004         d->mTemporaryOfflineTimer->setSingleShot(true);
1005         connect(d->mTemporaryOfflineTimer, &QTimer::timeout, d, &AgentBasePrivate::slotTemporaryOfflineTimeout);
1006     }
1007     d->mTemporaryOfflineTimer->setInterval(std::chrono::seconds{makeOnlineInSeconds});
1008     d->mTemporaryOfflineTimer->start();
1009 }
1010 
1011 void AgentBase::setOnlineInternal(bool state)
1012 {
1013     Q_D(AgentBase);
1014     if (state && d->mNeedsNetwork) {
1015         if (QNetworkInformation::instance()->reachability() != QNetworkInformation::Reachability::Online) {
1016             // Don't go online if the resource needs network but there is none
1017             state = false;
1018         }
1019     }
1020     d->mOnline = state;
1021 
1022     if (d->mTemporaryOfflineTimer) {
1023         d->mTemporaryOfflineTimer->stop();
1024     }
1025 
1026     const QString newMessage = d->defaultReadyMessage();
1027     if (d->mStatusMessage != newMessage && d->mStatusCode != AgentBase::Broken) {
1028         Q_EMIT status(d->mStatusCode, newMessage);
1029     }
1030 
1031     doSetOnline(state);
1032     Q_EMIT onlineChanged(state);
1033 }
1034 
1035 void AgentBase::doSetOnline(bool online)
1036 {
1037     Q_UNUSED(online)
1038 }
1039 
1040 KAboutData AgentBase::aboutData() const
1041 {
1042     // akonadi_google_resource_1 -> org.kde.akonadi_google_resource
1043     const QString desktopEntry = QLatin1StringView("org.kde.") + qApp->applicationName().remove(QRegularExpression(QStringLiteral("_[0-9]+$")));
1044 
1045     KAboutData data(qApp->applicationName(), agentName(), qApp->applicationVersion());
1046     data.setDesktopFileName(desktopEntry);
1047     return data;
1048 }
1049 
1050 void AgentBase::configure(WId windowId)
1051 {
1052     Q_UNUSED(windowId)
1053 
1054     // Fallback if the agent implements the new plugin-based configuration,
1055     // but someone calls the deprecated configure() method
1056     auto instance = Akonadi::AgentManager::self()->instance(identifier());
1057     QPointer<AgentConfigurationDialog> dialog = new AgentConfigurationDialog(instance, nullptr);
1058     if (dialog->exec()) {
1059         Q_EMIT configurationDialogAccepted();
1060     } else {
1061         Q_EMIT configurationDialogRejected();
1062     }
1063     delete dialog;
1064 }
1065 
1066 #ifdef Q_OS_WIN // krazy:exclude=cpp
1067 void AgentBase::configure(qlonglong windowId)
1068 {
1069     configure(static_cast<WId>(windowId));
1070 }
1071 #endif
1072 
1073 WId AgentBase::winIdForDialogs() const
1074 {
1075     const bool registered = QDBusConnection::sessionBus().interface()->isServiceRegistered(QStringLiteral("org.freedesktop.akonaditray"));
1076     if (!registered) {
1077         return 0;
1078     }
1079 
1080     QDBusInterface dbus(QStringLiteral("org.freedesktop.akonaditray"), QStringLiteral("/Actions"), QStringLiteral("org.freedesktop.Akonadi.Tray"));
1081     const QDBusMessage reply = dbus.call(QStringLiteral("getWinId"));
1082 
1083     if (reply.type() == QDBusMessage::ErrorMessage) {
1084         return 0;
1085     }
1086 
1087     const WId winid = static_cast<WId>(reply.arguments().at(0).toLongLong());
1088 
1089     return winid;
1090 }
1091 
1092 void AgentBase::quit()
1093 {
1094     Q_D(AgentBase);
1095     aboutToQuit();
1096 
1097     if (d->mSettings) {
1098         d->mChangeRecorder->setConfig(nullptr);
1099         d->mSettings->sync();
1100         delete d->mSettings;
1101         d->mSettings = nullptr;
1102     }
1103 
1104     delete d->mEventLoopLocker;
1105     d->mEventLoopLocker = nullptr;
1106 }
1107 
1108 void AgentBase::aboutToQuit()
1109 {
1110     Q_D(AgentBase);
1111     d->mPendingQuit = true;
1112 }
1113 
1114 void AgentBase::cleanup()
1115 {
1116     Q_D(AgentBase);
1117     // prevent the monitor from picking up deletion signals for our own data if we are a resource
1118     // and thus avoid that we kill our own data as last act before our own death
1119     d->mChangeRecorder->blockSignals(true);
1120 
1121     aboutToQuit();
1122 
1123     const QString fileName = d->mSettings->fileName();
1124 
1125     /*
1126      * First destroy the settings object...
1127      */
1128     d->mChangeRecorder->setConfig(nullptr);
1129     delete d->mSettings;
1130     d->mSettings = nullptr;
1131 
1132     /*
1133      * ... then remove the file from hd.
1134      */
1135     if (!QFile::remove(fileName)) {
1136         qCWarning(AKONADIAGENTBASE_LOG) << "Impossible to remove " << fileName;
1137     }
1138 
1139     /*
1140      * ... and remove the changes file from hd.
1141      */
1142     const QString changeDataFileName = fileName + QStringLiteral("_changes.dat");
1143     if (!QFile::remove(changeDataFileName)) {
1144         qCWarning(AKONADIAGENTBASE_LOG) << "Impossible to remove " << changeDataFileName;
1145     }
1146 
1147     /*
1148      * ... and also remove the agent configuration file if there is one.
1149      */
1150     const QString configFile = QStandardPaths::writableLocation(QStandardPaths::ConfigLocation) + QLatin1Char('/') + config()->name();
1151     if (!QFile::remove(configFile)) {
1152         qCWarning(AKONADIAGENTBASE_LOG) << "Impossible to remove config file " << configFile;
1153     }
1154 
1155     delete d->mEventLoopLocker;
1156     d->mEventLoopLocker = nullptr;
1157 }
1158 
1159 void AgentBase::registerObserver(Observer *observer)
1160 {
1161     Q_D(AgentBase);
1162 
1163     // TODO in theory we should re-connect change recorder signals here that we disconnected previously
1164     d->mObserver = observer;
1165 
1166     const bool hasObserverV3 = (dynamic_cast<AgentBase::ObserverV3 *>(d->mObserver) != nullptr);
1167     const bool hasObserverV4 = (dynamic_cast<AgentBase::ObserverV4 *>(d->mObserver) != nullptr);
1168 
1169     disconnect(d->mChangeRecorder, &Monitor::tagAdded, d, &AgentBasePrivate::tagAdded);
1170     disconnect(d->mChangeRecorder, &Monitor::tagChanged, d, &AgentBasePrivate::tagChanged);
1171     disconnect(d->mChangeRecorder, &Monitor::tagRemoved, d, &AgentBasePrivate::tagRemoved);
1172     disconnect(d->mChangeRecorder, &Monitor::itemsTagsChanged, d, &AgentBasePrivate::itemsTagsChanged);
1173     disconnect(d->mChangeRecorder, &Monitor::itemsFlagsChanged, d, &AgentBasePrivate::itemsFlagsChanged);
1174     disconnect(d->mChangeRecorder, &Monitor::itemsMoved, d, &AgentBasePrivate::itemsMoved);
1175     disconnect(d->mChangeRecorder, &Monitor::itemsRemoved, d, &AgentBasePrivate::itemsRemoved);
1176     disconnect(d->mChangeRecorder, &Monitor::itemsLinked, d, &AgentBasePrivate::itemsLinked);
1177     disconnect(d->mChangeRecorder, &Monitor::itemsUnlinked, d, &AgentBasePrivate::itemsUnlinked);
1178     disconnect(d->mChangeRecorder, &Monitor::itemMoved, d, &AgentBasePrivate::itemMoved);
1179     disconnect(d->mChangeRecorder, &Monitor::itemRemoved, d, &AgentBasePrivate::itemRemoved);
1180     disconnect(d->mChangeRecorder, &Monitor::itemLinked, d, &AgentBasePrivate::itemLinked);
1181     disconnect(d->mChangeRecorder, &Monitor::itemUnlinked, d, &AgentBasePrivate::itemUnlinked);
1182 
1183     if (hasObserverV4) {
1184         connect(d->mChangeRecorder, &Monitor::tagAdded, d, &AgentBasePrivate::tagAdded);
1185         connect(d->mChangeRecorder, &Monitor::tagChanged, d, &AgentBasePrivate::tagChanged);
1186         connect(d->mChangeRecorder, &Monitor::tagRemoved, d, &AgentBasePrivate::tagRemoved);
1187         connect(d->mChangeRecorder, &Monitor::itemsTagsChanged, d, &AgentBasePrivate::itemsTagsChanged);
1188     }
1189 
1190     if (hasObserverV3) {
1191         connect(d->mChangeRecorder, &Monitor::itemsFlagsChanged, d, &AgentBasePrivate::itemsFlagsChanged);
1192         connect(d->mChangeRecorder, &Monitor::itemsMoved, d, &AgentBasePrivate::itemsMoved);
1193         connect(d->mChangeRecorder, &Monitor::itemsRemoved, d, &AgentBasePrivate::itemsRemoved);
1194         connect(d->mChangeRecorder, &Monitor::itemsLinked, d, &AgentBasePrivate::itemsLinked);
1195         connect(d->mChangeRecorder, &Monitor::itemsUnlinked, d, &AgentBasePrivate::itemsUnlinked);
1196     } else {
1197         // V2 - don't connect these if we have V3
1198         connect(d->mChangeRecorder, &Monitor::itemMoved, d, &AgentBasePrivate::itemMoved);
1199         connect(d->mChangeRecorder, &Monitor::itemRemoved, d, &AgentBasePrivate::itemRemoved);
1200         connect(d->mChangeRecorder, &Monitor::itemLinked, d, &AgentBasePrivate::itemLinked);
1201         connect(d->mChangeRecorder, &Monitor::itemUnlinked, d, &AgentBasePrivate::itemUnlinked);
1202     }
1203 }
1204 
1205 QString AgentBase::identifier() const
1206 {
1207     return d_ptr->mId;
1208 }
1209 
1210 void AgentBase::setAgentName(const QString &name)
1211 {
1212     Q_D(AgentBase);
1213     if (name == d->mName) {
1214         return;
1215     }
1216 
1217     // TODO: rename collection
1218     d->mName = name;
1219 
1220     if (d->mName.isEmpty() || d->mName == d->mId) {
1221         d->mSettings->remove(QStringLiteral("Resource/Name"));
1222         d->mSettings->remove(QStringLiteral("Agent/Name"));
1223     } else {
1224         d->mSettings->setValue(QStringLiteral("Agent/Name"), d->mName);
1225     }
1226 
1227     d->mSettings->sync();
1228 
1229     d->setProgramName();
1230 
1231     Q_EMIT agentNameChanged(d->mName);
1232 }
1233 
1234 QString AgentBase::agentName() const
1235 {
1236     Q_D(const AgentBase);
1237     if (d->mName.isEmpty()) {
1238         return d->mId;
1239     } else {
1240         return d->mName;
1241     }
1242 }
1243 
1244 void AgentBase::changeProcessed()
1245 {
1246     Q_D(AgentBase);
1247     d->changeProcessed();
1248 }
1249 
1250 ChangeRecorder *AgentBase::changeRecorder() const
1251 {
1252     return d_ptr->mChangeRecorder;
1253 }
1254 
1255 KSharedConfigPtr AgentBase::config()
1256 {
1257     return KSharedConfig::openConfig();
1258 }
1259 
1260 void AgentBase::abort()
1261 {
1262     Q_EMIT abortRequested();
1263 }
1264 
1265 void AgentBase::reconfigure()
1266 {
1267     Q_EMIT reloadConfiguration();
1268 }
1269 
1270 #include "moc_agentbase.cpp"
1271 #include "moc_agentbase_p.cpp"