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"