specialcollectionshelperjobs.cpp 21.2 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
/*
    Copyright (c) 2009 Constantin Berzan <exit3219@gmail.com>

    This library is free software; you can redistribute it and/or modify it
    under the terms of the GNU Library General Public License as published by
    the Free Software Foundation; either version 2 of the License, or (at your
    option) any later version.

    This library is distributed in the hope that it will be useful, but WITHOUT
    ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
    FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Library General Public
    License for more details.

    You should have received a copy of the GNU Library General Public License
    along with this library; see the file COPYING.LIB.  If not, write to the
    Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
    02110-1301, USA.
*/

#include "specialcollectionshelperjobs_p.h"

22
#include <QDBusConnection>
23
#include "specialcollectionattribute.h"
24
#include "specialcollections.h"
25
#include "servermanager.h"
26

27

Daniel Vrátil's avatar
Daniel Vrátil committed
28 29 30 31 32 33 34 35
#include "agentinstance.h"
#include "agentinstancecreatejob.h"
#include "agentmanager.h"
#include "collectionfetchjob.h"
#include "collectionfetchscope.h"
#include "collectionmodifyjob.h"
#include "entitydisplayattribute.h"
#include "resourcesynchronizationjob.h"
36

37 38
#include "akonadicore_debug.h"

39
#include <KLocalizedString>
40
#include <KCoreConfigSkeleton>
41

Daniel Vrátil's avatar
Daniel Vrátil committed
42 43 44 45 46 47
#include <QDBusConnectionInterface>
#include <QDBusInterface>
#include <QDBusServiceWatcher>
#include <QMetaMethod>
#include <QTime>
#include <QTimer>
48

49
#define LOCK_WAIT_TIMEOUT_SECONDS 30
50 51 52 53

using namespace Akonadi;

// convenient methods to get/set the default resource id
Guy Maurel's avatar
Guy Maurel committed
54
static void setDefaultResourceId(KCoreConfigSkeleton *settings, const QString &value)
55
{
56
    KConfigSkeletonItem *item = settings->findItem(QStringLiteral("DefaultResourceId"));
Guy Maurel's avatar
Guy Maurel committed
57 58
    Q_ASSERT(item);
    item->setProperty(value);
59 60
}

Guy Maurel's avatar
Guy Maurel committed
61
static QString defaultResourceId(KCoreConfigSkeleton *settings)
62
{
63
    const KConfigSkeletonItem *item = settings->findItem(QStringLiteral("DefaultResourceId"));
Guy Maurel's avatar
Guy Maurel committed
64 65
    Q_ASSERT(item);
    return item->property().toString();
66 67
}

68 69
static QString dbusServiceName()
{
70
    QString service = QStringLiteral("org.kde.pim.SpecialCollections");
Guy Maurel's avatar
Guy Maurel committed
71 72 73 74
    if (ServerManager::hasInstanceIdentifier()) {
        return service + ServerManager::instanceIdentifier();
    }
    return service;
75 76
}

Guy Maurel's avatar
Guy Maurel committed
77
static QVariant::Type argumentType(const QMetaObject *mo, const QString &method)
78
{
Guy Maurel's avatar
Guy Maurel committed
79 80
    QMetaMethod m;
    for (int i = 0; i < mo->methodCount(); ++i) {
Laurent Montel's avatar
Laurent Montel committed
81
        const QString signature = QString::fromLatin1(mo->method(i).methodSignature());
Guy Maurel's avatar
Guy Maurel committed
82 83 84 85
        if (signature.startsWith(method)) {
            m = mo->method(i);
        }
    }
86

Daniel Vrátil's avatar
Daniel Vrátil committed
87
    if (m.methodSignature().isEmpty()) {
Guy Maurel's avatar
Guy Maurel committed
88 89
        return QVariant::Invalid;
    }
90

Guy Maurel's avatar
Guy Maurel committed
91 92 93 94
    const QList<QByteArray> argTypes = m.parameterTypes();
    if (argTypes.count() != 1) {
        return QVariant::Invalid;
    }
95

Daniel Vrátil's avatar
Daniel Vrátil committed
96
    return QVariant::nameToType(argTypes.first().constData());
97 98 99 100 101 102 103
}

// ===================== ResourceScanJob ============================

