20 #include "specialcollectionshelperjobs_p.h"
22 #include "dbusconnectionpool.h"
23 #include "specialcollectionattribute_p.h"
24 #include "specialcollections.h"
25 #include "servermanager.h"
27 #include <akonadi/agentinstance.h>
28 #include <akonadi/agentinstancecreatejob.h>
29 #include <akonadi/agentmanager.h>
30 #include <akonadi/collectionfetchjob.h>
31 #include <akonadi/collectionfetchscope.h>
32 #include <akonadi/collectionmodifyjob.h>
33 #include <akonadi/entitydisplayattribute.h>
34 #include <akonadi/resourcesynchronizationjob.h>
37 #include <KLocalizedString>
38 #include <KStandardDirs>
39 #include <kcoreconfigskeleton.h>
41 #include <QtDBus/QDBusConnectionInterface>
42 #include <QtDBus/QDBusInterface>
43 #include <QtDBus/QDBusServiceWatcher>
44 #include <QtCore/QMetaMethod>
45 #include <QtCore/QTime>
46 #include <QtCore/QTimer>
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 QString dbusServiceName()
69 QString service = QString::fromLatin1(
"org.kde.pim.SpecialCollections");
75 static QVariant::Type argumentType(
const QMetaObject *mo,
const QString &method )
78 for (
int i = 0; i < mo->methodCount(); ++i ) {
79 const QString signature = QString::fromLatin1( mo->method( i ).signature() );
80 if ( signature.startsWith( method ) )
85 return QVariant::Invalid;
87 const QList<QByteArray> argTypes = m.parameterTypes();
88 if ( argTypes.count() != 1 )
89 return QVariant::Invalid;
91 return QVariant::nameToType( argTypes.first() );
99 class Akonadi::ResourceScanJob::Private
104 void fetchResult( KJob *job );
110 KCoreConfigSkeleton *mSettings;
117 ResourceScanJob::Private::Private( KCoreConfigSkeleton *settings,
ResourceScanJob *qq )
118 : q( qq ), mSettings( settings )
122 void ResourceScanJob::Private::fetchResult( KJob *job )
124 if ( job->error() ) {
125 kWarning() << job->errorText();
130 Q_ASSERT( fetchJob );
132 Q_ASSERT( !mRootCollection.isValid() );
133 Q_ASSERT( mSpecialCollections.isEmpty() );
136 if ( mRootCollection.isValid() )
137 kWarning() <<
"Resource has more than one root collection. I don't know what to do.";
139 mRootCollection = collection;
143 mSpecialCollections.append( collection );
146 kDebug() <<
"Fetched root collection" << mRootCollection.
id()
147 <<
"and" << mSpecialCollections.count() <<
"local folders"
148 <<
"(total" << fetchJob->
collections().count() <<
"collections).";
150 if ( !mRootCollection.isValid() ) {
151 q->setError( Unknown );
152 q->setErrorText( i18n(
"Could not fetch root collection of resource %1.", mResourceId ) );
165 d( new Private( settings, this ) )
177 return d->mResourceId;
187 return d->mRootCollection;
192 return d->mSpecialCollections;
197 if ( d->mResourceId.isEmpty() ) {
198 kError() <<
"No resource ID given.";
200 setErrorText( i18n(
"No resource ID given." ) );
209 connect( fetchJob, SIGNAL(result(KJob*)),
this, SLOT(fetchResult(KJob*)) );
218 class Akonadi::DefaultResourceJobPrivate
223 void tryFetchResource();
224 void resourceCreateResult( KJob *job );
225 void resourceSyncResult( KJob *job );
226 void collectionFetchResult( KJob *job );
227 void collectionModifyResult( KJob *job );
230 KCoreConfigSkeleton *mSettings;
231 bool mResourceWasPreexisting;
232 int mPendingModifyJobs;
233 QString mDefaultResourceType;
234 QVariantMap mDefaultResourceOptions;
235 QList<QByteArray> mKnownTypes;
236 QMap<QByteArray, QString> mNameForTypeMap;
237 QMap<QByteArray, QString> mIconForTypeMap;
240 DefaultResourceJobPrivate::DefaultResourceJobPrivate( KCoreConfigSkeleton *settings,
DefaultResourceJob *qq )
242 mSettings( settings ),
243 mResourceWasPreexisting( true ),
244 mPendingModifyJobs( 0 )
248 void DefaultResourceJobPrivate::tryFetchResource()
251 mSettings->readConfig();
253 const QString resourceId = defaultResourceId( mSettings );
255 kDebug() <<
"Read defaultResourceId" << resourceId <<
"from config.";
260 mResourceWasPreexisting =
true;
261 kDebug() <<
"Found resource" << resourceId;
262 q->setResourceId( resourceId );
267 q->connect( fetchJob, SIGNAL(result(KJob*)), q, SLOT(collectionFetchResult(KJob*)) );
275 if ( resource.
name() == mDefaultResourceOptions.value( QLatin1String(
"Name" ) ).toString() ) {
277 setDefaultResourceId( mSettings, resource.
identifier() );
278 mSettings->writeConfig();
279 mResourceWasPreexisting =
true;
280 kDebug() <<
"Found resource" << resource.
identifier();
282 q->ResourceScanJob::doStart();
289 mResourceWasPreexisting =
false;
290 kDebug() <<
"Creating maildir resource.";
293 QObject::connect( job, SIGNAL(result(KJob*)), q, SLOT(resourceCreateResult(KJob*)) );
298 void DefaultResourceJobPrivate::resourceCreateResult( KJob *job )
300 if ( job->error() ) {
301 kWarning() << job->errorText();
303 q->setError( job->error() );
304 q->setErrorText( job->errorText() );
314 Q_ASSERT( createJob );
316 setDefaultResourceId( mSettings, agent.
identifier() );
317 kDebug() <<
"Created maildir resource with id" << defaultResourceId( mSettings );
320 const QString defaultId = defaultResourceId( mSettings );
324 agent.
setName( mDefaultResourceOptions.value( QLatin1String(
"Name" ) ).toString() );
326 QDBusInterface conf( QString::fromLatin1(
"org.freedesktop.Akonadi.Resource." ) + defaultId,
327 QString::fromLatin1(
"/Settings" ), QString() );
329 if ( !conf.isValid() ) {
331 q->setErrorText( i18n(
"Invalid resource identifier '%1'", defaultId ) );
336 QMapIterator<QString, QVariant> it( mDefaultResourceOptions );
337 while ( it.hasNext() ) {
340 if ( it.key() == QLatin1String(
"Name" ) )
343 const QString methodName = QString::fromLatin1(
"set%1" ).arg( it.key() );
344 const QVariant::Type argType = argumentType( conf.metaObject(), methodName );
345 if ( argType == QVariant::Invalid ) {
347 q->setErrorText( i18n(
"Failed to configure default resource via D-Bus." ) );
352 QDBusReply<void> reply = conf.call( methodName, it.value() );
353 if ( !reply.isValid() ) {
355 q->setErrorText( i18n(
"Failed to configure default resource via D-Bus." ) );
361 conf.call( QLatin1String(
"writeConfig" ) );
369 QObject::connect( syncJob, SIGNAL(result(KJob*)), q, SLOT(resourceSyncResult(KJob*)) );
374 void DefaultResourceJobPrivate::resourceSyncResult( KJob *job )
376 if ( job->error() ) {
377 kWarning() << job->errorText();
383 kDebug() <<
"Fetching maildir collections.";
386 QObject::connect( fetchJob, SIGNAL(result(KJob*)), q, SLOT(collectionFetchResult(KJob*)) );
389 void DefaultResourceJobPrivate::collectionFetchResult( KJob *job )
391 if ( job->error() ) {
392 kWarning() << job->errorText();
398 Q_ASSERT( fetchJob );
401 kDebug() <<
"Fetched" << collections.count() <<
"collections.";
406 foreach (
const Collection &collection, collections ) {
408 resourceCollection = collection;
409 toRecover.append( collection );
414 if ( !resourceCollection.
isValid() ) {
416 q->setErrorText( i18n(
"Failed to fetch the resource collection." ) );
422 foreach (
const Collection &collection, collections ) {
424 toRecover.append( collection );
428 QHash<QString, QByteArray> typeForName;
429 foreach (
const QByteArray &type, mKnownTypes ) {
430 const QString displayName = mNameForTypeMap.value( type );
431 typeForName[ displayName ] = type;
436 Q_ASSERT( mPendingModifyJobs == 0 );
437 foreach (
Collection collection, toRecover ) {
443 QString name = collection.
name();
446 if (!displayName.isEmpty())
449 const QByteArray type = typeForName.value( name );
451 if ( !type.isEmpty() ) {
452 kDebug() <<
"Recovering collection" << name;
456 QObject::connect( modifyJob, SIGNAL(result(KJob*)), q, SLOT(collectionModifyResult(KJob*)) );
457 mPendingModifyJobs++;
459 kDebug() <<
"Searching for names: " << typeForName.keys();
460 kDebug() <<
"Unknown collection name" << name <<
"-- not recovering.";
464 if ( mPendingModifyJobs == 0 ) {
466 q->setResourceId( defaultResourceId( mSettings ) );
467 q->ResourceScanJob::doStart();
471 void DefaultResourceJobPrivate::collectionModifyResult( KJob *job )
473 if ( job->error() ) {
474 kWarning() << job->errorText();
479 Q_ASSERT( mPendingModifyJobs > 0 );
480 mPendingModifyJobs--;
481 kDebug() <<
"pendingModifyJobs now" << mPendingModifyJobs;
482 if ( mPendingModifyJobs == 0 ) {
484 kDebug() <<
"Writing defaultResourceId" << defaultResourceId( mSettings ) <<
"to config.";
485 mSettings->writeConfig();
488 q->setResourceId( defaultResourceId( mSettings ) );
489 q->ResourceScanJob::doStart();
497 d( new DefaultResourceJobPrivate( settings, this ) )
508 d->mDefaultResourceType = type;
513 d->mDefaultResourceOptions = options;
518 d->mKnownTypes = types;
523 d->mNameForTypeMap = map;
528 d->mIconForTypeMap = map;
533 d->tryFetchResource();
536 void DefaultResourceJob::slotResult( KJob *job )
538 if ( job->error() ) {
539 kWarning() << job->errorText();
541 if ( !d->mResourceWasPreexisting ) {
545 kDebug() <<
"Removing resource" << resource.
identifier();
550 Job::slotResult( job );
555 class Akonadi::GetLockJob::Private
561 void serviceOwnerChanged(
const QString &name,
const QString &oldOwner,
562 const QString &newOwner );
566 QTimer *mSafetyTimer;
569 GetLockJob::Private::Private(
GetLockJob *qq )
575 void GetLockJob::Private::doStart()
580 QDBusConnection bus = DBusConnectionPool::threadConnection();
581 const bool alreadyLocked = bus.interface()->isServiceRegistered( dbusServiceName() );
582 const bool gotIt = bus.registerService( dbusServiceName() );
584 if ( gotIt && !alreadyLocked ) {
588 QDBusServiceWatcher *watcher =
new QDBusServiceWatcher( dbusServiceName(), DBusConnectionPool::threadConnection(),
589 QDBusServiceWatcher::WatchForOwnerChange, q );
591 connect( watcher, SIGNAL(serviceOwnerChanged(QString,QString,QString)),
592 q, SLOT(serviceOwnerChanged(QString,QString,QString)) );
594 mSafetyTimer =
new QTimer( q );
595 mSafetyTimer->setSingleShot(
true );
596 mSafetyTimer->setInterval( LOCK_WAIT_TIMEOUT_SECONDS * 1000 );
597 mSafetyTimer->start();
598 connect( mSafetyTimer, SIGNAL(timeout()), q, SLOT(timeout()) );
602 void GetLockJob::Private::serviceOwnerChanged(
const QString&,
const QString&,
const QString &newOwner )
604 if ( newOwner.isEmpty() ) {
605 const bool gotIt = DBusConnectionPool::threadConnection().registerService( dbusServiceName() );
607 mSafetyTimer->stop();
613 void GetLockJob::Private::timeout()
615 kWarning() <<
"Timeout trying to get lock. Check who has acquired the name" << dbusServiceName() <<
"on DBus, using qdbus or qdbusviewer.";
617 q->setErrorText( i18n(
"Timeout trying to get lock." ) );
624 d( new Private( this ) )
633 void GetLockJob::start()
635 QTimer::singleShot( 0,
this, SLOT(doStart()) );
639 const QMap<QByteArray, QString> &nameForType,
640 const QMap<QByteArray, QString> &iconForType )
658 return DBusConnectionPool::threadConnection().unregisterService( dbusServiceName() );
661 #include "moc_specialcollectionshelperjobs_p.cpp"