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

akonadi

  • 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 
194 #ifndef KDEPIM_NO_KRESOURCES
195  // KDE5: remove me
196  // check if there is a kresource setup for this type already
197  const QString kresType = resourceTypeForMimetype(type.mimeTypes());
198  if (!kresType.isEmpty()) {
199  const QString kresCfgFile = KStandardDirs::locateLocal("config", QString::fromLatin1("kresources/%1/stdrc").arg(kresType));
200  KConfig resCfg(kresCfgFile);
201  const KConfigGroup resGroup(&resCfg, "General");
202  bool legacyResourceFound = false;
203  const QStringList kresResources = resGroup.readEntry("ResourceKeys", QStringList())
204  + resGroup.readEntry("PassiveResourceKeys", QStringList());
205  foreach (const QString &kresResource, kresResources) {
206  const KConfigGroup cfg(&resCfg, QString::fromLatin1("Resource_%1").arg(kresResource));
207  if (cfg.readEntry("ResourceType", QString()) != QLatin1String("akonadi")) { // not a bridge
208  legacyResourceFound = true;
209  break;
210  }
211  }
212  if (legacyResourceFound) {
213  kDebug() << "ignoring " << mCurrentDefault->name() << " as there is a KResource setup for its type already.";
214  KConfigGroup cfg(mConfig, "ProcessedDefaults");
215  cfg.writeEntry(agentCfg.readEntry("Id", QString()), QString::fromLatin1("kres"));
216  cfg.sync();
217  setupNext();
218  return;
219  }
220  }
221 #endif
222 
223  AgentInstanceCreateJob *job = new AgentInstanceCreateJob(type);
224  connect(job, SIGNAL(result(KJob*)), SLOT(instanceCreated(KJob*)));
225  job->start();
226 }
227 
228 void Firstrun::instanceCreated(KJob *job)
229 {
230  Q_ASSERT(mCurrentDefault);
231 
232  if (job->error()) {
233  kError() << "Creating agent instance failed for " << mCurrentDefault->name();
234  setupNext();
235  return;
236  }
237 
238  AgentInstance instance = static_cast<AgentInstanceCreateJob *>(job)->instance();
239  const KConfigGroup agentCfg = KConfigGroup(mCurrentDefault, "Agent");
240  const QString agentName = agentCfg.readEntry("Name", QString());
241  if (!agentName.isEmpty()) {
242  instance.setName(agentName);
243  }
244 
245  // agent specific settings, using the D-Bus <-> KConfigXT bridge
246  const KConfigGroup settings = KConfigGroup(mCurrentDefault, "Settings");
247 
248  QDBusInterface *iface = new QDBusInterface(QString::fromLatin1("org.freedesktop.Akonadi.Agent.%1").arg(instance.identifier()),
249  QLatin1String("/Settings"), QString(),
250  DBusConnectionPool::threadConnection(), this);
251  if (!iface->isValid()) {
252  kError() << "Unable to obtain the KConfigXT D-Bus interface of " << instance.identifier();
253  setupNext();
254  delete iface;
255  return;
256  }
257 
258  foreach (const QString &setting, settings.keyList()) {
259  kDebug() << "Setting up " << setting << " for agent " << instance.identifier();
260  const QString methodName = QString::fromLatin1("set%1").arg(setting);
261  const QVariant::Type argType = argumentType(iface->metaObject(), methodName);
262  if (argType == QVariant::Invalid) {
263  kError() << "Setting " << setting << " not found in agent configuration interface of " << instance.identifier();
264  continue;
265  }
266 
267  QVariant arg;
268  if (argType == QVariant::String) {
269  // Since a string could be a path we always use readPathEntry here,
270  // that shouldn't harm any normal string settings
271  arg = settings.readPathEntry(setting, QString());
272  } else {
273  arg = settings.readEntry(setting, QVariant(argType));
274  }
275 
276  const QDBusReply<void> reply = iface->call(methodName, arg);
277  if (!reply.isValid()) {
278  kError() << "Setting " << setting << " failed for agent " << instance.identifier();
279  }
280  }
281 
282  iface->call(QLatin1String("writeConfig"));
283 
284  instance.reconfigure();
285  instance.synchronize();
286  delete iface;
287 
288  // remember we set this one up already
289  KConfigGroup cfg(mConfig, "ProcessedDefaults");
290  cfg.writeEntry(agentCfg.readEntry("Id", QString()), instance.identifier());
291  cfg.sync();
292 
293  setupNext();
294 }
295 
296 QVariant::Type Firstrun::argumentType(const QMetaObject *mo, const QString &method)
297 {
298  QMetaMethod m;
299  for (int i = 0; i < mo->methodCount(); ++i) {
300  const QString signature = QString::fromLatin1(mo->method(i).signature());
301  if (signature.startsWith(method)) {
302  m = mo->method(i);
303  }
304  }
305 
306  if (!m.signature()) {
307  return QVariant::Invalid;
308  }
309 
310  const QList<QByteArray> argTypes = m.parameterTypes();
311  if (argTypes.count() != 1) {
312  return QVariant::Invalid;
313  }
314 
315  return QVariant::nameToType(argTypes.first());
316 }
317 
318 #include "moc_firstrun_p.cpp"
Akonadi::AgentInstance::synchronize
void synchronize()
Triggers the agent instance to start synchronization.
Definition: agentinstance.cpp:110
Akonadi::AgentType::isValid
bool isValid() const
Returns whether the agent type is valid.
Definition: agenttype.cpp:41
Akonadi::AgentInstance::identifier
QString identifier() const
Returns the unique identifier of the agent instance.
Definition: agentinstance.cpp:55
Akonadi::ServerManager
Provides methods to control the Akonadi server process.
Definition: servermanager.h:42
Akonadi::AgentType
A representation of an agent type.
Definition: agenttype.h:58
Akonadi::AgentType::mimeTypes
QStringList mimeTypes() const
Returns the list of supported mime types of the agent type.
Definition: agenttype.cpp:71
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:395
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
Akonadi
FreeBusyManager::Singleton.
Definition: actionstatemanager_p.h:28
Akonadi::AgentManager::self
static AgentManager * self()
Returns the global instance of the agent manager.
Definition: agentmanager.cpp:381
Akonadi::AgentInstance
A representation of an agent instance.
Definition: agentinstance.h:62
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
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-2014 The KDE developers.
Generated on Thu Nov 13 2014 13:29:34 by doxygen 1.8.8 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-4.14.3 API Reference

Skip menu "kdepimlibs-4.14.3 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
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