/**
  @internal
*/
104
class Q_DECL_HIDDEN Akonadi::ResourceScanJob::Private
105
{
Guy Maurel's avatar
Guy Maurel committed
106 107
public:
    Private(KCoreConfigSkeleton *settings, ResourceScanJob *qq);
108

Guy Maurel's avatar
Guy Maurel committed
109
    void fetchResult(KJob *job);   // slot
110 111 112 113 114

    ResourceScanJob *const q;

    // Input:
    QString mResourceId;
Laurent Montel's avatar
Laurent Montel committed
115
    KCoreConfigSkeleton *mSettings = nullptr;
116 117 118 119 120 121

    // Output:
    Collection mRootCollection;
    Collection::List mSpecialCollections;
};

Guy Maurel's avatar
Guy Maurel committed
122 123 124
ResourceScanJob::Private::Private(KCoreConfigSkeleton *settings, ResourceScanJob *qq)
    : q(qq)
    , mSettings(settings)
125 126 127
{
}

Guy Maurel's avatar
Guy Maurel committed
128
void ResourceScanJob::Private::fetchResult(KJob *job)
129
{
Guy Maurel's avatar
Guy Maurel committed
130
    if (job->error()) {
131
        qCWarning(AKONADICORE_LOG) << job->errorText();
Guy Maurel's avatar
Guy Maurel committed
132 133
        return;
    }
134

Guy Maurel's avatar
Guy Maurel committed
135 136 137 138 139
    CollectionFetchJob *fetchJob = qobject_cast<CollectionFetchJob *>(job);
    Q_ASSERT(fetchJob);

    Q_ASSERT(!mRootCollection.isValid());
    Q_ASSERT(mSpecialCollections.isEmpty());
Laurent Montel's avatar
Laurent Montel committed
140 141
    const Akonadi::Collection::List lstCols = fetchJob->collections();
    for (const Collection &collection : lstCols) {
Guy Maurel's avatar
Guy Maurel committed
142 143
        if (collection.parentCollection() == Collection::root()) {
            if (mRootCollection.isValid()) {
144
                qCWarning(AKONADICORE_LOG) << "Resource has more than one root collection. I don't know what to do.";
Guy Maurel's avatar
Guy Maurel committed
145 146 147 148
            } else {
                mRootCollection = collection;
            }
        }
149

Guy Maurel's avatar
Guy Maurel committed
150 151 152
        if (collection.hasAttribute<SpecialCollectionAttribute>()) {
            mSpecialCollections.append(collection);
        }
153 154
    }

155 156 157
    qCDebug(AKONADICORE_LOG) << "Fetched root collection" << mRootCollection.id()
                             << "and" << mSpecialCollections.count() << "local folders"
                             << "(total" << fetchJob->collections().count() << "collections).";
158

Guy Maurel's avatar
Guy Maurel committed
159 160 161 162 163 164
    if (!mRootCollection.isValid()) {
        q->setError(Unknown);
        q->setErrorText(i18n("Could not fetch root collection of resource %1.", mResourceId));
        q->emitResult();
        return;
    }
165

Guy Maurel's avatar
Guy Maurel committed
166
    // We are done!
167
    q->emitResult();
168 169
}

Guy Maurel's avatar
Guy Maurel committed
170 171 172
ResourceScanJob::ResourceScanJob(const QString &resourceId, KCoreConfigSkeleton *settings, QObject *parent)
    : Job(parent)
    , d(new Private(settings, this))
173
{
Guy Maurel's avatar
Guy Maurel committed
174
    setResourceId(resourceId);
175 176 177 178
}

ResourceScanJob::~ResourceScanJob()
{
Guy Maurel's avatar
Guy Maurel committed
179
    delete d;
180 181 182 183
}

QString ResourceScanJob::resourceId() const
{
Guy Maurel's avatar
Guy Maurel committed
184
    return d->mResourceId;
185 186
}

Guy Maurel's avatar
Guy Maurel committed
187
void ResourceScanJob::setResourceId(const QString &resourceId)
188
{
Guy Maurel's avatar
Guy Maurel committed
189
    d->mResourceId = resourceId;
190 191
}

Dirk Mueller's avatar
Dirk Mueller committed
192
Akonadi::Collection ResourceScanJob::rootResourceCollection() const
193
{
Guy Maurel's avatar
Guy Maurel committed
194
    return d->mRootCollection;
195 196
}

