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
96 void fetchResult( KJob *job );
102 KCoreConfigSkeleton *mSettings;
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();
122 Q_ASSERT( fetchJob );
124 Q_ASSERT( !mRootCollection.isValid() );
125 Q_ASSERT( mSpecialCollections.isEmpty() );
128 if ( mRootCollection.isValid() )
129 kWarning() <<
"Resource has more than one root collection. I don't know what to do.";
131 mRootCollection = collection;
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 ) );
157 d( new Private( settings, this ) )
169 return d->mResourceId;
179 return d->mRootCollection;
184 return d->mSpecialCollections;
189 if ( d->mResourceId.isEmpty() ) {
190 kError() <<
"No resource ID given.";
192 setErrorText( i18n(
"No resource ID given." ) );
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 );
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 ) {
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() ) {
347 q->setErrorText( i18n(
"Failed to configure default resource via D-Bus." ) );
353 conf.call( QLatin1String(
"writeConfig" ) );
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.";
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();
390 Q_ASSERT( fetchJob );
393 kDebug() <<
"Fetched" << collections.count() <<
"collections.";
398 foreach (
const Collection &collection, collections ) {
400 resourceCollection = collection;
401 toRecover.append( collection );
406 if ( !resourceCollection.
isValid() ) {
408 q->setErrorText( i18n(
"Failed to fetch the resource collection." ) );
414 foreach (
const Collection &collection, collections ) {
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 ) {
435 QString name = collection.
name();
438 if (!displayName.isEmpty())
441 const QByteArray type = typeForName.value( name );
443 if ( !type.isEmpty() ) {
444 kDebug() <<
"Recovering collection" << name;
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();
489 d( new DefaultResourceJobPrivate( settings, this ) )
500 d->mDefaultResourceType = type;
505 d->mDefaultResourceOptions = options;
510 d->mKnownTypes = types;
515 d->mNameForTypeMap = map;
520 d->mIconForTypeMap = map;
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
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.";
609 q->setErrorText( i18n(
"Timeout trying to get lock." ) );
616 d( new Private( this ) )
625 void GetLockJob::start()
627 QTimer::singleShot( 0,
this, SLOT(doStart()) );
631 const QMap<QByteArray, QString> &nameForType,
632 const QMap<QByteArray, QString> &iconForType )
650 return DBusConnectionPool::threadConnection().unregisterService( DBUS_SERVICE_NAME );
653 #include "specialcollectionshelperjobs_p.moc"