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
00022 #include <akonadi/agenttype.h>
00023 #include <akonadi/agentmanager.h>
00024 #include <akonadi/agentinstance.h>
00025 #include <akonadi/agentinstancecreatejob.h>
00026
00027 #include <KConfig>
00028 #include <KConfigGroup>
00029 #include <KDebug>
00030 #include <KGlobal>
00031 #include <KProcess>
00032 #include <KStandardDirs>
00033
00034 #include <QDBusConnection>
00035 #include <QDBusInterface>
00036 #include <QDBusReply>
00037 #include <QDir>
00038 #include <QMetaMethod>
00039 #include <QMetaObject>
00040
00041 using namespace Akonadi;
00042
00043 Firstrun::Firstrun( QObject *parent ) :
00044 QObject( parent ),
00045 mConfig( new KConfig( QLatin1String("akonadi-firstrunrc") ) ),
00046 mCurrentDefault( 0 ),
00047 mProcess( 0 )
00048 {
00049 findPendingDefaults();
00050 kDebug() << mPendingDefaults;
00051 setupNext();
00052 }
00053
00054 Firstrun::~Firstrun()
00055 {
00056 delete mConfig;
00057 kDebug() << "done";
00058 }
00059
00060 void Firstrun::findPendingDefaults()
00061 {
00062 const KConfigGroup cfg( mConfig, "ProcessedDefaults" );
00063 foreach ( const QString &dirName, KGlobal::dirs()->findDirs( "data", QLatin1String("akonadi/firstrun") ) ) {
00064 const QStringList files = QDir( dirName ).entryList( QDir::Files | QDir::Readable );
00065 foreach ( const QString &fileName, files ) {
00066 const QString fullName = dirName + fileName;
00067 KConfig c( fullName );
00068 const QString id = KConfigGroup( &c, "Agent" ).readEntry( "Id", QString() );
00069 if ( id.isEmpty() ) {
00070 kWarning() << "Found invalid default configuration in " << fullName;
00071 continue;
00072 }
00073 if ( cfg.hasKey( id ) )
00074 continue;
00075 mPendingDefaults << dirName + fileName;
00076 }
00077 }
00078 }
00079
00080 static QString resourceTypeForMimetype( const QStringList &mimeTypes )
00081 {
00082 if ( mimeTypes.contains( QLatin1String("text/directory") ) )
00083 return QString::fromLatin1( "contact" );
00084 if ( mimeTypes.contains( QLatin1String("text/calendar") ) )
00085 return QString::fromLatin1( "calendar" );
00086
00087 return QString();
00088 }
00089
00090 void Firstrun::migrateKresType( const QString& resourceFamily )
00091 {
00092 mResourceFamily = resourceFamily;
00093 KConfig config( QLatin1String("kres-migratorrc") );
00094 KConfigGroup migrationCfg( &config, "Migration" );
00095 const bool enabled = migrationCfg.readEntry( "Enabled", false );
00096 const bool setupClientBridge = migrationCfg.readEntry( "SetupClientBridge", true );
00097 const int currentVersion = migrationCfg.readEntry( QString::fromLatin1("Version-%1").arg( resourceFamily ), 0 );
00098 const int targetVersion = migrationCfg.readEntry( "TargetVersion", 0 );
00099 if ( enabled && currentVersion < targetVersion ) {
00100 kDebug() << "Performing migration of legacy KResource settings. Good luck!";
00101 mProcess = new KProcess( this );
00102 connect( mProcess, SIGNAL(finished(int)), SLOT(migrationFinished(int)) );
00103 QStringList args = QStringList() << QLatin1String("--interactive-on-change")
00104 << QLatin1String("--type") << resourceFamily;
00105 if ( !setupClientBridge )
00106 args << QLatin1String( "--omit-client-bridge" );
00107 mProcess->setProgram( QLatin1String("kres-migrator"), args );
00108 mProcess->start();
00109 if ( !mProcess->waitForStarted() )
00110 migrationFinished( -1 );
00111 } else {
00112
00113 setupNext();
00114 }
00115 }
00116
00117 void Firstrun::migrationFinished(int exitCode)
00118 {
00119 Q_ASSERT( mProcess );
00120 if ( exitCode == 0 ) {
00121 kDebug() << "KResource -> Akonadi migration has been successful";
00122 KConfig config( QLatin1String("kres-migratorrc") );
00123 KConfigGroup migrationCfg( &config, "Migration" );
00124 const int targetVersion = migrationCfg.readEntry( "TargetVersion", 0 );
00125 migrationCfg.writeEntry( QString::fromLatin1("Version-%1").arg( mResourceFamily ), targetVersion );
00126 migrationCfg.sync();
00127 } else if ( exitCode != 1 ) {
00128
00129 kError() << "KResource -> Akonadi migration failed!";
00130 kError() << "command was: " << mProcess->program();
00131 kError() << "exit code: " << mProcess->exitCode();
00132 kError() << "stdout: " << mProcess->readAllStandardOutput();
00133 kError() << "stderr: " << mProcess->readAllStandardError();
00134 }
00135
00136 setupNext();
00137 }
00138
00139
00140 void Firstrun::setupNext()
00141 {
00142 delete mCurrentDefault;
00143 mCurrentDefault = 0;
00144
00145 if ( mPendingDefaults.isEmpty() ) {
00146 deleteLater();
00147 return;
00148 }
00149
00150 mCurrentDefault = new KConfig( mPendingDefaults.takeFirst() );
00151 const KConfigGroup agentCfg = KConfigGroup( mCurrentDefault, "Agent" );
00152
00153 AgentType type = AgentManager::self()->type( agentCfg.readEntry( "Type", QString() ) );
00154 if ( !type.isValid() ) {
00155 kError() << "Unable to obtain agent type for default resource agent configuration " << mCurrentDefault->name();
00156 setupNext();
00157 return;
00158 }
00159
00160
00161
00162 const QString kresType = resourceTypeForMimetype( type.mimeTypes() );
00163 if ( !kresType.isEmpty() ) {
00164 const QString kresCfgFile = KStandardDirs::locateLocal( "config", QString::fromLatin1( "kresources/%1/stdrc" ).arg( kresType ) );
00165 KConfig resCfg( kresCfgFile );
00166 const KConfigGroup resGroup( &resCfg, "General" );
00167 bool legacyResourceFound = false;
00168 const QStringList kresResources = resGroup.readEntry( "ResourceKeys", QStringList() )
00169 + resGroup.readEntry( "PassiveResourceKeys", QStringList() );
00170 foreach ( const QString &kresResource, kresResources ) {
00171 const KConfigGroup cfg( &resCfg, QString::fromLatin1("Resource_%1").arg( kresResource ) );
00172 if ( cfg.readEntry( "ResourceType", QString() ) != QLatin1String( "akonadi" ) ) {
00173 legacyResourceFound = true;
00174 break;
00175 }
00176 }
00177 if ( legacyResourceFound ) {
00178 kDebug() << "ignoring " << mCurrentDefault->name() << " as there is a KResource setup for its type already.";
00179 KConfigGroup cfg( mConfig, "ProcessedDefaults" );
00180 cfg.writeEntry( agentCfg.readEntry( "Id", QString() ), QString::fromLatin1( "kres" ) );
00181 cfg.sync();
00182 migrateKresType( kresType );
00183 return;
00184 }
00185 }
00186
00187 AgentInstanceCreateJob *job = new AgentInstanceCreateJob( type );
00188 connect( job, SIGNAL(result(KJob*)), SLOT(instanceCreated(KJob*)) );
00189 job->start();
00190 }
00191
00192 void Firstrun::instanceCreated( KJob *job )
00193 {
00194 Q_ASSERT( mCurrentDefault );
00195
00196 if ( job->error() ) {
00197 kError() << "Creating agent instance failed for " << mCurrentDefault->name();
00198 setupNext();
00199 return;
00200 }
00201
00202 AgentInstance instance = static_cast<AgentInstanceCreateJob*>( job )->instance();
00203 const KConfigGroup agentCfg = KConfigGroup( mCurrentDefault, "Agent" );
00204 const QString agentName = agentCfg.readEntry( "Name", QString() );
00205 if ( !agentName.isEmpty() )
00206 instance.setName( agentName );
00207
00208
00209 const KConfigGroup settings = KConfigGroup( mCurrentDefault, "Settings" );
00210
00211 QDBusInterface *iface = new QDBusInterface( QString::fromLatin1("org.freedesktop.Akonadi.Agent.%1").arg( instance.identifier() ),
00212 QLatin1String("/Settings"), QString(), QDBusConnection::sessionBus(), this );
00213 if ( !iface->isValid() ) {
00214 kError() << "Unable to obtain the KConfigXT D-Bus interface of " << instance.identifier();
00215 setupNext();
00216 delete iface;
00217 return;
00218 }
00219
00220 foreach ( const QString &setting, settings.keyList() ) {
00221 kDebug() << "Setting up " << setting << " for agent " << instance.identifier();
00222 const QString methodName = QString::fromLatin1("set%1").arg( setting );
00223 const QVariant::Type argType = argumentType( iface->metaObject(), methodName );
00224 if ( argType == QVariant::Invalid ) {
00225 kError() << "Setting " << setting << " not found in agent configuration interface of " << instance.identifier();
00226 continue;
00227 }
00228 const QVariant arg = settings.readEntry( setting, QVariant( argType ) );
00229 const QDBusReply<void> reply = iface->call( methodName, arg );
00230 if ( !reply.isValid() )
00231 kError() << "Setting " << setting << " failed for agent " << instance.identifier();
00232 }
00233
00234 instance.reconfigure();
00235 delete iface;
00236
00237
00238 KConfigGroup cfg( mConfig, "ProcessedDefaults" );
00239 cfg.writeEntry( agentCfg.readEntry( "Id", QString() ), instance.identifier() );
00240 cfg.sync();
00241
00242 setupNext();
00243 }
00244
00245 QVariant::Type Firstrun::argumentType( const QMetaObject *mo, const QString &method )
00246 {
00247 QMetaMethod m;
00248 for ( int i = 0; i < mo->methodCount(); ++i ) {
00249 const QString signature = QString::fromLatin1( mo->method(i).signature() );
00250 if ( signature.startsWith( method ) )
00251 m = mo->method( i );
00252 }
00253
00254 if ( !m.signature() )
00255 return QVariant::Invalid;
00256
00257 const QList<QByteArray> argTypes = m.parameterTypes();
00258 if ( argTypes.count() != 1 )
00259 return QVariant::Invalid;
00260
00261 return QVariant::nameToType( argTypes.first() );
00262 }
00263
00264 #include "firstrun_p.moc"