contacthandler.cpp 13.8 KB
Newer Older
1
/*
2 3 4
 * SPDX-FileCopyrightText: 2020 Shashwat Jolly <shashwat.jolly@gmail.com>
 * 
 * SPDX-License-Identifier: GPL-2.0-or-later
5 6 7 8 9 10 11
 */

#include "contacthandler.h"

#include <kcontacts/vcardconverter.h>

#include <AkonadiCore/CollectionModifyJob>
12
#include <AkonadiCore/ItemModifyJob>
13
#include <KLocalizedString>
14 15 16 17 18 19 20 21
#include <QFile>

#include "entriesfetchjob.h"
#include "etesync_debug.h"
#include "etesyncresource.h"

using namespace Akonadi;

22
ContactHandler::ContactHandler(EteSyncResource *resource) : BaseHandler(resource)
23
{
24
    initialiseBaseDirectory();
25 26
}

27 28 29 30 31
const QString ContactHandler::mimeType()
{
    return KContacts::Addressee::mimeType();
}

32
const QString ContactHandler::etesyncCollectionType()
33 34 35 36
{
    return QStringLiteral(ETESYNC_COLLECTION_TYPE_ADDRESS_BOOK);
}

37
void ContactHandler::getItemListFromEntries(std::vector<EteSyncEntryPtr> &entries, Item::List &changedItems, Item::List &removedItems, Collection &collection, const QString &journalUid, QString &prevUid)
38
{
39
    const EteSyncJournalPtr &journal = mResource->getJournal(journalUid);
Shashwat Jolly's avatar
Shashwat Jolly committed
40
    if (!journal) {
41
        qCDebug(ETESYNC_LOG) << "SetupItems: Could not get journal" << journalUid;
Shashwat Jolly's avatar
Shashwat Jolly committed
42 43 44
        mResource->cancelTask(i18n("Could not get journal"));
        return;
    }
45
    EteSyncCryptoManagerPtr cryptoManager = etesync_journal_get_crypto_manager(journal.get(), mClientState->derived(), mClientState->keypair());
46 47 48

    QMap<QString, KContacts::Addressee> contacts;

49
    for (auto &entry : entries) {
Shashwat Jolly's avatar
Shashwat Jolly committed
50 51 52 53 54
        if (!entry) {
            qCDebug(ETESYNC_LOG) << "SetupItems: Entry is null";
            prevUid = QStringFromCharPtr(CharPtr(etesync_entry_get_uid(entry.get())));
            continue;
        }
55
        EteSyncSyncEntryPtr syncEntry = etesync_entry_get_sync_entry(entry.get(), cryptoManager.get(), prevUid);
56

Shashwat Jolly's avatar
Shashwat Jolly committed
57 58
        if (!syncEntry) {
            qCDebug(ETESYNC_LOG) << "SetupItems: syncEntry is null for entry" << etesync_entry_get_uid(entry.get());
59
            qCDebug(ETESYNC_LOG) << "EteSync error" << QStringFromCharPtr(CharPtr(etesync_get_error_message()));
Shashwat Jolly's avatar
Shashwat Jolly committed
60 61 62 63
            prevUid = QStringFromCharPtr(CharPtr(etesync_entry_get_uid(entry.get())));
            continue;
        }

64 65 66 67 68
        KContacts::VCardConverter converter;
        CharPtr contentStr(etesync_sync_entry_get_content(syncEntry.get()));
        QByteArray content(contentStr.get());
        const KContacts::Addressee contact = converter.parseVCard(content);

69
        if ((contact.uid()).isEmpty()) {
Shashwat Jolly's avatar
Shashwat Jolly committed
70 71
            qCDebug(ETESYNC_LOG) << "Couldn't parse entry with uid" << etesync_entry_get_uid(entry.get());
            prevUid = QStringFromCharPtr(CharPtr(etesync_entry_get_uid(entry.get())));
72 73 74
            continue;
        }

Shashwat Jolly's avatar
Shashwat Jolly committed
75 76
        qCDebug(ETESYNC_LOG) << "Entry parsed into contact - UID" << contact.uid();

77 78 79 80 81 82 83 84
        const QString action = QStringFromCharPtr(CharPtr(etesync_sync_entry_get_action(syncEntry.get())));
        if (action == QStringLiteral(ETESYNC_SYNC_ENTRY_ACTION_ADD) || action == QStringLiteral(ETESYNC_SYNC_ENTRY_ACTION_CHANGE)) {
            contacts[contact.uid()] = contact;
        } else if (action == QStringLiteral(ETESYNC_SYNC_ENTRY_ACTION_DELETE)) {
            if (contacts.contains(contact.uid())) {
                contacts.remove(contact.uid());
            } else {
                Item item;
85
                item.setMimeType(mimeType());
86 87
                item.setParentCollection(collection);
                item.setRemoteId(contact.uid());
88
                removedItems.push_back(item);
89

90
                deleteLocalContact(contact.uid());
91 92 93 94 95 96 97 98
            }
        }

        prevUid = QStringFromCharPtr(CharPtr(etesync_entry_get_uid(entry.get())));
    }

    for (auto it = contacts.constBegin(); it != contacts.constEnd(); it++) {
        Item item;
99
        item.setMimeType(mimeType());
100 101 102
        item.setParentCollection(collection);
        item.setRemoteId(it.key());
        item.setPayload<KContacts::Addressee>(it.value());
103
        changedItems.push_back(item);
104 105 106 107 108 109 110 111 112 113

        updateLocalContact(it.value());
    }
}