Dirk Mueller's avatar
Dirk Mueller committed
197
Akonadi::Collection::List ResourceScanJob::specialCollections() const
198
{
Guy Maurel's avatar
Guy Maurel committed
199
    return d->mSpecialCollections;
200 201 202 203
}

void ResourceScanJob::doStart()
{
Guy Maurel's avatar
Guy Maurel committed
204
    if (d->mResourceId.isEmpty()) {
Laurent Montel's avatar
Laurent Montel committed
205
        if (!qobject_cast<DefaultResourceJob *>(this)) {
206
            qCCritical(AKONADICORE_LOG) << "No resource ID given.";
207 208 209
            setError(Job::Unknown);
            setErrorText(i18n("No resource ID given."));
        }
Guy Maurel's avatar
Guy Maurel committed
210 211 212
        emitResult();
        return;
    }
213

Guy Maurel's avatar
Guy Maurel committed
214
    CollectionFetchJob *fetchJob = new CollectionFetchJob(Collection::root(),
Laurent Montel's avatar
Laurent Montel committed
215
            CollectionFetchJob::Recursive, this);
Guy Maurel's avatar
Guy Maurel committed
216 217
    fetchJob->fetchScope().setResource(d->mResourceId);
    fetchJob->fetchScope().setIncludeStatistics(true);
218
    fetchJob->fetchScope().setListFilter(CollectionFetchScope::Display);
Laurent Montel's avatar
Laurent Montel committed
219
    connect(fetchJob, &CollectionFetchJob::result, this, [this](KJob *job) { d->fetchResult(job); });
220 221 222 223 224 225 226 227 228
}

// ===================== DefaultResourceJob ============================

/**
  @internal
*/
class Akonadi::DefaultResourceJobPrivate
{
Guy Maurel's avatar
Guy Maurel committed
229 230
public:
    DefaultResourceJobPrivate(KCoreConfigSkeleton *settings, DefaultResourceJob *qq);
231 232

    void tryFetchResource();
Guy Maurel's avatar
Guy Maurel committed
233 234 235 236
    void resourceCreateResult(KJob *job);   // slot
    void resourceSyncResult(KJob *job);   // slot
    void collectionFetchResult(KJob *job);   // slot
    void collectionModifyResult(KJob *job);   // slot
237 238

    DefaultResourceJob *const q;
Laurent Montel's avatar
Laurent Montel committed
239
    KCoreConfigSkeleton *mSettings = nullptr;
240 241 242 243
    QVariantMap mDefaultResourceOptions;
    QList<QByteArray> mKnownTypes;
    QMap<QByteArray, QString> mNameForTypeMap;
    QMap<QByteArray, QString> mIconForTypeMap;
244 245 246
    QString mDefaultResourceType;
    int mPendingModifyJobs = 0;
    bool mResourceWasPreexisting = true;
247 248
};

Guy Maurel's avatar
Guy Maurel committed
249 250 251 252
DefaultResourceJobPrivate::DefaultResourceJobPrivate(KCoreConfigSkeleton *settings, DefaultResourceJob *qq)
    : q(qq)
    , mSettings(settings)
    , mPendingModifyJobs(0)
253
    , mResourceWasPreexisting(true /* for safety, so as not to accidentally delete data */)
254 255 256 257 258
{
}

