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

akonadi

  • sources
  • kde-4.12
  • 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  mProcess->setProgram( QLatin1String( "kres-migrator" ), args );
134  mProcess->start();
135  if ( !mProcess->waitForStarted() )
136  migrationFinished( -1 );
137  } else {
138  // nothing to do
139  setupNext();
140  }
141 }
142 
143 void Firstrun::migrationFinished( int exitCode )
144 {
145  Q_ASSERT( mProcess );
146  if ( exitCode == 0 ) {
147  kDebug() << "KResource -> Akonadi migration has been successful";
148  KConfig config( QLatin1String( "kres-migratorrc" ) );
149  KConfigGroup migrationCfg( &config, "Migration" );
150  const int targetVersion = migrationCfg.readEntry( "TargetVersion", 0 );
151  migrationCfg.writeEntry( QString::fromLatin1( "Version-%1" ).arg( mResourceFamily ), targetVersion );
152  migrationCfg.sync();
153  } else if ( exitCode != 1 ) {
154  // exit code 1 means it is already running, so we are probably called by a migrator instance
155  kError() << "KResource -> Akonadi migration failed!";
156  kError() << "command was: " << mProcess->program();
157  kError() << "exit code: " << mProcess->exitCode();
158  kError() << "stdout: " << mProcess->readAllStandardOutput();
159  kError() << "stderr: " << mProcess->readAllStandardError();
160  }
161 
162  setupNext();
163 }
164 #endif
165 
166 void Firstrun::setupNext()
167 {
168  delete mCurrentDefault;
169  mCurrentDefault = 0;
170 
171  if ( mPendingDefaults.isEmpty() ) {
172 #ifndef KDEPIM_NO_KRESOURCES
173  if ( !mPendingKres.isEmpty() ) {
174  migrateKresType( mPendingKres.takeFirst() );
175  return;
176  }
177 #endif
178  deleteLater();
179  return;
180  }
181 
182  mCurrentDefault = new KConfig( mPendingDefaults.takeFirst() );
183  const KConfigGroup agentCfg = KConfigGroup( mCurrentDefault, "Agent" );
184 
185  AgentType type = AgentManager::self()->type( agentCfg.readEntry( "Type", QString() ) );
186  if ( !type.isValid() ) {
187  kError() << "Unable to obtain agent type for default resource agent configuration " << mCurrentDefault->name();
188  setupNext();
189  return;
190  }
191 
192 #ifndef KDEPIM_NO_KRESOURCES
193  // KDE5: remove me
194  // check if there is a kresource setup for this type already
195  const QString kresType = resourceTypeForMimetype( type.mimeTypes() );
196  if ( !kresType.isEmpty() ) {
197  const QString kresCfgFile = KStandardDirs::locateLocal( "config", QString::fromLatin1( "kresources/%1/stdrc" ).arg( kresType ) );
198  KConfig resCfg( kresCfgFile );
199  const KConfigGroup resGroup( &resCfg, "General" );
200  bool legacyResourceFound = false;
201  const QStringList kresResources = resGroup.readEntry( "ResourceKeys", QStringList() )
202  + resGroup.readEntry( "PassiveResourceKeys", QStringList() );
203  foreach ( const QString &kresResource, kresResources ) {
204  const KConfigGroup cfg( &resCfg, QString::fromLatin1( "Resource_%1" ).arg( kresResource ) );
205  if ( cfg.readEntry( "ResourceType", QString() ) != QLatin1String( "akonadi" ) ) { // not a bridge
206  legacyResourceFound = true;
207  break;
208  }
209  }
210  if ( legacyResourceFound ) {
211  kDebug() << "ignoring " << mCurrentDefault->name() << " as there is a KResource setup for its type already.";
212  KConfigGroup cfg( mConfig, "ProcessedDefaults" );
213  cfg.writeEntry( agentCfg.readEntry( "Id", QString() ), QString::fromLatin1( "kres" ) );
214  cfg.sync();
215  setupNext();
216  return;
217  }
218  }
219 #endif
220 
221  AgentInstanceCreateJob *job = new AgentInstanceCreateJob( type );
222  connect( job, SIGNAL(result(KJob*)), SLOT(instanceCreated(KJob*)) );
223  job->start();
224 }
225 
226 void Firstrun::instanceCreated( KJob *job )
227 {
228  Q_ASSERT( mCurrentDefault );
229 
230  if ( job->error() ) {
231  kError() << "Creating agent instance failed for " << mCurrentDefault->name();
232  setupNext();
233  return;
234  }
235 
236  AgentInstance instance = static_cast<AgentInstanceCreateJob*>( job )->instance();
237  const KConfigGroup agentCfg = KConfigGroup( mCurrentDefault, "Agent" );
238  const QString agentName = agentCfg.readEntry( "Name", QString() );
239  if ( !agentName.isEmpty() )
240  instance.setName( agentName );
241 
242  // agent specific settings, using the D-Bus <-> KConfigXT bridge
243  const KConfigGroup settings = KConfigGroup( mCurrentDefault, "Settings" );
244 
245  QDBusInterface *iface = new QDBusInterface( QString::fromLatin1( "org.freedesktop.Akonadi.Agent.%1" ).arg( instance.identifier() ),
246  QLatin1String( "/Settings" ), QString(),
247  DBusConnectionPool::threadConnection(), this );
248  if ( !iface->isValid() ) {
249  kError() << "Unable to obtain the KConfigXT D-Bus interface of " << instance.identifier();
250  setupNext();
251  delete iface;
252  return;
253  }
254 
255  foreach ( const QString &setting, settings.keyList() ) {
256  kDebug() << "Setting up " << setting << " for agent " << instance.identifier();
257  const QString methodName = QString::fromLatin1( "set%1" ).arg( setting );
258  const QVariant::Type argType = argumentType( iface->metaObject(), methodName );
259  if ( argType == QVariant::Invalid ) {
260  kError() << "Setting " << setting << " not found in agent configuration interface of " << instance.identifier();
261  continue;
262  }
263 
264  QVariant arg;
265  if ( argType == QVariant::String ) {
266  // Since a string could be a path we always use readPathEntry here,
267  // that shouldn't harm any normal string settings
268  arg = settings.readPathEntry( setting, QString() );
269  } else
270  arg = settings.readEntry( setting, QVariant( argType ) );
271 
272  const QDBusReply<void> reply = iface->call( methodName, arg );
273  if ( !reply.isValid() )
274  kError() << "Setting " << setting << " failed for agent " << instance.identifier();
275  }
276 
277  iface->call( QLatin1String( "writeConfig" ) );
278 
279  instance.reconfigure();
280  instance.synchronize();
281  delete iface;
282 
283  // remember we set this one up already
284  KConfigGroup cfg( mConfig, "ProcessedDefaults" );
285  cfg.writeEntry( agentCfg.readEntry( "Id", QString() ), instance.identifier() );
286  cfg.sync();
287 
288  setupNext();
289 }
290 
291 QVariant::Type Firstrun::argumentType( const QMetaObject *mo, const QString &method )
292 {
293  QMetaMethod m;
294  for ( int i = 0; i < mo->methodCount(); ++i ) {
295  const QString signature = QString::fromLatin1( mo->method( i ).signature() );
296  if ( signature.startsWith( method ) )
297  m = mo->method( i );
298  }
299 
300  if ( !m.signature() )
301  return QVariant::Invalid;
302 
303  const QList<QByteArray> argTypes = m.parameterTypes();
304  if ( argTypes.count() != 1 )
305  return QVariant::Invalid;
306 
307  return QVariant::nameToType( argTypes.first() );
308 }
309 
310 #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:394
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::AgentManager::self
static AgentManager * self()
Returns the global instance of the agent manager.
Definition: agentmanager.cpp:380
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:280
Akonadi::AgentInstanceCreateJob::start
void start()
Starts the instance creation.
Definition: agentinstancecreatejob.cpp:175
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 Tue Oct 14 2014 23:00:27 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
  • kldap
  • kmbox
  • kmime
  • kpimidentities
  • kpimtextedit
  • kresources
  • ktnef
  • kxmlrpcclient
  • microblog

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