00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020 #include "specialcollectionshelperjobs_p.h"
00021
00022 #include "specialcollectionattribute_p.h"
00023 #include "specialcollections.h"
00024
00025 #include <akonadi/agentinstance.h>
00026 #include <akonadi/agentinstancecreatejob.h>
00027 #include <akonadi/agentmanager.h>
00028 #include <akonadi/collectionfetchjob.h>
00029 #include <akonadi/collectionfetchscope.h>
00030 #include <akonadi/collectionmodifyjob.h>
00031 #include <akonadi/entitydisplayattribute.h>
00032 #include <akonadi/resourcesynchronizationjob.h>
00033
00034 #include <KDebug>
00035 #include <KLocalizedString>
00036 #include <KStandardDirs>
00037 #include <kcoreconfigskeleton.h>
00038
00039 #include <QtDBus/QDBusConnectionInterface>
00040 #include <QtDBus/QDBusInterface>
00041 #include <QtCore/QMetaMethod>
00042 #include <QtCore/QTime>
00043 #include <QtCore/QTimer>
00044
00045 #define DBUS_SERVICE_NAME QLatin1String( "org.kde.pim.SpecialCollections" )
00046 #define LOCK_WAIT_TIMEOUT_SECONDS 3
00047
00048 using namespace Akonadi;
00049
00050
00051 static void setDefaultResourceId( KCoreConfigSkeleton *settings, const QString &value )
00052 {
00053 KConfigSkeletonItem *item = settings->findItem( QLatin1String( "DefaultResourceId" ) );
00054 Q_ASSERT( item );
00055 item->setProperty( value );
00056 }
00057
00058 static QString defaultResourceId( KCoreConfigSkeleton *settings )
00059 {
00060 const KConfigSkeletonItem *item = settings->findItem( QLatin1String( "DefaultResourceId" ) );
00061 Q_ASSERT( item );
00062 return item->property().toString();
00063 }
00064
00065 static QVariant::Type argumentType( const QMetaObject *mo, const QString &method )
00066 {
00067 QMetaMethod m;
00068 for ( int i = 0; i < mo->methodCount(); ++i ) {
00069 const QString signature = QString::fromLatin1( mo->method( i ).signature() );
00070 if ( signature.startsWith( method ) )
00071 m = mo->method( i );
00072 }
00073
00074 if ( !m.signature() )
00075 return QVariant::Invalid;
00076
00077 const QList<QByteArray> argTypes = m.parameterTypes();
00078 if ( argTypes.count() != 1 )
00079 return QVariant::Invalid;
00080
00081 return QVariant::nameToType( argTypes.first() );
00082 }
00083
00084
00085
00089 class Akonadi::ResourceScanJob::Private
00090 {
00091 public:
00092 Private( KCoreConfigSkeleton *settings, ResourceScanJob *qq );
00093
00094 void fetchResult( KJob *job );
00095
00096 ResourceScanJob *const q;
00097
00098
00099 QString mResourceId;
00100 KCoreConfigSkeleton *mSettings;
00101
00102
00103 Collection mRootCollection;
00104 Collection::List mSpecialCollections;
00105 };
00106
00107 ResourceScanJob::Private::Private( KCoreConfigSkeleton *settings, ResourceScanJob *qq )
00108 : q( qq ), mSettings( settings )
00109 {
00110 }
00111
00112 void ResourceScanJob::Private::fetchResult( KJob *job )
00113 {
00114 if ( job->error() ) {
00115 kWarning() << job->errorText();
00116 return;
00117 }
00118
00119 CollectionFetchJob *fetchJob = qobject_cast<CollectionFetchJob *>( job );
00120 Q_ASSERT( fetchJob );
00121
00122 Q_ASSERT( !mRootCollection.isValid() );
00123 Q_ASSERT( mSpecialCollections.isEmpty() );
00124 foreach ( const Collection &collection, fetchJob->collections() ) {
00125 if ( collection.parentCollection() == Collection::root() ) {
00126 if ( mRootCollection.isValid() )
00127 kWarning() << "Resource has more than one root collection. I don't know what to do.";
00128 else
00129 mRootCollection = collection;
00130 }
00131
00132 if ( collection.hasAttribute<SpecialCollectionAttribute>() )
00133 mSpecialCollections.append( collection );
00134 }
00135
00136 kDebug() << "Fetched root collection" << mRootCollection.id()
00137 << "and" << mSpecialCollections.count() << "local folders"
00138 << "(total" << fetchJob->collections().count() << "collections).";
00139
00140 if ( !mRootCollection.isValid() ) {
00141 q->setError( Unknown );
00142 q->setErrorText( i18n( "Could not fetch root collection of resource %1.", mResourceId ) );
00143 q->commit();
00144 return;
00145 }
00146
00147
00148 q->commit();
00149 }
00150
00151
00152
00153 ResourceScanJob::ResourceScanJob( const QString &resourceId, KCoreConfigSkeleton *settings, QObject *parent )
00154 : TransactionSequence( parent ),
00155 d( new Private( settings, this ) )
00156 {
00157 setResourceId( resourceId );
00158 }
00159
00160 ResourceScanJob::~ResourceScanJob()
00161 {
00162 delete d;
00163 }
00164
00165 QString ResourceScanJob::resourceId() const
00166 {
00167 return d->mResourceId;
00168 }
00169
00170 void ResourceScanJob::setResourceId( const QString &resourceId )
00171 {
00172 d->mResourceId = resourceId;
00173 }
00174
00175 Collection ResourceScanJob::rootResourceCollection() const
00176 {
00177 return d->mRootCollection;
00178 }
00179
00180 Collection::List ResourceScanJob::specialCollections() const
00181 {
00182 return d->mSpecialCollections;
00183 }
00184
00185 void ResourceScanJob::doStart()
00186 {
00187 if ( d->mResourceId.isEmpty() ) {
00188 kError() << "No resource ID given.";
00189 setError( Job::Unknown );
00190 setErrorText( i18n( "No resource ID given." ) );
00191 emitResult();
00192 TransactionSequence::doStart();
00193
00194 return;
00195 }
00196
00197 CollectionFetchJob *fetchJob = new CollectionFetchJob( Collection::root(),
00198 CollectionFetchJob::Recursive, this );
00199 fetchJob->fetchScope().setResource( d->mResourceId );
00200 connect( fetchJob, SIGNAL( result( KJob* ) ), this, SLOT( fetchResult( KJob* ) ) );
00201 }
00202
00203
00204
00205
00209 class Akonadi::DefaultResourceJobPrivate
00210 {
00211 public:
00212 DefaultResourceJobPrivate( KCoreConfigSkeleton *settings, DefaultResourceJob *qq );
00213
00214 void tryFetchResource();
00215 void resourceCreateResult( KJob *job );
00216 void resourceSyncResult( KJob *job );
00217 void collectionFetchResult( KJob *job );
00218 void collectionModifyResult( KJob *job );
00219
00220 DefaultResourceJob *const q;
00221 KCoreConfigSkeleton *mSettings;
00222 bool mResourceWasPreexisting;
00223 int mPendingModifyJobs;
00224 QString mDefaultResourceType;
00225 QVariantMap mDefaultResourceOptions;
00226 QList<QByteArray> mKnownTypes;
00227 QMap<QByteArray, QString> mNameForTypeMap;
00228 QMap<QByteArray, QString> mIconForTypeMap;
00229 };
00230
00231 DefaultResourceJobPrivate::DefaultResourceJobPrivate( KCoreConfigSkeleton *settings, DefaultResourceJob *qq )
00232 : q( qq ),
00233 mSettings( settings ),
00234 mResourceWasPreexisting( true ),
00235 mPendingModifyJobs( 0 )
00236 {
00237 }
00238
00239 void DefaultResourceJobPrivate::tryFetchResource()
00240 {
00241
00242 mSettings->readConfig();
00243
00244 const QString resourceId = defaultResourceId( mSettings );
00245
00246 kDebug() << "Read defaultResourceId" << resourceId << "from config.";
00247
00248 const AgentInstance resource = AgentManager::self()->instance( resourceId );
00249 if ( resource.isValid() ) {
00250
00251 mResourceWasPreexisting = true;
00252 kDebug() << "Found resource" << resourceId;
00253 q->setResourceId( resourceId );
00254 q->ResourceScanJob::doStart();
00255 } else {
00256
00257 mResourceWasPreexisting = false;
00258 kDebug() << "Creating maildir resource.";
00259 const AgentType type = AgentManager::self()->type( mDefaultResourceType );
00260 AgentInstanceCreateJob *job = new AgentInstanceCreateJob( type, q );
00261 QObject::connect( job, SIGNAL( result( KJob* ) ), q, SLOT( resourceCreateResult( KJob* ) ) );
00262 job->start();
00263 }
00264 }
00265
00266 void DefaultResourceJobPrivate::resourceCreateResult( KJob *job )
00267 {
00268 if ( job->error() ) {
00269 kWarning() << job->errorText();
00270
00271 q->setError( job->error() );
00272 q->setErrorText( job->errorText() );
00273 q->emitResult();
00274 return;
00275 }
00276
00277 AgentInstance agent;
00278
00279
00280 {
00281 AgentInstanceCreateJob *createJob = qobject_cast<AgentInstanceCreateJob*>( job );
00282 Q_ASSERT( createJob );
00283 agent = createJob->instance();
00284 setDefaultResourceId( mSettings, agent.identifier() );
00285 kDebug() << "Created maildir resource with id" << defaultResourceId( mSettings );
00286 }
00287
00288 const QString defaultId = defaultResourceId( mSettings );
00289
00290
00291 {
00292 agent.setName( mDefaultResourceOptions.value( QLatin1String( "Name" ) ).toString() );
00293
00294 QDBusInterface conf( QString::fromLatin1( "org.freedesktop.Akonadi.Resource." ) + defaultId,
00295 QString::fromLatin1( "/Settings" ), QString() );
00296
00297 if( ! conf.isValid() ) {
00298 q->setError( -1 );
00299 q->setErrorText( i18n("Invalid resource identifier '%1'", defaultId) );
00300 q->emitResult();
00301 return;
00302 }
00303
00304 QMapIterator<QString, QVariant> it( mDefaultResourceOptions );
00305 while ( it.hasNext() ) {
00306 it.next();
00307
00308 if ( it.key() == QLatin1String( "Name" ) )
00309 continue;
00310
00311 const QString methodName = QString::fromLatin1( "set%1" ).arg( it.key() );
00312 const QVariant::Type argType = argumentType( conf.metaObject(), methodName );
00313 if ( argType == QVariant::Invalid ) {
00314 q->setError( Job::Unknown );
00315 q->setErrorText( i18n( "Failed to configure default resource via D-Bus." ) );
00316 q->commit();
00317 return;
00318 }
00319
00320 QDBusReply<void> reply = conf.call( methodName, it.value() );
00321 if ( !reply.isValid() ) {
00322 q->setError( Job::Unknown );
00323 q->setErrorText( i18n( "Failed to configure default resource via D-Bus." ) );
00324 q->commit();
00325 return;
00326 }
00327 }
00328
00329 agent.reconfigure();
00330 }
00331
00332
00333 {
00334 ResourceSynchronizationJob *syncJob = new ResourceSynchronizationJob( agent, q );
00335 QObject::connect( syncJob, SIGNAL( result( KJob* ) ), q, SLOT( resourceSyncResult( KJob* ) ) );
00336 syncJob->start();
00337 }
00338 }
00339
00340 void DefaultResourceJobPrivate::resourceSyncResult( KJob *job )
00341 {
00342 if ( job->error() ) {
00343 kWarning() << job->errorText();
00344
00345 return;
00346 }
00347
00348
00349 kDebug() << "Fetching maildir collections.";
00350 CollectionFetchJob *fetchJob = new CollectionFetchJob( Collection::root(), CollectionFetchJob::Recursive, q );
00351 fetchJob->fetchScope().setResource( defaultResourceId( mSettings ) );
00352 QObject::connect( fetchJob, SIGNAL( result( KJob* ) ), q, SLOT( collectionFetchResult( KJob* ) ) );
00353 }
00354
00355 void DefaultResourceJobPrivate::collectionFetchResult( KJob *job )
00356 {
00357 if ( job->error() ) {
00358 kWarning() << job->errorText();
00359
00360 return;
00361 }
00362
00363 CollectionFetchJob *fetchJob = qobject_cast<CollectionFetchJob *>( job );
00364 Q_ASSERT( fetchJob );
00365
00366 const Collection::List collections = fetchJob->collections();
00367 kDebug() << "Fetched" << collections.count() << "collections.";
00368
00369
00370 Collection::List toRecover;
00371 Collection resourceCollection;
00372 foreach ( const Collection &collection, collections ) {
00373 if ( collection.parentCollection() == Collection::root() ) {
00374 resourceCollection = collection;
00375 toRecover.append( collection );
00376 break;
00377 }
00378 }
00379
00380 if ( !resourceCollection.isValid() ) {
00381 q->setError( Job::Unknown );
00382 q->setErrorText( i18n( "Failed to fetch the resource collection." ) );
00383 q->commit();
00384 return;
00385 }
00386
00387
00388 foreach ( const Collection &collection, collections ) {
00389 if ( collection.parentCollection() == resourceCollection ) {
00390 toRecover.append( collection );
00391 }
00392 }
00393
00394 QHash<QString, QByteArray> typeForName;
00395 foreach ( const QByteArray &type, mKnownTypes ) {
00396 const QString displayName = mNameForTypeMap.value( type );
00397 typeForName[ displayName ] = type;
00398 }
00399
00400
00401
00402 Q_ASSERT( mPendingModifyJobs == 0 );
00403 foreach ( Collection collection, toRecover ) {
00404
00405
00406 QByteArray type;
00407 QString name = collection.name();
00408 if ( collection.hasAttribute<EntityDisplayAttribute>() )
00409 name = collection.attribute<EntityDisplayAttribute>()->displayName();
00410 if ( typeForName.contains( name ) )
00411 type = typeForName[ name ];
00412
00413 if ( !type.isEmpty() ) {
00414 kDebug() << "Recovering collection" << name;
00415 setCollectionAttributes( collection, type, mNameForTypeMap, mIconForTypeMap );
00416
00417 CollectionModifyJob *modifyJob = new CollectionModifyJob( collection, q );
00418 QObject::connect( modifyJob, SIGNAL( result( KJob* ) ), q, SLOT( collectionModifyResult( KJob* ) ) );
00419 mPendingModifyJobs++;
00420 } else {
00421 kDebug() << "Searching for names: " << typeForName.keys();
00422 kDebug() << "Unknown collection name" << name << "-- not recovering.";
00423 }
00424 }
00425
00426 if ( mPendingModifyJobs == 0 )
00427 q->commit();
00428 }
00429
00430 void DefaultResourceJobPrivate::collectionModifyResult( KJob *job )
00431 {
00432 if ( job->error() ) {
00433 kWarning() << job->errorText();
00434
00435 return;
00436 }
00437
00438 Q_ASSERT( mPendingModifyJobs > 0 );
00439 mPendingModifyJobs--;
00440 kDebug() << "pendingModifyJobs now" << mPendingModifyJobs;
00441 if ( mPendingModifyJobs == 0 ) {
00442
00443 kDebug() << "Writing defaultResourceId" << defaultResourceId( mSettings ) << "to config.";
00444 mSettings->writeConfig();
00445
00446
00447 q->setResourceId( defaultResourceId( mSettings ) );
00448 q->ResourceScanJob::doStart();
00449 }
00450 }
00451
00452
00453
00454 DefaultResourceJob::DefaultResourceJob( KCoreConfigSkeleton *settings, QObject *parent )
00455 : ResourceScanJob( QString(), settings, parent ),
00456 d( new DefaultResourceJobPrivate( settings, this ) )
00457 {
00458 }
00459
00460 DefaultResourceJob::~DefaultResourceJob()
00461 {
00462 delete d;
00463 }
00464
00465 void DefaultResourceJob::setDefaultResourceType( const QString &type )
00466 {
00467 d->mDefaultResourceType = type;
00468 }
00469
00470 void DefaultResourceJob::setDefaultResourceOptions( const QVariantMap &options )
00471 {
00472 d->mDefaultResourceOptions = options;
00473 }
00474
00475 void DefaultResourceJob::setTypes( const QList<QByteArray> &types )
00476 {
00477 d->mKnownTypes = types;
00478 }
00479
00480 void DefaultResourceJob::setNameForTypeMap( const QMap<QByteArray, QString> &map )
00481 {
00482 d->mNameForTypeMap = map;
00483 }
00484
00485 void DefaultResourceJob::setIconForTypeMap( const QMap<QByteArray, QString> &map )
00486 {
00487 d->mIconForTypeMap = map;
00488 }
00489
00490 void DefaultResourceJob::doStart()
00491 {
00492 d->tryFetchResource();
00493 }
00494
00495 void DefaultResourceJob::slotResult( KJob *job )
00496 {
00497 if ( job->error() ) {
00498 kWarning() << job->errorText();
00499
00500 if ( !d->mResourceWasPreexisting ) {
00501
00502
00503 const AgentInstance resource = AgentManager::self()->instance( defaultResourceId( d->mSettings ) );
00504 kDebug() << "Removing resource" << resource.identifier();
00505 AgentManager::self()->removeInstance( resource );
00506 }
00507 }
00508
00509 TransactionSequence::slotResult( job );
00510 }
00511
00512
00513
00514 class Akonadi::GetLockJob::Private
00515 {
00516 public:
00517 Private( GetLockJob *qq );
00518
00519 void doStart();
00520 void serviceOwnerChanged( const QString &name, const QString &oldOwner,
00521 const QString &newOwner );
00522 void timeout();
00523
00524 GetLockJob *const q;
00525 QTimer *mSafetyTimer;
00526 };
00527
00528 GetLockJob::Private::Private( GetLockJob *qq )
00529 : q( qq ),
00530 mSafetyTimer( 0 )
00531 {
00532 }
00533
00534 void GetLockJob::Private::doStart()
00535 {
00536
00537
00538
00539 QDBusConnection bus = QDBusConnection::sessionBus();
00540 const bool alreadyLocked = bus.interface()->isServiceRegistered( DBUS_SERVICE_NAME );
00541 const bool gotIt = bus.registerService( DBUS_SERVICE_NAME );
00542
00543 if ( gotIt && !alreadyLocked ) {
00544
00545 q->emitResult();
00546 } else {
00547
00548 connect( QDBusConnection::sessionBus().interface(), SIGNAL( serviceOwnerChanged( QString, QString, QString ) ),
00549 q, SLOT( serviceOwnerChanged( QString, QString, QString ) ) );
00550
00551 mSafetyTimer = new QTimer( q );
00552 mSafetyTimer->setSingleShot( true );
00553 mSafetyTimer->setInterval( LOCK_WAIT_TIMEOUT_SECONDS * 1000 );
00554 mSafetyTimer->start();
00555 connect( mSafetyTimer, SIGNAL( timeout() ), q, SLOT( timeout() ) );
00556 }
00557 }
00558
00559 void GetLockJob::Private::serviceOwnerChanged( const QString &name, const QString &oldOwner, const QString &newOwner )
00560 {
00561 Q_UNUSED( oldOwner );
00562
00563 if ( name == DBUS_SERVICE_NAME && newOwner.isEmpty() ) {
00564 const bool gotIt = QDBusConnection::sessionBus().registerService( DBUS_SERVICE_NAME );
00565 if ( gotIt ) {
00566 mSafetyTimer->stop();
00567 q->emitResult();
00568 }
00569 }
00570 }
00571
00572 void GetLockJob::Private::timeout()
00573 {
00574 kWarning() << "Timeout trying to get lock.";
00575 q->setError( Job::Unknown );
00576 q->setErrorText( i18n( "Timeout trying to get lock." ) );
00577 q->emitResult();
00578 }
00579
00580
00581 GetLockJob::GetLockJob( QObject *parent )
00582 : KJob( parent ),
00583 d( new Private( this ) )
00584 {
00585 }
00586
00587 GetLockJob::~GetLockJob()
00588 {
00589 delete d;
00590 }
00591
00592 void GetLockJob::start()
00593 {
00594 QTimer::singleShot( 0, this, SLOT( doStart() ) );
00595 }
00596
00597 void Akonadi::setCollectionAttributes( Akonadi::Collection &collection, const QByteArray &type,
00598 const QMap<QByteArray, QString> &nameForType,
00599 const QMap<QByteArray, QString> &iconForType )
00600 {
00601 {
00602 EntityDisplayAttribute *attr = new EntityDisplayAttribute;
00603 attr->setIconName( iconForType.value( type ) );
00604 attr->setDisplayName( nameForType.value( type ) );
00605 collection.addAttribute( attr );
00606 }
00607
00608 {
00609 SpecialCollectionAttribute *attr = new SpecialCollectionAttribute;
00610 attr->setCollectionType( type );
00611 collection.addAttribute( attr );
00612 }
00613 }
00614
00615 bool Akonadi::releaseLock()
00616 {
00617 return QDBusConnection::sessionBus().unregisterService( DBUS_SERVICE_NAME );
00618 }
00619
00620 #include "specialcollectionshelperjobs_p.moc"