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

0001 /*
0002     SPDX-FileCopyrightText: 2008 Volker Krause <vkrause@kde.org>
0003 
0004     SPDX-License-Identifier: LGPL-2.0-or-later
0005 */
0006 
0007 #include "firstrun_p.h"
0008 #include "servermanager.h"
0009 #include <QDBusConnection>
0010 
0011 #include "agentinstance.h"
0012 #include "agentinstancecreatejob.h"
0013 #include "agentmanager.h"
0014 #include "agenttype.h"
0015 #include "private/standarddirs_p.h"
0016 
0017 #include "akonadicore_debug.h"
0018 
0019 #include <KConfig>
0020 #include <KConfigGroup>
0021 
0022 #include <QCoreApplication>
0023 #include <QDBusInterface>
0024 #include <QDBusReply>
0025 #include <QDir>
0026 #include <QMetaMethod>
0027 #include <QMetaObject>
0028 #include <QStandardPaths>
0029 
0030 static const char FIRSTRUN_DBUSLOCK[] = "org.kde.Akonadi.Firstrun.lock";
0031 
0032 using namespace Akonadi;
0033 
0034 Firstrun::Firstrun(QObject *parent)
0035     : QObject(parent)
0036     , mConfig(new KConfig(ServerManager::addNamespace(QStringLiteral("akonadi-firstrunrc"))))
0037 {
0038     // The code in firstrun is not safe in multi-instance mode
0039     Q_ASSERT(!ServerManager::hasInstanceIdentifier());
0040     if (ServerManager::hasInstanceIdentifier()) {
0041         deleteLater();
0042         return;
0043     }
0044     if (QDBusConnection::sessionBus().registerService(QLatin1StringView(FIRSTRUN_DBUSLOCK))) {
0045         findPendingDefaults();
0046         qCDebug(AKONADICORE_LOG) << "D-Bus lock acquired, pending defaults:" << mPendingDefaults;
0047         setupNext();
0048     } else {
0049         qCDebug(AKONADICORE_LOG) << "D-Bus lock found, so someone else does the work for us already.";
0050         deleteLater();
0051     }
0052 }
0053 
0054 Firstrun::~Firstrun()
0055 {
0056     if (qApp) {
0057         QDBusConnection::sessionBus().unregisterService(QLatin1StringView(FIRSTRUN_DBUSLOCK));
0058     }
0059     delete mConfig;
0060     qCDebug(AKONADICORE_LOG) << "done";
0061 }
0062 
0063 void Firstrun::findPendingDefaults()
0064 {
0065     const KConfigGroup cfg(mConfig, QStringLiteral("ProcessedDefaults"));
0066     const auto paths = StandardDirs::locateAllResourceDirs(QStringLiteral("akonadi/firstrun"));
0067     for (const QString &dirName : paths) {
0068         const QStringList files = QDir(dirName).entryList(QDir::Files | QDir::Readable);
0069         for (const QString &fileName : files) {
0070             const QString fullName = dirName + QLatin1Char('/') + fileName;
0071             KConfig c(fullName);
0072             const QString id = KConfigGroup(&c, QStringLiteral("Agent")).readEntry("Id", QString());
0073             if (id.isEmpty()) {
0074                 qCWarning(AKONADICORE_LOG) << "Found invalid default configuration in " << fullName;
0075                 continue;
0076             }
0077             if (cfg.hasKey(id)) {
0078                 continue;
0079             }
0080             mPendingDefaults << fullName;
0081         }
0082     }
0083 }
0084 
0085 void Firstrun::setupNext()
0086 {
0087     delete mCurrentDefault;
0088     mCurrentDefault = nullptr;
0089 
0090     if (mPendingDefaults.isEmpty()) {
0091         deleteLater();
0092         return;
0093     }
0094 
0095     mCurrentDefault = new KConfig(mPendingDefaults.takeFirst());
0096     const KConfigGroup agentCfg = KConfigGroup(mCurrentDefault, QStringLiteral("Agent"));
0097 
0098     AgentType type = AgentManager::self()->type(agentCfg.readEntry("Type", QString()));
0099     if (!type.isValid()) {
0100         qCCritical(AKONADICORE_LOG) << "Unable to obtain agent type for default resource agent configuration " << mCurrentDefault->name();
0101         setupNext();
0102         return;
0103     }
0104     if (type.capabilities().contains(QLatin1StringView("Unique"))) {
0105         const Akonadi::AgentInstance::List lstAgents = AgentManager::self()->instances();
0106         for (const AgentInstance &agent : lstAgents) {
0107             if (agent.type() == type) {
0108                 // remember we set this one up already
0109                 KConfigGroup cfg(mConfig, QStringLiteral("ProcessedDefaults"));
0110                 cfg.writeEntry(agentCfg.readEntry("Id", QString()), agent.identifier());
0111                 cfg.sync();
0112                 setupNext();
0113                 return;
0114             }
0115         }
0116     }
0117 
0118     auto job = new AgentInstanceCreateJob(type);
0119     connect(job, &AgentInstanceCreateJob::result, this, &Firstrun::instanceCreated);
0120     job->start();
0121 }
0122 
0123 void Firstrun::instanceCreated(KJob *job)
0124 {
0125     Q_ASSERT(mCurrentDefault);
0126 
0127     if (job->error()) {
0128         qCCritical(AKONADICORE_LOG) << "Creating agent instance failed for " << mCurrentDefault->name();
0129         setupNext();
0130         return;
0131     }
0132 
0133     AgentInstance instance = static_cast<AgentInstanceCreateJob *>(job)->instance();
0134     const KConfigGroup agentCfg = KConfigGroup(mCurrentDefault, QStringLiteral("Agent"));
0135     const QString agentName = agentCfg.readEntry("Name", QString());
0136     if (!agentName.isEmpty()) {
0137         instance.setName(agentName);
0138     }
0139 
0140     const auto service = ServerManager::agentServiceName(ServerManager::Agent, instance.identifier());
0141     auto iface = new QDBusInterface(service, QStringLiteral("/Settings"), QString(), QDBusConnection::sessionBus(), this);
0142     if (!iface->isValid()) {
0143         qCCritical(AKONADICORE_LOG) << "Unable to obtain the KConfigXT D-Bus interface of " << instance.identifier();
0144         setupNext();
0145         delete iface;
0146         return;
0147     }
0148     // agent specific settings, using the D-Bus <-> KConfigXT bridge
0149     const KConfigGroup settings = KConfigGroup(mCurrentDefault, QStringLiteral("Settings"));
0150 
0151     const QStringList lstSettings = settings.keyList();
0152     for (const QString &setting : lstSettings) {
0153         qCDebug(AKONADICORE_LOG) << "Setting up " << setting << " for agent " << instance.identifier();
0154         const QString methodName = QStringLiteral("set%1").arg(setting);
0155         const QMetaType::Type argType = argumentType(iface->metaObject(), methodName);
0156         if (argType == QMetaType::UnknownType) {
0157             qCCritical(AKONADICORE_LOG) << "Setting " << setting << " not found in agent configuration interface of " << instance.identifier();
0158             continue;
0159         }
0160 
0161         QVariant arg;
0162         if (argType == QMetaType::QString) {
0163             // Since a string could be a path we always use readPathEntry here,
0164             // that shouldn't harm any normal string settings
0165             arg = settings.readPathEntry(setting, QString());
0166         } else {
0167             arg = settings.readEntry(setting, QVariant(QMetaType(argType)));
0168         }
0169 
0170         const QDBusReply<void> reply = iface->call(methodName, arg);
0171         if (!reply.isValid()) {
0172             qCCritical(AKONADICORE_LOG) << "Setting " << setting << " failed for agent " << instance.identifier();
0173         }
0174     }
0175 
0176     iface->call(QStringLiteral("save"));
0177 
0178     instance.reconfigure();
0179     instance.synchronize();
0180     delete iface;
0181 
0182     // remember we set this one up already
0183     KConfigGroup cfg(mConfig, QStringLiteral("ProcessedDefaults"));
0184     cfg.writeEntry(agentCfg.readEntry("Id", QString()), instance.identifier());
0185     cfg.sync();
0186 
0187     setupNext();
0188 }
0189 
0190 QMetaType::Type Firstrun::argumentType(const QMetaObject *metaObject, const QString &method)
0191 {
0192     QMetaMethod metaMethod;
0193     for (int i = 0; i < metaObject->methodCount(); ++i) {
0194         const QString signature = QString::fromLatin1(metaObject->method(i).methodSignature());
0195         if (signature.startsWith(method)) {
0196             metaMethod = metaObject->method(i);
0197         }
0198     }
0199 
0200     if (metaMethod.methodSignature().isEmpty()) {
0201         return QMetaType::UnknownType;
0202     }
0203 
0204     const QList<QByteArray> argTypes = metaMethod.parameterTypes();
0205     if (argTypes.count() != 1) {
0206         return QMetaType::UnknownType;
0207     }
0208 
0209     return static_cast<QMetaType::Type>(QMetaType::fromName(argTypes.first().constData()).id());
0210 }
0211 
0212 #include "moc_firstrun_p.cpp"