20 #include "specialcollectionshelperjobs_p.h"
22 #include "dbusconnectionpool.h"
23 #include "specialcollectionattribute_p.h"
24 #include "specialcollections.h"
26 #include <akonadi/agentinstance.h>
27 #include <akonadi/agentinstancecreatejob.h>
28 #include <akonadi/agentmanager.h>
29 #include <akonadi/collectionfetchjob.h>
30 #include <akonadi/collectionfetchscope.h>
31 #include <akonadi/collectionmodifyjob.h>
32 #include <akonadi/entitydisplayattribute.h>
33 #include <akonadi/resourcesynchronizationjob.h>
36 #include <KLocalizedString>
37 #include <KStandardDirs>
38 #include <kcoreconfigskeleton.h>
40 #include <QtDBus/QDBusConnectionInterface>
41 #include <QtDBus/QDBusInterface>
42 #include <QtDBus/QDBusServiceWatcher>
43 #include <QtCore/QMetaMethod>
44 #include <QtCore/QTime>
45 #include <QtCore/QTimer>
47 #define DBUS_SERVICE_NAME QLatin1String( "org.kde.pim.SpecialCollections" )
48 #define LOCK_WAIT_TIMEOUT_SECONDS 10
50 using namespace Akonadi;
53 static void setDefaultResourceId( KCoreConfigSkeleton *settings,
const QString &value )
55 KConfigSkeletonItem *item = settings->findItem( QLatin1String(
"DefaultResourceId" ) );
57 item->setProperty( value );
60 static QString defaultResourceId( KCoreConfigSkeleton *settings )
62 const KConfigSkeletonItem *item = settings->findItem( QLatin1String(
"DefaultResourceId" ) );
64 return item->property().toString();
67 static QVariant::Type argumentType(
const QMetaObject *mo,
const QString &method )
70 for (
int i = 0; i < mo->methodCount(); ++i ) {
71 const QString signature = QString::fromLatin1( mo->method( i ).signature() );
72 if ( signature.startsWith( method ) )
77 return QVariant::Invalid;
79 const QList<QByteArray> argTypes = m.parameterTypes();
80 if ( argTypes.count() != 1 )
81 return QVariant::Invalid;
83 return QVariant::nameToType( argTypes.first() );
91 class Akonadi::ResourceScanJob::Private
94 Private( KCoreConfigSkeleton *settings, ResourceScanJob *qq );
96 void fetchResult( KJob *job );
98 ResourceScanJob *
const q;
102 KCoreConfigSkeleton *mSettings;
105 Collection mRootCollection;
106 Collection::List mSpecialCollections;
109 ResourceScanJob::Private::Private( KCoreConfigSkeleton *settings, ResourceScanJob *qq )
110 : q( qq ), mSettings( settings )
114 void ResourceScanJob::Private::fetchResult( KJob *job )
116 if ( job->error() ) {
117 kWarning() << job->errorText();
121 CollectionFetchJob *fetchJob = qobject_cast<CollectionFetchJob *>( job );
122 Q_ASSERT( fetchJob );
124 Q_ASSERT( !mRootCollection.isValid() );
125 Q_ASSERT( mSpecialCollections.isEmpty() );
126 foreach (
const Collection &collection, fetchJob->collections() ) {
127 if ( collection.parentCollection() == Collection::root() ) {
128 if ( mRootCollection.isValid() )
129 kWarning() <<
"Resource has more than one root collection. I don't know what to do.";
131 mRootCollection = collection;
134 if ( collection.hasAttribute<SpecialCollectionAttribute>() )
135 mSpecialCollections.append( collection );
138 kDebug() <<
"Fetched root collection" << mRootCollection.id()
139 <<
"and" << mSpecialCollections.count() <<
"local folders"
140 <<
"(total" << fetchJob->collections().count() <<
"collections).";
142 if ( !mRootCollection.isValid() ) {
143 q->setError( Unknown );
144 q->setErrorText( i18n(
"Could not fetch root collection of resource %1.", mResourceId ) );
155 ResourceScanJob::ResourceScanJob(
const QString &resourceId, KCoreConfigSkeleton *settings, QObject *parent )
157 d( new Private( settings, this ) )
162 ResourceScanJob::~ResourceScanJob()
167 QString ResourceScanJob::resourceId()
const
169 return d->mResourceId;
172 void ResourceScanJob::setResourceId(
const QString &resourceId )
174 d->mResourceId = resourceId;
177 Akonadi::Collection ResourceScanJob::rootResourceCollection()
const
179 return d->mRootCollection;
182 Akonadi::Collection::List ResourceScanJob::specialCollections()
const
184 return d->mSpecialCollections;
187 void ResourceScanJob::doStart()
189 if ( d->mResourceId.isEmpty() ) {
190 kError() <<
"No resource ID given.";
191 setError( Job::Unknown );
192 setErrorText( i18n(
"No resource ID given." ) );
198 CollectionFetchJob::Recursive,
this );
201 connect( fetchJob, SIGNAL(result(KJob*)),
this, SLOT(fetchResult(KJob*)) );
210 class Akonadi::DefaultResourceJobPrivate
215 void tryFetchResource();
216 void resourceCreateResult( KJob *job );
217 void resourceSyncResult( KJob *job );
218 void collectionFetchResult( KJob *job );
219 void collectionModifyResult( KJob *job );
222 KCoreConfigSkeleton *mSettings;
223 bool mResourceWasPreexisting;
224 int mPendingModifyJobs;
225 QString mDefaultResourceType;
226 QVariantMap mDefaultResourceOptions;
227 QList<QByteArray> mKnownTypes;
228 QMap<QByteArray, QString> mNameForTypeMap;
229 QMap<QByteArray, QString> mIconForTypeMap;
232 DefaultResourceJobPrivate::DefaultResourceJobPrivate( KCoreConfigSkeleton *settings, DefaultResourceJob *qq )
234 mSettings( settings ),
235 mResourceWasPreexisting( true ),
236 mPendingModifyJobs( 0 )
240 void DefaultResourceJobPrivate::tryFetchResource()
243 mSettings->readConfig();
245 const QString resourceId = defaultResourceId( mSettings );
247 kDebug() <<
"Read defaultResourceId" << resourceId <<
"from config.";
252 mResourceWasPreexisting =
true;
253 kDebug() <<
"Found resource" << resourceId;
254 q->setResourceId( resourceId );
256 CollectionFetchJob *fetchJob =
new CollectionFetchJob( Collection::root(), CollectionFetchJob::Recursive, q );
257 fetchJob->fetchScope().setResource( resourceId );
258 fetchJob->fetchScope().setIncludeStatistics(
true );
259 q->connect( fetchJob, SIGNAL(result(KJob*)), q, SLOT(collectionFetchResult(KJob*)) );
267 if ( resource.
name() == mDefaultResourceOptions.value( QLatin1String(
"Name" ) ).toString() ) {
269 setDefaultResourceId( mSettings, resource.
identifier() );
270 mSettings->writeConfig();
271 mResourceWasPreexisting =
true;
272 kDebug() <<
"Found resource" << resource.
identifier();
274 q->ResourceScanJob::doStart();
281 mResourceWasPreexisting =
false;
282 kDebug() <<
"Creating maildir resource.";
285 QObject::connect( job, SIGNAL(result(KJob*)), q, SLOT(resourceCreateResult(KJob*)) );
290 void DefaultResourceJobPrivate::resourceCreateResult( KJob *job )
292 if ( job->error() ) {
293 kWarning() << job->errorText();
295 q->setError( job->error() );
296 q->setErrorText( job->errorText() );
306 Q_ASSERT( createJob );
308 setDefaultResourceId( mSettings, agent.
identifier() );
309 kDebug() <<
"Created maildir resource with id" << defaultResourceId( mSettings );
312 const QString defaultId = defaultResourceId( mSettings );
316 agent.
setName( mDefaultResourceOptions.value( QLatin1String(
"Name" ) ).toString() );
318 QDBusInterface conf( QString::fromLatin1(
"org.freedesktop.Akonadi.Resource." ) + defaultId,
319 QString::fromLatin1(
"/Settings" ), QString() );
321 if ( !conf.isValid() ) {
323 q->setErrorText( i18n(
"Invalid resource identifier '%1'", defaultId ) );
328 QMapIterator<QString, QVariant> it( mDefaultResourceOptions );
329 while ( it.hasNext() ) {
332 if ( it.key() == QLatin1String(
"Name" ) )
335 const QString methodName = QString::fromLatin1(
"set%1" ).arg( it.key() );
336 const QVariant::Type argType = argumentType( conf.metaObject(), methodName );
337 if ( argType == QVariant::Invalid ) {
338 q->setError( Job::Unknown );
339 q->setErrorText( i18n(
"Failed to configure default resource via D-Bus." ) );
344 QDBusReply<void> reply = conf.call( methodName, it.value() );
345 if ( !reply.isValid() ) {
346 q->setError( Job::Unknown );
347 q->setErrorText( i18n(
"Failed to configure default resource via D-Bus." ) );
353 conf.call( QLatin1String(
"writeConfig" ) );
360 ResourceSynchronizationJob *syncJob =
new ResourceSynchronizationJob( agent, q );
361 QObject::connect( syncJob, SIGNAL(result(KJob*)), q, SLOT(resourceSyncResult(KJob*)) );
366 void DefaultResourceJobPrivate::resourceSyncResult( KJob *job )
368 if ( job->error() ) {
369 kWarning() << job->errorText();
375 kDebug() <<
"Fetching maildir collections.";
376 CollectionFetchJob *fetchJob =
new CollectionFetchJob( Collection::root(), CollectionFetchJob::Recursive, q );
377 fetchJob->fetchScope().setResource( defaultResourceId( mSettings ) );
378 QObject::connect( fetchJob, SIGNAL(result(KJob*)), q, SLOT(collectionFetchResult(KJob*)) );
381 void DefaultResourceJobPrivate::collectionFetchResult( KJob *job )
383 if ( job->error() ) {
384 kWarning() << job->errorText();
389 CollectionFetchJob *fetchJob = qobject_cast<CollectionFetchJob *>( job );
390 Q_ASSERT( fetchJob );
392 const Collection::List collections = fetchJob->collections();
393 kDebug() <<
"Fetched" << collections.count() <<
"collections.";
396 Collection::List toRecover;
397 Collection resourceCollection;
398 foreach (
const Collection &collection, collections ) {
399 if ( collection.parentCollection() == Collection::root() ) {
400 resourceCollection = collection;
401 toRecover.append( collection );
406 if ( !resourceCollection.isValid() ) {
407 q->setError( Job::Unknown );
408 q->setErrorText( i18n(
"Failed to fetch the resource collection." ) );
414 foreach (
const Collection &collection, collections ) {
415 if ( collection.parentCollection() == resourceCollection ) {
416 toRecover.append( collection );
420 QHash<QString, QByteArray> typeForName;
421 foreach (
const QByteArray &type, mKnownTypes ) {
422 const QString displayName = mNameForTypeMap.value( type );
423 typeForName[ displayName ] = type;
428 Q_ASSERT( mPendingModifyJobs == 0 );
429 foreach ( Collection collection, toRecover ) {
431 if ( collection.hasAttribute<SpecialCollectionAttribute>() )
435 QString name = collection.name();
436 if ( collection.hasAttribute<EntityDisplayAttribute>() ) {
437 const QString displayName = collection.attribute<EntityDisplayAttribute>()->displayName();
438 if (!displayName.isEmpty())
441 const QByteArray type = typeForName.value( name );
443 if ( !type.isEmpty() ) {
444 kDebug() <<
"Recovering collection" << name;
447 CollectionModifyJob *modifyJob =
new CollectionModifyJob( collection, q );
448 QObject::connect( modifyJob, SIGNAL(result(KJob*)), q, SLOT(collectionModifyResult(KJob*)) );
449 mPendingModifyJobs++;
451 kDebug() <<
"Searching for names: " << typeForName.keys();
452 kDebug() <<
"Unknown collection name" << name <<
"-- not recovering.";
456 if ( mPendingModifyJobs == 0 ) {
458 q->setResourceId( defaultResourceId( mSettings ) );
459 q->ResourceScanJob::doStart();
463 void DefaultResourceJobPrivate::collectionModifyResult( KJob *job )
465 if ( job->error() ) {
466 kWarning() << job->errorText();
471 Q_ASSERT( mPendingModifyJobs > 0 );
472 mPendingModifyJobs--;
473 kDebug() <<
"pendingModifyJobs now" << mPendingModifyJobs;
474 if ( mPendingModifyJobs == 0 ) {
476 kDebug() <<
"Writing defaultResourceId" << defaultResourceId( mSettings ) <<
"to config.";
477 mSettings->writeConfig();
480 q->setResourceId( defaultResourceId( mSettings ) );
481 q->ResourceScanJob::doStart();
487 DefaultResourceJob::DefaultResourceJob( KCoreConfigSkeleton *settings, QObject *parent )
489 d( new DefaultResourceJobPrivate( settings, this ) )
493 DefaultResourceJob::~DefaultResourceJob()
498 void DefaultResourceJob::setDefaultResourceType(
const QString &type )
500 d->mDefaultResourceType = type;
503 void DefaultResourceJob::setDefaultResourceOptions(
const QVariantMap &options )
505 d->mDefaultResourceOptions = options;
508 void DefaultResourceJob::setTypes(
const QList<QByteArray> &types )
510 d->mKnownTypes = types;
513 void DefaultResourceJob::setNameForTypeMap(
const QMap<QByteArray, QString> &map )
515 d->mNameForTypeMap = map;
518 void DefaultResourceJob::setIconForTypeMap(
const QMap<QByteArray, QString> &map )
520 d->mIconForTypeMap = map;
523 void DefaultResourceJob::doStart()
525 d->tryFetchResource();
528 void DefaultResourceJob::slotResult( KJob *job )
530 if ( job->error() ) {
531 kWarning() << job->errorText();
533 if ( !d->mResourceWasPreexisting ) {
537 kDebug() <<
"Removing resource" << resource.
identifier();
542 Job::slotResult( job );
547 class Akonadi::GetLockJob::Private
550 Private( GetLockJob *qq );
553 void serviceOwnerChanged(
const QString &name,
const QString &oldOwner,
554 const QString &newOwner );
558 QTimer *mSafetyTimer;
561 GetLockJob::Private::Private( GetLockJob *qq )
567 void GetLockJob::Private::doStart()
572 QDBusConnection bus = DBusConnectionPool::threadConnection();
573 const bool alreadyLocked = bus.interface()->isServiceRegistered( DBUS_SERVICE_NAME );
574 const bool gotIt = bus.registerService( DBUS_SERVICE_NAME );
576 if ( gotIt && !alreadyLocked ) {
580 QDBusServiceWatcher *watcher =
new QDBusServiceWatcher( DBUS_SERVICE_NAME, DBusConnectionPool::threadConnection(),
581 QDBusServiceWatcher::WatchForOwnerChange, q );
583 connect( watcher, SIGNAL(serviceOwnerChanged(QString,QString,QString)),
584 q, SLOT(serviceOwnerChanged(QString,QString,QString)) );
586 mSafetyTimer =
new QTimer( q );
587 mSafetyTimer->setSingleShot(
true );
588 mSafetyTimer->setInterval( LOCK_WAIT_TIMEOUT_SECONDS * 1000 );
589 mSafetyTimer->start();
590 connect( mSafetyTimer, SIGNAL(timeout()), q, SLOT(timeout()) );
594 void GetLockJob::Private::serviceOwnerChanged(
const QString&,
const QString&,
const QString &newOwner )
596 if ( newOwner.isEmpty() ) {
597 const bool gotIt = DBusConnectionPool::threadConnection().registerService( DBUS_SERVICE_NAME );
599 mSafetyTimer->stop();
605 void GetLockJob::Private::timeout()
607 kWarning() <<
"Timeout trying to get lock. Check who has acquired the name" << DBUS_SERVICE_NAME <<
"on DBus, using qdbus or qdbusviewer.";
608 q->setError( Job::Unknown );
609 q->setErrorText( i18n(
"Timeout trying to get lock." ) );
614 GetLockJob::GetLockJob( QObject *parent )
616 d( new Private( this ) )
620 GetLockJob::~GetLockJob()
625 void GetLockJob::start()
627 QTimer::singleShot( 0,
this, SLOT(doStart()) );
631 const QMap<QByteArray, QString> &nameForType,
632 const QMap<QByteArray, QString> &iconForType )
635 EntityDisplayAttribute *attr =
new EntityDisplayAttribute;
636 attr->setIconName( iconForType.value( type ) );
637 attr->setDisplayName( nameForType.value( type ) );
638 collection.addAttribute( attr );
642 SpecialCollectionAttribute *attr =
new SpecialCollectionAttribute;
643 attr->setCollectionType( type );
644 collection.addAttribute( attr );
650 return DBusConnectionPool::threadConnection().unregisterService( DBUS_SERVICE_NAME );
653 #include "specialcollectionshelperjobs_p.moc"