void DefaultResourceJobPrivate::tryFetchResource()
{
Guy Maurel's avatar
Guy Maurel committed
259
    // Get the resourceId from config. Another instance might have changed it in the meantime.
Laurent Montel's avatar
Laurent Montel committed
260
    mSettings->load();
Guy Maurel's avatar
Guy Maurel committed
261 262 263

    const QString resourceId = defaultResourceId(mSettings);

264
    qCDebug(AKONADICORE_LOG) << "Read defaultResourceId" << resourceId << "from config.";
Guy Maurel's avatar
Guy Maurel committed
265 266 267 268 269

    const AgentInstance resource = AgentManager::self()->instance(resourceId);
    if (resource.isValid()) {
        // The resource exists; scan it.
        mResourceWasPreexisting = true;
270
        qCDebug(AKONADICORE_LOG) << "Found resource" << resourceId;
Guy Maurel's avatar
Guy Maurel committed
271 272 273 274 275
        q->setResourceId(resourceId);

        CollectionFetchJob *fetchJob = new CollectionFetchJob(Collection::root(), CollectionFetchJob::Recursive, q);
        fetchJob->fetchScope().setResource(resourceId);
        fetchJob->fetchScope().setIncludeStatistics(true);
Laurent Montel's avatar
Laurent Montel committed
276
        q->connect(fetchJob, &CollectionFetchJob::result, q, [this](KJob *job) { collectionFetchResult(job); });
Guy Maurel's avatar
Guy Maurel committed
277 278 279 280 281
    } else {
        // Try harder: maybe the default resource has been removed and another one added
        //             without updating the config file, in this case search for a resource
        //             of the same type and the default name
        const AgentInstance::List resources = AgentManager::self()->instances();
Laurent Montel's avatar
Laurent Montel committed
282
        for (const AgentInstance &resource : resources) {
Guy Maurel's avatar
Guy Maurel committed
283
            if (resource.type().identifier() == mDefaultResourceType) {
284
                if (resource.name() == mDefaultResourceOptions.value(QStringLiteral("Name")).toString()) {
Guy Maurel's avatar
Guy Maurel committed
285 286
                    // found a matching one...
                    setDefaultResourceId(mSettings, resource.identifier());
Laurent Montel's avatar
Laurent Montel committed
287
                    mSettings->save();
Guy Maurel's avatar
Guy Maurel committed
288
                    mResourceWasPreexisting = true;
289
                    qCDebug(AKONADICORE_LOG) << "Found resource" << resource.identifier();
Guy Maurel's avatar
Guy Maurel committed
290 291 292 293 294
                    q->setResourceId(resource.identifier());
                    q->ResourceScanJob::doStart();
                    return;
                }
            }
295 296
        }

Guy Maurel's avatar
Guy Maurel committed
297 298
        // Create the resource.
        mResourceWasPreexisting = false;
299
        qCDebug(AKONADICORE_LOG) << "Creating maildir resource.";
Guy Maurel's avatar
Guy Maurel committed
300 301
        const AgentType type = AgentManager::self()->type(mDefaultResourceType);
        AgentInstanceCreateJob *job = new AgentInstanceCreateJob(type, q);
Laurent Montel's avatar
Laurent Montel committed
302
        QObject::connect(job, &AgentInstanceCreateJob::result, q, [this](KJob *job) { resourceCreateResult(job); });
Guy Maurel's avatar
Guy Maurel committed
303 304
        job->start(); // non-Akonadi::Job
    }
305 306
}

