akonadi
firstrun.cpp
00001 /* 00002 Copyright (c) 2008 Volker Krause <vkrause@kde.org> 00003 00004 This library is free software; you can redistribute it and/or modify it 00005 under the terms of the GNU Library General Public License as published by 00006 the Free Software Foundation; either version 2 of the License, or (at your 00007 option) any later version. 00008 00009 This library is distributed in the hope that it will be useful, but WITHOUT 00010 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 00011 FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public 00012 License for more details. 00013 00014 You should have received a copy of the GNU Library General Public License 00015 along with this library; see the file COPYING.LIB. If not, write to the 00016 Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 00017 02110-1301, USA. 00018 */ 00019 00020 #include "firstrun_p.h" 00021 #include "dbusconnectionpool.h" 00022 00023 #include <akonadi/agentinstance.h> 00024 #include <akonadi/agentinstancecreatejob.h> 00025 #include <akonadi/agentmanager.h> 00026 #include <akonadi/agenttype.h> 00027 00028 #include <KConfig> 00029 #include <KConfigGroup> 00030 #include <KDebug> 00031 #include <KGlobal> 00032 #include <KProcess> 00033 #include <KStandardDirs> 00034 00035 #include <QtDBus/QDBusConnection> 00036 #include <QtDBus/QDBusInterface> 00037 #include <QtDBus/QDBusReply> 00038 #include <QtCore/QDir> 00039 #include <QtCore/QMetaMethod> 00040 #include <QtCore/QMetaObject> 00041 00042 static char FIRSTRUN_DBUSLOCK[] = "org.kde.Akonadi.Firstrun.lock"; 00043 00044 using namespace Akonadi; 00045 00046 Firstrun::Firstrun( QObject *parent ) 00047 : QObject( parent ), 00048 mConfig( new KConfig( QLatin1String( "akonadi-firstrunrc" ) ) ), 00049 mCurrentDefault( 0 ), 00050 mProcess( 0 ) 00051 { 00052 kDebug(); 00053 if ( DBusConnectionPool::threadConnection().registerService( QLatin1String( FIRSTRUN_DBUSLOCK ) ) ) { 00054 findPendingDefaults(); 00055 kDebug() << mPendingDefaults; 00056 setupNext(); 00057 } else { 00058 kDebug() << "D-Bus lock found, so someone else does the work for us already."; 00059 deleteLater(); 00060 } 00061 } 00062 00063 Firstrun::~Firstrun() 00064 { 00065 DBusConnectionPool::threadConnection().unregisterService( QLatin1String( FIRSTRUN_DBUSLOCK ) ); 00066 delete mConfig; 00067 kDebug() << "done"; 00068 } 00069 00070 void Firstrun::findPendingDefaults() 00071 { 00072 const KConfigGroup cfg( mConfig, "ProcessedDefaults" ); 00073 foreach ( const QString &dirName, KGlobal::dirs()->findDirs( "data", QLatin1String( "akonadi/firstrun" ) ) ) { 00074 const QStringList files = QDir( dirName ).entryList( QDir::Files | QDir::Readable ); 00075 foreach ( const QString &fileName, files ) { 00076 const QString fullName = dirName + fileName; 00077 KConfig c( fullName ); 00078 const QString id = KConfigGroup( &c, "Agent" ).readEntry( "Id", QString() ); 00079 if ( id.isEmpty() ) { 00080 kWarning() << "Found invalid default configuration in " << fullName; 00081 continue; 00082 } 00083 if ( cfg.hasKey( id ) ) 00084 continue; 00085 mPendingDefaults << dirName + fileName; 00086 } 00087 } 00088 00089 #ifndef KDEPIM_NO_KRESOURCES 00090 // always check legacy kres for migration, their migrator might have changed again 00091 mPendingKres << QLatin1String("contact") << QLatin1String("calendar"); 00092 #endif 00093 } 00094 00095 #ifndef KDEPIM_NO_KRESOURCES 00096 static QString resourceTypeForMimetype( const QStringList &mimeTypes ) 00097 { 00098 if ( mimeTypes.contains( QLatin1String( "text/directory" ) ) ) 00099 return QString::fromLatin1( "contact" ); 00100 if ( mimeTypes.contains( QLatin1String( "text/calendar" ) ) ) 00101 return QString::fromLatin1( "calendar" ); 00102 // TODO notes 00103 return QString(); 00104 } 00105 00106 void Firstrun::migrateKresType( const QString& resourceFamily ) 00107 { 00108 mResourceFamily = resourceFamily; 00109 KConfig config( QLatin1String( "kres-migratorrc" ) ); 00110 KConfigGroup migrationCfg( &config, "Migration" ); 00111 const bool enabled = migrationCfg.readEntry( "Enabled", false ); 00112 const bool setupClientBridge = migrationCfg.readEntry( "SetupClientBridge", true ); 00113 const int currentVersion = migrationCfg.readEntry( QString::fromLatin1( "Version-%1" ).arg( resourceFamily ), 0 ); 00114 const int targetVersion = migrationCfg.readEntry( "TargetVersion", 0 ); 00115 if ( enabled && currentVersion < targetVersion ) { 00116 kDebug() << "Performing migration of legacy KResource settings. Good luck!"; 00117 mProcess = new KProcess( this ); 00118 connect( mProcess, SIGNAL(finished(int)), SLOT(migrationFinished(int)) ); 00119 QStringList args = QStringList() << QLatin1String( "--interactive-on-change" ) 00120 << QLatin1String( "--type" ) << resourceFamily; 00121 if ( !setupClientBridge ) 00122 args << QLatin1String( "--omit-client-bridge" ); 00123 mProcess->setProgram( QLatin1String( "kres-migrator" ), args ); 00124 mProcess->start(); 00125 if ( !mProcess->waitForStarted() ) 00126 migrationFinished( -1 ); 00127 } else { 00128 // nothing to do 00129 setupNext(); 00130 } 00131 } 00132 00133 void Firstrun::migrationFinished( int exitCode ) 00134 { 00135 Q_ASSERT( mProcess ); 00136 if ( exitCode == 0 ) { 00137 kDebug() << "KResource -> Akonadi migration has been successful"; 00138 KConfig config( QLatin1String( "kres-migratorrc" ) ); 00139 KConfigGroup migrationCfg( &config, "Migration" ); 00140 const int targetVersion = migrationCfg.readEntry( "TargetVersion", 0 ); 00141 migrationCfg.writeEntry( QString::fromLatin1( "Version-%1" ).arg( mResourceFamily ), targetVersion ); 00142 migrationCfg.sync(); 00143 } else if ( exitCode != 1 ) { 00144 // exit code 1 means it is already running, so we are probably called by a migrator instance 00145 kError() << "KResource -> Akonadi migration failed!"; 00146 kError() << "command was: " << mProcess->program(); 00147 kError() << "exit code: " << mProcess->exitCode(); 00148 kError() << "stdout: " << mProcess->readAllStandardOutput(); 00149 kError() << "stderr: " << mProcess->readAllStandardError(); 00150 } 00151 00152 setupNext(); 00153 } 00154 #endif 00155 00156 00157 void Firstrun::setupNext() 00158 { 00159 delete mCurrentDefault; 00160 mCurrentDefault = 0; 00161 00162 if ( mPendingDefaults.isEmpty() ) { 00163 #ifndef KDEPIM_NO_KRESOURCES 00164 if ( !mPendingKres.isEmpty() ) { 00165 migrateKresType( mPendingKres.takeFirst() ); 00166 return; 00167 } 00168 #endif 00169 deleteLater(); 00170 return; 00171 } 00172 00173 mCurrentDefault = new KConfig( mPendingDefaults.takeFirst() ); 00174 const KConfigGroup agentCfg = KConfigGroup( mCurrentDefault, "Agent" ); 00175 00176 AgentType type = AgentManager::self()->type( agentCfg.readEntry( "Type", QString() ) ); 00177 if ( !type.isValid() ) { 00178 kError() << "Unable to obtain agent type for default resource agent configuration " << mCurrentDefault->name(); 00179 setupNext(); 00180 return; 00181 } 00182 00183 #ifndef KDEPIM_NO_KRESOURCES 00184 // KDE5: remove me 00185 // check if there is a kresource setup for this type already 00186 const QString kresType = resourceTypeForMimetype( type.mimeTypes() ); 00187 if ( !kresType.isEmpty() ) { 00188 const QString kresCfgFile = KStandardDirs::locateLocal( "config", QString::fromLatin1( "kresources/%1/stdrc" ).arg( kresType ) ); 00189 KConfig resCfg( kresCfgFile ); 00190 const KConfigGroup resGroup( &resCfg, "General" ); 00191 bool legacyResourceFound = false; 00192 const QStringList kresResources = resGroup.readEntry( "ResourceKeys", QStringList() ) 00193 + resGroup.readEntry( "PassiveResourceKeys", QStringList() ); 00194 foreach ( const QString &kresResource, kresResources ) { 00195 const KConfigGroup cfg( &resCfg, QString::fromLatin1( "Resource_%1" ).arg( kresResource ) ); 00196 if ( cfg.readEntry( "ResourceType", QString() ) != QLatin1String( "akonadi" ) ) { // not a bridge 00197 legacyResourceFound = true; 00198 break; 00199 } 00200 } 00201 if ( legacyResourceFound ) { 00202 kDebug() << "ignoring " << mCurrentDefault->name() << " as there is a KResource setup for its type already."; 00203 KConfigGroup cfg( mConfig, "ProcessedDefaults" ); 00204 cfg.writeEntry( agentCfg.readEntry( "Id", QString() ), QString::fromLatin1( "kres" ) ); 00205 cfg.sync(); 00206 setupNext(); 00207 return; 00208 } 00209 } 00210 #endif 00211 00212 AgentInstanceCreateJob *job = new AgentInstanceCreateJob( type ); 00213 connect( job, SIGNAL(result(KJob*)), SLOT(instanceCreated(KJob*)) ); 00214 job->start(); 00215 } 00216 00217 void Firstrun::instanceCreated( KJob *job ) 00218 { 00219 Q_ASSERT( mCurrentDefault ); 00220 00221 if ( job->error() ) { 00222 kError() << "Creating agent instance failed for " << mCurrentDefault->name(); 00223 setupNext(); 00224 return; 00225 } 00226 00227 AgentInstance instance = static_cast<AgentInstanceCreateJob*>( job )->instance(); 00228 const KConfigGroup agentCfg = KConfigGroup( mCurrentDefault, "Agent" ); 00229 const QString agentName = agentCfg.readEntry( "Name", QString() ); 00230 if ( !agentName.isEmpty() ) 00231 instance.setName( agentName ); 00232 00233 // agent specific settings, using the D-Bus <-> KConfigXT bridge 00234 const KConfigGroup settings = KConfigGroup( mCurrentDefault, "Settings" ); 00235 00236 QDBusInterface *iface = new QDBusInterface( QString::fromLatin1( "org.freedesktop.Akonadi.Agent.%1" ).arg( instance.identifier() ), 00237 QLatin1String( "/Settings" ), QString(), 00238 DBusConnectionPool::threadConnection(), this ); 00239 if ( !iface->isValid() ) { 00240 kError() << "Unable to obtain the KConfigXT D-Bus interface of " << instance.identifier(); 00241 setupNext(); 00242 delete iface; 00243 return; 00244 } 00245 00246 foreach ( const QString &setting, settings.keyList() ) { 00247 kDebug() << "Setting up " << setting << " for agent " << instance.identifier(); 00248 const QString methodName = QString::fromLatin1( "set%1" ).arg( setting ); 00249 const QVariant::Type argType = argumentType( iface->metaObject(), methodName ); 00250 if ( argType == QVariant::Invalid ) { 00251 kError() << "Setting " << setting << " not found in agent configuration interface of " << instance.identifier(); 00252 continue; 00253 } 00254 00255 QVariant arg; 00256 if ( argType == QVariant::String ) { 00257 // Since a string could be a path we always use readPathEntry here, 00258 // that shouldn't harm any normal string settings 00259 arg = settings.readPathEntry( setting, QString() ); 00260 } else 00261 arg = settings.readEntry( setting, QVariant( argType ) ); 00262 00263 const QDBusReply<void> reply = iface->call( methodName, arg ); 00264 if ( !reply.isValid() ) 00265 kError() << "Setting " << setting << " failed for agent " << instance.identifier(); 00266 } 00267 00268 iface->call( QLatin1String( "writeConfig" ) ); 00269 00270 instance.reconfigure(); 00271 instance.synchronize(); 00272 delete iface; 00273 00274 // remember we set this one up already 00275 KConfigGroup cfg( mConfig, "ProcessedDefaults" ); 00276 cfg.writeEntry( agentCfg.readEntry( "Id", QString() ), instance.identifier() ); 00277 cfg.sync(); 00278 00279 setupNext(); 00280 } 00281 00282 QVariant::Type Firstrun::argumentType( const QMetaObject *mo, const QString &method ) 00283 { 00284 QMetaMethod m; 00285 for ( int i = 0; i < mo->methodCount(); ++i ) { 00286 const QString signature = QString::fromLatin1( mo->method( i ).signature() ); 00287 if ( signature.startsWith( method ) ) 00288 m = mo->method( i ); 00289 } 00290 00291 if ( !m.signature() ) 00292 return QVariant::Invalid; 00293 00294 const QList<QByteArray> argTypes = m.parameterTypes(); 00295 if ( argTypes.count() != 1 ) 00296 return QVariant::Invalid; 00297 00298 return QVariant::nameToType( argTypes.first() ); 00299 } 00300 00301 #include "firstrun_p.moc"
This file is part of the KDE documentation.
Documentation copyright © 1996-2012 The KDE developers.
Generated on Tue May 8 2012 00:00:43 by doxygen 1.8.0 written by Dimitri van Heesch, © 1997-2006
Documentation copyright © 1996-2012 The KDE developers.
Generated on Tue May 8 2012 00:00:43 by doxygen 1.8.0 written by Dimitri van Heesch, © 1997-2006
KDE's Doxygen guidelines are available online.