• Skip to content
  • Skip to link menu
KDE API Reference
  • KDE API Reference
  • kdepimlibs API Reference
  • KDE Home
  • Contact Us
 

akonadi

  • sources
  • kde-4.14
  • kdepimlibs
  • akonadi
firstrun.cpp
1 /*
2  Copyright (c) 2008 Volker Krause <vkrause@kde.org>
3 
4  This library is free software; you can redistribute it and/or modify it
5  under the terms of the GNU Library General Public License as published by
6  the Free Software Foundation; either version 2 of the License, or (at your
7  option) any later version.
8 
9  This library is distributed in the hope that it will be useful, but WITHOUT
10  ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11  FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public
12  License for more details.
13 
14  You should have received a copy of the GNU Library General Public License
15  along with this library; see the file COPYING.LIB. If not, write to the
16  Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
17  02110-1301, USA.
18 */
19 
20 #include "firstrun_p.h"
21 #include "dbusconnectionpool.h"
22 #include "servermanager.h"
23 
24 #include <akonadi/agentinstance.h>
25 #include <akonadi/agentinstancecreatejob.h>
26 #include <akonadi/agentmanager.h>
27 #include <akonadi/agenttype.h>
28 
29 #include <KConfig>
30 #include <KConfigGroup>
31 #include <KDebug>
32 #include <KGlobal>
33 #include <KProcess>
34 #include <KStandardDirs>
35 
36 #include <QtDBus/QDBusConnection>
37 #include <QtDBus/QDBusInterface>
38 #include <QtDBus/QDBusReply>
39 #include <QtCore/QDir>
40 #include <QtCore/QMetaMethod>
41 #include <QtCore/QMetaObject>
42 
43 static char FIRSTRUN_DBUSLOCK[] = "org.kde.Akonadi.Firstrun.lock";
44 
45 using namespace Akonadi;
46 
47 Firstrun::Firstrun(QObject *parent)
48  : QObject(parent)
49  , mConfig(new KConfig(ServerManager::addNamespace(QLatin1String("akonadi-firstrunrc"))))
50  , mCurrentDefault(0)
51  , mProcess(0)
52 {
53  //The code in firstrun is not safe in multi-instance mode
54  Q_ASSERT(!ServerManager::hasInstanceIdentifier());
55  if (ServerManager::hasInstanceIdentifier()) {
56  deleteLater();
57  return;
58  }
59  kDebug();
60  if (DBusConnectionPool::threadConnection().registerService(QLatin1String(FIRSTRUN_DBUSLOCK))) {
61  findPendingDefaults();
62  kDebug() << mPendingDefaults;
63  setupNext();
64  } else {
65  kDebug() << "D-Bus lock found, so someone else does the work for us already.";
66  deleteLater();
67  }
68 }
69 
70 Firstrun::~Firstrun()
71 {
72  DBusConnectionPool::threadConnection().unregisterService(QLatin1String(FIRSTRUN_DBUSLOCK));
73  delete mConfig;
74  kDebug() << "done";
75 }
76 
77 void Firstrun::findPendingDefaults()
78 {
79  const KConfigGroup cfg(mConfig, "ProcessedDefaults");
80  foreach (const QString &dirName, KGlobal::dirs()->findDirs("data", QLatin1String("akonadi/firstrun"))) {
81  const QStringList files = QDir(dirName).entryList(QDir::Files | QDir::Readable);
82  foreach (const QString &fileName, files) {
83  const QString fullName = dirName + fileName;
84  KConfig c(fullName);
85  const QString id = KConfigGroup(&c, "Agent").readEntry("Id", QString());
86  if (id.isEmpty()) {
87  kWarning() << "Found invalid default configuration in " << fullName;
88  continue;
89  }
90  if (cfg.hasKey(id)) {
91  continue;
92  }
93  mPendingDefaults << dirName + fileName;
94  }
95  }
96 
97 #ifndef KDEPIM_NO_KRESOURCES
98  // always check legacy kres for migration, their migrator might have changed again
99  mPendingKres << QLatin1String("contact") << QLatin1String("calendar");
100 #endif
101 }
102 
103 #ifndef KDEPIM_NO_KRESOURCES
104 static QString resourceTypeForMimetype(const QStringList &mimeTypes)
105 {
106  if (mimeTypes.contains(QLatin1String("text/directory"))) {
107  return QString::fromLatin1("contact");
108  }
109  if (mimeTypes.contains(QLatin1String("text/calendar"))) {
110  return QString::fromLatin1("calendar");
111  }
112  // TODO notes
113  return QString();
114 }
115 
116 void Firstrun::migrateKresType(const QString &resourceFamily)
117 {
118  mResourceFamily = resourceFamily;
119  KConfig config(QLatin1String("kres-migratorrc"));
120  KConfigGroup migrationCfg(&config, "Migration");
121  const bool enabled = migrationCfg.readEntry("Enabled", false);
122  const bool setupClientBridge = migrationCfg.readEntry("SetupClientBridge", true);
123  const int currentVersion = migrationCfg.readEntry(QString::fromLatin1("Version-%1").arg(resourceFamily), 0);
124  const int targetVersion = migrationCfg.readEntry("TargetVersion", 0);
125  if (enabled && currentVersion < targetVersion) {
126  kDebug() << "Performing migration of legacy KResource settings. Good luck!";
127  mProcess = new KProcess(this);
128  connect(mProcess, SIGNAL(finished(int)), SLOT(migrationFinished(int)));
129  QStringList args = QStringList() << QLatin1String("--interactive-on-change")
130  << QLatin1String("--type") << resourceFamily;
131  if (!setupClientBridge) {
132  args << QLatin1String("--omit-client-bridge");
133  }
134  mProcess->setProgram(QLatin1String("kres-migrator"), args);
135  mProcess->start();
136  if (!mProcess->waitForStarted()) {
137  migrationFinished(-1);
138  }
139  } else {
140  // nothing to do
141  setupNext();
142  }
143 }
144 
145 void Firstrun::migrationFinished(int exitCode)
146 {
147  Q_ASSERT(mProcess);
148  if (exitCode == 0) {
149  kDebug() << "KResource -> Akonadi migration has been successful";
150  KConfig config(QLatin1String("kres-migratorrc"));
151  KConfigGroup migrationCfg(&config, "Migration");
152  const int targetVersion = migrationCfg.readEntry("TargetVersion", 0);
153  migrationCfg.writeEntry(QString::fromLatin1("Version-%1").arg(mResourceFamily), targetVersion);
154  migrationCfg.sync();
155  } else if (exitCode != 1) {
156  // exit code 1 means it is already running, so we are probably called by a migrator instance
157  kError() << "KResource -> Akonadi migration failed!";
158  kError() << "command was: " << mProcess->program();
159  kError() << "exit code: " << mProcess->exitCode();
160  kError() << "stdout: " << mProcess->readAllStandardOutput();
161  kError() << "stderr: " << mProcess->readAllStandardError();
162  }
163 
164  setupNext();
165 }
166 #endif
167 
168 void Firstrun::setupNext()
169 {
170  delete mCurrentDefault;
171  mCurrentDefault = 0;
172 
173  if (mPendingDefaults.isEmpty()) {
174 #ifndef KDEPIM_NO_KRESOURCES
175  if (!mPendingKres.isEmpty()) {
176  migrateKresType(mPendingKres.takeFirst());
177  return;
178  }
179 #endif
180  deleteLater();
181  return;
182  }
183 
184  mCurrentDefault = new KConfig(mPendingDefaults.takeFirst());
185  const KConfigGroup agentCfg = KConfigGroup(mCurrentDefault, "Agent");
186 
187  AgentType type = AgentManager::self()->type(agentCfg.readEntry("Type", QString()));
188  if (!type.isValid()) {
189  kError() << "Unable to obtain agent type for default resource agent configuration " << mCurrentDefault->name();
190  setupNext();
191  return;
192  }
193  if (type.capabilities().contains(QLatin1String("Unique"))) {
194  Q_FOREACH (const AgentInstance &agent, AgentManager::self()->instances()) {
195  if (agent.type() == type) {
196  // remember we set this one up already
197  KConfigGroup cfg(mConfig, "ProcessedDefaults");
198  cfg.writeEntry(agentCfg.readEntry("Id", QString()), agent.identifier());
199  cfg.sync();
200  setupNext();
201  return;
202  }
203  }
204  }
205 #ifndef KDEPIM_NO_KRESOURCES
206  // KDE5: remove me
207  // check if there is a kresource setup for this type already
208  const QString kresType = resourceTypeForMimetype(type.mimeTypes());
209  if (!kresType.isEmpty()) {
210  const QString kresCfgFile = KStandardDirs::locateLocal("config", QString::fromLatin1("kresources/%1/stdrc").arg(kresType));
211  KConfig resCfg(kresCfgFile);
212  const KConfigGroup resGroup(&resCfg, "General");
213  bool legacyResourceFound = false;
214  const QStringList kresResources = resGroup.readEntry("ResourceKeys", QStringList())
215  + resGroup.readEntry("PassiveResourceKeys", QStringList());
216  foreach (const QString &kresResource, kresResources) {
217  const KConfigGroup cfg(&resCfg, QString::fromLatin1("Resource_%1").arg(kresResource));
218  if (cfg.readEntry("ResourceType", QString()) != QLatin1String("akonadi")) { // not a bridge
219  legacyResourceFound = true;
220  break;
221  }
222  }
223  if (legacyResourceFound) {
224  kDebug() << "ignoring " << mCurrentDefault->name() << " as there is a KResource setup for its type already.";
225  KConfigGroup cfg(mConfig, "ProcessedDefaults");
226  cfg.writeEntry(agentCfg.readEntry("Id", QString()), QString::fromLatin1("kres"));
227  cfg.sync();
228  setupNext();
229  return;
230  }
231  }
232 #endif
233 
234  AgentInstanceCreateJob *job = new AgentInstanceCreateJob(type);
235  connect(job, SIGNAL(result(KJob*)), SLOT(instanceCreated(KJob*)));
236  job->start();
237 }
238 
239 void Firstrun::instanceCreated(KJob *job)
240 {
241  Q_ASSERT(mCurrentDefault);
242 
243  if (job->error()) {
244  kError() << "Creating agent instance failed for " << mCurrentDefault->name();
245  setupNext();
246  return;
247  }
248 
249  AgentInstance instance = static_cast<AgentInstanceCreateJob *>(job)->instance();
250  const KConfigGroup agentCfg = KConfigGroup(mCurrentDefault, "Agent");
251  const QString agentName = agentCfg.readEntry("Name", QString());
252  if (!agentName.isEmpty()) {
253  instance.setName(agentName);
254  }
255 
256  // agent specific settings, using the D-Bus <-> KConfigXT bridge
257  const KConfigGroup settings = KConfigGroup(mCurrentDefault, "Settings");
258 
259  QDBusInterface *iface = new QDBusInterface(QString::fromLatin1("org.freedesktop.Akonadi.Agent.%1").arg(instance.identifier()),
260  QLatin1String("/Settings"), QString(),
261  DBusConnectionPool::threadConnection(), this);
262  if (!iface->isValid()) {
263  kError() << "Unable to obtain the KConfigXT D-Bus interface of " << instance.identifier();
264  setupNext();
265  delete iface;
266  return;
267  }
268 
269  foreach (const QString &setting, settings.keyList()) {
270  kDebug() << "Setting up " << setting << " for agent " << instance.identifier();
271  const QString methodName = QString::fromLatin1("set%1").arg(setting);
272  const QVariant::Type argType = argumentType(iface->metaObject(), methodName);
273  if (argType == QVariant::Invalid) {
274  kError() << "Setting " << setting << " not found in agent configuration interface of " << instance.identifier();
275  continue;
276  }
277 
278  QVariant arg;
279  if (argType == QVariant::String) {
280  // Since a string could be a path we always use readPathEntry here,
281  // that shouldn't harm any normal string settings
282  arg = settings.readPathEntry(setting, QString());
283  } else {
284  arg = settings.readEntry(setting, QVariant(argType));
285  }
286 
287  const QDBusReply<void> reply = iface->call(methodName, arg);
288  if (!reply.isValid()) {
289  kError() << "Setting " << setting << " failed for agent " << instance.identifier();
290  }
291  }
292 
293  iface->call(QLatin1String("writeConfig"));
294 
295  instance.reconfigure();
296  instance.synchronize();
297  delete iface;
298 
299  // remember we set this one up already
300  KConfigGroup cfg(mConfig, "ProcessedDefaults");
301  cfg.writeEntry(agentCfg.readEntry("Id", QString()), instance.identifier());
302  cfg.sync();
303 
304  setupNext();
305 }
306 
307 QVariant::Type Firstrun::argumentType(const QMetaObject *mo, const QString &method)
308 {
309  QMetaMethod m;
310  for (int i = 0; i < mo->methodCount(); ++i) {
311  const QString signature = QString::fromLatin1(mo->method(i).signature());
312  if (signature.startsWith(method)) {
313  m = mo->method(i);
314  }
315  }
316 
317  if (!m.signature()) {
318  return QVariant::Invalid;
319  }
320 
321  const QList<QByteArray> argTypes = m.parameterTypes();
322  if (argTypes.count() != 1) {
323  return QVariant::Invalid;
324  }
325 
326  return QVariant::nameToType(argTypes.first());
327 }
328 
329 #include "moc_firstrun_p.cpp"
QDBusAbstractInterface::isValid
bool isValid() const
QDBusReply
Akonadi::AgentInstance::synchronize
void synchronize()
Triggers the agent instance to start synchronization.
Definition: agentinstance.cpp:110
Akonadi::AgentInstance::type
AgentType type() const
Returns the agent type of this instance.
Definition: agentinstance.cpp:50
QStringList::contains
bool contains(const QString &str, Qt::CaseSensitivity cs) const
QObject::metaObject
virtual const QMetaObject * metaObject() const
Akonadi::AgentType::isValid
bool isValid() const
Returns whether the agent type is valid.
Definition: agenttype.cpp:41
QDBusReply::isValid
bool isValid() const
Akonadi::AgentInstance::identifier
QString identifier() const
Returns the unique identifier of the agent instance.
Definition: agentinstance.cpp:55
QMetaMethod::parameterTypes
QList< QByteArray > parameterTypes() const
Akonadi::ServerManager
Provides methods to control the Akonadi server process.
Definition: servermanager.h:42
QDBusAbstractInterface::call
QDBusMessage call(const QString &method, const QVariant &arg1, const QVariant &arg2, const QVariant &arg3, const QVariant &arg4, const QVariant &arg5, const QVariant &arg6, const QVariant &arg7, const QVariant &arg8)
QList::count
int count(const T &value) const
Akonadi::AgentType
A representation of an agent type.
Definition: agenttype.h:58
QMetaObject
Akonadi::AgentType::mimeTypes
QStringList mimeTypes() const
Returns the list of supported mime types of the agent type.
Definition: agenttype.cpp:71
QObject
QVariant::nameToType
Type nameToType(const char *name)
QList::isEmpty
bool isEmpty() const
QString::isEmpty
bool isEmpty() const
QMetaObject::methodCount
int methodCount() const
QString::startsWith
bool startsWith(const QString &s, Qt::CaseSensitivity cs) const
Akonadi::AgentType::capabilities
QStringList capabilities() const
Returns the list of supported capabilities of the agent type.
Definition: agenttype.cpp:76
QMetaMethod::signature
const char * signature() const
QObject::deleteLater
void deleteLater()
QList::first
T & first()
QString
QList< QByteArray >
QStringList
QDBusInterface
Akonadi::AgentManager::type
AgentType type(const QString &identifier) const
Returns the agent type with the given identifier or an invalid agent type if the identifier does not ...
Definition: agentmanager.cpp:391
Akonadi::AgentInstance::setName
void setName(const QString &name)
Sets the user visible name of the agent instance.
Definition: agentinstance.cpp:60
Akonadi::AgentInstanceCreateJob
Job for creating new agent instances.
Definition: agentinstancecreatejob.h:71
QDir
QList::takeFirst
T takeFirst()
QLatin1String
QDir::entryList
QStringList entryList(QFlags< QDir::Filter > filters, QFlags< QDir::SortFlag > sort) const
Akonadi::AgentManager::self
static AgentManager * self()
Returns the global instance of the agent manager.
Definition: agentmanager.cpp:377
QString::fromLatin1
QString fromLatin1(const char *str, int size)
Akonadi::AgentInstance
A representation of an agent instance.
Definition: agentinstance.h:62
QObject::connect
bool connect(const QObject *sender, const char *signal, const QObject *receiver, const char *method, Qt::ConnectionType type)
QString::arg
QString arg(qlonglong a, int fieldWidth, int base, const QChar &fillChar) const
QMetaObject::method
QMetaMethod method(int index) const
QMetaMethod
Akonadi::ServerManager::hasInstanceIdentifier
static bool hasInstanceIdentifier()
Returns true if we are connected to a non-default Akonadi server instance.
Definition: servermanager.cpp:289
Akonadi::AgentInstanceCreateJob::start
void start()
Starts the instance creation.
Definition: agentinstancecreatejob.cpp:176
QVariant
Akonadi::AgentInstance::reconfigure
void reconfigure() const
Tell the agent that its configuration has been changed remotely via D-Bus.
Definition: agentinstance.cpp:149
This file is part of the KDE documentation.
Documentation copyright © 1996-2020 The KDE developers.
Generated on Mon Jun 22 2020 13:38:03 by doxygen 1.8.7 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.

akonadi

Skip menu "akonadi"
  • Main Page
  • Namespace List
  • Namespace Members
  • Alphabetical List
  • Class List
  • Class Hierarchy
  • Class Members
  • File List
  • Modules
  • Related Pages

kdepimlibs API Reference

Skip menu "kdepimlibs API Reference"
  • akonadi
  •   contact
  •   kmime
  •   socialutils
  • kabc
  • kalarmcal
  • kblog
  • kcal
  • kcalcore
  • kcalutils
  • kholidays
  • kimap
  • kioslave
  •   imap4
  •   mbox
  •   nntp
  • kldap
  • kmbox
  • kmime
  • kontactinterface
  • kpimidentities
  • kpimtextedit
  • kpimutils
  • kresources
  • ktnef
  • kxmlrpcclient
  • mailtransport
  • microblog
  • qgpgme
  • syndication
  •   atom
  •   rdf
  •   rss2

Search



Report problems with this website to our bug tracking system.
Contact the specific authors with questions and comments about the page contents.

KDE® and the K Desktop Environment® logo are registered trademarks of KDE e.V. | Legal