Guy Maurel's avatar
Guy Maurel committed
307
void DefaultResourceJobPrivate::resourceCreateResult(KJob *job)
308
{
Guy Maurel's avatar
Guy Maurel committed
309
    if (job->error()) {
310
        qCWarning(AKONADICORE_LOG) << job->errorText();
Guy Maurel's avatar
Guy Maurel committed
311 312 313 314 315
        //fail( i18n( "Failed to create the default resource (%1).", job->errorString() ) );
        q->setError(job->error());
        q->setErrorText(job->errorText());
        q->emitResult();
        return;
Volker Krause's avatar
Volker Krause committed
316
    }
317

Guy Maurel's avatar
Guy Maurel committed
318
    AgentInstance agent;
319

Guy Maurel's avatar
Guy Maurel committed
320 321 322 323 324 325
    // Get the resource instance.
    {
        AgentInstanceCreateJob *createJob = qobject_cast<AgentInstanceCreateJob *>(job);
        Q_ASSERT(createJob);
        agent = createJob->instance();
        setDefaultResourceId(mSettings, agent.identifier());
326
        qCDebug(AKONADICORE_LOG) << "Created maildir resource with id" << defaultResourceId(mSettings);
Guy Maurel's avatar
Guy Maurel committed
327
    }
328

Guy Maurel's avatar
Guy Maurel committed
329
    const QString defaultId = defaultResourceId(mSettings);
330

Guy Maurel's avatar
Guy Maurel committed
331 332
    // Configure the resource.
    {
333
        agent.setName(mDefaultResourceOptions.value(QStringLiteral("Name")).toString());
334

335 336
        const auto service = ServerManager::agentServiceName(ServerManager::Resource, defaultId);
        QDBusInterface conf(service, QStringLiteral("/Settings"), QString());
Guy Maurel's avatar
Guy Maurel committed
337 338 339 340 341 342 343 344

        if (!conf.isValid()) {
            q->setError(-1);
            q->setErrorText(i18n("Invalid resource identifier '%1'", defaultId));
            q->emitResult();
            return;
        }

Laurent Montel's avatar
Laurent Montel committed
345 346 347
        QMap<QString, QVariant>::const_iterator it = mDefaultResourceOptions.cbegin();
        const QMap<QString, QVariant>::const_iterator itEnd = mDefaultResourceOptions.cend();
        for (;it != itEnd; ++it) {
Guy Maurel's avatar
Guy Maurel committed
348

349
            if (it.key() == QLatin1String("Name")) {
Guy Maurel's avatar
Guy Maurel committed
350 351 352
                continue;
            }

353
            const QString methodName = QStringLiteral("set%1").arg(it.key());
Guy Maurel's avatar
Guy Maurel committed
354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369
            const QVariant::Type argType = argumentType(conf.metaObject(), methodName);
            if (argType == QVariant::Invalid) {
                q->setError(Job::Unknown);
                q->setErrorText(i18n("Failed to configure default resource via D-Bus."));
                q->emitResult();
                return;
            }

            QDBusReply<void> reply = conf.call(methodName, it.value());
            if (!reply.isValid()) {
                q->setError(Job::Unknown);
                q->setErrorText(i18n("Failed to configure default resource via D-Bus."));
                q->emitResult();
                return;
            }
        }
370

371
        conf.call(QStringLiteral("save"));
372

Guy Maurel's avatar
Guy Maurel committed
373 374 375 376 377 378
        agent.reconfigure();
    }

    // Sync the resource.
    {
        ResourceSynchronizationJob *syncJob = new ResourceSynchronizationJob(agent, q);
Laurent Montel's avatar
Laurent Montel committed
379
        QObject::connect(syncJob, &ResourceSynchronizationJob::result, q, [this](KJob *job) { resourceSyncResult(job); });
Guy Maurel's avatar
Guy Maurel committed
380 381
        syncJob->start(); // non-Akonadi
    }
382 383
}

Guy Maurel's avatar
Guy Maurel committed
384
void DefaultResourceJobPrivate::resourceSyncResult(KJob *job)
385
{
Guy Maurel's avatar
Guy Maurel committed
386
    if (job->error()) {
387
        qCWarning(AKONADICORE_LOG) << job->errorText();
Guy Maurel's avatar
Guy Maurel committed
388 389 390
        //fail( i18n( "ResourceSynchronizationJob failed (%1).", job->errorString() ) );
        return;
    }
391

Guy Maurel's avatar
Guy Maurel committed
392
    // Fetch the collections of the resource.
393
    qCDebug(AKONADICORE_LOG) << "Fetching maildir collections.";
Guy Maurel's avatar
Guy Maurel committed
394 395
    CollectionFetchJob *fetchJob = new CollectionFetchJob(Collection::root(), CollectionFetchJob::Recursive, q);
    fetchJob->fetchScope().setResource(defaultResourceId(mSettings));
Laurent Montel's avatar
Laurent Montel committed
396
    QObject::connect(fetchJob, &CollectionFetchJob::result, q, [this](KJob *job) { collectionFetchResult(job); });
397 398
}

