File indexing completed on 2024-11-10 04:40:37

0001 /*
0002     SPDX-FileCopyrightText: 2006-2008 Tobias Koenig <tokoe@kde.org>
0003 
0004     SPDX-License-Identifier: LGPL-2.0-or-later
0005 */
0006 
0007 #include "agentmanager.h"
0008 #include "agentmanager_p.h"
0009 
0010 #include "agentinstance_p.h"
0011 #include "agenttype_p.h"
0012 #include "collection.h"
0013 #include "servermanager.h"
0014 #include <QDBusConnection>
0015 
0016 #include "shared/akranges.h"
0017 
0018 #include <QDBusServiceWatcher>
0019 #include <QWidget>
0020 
0021 using namespace Akonadi;
0022 using namespace AkRanges;
0023 
0024 // @cond PRIVATE
0025 
0026 AgentInstance AgentManagerPrivate::createInstance(const AgentType &type)
0027 {
0028     const QString &identifier = mManager->createAgentInstance(type.identifier());
0029     if (identifier.isEmpty()) {
0030         return AgentInstance();
0031     }
0032 
0033     return fillAgentInstanceLight(identifier);
0034 }
0035 
0036 void AgentManagerPrivate::agentTypeAdded(const QString &identifier)
0037 {
0038     // Ignore agent types we already know about, for example because we called
0039     // readAgentTypes before.
0040     if (mTypes.contains(identifier)) {
0041         return;
0042     }
0043 
0044     if (mTypes.isEmpty()) {
0045         // The Akonadi ServerManager assumes that the server is up and running as soon
0046         // as it knows about at least one agent type.
0047         // If we Q_EMIT the typeAdded() signal here, it therefore thinks the server is
0048         // running. However, the AgentManager does not know about all agent types yet,
0049         // as the server might still have pending agentTypeAdded() signals, even though
0050         // it internally knows all agent types already.
0051         // This can cause situations where the client gets told by the ServerManager that
0052         // the server is running, yet the client will encounter an error because the
0053         // AgentManager doesn't know all types yet.
0054         //
0055         // Therefore, we read all agent types from the server here so they are known.
0056         readAgentTypes();
0057     }
0058 
0059     const AgentType type = fillAgentType(identifier);
0060     if (type.isValid()) {
0061         mTypes.insert(identifier, type);
0062 
0063         Q_EMIT mParent->typeAdded(type);
0064     }
0065 }
0066 
0067 void AgentManagerPrivate::agentTypeRemoved(const QString &identifier)
0068 {
0069     if (!mTypes.contains(identifier)) {
0070         return;
0071     }
0072 
0073     const AgentType type = mTypes.take(identifier);
0074     Q_EMIT mParent->typeRemoved(type);
0075 }
0076 
0077 void AgentManagerPrivate::agentInstanceAdded(const QString &identifier)
0078 {
0079     const AgentInstance instance = fillAgentInstance(identifier);
0080     if (instance.isValid()) {
0081         // It is possible that this function is called when the instance is already
0082         // in our list we filled initially in the constructor.
0083         // This happens when the constructor is called during Akonadi startup, when
0084         // the agent processes are not fully loaded and have no D-Bus interface yet.
0085         // The server-side agent manager then emits the instance added signal when
0086         // the D-Bus interface for the agent comes up.
0087         // In this case, we simply notify that the instance status has changed.
0088         const bool newAgentInstance = !mInstances.contains(identifier);
0089         if (newAgentInstance) {
0090             mInstances.insert(identifier, instance);
0091             Q_EMIT mParent->instanceAdded(instance);
0092         } else {
0093             mInstances.remove(identifier);
0094             mInstances.insert(identifier, instance);
0095             Q_EMIT mParent->instanceStatusChanged(instance);
0096         }
0097     }
0098 }
0099 
0100 void AgentManagerPrivate::agentInstanceRemoved(const QString &identifier)
0101 {
0102     if (!mInstances.contains(identifier)) {
0103         return;
0104     }
0105 
0106     const AgentInstance instance = mInstances.take(identifier);
0107     Q_EMIT mParent->instanceRemoved(instance);
0108 }
0109 
0110 void AgentManagerPrivate::agentInstanceStatusChanged(const QString &identifier, int status, const QString &msg)
0111 {
0112     if (!mInstances.contains(identifier)) {
0113         return;
0114     }
0115 
0116     AgentInstance &instance = mInstances[identifier];
0117     instance.d->mStatus = status;
0118     instance.d->mStatusMessage = msg;
0119 
0120     Q_EMIT mParent->instanceStatusChanged(instance);
0121 }
0122 
0123 void AgentManagerPrivate::agentInstanceProgressChanged(const QString &identifier, uint progress, const QString &msg)
0124 {
0125     if (!mInstances.contains(identifier)) {
0126         return;
0127     }
0128 
0129     AgentInstance &instance = mInstances[identifier];
0130     instance.d->mProgress = progress;
0131     if (!msg.isEmpty()) {
0132         instance.d->mStatusMessage = msg;
0133     }
0134 
0135     Q_EMIT mParent->instanceProgressChanged(instance);
0136 }
0137 
0138 void AgentManagerPrivate::agentInstanceWarning(const QString &identifier, const QString &msg)
0139 {
0140     if (!mInstances.contains(identifier)) {
0141         return;
0142     }
0143 
0144     AgentInstance &instance = mInstances[identifier];
0145     Q_EMIT mParent->instanceWarning(instance, msg);
0146 }
0147 
0148 void AgentManagerPrivate::agentInstanceError(const QString &identifier, const QString &msg)
0149 {
0150     if (!mInstances.contains(identifier)) {
0151         return;
0152     }
0153 
0154     AgentInstance &instance = mInstances[identifier];
0155     Q_EMIT mParent->instanceError(instance, msg);
0156 }
0157 
0158 void AgentManagerPrivate::agentInstanceOnlineChanged(const QString &identifier, bool state)
0159 {
0160     if (!mInstances.contains(identifier)) {
0161         return;
0162     }
0163 
0164     AgentInstance &instance = mInstances[identifier];
0165     instance.d->mIsOnline = state;
0166     Q_EMIT mParent->instanceOnline(instance, state);
0167 }
0168 
0169 void AgentManagerPrivate::agentInstanceNameChanged(const QString &identifier, const QString &name)
0170 {
0171     if (!mInstances.contains(identifier)) {
0172         return;
0173     }
0174 
0175     AgentInstance &instance = mInstances[identifier];
0176     instance.d->mName = name;
0177 
0178     Q_EMIT mParent->instanceNameChanged(instance);
0179 }
0180 
0181 void AgentManagerPrivate::readAgentTypes()
0182 {
0183     const QDBusReply<QStringList> types = mManager->agentTypes();
0184     if (types.isValid()) {
0185         const QStringList lst = types.value();
0186         for (const QString &type : lst) {
0187             const AgentType agentType = fillAgentType(type);
0188             if (agentType.isValid()) {
0189                 mTypes.insert(type, agentType);
0190                 Q_EMIT mParent->typeAdded(agentType);
0191             }
0192         }
0193     }
0194 }
0195 
0196 void AgentManagerPrivate::readAgentInstances()
0197 {
0198     const QDBusReply<QStringList> instances = mManager->agentInstances();
0199     if (instances.isValid()) {
0200         const QStringList lst = instances.value();
0201         for (const QString &instance : lst) {
0202             const AgentInstance agentInstance = fillAgentInstance(instance);
0203             if (agentInstance.isValid()) {
0204                 mInstances.insert(instance, agentInstance);
0205                 Q_EMIT mParent->instanceAdded(agentInstance);
0206             }
0207         }
0208     }
0209 }
0210 
0211 AgentType AgentManagerPrivate::fillAgentType(const QString &identifier) const
0212 {
0213     AgentType type;
0214     type.d->mIdentifier = identifier;
0215     type.d->mName = mManager->agentName(identifier);
0216     type.d->mDescription = mManager->agentComment(identifier);
0217     type.d->mIconName = mManager->agentIcon(identifier);
0218     type.d->mMimeTypes = mManager->agentMimeTypes(identifier);
0219     type.d->mCapabilities = mManager->agentCapabilities(identifier);
0220     type.d->mCustomProperties = mManager->agentCustomProperties(identifier);
0221 
0222     return type;
0223 }
0224 
0225 void AgentManagerPrivate::setName(const AgentInstance &instance, const QString &name)
0226 {
0227     mManager->setAgentInstanceName(instance.identifier(), name);
0228 }
0229 
0230 void AgentManagerPrivate::setOnline(const AgentInstance &instance, bool state)
0231 {
0232     mManager->setAgentInstanceOnline(instance.identifier(), state);
0233 }
0234 
0235 void AgentManagerPrivate::configure(const AgentInstance &instance, QWidget *parent)
0236 {
0237     qlonglong winId = 0;
0238     if (parent) {
0239         winId = static_cast<qlonglong>(parent->window()->winId());
0240     }
0241 
0242     mManager->agentInstanceConfigure(instance.identifier(), winId);
0243 }
0244 
0245 void AgentManagerPrivate::synchronize(const AgentInstance &instance)
0246 {
0247     mManager->agentInstanceSynchronize(instance.identifier());
0248 }
0249 
0250 void AgentManagerPrivate::synchronizeCollectionTree(const AgentInstance &instance)
0251 {
0252     mManager->agentInstanceSynchronizeCollectionTree(instance.identifier());
0253 }
0254 
0255 void AgentManagerPrivate::synchronizeTags(const AgentInstance &instance)
0256 {
0257     mManager->agentInstanceSynchronizeTags(instance.identifier());
0258 }
0259 
0260 void AgentManagerPrivate::synchronizeRelations(const AgentInstance &instance)
0261 {
0262     mManager->agentInstanceSynchronizeRelations(instance.identifier());
0263 }
0264 
0265 AgentInstance AgentManagerPrivate::fillAgentInstance(const QString &identifier) const
0266 {
0267     AgentInstance instance;
0268 
0269     const QString agentTypeIdentifier = mManager->agentInstanceType(identifier);
0270     if (!mTypes.contains(agentTypeIdentifier)) {
0271         return instance;
0272     }
0273 
0274     instance.d->mType = mTypes.value(agentTypeIdentifier);
0275     instance.d->mIdentifier = identifier;
0276     instance.d->mName = mManager->agentInstanceName(identifier);
0277     instance.d->mStatus = mManager->agentInstanceStatus(identifier);
0278     instance.d->mStatusMessage = mManager->agentInstanceStatusMessage(identifier);
0279     instance.d->mProgress = mManager->agentInstanceProgress(identifier);
0280     instance.d->mIsOnline = mManager->agentInstanceOnline(identifier);
0281 
0282     return instance;
0283 }
0284 
0285 AgentInstance AgentManagerPrivate::fillAgentInstanceLight(const QString &identifier) const
0286 {
0287     AgentInstance instance;
0288 
0289     const QString agentTypeIdentifier = mManager->agentInstanceType(identifier);
0290     Q_ASSERT_X(mTypes.contains(agentTypeIdentifier), "fillAgentInstanceLight", "Requests non-existing agent type");
0291 
0292     instance.d->mType = mTypes.value(agentTypeIdentifier);
0293     instance.d->mIdentifier = identifier;
0294 
0295     return instance;
0296 }
0297 
0298 void AgentManagerPrivate::createDBusInterface()
0299 {
0300     mTypes.clear();
0301     mInstances.clear();
0302 
0303     using AgentManagerIface = org::freedesktop::Akonadi::AgentManager;
0304     mManager = std::make_unique<AgentManagerIface>(ServerManager::serviceName(ServerManager::Control),
0305                                                    QStringLiteral("/AgentManager"),
0306                                                    QDBusConnection::sessionBus(),
0307                                                    mParent);
0308 
0309     connect(mManager.get(), &AgentManagerIface::agentTypeAdded, this, &AgentManagerPrivate::agentTypeAdded);
0310     connect(mManager.get(), &AgentManagerIface::agentTypeRemoved, this, &AgentManagerPrivate::agentTypeRemoved);
0311     connect(mManager.get(), &AgentManagerIface::agentInstanceAdded, this, &AgentManagerPrivate::agentInstanceAdded);
0312     connect(mManager.get(), &AgentManagerIface::agentInstanceRemoved, this, &AgentManagerPrivate::agentInstanceRemoved);
0313     connect(mManager.get(), &AgentManagerIface::agentInstanceStatusChanged, this, &AgentManagerPrivate::agentInstanceStatusChanged);
0314     connect(mManager.get(), &AgentManagerIface::agentInstanceProgressChanged, this, &AgentManagerPrivate::agentInstanceProgressChanged);
0315     connect(mManager.get(), &AgentManagerIface::agentInstanceNameChanged, this, &AgentManagerPrivate::agentInstanceNameChanged);
0316     connect(mManager.get(), &AgentManagerIface::agentInstanceWarning, this, &AgentManagerPrivate::agentInstanceWarning);
0317     connect(mManager.get(), &AgentManagerIface::agentInstanceError, this, &AgentManagerPrivate::agentInstanceError);
0318     connect(mManager.get(), &AgentManagerIface::agentInstanceOnlineChanged, this, &AgentManagerPrivate::agentInstanceOnlineChanged);
0319 
0320     if (mManager->isValid()) {
0321         readAgentTypes();
0322         readAgentInstances();
0323     }
0324 }
0325 
0326 AgentManager *AgentManagerPrivate::mSelf = nullptr;
0327 
0328 AgentManager::AgentManager()
0329     : QObject(nullptr)
0330     , d(new AgentManagerPrivate(this))
0331 {
0332     // needed for queued connections on our signals
0333     qRegisterMetaType<Akonadi::AgentType>();
0334     qRegisterMetaType<Akonadi::AgentInstance>();
0335 
0336     d->createDBusInterface();
0337 
0338     d->mServiceWatcher = std::make_unique<QDBusServiceWatcher>(ServerManager::serviceName(ServerManager::Control),
0339                                                                QDBusConnection::sessionBus(),
0340                                                                QDBusServiceWatcher::WatchForRegistration);
0341     connect(d->mServiceWatcher.get(), &QDBusServiceWatcher::serviceRegistered, this, [this]() {
0342         if (d->mTypes.isEmpty()) { // just to be safe
0343             d->readAgentTypes();
0344         }
0345         if (d->mInstances.isEmpty()) {
0346             d->readAgentInstances();
0347         }
0348     });
0349 }
0350 
0351 /// @endcond
0352 
0353 AgentManager::~AgentManager() = default;
0354 
0355 AgentManager *AgentManager::self()
0356 {
0357     if (!AgentManagerPrivate::mSelf) {
0358         AgentManagerPrivate::mSelf = new AgentManager();
0359     }
0360 
0361     return AgentManagerPrivate::mSelf;
0362 }
0363 
0364 AgentType::List AgentManager::types() const
0365 {
0366     // Maybe the Control process is up and ready but we haven't been to the event loop yet so
0367     // QDBusServiceWatcher hasn't notified us yet.
0368     // In that case make sure to do it here, to avoid going into Broken state.
0369     if (d->mTypes.isEmpty()) {
0370         d->readAgentTypes();
0371     }
0372     return d->mTypes | Views::values | Actions::toQVector;
0373 }
0374 
0375 AgentType AgentManager::type(const QString &identifier) const
0376 {
0377     return d->mTypes.value(identifier);
0378 }
0379 
0380 AgentInstance::List AgentManager::instances() const
0381 {
0382     return d->mInstances | Views::values | Actions::toQVector;
0383 }
0384 
0385 AgentInstance AgentManager::instance(const QString &identifier) const
0386 {
0387     return d->mInstances.value(identifier);
0388 }
0389 
0390 void AgentManager::removeInstance(const AgentInstance &instance)
0391 {
0392     d->mManager->removeAgentInstance(instance.identifier());
0393 }
0394 
0395 void AgentManager::synchronizeCollection(const Collection &collection)
0396 {
0397     synchronizeCollection(collection, false);
0398 }
0399 
0400 void AgentManager::synchronizeCollection(const Collection &collection, bool recursive)
0401 {
0402     const QString resId = collection.resource();
0403     Q_ASSERT(!resId.isEmpty());
0404     d->mManager->agentInstanceSynchronizeCollection(resId, collection.id(), recursive);
0405 }
0406 
0407 #include "moc_agentmanager.cpp"
0408 
0409 #include "moc_agentmanager_p.cpp"