QString ContactHandler::baseDirectoryPath() const
{
    return mResource->baseDirectoryPath() + QStringLiteral("/Contacts");
}

Shashwat Jolly's avatar
Shashwat Jolly committed
114
QString ContactHandler::getLocalContact(const QString &contactUid) const
115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139
{
    const QString path = baseDirectoryPath() + QLatin1Char('/') + contactUid + QLatin1String(".vcf");

    QFile file(path);
    if (!file.open(QFile::ReadOnly | QFile::Text)) {
        qCDebug(ETESYNC_LOG) << "Unable to read " << path << file.errorString();
        return QString();
    }
    QTextStream in(&file);
    return in.readAll();
}

void ContactHandler::updateLocalContact(const KContacts::Addressee &contact)
{
    const QString path = baseDirectoryPath() + QLatin1Char('/') + contact.uid() + QLatin1String(".vcf");

    QFile file(path);
    if (!file.open(QIODevice::WriteOnly)) {
        qCDebug(ETESYNC_LOG) << "Unable to open " << path << file.errorString();
        return;
    }
    KContacts::VCardConverter converter;
    file.write(converter.createVCard(contact));
}

140
void ContactHandler::deleteLocalContact(const QString &contactUid)
141
{
142
    const QString path = baseDirectoryPath() + QLatin1Char('/') + contactUid + QLatin1String(".vcf");
143 144 145 146 147 148 149 150 151 152
    QFile file(path);
    if (!file.remove()) {
        qCDebug(ETESYNC_LOG) << "Unable to remove " << path << file.errorString();
        return;
    }
}

void ContactHandler::itemAdded(const Akonadi::Item &item,
                               const Akonadi::Collection &collection)
{
153 154 155 156 157
    if (!item.hasPayload<KContacts::Addressee>()) {
        qCDebug(ETESYNC_LOG) << "Received item with unknown payload";
        mResource->cancelTask(i18n("Received item with unknown payload %1", item.mimeType()));
        return;
    }
Shashwat Jolly's avatar
Shashwat Jolly committed
158
    const QString journalUid = collection.remoteId();
159
    const EteSyncJournalPtr &journal = mResource->getJournal(journalUid);
Shashwat Jolly's avatar
Shashwat Jolly committed
160

161
    if (!journal) {
162
        qCDebug(ETESYNC_LOG) << "Could not get journal" << journalUid;
163 164 165 166
        mResource->cancelTask(i18n("Could not get journal"));
        return;
    }

167
    EteSyncCryptoManagerPtr cryptoManager = etesync_journal_get_crypto_manager(journal.get(), mClientState->derived(), mClientState->keypair());
168 169

    KContacts::VCardConverter converter;
170 171
    const auto contact = item.payload<KContacts::Addressee>();
    QByteArray content = converter.createVCard(contact);
172

Shashwat Jolly's avatar
Shashwat Jolly committed
173 174 175 176 177 178
    if (content.isEmpty()) {
        qCDebug(ETESYNC_LOG) << "Could not create vcard from payload";
        mResource->cancelTask(i18n("Could not create vcard from payload"));
        return;
    }

179
    EteSyncSyncEntryPtr syncEntry = etesync_sync_entry_new(QStringLiteral(ETESYNC_SYNC_ENTRY_ACTION_ADD), QString::fromUtf8(content));
180 181

    if (!createEteSyncEntry(syncEntry.get(), cryptoManager.get(), collection)) {
182 183
        qCDebug(ETESYNC_LOG) << "Could not create EteSync entry";
        mResource->cancelTask(i18n("Could not create EteSync entry"));
184 185
        return;
    }
186

187 188 189 190
    Item newItem(item);
    newItem.setRemoteId(contact.uid());
    newItem.setPayload<KContacts::Addressee>(contact);
    mResource->changeCommitted(newItem);
191 192 193 194 195 196 197

    updateLocalContact(item.payload<KContacts::Addressee>());
}