Guy Maurel's avatar
Guy Maurel committed
399
void DefaultResourceJobPrivate::collectionFetchResult(KJob *job)
400
{
Guy Maurel's avatar
Guy Maurel committed
401
    if (job->error()) {
402
        qCWarning(AKONADICORE_LOG) << job->errorText();
Guy Maurel's avatar
Guy Maurel committed
403 404 405
        //fail( i18n( "Failed to fetch the root maildir collection (%1).", job->errorString() ) );
        return;
    }
406

Guy Maurel's avatar
Guy Maurel committed
407 408
    CollectionFetchJob *fetchJob = qobject_cast<CollectionFetchJob *>(job);
    Q_ASSERT(fetchJob);
409

Guy Maurel's avatar
Guy Maurel committed
410
    const Collection::List collections = fetchJob->collections();
411
    qCDebug(AKONADICORE_LOG) << "Fetched" << collections.count() << "collections.";
412

Guy Maurel's avatar
Guy Maurel committed
413 414 415
    // Find the root maildir collection.
    Collection::List toRecover;
    Collection resourceCollection;
Laurent Montel's avatar
Laurent Montel committed
416
    for (const Collection &collection : collections) {
Guy Maurel's avatar
Guy Maurel committed
417 418 419 420 421
        if (collection.parentCollection() == Collection::root()) {
            resourceCollection = collection;
            toRecover.append(collection);
            break;
        }
422 423
    }

Guy Maurel's avatar
Guy Maurel committed
424 425 426 427 428 429
    if (!resourceCollection.isValid()) {
        q->setError(Job::Unknown);
        q->setErrorText(i18n("Failed to fetch the resource collection."));
        q->emitResult();
        return;
    }
430

Guy Maurel's avatar
Guy Maurel committed
431
    // Find all children of the resource collection.
Laurent Montel's avatar
Laurent Montel committed
432
    for (const Collection &collection : qAsConst(collections)) {
Guy Maurel's avatar
Guy Maurel committed
433 434 435
        if (collection.parentCollection() == resourceCollection) {
            toRecover.append(collection);
        }
436 437
    }

Guy Maurel's avatar
Guy Maurel committed
438
    QHash<QString, QByteArray> typeForName;
Laurent Montel's avatar
Laurent Montel committed
439
    for (const QByteArray &type : qAsConst(mKnownTypes)) {
Guy Maurel's avatar
Guy Maurel committed
440 441 442
        const QString displayName = mNameForTypeMap.value(type);
        typeForName[displayName] = type;
    }
443

Guy Maurel's avatar
Guy Maurel committed
444 445 446
    // These collections have been created by the maildir resource, when it
    // found the folders on disk. So give them the necessary attributes now.
    Q_ASSERT(mPendingModifyJobs == 0);
Laurent Montel's avatar
Laurent Montel committed
447
    for (Collection collection : qAsConst(toRecover)) {             // krazy:exclude=foreach
448

Guy Maurel's avatar
Guy Maurel committed
449 450 451
        if (collection.hasAttribute<SpecialCollectionAttribute>()) {
            continue;
        }
452

Guy Maurel's avatar
Guy Maurel committed
453 454 455
        // Find the type for the collection.
        const QString name = collection.displayName();
        const QByteArray type = typeForName.value(name);
456

Guy Maurel's avatar
Guy Maurel committed
457
        if (!type.isEmpty()) {
458
            qCDebug(AKONADICORE_LOG) << "Recovering collection" << name;
Guy Maurel's avatar
Guy Maurel committed
459
            setCollectionAttributes(collection, type, mNameForTypeMap, mIconForTypeMap);
460

Guy Maurel's avatar
Guy Maurel committed
461
            CollectionModifyJob *modifyJob = new CollectionModifyJob(collection, q);
Laurent Montel's avatar
Laurent Montel committed
462
            QObject::connect(modifyJob, &CollectionModifyJob::result, q, [this](KJob *job) {collectionModifyResult(job); });
Guy Maurel's avatar
Guy Maurel committed
463 464
            mPendingModifyJobs++;
        } else {
465 466
            qCDebug(AKONADICORE_LOG) << "Searching for names: " << typeForName.keys();
            qCDebug(AKONADICORE_LOG) << "Unknown collection name" << name << "-- not recovering.";
Guy Maurel's avatar
Guy Maurel committed
467
        }
468
    }
Volker Krause's avatar
Volker Krause committed
469

Guy Maurel's avatar
Guy Maurel committed
470 471 472 473 474
    if (mPendingModifyJobs == 0) {
        // Scan the resource.
        q->setResourceId(defaultResourceId(mSettings));
        q->ResourceScanJob::doStart();
    }
475 476
}

