[Merge] lp:~renatofilho/buteo-sync-plugins-contacts/new-code into lp:buteo-sync-plugins-contacts

Michael Sheldon michael.sheldon at canonical.com
Mon Jul 20 16:23:34 UTC 2015


Up to line 3000 now, still mostly just small corrections and a few questions.

Diff comments:

> 
> === added file 'buteo-contact-client/UContactsBackend.cpp'
> --- buteo-contact-client/UContactsBackend.cpp	1970-01-01 00:00:00 +0000
> +++ buteo-contact-client/UContactsBackend.cpp	2015-07-20 14:20:21 +0000
> @@ -0,0 +1,557 @@
> +/*
> + * This file is part of buteo-sync-plugins-goole package
> + *
> + * Copyright (C) 2013 Jolla Ltd. and/or its subsidiary(-ies).
> + *               2015 Canonical Ltd
> + *
> + * Contributors: Sateesh Kavuri <sateesh.kavuri at gmail.com>
> + *               Mani Chandrasekar <maninc at gmail.com>
> + *               Renato Araujo Oliveira Filho <renato.filho at canonical.com>
> + *
> + * This library is free software; you can redistribute it and/or
> + * modify it under the terms of the GNU Lesser General Public License
> + * version 2.1 as published by the Free Software Foundation.
> + *
> + * 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
> + * Lesser General Public License for more details.
> + *
> + * You should have received a copy of the GNU Lesser General Public
> + * License along with this library; if not, write to the Free Software
> + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
> + * 02110-1301 USA
> + *
> + */
> +
> +#include "config.h"
> +#include "UContactsBackend.h"
> +#include "UContactsCustomDetail.h"
> +
> +#include <LogMacros.h>
> +
> +#include <QContactTimestamp>
> +#include <QContactIdFilter>
> +#include <QContactIntersectionFilter>
> +#include <QContactSyncTarget>
> +#include <QContactDetailFilter>
> +#include <QContactGuid>
> +#include <QContactDisplayLabel>
> +#include <QContactExtendedDetail>
> +#include <QContactSyncTarget>
> +
> +#include <QBuffer>
> +#include <QSet>
> +#include <QHash>
> +
> +#include <QDBusInterface>
> +#include <QDBusReply>
> +
> +static const QString CPIM_SERVICE_NAME             ("com.canonical.pim");
> +static const QString CPIM_ADDRESSBOOK_OBJECT_PATH  ("/com/canonical/pim/AddressBook");
> +static const QString CPIM_ADDRESSBOOK_IFACE_NAME   ("com.canonical.pim.AddressBook");
> +
> +UContactsBackend::UContactsBackend(const QString &managerName, QObject* parent)
> +    : QObject (parent),
> +      iMgr(new QContactManager(managerName))
> +{
> +    FUNCTION_CALL_TRACE;
> +}
> +
> +UContactsBackend::~UContactsBackend()
> +{
> +    FUNCTION_CALL_TRACE;
> +
> +    delete iMgr;
> +    iMgr = NULL;
> +}
> +
> +bool
> +UContactsBackend::init(uint syncAccount, const QString &syncTarget)
> +{
> +    FUNCTION_CALL_TRACE;
> +
> +    // create address book it it does not exists
> +    // check if the source already exists
> +    QContactDetailFilter filter;
> +    filter.setDetailType(QContactDetail::TypeType, QContactType::FieldType);
> +    filter.setValue(QContactType::TypeGroup);
> +
> +    QList<QContact> sources = iMgr->contacts(filter);
> +    Q_FOREACH(const QContact &contact, sources) {
> +        QContactExtendedDetail exd = UContactsCustomDetail::getCustomField(contact,
> +                                                                           "ACCOUNT-ID");
> +        if (!exd.isEmpty() && (exd.data().toUInt() == syncAccount)) {
> +            mSyncTargetId = contact.detail<QContactGuid>().guid();
> +            return true;
> +        }
> +    }
> +
> +    // memory/mock manager does not support syncTarget
> +    if (iMgr->managerName() != "mock") {
> +        // create a new source if necessary
> +        QContact contact;
> +        contact.setType(QContactType::TypeGroup);
> +
> +        QContactDisplayLabel label;
> +        label.setLabel(syncTarget);
> +        contact.saveDetail(&label);
> +
> +        // set the new source as default
> +        QContactExtendedDetail isDefault;
> +        isDefault.setName("IS-PRIMARY");
> +        isDefault.setData(true);
> +        contact.saveDetail(&isDefault);
> +
> +        // Link source with account
> +        QContactExtendedDetail accountId;
> +        accountId.setName("ACCOUNT-ID");
> +        accountId.setData(syncAccount);
> +        contact.saveDetail(&accountId);
> +
> +        if (!iMgr->saveContact(&contact)) {
> +            qWarning() << "Fail to create contact source:" << syncTarget;
> +            return false;
> +        }
> +
> +        mSyncTargetId = contact.detail<QContactGuid>().guid();
> +    }
> +
> +    return true;
> +}
> +
> +bool
> +UContactsBackend::uninit()
> +{
> +    FUNCTION_CALL_TRACE;
> +
> +    return true;
> +}
> +
> +QList<QContactId>
> +UContactsBackend::getAllContactIds()
> +{
> +    FUNCTION_CALL_TRACE;
> +    Q_ASSERT (iMgr);
> +    return iMgr->contactIds(getSyncTargetFilter());
> +}
> +
> +RemoteToLocalIdMap
> +UContactsBackend::getAllNewContactIds(const QDateTime &aTimeStamp)
> +{
> +    FUNCTION_CALL_TRACE;
> +    LOG_DEBUG("Retrieve New Contacts Since " << aTimeStamp);
> +
> +    RemoteToLocalIdMap idList;
> +    const QContactChangeLogFilter::EventType eventType =
> +            QContactChangeLogFilter::EventAdded;
> +
> +    getSpecifiedContactIds(eventType, aTimeStamp, &idList);
> +
> +    return idList;
> +}
> +
> +RemoteToLocalIdMap
> +UContactsBackend::getAllModifiedContactIds(const QDateTime &aTimeStamp)
> +{
> +
> +    FUNCTION_CALL_TRACE;
> +
> +    LOG_DEBUG("Retrieve Modified Contacts Since " << aTimeStamp);
> +
> +    RemoteToLocalIdMap idList;
> +    const QContactChangeLogFilter::EventType eventType =
> +            QContactChangeLogFilter::EventChanged;
> +
> +    getSpecifiedContactIds(eventType, aTimeStamp, &idList);
> +
> +    return idList;
> +}
> +
> +RemoteToLocalIdMap
> +UContactsBackend::getAllDeletedContactIds(const QDateTime &aTimeStamp)
> +{
> +    FUNCTION_CALL_TRACE;
> +    LOG_DEBUG("Retrieve Deleted Contacts Since " << aTimeStamp);
> +
> +    RemoteToLocalIdMap idList;
> +    const QContactChangeLogFilter::EventType eventType =
> +            QContactChangeLogFilter::EventRemoved;
> +
> +    getSpecifiedContactIds(eventType, aTimeStamp, &idList);
> +
> +    return idList;
> +}
> +
> +bool
> +UContactsBackend::addContacts(QList<QContact>& aContactList,
> +                              QMap<int, UContactsStatus> *aStatusMap)
> +{
> +    FUNCTION_CALL_TRACE;
> +    Q_ASSERT(iMgr);
> +    Q_ASSERT(aStatusMap);
> +
> +    QMap<int, QContactManager::Error> errorMap;
> +
> +    // Check if contact already exists if it exists set the contact id
> +    // to cause an update instead of create a new one
> +    for(int i=0; i < aContactList.size(); i++) {
> +        QContact &c = aContactList[i];
> +        QString remoteId = getRemoteId(c);
> +        QContactId id = entryExists(remoteId);
> +        if (!id.isNull()) {
> +            c.setId(id);
> +        } else {
> +            // make sure that all contacts retrieved are saved on the correct sync target
> +            QContactSyncTarget syncTarget = c.detail<QContactSyncTarget>();
> +            syncTarget.setSyncTarget(syncTargetId());
> +            c.saveDetail(&syncTarget);
> +        }
> +
> +        // remove guid field if it exists
> +        QContactGuid guid = c.detail<QContactGuid>();
> +        if (!guid.isEmpty()) {
> +            c.removeDetail(&guid);
> +        }
> +    }
> +
> +    bool retVal = iMgr->saveContacts(&aContactList, &errorMap);
> +    if (!retVal) {
> +        LOG_WARNING( "Errors reported while saving contacts:" << iMgr->error() );
> +    }
> +
> +    // QContactManager will populate errorMap only for errors, but we use this as a status map,
> +    // so populate NoError if there's no error.
> +    for (int i = 0; i < aContactList.size(); i++)
> +    {
> +        UContactsStatus status;
> +        status.id = i;
> +        if (!errorMap.contains(i)) {
> +            status.errorCode = QContactManager::NoError;
> +        } else {
> +            LOG_WARNING("Contact with id " <<  aContactList.at(i).id() << " and index " << i <<" is in error");
> +            status.errorCode = errorMap.value(i);
> +        }
> +        aStatusMap->insert(i, status);
> +    }
> +
> +    return retVal;
> +}
> +
> +QMap<int,UContactsStatus>
> +UContactsBackend::modifyContacts(QList<QContact> *aContactList)
> +{
> +    FUNCTION_CALL_TRACE;
> +
> +    Q_ASSERT (iMgr);
> +    UContactsStatus status;
> +
> +    QMap<int,QContactManager::Error> errors;
> +    QMap<int,UContactsStatus> statusMap;
> +
> +    // WORKAROUND: Our backend uses GUid as contact id due problems with contact id serialization
> +    // we can not use this field
> +    for (int i = 0; i < aContactList->size(); i++) {
> +        QContact &newContact = (*aContactList)[i];
> +        QString remoteId = getRemoteId(newContact);
> +
> +        // if the contact was created the remoteId will not exists on local database
> +        QContactId localId = entryExists(remoteId);
> +
> +        // nt this case we should use the guid stored on contact

nt -> in

> +        QContactGuid guid = newContact.detail<QContactGuid>();
> +
> +        if (localId.isNull() && !guid.isEmpty()) {
> +            // try the guid (should contains the local id) field
> +            localId = QContactId::fromString(guid.guid());
> +        }
> +        newContact.setId(localId);
> +        newContact.removeDetail(&guid);
> +    }
> +
> +    if(iMgr->saveContacts(aContactList , &errors)) {
> +        LOG_DEBUG("Batch Modification of Contacts Succeeded");
> +    } else {
> +        LOG_DEBUG("Batch Modification of Contacts Failed");
> +    }
> +
> +    // QContactManager will populate errorMap only for errors, but we use this as a status map,
> +    // so populate NoError if there's no error.
> +    // TODO QContactManager populates indices from the aContactList, but we populate keys, is this OK?
> +    for (int i = 0; i < aContactList->size(); i++) {
> +        QContactId contactId = aContactList->at(i).id();
> +        if( !errors.contains(i) ) {
> +            LOG_DEBUG("No error for contact with id " << contactId << " and index " << i);
> +            status.errorCode = QContactManager::NoError;
> +            statusMap.insert(i, status);
> +        } else {
> +            LOG_DEBUG("contact with id " << contactId << " and index " << i <<" is in error");
> +            QContactManager::Error errorCode = errors.value(i);
> +            status.errorCode = errorCode;
> +            statusMap.insert(i, status);
> +        }
> +    }
> +    return statusMap;
> +}
> +
> +QMap<int, UContactsStatus>
> +UContactsBackend::deleteContacts(const QStringList &aContactIDList)
> +{
> +    FUNCTION_CALL_TRACE;
> +
> +    QList<QContactId> qContactIdList;
> +    foreach (QString id, aContactIDList) {
> +        qContactIdList.append(QContactId::fromString(id));
> +    }
> +
> +    return deleteContacts(qContactIdList);
> +}
> +
> +QMap<int, UContactsStatus>
> +UContactsBackend::deleteContacts(const QList<QContactId> &aContactIDList) {
> +    FUNCTION_CALL_TRACE;
> +
> +    Q_ASSERT (iMgr);
> +    UContactsStatus status;
> +    QMap<int, QContactManager::Error> errors;
> +    QMap<int, UContactsStatus> statusMap;
> +
> +    if(iMgr->removeContacts(aContactIDList , &errors)) {
> +        LOG_DEBUG("Successfully Removed all contacts ");
> +    }
> +    else {
> +        LOG_WARNING("Failed Removing Contacts");
> +    }
> +
> +    // QContactManager will populate errorMap only for errors, but we use this as a status map,
> +    // so populate NoError if there's no error.
> +    for (int i = 0; i < aContactIDList.size(); i++) {
> +        QContactId contactId = aContactIDList.value(i);
> +        if( !errors.contains(i) )
> +        {
> +            LOG_DEBUG("No error for contact with id " << contactId << " and index " << i);
> +            status.errorCode = QContactManager::NoError;
> +            statusMap.insert(i, status);
> +        }
> +        else
> +        {
> +            LOG_DEBUG("contact with id " << contactId << " and index " << i <<" is in error");
> +            QContactManager::Error errorCode = errors.value(i);
> +            status.errorCode = errorCode;
> +            statusMap.insert(i, status);
> +        }
> +    }
> +
> +    return statusMap;
> +}
> +
> +
> +void
> +UContactsBackend::getSpecifiedContactIds(const QContactChangeLogFilter::EventType aEventType,
> +                                         const QDateTime& aTimeStamp,
> +                                         RemoteToLocalIdMap *aIdList)
> +{
> +    FUNCTION_CALL_TRACE;
> +    Q_ASSERT(aIdList);
> +
> +    QList<QContactId> localIdList;
> +    QContactChangeLogFilter filter(aEventType);
> +    filter.setSince(aTimeStamp);
> +
> +    localIdList = iMgr->contactIds(filter  & getSyncTargetFilter());
> +    LOG_DEBUG("Local ID added =  " << localIdList.size() << "    Datetime from when this " << aTimeStamp.toString());
> +    // Filter out ids for items that were added after the specified time.
> +    if (aEventType != QContactChangeLogFilter::EventAdded)
> +    {
> +        filter.setEventType(QContactChangeLogFilter::EventAdded);
> +        QList<QContactId> addedList = iMgr->contactIds(filter  & getSyncTargetFilter());
> +        foreach (const QContactId &id, addedList)
> +        {
> +            localIdList.removeAll(id);
> +        }
> +    }
> +
> +    // This is a defensive procedure to prevent duplicate items being sent.
> +    // QSet does not allow duplicates, thus transforming QList to QSet and back
> +    // again will remove any duplicate items in the original QList.
> +    int originalIdCount = localIdList.size();
> +    QSet<QContactId> idSet = localIdList.toSet();
> +    int idCountAfterDupRemoval = idSet.size();
> +
> +    LOG_DEBUG("Item IDs found (returned / incl. duplicates): " << idCountAfterDupRemoval << "/" << originalIdCount);
> +    if (originalIdCount != idCountAfterDupRemoval) {
> +        LOG_WARNING("Contacts backend returned duplicate items for requested list");
> +        LOG_WARNING("Duplicate item IDs have been removed");
> +    } // no else
> +
> +    localIdList = idSet.toList();
> +
> +    QContactFetchHint remoteIdHint;
> +    QList <QContactDetail::DetailType> detailTypes;
> +    detailTypes << QContactExtendedDetail::Type;
> +    remoteIdHint.setDetailTypesHint(detailTypes);
> +
> +    QList<QContact> contacts = iMgr->contacts(localIdList, remoteIdHint);
> +    foreach (const QContact &contact, contacts) {
> +        QString rid = getRemoteId(contact);
> +        aIdList->insertMulti(rid, contact.id());
> +    }
> +}
> +
> +/*!
> +    \fn GContactsBackend::getContact(QContactId aContactId)
> + */
> +QContact
> +UContactsBackend::getContact(const QContactId& aContactId)
> +{
> +    FUNCTION_CALL_TRACE;
> +    Q_ASSERT (iMgr);
> +    QList<QContact> returnedContacts;
> +
> +    LOG_DEBUG("Contact ID to be retreived = " << aContactId.toString());
> +    returnedContacts = iMgr->contacts(QList<QContactId>() << aContactId);
> +
> +    LOG_DEBUG("Contacts retreived from Contact manager  = " << returnedContacts.count());
> +    return returnedContacts.value(0, QContact());
> +}
> +
> +QContact
> +UContactsBackend::getContact(const QString& remoteId)
> +{
> +    FUNCTION_CALL_TRACE;
> +    Q_ASSERT (iMgr);
> +    LOG_DEBUG("Remote id to be searched for = " << remoteId);
> +
> +    //FIXME: use guid field when supported by address-book-service
> +    QContactIntersectionFilter remoteIdFilter = getRemoteIdFilter(remoteId);
> +    QList<QContact> contactList = iMgr->contacts(remoteIdFilter & getSyncTargetFilter());
> +    if (contactList.size() > 0) {
> +        return contactList.at(0);
> +    }
> +    return QContact();
> +}
> +
> +QContactId
> +UContactsBackend::entryExists(const QString remoteId)
> +{
> +    if (remoteId.isEmpty()) {
> +        return QContactId();
> +    }
> +
> +    QContactFilter ridFilter = getRemoteIdFilter(remoteId);
> +    QList<QContactId> idList = iMgr->contactIds(ridFilter & getSyncTargetFilter());
> +    if (idList.size () > 0)
> +        return idList.first ();
> +    else
> +        return QContactId();
> +}
> +
> +QString
> +UContactsBackend::syncTargetId() const
> +{
> +    return mSyncTargetId;
> +}
> +
> +const QStringList
> +UContactsBackend::localIds(const QStringList remoteIds)
> +{
> +    QStringList localIdList;
> +    foreach (QString guid , remoteIds) {
> +        QString localId = entryExists(guid).toString();
> +        if (!localId.isEmpty()) {
> +            localIdList << localId;
> +        }
> +    }
> +    Q_ASSERT(localIdList.count() == remoteIds.count());
> +    return localIdList;
> +}
> +
> +QContactFilter
> +UContactsBackend::getRemoteIdFilter(const QString &remoteId) const
> +{
> +    QContactIntersectionFilter remoteFilter;
> +
> +    QContactDetailFilter xDetailNameFilter;
> +    xDetailNameFilter.setDetailType(QContactExtendedDetail::Type,
> +                                    QContactExtendedDetail::FieldName);
> +    xDetailNameFilter.setValue(UContactsCustomDetail::FieldRemoteId);
> +
> +    QContactDetailFilter xDetailValueFilter;
> +    xDetailValueFilter.setDetailType(QContactExtendedDetail::Type,
> +                                     QContactExtendedDetail::FieldData);
> +    xDetailValueFilter.setValue(remoteId);
> +
> +    remoteFilter << xDetailNameFilter
> +                 << xDetailValueFilter;
> +    return remoteFilter;
> +}
> +
> +QString
> +UContactsBackend::getRemoteId(const QContact &contact)
> +{
> +    return UContactsCustomDetail::getCustomField(contact, UContactsCustomDetail::FieldRemoteId).data().toString();
> +}
> +
> +void UContactsBackend::setRemoteId(QContact &contact, const QString &remoteId)
> +{
> +    UContactsCustomDetail::setCustomField(contact, UContactsCustomDetail::FieldRemoteId, QVariant(remoteId));
> +}
> +
> +QString UContactsBackend::getLocalId(const QContact &contact)
> +{
> +    QContactGuid guid = contact.detail<QContactGuid>();
> +    return guid.guid();
> +}
> +
> +void UContactsBackend::setLocalId(QContact &contact, const QString &localId)
> +{
> +    QContactGuid guid = contact.detail<QContactGuid>();
> +    guid.setGuid(localId);
> +    contact.saveDetail(&guid);
> +}
> +
> +bool UContactsBackend::deleted(const QContact &contact)
> +{
> +    QString deletedAt = UContactsCustomDetail::getCustomField(contact, UContactsCustomDetail::FieldDeletedAt).data().toString();
> +    return !deletedAt.isEmpty();
> +}
> +
> +void
> +UContactsBackend::purgecontacts()
> +{
> +    QDBusInterface iface(CPIM_SERVICE_NAME,
> +                         CPIM_ADDRESSBOOK_OBJECT_PATH,
> +                         CPIM_ADDRESSBOOK_IFACE_NAME);
> +    QDBusReply<void> reply = iface.call("purgeContacts", QString(""), mSyncTargetId);
> +    if (reply.error().isValid()) {
> +        LOG_WARNING("Fail to purge contacts" << reply.error());
> +    } else {
> +        LOG_DEBUG("Purged backend contacts");
> +    }
> +}
> +
> +QContactFilter
> +UContactsBackend::getSyncTargetFilter() const
> +{
> +    // user entered contacts, i.e. all other contacts that are not sourcing
> +    // from restricted backends or instant messaging service
> +    static QContactDetailFilter detailFilterDefaultSyncTarget;
> +
> +    if (!mSyncTargetId.isEmpty() &&
> +        detailFilterDefaultSyncTarget.value().isNull()) {
> +        detailFilterDefaultSyncTarget.setDetailType(QContactSyncTarget::Type,
> +                                                    QContactSyncTarget::FieldSyncTarget + 1);
> +        detailFilterDefaultSyncTarget.setValue(mSyncTargetId);
> +    } else if (mSyncTargetId.isEmpty()) {
> +        return QContactFilter();
> +    }
> +
> +    // return the union
> +    return detailFilterDefaultSyncTarget;
> +}
> +
> +
> +QtContacts::QContactManager *UContactsBackend::manager() const
> +{
> +    return iMgr;
> +}
> 
> === added file 'buteo-contact-client/UContactsClient.cpp'
> --- buteo-contact-client/UContactsClient.cpp	1970-01-01 00:00:00 +0000
> +++ buteo-contact-client/UContactsClient.cpp	2015-07-20 14:20:21 +0000
> @@ -0,0 +1,946 @@
> +/*
> + * This file is part of buteo-sync-plugins-goole package
> + *
> + * Copyright (C) 2013 Jolla Ltd. and/or its subsidiary(-ies).
> + *               2015 Canonical Ltd
> + *
> + * Contributors: Sateesh Kavuri <sateesh.kavuri at gmail.com>
> + *               Mani Chandrasekar <maninc at gmail.com>
> + *               Renato Araujo Oliveira Filho <renato.filho at canonical.com>
> + *
> + * This library is free software; you can redistribute it and/or
> + * modify it under the terms of the GNU Lesser General Public License
> + * version 2.1 as published by the Free Software Foundation.
> + *
> + * 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
> + * Lesser General Public License for more details.
> + *
> + * You should have received a copy of the GNU Lesser General Public
> + * License along with this library; if not, write to the Free Software
> + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
> + * 02110-1301 USA
> + *
> + */
> +
> +#include "UContactsClient.h"
> +#include "UContactsBackend.h"
> +#include "UAbstractRemoteSource.h"
> +#include "UAuth.h"
> +#include "config.h"
> +
> +//Buteo
> +#include <LogMacros.h>
> +#include <ProfileEngineDefs.h>
> +#include <ProfileManager.h>
> +
> +#include <QLibrary>
> +#include <QtNetwork>
> +#include <QDateTime>
> +#include <QContactGuid>
> +#include <QContactDetailFilter>
> +#include <QContactAvatar>
> +
> +class UContactsClientPrivate
> +{
> +public:
> +    UContactsClientPrivate(const QString &serviceName)
> +        : mAuth(0),
> +          mContactBackend(0),
> +          mRemoteSource(0),
> +          mServiceName(serviceName)
> +    {
> +    }
> +
> +    UAuth*                      mAuth;
> +    UContactsBackend*           mContactBackend;
> +    UAbstractRemoteSource*      mRemoteSource;
> +    bool                        mSlowSync;
> +    QString                     mServiceName;
> +    // local database information
> +    QSet<QContactId>            mAllLocalContactIds;
> +    RemoteToLocalIdMap  mAddedContactIds;
> +    RemoteToLocalIdMap  mModifiedContactIds;
> +    RemoteToLocalIdMap  mDeletedContactIds;
> +    // sync report
> +    QMap<QString, Buteo::DatabaseResults> mItemResults;
> +    Buteo::SyncResults          mResults;
> +    // sync profile
> +    QString mSyncTarget;
> +    qint32 mAccountId;
> +    Buteo::SyncProfile::SyncDirection mSyncDirection;
> +    Buteo::SyncProfile::ConflictResolutionPolicy mConflictResPolicy;
> +};
> +
> +UContactsClient::UContactsClient(const QString& aPluginName,
> +                                 const Buteo::SyncProfile& aProfile,
> +                                 Buteo::PluginCbInterface *aCbInterface, const QString &serviceName)
> +    : ClientPlugin(aPluginName, aProfile, aCbInterface),
> +      d_ptr(new UContactsClientPrivate(serviceName))
> +{
> +    FUNCTION_CALL_TRACE;
> +}
> +
> +UContactsClient::~UContactsClient()
> +{
> +    FUNCTION_CALL_TRACE;
> +    Q_D(UContactsClient);
> +
> +    delete d->mAuth;
> +    delete d->mRemoteSource;
> +}
> +
> +bool
> +UContactsClient::init()
> +{
> +    FUNCTION_CALL_TRACE;
> +    Q_D(UContactsClient);
> +
> +    if (lastSyncTime().isNull()) {
> +        d->mSlowSync = true;
> +    } else {
> +        d->mSlowSync = false;
> +    }
> +
> +    LOG_DEBUG ("Last sync date:" << lastSyncTime() << "Using slow sync?" << d->mSlowSync);
> +    if (!initConfig()) {
> +        LOG_CRITICAL("Fail to init configuration");
> +        return false;
> +    }
> +
> +    d->mAuth = crateAuthenticator(this);
> +    if (!d->mAuth || !d->mAuth->init(d->mAccountId, d->mServiceName)) {
> +        LOG_CRITICAL("Fail to create auth object");
> +        goto init_fail;
> +    }
> +
> +    d->mContactBackend = createContactsBackend(this);
> +    if (!d->mContactBackend || !d->mContactBackend->init(d->mAccountId,
> +                                                         d->mAuth->accountDisplayName())) {
> +        LOG_CRITICAL("Fail to create contact backend");
> +        goto init_fail;
> +    }
> +
> +
> +    // remote source must be initialized after mAuth because its uses the account name property
> +    d->mRemoteSource = createRemoteSource(this);
> +    if (!d->mRemoteSource) {
> +        LOG_CRITICAL("Fail to create remote contact backend");
> +        goto init_fail;
> +    }
> +
> +    // sign in.
> +    connect(d->mAuth, SIGNAL(success()), SLOT(start()));
> +    connect(d->mAuth, SIGNAL(failed()), SLOT(onAuthenticationError()));
> +
> +    // syncStateChanged to signal changes from CONNECTING, RECEIVING
> +    // SENDING, DISCONNECTING, CLOSED
> +    connect(this,
> +            SIGNAL(stateChanged(Sync::SyncProgressDetail)),
> +            SLOT(onStateChanged(Sync::SyncProgressDetail)));
> +
> +    // Take necessary action when sync is finished
> +    connect(this,
> +            SIGNAL(syncFinished(Sync::SyncStatus)),
> +            SLOT(onSyncFinished(Sync::SyncStatus)));
> +
> +    return true;
> +
> +init_fail:
> +
> +    delete d->mRemoteSource;
> +    delete d->mContactBackend;
> +    delete d->mAuth;
> +    d->mRemoteSource = 0;
> +    d->mContactBackend = 0;
> +    d->mAuth = 0;
> +    return false;
> +}
> +
> +bool
> +UContactsClient::uninit()
> +{
> +    FUNCTION_CALL_TRACE;
> +    Q_D(UContactsClient);
> +
> +    delete d->mRemoteSource;
> +    delete d->mContactBackend;
> +    delete d->mAuth;
> +    d->mRemoteSource = 0;
> +    d->mContactBackend = 0;
> +    d->mAuth = 0;
> +
> +    return true;
> +}
> +
> +bool
> +UContactsClient::isReadyToSync() const
> +{
> +    const Q_D(UContactsClient);
> +    return (d->mContactBackend && d->mRemoteSource && d->mAuth);
> +}
> +
> +UContactsBackend *UContactsClient::createContactsBackend(QObject *parent) const
> +{
> +    return new UContactsBackend(QCONTACTS_BACKEND_NAME, parent);
> +}
> +
> +UAuth *UContactsClient::crateAuthenticator(QObject *parent) const
> +{
> +    return new UAuth(parent);
> +}
> +
> +bool
> +UContactsClient::startSync()
> +{
> +    FUNCTION_CALL_TRACE;
> +
> +    if (!isReadyToSync()) {
> +        LOG_WARNING ("Ubuntu plugin is not ready to sync.");
> +        return false;
> +    }
> +
> +    Q_D(UContactsClient);
> +    LOG_DEBUG ("Init done. Continuing with sync");
> +
> +    return d->mAuth->authenticate();
> +}
> +
> +void
> +UContactsClient::abortSync(Sync::SyncStatus aStatus)
> +{
> +    FUNCTION_CALL_TRACE;
> +    Q_D(UContactsClient);
> +
> +    d->mRemoteSource->abort();
> +    emit syncFinished(Sync::SYNC_ABORTED);
> +}
> +
> +bool
> +UContactsClient::initConfig()
> +{
> +    FUNCTION_CALL_TRACE;
> +    Q_D(UContactsClient);
> +
> +    //TODO: support multiple remote databases "scopes"
> +    QStringList accountList = iProfile.keyValues(Buteo::KEY_ACCOUNT_ID);
> +    if (!accountList.isEmpty()) {
> +        QString aId = accountList.first();
> +        if (aId != NULL) {
> +            d->mAccountId = aId.toInt();
> +        }
> +    } else {
> +        d->mAccountId = 0;
> +        LOG_WARNING("Account id not found in config profile");
> +        return false;
> +    }
> +
> +    QStringList databaseName = iProfile.keyValues(Buteo::KEY_DISPLAY_NAME);
> +    if (databaseName.isEmpty()) {
> +        LOG_WARNING("\"displayname\" is missing on configuration file");
> +        return false;
> +    }
> +    d->mSyncTarget = databaseName.first();
> +    d->mSyncDirection = iProfile.syncDirection();
> +    d->mConflictResPolicy = iProfile.conflictResolutionPolicy();
> +    d->mItemResults.insert(d->mSyncTarget, Buteo::DatabaseResults());
> +
> +    return true;
> +}
> +
> +void
> +UContactsClient::onAuthenticationError()
> +{
> +    LOG_WARNING("Fail to authenticate with account");
> +    emit syncFinished (Sync::SYNC_AUTHENTICATION_FAILURE);
> +}
> +
> +bool
> +UContactsClient::start()
> +{
> +    FUNCTION_CALL_TRACE;
> +    Q_D(UContactsClient);
> +
> +    /*
> +      1. If no previous sync, go for slow-sync. Fetch all contacts
> +         from server
> +      2. Check if previous sync happened (from SyncLog). If yes,
> +         fetch the time of last sync
> +      3. Using the last sync time, retrieve all contacts from server
> +         that were added/modified/deleted
> +      4. Fetch all added/modified/deleted items from device
> +      5. Check for conflicts. Take the default policy as "server-wins"
> +      6. Save the list from the server to device
> +      7. Push "client changes" - "conflicting items" to the server
> +      8. Save the sync log
> +     */
> +
> +    // Remote source will be create after authentication since it needs some information
> +    // about the authentication (auth-token, etc..)
> +
> +    if (!d->mRemoteSource->init(remoteSourceProperties())) {
> +        LOG_WARNING("Fail to init remote source");
> +        return false;
> +    }
> +
> +    switch (d->mSyncDirection)
> +    {
> +    case Buteo::SyncProfile::SYNC_DIRECTION_TWO_WAY:
> +    {
> +        QDateTime sinceDate = d->mSlowSync ? QDateTime() : lastSyncTime();
> +
> +        LOG_DEBUG("load all contacts since" << sinceDate << sinceDate.isValid());
> +        // load changed contact since the last sync date or all contacts if no
> +        // sync was done before
> +        loadLocalContacts(sinceDate);
> +
> +        // load remote contacts
> +        if (d->mSlowSync) {
> +            connect(d->mRemoteSource,
> +                    SIGNAL(contactsFetched(QList<QtContacts::QContact>,Sync::SyncStatus)),
> +                    SLOT(onRemoteContactsFetchedForSlowSync(QList<QtContacts::QContact>,Sync::SyncStatus)));
> +        } else {
> +            connect(d->mRemoteSource,
> +                    SIGNAL(contactsFetched(QList<QtContacts::QContact>,Sync::SyncStatus)),
> +                    SLOT(onRemoteContactsFetchedForFastSync(QList<QtContacts::QContact>,Sync::SyncStatus)));
> +        }
> +        d->mRemoteSource->fetchContacts(sinceDate, !d->mSlowSync, true);
> +        break;
> +    }
> +    case Buteo::SyncProfile::SYNC_DIRECTION_FROM_REMOTE:
> +        LOG_WARNING("SYNC_DIRECTION_FROM_REMOTE: not implemented");
> +        return false;
> +    case Buteo::SyncProfile::SYNC_DIRECTION_TO_REMOTE:
> +        LOG_WARNING("SYNC_DIRECTION_TO_REMOTE: not implemented");
> +        return false;
> +    case Buteo::SyncProfile::SYNC_DIRECTION_UNDEFINED:
> +        // Not required
> +    default:
> +        // throw configuration error
> +        return false;
> +        break;
> +    };
> +
> +    return true;
> +}
> +
> +QList<QContact>
> +UContactsClient::prepareContactsToUpload(UContactsBackend *backend,
> +                                         const QSet<QContactId> &ids)
> +{
> +    QList<QContact> toUpdate;
> +
> +    foreach(const QContactId &id, ids) {
> +        QContact contact = backend->getContact(id);
> +        if (!contact.isEmpty()) {
> +            toUpdate << contact;
> +        } else {
> +            LOG_CRITICAL("Fail to find local contact with id:" << id);
> +            return QList<QContact>();
> +        }
> +    }
> +
> +    return toUpdate;
> +}
> +
> +void
> +UContactsClient::onRemoteContactsFetchedForSlowSync(const QList<QContact> contacts,
> +                                                    Sync::SyncStatus status)
> +{
> +    FUNCTION_CALL_TRACE;
> +    Q_D(UContactsClient);
> +    if ((status != Sync::SYNC_PROGRESS) && (status != Sync::SYNC_STARTED)) {
> +        disconnect(d->mRemoteSource);
> +    }
> +
> +    if ((status == Sync::SYNC_PROGRESS) || (status == Sync::SYNC_DONE)) {
> +        // save remote contacts locally
> +        storeToLocalForSlowSync(contacts);
> +
> +        if (status == Sync::SYNC_DONE) {
> +            QList<QContact> toUpload = prepareContactsToUpload(d->mContactBackend, d->mAllLocalContactIds);
> +            connect(d->mRemoteSource,
> +                    SIGNAL(transactionCommited(QList<QtContacts::QContact>,
> +                                               QList<QtContacts::QContact>,
> +                                               QStringList,Sync::SyncStatus)),
> +                    SLOT(onContactsSavedForSlowSync(QList<QtContacts::QContact>,
> +                                                    QList<QtContacts::QContact>,
> +                                                    QStringList,Sync::SyncStatus)));
> +
> +            d->mRemoteSource->transaction();
> +            d->mRemoteSource->saveContacts(toUpload);
> +            d->mRemoteSource->commit();
> +        }
> +    } else {
> +        emit syncFinished(status);
> +    }
> +}
> +
> +void
> +UContactsClient::onContactsSavedForSlowSync(const QList<QtContacts::QContact> &createdContacts,
> +                                            const QList<QtContacts::QContact> &updatedContacts,
> +                                            const QStringList &removedContacts,
> +                                            Sync::SyncStatus status)
> +{
> +    FUNCTION_CALL_TRACE;
> +    Q_D(UContactsClient);
> +
> +    LOG_DEBUG("AFTER UPLOAD(Slow sync):"
> +                << "\n\tCreated on remote:" << createdContacts.size()
> +                << "\n\tUpdated on remote:" << updatedContacts.size()
> +                << "\n\tRemoved from remote:" << removedContacts.size());
> +
> +    if ((status != Sync::SYNC_PROGRESS) && (status != Sync::SYNC_STARTED)) {
> +        disconnect(d->mRemoteSource);
> +    }
> +
> +    if ((status == Sync::SYNC_PROGRESS) || (status == Sync::SYNC_DONE)) {
> +        QList<QContact> changedContacts;
> +
> +        changedContacts += createdContacts;
> +        changedContacts += updatedContacts;
> +        updateIdsToLocal(changedContacts);
> +
> +        // sync report
> +        addProcessedItem(Sync::ITEM_ADDED,
> +                         Sync::REMOTE_DATABASE,
> +                         syncTargetId(),
> +                         createdContacts.size());
> +
> +        if (status == Sync::SYNC_PROGRESS) {
> +            // sync still in progress
> +            return;
> +        } else {
> +            // WORKARDOUND: 'galera' contacts service take a while to fire contacts
> +            // changed singal, this can cause a new sync due the storage change plugin
> +            // lets wait 2 secs before fire sync finished signal
> +            QTimer::singleShot(2000, this, SLOT(fireSyncFinishedSucessfully()));
> +            return;
> +        }
> +    }
> +
> +    emit syncFinished(status);
> +}
> +
> +void UContactsClient::onRemoteContactsFetchedForFastSync(const QList<QContact> contacts,
> +                                                         Sync::SyncStatus status)
> +{
> +    FUNCTION_CALL_TRACE;
> +    Q_D(UContactsClient);
> +    if ((status != Sync::SYNC_PROGRESS) && (status != Sync::SYNC_STARTED)) {
> +        disconnect(d->mRemoteSource);
> +    }
> +
> +    if ((status == Sync::SYNC_PROGRESS) || (status == Sync::SYNC_DONE)) {
> +        // save remote contacts locally
> +        storeToLocalForFastSync(contacts);
> +
> +        if (status == Sync::SYNC_DONE) {
> +            QList<QContact> contactsToUpload;
> +            QList<QContact> contactsToRemove;
> +
> +            // Contacts created locally
> +            LOG_DEBUG("Total number of Contacts ADDED : " << d->mAddedContactIds.count());
> +            contactsToUpload = prepareContactsToUpload(d->mContactBackend,
> +                                                       d->mAddedContactIds.values().toSet());
> +
> +            // Contacts modified locally
> +            LOG_DEBUG("Total number of Contacts MODIFIED : " << d->mModifiedContactIds.count());
> +            contactsToUpload += prepareContactsToUpload(d->mContactBackend,
> +                                                        d->mModifiedContactIds.values().toSet());
> +
> +            // Contacts deleted locally
> +            LOG_DEBUG("Total number of Contacts DELETED : " << d->mDeletedContactIds.count());
> +            contactsToRemove = prepareContactsToUpload(d->mContactBackend,
> +                                                       d->mDeletedContactIds.values().toSet());
> +
> +            connect(d->mRemoteSource,
> +                    SIGNAL(transactionCommited(QList<QtContacts::QContact>,
> +                                               QList<QtContacts::QContact>,
> +                                               QStringList,Sync::SyncStatus)),
> +                    SLOT(onContactsSavedForFastSync(QList<QtContacts::QContact>,
> +                                                    QList<QtContacts::QContact>,
> +                                                    QStringList,Sync::SyncStatus)));
> +
> +            d->mRemoteSource->transaction();
> +            d->mRemoteSource->saveContacts(contactsToUpload);
> +            d->mRemoteSource->removeContacts(contactsToRemove);
> +            d->mRemoteSource->commit();
> +        }
> +    } else {
> +        emit syncFinished(status);
> +    }
> +}
> +
> +void
> +UContactsClient::onContactsSavedForFastSync(const QList<QtContacts::QContact> &createdContacts,
> +                                            const QList<QtContacts::QContact> &updatedContacts,
> +                                            const QStringList &removedContacts,
> +                                            Sync::SyncStatus status)
> +{
> +    Q_UNUSED(updatedContacts)
> +    Q_UNUSED(removedContacts)
> +    FUNCTION_CALL_TRACE;
> +    Q_D(UContactsClient);
> +
> +    LOG_DEBUG("AFTER UPLOAD(Fast sync):" << status
> +                << "\n\tCreated on remote:" << createdContacts.size()
> +                << "\n\tUpdated on remote:" << updatedContacts.size()
> +                << "\n\tRemoved from remote:" << removedContacts.size());
> +
> +    if ((status != Sync::SYNC_PROGRESS) && (status != Sync::SYNC_STARTED)) {
> +        disconnect(d->mRemoteSource);
> +    }
> +
> +    if ((status == Sync::SYNC_PROGRESS) || (status == Sync::SYNC_DONE)) {
> +        QList<QContact> changedContacts;
> +
> +        changedContacts += createdContacts;
> +        changedContacts += updatedContacts;
> +
> +        updateIdsToLocal(changedContacts);
> +
> +        // sync report
> +        addProcessedItem(Sync::ITEM_ADDED,
> +                         Sync::REMOTE_DATABASE,
> +                         syncTargetId(),
> +                         createdContacts.size());
> +        addProcessedItem(Sync::ITEM_MODIFIED,
> +                         Sync::REMOTE_DATABASE,
> +                         syncTargetId(),
> +                         updatedContacts.size());
> +        addProcessedItem(Sync::ITEM_DELETED,
> +                         Sync::REMOTE_DATABASE,
> +                         syncTargetId(),
> +                         removedContacts.size());
> +
> +        if (status == Sync::SYNC_PROGRESS) {
> +            // sync still in progress
> +            return;
> +        } else {
> +            // WORKARDOUND: 'galera' contacts service take a while to fir contacts

to fir -> for

> +            // changed singal, this can cause a new sync due the storage change plugin
> +            // lets wait 2 secs before fire sync finished signal
> +            QTimer::singleShot(2000, this, SLOT(fireSyncFinishedSucessfully()));
> +            return;
> +        }
> +    }
> +
> +    emit syncFinished(status);
> +}
> +
> +bool
> +UContactsClient::storeToLocalForSlowSync(const QList<QContact> &remoteContacts)
> +{
> +    FUNCTION_CALL_TRACE;
> +
> +    Q_D(UContactsClient);
> +    Q_ASSERT(d->mSlowSync);
> +
> +    bool syncSuccess = false;
> +
> +    LOG_DEBUG ("@@@storeToLocal#SLOW SYNC");
> +    // Since we request for all the deleted contacts, if
> +    // slow sync is performed many times, even deleted contacts
> +    // will appear in *remoteContacts. Filter them out while
> +    // saving them to device
> +    LOG_DEBUG ("TOTAL REMOTE CONTACTS:" << remoteContacts.size());
> +
> +    if (!remoteContacts.isEmpty()) {
> +        QMap<int, UContactsStatus> statusMap;
> +        QList<QContact> cpyContacts(remoteContacts);
> +        if (d->mContactBackend->addContacts(cpyContacts, &statusMap)) {
> +            // TODO: Saving succeeded. Update sync results

Should this TODO be addressed?

> +            syncSuccess = true;
> +
> +            // sync report
> +            addProcessedItem(Sync::ITEM_ADDED,
> +                             Sync::LOCAL_DATABASE,
> +                             syncTargetId(),
> +                             cpyContacts.count());
> +        } else {
> +            // TODO: Saving failed. Update sync results and probably stop sync

Same as above

> +            syncSuccess = false;
> +        }
> +    }
> +
> +    return syncSuccess;
> +}
> +
> +bool
> +UContactsClient::storeToLocalForFastSync(const QList<QContact> &remoteContacts)
> +{
> +    FUNCTION_CALL_TRACE;
> +    Q_D(UContactsClient);
> +    Q_ASSERT(!d->mSlowSync);
> +
> +    bool syncSuccess = false;
> +    LOG_DEBUG ("@@@storeToLocal#FAST SYNC");
> +    QList<QContact> remoteAddedContacts, remoteModifiedContacts, remoteDeletedContacts;
> +    filterRemoteAddedModifiedDeletedContacts(remoteContacts,
> +                                             remoteAddedContacts,
> +                                             remoteModifiedContacts,
> +                                             remoteDeletedContacts);
> +
> +    resolveConflicts(remoteModifiedContacts, remoteDeletedContacts);
> +
> +    if (!remoteAddedContacts.isEmpty()) {
> +        LOG_DEBUG ("***Adding " << remoteAddedContacts.size() << " contacts");
> +        QMap<int, UContactsStatus> addedStatusMap;
> +        syncSuccess = d->mContactBackend->addContacts(remoteAddedContacts, &addedStatusMap);
> +
> +        if (syncSuccess) {
> +            // sync report
> +            addProcessedItem(Sync::ITEM_ADDED,
> +                             Sync::LOCAL_DATABASE,
> +                             syncTargetId(),
> +                             remoteAddedContacts.count());
> +        }
> +    }
> +
> +    if (!remoteModifiedContacts.isEmpty()) {
> +        LOG_DEBUG ("***Modifying " << remoteModifiedContacts.size() << " contacts");
> +        QMap<int, UContactsStatus> modifiedStatusMap =
> +                d->mContactBackend->modifyContacts(&remoteModifiedContacts);
> +
> +        syncSuccess = (modifiedStatusMap.size() > 0);
> +
> +        if (syncSuccess) {
> +            // sync report
> +            addProcessedItem(Sync::ITEM_MODIFIED,
> +                             Sync::LOCAL_DATABASE,
> +                             syncTargetId(),
> +                             modifiedStatusMap.size());
> +        }
> +    }
> +
> +    if (!remoteDeletedContacts.isEmpty()) {
> +        LOG_DEBUG ("***Deleting " << remoteDeletedContacts.size() << " contacts");
> +        QStringList guidList;
> +        for (int i=0; i<remoteDeletedContacts.size(); i++) {
> +            guidList << UContactsBackend::getRemoteId(remoteDeletedContacts.at(i));
> +        }
> +
> +        QStringList localIdList = d->mContactBackend->localIds(guidList);
> +        QMap<int, UContactsStatus> deletedStatusMap =
> +                d->mContactBackend->deleteContacts(localIdList);
> +
> +        syncSuccess = (deletedStatusMap.size() > 0);
> +        if (syncSuccess) {
> +            // sync report
> +            addProcessedItem(Sync::ITEM_DELETED,
> +                             Sync::LOCAL_DATABASE,
> +                             syncTargetId(),
> +                             localIdList.size());
> +        }
> +    }
> +
> +    return syncSuccess;
> +}
> +
> +bool
> +UContactsClient::cleanUp()
> +{
> +    FUNCTION_CALL_TRACE;
> +    //TODO

What isn't being cleaned up at the moment?

> +    return true;
> +}
> +
> +void UContactsClient::connectivityStateChanged(Sync::ConnectivityType aType, bool aState)
> +{
> +    FUNCTION_CALL_TRACE;
> +    LOG_DEBUG("Received connectivity change event:" << aType << " changed to " << aState);
> +}
> +
> +void
> +UContactsClient::loadLocalContacts(const QDateTime &since)
> +{
> +    FUNCTION_CALL_TRACE;
> +    Q_D(UContactsClient);
> +
> +    if (!since.isValid()) {
> +        d->mAllLocalContactIds = d->mContactBackend->getAllContactIds().toSet();
> +
> +        LOG_DEBUG ("Number of contacts:" << d->mAllLocalContactIds.size ());
> +    } else {
> +        d->mAddedContactIds = d->mContactBackend->getAllNewContactIds(since);
> +        d->mModifiedContactIds = d->mContactBackend->getAllModifiedContactIds(since);
> +        d->mDeletedContactIds = d->mContactBackend->getAllDeletedContactIds(since);
> +
> +        LOG_DEBUG ("Number of local added contacts:" << d->mAddedContactIds.size());
> +        LOG_DEBUG ("Number of local modified contacts:" << d->mModifiedContactIds.size());
> +        LOG_DEBUG ("Number of local removed contacts:" << d->mDeletedContactIds.size());
> +    }
> +}
> +
> +void
> +UContactsClient::onStateChanged(Sync::SyncProgressDetail aState)
> +{
> +    FUNCTION_CALL_TRACE;
> +
> +    switch(aState) {
> +    case Sync::SYNC_PROGRESS_SENDING_ITEMS: {
> +        emit syncProgressDetail(getProfileName(), Sync::SYNC_PROGRESS_SENDING_ITEMS);
> +        break;
> +    }
> +    case Sync::SYNC_PROGRESS_RECEIVING_ITEMS: {
> +        emit syncProgressDetail(getProfileName(), Sync::SYNC_PROGRESS_RECEIVING_ITEMS);
> +        break;
> +    }
> +    case Sync::SYNC_PROGRESS_FINALISING: {
> +        emit syncProgressDetail(getProfileName(), Sync::SYNC_PROGRESS_FINALISING);
> +        break;
> +    }
> +    default:
> +        //do nothing
> +        break;
> +    };
> +}
> +
> +void
> +UContactsClient::onSyncFinished(Sync::SyncStatus aState)
> +{
> +    FUNCTION_CALL_TRACE;
> +    Q_D(UContactsClient);
> +
> +    switch(aState)
> +    {
> +        case Sync::SYNC_ERROR:
> +        case Sync::SYNC_AUTHENTICATION_FAILURE:
> +        case Sync::SYNC_DATABASE_FAILURE:
> +        case Sync::SYNC_CONNECTION_ERROR:
> +        case Sync::SYNC_NOTPOSSIBLE:
> +        {
> +            generateResults(false);
> +            emit error(getProfileName(), "", aState);
> +            break;
> +        }
> +        case Sync::SYNC_DONE:
> +            // purge all deleted contacts
> +            d->mContactBackend->purgecontacts();
> +        case Sync::SYNC_ABORTED:
> +        {
> +            generateResults(true);
> +            emit success(getProfileName(), QString::number(aState));
> +            break;
> +        }
> +        case Sync::SYNC_QUEUED:
> +        case Sync::SYNC_STARTED:
> +        case Sync::SYNC_PROGRESS:
> +        default:
> +        {
> +            generateResults(false);
> +            emit error(getProfileName(), "", aState);
> +            break;
> +        }
> +    }
> +}
> +
> +void UContactsClient::fireSyncFinishedSucessfully()
> +{
> +    emit syncFinished(Sync::SYNC_DONE);
> +}
> +
> +
> +Buteo::SyncResults
> +UContactsClient::getSyncResults() const
> +{
> +    return d_ptr->mResults;
> +}
> +
> +QString
> +UContactsClient::authToken() const
> +{
> +    return d_ptr->mAuth->token();
> +}
> +
> +QString
> +UContactsClient::syncTargetId() const
> +{
> +    return d_ptr->mContactBackend->syncTargetId();
> +}
> +
> +QString UContactsClient::accountName() const
> +{
> +    if (d_ptr->mAuth) {
> +        return d_ptr->mAuth->accountDisplayName();
> +    }
> +    return QString();
> +}
> +
> +const QDateTime
> +UContactsClient::lastSyncTime() const
> +{
> +    FUNCTION_CALL_TRACE;
> +
> +    Buteo::ProfileManager pm;
> +    Buteo::SyncProfile* sp = pm.syncProfile (iProfile.name ());
> +    QDateTime lastTime = sp->lastSuccessfulSyncTime();
> +    if (!lastTime.isNull()) {
> +        // return UTC time used by google
> +        return lastTime.addSecs(6).toUTC();
> +    } else {
> +        return lastTime;
> +    }
> +}
> +
> +
> +void
> +UContactsClient::filterRemoteAddedModifiedDeletedContacts(const QList<QContact> remoteContacts,
> +                                                          QList<QContact> &remoteAddedContacts,
> +                                                          QList<QContact> &remoteModifiedContacts,
> +                                                          QList<QContact> &remoteDeletedContacts)
> +{
> +    FUNCTION_CALL_TRACE;
> +    Q_D(UContactsClient);
> +
> +    foreach (const QContact &contact, remoteContacts) {
> +        if (UContactsBackend::deleted(contact)) {
> +            remoteDeletedContacts.append(contact);
> +            continue;
> +        }
> +
> +        QString remoteId = UContactsBackend::getRemoteId(contact);
> +        QContactId localId = d->mContactBackend->entryExists(remoteId);
> +        if (localId.isNull()) {
> +            remoteAddedContacts.append(contact);
> +        } else {
> +            remoteModifiedContacts.append(contact);
> +        }
> +    }
> +}
> +
> +void
> +UContactsClient::resolveConflicts(QList<QContact> &modifiedRemoteContacts,
> +                                  QList<QContact> &deletedRemoteContacts)
> +{
> +    FUNCTION_CALL_TRACE;
> +    Q_D(UContactsClient);
> +
> +    // TODO: Handle conflicts. The steps:
> +    // o Compare the list of local modified/deleted contacts with
> +    //   the list of remote modified/deleted contacts
> +    // o Create a new list (a map maybe) that has the contacts to
> +    //   be modified/deleted using the conflict resolution policy
> +    //   (server-wins, client-wins, add-new)
> +    // o Return the list

Is this still really a TODO?

> +
> +    //QListIterator<GContactEntry*> iter (modifiedRemoteContacts);
> +    QList<QContact>::iterator iter;
> +    for (iter = modifiedRemoteContacts.begin (); iter != modifiedRemoteContacts.end (); ++iter) {
> +        QContact contact = *iter;
> +        QString remoteId = UContactsBackend::getRemoteId(contact);
> +
> +        if (d->mModifiedContactIds.contains(remoteId)) {
> +            if (d->mConflictResPolicy == Buteo::SyncProfile::CR_POLICY_PREFER_LOCAL_CHANGES) {
> +                modifiedRemoteContacts.erase(iter);
> +            } else {
> +                d->mModifiedContactIds.remove(remoteId);
> +            }
> +        }
> +
> +        if (d->mDeletedContactIds.contains(remoteId)) {
> +            if (d->mConflictResPolicy == Buteo::SyncProfile::CR_POLICY_PREFER_LOCAL_CHANGES) {
> +                modifiedRemoteContacts.erase(iter);
> +            } else {
> +                d->mDeletedContactIds.remove(remoteId);
> +            }
> +        }
> +    }
> +
> +    for (iter = deletedRemoteContacts.begin (); iter != deletedRemoteContacts.end (); ++iter) {
> +        QContact contact = *iter;
> +        QString remoteId = UContactsBackend::getRemoteId(contact);
> +
> +        if (d->mModifiedContactIds.contains(remoteId)) {
> +            if (d->mConflictResPolicy == Buteo::SyncProfile::CR_POLICY_PREFER_LOCAL_CHANGES) {
> +                deletedRemoteContacts.erase(iter);
> +            } else {
> +                d->mModifiedContactIds.remove(remoteId);
> +            }
> +        }
> +
> +        if (d->mDeletedContactIds.contains(remoteId)) {
> +            // If the entry is deleted both at the server and
> +            // locally, then just remove it from the lists
> +            // so that no further action need to be taken
> +            deletedRemoteContacts.erase(iter);
> +            d->mDeletedContactIds.remove(remoteId);
> +        }
> +    }
> +}
> +
> +void
> +UContactsClient::updateIdsToLocal(const QList<QContact> &contacts)
> +{
> +    FUNCTION_CALL_TRACE;
> +    Q_D(UContactsClient);
> +    QList<QContact> newList(contacts);
> +    d->mContactBackend->modifyContacts(&newList);
> +}
> +
> +void
> +UContactsClient::addProcessedItem(Sync::TransferType modificationType,
> +                                  Sync::TransferDatabase database,
> +                                  const QString &modifiedDatabase,
> +                                  int count)
> +{
> +    FUNCTION_CALL_TRACE;
> +    Q_D(UContactsClient);
> +
> +    Buteo::DatabaseResults& results = d->mItemResults[modifiedDatabase];
> +    if (database == Sync::LOCAL_DATABASE) {
> +        if (modificationType == Sync::ITEM_ADDED) {
> +            results.iLocalItemsAdded += count;
> +        } else if (modificationType == Sync::ITEM_MODIFIED) {
> +            results.iLocalItemsModified += count;
> +        } else if (modificationType == Sync::ITEM_DELETED) {
> +            results.iLocalItemsDeleted += count;
> +        }
> +    } else if (database == Sync::REMOTE_DATABASE) {
> +        if( modificationType == Sync::ITEM_ADDED) {
> +            results.iRemoteItemsAdded += count;
> +        } else if (modificationType == Sync::ITEM_MODIFIED) {
> +            results.iRemoteItemsModified += count;
> +        } else if (modificationType == Sync::ITEM_DELETED) {
> +            results.iRemoteItemsDeleted += count;
> +        }
> +    }
> +}
> +
> +void
> +UContactsClient::generateResults(bool aSuccessful)
> +{
> +    FUNCTION_CALL_TRACE;
> +    Q_D(UContactsClient);
> +
> +    d->mResults.setMajorCode(aSuccessful ? Buteo::SyncResults::SYNC_RESULT_SUCCESS :
> +                                           Buteo::SyncResults::SYNC_RESULT_FAILED );
> +    d->mResults.setTargetId(iProfile.name());
> +    if (d->mItemResults.isEmpty()) {
> +        LOG_DEBUG("No items transferred");
> +    } else {
> +        QMapIterator<QString, Buteo::DatabaseResults> i(d->mItemResults);
> +        while (i.hasNext())
> +        {
> +            i.next();
> +            const Buteo::DatabaseResults &r = i.value();
> +            Buteo::TargetResults targetResults(i.key(), // Target name
> +                                               Buteo::ItemCounts(r.iLocalItemsAdded,
> +                                                                 r.iLocalItemsDeleted,
> +                                                                 r.iLocalItemsModified),
> +                                               Buteo::ItemCounts(r.iRemoteItemsAdded,
> +                                                                 r.iRemoteItemsDeleted,
> +                                                                 r.iRemoteItemsModified));
> +            d->mResults.addTargetResults(targetResults);
> +            LOG_DEBUG("Items for" << targetResults.targetName() << ":");
> +            LOG_DEBUG("LA:" << targetResults.localItems().added <<
> +                      "LD:" << targetResults.localItems().deleted <<
> +                      "LM:" << targetResults.localItems().modified <<
> +                      "RA:" << targetResults.remoteItems().added <<
> +                      "RD:" << targetResults.remoteItems().deleted <<
> +                      "RM:" << targetResults.remoteItems().modified);
> +        }
> +    }
> +}
> 
> === added file 'buteo-contact-client/UContactsClient.h'
> --- buteo-contact-client/UContactsClient.h	1970-01-01 00:00:00 +0000
> +++ buteo-contact-client/UContactsClient.h	2015-07-20 14:20:21 +0000
> @@ -0,0 +1,164 @@
> +/*
> + * This file is part of buteo-sync-plugins-goole package
> + *
> + * Copyright (C) 2013 Jolla Ltd. and/or its subsidiary(-ies).
> + *               2015 Canonical Ltd
> + *
> + * Contributors: Sateesh Kavuri <sateesh.kavuri at gmail.com>
> + *               Mani Chandrasekar <maninc at gmail.com>
> + *               Renato Araujo Oliveira Filho <renato.filho at canonical.com>
> + *
> + * This library is free software; you can redistribute it and/or
> + * modify it under the terms of the GNU Lesser General Public License
> + * version 2.1 as published by the Free Software Foundation.
> + *
> + * 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
> + * Lesser General Public License for more details.
> + *
> + * You should have received a copy of the GNU Lesser General Public
> + * License along with this library; if not, write to the Free Software
> + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
> + * 02110-1301 USA
> + *
> + */
> +
> +#ifndef UCONTACSTCLIENT_H