void ContactHandler::itemChanged(const Akonadi::Item &item,
                                 const QSet<QByteArray> &parts)
{
198 199
    Q_UNUSED(parts);

200 201 202 203 204
    if (!item.hasPayload<KContacts::Addressee>()) {
        qCDebug(ETESYNC_LOG) << "Received item with unknown payload";
        mResource->cancelTask(i18n("Received item with unknown payload %1", item.mimeType()));
        return;
    }
205 206
    Collection collection = item.parentCollection();

Shashwat Jolly's avatar
Shashwat Jolly committed
207
    const QString journalUid = collection.remoteId();
208
    const EteSyncJournalPtr &journal = mResource->getJournal(journalUid);
Shashwat Jolly's avatar
Shashwat Jolly committed
209

210
    if (!journal) {
211
        qCDebug(ETESYNC_LOG) << "Could not get journal" << journalUid;
212 213 214 215
        mResource->cancelTask(i18n("Could not get journal"));
        return;
    }

216
    EteSyncCryptoManagerPtr cryptoManager = etesync_journal_get_crypto_manager(journal.get(), mClientState->derived(), mClientState->keypair());
217 218

    KContacts::VCardConverter converter;
219 220
    const auto contact = item.payload<KContacts::Addressee>();
    QByteArray content = converter.createVCard(contact);
221

Shashwat Jolly's avatar
Shashwat Jolly committed
222 223 224 225 226 227
    if (content.isEmpty()) {
        qCDebug(ETESYNC_LOG) << "Could not create vcard from content";
        mResource->cancelTask(i18n("Could not create vcard from content"));
        return;
    }

228
    EteSyncSyncEntryPtr syncEntry = etesync_sync_entry_new(QStringLiteral(ETESYNC_SYNC_ENTRY_ACTION_CHANGE), QString::fromUtf8(content));
229 230

    if (!createEteSyncEntry(syncEntry.get(), cryptoManager.get(), collection)) {
231 232
        qCDebug(ETESYNC_LOG) << "Could not create EteSync entry";
        mResource->cancelTask(i18n("Could not create EteSync entry"));
233 234
        return;
    }
235

236
    // Using ItemModifyJob + changeProcessed() instead of changeCommitted to handle conflict error - ItemSync modifies local item payload
237 238
    Item newItem(item);
    newItem.setPayload<KContacts::Addressee>(contact);
239 240 241
    Akonadi::ItemModifyJob *modifyJob = new Akonadi::ItemModifyJob(newItem);
    modifyJob->disableRevisionCheck();
    mResource->changeProcessed();
242 243 244 245 246 247 248 249

    updateLocalContact(item.payload<KContacts::Addressee>());
}

void ContactHandler::itemRemoved(const Akonadi::Item &item)
{
    Collection collection = item.parentCollection();

250 251 252 253 254 255 256 257 258 259 260
    const QString contact = getLocalContact(item.remoteId());

    if (contact.isEmpty()) {
        qCDebug(ETESYNC_LOG) << "Could not get local contact";
        mResource->cancelTask(i18n("Could not get local contact"));
        return;
    }

    // Delete now, because itemRemoved() may be called when collection is removed
    deleteLocalContact(item.remoteId());

Shashwat Jolly's avatar
Shashwat Jolly committed
261
    const QString journalUid = collection.remoteId();
262
    const EteSyncJournalPtr &journal = mResource->getJournal(journalUid);
Shashwat Jolly's avatar
Shashwat Jolly committed
263

264 265 266 267 268
    if (!journal) {
        mResource->cancelTask();
        return;
    }

269
    EteSyncCryptoManagerPtr cryptoManager = etesync_journal_get_crypto_manager(journal.get(), mClientState->derived(), mClientState->keypair());
270

271
    EteSyncSyncEntryPtr syncEntry = etesync_sync_entry_new(QStringLiteral(ETESYNC_SYNC_ENTRY_ACTION_DELETE), contact);
272 273

    if (!createEteSyncEntry(syncEntry.get(), cryptoManager.get(), collection)) {
274 275
        qCDebug(ETESYNC_LOG) << "Could not create EteSync entry";
        mResource->cancelTask(i18n("Could not create EteSync entry"));
276 277
        return;
    }
278

279
    mResource->changeProcessed();
280 281 282 283
}