Guy Maurel's avatar
Guy Maurel committed
477
void DefaultResourceJobPrivate::collectionModifyResult(KJob *job)
478
{
Guy Maurel's avatar
Guy Maurel committed
479
    if (job->error()) {
480
        qCWarning(AKONADICORE_LOG) << job->errorText();
Guy Maurel's avatar
Guy Maurel committed
481 482 483
        //fail( i18n( "Failed to modify the root maildir collection (%1).", job->errorString() ) );
        return;
    }
484

Guy Maurel's avatar
Guy Maurel committed
485 486
    Q_ASSERT(mPendingModifyJobs > 0);
    mPendingModifyJobs--;
487
    qCDebug(AKONADICORE_LOG) << "pendingModifyJobs now" << mPendingModifyJobs;
Guy Maurel's avatar
Guy Maurel committed
488 489
    if (mPendingModifyJobs == 0) {
        // Write the updated config.
490
        qCDebug(AKONADICORE_LOG) << "Writing defaultResourceId" << defaultResourceId(mSettings) << "to config.";
Laurent Montel's avatar
Laurent Montel committed
491
        mSettings->save();
Guy Maurel's avatar
Guy Maurel committed
492 493 494 495 496

        // Scan the resource.
        q->setResourceId(defaultResourceId(mSettings));
        q->ResourceScanJob::doStart();
    }
497 498
}

Guy Maurel's avatar
Guy Maurel committed
499 500 501
DefaultResourceJob::DefaultResourceJob(KCoreConfigSkeleton *settings, QObject *parent)
    : ResourceScanJob(QString(), settings, parent)
    , d(new DefaultResourceJobPrivate(settings, this))
502 503 504 505 506
{
}

DefaultResourceJob::~DefaultResourceJob()
{
Guy Maurel's avatar
Guy Maurel committed
507
    delete d;
508 509
}

Guy Maurel's avatar
Guy Maurel committed
510
void DefaultResourceJob::setDefaultResourceType(const QString &type)
511
{
Guy Maurel's avatar
Guy Maurel committed
512
    d->mDefaultResourceType = type;
513 514
}

Guy Maurel's avatar
Guy Maurel committed
515
void DefaultResourceJob::setDefaultResourceOptions(const QVariantMap &options)
516
{
Guy Maurel's avatar
Guy Maurel committed
517
    d->mDefaultResourceOptions = options;
518 519
}

Guy Maurel's avatar
Guy Maurel committed
520
void DefaultResourceJob::setTypes(const QList<QByteArray> &types)
521
{
Guy Maurel's avatar
Guy Maurel committed
522
    d->mKnownTypes = types;
523 524
}

Guy Maurel's avatar
Guy Maurel committed
525
void DefaultResourceJob::setNameForTypeMap(const QMap<QByteArray, QString> &map)
526
{
Guy Maurel's avatar
Guy Maurel committed
527
    d->mNameForTypeMap = map;
528 529
}

Guy Maurel's avatar
Guy Maurel committed
530
void DefaultResourceJob::setIconForTypeMap(const QMap<QByteArray, QString> &map)
531
{
Guy Maurel's avatar
Guy Maurel committed
532
    d->mIconForTypeMap = map;
533 534 535 536
}

void DefaultResourceJob::doStart()
{
Guy Maurel's avatar
Guy Maurel committed
537
    d->tryFetchResource();
538 539
}

Guy Maurel's avatar
Guy Maurel committed
540
void DefaultResourceJob::slotResult(KJob *job)
541
{
Guy Maurel's avatar
Guy Maurel committed
542
    if (job->error()) {
543
        qCWarning(AKONADICORE_LOG) << job->errorText();
Guy Maurel's avatar
Guy Maurel committed
544 545 546 547 548
        // Do some cleanup.
        if (!d->mResourceWasPreexisting) {
            // We only removed the resource instance if we have created it.
            // Otherwise we might lose the user's data.
            const AgentInstance resource = AgentManager::self()->instance(defaultResourceId(d->mSettings));
549
            qCDebug(AKONADICORE_LOG) << "Removing resource" << resource.identifier();
Guy Maurel's avatar
Guy Maurel committed
550 551
            AgentManager::self()->removeInstance(resource);
        }
552 553
    }

Guy Maurel's avatar
Guy Maurel committed
554
    Job::slotResult(job);
555 556 557 558
}

// ===================== GetLockJob ============================

559
class Q_DECL_HIDDEN Akonadi::GetLockJob::Private
560
{
Guy Maurel's avatar
Guy Maurel committed
561 562
public:
    Private(GetLockJob *qq);
563 564 565 566 567

    void doStart(); // slot
    void timeout(); // slot

    GetLockJob *const q;
Laurent Montel's avatar
Laurent Montel committed
568
    QTimer *mSafetyTimer = nullptr;
569 570
};