UCONTACSTCLIENT_H -> UCONTACTSCLIENT_H

> +#define UCONTACSTCLIENT_H

UCONTACSTCLIENT_H -> UCONTACTSCLIENT_H

> +
> +#include <QNetworkReply>
> +#include <QContact>
> +#include <QList>
> +#include <QPair>
> +
> +#include <ClientPlugin.h>
> +
> +class UContactsClientPrivate;
> +class UAuth;
> +class UAbstractRemoteSource;
> +class UContactsBackend;
> +
> +class UContactsClient : public Buteo::ClientPlugin
> +{
> +    Q_OBJECT
> +    Q_DECLARE_PRIVATE(UContactsClient)
> +
> +public:
> +
> +    /*! \brief Constructor
> +     *
> +     * @param aPluginName Name of this client plugin
> +     * @param aProfile Sync profile
> +     * @param aCbInterface Pointer to the callback interface
> +     * @param authenticator a instance of UAuth class to be used during the authentication
> +     */
> +    UContactsClient(const QString& aPluginName,
> +                    const Buteo::SyncProfile &aProfile,
> +                    Buteo::PluginCbInterface *aCbInterface,
> +                    const QString& serviceName);
> +
> +    /*! \brief Destructor
> +     *
> +     * Call uninit before destroying the object.
> +     */
> +    virtual ~UContactsClient();
> +
> +    //! @see SyncPluginBase::init
> +    virtual bool init();
> +
> +    //! @see SyncPluginBase::uninit
> +    virtual bool uninit();
> +
> +    //! @see ClientPlugin::startSync
> +    virtual bool startSync();
> +
> +    //! @see SyncPluginBase::abortSync
> +    virtual void abortSync(Sync::SyncStatus aStatus = Sync::SYNC_ABORTED);
> +
> +    //! @see SyncPluginBase::getSyncResults
> +    virtual Buteo::SyncResults getSyncResults() const;
> +
> +    //! @see SyncPluginBase::cleanUp
> +    virtual bool cleanUp();
> +
> +public slots:
> +    //! @see SyncPluginBase::connectivityStateChanged
> +    virtual void connectivityStateChanged( Sync::ConnectivityType aType,
> +                                           bool aState );
> +
> +protected:
> +    QString authToken() const;
> +    QString syncTargetId() const;
> +    QString accountName() const;
> +
> +    // Must be implemented by the plugins
> +    virtual UAbstractRemoteSource* createRemoteSource(QObject *parent) const = 0;
> +    virtual QVariantMap remoteSourceProperties() const = 0;
> +
> +    virtual UContactsBackend* createContactsBackend(QObject *parent) const;
> +    virtual UAuth* crateAuthenticator(QObject *parent) const;
> +
> +    virtual bool isReadyToSync() const;
> +    virtual const QDateTime lastSyncTime() const;
> +
> +signals:
> +    void stateChanged(Sync::SyncProgressDetail progress);
> +    void itemProcessed(Sync::TransferType type,
> +                       Sync::TransferDatabase db,
> +                       int committedItems);
> +    void syncFinished(Sync::SyncStatus);
> +
> +
> +private:
> +    QScopedPointer<UContactsClientPrivate> d_ptr;
> +
> +    void loadLocalContacts(const QDateTime &since);
> +
> +    bool initConfig();
> +    void generateResults(bool aSuccessful);
> +    void updateIdsToLocal(const QList<QtContacts::QContact> &contacts);
> +    void filterRemoteAddedModifiedDeletedContacts(const QList<QTCONTACTS_PREPEND_NAMESPACE(QContact)> remoteContacts,
> +                                                  QList<QTCONTACTS_PREPEND_NAMESPACE(QContact)> &remoteAddedContacts,
> +                                                  QList<QTCONTACTS_PREPEND_NAMESPACE(QContact)> &remoteModifiedContacts,
> +                                                  QList<QTCONTACTS_PREPEND_NAMESPACE(QContact)> &remoteDeletedContacts);
> +    void resolveConflicts(QList<QTCONTACTS_PREPEND_NAMESPACE(QContact)> &modifiedRemoteContacts,
> +                          QList<QTCONTACTS_PREPEND_NAMESPACE(QContact)> &deletedRemoteContacts);
> +    void addProcessedItem(Sync::TransferType modificationType,
> +                          Sync::TransferDatabase database,
> +                          const QString &modifiedDatabase,
> +                          int count = 1);
> +    QList<QTCONTACTS_PREPEND_NAMESPACE(QContact)> prepareContactsToUpload(UContactsBackend *backend,
> +                                                                          const QSet<QTCONTACTS_PREPEND_NAMESPACE(QContactId)> &ids);
> +
> +    /* slow sync */
> +    bool storeToLocalForSlowSync(const QList<QTCONTACTS_PREPEND_NAMESPACE(QContact)> &remoteContacts);
> +
> +    /* fast sync */
> +    bool storeToLocalForFastSync(const QList<QTCONTACTS_PREPEND_NAMESPACE(QContact)> &remoteContacts);
> +
> +private slots:
> +    bool start();
> +    void onAuthenticationError();
> +    void onStateChanged(Sync::SyncProgressDetail progress);
> +    void onSyncFinished(Sync::SyncStatus status);
> +    void fireSyncFinishedSucessfully();
> +
> +    /* slow sync */
> +    void onRemoteContactsFetchedForSlowSync(const QList<QtContacts::QContact> contacts,
> +                                            Sync::SyncStatus status);
> +    void onContactsSavedForSlowSync(const QList<QtContacts::QContact> &createdContacts,
> +                                    const QList<QtContacts::QContact> &updatedContacts,
> +                                    const QStringList &removedContacts,
> +                                    Sync::SyncStatus status);
> +    /* fast sync */
> +    void onRemoteContactsFetchedForFastSync(const QList<QtContacts::QContact> contacts,
> +                                            Sync::SyncStatus status);
> +    void onContactsSavedForFastSync(const QList<QtContacts::QContact> &createdContacts,
> +                                    const QList<QtContacts::QContact> &updatedContacts,
> +                                    const QStringList &removedContacts,
> +                                    Sync::SyncStatus status);
> +
> +};
> +
> +#endif // UCONTACTCLIENT_H