void ContactHandler::collectionAdded(const Akonadi::Collection &collection, const Akonadi::Collection &parent)
{
284 285
    Q_UNUSED(parent);

Shashwat Jolly's avatar
Shashwat Jolly committed
286
    const QString journalUid = QStringFromCharPtr(CharPtr(etesync_gen_uid()));
287
    EteSyncJournalPtr journal = etesync_journal_new(journalUid, ETESYNC_CURRENT_VERSION);
288

289
    EteSyncCollectionInfoPtr info = etesync_collection_info_new(etesyncCollectionType(), collection.displayName(), QString(), ETESYNC_COLLECTION_DEFAULT_COLOR);
290

291
    EteSyncCryptoManagerPtr cryptoManager = etesync_journal_get_crypto_manager(journal.get(), mClientState->derived(), mClientState->keypair());
292

Shashwat Jolly's avatar
Shashwat Jolly committed
293 294
    if (etesync_journal_set_info(journal.get(), cryptoManager.get(), info.get())) {
        qCDebug(ETESYNC_LOG) << "Could not set journal info";
295
        qCDebug(ETESYNC_LOG) << "EteSync error" << QStringFromCharPtr(CharPtr(etesync_get_error_message()));
Shashwat Jolly's avatar
Shashwat Jolly committed
296 297 298
        mResource->cancelTask(i18n("Could not set journal info"));
        return;
    };
299

Shashwat Jolly's avatar
Shashwat Jolly committed
300 301
    if (etesync_journal_manager_create(mClientState->journalManager(), journal.get())) {
        qCDebug(ETESYNC_LOG) << "Could not create journal";
302
        qCDebug(ETESYNC_LOG) << "EteSync error" << QStringFromCharPtr(CharPtr(etesync_get_error_message()));
Shashwat Jolly's avatar
Shashwat Jolly committed
303
        mResource->handleError();
304 305
        return;
    }
306 307 308

    Collection newCollection(collection);
    mResource->setupCollection(newCollection, journal.get());
309
    mResource->mJournalsCache[newCollection.remoteId()] = std::move(journal);
310 311 312 313 314
    mResource->changeCommitted(newCollection);
}

void ContactHandler::collectionChanged(const Akonadi::Collection &collection)
{
Shashwat Jolly's avatar
Shashwat Jolly committed
315
    const QString journalUid = collection.remoteId();
316
    const EteSyncJournalPtr &journal = mResource->getJournal(journalUid);
317

318
    if (!journal) {
319
        qCDebug(ETESYNC_LOG) << "Could not get journal" << journalUid;
320 321 322 323
        mResource->cancelTask(i18n("Could not get journal"));
        return;
    }

324
    EteSyncCollectionInfoPtr info = etesync_collection_info_new(etesyncCollectionType(), collection.displayName(), QString(), EteSyncDEFAULT_COLOR);
325

326
    EteSyncCryptoManagerPtr cryptoManager = etesync_journal_get_crypto_manager(journal.get(), mClientState->derived(), mClientState->keypair());
327

Shashwat Jolly's avatar
Shashwat Jolly committed
328 329
    if (etesync_journal_set_info(journal.get(), cryptoManager.get(), info.get())) {
        qCDebug(ETESYNC_LOG) << "Could not set journal info";
330
        qCDebug(ETESYNC_LOG) << "EteSync error" << QStringFromCharPtr(CharPtr(etesync_get_error_message()));
Shashwat Jolly's avatar
Shashwat Jolly committed
331 332 333
        mResource->cancelTask(i18n("Could not set journal info"));
        return;
    };
334

Shashwat Jolly's avatar
Shashwat Jolly committed
335 336
    if (etesync_journal_manager_update(mClientState->journalManager(), journal.get())) {
        qCDebug(ETESYNC_LOG) << "Could not update journal";
337
        qCDebug(ETESYNC_LOG) << "EteSync error" << QStringFromCharPtr(CharPtr(etesync_get_error_message()));
Shashwat Jolly's avatar
Shashwat Jolly committed
338
        mResource->handleError();
339 340 341
        return;
    }

342 343 344
    Collection newCollection(collection);
    mResource->setupCollection(newCollection, journal.get());
    mResource->changeCommitted(newCollection);
345 346 347 348
}

void ContactHandler::collectionRemoved(const Akonadi::Collection &collection)
{
Shashwat Jolly's avatar
Shashwat Jolly committed
349
    const QString journalUid = collection.remoteId();
350
    const EteSyncJournalPtr &journal = mResource->getJournal(journalUid);
351

352
    if (!journal) {
353
        qCDebug(ETESYNC_LOG) << "Could not get journal" << journalUid;
354 355 356 357
        mResource->cancelTask(i18n("Could not get journal"));
        return;
    }

Shashwat Jolly's avatar
Shashwat Jolly committed
358 359
    if (etesync_journal_manager_delete(mClientState->journalManager(), journal.get())) {
        qCDebug(ETESYNC_LOG) << "Could not delete journal";
360
        qCDebug(ETESYNC_LOG) << "EteSync error" << QStringFromCharPtr(CharPtr(etesync_get_error_message()));
Shashwat Jolly's avatar
Shashwat Jolly committed
361
        mResource->handleError();
362 363
        return;
    }
364
    mResource->changeProcessed();
365
}