00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
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
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
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
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
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
00185
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" ) ) {
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
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
00258
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 instance.reconfigure();
00269 instance.synchronize();
00270 delete iface;
00271
00272
00273 KConfigGroup cfg( mConfig, "ProcessedDefaults" );
00274 cfg.writeEntry( agentCfg.readEntry( "Id", QString() ), instance.identifier() );
00275 cfg.sync();
00276
00277 setupNext();
00278 }
00279
00280 QVariant::Type Firstrun::argumentType( const QMetaObject *mo, const QString &method )
00281 {
00282 QMetaMethod m;
00283 for ( int i = 0; i < mo->methodCount(); ++i ) {
00284 const QString signature = QString::fromLatin1( mo->method( i ).signature() );
00285 if ( signature.startsWith( method ) )
00286 m = mo->method( i );
00287 }
00288
00289 if ( !m.signature() )
00290 return QVariant::Invalid;
00291
00292 const QList<QByteArray> argTypes = m.parameterTypes();
00293 if ( argTypes.count() != 1 )
00294 return QVariant::Invalid;
00295
00296 return QVariant::nameToType( argTypes.first() );
00297 }
00298
00299 #include "firstrun_p.moc"