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

0001 /***************************************************************************
0002  *   SPDX-FileCopyrightText: 2006 Tobias Koenig <tokoe@kde.org>            *
0003  *   SPDX-FileCopyrightText: 2007 Volker Krause <vkrause@kde.org>          *
0004  *                                                                         *
0005  *   SPDX-License-Identifier: LGPL-2.0-or-later                            *
0006  ***************************************************************************/
0007 
0008 #include "agentmanager.h"
0009 
0010 #include "agentbrokeninstance.h"
0011 #include "agentmanageradaptor.h"
0012 #include "agentmanagerinternaladaptor.h"
0013 #include "agentprocessinstance.h"
0014 #include "agentserverinterface.h"
0015 #include "agentthreadinstance.h"
0016 #include "akonadicontrol_debug.h"
0017 #include "preprocessor_manager.h"
0018 #include "processcontrol.h"
0019 #include "resource_manager.h"
0020 #include "serverinterface.h"
0021 
0022 #include "private/dbus_p.h"
0023 #include "private/instance_p.h"
0024 #include "private/protocol_p.h"
0025 #include "private/standarddirs_p.h"
0026 
0027 #include "shared/akapplication.h"
0028 
0029 #include <QCoreApplication>
0030 #include <QDBusConnection>
0031 #include <QDir>
0032 #include <QScopedPointer>
0033 #include <QSettings>
0034 
0035 using Akonadi::ProcessControl;
0036 using namespace std::chrono_literals;
0037 
0038 static const bool enableAgentServerDefault = false;
0039 
0040 class StorageProcessControl : public Akonadi::ProcessControl
0041 {
0042     Q_OBJECT
0043 public:
0044     explicit StorageProcessControl(const QStringList &args)
0045     {
0046         setShutdownTimeout(15s);
0047         connect(this, &Akonadi::ProcessControl::unableToStart, this, []() {
0048             QCoreApplication::instance()->exit(255);
0049         });
0050         start(QStringLiteral("akonadiserver"), args, RestartOnCrash);
0051     }
0052 
0053     ~StorageProcessControl() override
0054     {
0055         setCrashPolicy(ProcessControl::StopOnCrash);
0056         org::freedesktop::Akonadi::Server serverIface(Akonadi::DBus::serviceName(Akonadi::DBus::Server),
0057                                                       QStringLiteral("/Server"),
0058                                                       QDBusConnection::sessionBus());
0059         serverIface.quit();
0060     }
0061 };
0062 
0063 class AgentServerProcessControl : public Akonadi::ProcessControl
0064 {
0065     Q_OBJECT
0066 public:
0067     explicit AgentServerProcessControl(const QStringList &args)
0068     {
0069         connect(this, &Akonadi::ProcessControl::unableToStart, this, []() {
0070             qCCritical(AKONADICONTROL_LOG) << "Failed to start AgentServer!";
0071         });
0072         start(QStringLiteral("akonadi_agent_server"), args, RestartOnCrash);
0073     }
0074 
0075     ~AgentServerProcessControl() override
0076     {
0077         setCrashPolicy(ProcessControl::StopOnCrash);
0078         org::freedesktop::Akonadi::AgentServer agentServerIface(Akonadi::DBus::serviceName(Akonadi::DBus::AgentServer),
0079                                                                 QStringLiteral("/AgentServer"),
0080                                                                 QDBusConnection::sessionBus(),
0081                                                                 this);
0082         agentServerIface.quit();
0083     }
0084 };
0085 
0086 AgentManager::AgentManager(bool verbose, QObject *parent)
0087     : QObject(parent)
0088     , mAgentServer(nullptr)
0089     , mVerbose(verbose)
0090 {
0091     new AgentManagerAdaptor(this);
0092     new AgentManagerInternalAdaptor(this);
0093     QDBusConnection::sessionBus().registerObject(QStringLiteral("/AgentManager"), this);
0094 
0095     connect(QDBusConnection::sessionBus().interface(), &QDBusConnectionInterface::serviceOwnerChanged, this, &AgentManager::serviceOwnerChanged);
0096 
0097     if (QDBusConnection::sessionBus().interface()->isServiceRegistered(Akonadi::DBus::serviceName(Akonadi::DBus::Server))) {
0098         qFatal("akonadiserver already running!");
0099     }
0100 
0101     const QSettings settings(Akonadi::StandardDirs::agentsConfigFile(Akonadi::StandardDirs::ReadOnly), QSettings::IniFormat);
0102     mAgentServerEnabled = settings.value(QStringLiteral("AgentServer/Enabled"), enableAgentServerDefault).toBool();
0103 
0104     QStringList serviceArgs;
0105     if (Akonadi::Instance::hasIdentifier()) {
0106         serviceArgs << QStringLiteral("--instance") << Akonadi::Instance::identifier();
0107     }
0108     if (verbose) {
0109         serviceArgs << QStringLiteral("--verbose");
0110     }
0111 
0112     mStorageController = std::unique_ptr<Akonadi::ProcessControl>(new StorageProcessControl(serviceArgs));
0113 
0114     if (mAgentServerEnabled) {
0115         mAgentServer = std::unique_ptr<Akonadi::ProcessControl>(new AgentServerProcessControl(serviceArgs));
0116     }
0117 }
0118 
0119 void AgentManager::continueStartup()
0120 {
0121     // prevent multiple calls in case the server has to be restarted
0122     static bool first = true;
0123     if (!first) {
0124         return;
0125     }
0126 
0127     first = false;
0128 
0129     readPluginInfos();
0130     for (const AgentType &info : std::as_const(mAgents)) {
0131         Q_EMIT agentTypeAdded(info.identifier);
0132     }
0133 
0134     load();
0135     for (const AgentType &info : std::as_const(mAgents)) {
0136         ensureAutoStart(info);
0137     }
0138 
0139     // register the real service name once everything is up an running
0140     if (!QDBusConnection::sessionBus().registerService(Akonadi::DBus::serviceName(Akonadi::DBus::Control))) {
0141         // besides a race with an older Akonadi server I have no idea how we could possibly get here...
0142         qFatal("Unable to register service as %s despite having the lock. Error was: %s",
0143                qPrintable(Akonadi::DBus::serviceName(Akonadi::DBus::Control)),
0144                qPrintable(QDBusConnection::sessionBus().lastError().message()));
0145     }
0146     qCInfo(AKONADICONTROL_LOG) << "Akonadi server is now operational.";
0147 }
0148 
0149 AgentManager::~AgentManager()
0150 {
0151     cleanup();
0152 }
0153 
0154 void AgentManager::cleanup()
0155 {
0156     for (const AgentInstance::Ptr &instance : std::as_const(mAgentInstances)) {
0157         instance->quit();
0158     }
0159     mAgentInstances.clear();
0160 
0161     mStorageController.reset();
0162     mStorageController.reset();
0163 }
0164 
0165 QStringList AgentManager::agentTypes() const
0166 {
0167     return mAgents.keys();
0168 }
0169 
0170 QString AgentManager::agentName(const QString &identifier) const
0171 {
0172     if (!checkAgentExists(identifier)) {
0173         return QString();
0174     }
0175 
0176     return mAgents.value(identifier).name;
0177 }
0178 
0179 QString AgentManager::agentComment(const QString &identifier) const
0180 {
0181     if (!checkAgentExists(identifier)) {
0182         return QString();
0183     }
0184 
0185     return mAgents.value(identifier).comment;
0186 }
0187 
0188 QString AgentManager::agentIcon(const QString &identifier) const
0189 {
0190     if (!checkAgentExists(identifier)) {
0191         return QString();
0192     }
0193 
0194     const AgentType info = mAgents.value(identifier);
0195     if (!info.icon.isEmpty()) {
0196         return info.icon;
0197     }
0198 
0199     return QStringLiteral("application-x-executable");
0200 }
0201 
0202 QStringList AgentManager::agentMimeTypes(const QString &identifier) const
0203 {
0204     if (!checkAgentExists(identifier)) {
0205         return QStringList();
0206     }
0207 
0208     return mAgents.value(identifier).mimeTypes;
0209 }
0210 
0211 QStringList AgentManager::agentCapabilities(const QString &identifier) const
0212 {
0213     if (!checkAgentExists(identifier)) {
0214         return QStringList();
0215     }
0216     return mAgents.value(identifier).capabilities;
0217 }
0218 
0219 QVariantMap AgentManager::agentCustomProperties(const QString &identifier) const
0220 {
0221     if (!checkAgentExists(identifier)) {
0222         return QVariantMap();
0223     }
0224 
0225     return mAgents.value(identifier).custom;
0226 }
0227 
0228 AgentInstance::Ptr AgentManager::createAgentInstance(const AgentType &info)
0229 {
0230     switch (info.launchMethod) {
0231     case AgentType::Server:
0232         return QSharedPointer<Akonadi::AgentThreadInstance>::create(*this);
0233     case AgentType::Launcher: // Fall through
0234     case AgentType::Process:
0235         return QSharedPointer<Akonadi::AgentProcessInstance>::create(*this);
0236     default:
0237         Q_ASSERT_X(false, "AgentManger::createAgentInstance", "Unhandled AgentType::LaunchMethod case");
0238     }
0239 
0240     return AgentInstance::Ptr();
0241 }
0242 
0243 QString AgentManager::createAgentInstance(const QString &identifier)
0244 {
0245     if (!checkAgentExists(identifier)) {
0246         return QString();
0247     }
0248 
0249     const AgentType agentInfo = mAgents[identifier];
0250     mAgents[identifier].instanceCounter++;
0251 
0252     const auto instance = createAgentInstance(agentInfo);
0253     if (agentInfo.capabilities.contains(AgentType::CapabilityUnique)) {
0254         instance->setIdentifier(identifier);
0255     } else {
0256         instance->setIdentifier(QStringLiteral("%1_%2").arg(identifier, QString::number(agentInfo.instanceCounter)));
0257     }
0258 
0259     const QString instanceIdentifier = instance->identifier();
0260     if (mAgentInstances.contains(instanceIdentifier)) {
0261         qCWarning(AKONADICONTROL_LOG) << "Cannot create another instance of agent" << identifier;
0262         return QString();
0263     }
0264 
0265     // Return from this dbus call before we do the next. Otherwise dbus brakes for
0266     // this process.
0267     if (calledFromDBus()) {
0268         connection().send(message().createReply(instanceIdentifier));
0269     }
0270 
0271     if (!instance->start(agentInfo)) {
0272         return QString();
0273     }
0274 
0275     mAgentInstances.insert(instanceIdentifier, instance);
0276     registerAgentAtServer(instanceIdentifier, agentInfo);
0277     save();
0278 
0279     return instanceIdentifier;
0280 }
0281 
0282 void AgentManager::removeAgentInstance(const QString &identifier)
0283 {
0284     const auto instance = mAgentInstances.value(identifier);
0285     if (!instance) {
0286         qCWarning(AKONADICONTROL_LOG) << Q_FUNC_INFO << "Agent instance with identifier" << identifier << "does not exist";
0287         return;
0288     }
0289 
0290     if (instance->hasAgentInterface()) {
0291         instance->cleanup();
0292     } else {
0293         qCWarning(AKONADICONTROL_LOG) << "Agent instance" << identifier << "has no interface!";
0294     }
0295 
0296     mAgentInstances.remove(identifier);
0297 
0298     save();
0299 
0300     org::freedesktop::Akonadi::ResourceManager resmanager(Akonadi::DBus::serviceName(Akonadi::DBus::Server),
0301                                                           QStringLiteral("/ResourceManager"),
0302                                                           QDBusConnection::sessionBus(),
0303                                                           this);
0304     resmanager.removeResourceInstance(instance->identifier());
0305 
0306     // Kill the preprocessor instance, if any.
0307     org::freedesktop::Akonadi::PreprocessorManager preProcessorManager(Akonadi::DBus::serviceName(Akonadi::DBus::Server),
0308                                                                        QStringLiteral("/PreprocessorManager"),
0309                                                                        QDBusConnection::sessionBus(),
0310                                                                        this);
0311 
0312     preProcessorManager.unregisterInstance(instance->identifier());
0313 
0314     if (instance->hasAgentInterface()) {
0315         qCDebug(AKONADICONTROL_LOG) << "AgentManager::removeAgentInstance: calling instance->quit()";
0316         instance->quit();
0317     } else {
0318         qCWarning(AKONADICONTROL_LOG) << "Agent instance" << identifier << "has no interface!";
0319     }
0320 
0321     Q_EMIT agentInstanceRemoved(identifier);
0322 }
0323 
0324 QString AgentManager::agentInstanceType(const QString &identifier)
0325 {
0326     const AgentInstance::Ptr agent = mAgentInstances.value(identifier);
0327     if (!agent) {
0328         qCWarning(AKONADICONTROL_LOG) << "Agent instance with identifier" << identifier << "does not exist";
0329         return QString();
0330     }
0331 
0332     return agent->agentType();
0333 }
0334 
0335 QStringList AgentManager::agentInstances() const
0336 {
0337     return mAgentInstances.keys();
0338 }
0339 
0340 int AgentManager::agentInstanceStatus(const QString &identifier) const
0341 {
0342     if (!checkInstance(identifier)) {
0343         return 2;
0344     }
0345 
0346     return mAgentInstances.value(identifier)->status();
0347 }
0348 
0349 QString AgentManager::agentInstanceStatusMessage(const QString &identifier) const
0350 {
0351     if (!checkInstance(identifier)) {
0352         return QString();
0353     }
0354 
0355     return mAgentInstances.value(identifier)->statusMessage();
0356 }
0357 
0358 uint AgentManager::agentInstanceProgress(const QString &identifier) const
0359 {
0360     if (!checkInstance(identifier)) {
0361         return 0;
0362     }
0363 
0364     return mAgentInstances.value(identifier)->progress();
0365 }
0366 
0367 QString AgentManager::agentInstanceProgressMessage(const QString &identifier) const
0368 {
0369     Q_UNUSED(identifier)
0370 
0371     return QString();
0372 }
0373 
0374 void AgentManager::agentInstanceConfigure(const QString &identifier, qlonglong windowId)
0375 {
0376     if (!checkAgentInterfaces(identifier, QStringLiteral("agentInstanceConfigure"))) {
0377         return;
0378     }
0379 
0380     mAgentInstances.value(identifier)->configure(windowId);
0381 }
0382 
0383 bool AgentManager::agentInstanceOnline(const QString &identifier)
0384 {
0385     if (!checkInstance(identifier)) {
0386         return false;
0387     }
0388 
0389     return mAgentInstances.value(identifier)->isOnline();
0390 }
0391 
0392 void AgentManager::setAgentInstanceOnline(const QString &identifier, bool state)
0393 {
0394     if (!checkAgentInterfaces(identifier, QStringLiteral("setAgentInstanceOnline"))) {
0395         return;
0396     }
0397 
0398     mAgentInstances.value(identifier)->statusInterface()->setOnline(state);
0399 }
0400 
0401 // resource specific methods //
0402 void AgentManager::setAgentInstanceName(const QString &identifier, const QString &name)
0403 {
0404     if (!checkResourceInterface(identifier, QStringLiteral("setAgentInstanceName"))) {
0405         return;
0406     }
0407 
0408     mAgentInstances.value(identifier)->resourceInterface()->setName(name);
0409 }
0410 
0411 QString AgentManager::agentInstanceName(const QString &identifier) const
0412 {
0413     if (!checkInstance(identifier)) {
0414         return QString();
0415     }
0416 
0417     const AgentInstance::Ptr instance = mAgentInstances.value(identifier);
0418     if (!instance->resourceName().isEmpty()) {
0419         return instance->resourceName();
0420     }
0421 
0422     if (!checkAgentExists(instance->agentType())) {
0423         return QString();
0424     }
0425 
0426     return mAgents.value(instance->agentType()).name;
0427 }
0428 
0429 void AgentManager::agentInstanceSynchronize(const QString &identifier)
0430 {
0431     if (!checkResourceInterface(identifier, QStringLiteral("agentInstanceSynchronize"))) {
0432         return;
0433     }
0434 
0435     mAgentInstances.value(identifier)->resourceInterface()->synchronize();
0436 }
0437 
0438 void AgentManager::agentInstanceSynchronizeCollectionTree(const QString &identifier)
0439 {
0440     if (!checkResourceInterface(identifier, QStringLiteral("agentInstanceSynchronizeCollectionTree"))) {
0441         return;
0442     }
0443 
0444     mAgentInstances.value(identifier)->resourceInterface()->synchronizeCollectionTree();
0445 }
0446 
0447 void AgentManager::agentInstanceSynchronizeCollection(const QString &identifier, qint64 collection)
0448 {
0449     agentInstanceSynchronizeCollection(identifier, collection, false);
0450 }
0451 
0452 void AgentManager::agentInstanceSynchronizeCollection(const QString &identifier, qint64 collection, bool recursive)
0453 {
0454     if (!checkResourceInterface(identifier, QStringLiteral("agentInstanceSynchronizeCollection"))) {
0455         return;
0456     }
0457 
0458     mAgentInstances.value(identifier)->resourceInterface()->synchronizeCollection(collection, recursive);
0459 }
0460 
0461 void AgentManager::agentInstanceSynchronizeTags(const QString &identifier)
0462 {
0463     if (!checkResourceInterface(identifier, QStringLiteral("agentInstanceSynchronizeTags"))) {
0464         return;
0465     }
0466 
0467     mAgentInstances.value(identifier)->resourceInterface()->synchronizeTags();
0468 }
0469 
0470 void AgentManager::agentInstanceSynchronizeRelations(const QString &identifier)
0471 {
0472     if (!checkResourceInterface(identifier, QStringLiteral("agentInstanceSynchronizeRelations"))) {
0473         return;
0474     }
0475 
0476     mAgentInstances.value(identifier)->resourceInterface()->synchronizeRelations();
0477 }
0478 
0479 void AgentManager::restartAgentInstance(const QString &identifier)
0480 {
0481     if (!checkInstance(identifier)) {
0482         return;
0483     }
0484 
0485     mAgentInstances.value(identifier)->restartWhenIdle();
0486 }
0487 
0488 void AgentManager::updatePluginInfos()
0489 {
0490     const QHash<QString, AgentType> oldInfos = mAgents;
0491     readPluginInfos();
0492 
0493     for (const AgentType &oldInfo : oldInfos) {
0494         if (!mAgents.contains(oldInfo.identifier)) {
0495             Q_EMIT agentTypeRemoved(oldInfo.identifier);
0496         }
0497     }
0498 
0499     for (const AgentType &newInfo : std::as_const(mAgents)) {
0500         if (!oldInfos.contains(newInfo.identifier)) {
0501             Q_EMIT agentTypeAdded(newInfo.identifier);
0502             ensureAutoStart(newInfo);
0503         }
0504     }
0505 }
0506 
0507 void AgentManager::readPluginInfos()
0508 {
0509     mAgents.clear();
0510 
0511     const QStringList pathList = pluginInfoPathList();
0512 
0513     for (const QString &path : pathList) {
0514         const QDir directory(path, QStringLiteral("*.desktop"));
0515         readPluginInfos(directory);
0516     }
0517 }
0518 
0519 void AgentManager::readPluginInfos(const QDir &directory)
0520 {
0521     const QStringList files = directory.entryList();
0522     qCDebug(AKONADICONTROL_LOG) << "PLUGINS: " << directory.canonicalPath();
0523     qCDebug(AKONADICONTROL_LOG) << "PLUGINS: " << files;
0524 
0525     for (int i = 0; i < files.count(); ++i) {
0526         const QString fileName = directory.absoluteFilePath(files[i]);
0527 
0528         AgentType agentInfo;
0529         if (agentInfo.load(fileName, this)) {
0530             if (mAgents.contains(agentInfo.identifier)) {
0531                 qCWarning(AKONADICONTROL_LOG) << "Duplicated agent identifier" << agentInfo.identifier << "from file" << fileName;
0532                 continue;
0533             }
0534 
0535             const QString disableAutostart = akGetEnv("AKONADI_DISABLE_AGENT_AUTOSTART");
0536             if (!disableAutostart.isEmpty()) {
0537                 qCDebug(AKONADICONTROL_LOG) << "Autostarting of agents is disabled.";
0538                 agentInfo.capabilities.removeOne(AgentType::CapabilityAutostart);
0539             }
0540 
0541             if (!mAgentServerEnabled && agentInfo.launchMethod == AgentType::Server) {
0542                 agentInfo.launchMethod = AgentType::Launcher;
0543             }
0544 
0545             if (agentInfo.launchMethod == AgentType::Process) {
0546                 const QString executable = Akonadi::StandardDirs::findExecutable(agentInfo.exec);
0547                 if (executable.isEmpty()) {
0548                     qCWarning(AKONADICONTROL_LOG) << "Executable" << agentInfo.exec << "for agent" << agentInfo.identifier << "could not be found!";
0549                     continue;
0550                 }
0551             }
0552 
0553             qCDebug(AKONADICONTROL_LOG) << "PLUGINS inserting: " << agentInfo.identifier << agentInfo.instanceCounter << agentInfo.capabilities;
0554             mAgents.insert(agentInfo.identifier, agentInfo);
0555         }
0556     }
0557 }
0558 
0559 QStringList AgentManager::pluginInfoPathList()
0560 {
0561     return Akonadi::StandardDirs::locateAllResourceDirs(QStringLiteral("akonadi/agents"));
0562 }
0563 
0564 void AgentManager::load()
0565 {
0566     org::freedesktop::Akonadi::ResourceManager resmanager(Akonadi::DBus::serviceName(Akonadi::DBus::Server),
0567                                                           QStringLiteral("/ResourceManager"),
0568                                                           QDBusConnection::sessionBus(),
0569                                                           this);
0570     const QStringList knownResources = resmanager.resourceInstances();
0571 
0572     QSettings file(Akonadi::StandardDirs::agentsConfigFile(Akonadi::StandardDirs::ReadOnly), QSettings::IniFormat);
0573     file.beginGroup(QStringLiteral("Instances"));
0574     const QStringList entries = file.childGroups();
0575     for (int i = 0; i < entries.count(); ++i) {
0576         const QString instanceIdentifier = entries[i];
0577 
0578         if (mAgentInstances.contains(instanceIdentifier)) {
0579             qCWarning(AKONADICONTROL_LOG) << "Duplicated instance identifier" << instanceIdentifier << "found in agentsrc";
0580             continue;
0581         }
0582 
0583         file.beginGroup(entries[i]);
0584 
0585         const QString agentType = file.value(QStringLiteral("AgentType")).toString();
0586         const auto typeIter = mAgents.constFind(agentType);
0587         if (typeIter == mAgents.cend() || typeIter->exec.isEmpty()) {
0588             qCWarning(AKONADICONTROL_LOG) << "Reference to unknown agent type" << agentType << "in agentsrc, creating a fake entry.";
0589             if (typeIter == mAgents.cend()) {
0590                 AgentType type;
0591                 type.identifier = type.name = agentType;
0592                 mAgents.insert(type.identifier, type);
0593             }
0594 
0595             auto brokenInstance = AgentInstance::Ptr{new Akonadi::AgentBrokenInstance{agentType, *this}};
0596             brokenInstance->setIdentifier(instanceIdentifier);
0597             mAgentInstances.insert(instanceIdentifier, brokenInstance);
0598             file.endGroup();
0599             continue;
0600         }
0601 
0602         const AgentType &type = *typeIter;
0603 
0604         // recover if the db has been deleted in the meantime or got otherwise corrupted
0605         if (!knownResources.contains(instanceIdentifier) && type.capabilities.contains(AgentType::CapabilityResource)) {
0606             qCDebug(AKONADICONTROL_LOG) << "Recovering instance" << instanceIdentifier << "after database loss";
0607             registerAgentAtServer(instanceIdentifier, type);
0608         }
0609 
0610         const AgentInstance::Ptr instance = createAgentInstance(type);
0611         instance->setIdentifier(instanceIdentifier);
0612         if (instance->start(type)) {
0613             mAgentInstances.insert(instanceIdentifier, instance);
0614         }
0615 
0616         file.endGroup();
0617     }
0618 
0619     file.endGroup();
0620 }
0621 
0622 void AgentManager::save()
0623 {
0624     QSettings file(Akonadi::StandardDirs::agentsConfigFile(Akonadi::StandardDirs::WriteOnly), QSettings::IniFormat);
0625 
0626     for (const AgentType &info : std::as_const(mAgents)) {
0627         info.save(&file);
0628     }
0629 
0630     file.beginGroup(QStringLiteral("Instances"));
0631     file.remove(QString());
0632     for (const AgentInstance::Ptr &instance : std::as_const(mAgentInstances)) {
0633         file.beginGroup(instance->identifier());
0634         file.setValue(QStringLiteral("AgentType"), instance->agentType());
0635         file.endGroup();
0636     }
0637 
0638     file.endGroup();
0639 }
0640 
0641 void AgentManager::serviceOwnerChanged(const QString &name, const QString &oldOwner, const QString &newOwner)
0642 {
0643     Q_UNUSED(oldOwner)
0644     // This is called by the D-Bus server when a service comes up, goes down or changes ownership for some reason
0645     // and this is where we "hook up" our different Agent interfaces.
0646 
0647     // Ignore DBus address name (e.g. :1.310)
0648     if (name.startsWith(QLatin1Char(':'))) {
0649         return;
0650     }
0651 
0652     // Ignore services belonging to another Akonadi instance
0653     const auto parsedInstance = Akonadi::DBus::parseInstanceIdentifier(name);
0654     const auto currentInstance = Akonadi::Instance::hasIdentifier() ? std::optional<QString>(Akonadi::Instance::identifier()) : std::nullopt;
0655     if (parsedInstance != currentInstance) {
0656         return;
0657     }
0658 
0659     qCDebug(AKONADICONTROL_LOG) << "Service" << name << "owner changed from" << oldOwner << "to" << newOwner;
0660 
0661     if ((name == Akonadi::DBus::serviceName(Akonadi::DBus::Server) || name == Akonadi::DBus::serviceName(Akonadi::DBus::AgentServer)) && !newOwner.isEmpty()) {
0662         if (QDBusConnection::sessionBus().interface()->isServiceRegistered(Akonadi::DBus::serviceName(Akonadi::DBus::Server))
0663             && (!mAgentServer || QDBusConnection::sessionBus().interface()->isServiceRegistered(Akonadi::DBus::serviceName(Akonadi::DBus::AgentServer)))) {
0664             // server is operational, start agents
0665             continueStartup();
0666         }
0667     }
0668 
0669     const auto service = Akonadi::DBus::parseAgentServiceName(name);
0670     if (!service.has_value()) {
0671         return;
0672     }
0673     switch (service->agentType) {
0674     case Akonadi::DBus::Agent: {
0675         // An agent service went up or down
0676         if (newOwner.isEmpty()) {
0677             return; // It went down: we don't care here.
0678         }
0679 
0680         if (!mAgentInstances.contains(service->identifier)) {
0681             return;
0682         }
0683 
0684         const AgentInstance::Ptr instance = mAgentInstances.value(service->identifier);
0685         const bool restarting = instance->hasAgentInterface();
0686         if (!instance->obtainAgentInterface()) {
0687             return;
0688         }
0689 
0690         Q_ASSERT(mAgents.contains(instance->agentType()));
0691         const bool isResource = mAgents.value(instance->agentType()).capabilities.contains(AgentType::CapabilityResource);
0692 
0693         if (!restarting && (!isResource || instance->hasResourceInterface())) {
0694             Q_EMIT agentInstanceAdded(service->identifier);
0695         }
0696 
0697         break;
0698     }
0699     case Akonadi::DBus::Resource: {
0700         // A resource service went up or down
0701         if (newOwner.isEmpty()) {
0702             return; // It went down: we don't care here.
0703         }
0704 
0705         if (!mAgentInstances.contains(service->identifier)) {
0706             return;
0707         }
0708 
0709         const AgentInstance::Ptr instance = mAgentInstances.value(service->identifier);
0710         const bool restarting = instance->hasResourceInterface();
0711         if (!instance->obtainResourceInterface()) {
0712             return;
0713         }
0714 
0715         if (!restarting && instance->hasAgentInterface()) {
0716             Q_EMIT agentInstanceAdded(service->identifier);
0717         }
0718 
0719         break;
0720     }
0721     case Akonadi::DBus::Preprocessor: {
0722         // A preprocessor service went up or down
0723 
0724         // If the preprocessor is going up then the org.freedesktop.Akonadi.Agent.* interface
0725         // should be already up (as it's registered before the preprocessor one).
0726         // So if we don't know about the preprocessor as agent instance
0727         // then it's not our preprocessor.
0728 
0729         // If the preprocessor is going down then either the agent interface already
0730         // went down (and it has been already unregistered on the manager side)
0731         // or it's still registered as agent and WE have to unregister it.
0732         // The order of interface deletions depends on Qt but we handle both cases.
0733 
0734         // Check if we "know" about it.
0735         qCDebug(AKONADICONTROL_LOG) << "Preprocessor " << service->identifier << " is going up or down...";
0736 
0737         if (!mAgentInstances.contains(service->identifier)) {
0738             qCDebug(AKONADICONTROL_LOG) << "But it isn't registered as agent... not mine (anymore?)";
0739             return; // not our agent (?)
0740         }
0741 
0742         org::freedesktop::Akonadi::PreprocessorManager preProcessorManager(Akonadi::DBus::serviceName(Akonadi::DBus::Server),
0743                                                                            QStringLiteral("/PreprocessorManager"),
0744                                                                            QDBusConnection::sessionBus(),
0745                                                                            this);
0746 
0747         if (!preProcessorManager.isValid()) {
0748             qCWarning(AKONADICONTROL_LOG) << "Could not connect to PreprocessorManager via D-Bus:" << preProcessorManager.lastError().message();
0749         } else {
0750             if (newOwner.isEmpty()) {
0751                 // The preprocessor went down. Unregister it on server side.
0752 
0753                 preProcessorManager.unregisterInstance(service->identifier);
0754 
0755             } else {
0756                 // The preprocessor went up. Register it on server side.
0757 
0758                 if (!mAgentInstances.value(service->identifier)->obtainPreprocessorInterface()) {
0759                     // Hm.. couldn't hook up its preprocessor interface..
0760                     // Make sure we don't have it in the preprocessor chain
0761                     qCWarning(AKONADICONTROL_LOG) << "Couldn't obtain preprocessor interface for instance" << service->identifier;
0762 
0763                     preProcessorManager.unregisterInstance(service->identifier);
0764                     return;
0765                 }
0766 
0767                 qCDebug(AKONADICONTROL_LOG) << "Registering preprocessor instance" << service->identifier;
0768 
0769                 // Add to the preprocessor chain
0770                 preProcessorManager.registerInstance(service->identifier);
0771             }
0772         }
0773 
0774         break;
0775     }
0776     default:
0777         break;
0778     }
0779 }
0780 
0781 bool AgentManager::checkInstance(const QString &identifier) const
0782 {
0783     if (!mAgentInstances.contains(identifier)) {
0784         qCWarning(AKONADICONTROL_LOG) << "Agent instance with identifier " << identifier << " does not exist";
0785         return false;
0786     }
0787 
0788     return true;
0789 }
0790 
0791 bool AgentManager::checkResourceInterface(const QString &identifier, const QString &method) const
0792 {
0793     if (!checkInstance(identifier)) {
0794         return false;
0795     }
0796 
0797     if (!mAgents[mAgentInstances[identifier]->agentType()].capabilities.contains(QLatin1StringView("Resource"))) {
0798         return false;
0799     }
0800 
0801     if (!mAgentInstances[identifier]->hasResourceInterface()) {
0802         qCWarning(AKONADICONTROL_LOG) << QLatin1StringView("AgentManager::") + method << " Agent instance " << identifier << " has no resource interface!";
0803         return false;
0804     }
0805 
0806     return true;
0807 }
0808 
0809 bool AgentManager::checkAgentExists(const QString &identifier) const
0810 {
0811     if (!mAgents.contains(identifier)) {
0812         qCWarning(AKONADICONTROL_LOG) << "Agent instance " << identifier << " does not exist.";
0813         return false;
0814     }
0815 
0816     return true;
0817 }
0818 
0819 bool AgentManager::checkAgentInterfaces(const QString &identifier, const QString &method) const
0820 {
0821     if (!checkInstance(identifier)) {
0822         return false;
0823     }
0824 
0825     if (!mAgentInstances.value(identifier)->hasAgentInterface()) {
0826         qCWarning(AKONADICONTROL_LOG) << "Agent instance (" << method << ") " << identifier << " has no agent interface.";
0827         return false;
0828     }
0829 
0830     return true;
0831 }
0832 
0833 void AgentManager::ensureAutoStart(const AgentType &info)
0834 {
0835     if (!info.capabilities.contains(AgentType::CapabilityAutostart)) {
0836         return; // no an autostart agent
0837     }
0838 
0839     org::freedesktop::Akonadi::AgentServer agentServer(Akonadi::DBus::serviceName(Akonadi::DBus::AgentServer),
0840                                                        QStringLiteral("/AgentServer"),
0841                                                        QDBusConnection::sessionBus(),
0842                                                        this);
0843 
0844     if (mAgentInstances.contains(info.identifier) || (agentServer.isValid() && agentServer.started(info.identifier))) {
0845         return; // already running
0846     }
0847 
0848     const AgentInstance::Ptr instance = createAgentInstance(info);
0849     instance->setIdentifier(info.identifier);
0850     if (instance->start(info)) {
0851         mAgentInstances.insert(instance->identifier(), instance);
0852         registerAgentAtServer(instance->identifier(), info);
0853         save();
0854     }
0855 }
0856 
0857 void AgentManager::agentExeChanged(const QString &fileName)
0858 {
0859     if (!QFile::exists(fileName)) {
0860         return;
0861     }
0862 
0863     for (const AgentType &type : std::as_const(mAgents)) {
0864         if (fileName.endsWith(type.exec)) {
0865             for (const AgentInstance::Ptr &instance : std::as_const(mAgentInstances)) {
0866                 if (instance->agentType() == type.identifier) {
0867                     instance->restartWhenIdle();
0868                 }
0869             }
0870         }
0871     }
0872 }
0873 
0874 void AgentManager::registerAgentAtServer(const QString &agentIdentifier, const AgentType &type)
0875 {
0876     if (type.capabilities.contains(AgentType::CapabilityResource)) {
0877         QScopedPointer<org::freedesktop::Akonadi::ResourceManager> resmanager(
0878             new org::freedesktop::Akonadi::ResourceManager(Akonadi::DBus::serviceName(Akonadi::DBus::Server),
0879                                                            QStringLiteral("/ResourceManager"),
0880                                                            QDBusConnection::sessionBus(),
0881                                                            this));
0882         resmanager->addResourceInstance(agentIdentifier, type.capabilities);
0883     }
0884 }
0885 
0886 void AgentManager::addSearch(const QString &query, const QString &queryLanguage, qint64 resultCollectionId)
0887 {
0888     qCDebug(AKONADICONTROL_LOG) << "AgentManager::addSearch" << query << queryLanguage << resultCollectionId;
0889     for (const AgentInstance::Ptr &instance : std::as_const(mAgentInstances)) {
0890         const AgentType type = mAgents.value(instance->agentType());
0891         if (type.capabilities.contains(AgentType::CapabilitySearch) && instance->searchInterface()) {
0892             instance->searchInterface()->addSearch(query, queryLanguage, resultCollectionId);
0893         }
0894     }
0895 }
0896 
0897 void AgentManager::removeSearch(quint64 resultCollectionId)
0898 {
0899     qCDebug(AKONADICONTROL_LOG) << "AgentManager::removeSearch" << resultCollectionId;
0900     for (const AgentInstance::Ptr &instance : std::as_const(mAgentInstances)) {
0901         const AgentType type = mAgents.value(instance->agentType());
0902         if (type.capabilities.contains(AgentType::CapabilitySearch) && instance->searchInterface()) {
0903             instance->searchInterface()->removeSearch(resultCollectionId);
0904         }
0905     }
0906 }
0907 
0908 #include "agentmanager.moc"
0909 
0910 #include "moc_agentmanager.cpp"