Guy Maurel's avatar
Guy Maurel committed
571 572
GetLockJob::Private::Private(GetLockJob *qq)
    : q(qq)
Laurent Montel's avatar
Laurent Montel committed
573
    , mSafetyTimer(nullptr)
574 575 576 577 578
{
}

void GetLockJob::Private::doStart()
{
Guy Maurel's avatar
Guy Maurel committed
579 580
    // Just doing registerService() and checking its return value is not sufficient,
    // since we may *already* own the name, and then registerService() returns true.
581

582
    QDBusConnection bus = QDBusConnection::sessionBus();
Guy Maurel's avatar
Guy Maurel committed
583 584
    const bool alreadyLocked = bus.interface()->isServiceRegistered(dbusServiceName());
    const bool gotIt = bus.registerService(dbusServiceName());
585

Guy Maurel's avatar
Guy Maurel committed
586
    if (gotIt && !alreadyLocked) {
587
        //qCDebug(AKONADICORE_LOG) << "Got lock immediately.";
Guy Maurel's avatar
Guy Maurel committed
588 589
        q->emitResult();
    } else {
590
        auto watcher = new QDBusServiceWatcher(dbusServiceName(), QDBusConnection::sessionBus(),
591 592 593
                QDBusServiceWatcher::WatchForUnregistration, q);
        connect(watcher, &QDBusServiceWatcher::serviceUnregistered,
                q, [this]() {
594
                    if (QDBusConnection::sessionBus().registerService(dbusServiceName())) {
595 596 597 598
                        mSafetyTimer->stop();
                        q->emitResult();
                    }
                });
Guy Maurel's avatar
Guy Maurel committed
599 600 601 602 603

        mSafetyTimer = new QTimer(q);
        mSafetyTimer->setSingleShot(true);
        mSafetyTimer->setInterval(LOCK_WAIT_TIMEOUT_SECONDS * 1000);
        mSafetyTimer->start();
Laurent Montel's avatar
Laurent Montel committed
604
        connect(mSafetyTimer, &QTimer::timeout, q, [this]() { timeout(); });
Guy Maurel's avatar
Guy Maurel committed
605 606 607
    }
}

608 609
void GetLockJob::Private::timeout()
{
610
    qCWarning(AKONADICORE_LOG) << "Timeout trying to get lock. Check who has acquired the name" << dbusServiceName() << "on DBus, using qdbus or qdbusviewer.";
Guy Maurel's avatar
Guy Maurel committed
611 612 613
    q->setError(Job::Unknown);
    q->setErrorText(i18n("Timeout trying to get lock."));
    q->emitResult();
614 615
}

Guy Maurel's avatar
Guy Maurel committed
616 617 618
GetLockJob::GetLockJob(QObject *parent)
    : KJob(parent)
    , d(new Private(this))
619 620 621 622 623
{
}

GetLockJob::~GetLockJob()
{
Guy Maurel's avatar
Guy Maurel committed
624
    delete d;
625 626 627 628
}

void GetLockJob::start()
{
Laurent Montel's avatar
Laurent Montel committed
629
    QTimer::singleShot(0, this, [this]() { d->doStart(); });
630 631
}

Guy Maurel's avatar
Guy Maurel committed
632 633 634
void Akonadi::setCollectionAttributes(Akonadi::Collection &collection, const QByteArray &type,
                                      const QMap<QByteArray, QString> &nameForType,
                                      const QMap<QByteArray, QString> &iconForType)
635
{
Guy Maurel's avatar
Guy Maurel committed
636 637 638 639 640 641
    {
        EntityDisplayAttribute *attr = new EntityDisplayAttribute;
        attr->setIconName(iconForType.value(type));
        attr->setDisplayName(nameForType.value(type));
        collection.addAttribute(attr);
    }
642

Guy Maurel's avatar
Guy Maurel committed
643 644 645 646 647
    {
        SpecialCollectionAttribute *attr = new SpecialCollectionAttribute;
        attr->setCollectionType(type);
        collection.addAttribute(attr);
    }
648 649 650 651
}

bool Akonadi::releaseLock()
{
652
    return QDBusConnection::sessionBus().unregisterService(dbusServiceName());
653 654
}

655
#include "moc_specialcollectionshelperjobs_p.cpp"