UCONTACTCLIENT_H -> UCONTACTSCLIENT_H

> 
> === added file 'buteo-contact-client/UContactsCustomDetail.h'
> --- buteo-contact-client/UContactsCustomDetail.h	1970-01-01 00:00:00 +0000
> +++ buteo-contact-client/UContactsCustomDetail.h	2015-07-20 14:20:21 +0000
> @@ -0,0 +1,52 @@
> +/*
> + * This file is part of buteo-sync-plugins-goole package
> + *
> + * Copyright (C) 2013 Jolla Ltd. and/or its subsidiary(-ies).
> + *               2015 Canonical Ltd
> + *
> + * Contributors: Sateesh Kavuri <sateesh.kavuri at gmail.com>
> + *               Mani Chandrasekar <maninc at gmail.com>
> + *               Renato Araujo Oliveira Filho <renato.filho at canonical.com>
> + *
> + * This library is free software; you can redistribute it and/or
> + * modify it under the terms of the GNU Lesser General Public License
> + * version 2.1 as published by the Free Software Foundation.
> + *
> + * 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
> + * Lesser General Public License for more details.
> + *
> + * You should have received a copy of the GNU Lesser General Public
> + * License along with this library; if not, write to the Free Software
> + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
> + * 02110-1301 USA
> + *
> + */
> +
> +#ifndef UCONTACTSCUSTOMDETAIL_H
> +#define UCONTACTSCUSTOMDETAIL_H
> +
> +#include <QString>
> +
> +#include <QContact>
> +#include <QContactExtendedDetail>
> +
> +QTCONTACTS_USE_NAMESPACE
> +
> +class UContactsCustomDetail
> +{
> +public:
> +    // Ubuntu fields
> +    static const QString FieldContactETag;
> +    static const QString FieldRemoteId;
> +    static const QString FieldGroupMembershipInfo;
> +    static const QString FieldDeletedAt;
> +    static const QString FieldCreatedAt;
> +    static const QString FieldContactAvatarETag;
> +
> +    static QContactExtendedDetail getCustomField(const QContact &contact, const QString &name);
> +    static void setCustomField(QContact &contact, const QString &name, const QVariant &value);
> +};
> +
> +#endif // GCONTACTCUSTOMDETAIL_H

GCONTACTCUSTOMDETAIL_H -> UCONTACTSCUSTOMDETAIL_H

> 
> === added file 'config.h.in'
> --- config.h.in	1970-01-01 00:00:00 +0000
> +++ config.h.in	2015-07-20 14:20:21 +0000
> @@ -0,0 +1,11 @@
> +#ifndef __CONFIG_H__
> +#define __CONFIG_H__
> +
> +#include <QString>
> +
> +const QString GOOGLE_CONTACTS_CLIENT_ID          ("759250720802-4sii0me9963n9fdqdmi7cepn6ub8luoh.apps.googleusercontent.com");
> +const QString GOOGLE_CONTACTS_CLIENT_SECRET      ("juFngKUcuhB7IRQqHtSLavqJ");

Is it okay for the secret to be kept in source control?

> +const QString GOOGLE_ETAG_DETAIL                 ("X-GOOGLE-ETAG");
> +const QString QCONTACTS_BACKEND_NAME             ("galera");
> +
> +#endif


-- 
https://code.launchpad.net/~renatofilho/buteo-sync-plugins-contacts/new-code/+merge/264335
Your team Ubuntu Phablet Team is subscribed to branch lp:buteo-sync-plugins-contacts.



More information about the Ubuntu-reviews mailing list