Akonadi

firstrun.cpp
1/*
2 SPDX-FileCopyrightText: 2008 Volker Krause <vkrause@kde.org>
3
4 SPDX-License-Identifier: LGPL-2.0-or-later
5*/
6
7#include "firstrun_p.h"
8#include "servermanager.h"
9#include <QDBusConnection>
10
11#include "agentinstance.h"
12#include "agentinstancecreatejob.h"
13#include "agentmanager.h"
14#include "agenttype.h"
15#include "private/standarddirs_p.h"
16
17#include "akonadicore_debug.h"
18
19#include <KConfig>
20#include <KConfigGroup>
21
22#include <QCoreApplication>
23#include <QDBusInterface>
24#include <QDBusReply>
25#include <QDir>
26#include <QMetaMethod>
27#include <QMetaObject>
28#include <QStandardPaths>
29
30static const char FIRSTRUN_DBUSLOCK[] = "org.kde.Akonadi.Firstrun.lock";
31
32using namespace Akonadi;
33
34Firstrun::Firstrun(QObject *parent)
35 : QObject(parent)
36 , mConfig(new KConfig(ServerManager::addNamespace(QStringLiteral("akonadi-firstrunrc"))))
37{
38 // The code in firstrun is not safe in multi-instance mode
39 Q_ASSERT(!ServerManager::hasInstanceIdentifier());
40 if (ServerManager::hasInstanceIdentifier()) {
41 deleteLater();
42 return;
43 }
44 if (QDBusConnection::sessionBus().registerService(QLatin1StringView(FIRSTRUN_DBUSLOCK))) {
45 findPendingDefaults();
46 qCDebug(AKONADICORE_LOG) << "D-Bus lock acquired, pending defaults:" << mPendingDefaults;
47 setupNext();
48 } else {
49 qCDebug(AKONADICORE_LOG) << "D-Bus lock found, so someone else does the work for us already.";
50 deleteLater();
51 }
52}
53
54Firstrun::~Firstrun()
55{
56 if (qApp) {
58 }
59 delete mConfig;
60 qCDebug(AKONADICORE_LOG) << "done";
61}
62
63void Firstrun::findPendingDefaults()
64{
65 const KConfigGroup cfg(mConfig, QStringLiteral("ProcessedDefaults"));
66 const auto paths = StandardDirs::locateAllResourceDirs(QStringLiteral("akonadi/firstrun"));
67 for (const QString &dirName : paths) {
68 const QStringList files = QDir(dirName).entryList(QDir::Files | QDir::Readable);
69 for (const QString &fileName : files) {
70 const QString fullName = dirName + QLatin1Char('/') + fileName;
71 KConfig c(fullName);
72 const QString id = KConfigGroup(&c, QStringLiteral("Agent")).readEntry("Id", QString());
73 if (id.isEmpty()) {
74 qCWarning(AKONADICORE_LOG) << "Found invalid default configuration in " << fullName;
75 continue;
76 }
77 if (cfg.hasKey(id)) {
78 continue;
79 }
80 mPendingDefaults << fullName;
81 }
82 }
83}
84
85void Firstrun::setupNext()
86{
87 delete mCurrentDefault;
88 mCurrentDefault = nullptr;
89
90 if (mPendingDefaults.isEmpty()) {
91 deleteLater();
92 return;
93 }
94
95 mCurrentDefault = new KConfig(mPendingDefaults.takeFirst());
96 const KConfigGroup agentCfg = KConfigGroup(mCurrentDefault, QStringLiteral("Agent"));
97
98 AgentType type = AgentManager::self()->type(agentCfg.readEntry("Type", QString()));
99 if (!type.isValid()) {
100 qCCritical(AKONADICORE_LOG) << "Unable to obtain agent type for default resource agent configuration " << mCurrentDefault->name();
101 setupNext();
102 return;
103 }
104 if (type.capabilities().contains(QLatin1StringView("Unique"))) {
105 const Akonadi::AgentInstance::List lstAgents = AgentManager::self()->instances();
106 for (const AgentInstance &agent : lstAgents) {
107 if (agent.type() == type) {
108 // remember we set this one up already
109 KConfigGroup cfg(mConfig, QStringLiteral("ProcessedDefaults"));
110 cfg.writeEntry(agentCfg.readEntry("Id", QString()), agent.identifier());
111 cfg.sync();
112 setupNext();
113 return;
114 }
115 }
116 }
117
118 auto job = new AgentInstanceCreateJob(type);
119 connect(job, &AgentInstanceCreateJob::result, this, &Firstrun::instanceCreated);
120 job->start();
121}
122
123void Firstrun::instanceCreated(KJob *job)
124{
125 Q_ASSERT(mCurrentDefault);
126
127 if (job->error()) {
128 qCCritical(AKONADICORE_LOG) << "Creating agent instance failed for " << mCurrentDefault->name();
129 setupNext();
130 return;
131 }
132
133 AgentInstance instance = static_cast<AgentInstanceCreateJob *>(job)->instance();
134 const KConfigGroup agentCfg = KConfigGroup(mCurrentDefault, QStringLiteral("Agent"));
135 const QString agentName = agentCfg.readEntry("Name", QString());
136 if (!agentName.isEmpty()) {
137 instance.setName(agentName);
138 }
139
140 const auto service = ServerManager::agentServiceName(ServerManager::Agent, instance.identifier());
141 auto iface = new QDBusInterface(service, QStringLiteral("/Settings"), QString(), QDBusConnection::sessionBus(), this);
142 if (!iface->isValid()) {
143 qCCritical(AKONADICORE_LOG) << "Unable to obtain the KConfigXT D-Bus interface of " << instance.identifier();
144 setupNext();
145 delete iface;
146 return;
147 }
148 // agent specific settings, using the D-Bus <-> KConfigXT bridge
149 const KConfigGroup settings = KConfigGroup(mCurrentDefault, QStringLiteral("Settings"));
150
151 const QStringList lstSettings = settings.keyList();
152 for (const QString &setting : lstSettings) {
153 qCDebug(AKONADICORE_LOG) << "Setting up " << setting << " for agent " << instance.identifier();
154 const QString methodName = QStringLiteral("set%1").arg(setting);
155 const QMetaType::Type argType = argumentType(iface->metaObject(), methodName);
156 if (argType == QMetaType::UnknownType) {
157 qCCritical(AKONADICORE_LOG) << "Setting " << setting << " not found in agent configuration interface of " << instance.identifier();
158 continue;
159 }
160
161 QVariant arg;
162 if (argType == QMetaType::QString) {
163 // Since a string could be a path we always use readPathEntry here,
164 // that shouldn't harm any normal string settings
165 arg = settings.readPathEntry(setting, QString());
166 } else {
167 arg = settings.readEntry(setting, QVariant(QMetaType(argType)));
168 }
169
170 const QDBusReply<void> reply = iface->call(methodName, arg);
171 if (!reply.isValid()) {
172 qCCritical(AKONADICORE_LOG) << "Setting " << setting << " failed for agent " << instance.identifier();
173 }
174 }
175
176 iface->call(QStringLiteral("save"));
177
178 instance.reconfigure();
179 instance.synchronize();
180 delete iface;
181
182 // remember we set this one up already
183 KConfigGroup cfg(mConfig, QStringLiteral("ProcessedDefaults"));
184 cfg.writeEntry(agentCfg.readEntry("Id", QString()), instance.identifier());
185 cfg.sync();
186
187 setupNext();
188}
189
190QMetaType::Type Firstrun::argumentType(const QMetaObject *metaObject, const QString &method)
191{
192 QMetaMethod metaMethod;
193 for (int i = 0; i < metaObject->methodCount(); ++i) {
194 const QString signature = QString::fromLatin1(metaObject->method(i).methodSignature());
195 if (signature.startsWith(method)) {
196 metaMethod = metaObject->method(i);
197 }
198 }
199
200 if (metaMethod.methodSignature().isEmpty()) {
202 }
203
204 const QList<QByteArray> argTypes = metaMethod.parameterTypes();
205 if (argTypes.count() != 1) {
207 }
208
209 return static_cast<QMetaType::Type>(QMetaType::fromName(argTypes.first().constData()).id());
210}
211
212#include "moc_firstrun_p.cpp"
Represents one agent instance and takes care of communication with it.
QString identifier() const
Set/get the unique identifier of this AgentInstance.
Job for creating new agent instances.
QString readPathEntry(const char *key, const QString &aDefault) const
QString readEntry(const char *key, const char *aDefault=nullptr) const
QStringList keyList() const
int error() const
QString fullName(const PartType &type)
Returns full part name.
Helper integration between Akonadi and Qt.
VehicleSection::Type type(QStringView coachNumber, QStringView coachClassification)
bool isEmpty() const const
QDBusConnection sessionBus()
bool unregisterService(const QString &serviceName)
bool isValid() const const
QStringList entryList(Filters filters, SortFlags sort) const const
qsizetype count() const const
T & first()
QByteArray methodSignature() const const
QList< QByteArray > parameterTypes() const const
QMetaMethod method(int index) const const
int methodCount() const const
QMetaType fromName(QByteArrayView typeName)
int id() const const
QString arg(Args &&... args) const const
QString fromLatin1(QByteArrayView str)
bool isEmpty() const const
bool startsWith(QChar c, Qt::CaseSensitivity cs) const const
QFuture< ArgsType< Signal > > connect(Sender *sender, Signal signal)
This file is part of the KDE documentation.
Documentation copyright © 1996-2024 The KDE developers.
Generated on Tue Mar 26 2024 11:13:38 by doxygen 1.10.0 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.