buteo-sync-plugins-contacts-0.1+16.04.20151211/0000755000015300001610000000000012632561656021277 5ustar pbuserpbgroup00000000000000buteo-sync-plugins-contacts-0.1+16.04.20151211/cmake/0000755000015300001610000000000012632561656022357 5ustar pbuserpbgroup00000000000000buteo-sync-plugins-contacts-0.1+16.04.20151211/cmake/lcov.cmake0000644000015300001610000000517612632560105024321 0ustar pbuserpbgroup00000000000000# - This module creates a new 'lcov' target which generates # a coverage analysis html output. # LCOV is a graphical front-end for GCC's coverage testing tool gcov. Please see # http://ltp.sourceforge.net/coverage/lcov.php # # Usage: you must add an option to your CMakeLists.txt to build your application # with coverage support. Then you need to include this file to the lcov target. # # Example: # IF(BUILD_WITH_COVERAGE) # SET(CMAKE_C_FLAGS "-g -O0 -Wall -fprofile-arcs -ftest-coverage") # SET(CMAKE_CXX_FLAGS "-g -O0 -Wall -fprofile-arcs -ftest-coverage") # SET(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -fprofile-arcs -ftest-coverage -lgcov") # include(${CMAKE_SOURCE_DIR}/cmake/lcov.cmake) # ENDIF(BUILD_WITH_COVERAGE) #============================================================================= # Copyright 2010 ascolab GmbH # # Distributed under the OSI-approved BSD License (the "License"); # see accompanying file Copyright.txt for details. # # This software is distributed WITHOUT ANY WARRANTY; without even the # implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. # See the License for more information. #============================================================================= # (To distributed this file outside of CMake, substitute the full # License text for the above reference.) set(REMOVE_PATTERN q*.h *.moc moc_*.cpp locale_facets.h new move.h qcontactmemorybackend.cpp limits /usr/include/c++/4.9/bits/* /usr/include/buteosyncfw5/* ) ## lcov target ADD_CUSTOM_TARGET(lcov) ADD_CUSTOM_COMMAND(TARGET lcov COMMAND mkdir -p coverage WORKING_DIRECTORY ${CMAKE_BINARY_DIR} ) ADD_CUSTOM_COMMAND(TARGET lcov COMMAND lcov --directory . --zerocounters WORKING_DIRECTORY ${CMAKE_BINARY_DIR} ) ADD_CUSTOM_COMMAND(TARGET lcov COMMAND make test WORKING_DIRECTORY ${CMAKE_BINARY_DIR} ) ADD_CUSTOM_COMMAND(TARGET lcov COMMAND lcov --directory . --capture --output-file ./coverage/stap_all.info --no-checksum --compat-libtool WORKING_DIRECTORY ${CMAKE_BINARY_DIR} ) ADD_CUSTOM_COMMAND(TARGET lcov COMMAND lcov --directory . -r ./coverage/stap_all.info ${REMOVE_PATTERN} --output-file ./coverage/stap.info WORKING_DIRECTORY ${CMAKE_BINARY_DIR} ) ADD_CUSTOM_COMMAND(TARGET lcov COMMAND genhtml -o ./coverage --title "Code Coverage" --legend --show-details --demangle-cpp ./coverage/stap.info WORKING_DIRECTORY ${CMAKE_BINARY_DIR} ) ADD_CUSTOM_COMMAND(TARGET lcov COMMAND echo "Open ${CMAKE_BINARY_DIR}/coverage/index.html to view the coverage analysis results." WORKING_DIRECTORY ${CMAKE_BINARY_DIR} ) buteo-sync-plugins-contacts-0.1+16.04.20151211/tests/0000755000015300001610000000000012632561656022441 5ustar pbuserpbgroup00000000000000buteo-sync-plugins-contacts-0.1+16.04.20151211/tests/unittest/0000755000015300001610000000000012632561656024320 5ustar pbuserpbgroup00000000000000buteo-sync-plugins-contacts-0.1+16.04.20151211/tests/unittest/MockRemoteSource.cpp0000644000015300001610000002005412632560105030237 0ustar pbuserpbgroup00000000000000/* * This file is part of buteo-gcontact-plugin package * * Copyright (C) 2015 Canonical Ltd. * * Contributors: Renato Araujo Oliveira Filho * * 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 "MockRemoteSource.h" #include #include #include #include #include #include #include #include #include #include #include #include #define LOCALID_FIELD_NAME "X-LOCALID" QTVERSIT_USE_NAMESPACE QTCONTACTS_USE_NAMESPACE MockRemoteSource::MockRemoteSource(QObject *parent) : UAbstractRemoteSource(parent), m_pageSize(-1) { QMap params; params.insert("id", "remote-source"); m_manager.reset(new QContactManager("mock", params)); connect(m_manager.data(), SIGNAL(contactsAdded(QList)), this, SLOT(onContactCreated(QList))); connect(m_manager.data(), SIGNAL(contactsChanged(QList)), this, SLOT(onContactChanged(QList))); connect(m_manager.data(), SIGNAL(contactsRemoved(QList)), this, SLOT(onContactRemoved(QList))); } MockRemoteSource::~MockRemoteSource() { } bool MockRemoteSource::init(const QVariantMap &properties) { m_properties = properties; m_initialized = true; return true; } void MockRemoteSource::abort() { } QContactManager *MockRemoteSource::manager() const { return m_manager.data(); } void MockRemoteSource::setPageSize(int pageSize) { m_pageSize = pageSize; } bool MockRemoteSource::exixts(const QContactId &remoteId) const { if (remoteId.isNull()) { return false; } QContact c = m_manager->contact(remoteId); return !c.isEmpty(); } void MockRemoteSource::fetchContacts(const QDateTime &since, bool includeDeleted, bool fetchAvatar) { Q_UNUSED(fetchAvatar); QList contacts; if (since.isValid()) { QContactUnionFilter iFilter; QContactChangeLogFilter changed(QContactChangeLogFilter::EventChanged); changed.setSince(since); QContactChangeLogFilter added(QContactChangeLogFilter::EventAdded); added.setSince(since); iFilter << changed << added; if (includeDeleted) { QContactChangeLogFilter removed(QContactChangeLogFilter::EventRemoved); removed.setSince(since); iFilter << removed; } contacts = m_manager->contacts(iFilter); } else { contacts = m_manager->contacts(); } //simulate page fetch QList localContacts = toLocalContacts(contacts); if (m_pageSize > 0) { while(!localContacts.isEmpty()) { int pageSize = qMin(localContacts.size(), m_pageSize); emit contactsFetched(localContacts.mid(0, pageSize), localContacts.size() > pageSize ? Sync::SYNC_PROGRESS : Sync::SYNC_DONE, -1.0); localContacts = localContacts.mid(pageSize); } } else { emit contactsFetched(localContacts, Sync::SYNC_DONE, -1.0); } } int MockRemoteSource::count() const { return m_manager->contactIds().size(); } void MockRemoteSource::saveContactsNonBatch(const QList contacts) { QList remoteContacts = toRemoteContact(contacts); m_manager->saveContacts(&remoteContacts); } void MockRemoteSource::removeContactsNonBatch(const QList contacts) { QList ids; foreach(const QContact &contact, contacts) { QString remoteId = UContactsBackend::getRemoteId(contact); if (!remoteId.isEmpty()) { ids << QContactId::fromString(remoteId); } } m_manager->removeContacts(ids); } void MockRemoteSource::batch(const QList &contactsToCreate, const QList &contactsToUpdate, const QList &contactsToRemove) { QList createdContacts; QList updatedContacts; QStringList removedContacts; // create QList remoteContacts = toRemoteContact(contactsToCreate); m_manager->saveContacts(&remoteContacts); for(int i=0; i < remoteContacts.size(); i++) { QContact newContact(remoteContacts.at(i)); // local id QContactGuid guid = newContact.detail(); guid.setGuid(contactsToCreate.at(i).id().toString()); newContact.saveDetail(&guid); // remote id UContactsBackend::setRemoteId(newContact, remoteContacts.at(i).id().toString()); createdContacts << newContact; } remoteContacts.clear(); // update remoteContacts = toRemoteContact(contactsToUpdate); m_manager->saveContacts(&remoteContacts); for(int i=0; i < remoteContacts.size(); i++) { QContact newContact(remoteContacts.at(i)); // local id QContactGuid guid = newContact.detail(); guid.setGuid(contactsToUpdate.at(i).id().toString()); newContact.saveDetail(&guid); // remote id UContactsBackend::setRemoteId(newContact, remoteContacts.at(i).id().toString()); updatedContacts << newContact; } remoteContacts.clear(); // remove QList removedIds; foreach(const QContact &contact, contactsToRemove) { QString remoteId = UContactsBackend::getRemoteId(contact); if (!remoteId.isEmpty()) { removedIds << QContactId::fromString(remoteId); removedContacts << remoteId; } } m_manager->removeContacts(removedIds); emit transactionCommited(createdContacts, updatedContacts, removedContacts, QMap(), Sync::SYNC_DONE); } void MockRemoteSource::onContactChanged(const QList &) { } void MockRemoteSource::onContactRemoved(const QList &) { } void MockRemoteSource::onContactCreated(const QList &) { } QList MockRemoteSource::toLocalContacts(const QList contacts) const { QList result; foreach(const QContact &c, contacts) { QContact localContact(c); localContact.setId(QContactId()); UContactsCustomDetail::setCustomField(localContact, UContactsCustomDetail::FieldRemoteId, c.id().toString()); result << localContact; } return result; } QList MockRemoteSource::toRemoteContact(const QList contacts) const { QList result; foreach(const QContact &c, contacts) { QContactId remoteId; QContact remoteContact(c); foreach(QContactExtendedDetail d, c.details()) { if (d.name() == UContactsCustomDetail::FieldRemoteId) { // remove remote-id from remote contact (we do not want to save that) remoteContact.removeDetail(&d); remoteId = QContactId::fromString(d.data().toString()); } } if (exixts(remoteId)) { remoteContact.setId(remoteId); } else { remoteContact.setId(QContactId()); } result << remoteContact; } return result; } buteo-sync-plugins-contacts-0.1+16.04.20151211/tests/unittest/TestContactsClient.cpp0000644000015300001610000000462712632560105030576 0ustar pbuserpbgroup00000000000000/* * This file is part of buteo-gcontact-plugin package * * Copyright (C) 2015 Canonical Ltd. * * Contributors: Renato Araujo Oliveira Filho * * 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 "TestContactsClient.h" #include "MockRemoteSource.h" #include "MockAuthenticator.h" #include #include #include #include #include #include #include TestContactsClient::TestContactsClient(const QString& aPluginName, const Buteo::SyncProfile& aProfile, Buteo::PluginCbInterface *aCbInterface) : UContactsClient(aPluginName, aProfile, aCbInterface, "test-plugin"), m_remoteSource(0), m_authenticator(0) { } TestContactsClient::~TestContactsClient() { } QVariantMap TestContactsClient::remoteSourceProperties() const { QVariantMap remoteProperties; return remoteProperties; } UAbstractRemoteSource *TestContactsClient::createRemoteSource(QObject *parent) const { const_cast(this)->m_remoteSource= new MockRemoteSource(parent); return qobject_cast(m_remoteSource.data()); } UContactsBackend *TestContactsClient::createContactsBackend(QObject *parent) const { const_cast(this)->m_localSource = new UContactsBackend(QStringLiteral("mock"), parent); return m_localSource.data(); } UAuth *TestContactsClient::crateAuthenticator(QObject *parent) const { const_cast(this)->m_authenticator = new MockAuthenticator(parent); return qobject_cast(m_authenticator.data()); } const QDateTime TestContactsClient::lastSyncTime() const { return m_lastSyncTime; } buteo-sync-plugins-contacts-0.1+16.04.20151211/tests/unittest/TestContactsMain.cpp0000644000015300001610000005362512632560105030246 0ustar pbuserpbgroup00000000000000/* * This file is part of buteo-gcontact-plugin package * * Copyright (C) 2015 Canonical Ltd. * * Contributors: Renato Araujo Oliveira Filho * * 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 "TestContactsClient.h" #include "MockAuthenticator.h" #include "MockRemoteSource.h" #include "config-tests.h" #include #include #include #include #include #include #include QTVERSIT_USE_NAMESPACE class ContactSyncTest : public QObject { Q_OBJECT private: TestContactsClient *m_client; Buteo::SyncProfile *loadFromXmlFile(const QString &fName) { QFile file(fName); if (!file.open(QIODevice::ReadOnly)) { return 0; } // no else QDomDocument doc; if (!doc.setContent(&file)) { file.close(); return 0; } // no else file.close(); return new Buteo::SyncProfile(doc.documentElement()); } QList toContacts(const QString &vcardFile, const QDateTime &createdAt = QDateTime()) { QFile file(vcardFile); if (!file.open(QIODevice::ReadOnly)) { return QList(); } QVersitReader reader(file.readAll()); reader.startReading(); reader.waitForFinished(); if (reader.results().count() > 0) { QList documents = reader.results(); QVersitContactImporter contactImporter; if (!contactImporter.importDocuments(documents)) { qWarning() << "Fail to import contacts"; return QList(); } QList contacts = contactImporter.contacts(); if (createdAt.isValid()) { for(int i=0; i < contacts.size(); i++) { QContact &c = contacts[i]; QContactTimestamp ts = c.detail(); ts.setCreated(createdAt); c.saveDetail(&ts); } } return contacts; } return QList(); } void importContactsFromVCardFile(QContactManager *target, const QString &vcardFile, const QDateTime &createdAt = QDateTime()) { QList contacts = toContacts(vcardFile, createdAt); QVERIFY(contacts.size() > 0); QVERIFY(target->saveContacts(&contacts)); } QString toVCard(const QList &contacts) { QByteArray vcard; QVersitWriter writer(&vcard); QVersitContactExporter contactExporter; if (!contactExporter.exportContacts(contacts)) { qWarning() << "Fail to export contacts"; return ""; } QList documents = contactExporter.documents(); writer.startWriting(documents); writer.waitForFinished(); return vcard; } QString toVCard(const QContact &c) { return toVCard(QList() << c); } bool compareLines(const QString &vcard1, const QString &vcardFile) { QFile file(vcardFile); if (!file.open(QIODevice::ReadOnly)) { qWarning() << "Fail to open file:" << vcardFile; return false; } QStringList vcard1Lines = vcard1.split('\n'); QString vcardFileData = QString(file.readAll()); QStringList vcard2Lines = vcardFileData.split('\n'); file.close(); if (vcard1Lines.size() != vcard2Lines.size()) { qWarning() << "Vcard files size does not match:" << "\n\tExpected:" << vcard2Lines.size() << "\n\tCurrent:" << vcard1Lines.size(); qWarning() << "VCARD1\n" << vcard1; qWarning() << "VCARD2\n" << vcardFileData; return false; } for(int l=0; l < vcard1Lines.size(); l++) { if (vcard1Lines[l] != vcard2Lines[l]) { qWarning() << "Vcard lines are diff:" << "\n\t" << vcard2Lines[l] << "\n\t" << vcard1Lines[l]; return false; } } return true; } private Q_SLOTS: void initTestCase() { QCoreApplication::addLibraryPath(MOCK_PLUGIN_PATH); QVERIFY(QContactManager::availableManagers().contains("mock")); } void init() { Buteo::SyncProfile *syncP = loadFromXmlFile(PROFILE_TEST_FN); QVERIFY(syncP); m_client = new TestContactsClient("test-plugin", *syncP, 0); QContact c; QContactRingtone r; r.setAudioRingtoneUrl(QUrl("http://audio.com")); r.setVibrationRingtoneUrl(QUrl("http://vibrating.com")); r.setVideoRingtoneUrl(QUrl("http://video.com")); c.saveDetail(&r); } void cleanup() { delete m_client; m_client = 0; } void testInitialization() { QVERIFY(m_client->init()); QVERIFY(m_client->m_authenticator); QVERIFY(m_client->m_authenticator->m_initialized); QVERIFY(m_client->m_remoteSource); } void testFailToAuthenticate() { QVERIFY(m_client->init()); QSignalSpy syncFinishedSpy(m_client, SIGNAL(syncFinished(Sync::SyncStatus))); // simulate fail in the authentication m_client->m_authenticator->failed(); // check if the sync finished was fired with the correct arg QTRY_COMPARE(syncFinishedSpy.count(), 1); QList arguments = syncFinishedSpy.takeFirst(); QCOMPARE(arguments.at(0).toInt(), int(Sync::SYNC_AUTHENTICATION_FAILURE)); } void testSync_data() { QTest::addColumn("createdAt"); QTest::addColumn("lastSyncTime"); QTest::addColumn("currentDateTime"); QTest::addColumn("remoteFile"); QTest::addColumn("remoteInitalCount"); QTest::addColumn("localFile"); QTest::addColumn("localInitalCount"); QTest::addColumn("finalRemoteFile"); QTest::addColumn("finalLocalFile"); QTest::newRow("slow sync new database") << QDateTime() << QDateTime() << QDateTime::currentDateTime() << "new_database_remote.vcf" << 3 << "new_database_local.vcf" << 1 << "new_database_remote_result.vcf" << "new_database_local_result.vcf"; QTest::newRow("slow local previous synced") << QDateTime() << QDateTime() << QDateTime::currentDateTime() << "local_previous_synced_remote.vcf" << 3 << "local_previous_synced_local.vcf" << 1 << "local_previous_synced_remote_result.vcf" << "local_previous_synced_local_result.vcf"; QTest::newRow("fast sync without changes") << QDateTime::fromString("2015-06-15T08:00:00", Qt::ISODate) << QDateTime::fromString("2015-06-15T10:30:00", Qt::ISODate) << QDateTime::currentDateTime() << "fast_sync_without_changes_remote.vcf" << 4 << "fast_sync_without_changes_local.vcf" << 4 << "fast_sync_without_changes_remote_result.vcf" << "fast_sync_without_changes_local_result.vcf"; QTest::newRow("fast sync with a remote change") << QDateTime::fromString("2015-06-15T08:00:00", Qt::ISODate) << QDateTime::fromString("2015-06-15T08:10:00", Qt::ISODate) << QDateTime::fromString("2015-06-15T10:00:00", Qt::ISODate) << "fast_sync_with_a_remote_change_remote.vcf" << 4 << "fast_sync_with_a_remote_change_local.vcf" << 4 << "fast_sync_with_a_remote_change_remote_result.vcf" << "fast_sync_with_a_remote_change_local_result.vcf"; QTest::newRow("fast sync with a local change") << QDateTime::fromString("2015-06-15T08:00:00", Qt::ISODate) << QDateTime::fromString("2015-06-15T08:10:00", Qt::ISODate) << QDateTime::fromString("2015-06-15T10:00:00", Qt::ISODate) << "fast_sync_with_a_local_change_remote.vcf" << 4 << "fast_sync_with_a_local_change_local.vcf" << 4 << "fast_sync_with_a_local_change_remote_result.vcf" << "fast_sync_with_a_local_change_local_result.vcf"; QTest::newRow("fast sync with a local removal") << QDateTime::fromString("2015-06-15T08:00:00", Qt::ISODate) << QDateTime::fromString("2015-06-15T08:10:00", Qt::ISODate) << QDateTime::fromString("2015-06-15T10:00:00", Qt::ISODate) << "fast_sync_with_a_local_removal_remote.vcf" << 4 << "fast_sync_with_a_local_removal_local.vcf" << 3 << "fast_sync_with_a_local_removal_remote_result.vcf" << "fast_sync_with_a_local_removal_local_result.vcf"; QTest::newRow("fast sync with a remote removal") << QDateTime::fromString("2015-06-15T08:00:00", Qt::ISODate) << QDateTime::fromString("2015-06-15T08:10:00", Qt::ISODate) << QDateTime::fromString("2015-06-15T10:00:00", Qt::ISODate) << "fast_sync_with_a_remote_removal_remote.vcf" << 3 << "fast_sync_with_a_remote_removal_local.vcf" << 4 << "fast_sync_with_a_remote_removal_remote_result.vcf" << "fast_sync_with_a_remote_removal_local_result.vcf"; QTest::newRow("fast sync changed both sides") << QDateTime::fromString("2015-06-15T08:00:00", Qt::ISODate) << QDateTime::fromString("2015-06-15T08:10:00", Qt::ISODate) << QDateTime::fromString("2015-06-15T10:00:00", Qt::ISODate) << "fast_sync_changed_both_sides_remote.vcf" << 3 << "fast_sync_changed_both_sides_local.vcf" << 3 << "fast_sync_changed_both_sides_remote_result.vcf" << "fast_sync_changed_both_sides_local_result.vcf"; QTest::newRow("fast sync delete same contact") << QDateTime::fromString("2015-06-15T08:00:00", Qt::ISODate) << QDateTime::fromString("2015-06-15T08:10:00", Qt::ISODate) << QDateTime::fromString("2015-06-15T10:00:00", Qt::ISODate) << "fast_sync_delete_same_contact_remote.vcf" << 3 << "fast_sync_delete_same_contact_local.vcf" << 3 << "fast_sync_delete_same_contact_remote_result.vcf" << "fast_sync_delete_same_contact_local_result.vcf"; QTest::newRow("fast sync with a new local contact") << QDateTime() << QDateTime::fromString("2015-06-15T08:10:00", Qt::ISODate) << QDateTime::fromString("2015-06-15T10:00:00", Qt::ISODate) << "fast_sync_with_new_local_contact_remote.vcf" << 1 << "fast_sync_with_new_local_contact_local.vcf" << 2 << "fast_sync_with_new_local_contact_remote_result.vcf" << "fast_sync_with_new_local_contact_local_result.vcf"; } void testSync() { QFETCH(QDateTime, createdAt); QFETCH(QDateTime, lastSyncTime); QFETCH(QDateTime, currentDateTime); QFETCH(QString, remoteFile); QFETCH(int, remoteInitalCount); QFETCH(QString, localFile); QFETCH(int, localInitalCount); QFETCH(QString, finalRemoteFile); QFETCH(QString, finalLocalFile); m_client->m_lastSyncTime = lastSyncTime; m_client->init(); // prepare remote source m_client->m_remoteSource->manager()->setProperty("CURRENT_DATE_TIME", currentDateTime); importContactsFromVCardFile(m_client->m_remoteSource->manager(), TEST_DATA_DIR + remoteFile, createdAt); QTRY_COMPARE(m_client->m_remoteSource->count(), remoteInitalCount); // prepare local source importContactsFromVCardFile(m_client->m_localSource->manager(), TEST_DATA_DIR + localFile, createdAt); QTRY_COMPARE(m_client->m_localSource->manager()->contactIds().count(), localInitalCount); m_client->m_localSource->reloadCache(); QTRY_COMPARE(m_client->m_localSource->getAllContactIds().count(), localInitalCount); QSignalSpy syncFinishedSpy(m_client, SIGNAL(syncFinished(Sync::SyncStatus))); m_client->startSync(); // remote source will be initialized after success QTRY_VERIFY(m_client->m_remoteSource->m_initialized); // wait for sync finish and check sync result QTRY_COMPARE(syncFinishedSpy.count(), 1); QList arguments = syncFinishedSpy.takeFirst(); QCOMPARE(arguments.at(0).toInt(), int(Sync::SYNC_DONE)); // compare saved contacts with expected files QList local = m_client->m_localSource->manager()->contacts(); QList remote = m_client->m_remoteSource->manager()->contacts(); QVERIFY(local.size() > 0); QVERIFY(remote.size() > 0); QVERIFY(compareLines(toVCard(local), TEST_DATA_DIR + finalLocalFile)); QVERIFY(compareLines(toVCard(remote), TEST_DATA_DIR + finalRemoteFile)); QVERIFY(m_client->cleanUp()); } void testFetchWithPagination_data() { QTest::addColumn("pageSize"); QTest::addColumn("contactFetchedCount"); QTest::newRow("page bigger than contact list") << 30 << 1; QTest::newRow("page small than contact list") << 10 << 2; QTest::newRow("page division exactly") << 5 << 3; } void testFetchWithPagination() { QFETCH(int, pageSize); QFETCH(int, contactFetchedCount); m_client->init(); // prepare remote source m_client->m_remoteSource->setPageSize(pageSize); importContactsFromVCardFile(m_client->m_remoteSource->manager(), TEST_DATA_DIR + QStringLiteral("slow_sync_with_pages_remote.vcf"), QDateTime::currentDateTime()); QTRY_COMPARE(m_client->m_remoteSource->count(), 15); QCOMPARE(m_client->m_localSource->getAllContactIds().count(), 0); QSignalSpy contactFetched(m_client->m_remoteSource.data(), SIGNAL(contactsFetched(QList,Sync::SyncStatus, qreal))); QSignalSpy syncFinishedSpy(m_client, SIGNAL(syncFinished(Sync::SyncStatus))); m_client->startSync(); // we will receive one contactFetched for each page QTRY_COMPARE(contactFetched.count(), contactFetchedCount); // wait for sync finished signal QTRY_COMPARE(syncFinishedSpy.count(), 1); // check if all contacts was stored in local database QCOMPARE(m_client->m_localSource->getAllContactIds().count(), 15); } void testSlowSyncWithAnEmptyLocalDatabase() { m_client->init(); // populate remote database importContactsFromVCardFile(m_client->m_remoteSource->manager(), TEST_DATA_DIR + QStringLiteral("new_database_local.vcf"), QDateTime::currentDateTime()); QTRY_COMPARE(m_client->m_remoteSource->count(), 1); QCOMPARE(m_client->m_localSource->getAllContactIds().count(), 0); QSignalSpy remoteChangedSignal(m_client->m_remoteSource.data(), SIGNAL(contactsChanged(QList,Sync::SyncStatus))); QSignalSpy remoteRemovedSignal(m_client->m_remoteSource.data(), SIGNAL(contactsRemoved(QStringList,Sync::SyncStatus))); QSignalSpy remoteCreatedSignal(m_client->m_remoteSource.data(), SIGNAL(contactsCreated(QList,Sync::SyncStatus))); QSignalSpy remoteTransactionSignal(m_client->m_remoteSource.data(), SIGNAL(transactionCommited(QList, QList, QStringList, QMap, Sync::SyncStatus))); QSignalSpy syncFinishedSpy(m_client, SIGNAL(syncFinished(Sync::SyncStatus))); m_client->startSync(); // wait for sync finished signal QTRY_COMPARE(syncFinishedSpy.count(), 1); // check if no change was made on remote side QCOMPARE(remoteChangedSignal.count(), 0); QCOMPARE(remoteRemovedSignal.count(), 0); QCOMPARE(remoteCreatedSignal.count(), 0); // transaction commit will be fired once with empty list QCOMPARE(remoteTransactionSignal.count(), 1); QList arguments = remoteTransactionSignal.takeFirst(); QList createdContacts = arguments.at(0).value >(); QList changedContacts = arguments.at(1).value >(); QStringList removedContacts = arguments.at(2).value(); QMap errorMap = arguments.at(3).value >(); QVERIFY(createdContacts.isEmpty()); QVERIFY(changedContacts.isEmpty()); QVERIFY(removedContacts.isEmpty()); QVERIFY(errorMap.isEmpty()); QVERIFY(m_client->cleanUp()); } }; QTEST_MAIN(ContactSyncTest) #include "TestContactsMain.moc" buteo-sync-plugins-contacts-0.1+16.04.20151211/tests/unittest/MockAuthenticator.cpp0000644000015300001610000000245412632560105030441 0ustar pbuserpbgroup00000000000000/* * This file is part of buteo-gcontact-plugin package * * Copyright (C) 2015 Canonical Ltd. * * Contributors: Renato Araujo Oliveira Filho * * 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 "MockAuthenticator.h" MockAuthenticator::MockAuthenticator(QObject *parent) : UAuth(parent) { } MockAuthenticator::~MockAuthenticator() { } bool MockAuthenticator::authenticate() { // simulate authentication sucess emit success(); return true; } bool MockAuthenticator::init(const quint32 accountId, const QString serviceName) { m_accountId = accountId; m_serviceName = serviceName; m_initialized = true; return true; } buteo-sync-plugins-contacts-0.1+16.04.20151211/tests/unittest/GContactImageUploader.cpp0000644000015300001610000000640412632560105031155 0ustar pbuserpbgroup00000000000000/**************************************************************************** ** ** Copyright (C) 2015 Canonical Ltd. ** ** Contact: Renato Araujo Oliveira Filho ** ** This program/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 program/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 program/library; if not, write to the Free ** Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA ** 02110-1301 USA ** ****************************************************************************/ #include "GContactImageUploader.h" #include "GContactStream.h" #include "GContactAtom.h" #include "UContactsCustomDetail.h" #include #include #include #include #include #include #include #define GOOGLE_URL "https://www.google.com/m8/feeds/contacts/default/full" GContactImageUploader::GContactImageUploader(const QString &authToken, const QString &accountId, QObject *parent) : QObject(parent), mEventLoop(0), mAuthToken(authToken), mAccountId(accountId), mAbort(false) { } void GContactImageUploader::push(const QString &remoteId, const QUrl &imgUrl) { mQueue.push_back(qMakePair(remoteId, imgUrl)); } QMap GContactImageUploader::result() { return mResults; } /* * This is a sync function since the contact sync process happen in a different * thread we do not need this function to be async */ void GContactImageUploader::exec() { if (mQueue.isEmpty()) { return; } QDateTime startTime = QDateTime::currentDateTime().toUTC(); QScopedPointer networkAccessManager(new QNetworkAccessManager); connect(networkAccessManager.data(), SIGNAL(finished(QNetworkReply*)), SLOT(onRequestFinished(QNetworkReply*)), Qt::QueuedConnection); QEventLoop eventLoop; mEventLoop = &eventLoop; mUploadCompleted = false; while(!mQueue.isEmpty()) { QPair data = mQueue.takeFirst(); // upload photo UploaderReply reply; reply.newAvatarEtag = QString("%1-avatar").arg(data.first); reply.newEtag = QString("%1-new").arg(data.first); reply.remoteId = data.first; mResults[data.first] = reply; emit uploadFinished(data.first, reply); } mUploadCompleted = true; if (mAbort) { return; } } void GContactImageUploader::onRequestFinished(QNetworkReply *reply) { } QMap GContactImageUploader::parseEntryList(const QByteArray &data) const { return QMap(); } buteo-sync-plugins-contacts-0.1+16.04.20151211/tests/unittest/TestGoogleContactParser.cpp0000644000015300001610000015574112632560105031572 0ustar pbuserpbgroup00000000000000/* * This file is part of buteo-gcontact-plugin package * * Copyright (C) 2015 Canonical Ltd. * * Contributors: Renato Araujo Oliveira Filho * * 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-tests.h" #include "GContactStream.h" #include "GContactAtom.h" #include "UContactsCustomDetail.h" #include #include #include QTCONTACTS_USE_NAMESPACE class GoogleContactParserTest : public QObject { Q_OBJECT private slots: void testParseFromGoogleXml() { QFile xml(TEST_DATA_DIR + QStringLiteral("google_single_entry.txt")); QVERIFY(xml.open(QIODevice::ReadOnly)); GoogleContactStream parser(false); GoogleContactAtom *atom = parser.parse(xml.readAll()); QVERIFY(atom); QCOMPARE(atom->authorEmail(), QStringLiteral("test@gmail.com")); QCOMPARE(atom->authorName(), QStringLiteral("Ubuntu")); QCOMPARE(atom->updated(), QStringLiteral("2015-06-18T19:25:40.490Z")); QCOMPARE(atom->categorySchema(), QStringLiteral("http://schemas.google.com/g/2005#kind")); QCOMPARE(atom->categoryTerm(), QStringLiteral("http://schemas.google.com/contact/2008#contact")); QCOMPARE(atom->totalResults(), 1); QCOMPARE(atom->startIndex(), 1); QCOMPARE(atom->itemsPerPage(), 10); QCOMPARE(atom->id(), QStringLiteral("test@gmail.com")); QCOMPARE(atom->generatorName(), QStringLiteral("Contacts")); QCOMPARE(atom->generatorVersion(), QStringLiteral("1.0")); QCOMPARE(atom->generatorUri(), QStringLiteral("http://www.google.com/m8/feeds")); QCOMPARE(atom->title(), QStringLiteral("Contacts")); QList entries = atom->deletedEntryContacts(); QCOMPARE(entries.size(), 1); QContact contact = entries.at(0); QVERIFY(!contact.isEmpty()); // check each contact details // name // // Aaron Rossler // Aaron // Rossler // Mark // Sr. // Jr. // QContactName name = contact.detail(); QCOMPARE(name.firstName(), QStringLiteral("Aaron")); QCOMPARE(name.lastName(), QStringLiteral("Rossler")); QCOMPARE(name.middleName(), QStringLiteral("Mark")); QCOMPARE(name.prefix(), QStringLiteral("Sr.")); QCOMPARE(name.suffix(), QStringLiteral("Jr.")); // TODO: implement display label support // QContactDisplayLabel label = contact.detail(); // QCOMPARE(label.label(), QStringLiteral("Aaron Rossler")); // email QList emails = contact.details(); QCOMPARE(emails.size(), 3); QCOMPARE(emails.at(0).emailAddress(), QStringLiteral("rossler@other.info")); QCOMPARE(emails.at(0).contexts().size(), 1); QVERIFY(emails.at(0).contexts().contains(QContactDetail::ContextOther)); QCOMPARE(emails.at(1).emailAddress(), QStringLiteral("rossler@home.info")); QCOMPARE(emails.at(1).contexts().size(), 1); QVERIFY(emails.at(1).contexts().contains(QContactDetail::ContextHome)); QCOMPARE(emails.at(2).emailAddress(), QStringLiteral("rossler@work.info")); QCOMPARE(emails.at(2).contexts().size(), 1); QVERIFY(emails.at(2).contexts().contains(QContactDetail::ContextWork)); // phone number QList phones = contact.details(); QCOMPARE(phones.size(), 14); // +55-8704-0000 QCOMPARE(phones.at(0).number(), QStringLiteral("+55-8704-0000")); QCOMPARE(phones.at(0).contexts().size(), 1); QVERIFY(phones.at(0).contexts().contains(QContactDetail::ContextOther)); QCOMPARE(phones.at(0).subTypes().size(), 1); QVERIFY(phones.at(0).subTypes().contains(QContactPhoneNumber::SubTypeVoice)); // +55-8704-0001 QCOMPARE(phones.at(1).number(), QStringLiteral("+55-8704-0001")); QCOMPARE(phones.at(1).contexts().size(), 1); QVERIFY(phones.at(1).contexts().contains(QContactDetail::ContextHome)); QCOMPARE(phones.at(1).subTypes().size(), 1); QVERIFY(phones.at(1).subTypes().contains(QContactPhoneNumber::SubTypeVoice)); // +55-8704-0002 QCOMPARE(phones.at(2).number(), QStringLiteral("+55-8704-0002")); QCOMPARE(phones.at(2).contexts().size(), 1); QVERIFY(phones.at(2).contexts().contains(QContactDetail::ContextWork)); QCOMPARE(phones.at(2).subTypes().size(), 1); QVERIFY(phones.at(2).subTypes().contains(QContactPhoneNumber::SubTypeVoice)); // +55-8704-0003 QCOMPARE(phones.at(3).number(), QStringLiteral("+55-8704-0003")); QCOMPARE(phones.at(3).contexts().size(), 1); QVERIFY(phones.at(3).contexts().contains(QContactDetail::ContextWork)); QCOMPARE(phones.at(3).subTypes().size(), 1); QVERIFY(phones.at(3).subTypes().contains(QContactPhoneNumber::SubTypeFax)); // +55-8704-0004 QCOMPARE(phones.at(4).number(), QStringLiteral("+55-8704-0004")); QCOMPARE(phones.at(4).contexts().size(), 1); QVERIFY(phones.at(4).contexts().contains(QContactDetail::ContextHome)); QCOMPARE(phones.at(4).subTypes().size(), 1); QVERIFY(phones.at(4).subTypes().contains(QContactPhoneNumber::SubTypeFax)); // +55-8704-0005 QCOMPARE(phones.at(5).number(), QStringLiteral("+55-8704-0005")); QCOMPARE(phones.at(5).contexts().size(), 0); QCOMPARE(phones.at(5).subTypes().size(), 1); QVERIFY(phones.at(5).subTypes().contains(QContactPhoneNumber::SubTypeMobile)); // +55-8704-0006 QCOMPARE(phones.at(6).number(), QStringLiteral("+55-8704-0006")); QCOMPARE(phones.at(6).contexts().size(), 1); QVERIFY(phones.at(6).contexts().contains(QContactDetail::ContextWork)); QCOMPARE(phones.at(6).subTypes().size(), 1); QVERIFY(phones.at(6).subTypes().contains(QContactPhoneNumber::SubTypeMobile)); // +55-8704-0007 QCOMPARE(phones.at(7).number(), QStringLiteral("+55-8704-0007")); QCOMPARE(phones.at(7).contexts().size(), 1); QVERIFY(phones.at(7).contexts().contains(QContactDetail::ContextOther)); QCOMPARE(phones.at(7).subTypes().size(), 1); QVERIFY(phones.at(7).subTypes().contains(QContactPhoneNumber::SubTypeFax)); //+55-8704-0008 qDebug() << phones.at(8); QCOMPARE(phones.at(8).number(), QStringLiteral("+55-8704-0008")); QCOMPARE(phones.at(8).contexts().size(), 1); QVERIFY(phones.at(8).contexts().contains(QContactDetail::ContextHome)); QCOMPARE(phones.at(8).subTypes().size(), 1); QVERIFY(phones.at(8).subTypes().contains(QContactPhoneNumber::SubTypePager)); //+55-8704-0009 QCOMPARE(phones.at(9).number(), QStringLiteral("+55-8704-0009")); QCOMPARE(phones.at(9).contexts().size(), 1); QVERIFY(phones.at(9).contexts().contains(QContactDetail::ContextWork)); QCOMPARE(phones.at(9).subTypes().size(), 1); QVERIFY(phones.at(9).subTypes().contains(QContactPhoneNumber::SubTypePager)); //+55-8704-0010 QCOMPARE(phones.at(10).number(), QStringLiteral("+55-8704-0010")); QCOMPARE(phones.at(10).contexts().size(), 0); QCOMPARE(phones.at(10).subTypes().size(), 1); QVERIFY(phones.at(10).subTypes().contains(QContactPhoneNumber::SubTypeModem)); //+55-8704-0011 QCOMPARE(phones.at(11).number(), QStringLiteral("+55-8704-0011")); QCOMPARE(phones.at(11).contexts().size(), 0); QCOMPARE(phones.at(11).subTypes().size(), 1); QVERIFY(phones.at(11).subTypes().contains(QContactPhoneNumber::SubTypeCar)); //+55-8704-0012 QCOMPARE(phones.at(12).number(), QStringLiteral("+55-8704-0012")); QCOMPARE(phones.at(12).contexts().size(), 0); QCOMPARE(phones.at(12).subTypes().size(), 1); QVERIFY(phones.at(12).subTypes().contains(QContactPhoneNumber::SubTypeBulletinBoardSystem)); //+55-8704-0013 QCOMPARE(phones.at(13).number(), QStringLiteral("+55-8704-0013")); QCOMPARE(phones.at(13).contexts().size(), 0); QCOMPARE(phones.at(13).subTypes().size(), 1); QVERIFY(phones.at(13).subTypes().contains(QContactPhoneNumber::SubTypeAssistant)); // Address QList address = contact.details(); QCOMPARE(address.size(), 3); // // Other street 00 //00000 //Brazil // Other street 00 // 00000 // Brazil // PE // Other // POBOX0 // QCOMPARE(address.at(0).street(), QStringLiteral("Other street 00")); QCOMPARE(address.at(0).locality(), QStringLiteral("Other")); QCOMPARE(address.at(0).region(), QStringLiteral("PE")); QCOMPARE(address.at(0).postcode(), QStringLiteral("00000")); QCOMPARE(address.at(0).country(), QStringLiteral("Brazil")); QCOMPARE(address.at(0).postOfficeBox(), QStringLiteral("POBOX0")); QCOMPARE(address.at(0).contexts().size(), 1); QVERIFY(address.at(0).contexts().contains(QContactDetail::ContextOther)); // // Other street 01 //11111 //Brazil // Work street 01 // 11111 // Brazil // MA // Work // POBOX1 // QCOMPARE(address.at(1).street(), QStringLiteral("Work street 01")); QCOMPARE(address.at(1).locality(), QStringLiteral("Work")); QCOMPARE(address.at(1).region(), QStringLiteral("MA")); QCOMPARE(address.at(1).postcode(), QStringLiteral("11111")); QCOMPARE(address.at(1).country(), QStringLiteral("Brazil")); QCOMPARE(address.at(1).postOfficeBox(), QStringLiteral("POBOX1")); QCOMPARE(address.at(1).contexts().size(), 1); QVERIFY(address.at(1).contexts().contains(QContactDetail::ContextWork)); // // Other street 02 //22222 //Brazil // Home street 02 // 22222 // Brazil // MG // Home // POBOX2 // QCOMPARE(address.at(2).street(), QStringLiteral("Home street 02")); QCOMPARE(address.at(2).locality(), QStringLiteral("Home")); QCOMPARE(address.at(2).region(), QStringLiteral("MG")); QCOMPARE(address.at(2).postcode(), QStringLiteral("22222")); QCOMPARE(address.at(2).country(), QStringLiteral("Brazil")); QCOMPARE(address.at(2).postOfficeBox(), QStringLiteral("POBOX2")); QCOMPARE(address.at(2).contexts().size(), 1); QVERIFY(address.at(2).contexts().contains(QContactDetail::ContextHome)); // Online accounts QList accounts = contact.details(); QCOMPARE(accounts.size(), 10); // QCOMPARE(accounts.at(0).accountUri(), QStringLiteral("Aaron@gmail.com")); QCOMPARE(accounts.at(0).serviceProvider(), QStringLiteral("GOOGLE_TALK")); QCOMPARE(accounts.at(0).protocol(), QContactOnlineAccount::ProtocolUnknown); QVERIFY(accounts.at(0).capabilities().isEmpty()); QVERIFY(accounts.at(0).subTypes().isEmpty()); QCOMPARE(accounts.at(0).contexts().size(), 1); QVERIFY(accounts.at(0).contexts().contains(QContactDetail::ContextHome)); // QCOMPARE(accounts.at(1).accountUri(), QStringLiteral("Aaron@aim.com")); QCOMPARE(accounts.at(1).serviceProvider(), QStringLiteral("")); QCOMPARE(accounts.at(1).protocol(), QContactOnlineAccount::ProtocolAim); QVERIFY(accounts.at(1).capabilities().isEmpty()); QVERIFY(accounts.at(1).subTypes().isEmpty()); QCOMPARE(accounts.at(1).contexts().size(), 1); QVERIFY(accounts.at(1).contexts().contains(QContactDetail::ContextWork)); // QCOMPARE(accounts.at(2).accountUri(), QStringLiteral("Aaron@msn.com")); QCOMPARE(accounts.at(2).serviceProvider(), QStringLiteral("")); QCOMPARE(accounts.at(2).protocol(), QContactOnlineAccount::ProtocolMsn); QVERIFY(accounts.at(2).capabilities().isEmpty()); QVERIFY(accounts.at(2).subTypes().isEmpty()); QCOMPARE(accounts.at(2).contexts().size(), 1); QVERIFY(accounts.at(2).contexts().contains(QContactDetail::ContextWork)); // QCOMPARE(accounts.at(3).accountUri(), QStringLiteral("Aaron@yahoo.com")); QCOMPARE(accounts.at(3).serviceProvider(), QStringLiteral("")); QCOMPARE(accounts.at(3).protocol(), QContactOnlineAccount::ProtocolYahoo); QVERIFY(accounts.at(3).capabilities().isEmpty()); QVERIFY(accounts.at(3).subTypes().isEmpty()); QCOMPARE(accounts.at(3).contexts().size(), 1); QVERIFY(accounts.at(3).contexts().contains(QContactDetail::ContextOther)); // QCOMPARE(accounts.at(4).accountUri(), QStringLiteral("Aaron@skype.com")); QCOMPARE(accounts.at(4).serviceProvider(), QStringLiteral("")); QCOMPARE(accounts.at(4).protocol(), QContactOnlineAccount::ProtocolSkype); QVERIFY(accounts.at(4).capabilities().isEmpty()); QVERIFY(accounts.at(4).subTypes().isEmpty()); QCOMPARE(accounts.at(4).contexts().size(), 1); QVERIFY(accounts.at(4).contexts().contains(QContactDetail::ContextOther)); // QCOMPARE(accounts.at(5).accountUri(), QStringLiteral("Aaron@qq.com")); QCOMPARE(accounts.at(5).serviceProvider(), QStringLiteral("")); QCOMPARE(accounts.at(5).protocol(), QContactOnlineAccount::ProtocolQq); QVERIFY(accounts.at(5).capabilities().isEmpty()); QVERIFY(accounts.at(5).subTypes().isEmpty()); QCOMPARE(accounts.at(5).contexts().size(), 1); QVERIFY(accounts.at(5).contexts().contains(QContactDetail::ContextOther)); // QCOMPARE(accounts.at(6).accountUri(), QStringLiteral("Aaron@icq.com")); QCOMPARE(accounts.at(6).serviceProvider(), QStringLiteral("")); QCOMPARE(accounts.at(6).protocol(), QContactOnlineAccount::ProtocolIcq); QVERIFY(accounts.at(6).capabilities().isEmpty()); QVERIFY(accounts.at(6).subTypes().isEmpty()); QCOMPARE(accounts.at(6).contexts().size(), 1); QVERIFY(accounts.at(6).contexts().contains(QContactDetail::ContextOther)); // QCOMPARE(accounts.at(7).accountUri(), QStringLiteral("Aaron@jabber.com")); QCOMPARE(accounts.at(7).serviceProvider(), QStringLiteral("")); QCOMPARE(accounts.at(7).protocol(), QContactOnlineAccount::ProtocolJabber); QVERIFY(accounts.at(7).capabilities().isEmpty()); QVERIFY(accounts.at(7).subTypes().isEmpty()); QCOMPARE(accounts.at(7).contexts().size(), 1); QVERIFY(accounts.at(7).contexts().contains(QContactDetail::ContextHome)); // QCOMPARE(accounts.at(8).accountUri(), QStringLiteral("Aaron@irc.com")); QCOMPARE(accounts.at(8).serviceProvider(), QStringLiteral("")); QCOMPARE(accounts.at(8).protocol(), QContactOnlineAccount::ProtocolIrc); QVERIFY(accounts.at(8).capabilities().isEmpty()); QVERIFY(accounts.at(8).subTypes().isEmpty()); QCOMPARE(accounts.at(8).contexts().size(), 1); QVERIFY(accounts.at(8).contexts().contains(QContactDetail::ContextHome)); // QCOMPARE(accounts.at(9).accountUri(), QStringLiteral("Aaron@telegram.com")); QCOMPARE(accounts.at(9).serviceProvider(), QStringLiteral("TELEGRAM")); QCOMPARE(accounts.at(9).protocol(), QContactOnlineAccount::ProtocolUnknown); QVERIFY(accounts.at(9).capabilities().isEmpty()); QVERIFY(accounts.at(9).subTypes().isEmpty()); QCOMPARE(accounts.at(9).contexts().size(), 1); QVERIFY(accounts.at(9).contexts().contains(QContactDetail::ContextHome)); // QContactOrganization QList orgs = contact.details(); QCOMPARE(orgs.size(), 2); // // Google, Inc. // Tech Writer // Writes documentation // Software Development // GOOG // QCOMPARE(orgs.at(0).name(), QStringLiteral("Google, Inc.")); QCOMPARE(orgs.at(0).logoUrl().toString(), QStringLiteral("GOOG")); QCOMPARE(orgs.at(0).department().size(), 1); QCOMPARE(orgs.at(0).department().first(), QStringLiteral("Software Development")); QCOMPARE(orgs.at(0).location(), QStringLiteral("")); QCOMPARE(orgs.at(0).role(), QStringLiteral("Writes documentation")); QCOMPARE(orgs.at(0).title(), QStringLiteral("Tech Writer")); QCOMPARE(orgs.at(0).assistantName(), QStringLiteral("")); QCOMPARE(orgs.at(0).contexts().size(), 1); QVERIFY(orgs.at(0).contexts().contains(QContactDetail::ContextWork)); // // My house // cleaner // Clean the house // Room // HOUSE // QCOMPARE(orgs.at(1).name(), QStringLiteral("My house")); QCOMPARE(orgs.at(1).logoUrl().toString(), QStringLiteral("HOUSE")); QCOMPARE(orgs.at(1).department().size(), 1); QCOMPARE(orgs.at(1).department().first(), QStringLiteral("Room")); QCOMPARE(orgs.at(1).location(), QStringLiteral("")); QCOMPARE(orgs.at(1).role(), QStringLiteral("Clean the house")); QCOMPARE(orgs.at(1).title(), QStringLiteral("cleaner")); QCOMPARE(orgs.at(1).assistantName(), QStringLiteral("")); QCOMPARE(orgs.at(1).contexts().size(), 1); QVERIFY(orgs.at(1).contexts().contains(QContactDetail::ContextHome)); // QList avatars = contact.details(); QString avatarETag = UContactsCustomDetail::getCustomField(contact, UContactsCustomDetail::FieldContactAvatarETag).data().toString(); QCOMPARE(avatars.size(), 1); QCOMPARE(avatars.at(0).imageUrl(), QUrl("https://www.google.com/m8/feeds/photos/media/test%40gmail.com/948ac108f03db45")); QCOMPARE(avatarETag, QStringLiteral("\"eh19bkUofCt7I2BaHVJTdw9tEH0ifl8lcyg.\"")); // QList favorites = contact.details(); QCOMPARE(favorites.size(), 1); QCOMPARE(favorites.at(0).isFavorite(), true); // QContactExtendedDetail xProperty = UContactsCustomDetail::getCustomField(contact, QStringLiteral("X-PROPERTY")); QCOMPARE(xProperty.name(), QStringLiteral("X-PROPERTY")); QCOMPARE(xProperty.data().toString(), QStringLiteral("property-value")); // QContactExtendedDetail ex = UContactsCustomDetail::getCustomField(contact, UContactsCustomDetail::FieldDeletedAt); QVERIFY(ex.data().toDateTime().isValid()); //TypeAnniversary, // // // QList anniversaries = contact.details(); QCOMPARE(anniversaries.size(), 2); QCOMPARE(anniversaries.at(0).subType(), QContactAnniversary::SubTypeWedding); QCOMPARE(anniversaries.at(0).originalDateTime().toString(Qt::ISODate), QStringLiteral("2005-06-06T17:00:00")); QCOMPARE(anniversaries.at(0).event(), QStringLiteral("This weekend")); // // // QCOMPARE(anniversaries.at(1).subType(), QContactAnniversary::SubTypeMemorial); QCOMPARE(anniversaries.at(1).originalDateTime().toString(Qt::ISODate), QStringLiteral("2005-06-06T00:00:00")); QCOMPARE(anniversaries.at(1).event(), QStringLiteral("This month")); //TypeBirthday QList birthdays = contact.details(); // QCOMPARE(birthdays.size(), 1); QCOMPARE(birthdays.at(0).dateTime().toString(Qt::ISODate), QStringLiteral("1980-07-04T00:00:00")); //FIXME: QContactBirthday does not support birthdays without year // //TypeGender // QList genders = contact.details(); QCOMPARE(genders.size(), 1); QCOMPARE(genders.at(0).gender(), QContactGender::GenderFemale); //TypeNote QList notes = contact.details(); QCOMPARE(notes.size(), 2); //Lived in Orange County QCOMPARE(notes.at(0).contexts().size(), 1); QVERIFY(notes.at(0).contexts().contains(QContactDetail::ContextHome)); QCOMPARE(notes.at(0).note(), QStringLiteral("Lived in Orange County")); //Borrowed my copy of "Alice's Adventures in Wonderland" QCOMPARE(notes.at(1).contexts().size(), 1); QVERIFY(notes.at(1).contexts().contains(QContactDetail::ContextOther)); QCOMPARE(notes.at(1).note(), QStringLiteral("Borrowed my copy of \"Alice's Adventures in Wonderland\"")); //TypeUrl // FIXME: QContactUrl does not support label QList urls = contact.details(); QCOMPARE(urls.size(), 7); // QCOMPARE(urls.at(0).contexts().size(), 1); QVERIFY(urls.at(0).contexts().contains(QContactDetail::ContextOther)); QCOMPARE(urls.at(0).subType(), QContactUrl::SubTypeHomePage); QCOMPARE(urls.at(0).url(), QStringLiteral("http://www.ubuntu.com")); // QCOMPARE(urls.at(1).contexts().size(), 1); QVERIFY(urls.at(1).contexts().contains(QContactDetail::ContextOther)); QCOMPARE(urls.at(1).subType(), QContactUrl::SubTypeBlog); QCOMPARE(urls.at(1).url(), QStringLiteral("http://blog.canonical.com/")); // // FIXME: QContactUrl does not contains type 'profile' QCOMPARE(urls.at(2).contexts().size(), 1); QVERIFY(urls.at(2).contexts().contains(QContactDetail::ContextOther)); QCOMPARE(urls.at(2).subType(), QContactUrl::SubTypeFavourite); QCOMPARE(urls.at(2).url(), QStringLiteral("https://launchpad.net/~user")); // QCOMPARE(urls.at(3).contexts().size(), 1); QVERIFY(urls.at(3).contexts().contains(QContactDetail::ContextHome)); QCOMPARE(urls.at(3).subType(), QContactUrl::SubTypeFavourite); QCOMPARE(urls.at(3).url(), QStringLiteral("http://www.home.com")); // QCOMPARE(urls.at(4).contexts().size(), 1); QVERIFY(urls.at(4).contexts().contains(QContactDetail::ContextWork)); QCOMPARE(urls.at(4).subType(), QContactUrl::SubTypeFavourite); QCOMPARE(urls.at(4).url(), QStringLiteral("http://www.work.com")); // QCOMPARE(urls.at(5).contexts().size(), 1); QVERIFY(urls.at(5).contexts().contains(QContactDetail::ContextOther)); QCOMPARE(urls.at(5).subType(), QContactUrl::SubTypeFavourite); QCOMPARE(urls.at(5).url(), QStringLiteral("http://www.other.com")); // // FIXME: QContactUrl does not contains type 'ftp' QCOMPARE(urls.at(6).contexts().size(), 1); QVERIFY(urls.at(6).contexts().contains(QContactDetail::ContextOther)); QCOMPARE(urls.at(6).subType(), QContactUrl::SubTypeFavourite); QCOMPARE(urls.at(6).url(), QStringLiteral("http://www.ftp.com")); //TypeHobby //Paragliding QList hobbies = contact.details(); QCOMPARE(hobbies.size(), 1); QCOMPARE(hobbies.at(0).hobby(), QStringLiteral("Paragliding")); //TypeRingtone QList ringtones = contact.details(); QCOMPARE(ringtones.size(), 1); QCOMPARE(ringtones.at(0).audioRingtoneUrl(), QUrl("file://my-ringtone.mp3")); //TypeNickname //Dragon QList nicknames = contact.details(); QCOMPARE(nicknames.size(), 1); QCOMPARE(nicknames.at(0).nickname(), QStringLiteral("Dragon")); //TypeFamily //Katherine QList families = contact.details(); QCOMPARE(families.size(), 2); QCOMPARE(families.at(0).spouse(), QStringLiteral("Katherine")); QVERIFY(families.at(0).children().isEmpty()); //Mike QCOMPARE(families.at(1).children(), QStringList() << QStringLiteral("Mike")); QVERIFY(families.at(1).spouse().isEmpty()); //TODO: check for missing fields //TypeGeoLocation, //TypeGlobalPresence, //TypeNickname, //TypePresence, //TypeSyncTarget, //TypeTag, //TypeType, //TypeUrl, } void testParseToGoogleXml() { QStringList expectedXML; QContact contact; // name QContactName cName; cName.setPrefix(QStringLiteral("Sr.")); cName.setFirstName(QStringLiteral("Aaron")); cName.setMiddleName(QStringLiteral("Mark")); cName.setLastName(QStringLiteral("Rossler")); cName.setSuffix(QStringLiteral("Jr.")); contact.saveDetail(&cName); expectedXML << QStringLiteral("" "Aaron" "Mark" "Rossler" "Sr." "Jr." ""); // email QContactEmailAddress email; email.setEmailAddress(QStringLiteral("rossler@other.info")); email.setContexts(QList() << QContactDetail::ContextOther); contact.saveDetail(&email); expectedXML << QStringLiteral(""); email = QContactEmailAddress(); email.setEmailAddress(QStringLiteral("rossler@home.info")); email.setContexts(QList() << QContactDetail::ContextHome); contact.saveDetail(&email); expectedXML << QStringLiteral(""); email = QContactEmailAddress(); email.setEmailAddress(QStringLiteral("rossler@work.info")); email.setContexts(QList() << QContactDetail::ContextWork); contact.saveDetail(&email); expectedXML << QStringLiteral(""); // phone number QContactPhoneNumber phone; phone.setNumber(QStringLiteral("+55-8704-0000")); phone.setContexts(QList() << QContactDetail::ContextOther); phone.setSubTypes(QList() << QContactPhoneNumber::SubTypeVoice); contact.saveDetail(&phone); expectedXML << QStringLiteral("+55-8704-0000"); phone = QContactPhoneNumber(); phone.setNumber(QStringLiteral("+55-8704-0001")); phone.setContexts(QList() << QContactDetail::ContextHome); phone.setSubTypes(QList() << QContactPhoneNumber::SubTypeVoice); contact.saveDetail(&phone); expectedXML << QStringLiteral("+55-8704-0001"); phone = QContactPhoneNumber(); phone.setNumber(QStringLiteral("+55-8704-0002")); phone.setContexts(QList() << QContactDetail::ContextWork); phone.setSubTypes(QList() << QContactPhoneNumber::SubTypeVoice); contact.saveDetail(&phone); expectedXML << QStringLiteral("+55-8704-0002"); phone = QContactPhoneNumber(); phone.setNumber(QStringLiteral("+55-8704-0003")); phone.setContexts(QList() << QContactDetail::ContextWork); phone.setSubTypes(QList() << QContactPhoneNumber::SubTypeFax); contact.saveDetail(&phone); expectedXML << QStringLiteral("+55-8704-0003"); phone = QContactPhoneNumber(); phone.setNumber(QStringLiteral("+55-8704-0004")); phone.setContexts(QList() << QContactDetail::ContextHome); phone.setSubTypes(QList() << QContactPhoneNumber::SubTypeFax); contact.saveDetail(&phone); expectedXML << QStringLiteral("+55-8704-0004"); phone = QContactPhoneNumber(); phone.setNumber(QStringLiteral("+55-8704-0005")); phone.setSubTypes(QList() << QContactPhoneNumber::SubTypeMobile); contact.saveDetail(&phone); expectedXML << QStringLiteral("+55-8704-0005"); phone = QContactPhoneNumber(); phone.setNumber(QStringLiteral("+55-8704-0006")); phone.setContexts(QList() << QContactDetail::ContextWork); phone.setSubTypes(QList() << QContactPhoneNumber::SubTypeMobile); contact.saveDetail(&phone); expectedXML << QStringLiteral("+55-8704-0006"); phone = QContactPhoneNumber(); phone.setNumber(QStringLiteral("+55-8704-0007")); phone.setContexts(QList() << QContactDetail::ContextOther); phone.setSubTypes(QList() << QContactPhoneNumber::SubTypeFax); contact.saveDetail(&phone); expectedXML << QStringLiteral("+55-8704-0007"); phone = QContactPhoneNumber(); phone.setNumber(QStringLiteral("+55-8704-0008")); phone.setSubTypes(QList() << QContactPhoneNumber::SubTypePager); contact.saveDetail(&phone); expectedXML << QStringLiteral("+55-8704-0008"); phone = QContactPhoneNumber(); phone.setNumber(QStringLiteral("+55-8704-0009")); phone.setContexts(QList() << QContactDetail::ContextWork); phone.setSubTypes(QList() << QContactPhoneNumber::SubTypePager); contact.saveDetail(&phone); expectedXML << QStringLiteral("+55-8704-0009"); phone = QContactPhoneNumber(); phone.setNumber(QStringLiteral("+55-8704-0010")); phone.setSubTypes(QList() << QContactPhoneNumber::SubTypeModem); contact.saveDetail(&phone); expectedXML << QStringLiteral("+55-8704-0010"); phone = QContactPhoneNumber(); phone.setNumber(QStringLiteral("+55-8704-0011")); phone.setSubTypes(QList() << QContactPhoneNumber::SubTypeCar); contact.saveDetail(&phone); expectedXML << QStringLiteral("+55-8704-0011"); phone = QContactPhoneNumber(); phone.setNumber(QStringLiteral("+55-8704-0012")); phone.setSubTypes(QList() << QContactPhoneNumber::SubTypeBulletinBoardSystem); contact.saveDetail(&phone); expectedXML << QStringLiteral("+55-8704-0012"); phone = QContactPhoneNumber(); phone.setNumber(QStringLiteral("+55-8704-0013")); phone.setSubTypes(QList() << QContactPhoneNumber::SubTypeAssistant); contact.saveDetail(&phone); expectedXML << QStringLiteral("+55-8704-0013"); phone = QContactPhoneNumber(); phone.setNumber(QStringLiteral("+55-8704-0009")); phone.setContexts(QList() << QContactDetail::ContextWork); phone.setSubTypes(QList() << QContactPhoneNumber::SubTypePager); contact.saveDetail(&phone); expectedXML << QStringLiteral("+55-8704-0009"); // Address QContactAddress address; address.setStreet(QStringLiteral("Other street 00")); address.setLocality(QStringLiteral("Other")); address.setRegion(QStringLiteral("PE")); address.setPostcode(QStringLiteral("00000")); address.setCountry(QStringLiteral("Brazil")); address.setPostOfficeBox(QStringLiteral("POBOX0")); contact.saveDetail(&address); expectedXML << QStringLiteral("" "Other street 00" "Other" "POBOX0" "PE" "00000" "Brazil" ""); address = QContactAddress(); address.setStreet(QStringLiteral("Work street 01")); address.setLocality(QStringLiteral("Work")); address.setRegion(QStringLiteral("MA")); address.setPostcode(QStringLiteral("11111")); address.setCountry(QStringLiteral("Brazil")); address.setPostOfficeBox(QStringLiteral("POBOX1")); address.setContexts(QList() << QContactDetail::ContextWork); contact.saveDetail(&address); expectedXML << QStringLiteral("" "Work street 01" "Work" "POBOX1" "MA" "11111" "Brazil" ""); address = QContactAddress(); address.setStreet(QStringLiteral("Home street 02")); address.setLocality(QStringLiteral("Home")); address.setRegion(QStringLiteral("MG")); address.setPostcode(QStringLiteral("22222")); address.setCountry(QStringLiteral("Brazil")); address.setPostOfficeBox(QStringLiteral("POBOX2")); address.setContexts(QList() << QContactDetail::ContextHome); contact.saveDetail(&address); expectedXML << QStringLiteral("" "Home street 02" "Home" "POBOX2" "MG" "22222" "Brazil" ""); // Online accounts QContactOnlineAccount account; account.setAccountUri(QStringLiteral("Aaron@gmail.com")); account.setProtocol(QContactOnlineAccount::ProtocolUnknown); account.setContexts(QList() << QContactDetail::ContextHome); account.setServiceProvider(QStringLiteral("GOOGLE_TALK")); contact.saveDetail(&account); expectedXML << QStringLiteral(""); account = QContactOnlineAccount(); account.setAccountUri(QStringLiteral("Aaron@aim.com")); account.setProtocol(QContactOnlineAccount::ProtocolAim); account.setContexts(QList() << QContactDetail::ContextWork); contact.saveDetail(&account); expectedXML << QStringLiteral(""); account = QContactOnlineAccount(); account.setAccountUri(QStringLiteral("Aaron@msn.com")); account.setProtocol(QContactOnlineAccount::ProtocolMsn); account.setContexts(QList() << QContactDetail::ContextWork); contact.saveDetail(&account); expectedXML << QStringLiteral(""); account = QContactOnlineAccount(); account.setAccountUri(QStringLiteral("Aaron@yahoo.com")); account.setProtocol(QContactOnlineAccount::ProtocolYahoo); account.setContexts(QList() << QContactDetail::ContextOther); contact.saveDetail(&account); expectedXML << QStringLiteral(""); account = QContactOnlineAccount(); account.setAccountUri(QStringLiteral("Aaron@skype.com")); account.setProtocol(QContactOnlineAccount::ProtocolSkype); account.setContexts(QList() << QContactDetail::ContextOther); contact.saveDetail(&account); expectedXML << QStringLiteral(""); account = QContactOnlineAccount(); account.setAccountUri(QStringLiteral("Aaron@qq.com")); account.setProtocol(QContactOnlineAccount::ProtocolQq); account.setContexts(QList() << QContactDetail::ContextOther); contact.saveDetail(&account); expectedXML << QStringLiteral(""); account = QContactOnlineAccount(); account.setAccountUri(QStringLiteral("Aaron@icq.com")); account.setProtocol(QContactOnlineAccount::ProtocolIcq); account.setContexts(QList() << QContactDetail::ContextOther); contact.saveDetail(&account); expectedXML << QStringLiteral(""); account = QContactOnlineAccount(); account.setAccountUri(QStringLiteral("Aaron@jabber.com")); account.setProtocol(QContactOnlineAccount::ProtocolJabber); account.setContexts(QList() << QContactDetail::ContextHome); contact.saveDetail(&account); expectedXML << QStringLiteral(""); account = QContactOnlineAccount(); account.setAccountUri(QStringLiteral("Aaron@irc.com")); account.setProtocol(QContactOnlineAccount::ProtocolIrc); account.setContexts(QList() << QContactDetail::ContextHome); contact.saveDetail(&account); expectedXML << QStringLiteral(""); account = QContactOnlineAccount(); account.setAccountUri(QStringLiteral("Aaron@telegram.com")); account.setProtocol(QContactOnlineAccount::ProtocolUnknown); account.setContexts(QList() << QContactDetail::ContextHome); account.setServiceProvider(QStringLiteral("TELEGRAM")); contact.saveDetail(&account); expectedXML << QStringLiteral(""); // Organization QContactOrganization org; org.setName(QStringLiteral("Google, Inc.")); org.setDepartment(QStringList() << QStringLiteral("Software Development")); org.setRole(QStringLiteral("Writes documentation")); org.setTitle(QStringLiteral("Tech Writer")); org.setContexts(QList() << QContactDetail::ContextWork); contact.saveDetail(&org); expectedXML << QStringLiteral("" "Tech Writer" "Google, Inc." "Software Development" "Writes documentation" ""); org = QContactOrganization(); org.setName(QStringLiteral("My house")); org.setDepartment(QStringList() << QStringLiteral("Room")); org.setRole(QStringLiteral("Clear the house")); org.setTitle(QStringLiteral("cleaner")); org.setContexts(QList() << QContactDetail::ContextHome); contact.saveDetail(&org); expectedXML << QStringLiteral("" "cleaner" "My house" "Room" "Clear the house" ""); // Extended Details QContactExtendedDetail xDet; xDet.setName("X-PROPERTY"); xDet.setData("x-property-value"); contact.saveDetail(&xDet); expectedXML << QStringLiteral(""); // Favorite QContactFavorite fav; fav.setFavorite(true); contact.saveDetail(&fav); expectedXML << QStringLiteral(""); // Anniversary QContactAnniversary aniversary; aniversary.setOriginalDateTime(QDateTime(QDate(2015, 10, 4), QTime(12, 30))); aniversary.setSubType(QContactAnniversary::SubTypeWedding); aniversary.setEvent("My wedding"); contact.saveDetail(&aniversary); expectedXML << QStringLiteral("" "" ""); aniversary = QContactAnniversary(); aniversary.setOriginalDate(QDate(2015, 3, 20)); aniversary.setSubType(QContactAnniversary::SubTypeHouse); aniversary.setEvent("My house event"); contact.saveDetail(&aniversary); expectedXML << QStringLiteral("" "" ""); // Birthday QContactBirthday birthday; birthday.setDateTime(QDateTime(QDate(2015, 11, 4), QTime(12, 30))); contact.saveDetail(&birthday); expectedXML << QStringLiteral(""); // FIXME: QContacts api does not support birthday without year // birthday = QContactBirthday(); // birthday.setDate(QDate(0, 4, 23)); // contact.saveDetail(&birthday); // expectedXML << QStringLiteral(""); // Gender QContactGender gender; gender.setGender(QContactGender::GenderMale); contact.saveDetail(&gender); expectedXML << QStringLiteral(""); // Note QContactNote note; note.setNote("This is a note"); note.setContexts(QList() << QContactDetail::ContextWork); contact.saveDetail(¬e); expectedXML << QStringLiteral("This is a note"); // Url QContactUrl url; url.setUrl("http://www.ubuntu.com"); url.setSubType(QContactUrl::SubTypeHomePage); contact.saveDetail(&url); expectedXML << QStringLiteral(""); url = QContactUrl(); url.setUrl("http://blog.canonical.com"); url.setSubType(QContactUrl::SubTypeBlog); contact.saveDetail(&url); expectedXML << QStringLiteral(""); url = QContactUrl(); url.setUrl("http://www.canonical.com"); url.setSubType(QContactUrl::SubTypeFavourite); contact.saveDetail(&url); expectedXML << QStringLiteral(""); url = QContactUrl(); url.setUrl("http://www.home.com"); url.setSubType(QContactUrl::SubTypeFavourite); url.setContexts(QList() << QContactDetail::ContextHome); contact.saveDetail(&url); expectedXML << QStringLiteral(""); url = QContactUrl(); url.setUrl("http://www.work.com"); url.setSubType(QContactUrl::SubTypeFavourite); url.setContexts(QList() << QContactDetail::ContextWork); contact.saveDetail(&url); expectedXML << QStringLiteral(""); //TypeHobby QContactHobby hobby; hobby.setHobby("Paragliding"); contact.saveDetail(&hobby); expectedXML << QStringLiteral("Paragliding"); //TypeRingtone QContactRingtone ringtone; ringtone.setAudioRingtoneUrl(QUrl("file://my-ringtone.mp3")); contact.saveDetail(&ringtone); expectedXML << QStringLiteral(""); //TypeNickname QContactNickname nickname; nickname.setNickname("Dragon"); contact.saveDetail(&nickname); expectedXML << QStringLiteral("Dragon"); //TypeFamily QContactFamily family; family.setSpouse("Katherine"); family.setChildren(QStringList() << "Mike" << "Marvin"); contact.saveDetail(&family); expectedXML << QStringLiteral("Katherine"); expectedXML << QStringLiteral("Mike"); expectedXML << QStringLiteral("Marvin"); //TypeAvatar // QContactAvatar avatar; // avatar.setImageUrl(QUrl("http://www.canonical.com/ubuntu.png")); // contact.saveDetail(&avatar); // expectedXML << QStringLiteral(""); //TODO: check for missing fields //TypeGeoLocation, //TypeGlobalPresence, //TypeGuid, //TypePresence, //TypeSyncTarget, //TypeTag, //TypeType, //TypeVersion QMultiMap > batchPage; batchPage.insertMulti(GoogleContactStream::Add, qMakePair(contact, QStringList())); GoogleContactStream encoder(false, QStringLiteral("test@gmail.com")); QByteArray xml = encoder.encode(batchPage); foreach(QString line, expectedXML) { QVERIFY2(xml.contains(line.toUtf8()), qPrintable("Invalid parse for:" + line)); } } }; QTEST_MAIN(GoogleContactParserTest) #include "TestGoogleContactParser.moc" buteo-sync-plugins-contacts-0.1+16.04.20151211/tests/unittest/GTransport.cpp0000644000015300001610000001270412632560105027117 0ustar pbuserpbgroup00000000000000/* * This file is part of buteo-gcontact-plugin package * * Copyright (C) 2015 Canonical Ltd. * * Contributors: Renato Araujo Oliveira Filho * * 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 "GTransport.h" #include #include #include #include #include #include const int MAX_RESULTS = 10; const QString SCOPE_URL("https://www.google.com/m8/feeds/"); const QString GCONTACT_URL(SCOPE_URL + "/contacts/default/"); const QString GDATA_VERSION_TAG = "GData-Version"; const QString GDATA_VERSION = "3.1"; const QString G_DELETE_OVERRIDE_HEADER("X-HTTP-Method-Override: DELETE"); const QString G_ETAG_HEADER("If-Match "); const QString G_AUTH_HEADER ("Authorization"); const QString G_CONTENT_TYPE_HEADER = "application/atom+xml"; /* Query parameters */ const QString QUERY_TAG("q"); const QString MAX_RESULTS_TAG("max-results"); const QString START_INDEX_TAG("start-index"); const QString UPDATED_MIN_TAG("updated-min"); const QString ORDERBY_TAG("orderby"); const QString SHOW_DELETED_TAG("showdeleted"); const QString REQUIRE_ALL_DELETED("requirealldeleted"); const QString SORTORDER_TAG("sortorder"); const QString PHOTO_TAG("photos"); const QString MEDIA_TAG("media"); const QString BATCH_TAG("batch"); class GTransportPrivate { public: GTransportPrivate(QObject *parent) { } QList > mHeaders; }; GTransport::GTransport(QObject *parent) : QObject(parent), d_ptr(new GTransportPrivate(this)) { FUNCTION_CALL_TRACE; Q_D(GTransport); } GTransport::GTransport(QUrl url, QList > headers) : d_ptr(new GTransportPrivate(this)) { FUNCTION_CALL_TRACE; Q_D(GTransport); } GTransport::GTransport(QUrl url, QList > headers, QByteArray data) : d_ptr(new GTransportPrivate(this)) { FUNCTION_CALL_TRACE; Q_D(GTransport); } GTransport::~GTransport() { FUNCTION_CALL_TRACE; Q_D(GTransport); } void GTransport::setUrl(const QString &url) { FUNCTION_CALL_TRACE; Q_D(GTransport); setProperty("URL", url); } void GTransport::setData(QByteArray data) { FUNCTION_CALL_TRACE; Q_D(GTransport); setProperty("DATA", data); } void GTransport::setHeaders() { FUNCTION_CALL_TRACE; Q_D(GTransport); } void GTransport::addHeader(const QByteArray first, const QByteArray second) { FUNCTION_CALL_TRACE; Q_D(GTransport); QVariantMap headers = property("Headers").value(); headers.insert(first, second); setProperty("Headers", headers); } void GTransport::setAuthToken(const QString token) { FUNCTION_CALL_TRACE; Q_D(GTransport); } void GTransport::setGDataVersionHeader() { Q_D(GTransport); QVariantMap headers = property("Headers").value(); headers.insert(QStringLiteral("GData-Version"), QStringLiteral("3.0")); setProperty("Headers", headers); } void GTransport::setProxy (QString proxyHost, QString proxyPort) { FUNCTION_CALL_TRACE; Q_D(GTransport); setProperty("ProxyHost", proxyHost); setProperty("ProxyPort", proxyPort); } void GTransport::request(const HTTP_REQUEST_TYPE type) { FUNCTION_CALL_TRACE; Q_D(GTransport); QUrl url(property("URL").toString()); QByteArray data; emit requested(url, &data); setProperty("RequestType", (int) type); setProperty("ReplyBody", data); emit finishedRequest(); } bool GTransport::hasReply() const { FUNCTION_CALL_TRACE; return true; } const QByteArray GTransport::replyBody() const { FUNCTION_CALL_TRACE; return property("ReplyBody").toByteArray(); } void GTransport::readyRead() { FUNCTION_CALL_TRACE; Q_D(GTransport); } void GTransport::finishedSlot(QNetworkReply *reply) { FUNCTION_CALL_TRACE; Q_D(GTransport); emit finishedRequest(); } void GTransport::setUpdatedMin(const QDateTime datetime) { FUNCTION_CALL_TRACE; Q_D(GTransport); setProperty("UpdatedMin", datetime); } void GTransport::setMaxResults(unsigned int limit) { FUNCTION_CALL_TRACE; Q_D(GTransport); setProperty("MaxResults", limit); } void GTransport::setShowDeleted() { FUNCTION_CALL_TRACE; Q_D(GTransport); setProperty("ShowDeleted", true); } bool GTransport::showDeleted() const { const Q_D(GTransport); return property("ShowDeleted").toBool(); } void GTransport::setStartIndex(const int index) { FUNCTION_CALL_TRACE; Q_D(GTransport); setProperty("StartIndex", index); } GTransport::HTTP_REQUEST_TYPE GTransport::requestType() { return GTransport::HTTP_REQUEST_TYPE(property("RequestType").toInt()); } void GTransport::reset() { Q_D(GTransport); } void GTransport::setGroupFilter(const QString &account, const QString &groupId) { setProperty("GroupFilter", QString("%1@%2").arg(account).arg(groupId)); } buteo-sync-plugins-contacts-0.1+16.04.20151211/tests/unittest/data/0000755000015300001610000000000012632561656025231 5ustar pbuserpbgroup00000000000000buteo-sync-plugins-contacts-0.1+16.04.20151211/tests/unittest/data/new_database_remote_result.vcf0000644000015300001610000000101012632560105033273 0ustar pbuserpbgroup00000000000000BEGIN:VCARD VERSION:3.0 UID:0 N:teste3;teste3;;; FN:teste3 teste3 X-QTPROJECT-FAVORITE:false;0 TEL:3333333 CATEGORIES:T END:VCARD BEGIN:VCARD VERSION:3.0 UID:0 N:test34;teste;;; FN:teste test34 X-QTPROJECT-FAVORITE:false;0 TEL:44444 CATEGORIES:T END:VCARD BEGIN:VCARD VERSION:3.0 UID:2 N:teste2;teste;;; FN:teste teste2 X-QTPROJECT-FAVORITE:false;0 TEL:111111 CATEGORIES:T END:VCARD BEGIN:VCARD VERSION:3.0 UID:0 N:FirstName;LasteName;;; FN:FirstName LasteName TEL:1234567890 END:VCARD ././@LongLink0000000000000000000000000000015700000000000011220 Lustar 00000000000000buteo-sync-plugins-contacts-0.1+16.04.20151211/tests/unittest/data/fast_sync_without_changes_remote_result.vcfbuteo-sync-plugins-contacts-0.1+16.04.20151211/tests/unittest/data/fast_sync_without_changes_remote_0000644000015300001610000000115412632560105034117 0ustar pbuserpbgroup00000000000000BEGIN:VCARD VERSION:3.0 UID:0 N:teste3;teste3;;; FN:teste3 teste3 X-QTPROJECT-FAVORITE:false;0 TEL:3333333 CATEGORIES:T REV:2015-06-15T08:00:00 END:VCARD BEGIN:VCARD VERSION:3.0 UID:0 N:test34;teste;;; FN:teste test34 X-QTPROJECT-FAVORITE:false;0 TEL:44444 CATEGORIES:T REV:2015-06-15T08:00:00 END:VCARD BEGIN:VCARD VERSION:3.0 UID:2 N:teste2;teste;;; FN:teste teste2 X-QTPROJECT-FAVORITE:false;0 TEL:111111 CATEGORIES:T REV:2015-06-15T08:00:00 END:VCARD BEGIN:VCARD VERSION:3.0 UID:0 N:FirstName;LasteName;;; FN:FirstName LasteName TEL:1234567890 REV:2015-06-15T08:00:00 END:VCARD buteo-sync-plugins-contacts-0.1+16.04.20151211/tests/unittest/data/new_database_local_result.vcf0000644000015300001610000000154012632560105033102 0ustar pbuserpbgroup00000000000000BEGIN:VCARD VERSION:3.0 N:FirstName;LasteName;;; FN:FirstName LasteName TEL:1234567890 X-QTPROJECT-EXTENDED-DETAIL:X-REMOTE-ID;[\n "qtcontacts:mock:id=remote-so urce:5"\n]\n END:VCARD BEGIN:VCARD VERSION:3.0 N:teste3;teste3;;; FN:teste3 teste3 X-QTPROJECT-FAVORITE:false;0 TEL:3333333 CATEGORIES:T X-QTPROJECT-EXTENDED-DETAIL:X-REMOTE-ID;[\n "qtcontacts:mock:id=remote-so urce:2"\n]\n END:VCARD BEGIN:VCARD VERSION:3.0 N:test34;teste;;; FN:teste test34 X-QTPROJECT-FAVORITE:false;0 TEL:44444 CATEGORIES:T X-QTPROJECT-EXTENDED-DETAIL:X-REMOTE-ID;[\n "qtcontacts:mock:id=remote-so urce:3"\n]\n END:VCARD BEGIN:VCARD VERSION:3.0 N:teste2;teste;;; FN:teste teste2 X-QTPROJECT-FAVORITE:false;0 TEL:111111 CATEGORIES:T X-QTPROJECT-EXTENDED-DETAIL:X-REMOTE-ID;[\n "qtcontacts:mock:id=remote-so urce:4"\n]\n END:VCARD ././@LongLink0000000000000000000000000000015300000000000011214 Lustar 00000000000000buteo-sync-plugins-contacts-0.1+16.04.20151211/tests/unittest/data/fast_sync_changed_both_sides_remote.vcfbuteo-sync-plugins-contacts-0.1+16.04.20151211/tests/unittest/data/fast_sync_changed_both_sides_remo0000644000015300001610000000127012632560105034027 0ustar pbuserpbgroup00000000000000BEGIN:VCARD VERSION:3.0 UID:0 N:teste3;teste3;;; FN:teste3 teste3 X-QTPROJECT-FAVORITE:false;0 TEL:3333333 CATEGORIES:T REV:2015-06-15T08:00:00 END:VCARD BEGIN:VCARD VERSION:3.0 UID:0 N:test34;teste;;; FN:teste test34 X-QTPROJECT-FAVORITE:false;0 TEL:44444 CATEGORIES:T REV:2015-06-15T08:00:00 END:VCARD BEGIN:VCARD VERSION:3.0 UID:2 N:teste2;teste;;; FN:teste teste2 X-QTPROJECT-FAVORITE:false;0 TEL:111111 CATEGORIES:T REV:2015-06-15T10:00:00 X-QTPROJECT-EXTENDED-DETAIL:X-DELETED-AT;[\n "2015-06-15T10:00:00"\n]\n END:VCARD BEGIN:VCARD VERSION:3.0 UID:0 N:FirstName;LasteName;;; FN:FirstName LasteName TEL:1234567890 REV:2015-06-15T08:00:00 END:VCARD buteo-sync-plugins-contacts-0.1+16.04.20151211/tests/unittest/data/google_contact_created_page.txt0000644000015300001610000000335412632560105033435 0ustar pbuserpbgroup00000000000000 https://www.google.com/m8/feeds/contacts/renato%40gmail.com/full/batch/1435331592743000 2015-06-26T15:13:12.743Z Batch Feed qtcontacts:galera::f55c2eeb760ffd6843d2e98319d3544ff3e987b5 http://www.google.com/m8/feeds/contacts/renato.teste2%40gmail.com/base/5f66bbf78bed2765 2015-06-26T15:13:12.695Z 2015-06-26T15:13:12.695Z MyMy Loco MyMy Loco MyMy Loco ././@LongLink0000000000000000000000000000015300000000000011214 Lustar 00000000000000buteo-sync-plugins-contacts-0.1+16.04.20151211/tests/unittest/data/fast_sync_delete_same_contact_local.vcfbuteo-sync-plugins-contacts-0.1+16.04.20151211/tests/unittest/data/fast_sync_delete_same_contact_loc0000644000015300001610000000205412632560105034031 0ustar pbuserpbgroup00000000000000BEGIN:VCARD VERSION:3.0 UID:0 N:FirstName;LasteName;;; FN:FirstName LasteName TEL:1234567890 X-QTPROJECT-EXTENDED-DETAIL:X-REMOTE-ID;[\n "qtcontacts:mock:id=remote-so urce:5"\n]\n REV:2015-06-15T08:00:00 END:VCARD BEGIN:VCARD VERSION:3.0 UID:0 N:teste3;teste3;;; FN:teste3 teste3 X-QTPROJECT-FAVORITE:false;0 TEL:3333333 CATEGORIES:T X-QTPROJECT-EXTENDED-DETAIL:X-REMOTE-ID;[\n "qtcontacts:mock:id=remote-so urce:2"\n]\n REV:2015-06-15T08:00:00 END:VCARD BEGIN:VCARD VERSION:3.0 UID:0 N:test34;teste;;; FN:teste test34 X-QTPROJECT-FAVORITE:false;0 TEL:44444 CATEGORIES:T X-QTPROJECT-EXTENDED-DETAIL:X-REMOTE-ID;[\n "qtcontacts:mock:id=remote-so urce:3"\n]\n REV:2015-06-15T10:00:00 X-QTPROJECT-EXTENDED-DETAIL:X-DELETED-AT;[\n "2015-06-15T10:00:00"\n]\n END:VCARD BEGIN:VCARD VERSION:3.0 UID:2 N:teste2;teste;;; FN:teste teste2 X-QTPROJECT-FAVORITE:false;0 TEL:111111 CATEGORIES:T X-QTPROJECT-EXTENDED-DETAIL:X-REMOTE-ID;[\n "qtcontacts:mock:id=remote-so urce:4"\n]\n REV:2015-06-15T08:00:00 END:VCARD ././@LongLink0000000000000000000000000000015300000000000011214 Lustar 00000000000000buteo-sync-plugins-contacts-0.1+16.04.20151211/tests/unittest/data/local_previous_synced_remote_result.vcfbuteo-sync-plugins-contacts-0.1+16.04.20151211/tests/unittest/data/local_previous_synced_remote_resu0000644000015300001610000000101012632560105034134 0ustar pbuserpbgroup00000000000000BEGIN:VCARD VERSION:3.0 UID:0 N:teste3;teste3;;; FN:teste3 teste3 X-QTPROJECT-FAVORITE:false;0 TEL:3333333 CATEGORIES:T END:VCARD BEGIN:VCARD VERSION:3.0 UID:0 N:test34;teste;;; FN:teste test34 X-QTPROJECT-FAVORITE:false;0 TEL:44444 CATEGORIES:T END:VCARD BEGIN:VCARD VERSION:3.0 UID:2 N:teste2;teste;;; FN:teste teste2 X-QTPROJECT-FAVORITE:false;0 TEL:111111 CATEGORIES:T END:VCARD BEGIN:VCARD VERSION:3.0 UID:0 N:FirstName;LasteName;;; FN:FirstName LasteName TEL:1234567890 END:VCARD ././@LongLink0000000000000000000000000000015600000000000011217 Lustar 00000000000000buteo-sync-plugins-contacts-0.1+16.04.20151211/tests/unittest/data/fast_sync_with_a_remote_removal_remote.vcfbuteo-sync-plugins-contacts-0.1+16.04.20151211/tests/unittest/data/fast_sync_with_a_remote_removal_r0000644000015300001610000000127012632560105034105 0ustar pbuserpbgroup00000000000000BEGIN:VCARD VERSION:3.0 UID:0 N:teste3;teste3;;; FN:teste3 teste3 X-QTPROJECT-FAVORITE:false;0 TEL:3333333 CATEGORIES:T REV:2015-06-15T08:00:00 END:VCARD BEGIN:VCARD VERSION:3.0 UID:0 N:test34;teste;;; FN:teste test34 X-QTPROJECT-FAVORITE:false;0 TEL:44444 CATEGORIES:T REV:2015-06-15T10:00:00 X-QTPROJECT-EXTENDED-DETAIL:X-DELETED-AT;[\n "2015-06-15T10:00:00"\n]\n END:VCARD BEGIN:VCARD VERSION:3.0 UID:2 N:teste2;teste;;; FN:teste teste2 X-QTPROJECT-FAVORITE:false;0 TEL:111111 CATEGORIES:T REV:2015-06-15T08:00:00 END:VCARD BEGIN:VCARD VERSION:3.0 UID:0 N:FirstName;LasteName;;; FN:FirstName LasteName TEL:1234567890 REV:2015-06-15T08:00:00 END:VCARD ././@LongLink0000000000000000000000000000015600000000000011217 Lustar 00000000000000buteo-sync-plugins-contacts-0.1+16.04.20151211/tests/unittest/data/fast_sync_with_new_local_contact_local.vcfbuteo-sync-plugins-contacts-0.1+16.04.20151211/tests/unittest/data/fast_sync_with_new_local_contact_0000644000015300001610000000060312632560105034060 0ustar pbuserpbgroup00000000000000BEGIN:VCARD VERSION:3.0 UID:0 N:FirstName;LasteName;;; FN:FirstName LasteName TEL:1234567890 X-QTPROJECT-EXTENDED-DETAIL:X-REMOTE-ID;[\n "qtcontacts:mock:id=remote-so urce:0"\n]\n REV:2015-06-15T08:00:00 END:VCARD BEGIN:VCARD VERSION:3.0 UID:2 N:New;Contact;;; FN:Contact New X-QTPROJECT-FAVORITE:false;0 TEL:111111 CATEGORIES:T REV:2015-06-15T10:00:00 END:VCARD buteo-sync-plugins-contacts-0.1+16.04.20151211/tests/unittest/data/local_previous_synced_local.vcf0000644000015300001610000000031712632560105033463 0ustar pbuserpbgroup00000000000000BEGIN:VCARD VERSION:3.0 UID:0 N:FirstName;LasteName;;; FN:FirstName LasteName TEL:1234567890 X-QTPROJECT-EXTENDED-DETAIL:X-REMOTE-ID;[\n "qtcontacts:mock:id=remote-so urce:999"\n]\n END:VCARD ././@LongLink0000000000000000000000000000015400000000000011215 Lustar 00000000000000buteo-sync-plugins-contacts-0.1+16.04.20151211/tests/unittest/data/fast_sync_with_a_local_change_remote.vcfbuteo-sync-plugins-contacts-0.1+16.04.20151211/tests/unittest/data/fast_sync_with_a_local_change_rem0000644000015300001610000000115412632560105034007 0ustar pbuserpbgroup00000000000000BEGIN:VCARD VERSION:3.0 UID:0 N:teste3;teste3;;; FN:teste3 teste3 X-QTPROJECT-FAVORITE:false;0 TEL:3333333 CATEGORIES:T REV:2015-06-15T08:00:00 END:VCARD BEGIN:VCARD VERSION:3.0 UID:0 N:test34;teste;;; FN:teste test34 X-QTPROJECT-FAVORITE:false;0 TEL:44444 CATEGORIES:T REV:2015-06-15T08:00:00 END:VCARD BEGIN:VCARD VERSION:3.0 UID:2 N:teste2;teste;;; FN:teste teste2 X-QTPROJECT-FAVORITE:false;0 TEL:111111 CATEGORIES:T REV:2015-06-15T08:00:00 END:VCARD BEGIN:VCARD VERSION:3.0 UID:0 N:FirstName;LasteName;;; FN:FirstName LasteName TEL:1234567890 REV:2015-06-15T08:00:00 END:VCARD ././@LongLink0000000000000000000000000000016200000000000011214 Lustar 00000000000000buteo-sync-plugins-contacts-0.1+16.04.20151211/tests/unittest/data/fast_sync_changed_both_sides_remote_result.vcfbuteo-sync-plugins-contacts-0.1+16.04.20151211/tests/unittest/data/fast_sync_changed_both_sides_remo0000644000015300001610000000043712632560105034033 0ustar pbuserpbgroup00000000000000BEGIN:VCARD VERSION:3.0 UID:0 N:New;Name;;; FN:Name New X-QTPROJECT-FAVORITE:false;0 TEL:44444 CATEGORIES:T REV:2015-06-15T10:00:00 END:VCARD BEGIN:VCARD VERSION:3.0 UID:0 N:FirstName;LasteName;;; FN:FirstName LasteName TEL:1234567890 REV:2015-06-15T08:00:00 END:VCARD ././@LongLink0000000000000000000000000000015000000000000011211 Lustar 00000000000000buteo-sync-plugins-contacts-0.1+16.04.20151211/tests/unittest/data/google_contact_full_fetch_page_0.txtbuteo-sync-plugins-contacts-0.1+16.04.20151211/tests/unittest/data/google_contact_full_fetch_page_0.0000644000015300001610000004750012632560105033621 0ustar pbuserpbgroup00000000000000 renato.teste2@gmail.com 2015-06-18T19:25:40.490Z Filho Renato's Contacts Filho Renato renato.teste2@gmail.com Contacts 15 1 10 http://www.google.com/m8/feeds/contacts/renato.teste2%40gmail.com/base/6c224c08aaf939e 2015-06-18T16:13:40.822Z 2015-06-18T16:13:40.822Z Abby Knorr Abby Knorr Abby Knorr (07880) 6055638 +49-183-3155687 Im Kirberg 119 42864 Deutschland Im Kirberg 119 42864 Deutschland http://www.google.com/m8/feeds/contacts/renato.teste2%40gmail.com/base/948ac108f03db45 2015-06-18T16:13:40.822Z 2015-06-18T16:13:40.822Z Aaron Rossler Aaron Rossler Aaron Rossler +49-2345-76447801 +49-1266-53332172 Kandinskystr. 50c 85459 Deutschland Kandinskystr. 50c 85459 Deutschland http://www.google.com/m8/feeds/contacts/renato.teste2%40gmail.com/base/2a00fea80ab5770b 2015-06-18T16:13:40.822Z 2015-06-18T16:13:40.822Z Abdullah Grundmann Abdullah Grundmann Abdullah Grundmann +49-160-9044582 +49-654-1090618 Halligstr. 5 82489 Deutschland Halligstr. 5 82489 Deutschland http://www.google.com/m8/feeds/contacts/renato.teste2%40gmail.com/base/2b6a32d7085b9c93 2015-06-18T16:13:40.822Z 2015-06-18T16:13:40.822Z Ada Fried Ada Fried Ada Fried +49-148-1104290 (0188) 113931757 Sürderstr. 1 89919 Deutschland Sürderstr. 1 89919 Deutschland http://www.google.com/m8/feeds/contacts/renato.teste2%40gmail.com/base/2de5c02a089277f6 2015-06-18T19:12:11.717Z 2015-06-18T19:12:11.717Z Arwen Lauckner Arwen Lauckner Arwen Lauckner +49-6665-76654489 +49-162-5456933 Karl-Bückart-Str. 92a Bad Lucyland 35195 Deutschland Karl-Bückart-Str. 92a Bad Lucyland 35195 Deutschland http://www.google.com/m8/feeds/contacts/renato.teste2%40gmail.com/base/359c71990888c005 2015-06-18T19:11:24.575Z 2015-06-18T19:11:24.575Z Ashley Klemme Ashley Klemme Ashley Klemme +49-976-1795496 +49-1605-24722953 Gierener Weg 42a Neu Yvenscheid 40412 Deutschland Gierener Weg 42a Neu Yvenscheid 40412 Deutschland http://www.google.com/m8/feeds/contacts/renato.teste2%40gmail.com/base/379274bc0b5963e0 2015-06-18T19:12:00.914Z 2015-06-18T19:12:00.914Z Abby Tillack Abby Tillack Abby Tillack +49-124-2345121 (0935) 490770932 Tannenbergstr. 24b 45683 Deutschland Tannenbergstr. 24b 45683 Deutschland http://www.google.com/m8/feeds/contacts/renato.teste2%40gmail.com/base/3ffb9d0e0877f1b1 2015-06-18T16:13:40.822Z 2015-06-18T16:13:40.822Z Adrian Steinert Adrian Steinert Adrian Steinert +49-145-3500799 (08312) 0553652 Freudenthal 84c 52287 Deutschland Freudenthal 84c 52287 Deutschland http://www.google.com/m8/feeds/contacts/renato.teste2%40gmail.com/base/5014925909af0d19 2015-06-18T16:13:40.822Z 2015-06-18T16:13:40.822Z Adam Linke Adam Linke Adam Linke +49-4133-15890584 +49-135-6314318 Fichtenweg 63 62240 Deutschland Fichtenweg 63 62240 Deutschland http://www.google.com/m8/feeds/contacts/renato.teste2%40gmail.com/base/54d62aa10a68c0a0 2015-06-18T19:12:23.804Z 2015-06-18T19:12:23.804Z Aurora Hadfield Aurora Hadfield Aurora Hadfield +49-905-1913343 +49-182-8748386 Weinhäuser Str. 4 Süd Bennet 66856 Deutschland Weinhäuser Str. 4 Süd Bennet 66856 Deutschland ././@LongLink0000000000000000000000000000015200000000000011213 Lustar 00000000000000buteo-sync-plugins-contacts-0.1+16.04.20151211/tests/unittest/data/local_previous_synced_local_result.vcfbuteo-sync-plugins-contacts-0.1+16.04.20151211/tests/unittest/data/local_previous_synced_local_resul0000644000015300001610000000154012632560105034117 0ustar pbuserpbgroup00000000000000BEGIN:VCARD VERSION:3.0 N:FirstName;LasteName;;; FN:FirstName LasteName TEL:1234567890 X-QTPROJECT-EXTENDED-DETAIL:X-REMOTE-ID;[\n "qtcontacts:mock:id=remote-so urce:5"\n]\n END:VCARD BEGIN:VCARD VERSION:3.0 N:teste3;teste3;;; FN:teste3 teste3 X-QTPROJECT-FAVORITE:false;0 TEL:3333333 CATEGORIES:T X-QTPROJECT-EXTENDED-DETAIL:X-REMOTE-ID;[\n "qtcontacts:mock:id=remote-so urce:2"\n]\n END:VCARD BEGIN:VCARD VERSION:3.0 N:test34;teste;;; FN:teste test34 X-QTPROJECT-FAVORITE:false;0 TEL:44444 CATEGORIES:T X-QTPROJECT-EXTENDED-DETAIL:X-REMOTE-ID;[\n "qtcontacts:mock:id=remote-so urce:3"\n]\n END:VCARD BEGIN:VCARD VERSION:3.0 N:teste2;teste;;; FN:teste teste2 X-QTPROJECT-FAVORITE:false;0 TEL:111111 CATEGORIES:T X-QTPROJECT-EXTENDED-DETAIL:X-REMOTE-ID;[\n "qtcontacts:mock:id=remote-so urce:4"\n]\n END:VCARD ././@LongLink0000000000000000000000000000015300000000000011214 Lustar 00000000000000buteo-sync-plugins-contacts-0.1+16.04.20151211/tests/unittest/data/fast_sync_with_a_local_change_local.vcfbuteo-sync-plugins-contacts-0.1+16.04.20151211/tests/unittest/data/fast_sync_with_a_local_change_loc0000644000015300001610000000173012632560105034001 0ustar pbuserpbgroup00000000000000BEGIN:VCARD VERSION:3.0 UID:0 N:FirstName;LasteName;;; FN:FirstName LasteName TEL:1234567890 X-QTPROJECT-EXTENDED-DETAIL:X-REMOTE-ID;[\n "qtcontacts:mock:id=remote-so urce:5"\n]\n REV:2015-06-15T08:00:00 END:VCARD BEGIN:VCARD VERSION:3.0 UID:0 N:teste3;teste3;;; FN:teste3 teste3 X-QTPROJECT-FAVORITE:false;0 TEL:3333333 CATEGORIES:T X-QTPROJECT-EXTENDED-DETAIL:X-REMOTE-ID;[\n "qtcontacts:mock:id=remote-so urce:2"\n]\n REV:2015-06-15T08:00:00 END:VCARD BEGIN:VCARD VERSION:3.0 UID:0 N:New;Name;;; FN:Name New X-QTPROJECT-FAVORITE:false;0 TEL:44444 CATEGORIES:T X-QTPROJECT-EXTENDED-DETAIL:X-REMOTE-ID;[\n "qtcontacts:mock:id=remote-so urce:3"\n]\n REV:2015-06-15T10:00:00 END:VCARD BEGIN:VCARD VERSION:3.0 UID:2 N:teste2;teste;;; FN:teste teste2 X-QTPROJECT-FAVORITE:false;0 TEL:111111 CATEGORIES:T X-QTPROJECT-EXTENDED-DETAIL:X-REMOTE-ID;[\n "qtcontacts:mock:id=remote-so urce:4"\n]\n REV:2015-06-15T08:00:00 END:VCARD ././@LongLink0000000000000000000000000000016200000000000011214 Lustar 00000000000000buteo-sync-plugins-contacts-0.1+16.04.20151211/tests/unittest/data/fast_sync_delete_same_contact_local_result.vcfbuteo-sync-plugins-contacts-0.1+16.04.20151211/tests/unittest/data/fast_sync_delete_same_contact_loc0000644000015300001610000000134312632560105034031 0ustar pbuserpbgroup00000000000000BEGIN:VCARD VERSION:3.0 UID:0 N:FirstName;LasteName;;; FN:FirstName LasteName TEL:1234567890 X-QTPROJECT-EXTENDED-DETAIL:X-REMOTE-ID;[\n "qtcontacts:mock:id=remote-so urce:5"\n]\n REV:2015-06-15T08:00:00 END:VCARD BEGIN:VCARD VERSION:3.0 UID:0 N:teste3;teste3;;; FN:teste3 teste3 X-QTPROJECT-FAVORITE:false;0 TEL:3333333 CATEGORIES:T X-QTPROJECT-EXTENDED-DETAIL:X-REMOTE-ID;[\n "qtcontacts:mock:id=remote-so urce:2"\n]\n REV:2015-06-15T08:00:00 END:VCARD BEGIN:VCARD VERSION:3.0 UID:2 N:teste2;teste;;; FN:teste teste2 X-QTPROJECT-FAVORITE:false;0 TEL:111111 CATEGORIES:T X-QTPROJECT-EXTENDED-DETAIL:X-REMOTE-ID;[\n "qtcontacts:mock:id=remote-so urce:4"\n]\n REV:2015-06-15T08:00:00 END:VCARD ././@LongLink0000000000000000000000000000016300000000000011215 Lustar 00000000000000buteo-sync-plugins-contacts-0.1+16.04.20151211/tests/unittest/data/fast_sync_with_a_local_removal_local_result.vcfbuteo-sync-plugins-contacts-0.1+16.04.20151211/tests/unittest/data/fast_sync_with_a_local_removal_lo0000644000015300001610000000134312632560105034056 0ustar pbuserpbgroup00000000000000BEGIN:VCARD VERSION:3.0 UID:0 N:FirstName;LasteName;;; FN:FirstName LasteName TEL:1234567890 X-QTPROJECT-EXTENDED-DETAIL:X-REMOTE-ID;[\n "qtcontacts:mock:id=remote-so urce:5"\n]\n REV:2015-06-15T08:00:00 END:VCARD BEGIN:VCARD VERSION:3.0 UID:0 N:teste3;teste3;;; FN:teste3 teste3 X-QTPROJECT-FAVORITE:false;0 TEL:3333333 CATEGORIES:T X-QTPROJECT-EXTENDED-DETAIL:X-REMOTE-ID;[\n "qtcontacts:mock:id=remote-so urce:2"\n]\n REV:2015-06-15T08:00:00 END:VCARD BEGIN:VCARD VERSION:3.0 UID:2 N:teste2;teste;;; FN:teste teste2 X-QTPROJECT-FAVORITE:false;0 TEL:111111 CATEGORIES:T X-QTPROJECT-EXTENDED-DETAIL:X-REMOTE-ID;[\n "qtcontacts:mock:id=remote-so urce:4"\n]\n REV:2015-06-15T08:00:00 END:VCARD buteo-sync-plugins-contacts-0.1+16.04.20151211/tests/unittest/data/slow_sync_with_pages_remote.vcf0000644000015300001610000000644612632560105033534 0ustar pbuserpbgroup00000000000000BEGIN:VCARD VERSION:3.0 FN:Aaron Hentschke N:Hentschke;Aaron;;; EMAIL;TYPE=INTERNET:hentschke@hildenbrand.org TEL;TYPE=CELL:+49-178-2572849 TEL;TYPE=WORK:(05530) 8188983 ADR;TYPE=HOME:;;Moosweg 33c;;;81232;Deutschland END:VCARD BEGIN:VCARD VERSION:3.0 FN:Aaron Rossler N:Rossler;Aaron;;; EMAIL;TYPE=INTERNET:rossler@jegorovgruber.info TEL:+49-2345-76447801 TEL:+49-1266-53332172 ADR:;;Kandinskystr. 50c;;;85459;Deutschland END:VCARD BEGIN:VCARD VERSION:3.0 FN:Abby Burkhard N:Burkhard;Abby;;; EMAIL;TYPE=INTERNET:burkhard@schlachtervogelgsang.ch TEL:+49-1270-18191872 TEL:(0165) 571583823 ADR:;;Bergische Landstr. 31a;;;63108;Deutschland END:VCARD BEGIN:VCARD VERSION:3.0 FN:Abby Knorr N:Knorr;Abby;;; EMAIL;TYPE=INTERNET:knorr@lorenz.com TEL:(07880) 6055638 TEL:+49-183-3155687 ADR:;;Im Kirberg 119;;;42864;Deutschland END:VCARD BEGIN:VCARD VERSION:3.0 FN:Abby Tillack N:Tillack;Abby;;; EMAIL;TYPE=INTERNET:tillack@seitz.de TEL;TYPE=WORK;TYPE=FAX:+49-124-2345121 TEL;TYPE=HOME;TYPE=FAX:(0935) 490770932 ADR;TYPE=WORK:;;Tannenbergstr. 24b;;;45683;Deutschland END:VCARD BEGIN:VCARD VERSION:3.0 FN:Abdul Feld N:Feld;Abdul;;; EMAIL;TYPE=INTERNET:feld@prediger.org TEL:+49-7197-68234855 TEL:+49-198-5948206 ADR:;;Albin-Edelmann-Str. 98c;;;37466;Deutschland END:VCARD BEGIN:VCARD VERSION:3.0 FN:Abdullah Grundmann N:Grundmann;Abdullah;;; EMAIL;TYPE=INTERNET:grundmann@timmermann.com TEL:+49-160-9044582 TEL:+49-654-1090618 ADR:;;Halligstr. 5;;;82489;Deutschland END:VCARD BEGIN:VCARD VERSION:3.0 FN:Ada Fried N:Fried;Ada;;; EMAIL;TYPE=INTERNET:fried@schwerdtnerschupp.ch TEL;TYPE=HOME:+49-148-1104290 TEL;TYPE=HOME:(0188) 113931757 ADR:;;Sürderstr. 1;;;89919;Deutschland END:VCARD BEGIN:VCARD VERSION:3.0 FN:Adam Linke N:Linke;Adam;;; EMAIL;TYPE=INTERNET:linke@frantz.info TEL:+49-4133-15890584 TEL:+49-135-6314318 ADR:;;Fichtenweg 63;;;62240;Deutschland END:VCARD BEGIN:VCARD VERSION:3.0 FN:Adrian Steinert N:Steinert;Adrian;;; EMAIL;TYPE=INTERNET:steinert@holinski.info TEL:+49-145-3500799 TEL:(08312) 0553652 ADR:;;Freudenthal 84c;;;52287;Deutschland END:VCARD BEGIN:VCARD VERSION:3.0 FN:Arved Menga N:Menga;Arved;;; EMAIL;TYPE=INTERNET:menga@pomp.name item1.TEL:+49-5499-63405807 item1.X-ABLabel:home item2.TEL:+49-1134-87938626 item2.X-ABLabel:cell ADR:;;Gezelinallee 33;West Miekeburg;;62583;Deutschland END:VCARD BEGIN:VCARD VERSION:3.0 FN:Arvid Herzenberg N:Herzenberg;Arvid;;; EMAIL;TYPE=INTERNET:herzenberg@brutigam.com item1.TEL:+49-7780-75716285 item1.X-ABLabel:home item2.TEL:+49-1402-29654218 item2.X-ABLabel:cell ADR:;;Schäfershütte 124;Kühneldorf;;15713;Deutschland END:VCARD BEGIN:VCARD VERSION:3.0 FN:Arwen Lauckner N:Lauckner;Arwen;;; EMAIL;TYPE=INTERNET:lauckner@grau.de item1.TEL:+49-6665-76654489 item1.X-ABLabel:home item2.TEL:+49-162-5456933 item2.X-ABLabel:cell ADR;TYPE=HOME:;;Karl-Bückart-Str. 92a;Bad Lucyland;;35195;Deutschland END:VCARD BEGIN:VCARD VERSION:3.0 FN:Ashley Klemme N:Klemme;Ashley;;; EMAIL;TYPE=INTERNET:klemme@reuss.info item1.TEL:+49-976-1795496 item1.X-ABLabel:home item2.TEL:+49-1605-24722953 item2.X-ABLabel:cell ADR:;;Gierener Weg 42a;Neu Yvenscheid;;40412;Deutschland END:VCARD BEGIN:VCARD VERSION:3.0 FN:Aurora Hadfield N:Hadfield;Aurora;;; EMAIL;TYPE=INTERNET:hadfield@prey.org TEL;TYPE=HOME:+49-905-1913343 TEL;TYPE=CELL:+49-182-8748386 ADR;TYPE=HOME:;;Weinhäuser Str. 4;Süd Bennet;;66856;Deutschland END:VCARD ././@LongLink0000000000000000000000000000015400000000000011215 Lustar 00000000000000buteo-sync-plugins-contacts-0.1+16.04.20151211/tests/unittest/data/fast_sync_with_a_remote_change_local.vcfbuteo-sync-plugins-contacts-0.1+16.04.20151211/tests/unittest/data/fast_sync_with_a_remote_change_lo0000644000015300001610000000173112632560105034040 0ustar pbuserpbgroup00000000000000BEGIN:VCARD VERSION:3.0 UID:0 N:FirstName;LasteName;;; FN:FirstName LasteName TEL:1234567890 X-QTPROJECT-EXTENDED-DETAIL:X-REMOTE-ID;[\n "qtcontacts:mock:id=remote-so urce:5"\n]\n REV:2015-06-15T08:00:00 END:VCARD BEGIN:VCARD VERSION:3.0 UID:0 N:teste3;teste3;;; FN:teste3 teste3 X-QTPROJECT-FAVORITE:false;0 TEL:3333333 CATEGORIES:T X-QTPROJECT-EXTENDED-DETAIL:X-REMOTE-ID;[\n "qtcontacts:mock:id=remote-so urce:2"\n]\n REV:2015-06-15T08:00:00 END:VCARD BEGIN:VCARD VERSION:3.0 N:test34;teste;;; FN:teste test34 X-QTPROJECT-FAVORITE:false;0 TEL:44444 CATEGORIES:T X-QTPROJECT-EXTENDED-DETAIL:X-REMOTE-ID;[\n "qtcontacts:mock:id=remote-so urce:3"\n]\n REV:2015-06-15T08:00:00 END:VCARD BEGIN:VCARD VERSION:3.0 UID:2 N:teste2;teste;;; FN:teste teste2 X-QTPROJECT-FAVORITE:false;0 TEL:111111 CATEGORIES:T X-QTPROJECT-EXTENDED-DETAIL:X-REMOTE-ID;[\n "qtcontacts:mock:id=remote-so urce:4"\n]\n REV:2015-06-15T08:00:00 END:VCARD ././@LongLink0000000000000000000000000000015600000000000011217 Lustar 00000000000000buteo-sync-plugins-contacts-0.1+16.04.20151211/tests/unittest/data/fast_sync_without_changes_local_result.vcfbuteo-sync-plugins-contacts-0.1+16.04.20151211/tests/unittest/data/fast_sync_without_changes_local_r0000644000015300001610000000174012632560105034101 0ustar pbuserpbgroup00000000000000BEGIN:VCARD VERSION:3.0 UID:0 N:FirstName;LasteName;;; FN:FirstName LasteName TEL:1234567890 X-QTPROJECT-EXTENDED-DETAIL:X-REMOTE-ID;[\n "qtcontacts:mock:id=remote-so urce:5"\n]\n REV:2015-06-15T08:00:00 END:VCARD BEGIN:VCARD VERSION:3.0 UID:0 N:teste3;teste3;;; FN:teste3 teste3 X-QTPROJECT-FAVORITE:false;0 TEL:3333333 CATEGORIES:T X-QTPROJECT-EXTENDED-DETAIL:X-REMOTE-ID;[\n "qtcontacts:mock:id=remote-so urce:2"\n]\n REV:2015-06-15T08:00:00 END:VCARD BEGIN:VCARD VERSION:3.0 UID:0 N:test34;teste;;; FN:teste test34 X-QTPROJECT-FAVORITE:false;0 TEL:44444 CATEGORIES:T X-QTPROJECT-EXTENDED-DETAIL:X-REMOTE-ID;[\n "qtcontacts:mock:id=remote-so urce:3"\n]\n REV:2015-06-15T08:00:00 END:VCARD BEGIN:VCARD VERSION:3.0 UID:2 N:teste2;teste;;; FN:teste teste2 X-QTPROJECT-FAVORITE:false;0 TEL:111111 CATEGORIES:T X-QTPROJECT-EXTENDED-DETAIL:X-REMOTE-ID;[\n "qtcontacts:mock:id=remote-so urce:4"\n]\n REV:2015-06-15T08:00:00 END:VCARD buteo-sync-plugins-contacts-0.1+16.04.20151211/tests/unittest/data/google_contact_full_fetch.vcf0000644000015300001610000001624412632560105033106 0ustar pbuserpbgroup00000000000000BEGIN:VCARD VERSION:3.0 REV:2015-06-18T16:13:40.822Z PHOTO;TYPE=URL:https://www.google.com/m8/feeds/photos/media/renato.teste2%40 gmail.com/6c224c08aaf939e X-QTPROJECT-EXTENDED-DETAIL:X-AVATAR-REV;[\n "6c224c08aaf912"\n]\n N:Knorr;Abby;;; EMAIL;TYPE=OTHER:knorr@lorenz.com TEL;TYPE=VOICE,OTHER:(07880) 6055638 TEL;TYPE=VOICE,OTHER:+49-183-3155687 ADR;TYPE=OTHER:;;Im Kirberg 119;;;42864;Deutschland X-QTPROJECT-EXTENDED-DETAIL:X-GROUP-ID;[\n "6"\n]\n X-QTPROJECT-EXTENDED-DETAIL:X-GOOGLE-ETAG;[\n "\\"QXY5ejVSLit7I2A9XRVXFkk ORwY.\\""\n]\n X-QTPROJECT-EXTENDED-DETAIL:X-REMOTE-ID;[\n "6c224c08aaf939e"\n]\n END:VCARD BEGIN:VCARD VERSION:3.0 REV:2015-06-18T20:30:37Z N:Rossler;Aaron;;; EMAIL;TYPE=OTHER:rossler@jegorovgruber.info TEL;TYPE=VOICE,OTHER:+49-2345-76447801 TEL;TYPE=VOICE,OTHER:+49-1266-53332172 ADR;TYPE=OTHER:;;Kandinskystr. 50c;;;85459;Deutschland X-QTPROJECT-EXTENDED-DETAIL:X-GROUP-ID;[\n "6"\n]\n X-QTPROJECT-EXTENDED-DETAIL:X-GOOGLE-ETAG;[\n "\\"QXY5ejVSLit7I2A9XRVXFkk ORwY.\\""\n]\n X-QTPROJECT-EXTENDED-DETAIL:X-REMOTE-ID;[\n "948ac108f03db45"\n]\n END:VCARD BEGIN:VCARD VERSION:3.0 REV:2015-06-18T20:30:37Z N:Grundmann;Abdullah;;; EMAIL;TYPE=OTHER:grundmann@timmermann.com TEL;TYPE=VOICE,OTHER:+49-160-9044582 TEL;TYPE=VOICE,OTHER:+49-654-1090618 ADR;TYPE=OTHER:;;Halligstr. 5;;;82489;Deutschland X-QTPROJECT-EXTENDED-DETAIL:X-GROUP-ID;[\n "6"\n]\n X-QTPROJECT-EXTENDED-DETAIL:X-GOOGLE-ETAG;[\n "\\"QXY5ejVSLit7I2A9XRVXFkk ORwY.\\""\n]\n X-QTPROJECT-EXTENDED-DETAIL:X-REMOTE-ID;[\n "2a00fea80ab5770b"\n]\n END:VCARD BEGIN:VCARD VERSION:3.0 REV:2015-06-18T20:30:37Z N:Fried;Ada;;; EMAIL;TYPE=OTHER:fried@schwerdtnerschupp.ch TEL;TYPE=VOICE,HOME:+49-148-1104290 TEL;TYPE=VOICE,HOME:(0188) 113931757 ADR;TYPE=OTHER:;;Sürderstr. 1;;;89919;Deutschland X-QTPROJECT-EXTENDED-DETAIL:X-GROUP-ID;[\n "6"\n]\n X-QTPROJECT-EXTENDED-DETAIL:X-GOOGLE-ETAG;[\n "\\"QXY5ejVSLit7I2A9XRVXFkk ORwY.\\""\n]\n X-QTPROJECT-EXTENDED-DETAIL:X-REMOTE-ID;[\n "2b6a32d7085b9c93"\n]\n END:VCARD BEGIN:VCARD VERSION:3.0 REV:2015-06-18T20:30:37Z N:Lauckner;Arwen;;; EMAIL;TYPE=OTHER:lauckner@grau.de TEL;TYPE=VOICE,HOME:+49-6665-76654489 TEL;TYPE=CELL:+49-162-5456933 ADR;TYPE=HOME:;;Karl-Bückart-Str. 92a;;Bad Lucyland;35195;Deutschland X-QTPROJECT-EXTENDED-DETAIL:X-GROUP-ID;[\n "12b89a8e8c78a7e8"\n]\n X-QTPROJECT-EXTENDED-DETAIL:X-GROUP-ID;[\n "6"\n]\n X-QTPROJECT-EXTENDED-DETAIL:X-GOOGLE-ETAG;[\n "\\"QHk6fzVSLit7I2A9XRVXFkg OQAc.\\""\n]\n X-QTPROJECT-EXTENDED-DETAIL:X-REMOTE-ID;[\n "2de5c02a089277f6"\n]\n END:VCARD BEGIN:VCARD VERSION:3.0 REV:2015-06-18T20:30:37Z N:Klemme;Ashley;;; EMAIL;TYPE=OTHER:klemme@reuss.info TEL;TYPE=VOICE,OTHER:+49-976-1795496 TEL;TYPE=VOICE,OTHER:+49-1605-24722953 ADR;TYPE=OTHER:;;Gierener Weg 42a;;Neu Yvenscheid;40412;Deutschland X-QTPROJECT-EXTENDED-DETAIL:X-GROUP-ID;[\n "12b89a8e8c78a7e8"\n]\n X-QTPROJECT-EXTENDED-DETAIL:X-GROUP-ID;[\n "6"\n]\n X-QTPROJECT-EXTENDED-DETAIL:X-GOOGLE-ETAG;[\n "\\"RXs8fTVSLit7I2A9XRVXFkg OQQw.\\""\n]\n X-QTPROJECT-EXTENDED-DETAIL:X-REMOTE-ID;[\n "359c71990888c005"\n]\n END:VCARD BEGIN:VCARD VERSION:3.0 REV:2015-06-18T20:30:37Z N:Tillack;Abby;;; EMAIL;TYPE=OTHER:tillack@seitz.de TEL;TYPE=FAX,WORK:+49-124-2345121 TEL;TYPE=FAX,HOME:(0935) 490770932 ADR;TYPE=WORK:;;Tannenbergstr. 24b;;;45683;Deutschland X-QTPROJECT-EXTENDED-DETAIL:X-GROUP-ID;[\n "6"\n]\n X-QTPROJECT-EXTENDED-DETAIL:X-GOOGLE-ETAG;[\n "\\"QXc6fDVSLit7I2A9XRVXFkg OQAY.\\""\n]\n X-QTPROJECT-EXTENDED-DETAIL:X-REMOTE-ID;[\n "379274bc0b5963e0"\n]\n END:VCARD BEGIN:VCARD VERSION:3.0 REV:2015-06-18T20:30:37Z N:Steinert;Adrian;;; EMAIL;TYPE=OTHER:steinert@holinski.info TEL;TYPE=VOICE,OTHER:+49-145-3500799 TEL;TYPE=VOICE,OTHER:(08312) 0553652 ADR;TYPE=OTHER:;;Freudenthal 84c;;;52287;Deutschland X-QTPROJECT-EXTENDED-DETAIL:X-GROUP-ID;[\n "6"\n]\n X-QTPROJECT-EXTENDED-DETAIL:X-GOOGLE-ETAG;[\n "\\"QXY5ejVSLit7I2A9XRVXFkk ORwY.\\""\n]\n X-QTPROJECT-EXTENDED-DETAIL:X-REMOTE-ID;[\n "3ffb9d0e0877f1b1"\n]\n END:VCARD BEGIN:VCARD VERSION:3.0 REV:2015-06-18T20:30:37Z N:Linke;Adam;;; EMAIL;TYPE=OTHER:linke@frantz.info TEL;TYPE=VOICE,OTHER:+49-4133-15890584 TEL;TYPE=VOICE,OTHER:+49-135-6314318 ADR;TYPE=OTHER:;;Fichtenweg 63;;;62240;Deutschland X-QTPROJECT-EXTENDED-DETAIL:X-GROUP-ID;[\n "6"\n]\n X-QTPROJECT-EXTENDED-DETAIL:X-GOOGLE-ETAG;[\n "\\"QXY5ejVSLit7I2A9XRVXFkk ORwY.\\""\n]\n X-QTPROJECT-EXTENDED-DETAIL:X-REMOTE-ID;[\n "5014925909af0d19"\n]\n END:VCARD BEGIN:VCARD VERSION:3.0 REV:2015-06-18T20:30:37Z N:Hadfield;Aurora;;; EMAIL;TYPE=OTHER:hadfield@prey.org TEL;TYPE=VOICE,HOME:+49-905-1913343 TEL;TYPE=CELL:+49-182-8748386 ADR;TYPE=HOME:;;Weinhäuser Str. 4;;Süd Bennet;66856;Deutschland X-QTPROJECT-EXTENDED-DETAIL:X-GROUP-ID;[\n "6"\n]\n X-QTPROJECT-EXTENDED-DETAIL:X-GOOGLE-ETAG;[\n "\\"QnY7fDVSLyt7I2A9XRVXFkg OQAA.\\""\n]\n X-QTPROJECT-EXTENDED-DETAIL:X-REMOTE-ID;[\n "54d62aa10a68c0a0"\n]\n END:VCARD BEGIN:VCARD VERSION:3.0 REV:2015-06-18T20:30:37Z N:Hentschke;Aaron;;; EMAIL;TYPE=OTHER:hentschke@hildenbrand.org TEL;TYPE=CELL:+49-178-2572849 TEL;TYPE=VOICE,WORK:(05530) 8188983 ADR;TYPE=HOME:;;Moosweg 33c;;;81232;Deutschland X-QTPROJECT-EXTENDED-DETAIL:X-GROUP-ID;[\n "6"\n]\n X-QTPROJECT-EXTENDED-DETAIL:X-GOOGLE-ETAG;[\n "\\"Rn09cDVSLit7I2A9XRVXFkg OQAQ.\\""\n]\n X-QTPROJECT-EXTENDED-DETAIL:X-REMOTE-ID;[\n "604d67ec8adf79bc"\n]\n END:VCARD BEGIN:VCARD VERSION:3.0 REV:2015-06-18T20:30:37Z N:Feld;Abdul;;; EMAIL;TYPE=OTHER:feld@prediger.org TEL;TYPE=VOICE,OTHER:+49-7197-68234855 TEL;TYPE=VOICE,OTHER:+49-198-5948206 ADR;TYPE=OTHER:;;Albin-Edelmann-Str. 98c;;;37466;Deutschland X-QTPROJECT-EXTENDED-DETAIL:X-GROUP-ID;[\n "6"\n]\n X-QTPROJECT-EXTENDED-DETAIL:X-GOOGLE-ETAG;[\n "\\"QXY5ejVSLit7I2A9XRVXFkk ORwY.\\""\n]\n X-QTPROJECT-EXTENDED-DETAIL:X-REMOTE-ID;[\n "763e9f2d08921a16"\n]\n END:VCARD BEGIN:VCARD VERSION:3.0 REV:2015-06-18T20:30:37Z N:Burkhard;Abby;;; EMAIL;TYPE=OTHER:burkhard@schlachtervogelgsang.ch TEL;TYPE=VOICE,OTHER:+49-1270-18191872 TEL;TYPE=VOICE,OTHER:(0165) 571583823 ADR;TYPE=OTHER:;;Bergische Landstr. 31a;;;63108;Deutschland X-QTPROJECT-EXTENDED-DETAIL:X-GROUP-ID;[\n "6"\n]\n X-QTPROJECT-EXTENDED-DETAIL:X-GOOGLE-ETAG;[\n "\\"QXY5ejVSLit7I2A9XRVXFkk ORwY.\\""\n]\n X-QTPROJECT-EXTENDED-DETAIL:X-REMOTE-ID;[\n "76af90190bfd1a3f"\n]\n END:VCARD BEGIN:VCARD VERSION:3.0 REV:2015-06-18T19:11:24.575Z N:Menga;Arved;;; EMAIL;TYPE=OTHER:menga@pomp.name TEL;TYPE=VOICE,OTHER:+49-5499-63405807 TEL;TYPE=VOICE,OTHER:+49-1134-87938626 ADR;TYPE=OTHER:;;Gezelinallee 33;;West Miekeburg;62583;Deutschland X-QTPROJECT-EXTENDED-DETAIL:X-GROUP-ID;[\n "6"\n]\n X-QTPROJECT-EXTENDED-DETAIL:X-GOOGLE-ETAG;[\n "\\"RXs8fTVSLit7I2A9XRVXFkg OQQw.\\""\n]\n X-QTPROJECT-EXTENDED-DETAIL:X-REMOTE-ID;[\n "7848b97f0879cfdc"\n]\n END:VCARD BEGIN:VCARD VERSION:3.0 REV:2015-06-18T20:30:37Z N:Herzenberg;Arvid;;; EMAIL;TYPE=OTHER:herzenberg@brutigam.com TEL;TYPE=VOICE,OTHER:+49-7780-75716285 TEL;TYPE=VOICE,OTHER:+49-1402-29654218 ADR;TYPE=OTHER:;;Schäfershütte 124;;Kühneldorf;15713;Deutschland X-QTPROJECT-EXTENDED-DETAIL:X-GROUP-ID;[\n "6"\n]\n X-QTPROJECT-EXTENDED-DETAIL:X-GOOGLE-ETAG;[\n "\\"RXs8fTVSLit7I2A9XRVXFkg OQQw.\\""\n]\n X-QTPROJECT-EXTENDED-DETAIL:X-REMOTE-ID;[\n "7da271258ea03b89"\n]\n END:VCARD ././@LongLink0000000000000000000000000000015400000000000011215 Lustar 00000000000000buteo-sync-plugins-contacts-0.1+16.04.20151211/tests/unittest/data/fast_sync_with_a_local_removal_local.vcfbuteo-sync-plugins-contacts-0.1+16.04.20151211/tests/unittest/data/fast_sync_with_a_local_removal_lo0000644000015300001610000000205412632560105034056 0ustar pbuserpbgroup00000000000000BEGIN:VCARD VERSION:3.0 UID:0 N:FirstName;LasteName;;; FN:FirstName LasteName TEL:1234567890 X-QTPROJECT-EXTENDED-DETAIL:X-REMOTE-ID;[\n "qtcontacts:mock:id=remote-so urce:5"\n]\n REV:2015-06-15T08:00:00 END:VCARD BEGIN:VCARD VERSION:3.0 UID:0 N:teste3;teste3;;; FN:teste3 teste3 X-QTPROJECT-FAVORITE:false;0 TEL:3333333 CATEGORIES:T X-QTPROJECT-EXTENDED-DETAIL:X-REMOTE-ID;[\n "qtcontacts:mock:id=remote-so urce:2"\n]\n REV:2015-06-15T08:00:00 END:VCARD BEGIN:VCARD VERSION:3.0 UID:0 N:test34;teste;;; FN:teste test34 X-QTPROJECT-FAVORITE:false;0 TEL:44444 CATEGORIES:T X-QTPROJECT-EXTENDED-DETAIL:X-REMOTE-ID;[\n "qtcontacts:mock:id=remote-so urce:3"\n]\n REV:2015-06-15T10:00:00 X-QTPROJECT-EXTENDED-DETAIL:X-DELETED-AT;[\n "2015-06-15T10:00:00"\n]\n END:VCARD BEGIN:VCARD VERSION:3.0 UID:2 N:teste2;teste;;; FN:teste teste2 X-QTPROJECT-FAVORITE:false;0 TEL:111111 CATEGORIES:T X-QTPROJECT-EXTENDED-DETAIL:X-REMOTE-ID;[\n "qtcontacts:mock:id=remote-so urce:4"\n]\n REV:2015-06-15T08:00:00 END:VCARD ././@LongLink0000000000000000000000000000016100000000000011213 Lustar 00000000000000buteo-sync-plugins-contacts-0.1+16.04.20151211/tests/unittest/data/fast_sync_changed_both_sides_local_result.vcfbuteo-sync-plugins-contacts-0.1+16.04.20151211/tests/unittest/data/fast_sync_changed_both_sides_loca0000644000015300001610000000072212632560105034004 0ustar pbuserpbgroup00000000000000BEGIN:VCARD VERSION:3.0 UID:0 N:FirstName;LasteName;;; FN:FirstName LasteName TEL:1234567890 X-QTPROJECT-EXTENDED-DETAIL:X-REMOTE-ID;[\n "qtcontacts:mock:id=remote-so urce:5"\n]\n REV:2015-06-15T08:00:00 END:VCARD BEGIN:VCARD VERSION:3.0 N:New;Name;;; FN:Name New X-QTPROJECT-FAVORITE:false;0 TEL:44444 CATEGORIES:T REV:2015-06-15T10:00:00 X-QTPROJECT-EXTENDED-DETAIL:X-REMOTE-ID;[\n "qtcontacts:mock:id=remote-so urce:3"\n]\n END:VCARD buteo-sync-plugins-contacts-0.1+16.04.20151211/tests/unittest/data/google_contact_updated_reply.txt0000644000015300001610000000410712632560105033670 0ustar pbuserpbgroup00000000000000 https://www.google.com/m8/feeds/contacts/renato.teste2%40gmail.com/full/batch/1436214908674000 2015-07-06T20:35:08.674Z Batch Feed qtcontacts:galera::2c79456ba52fb29ecab9afedc4068f3421b77779 http://www.google.com/m8/feeds/contacts/renato_teste_2%40gmail.com/base/5b56e6f60f3d43d3 2015-07-06T20:35:08.622Z 2015-07-06T20:35:08.622Z Re Moreira Re Moreira Re Moreira 1111-111-1 222-2222 ././@LongLink0000000000000000000000000000016400000000000011216 Lustar 00000000000000buteo-sync-plugins-contacts-0.1+16.04.20151211/tests/unittest/data/fast_sync_with_a_remote_removal_local_result.vcfbuteo-sync-plugins-contacts-0.1+16.04.20151211/tests/unittest/data/fast_sync_with_a_remote_removal_l0000644000015300001610000000134312632560105034100 0ustar pbuserpbgroup00000000000000BEGIN:VCARD VERSION:3.0 UID:0 N:FirstName;LasteName;;; FN:FirstName LasteName TEL:1234567890 X-QTPROJECT-EXTENDED-DETAIL:X-REMOTE-ID;[\n "qtcontacts:mock:id=remote-so urce:5"\n]\n REV:2015-06-15T08:00:00 END:VCARD BEGIN:VCARD VERSION:3.0 UID:0 N:teste3;teste3;;; FN:teste3 teste3 X-QTPROJECT-FAVORITE:false;0 TEL:3333333 CATEGORIES:T X-QTPROJECT-EXTENDED-DETAIL:X-REMOTE-ID;[\n "qtcontacts:mock:id=remote-so urce:2"\n]\n REV:2015-06-15T08:00:00 END:VCARD BEGIN:VCARD VERSION:3.0 UID:2 N:teste2;teste;;; FN:teste teste2 X-QTPROJECT-FAVORITE:false;0 TEL:111111 CATEGORIES:T X-QTPROJECT-EXTENDED-DETAIL:X-REMOTE-ID;[\n "qtcontacts:mock:id=remote-so urce:4"\n]\n REV:2015-06-15T08:00:00 END:VCARD ././@LongLink0000000000000000000000000000016600000000000011220 Lustar 00000000000000buteo-sync-plugins-contacts-0.1+16.04.20151211/tests/unittest/data/fast_sync_with_new_local_contact_remote_result.vcfbuteo-sync-plugins-contacts-0.1+16.04.20151211/tests/unittest/data/fast_sync_with_new_local_contact_0000644000015300001610000000044612632560105034065 0ustar pbuserpbgroup00000000000000BEGIN:VCARD VERSION:3.0 UID:0 N:FirstName;LasteName;;; FN:FirstName LasteName TEL:1234567890 REV:2015-06-15T08:00:00 END:VCARD BEGIN:VCARD VERSION:3.0 UID:2 N:New;Contact;;; FN:Contact New X-QTPROJECT-FAVORITE:false;0 TEL:111111 CATEGORIES:T REV:2015-06-15T10:00:00 END:VCARD ././@LongLink0000000000000000000000000000015200000000000011213 Lustar 00000000000000buteo-sync-plugins-contacts-0.1+16.04.20151211/tests/unittest/data/fast_sync_changed_both_sides_local.vcfbuteo-sync-plugins-contacts-0.1+16.04.20151211/tests/unittest/data/fast_sync_changed_both_sides_loca0000644000015300001610000000204412632560105034003 0ustar pbuserpbgroup00000000000000BEGIN:VCARD VERSION:3.0 UID:0 N:FirstName;LasteName;;; FN:FirstName LasteName TEL:1234567890 X-QTPROJECT-EXTENDED-DETAIL:X-REMOTE-ID;[\n "qtcontacts:mock:id=remote-so urce:5"\n]\n REV:2015-06-15T08:00:00 END:VCARD BEGIN:VCARD VERSION:3.0 UID:0 N:teste3;teste3;;; FN:teste3 teste3 X-QTPROJECT-FAVORITE:false;0 TEL:3333333 CATEGORIES:T X-QTPROJECT-EXTENDED-DETAIL:X-REMOTE-ID;[\n "qtcontacts:mock:id=remote-so urce:2"\n]\n REV:2015-06-15T10:00:00 X-QTPROJECT-EXTENDED-DETAIL:X-DELETED-AT;[\n "2015-06-15T10:00:00"\n]\n END:VCARD BEGIN:VCARD VERSION:3.0 UID:0 N:New;Name;;; FN:Name New X-QTPROJECT-FAVORITE:false;0 TEL:44444 CATEGORIES:T X-QTPROJECT-EXTENDED-DETAIL:X-REMOTE-ID;[\n "qtcontacts:mock:id=remote-so urce:3"\n]\n REV:2015-06-15T10:00:00 END:VCARD BEGIN:VCARD VERSION:3.0 UID:2 N:teste2;teste;;; FN:teste teste2 X-QTPROJECT-FAVORITE:false;0 TEL:111111 CATEGORIES:T X-QTPROJECT-EXTENDED-DETAIL:X-REMOTE-ID;[\n "qtcontacts:mock:id=remote-so urce:4"\n]\n REV:2015-06-15T08:00:00 END:VCARD ././@LongLink0000000000000000000000000000016300000000000011215 Lustar 00000000000000buteo-sync-plugins-contacts-0.1+16.04.20151211/tests/unittest/data/fast_sync_delete_same_contact_remote_result.vcfbuteo-sync-plugins-contacts-0.1+16.04.20151211/tests/unittest/data/fast_sync_delete_same_contact_rem0000644000015300001610000000071412632560105034040 0ustar pbuserpbgroup00000000000000BEGIN:VCARD VERSION:3.0 UID:0 N:teste3;teste3;;; FN:teste3 teste3 X-QTPROJECT-FAVORITE:false;0 TEL:3333333 CATEGORIES:T REV:2015-06-15T08:00:00 END:VCARD BEGIN:VCARD VERSION:3.0 UID:2 N:teste2;teste;;; FN:teste teste2 X-QTPROJECT-FAVORITE:false;0 TEL:111111 CATEGORIES:T REV:2015-06-15T08:00:00 END:VCARD BEGIN:VCARD VERSION:3.0 UID:0 N:FirstName;LasteName;;; FN:FirstName LasteName TEL:1234567890 REV:2015-06-15T08:00:00 END:VCARD ././@LongLink0000000000000000000000000000015700000000000011220 Lustar 00000000000000buteo-sync-plugins-contacts-0.1+16.04.20151211/tests/unittest/data/fast_sync_with_new_local_contact_remote.vcfbuteo-sync-plugins-contacts-0.1+16.04.20151211/tests/unittest/data/fast_sync_with_new_local_contact_0000644000015300001610000000020712632560105034060 0ustar pbuserpbgroup00000000000000BEGIN:VCARD VERSION:3.0 UID:0 N:FirstName;LasteName;;; FN:FirstName LasteName TEL:1234567890 REV:2015-06-15T08:00:00 END:VCARD ././@LongLink0000000000000000000000000000016300000000000011215 Lustar 00000000000000buteo-sync-plugins-contacts-0.1+16.04.20151211/tests/unittest/data/fast_sync_with_a_local_change_remote_result.vcfbuteo-sync-plugins-contacts-0.1+16.04.20151211/tests/unittest/data/fast_sync_with_a_local_change_rem0000644000015300001610000000114412632560105034006 0ustar pbuserpbgroup00000000000000BEGIN:VCARD VERSION:3.0 UID:0 N:teste3;teste3;;; FN:teste3 teste3 X-QTPROJECT-FAVORITE:false;0 TEL:3333333 CATEGORIES:T REV:2015-06-15T08:00:00 END:VCARD BEGIN:VCARD VERSION:3.0 UID:0 N:New;Name;;; FN:Name New X-QTPROJECT-FAVORITE:false;0 TEL:44444 CATEGORIES:T REV:2015-06-15T10:00:00 END:VCARD BEGIN:VCARD VERSION:3.0 UID:2 N:teste2;teste;;; FN:teste teste2 X-QTPROJECT-FAVORITE:false;0 TEL:111111 CATEGORIES:T REV:2015-06-15T08:00:00 END:VCARD BEGIN:VCARD VERSION:3.0 UID:0 N:FirstName;LasteName;;; FN:FirstName LasteName TEL:1234567890 REV:2015-06-15T08:00:00 END:VCARD ././@LongLink0000000000000000000000000000016200000000000011214 Lustar 00000000000000buteo-sync-plugins-contacts-0.1+16.04.20151211/tests/unittest/data/fast_sync_with_a_local_change_local_result.vcfbuteo-sync-plugins-contacts-0.1+16.04.20151211/tests/unittest/data/fast_sync_with_a_local_change_loc0000644000015300001610000000172112632560105034001 0ustar pbuserpbgroup00000000000000BEGIN:VCARD VERSION:3.0 UID:0 N:FirstName;LasteName;;; FN:FirstName LasteName TEL:1234567890 X-QTPROJECT-EXTENDED-DETAIL:X-REMOTE-ID;[\n "qtcontacts:mock:id=remote-so urce:5"\n]\n REV:2015-06-15T08:00:00 END:VCARD BEGIN:VCARD VERSION:3.0 UID:0 N:teste3;teste3;;; FN:teste3 teste3 X-QTPROJECT-FAVORITE:false;0 TEL:3333333 CATEGORIES:T X-QTPROJECT-EXTENDED-DETAIL:X-REMOTE-ID;[\n "qtcontacts:mock:id=remote-so urce:2"\n]\n REV:2015-06-15T08:00:00 END:VCARD BEGIN:VCARD VERSION:3.0 N:New;Name;;; FN:Name New X-QTPROJECT-FAVORITE:false;0 TEL:44444 CATEGORIES:T REV:2015-06-15T10:00:00 X-QTPROJECT-EXTENDED-DETAIL:X-REMOTE-ID;[\n "qtcontacts:mock:id=remote-so urce:3"\n]\n END:VCARD BEGIN:VCARD VERSION:3.0 UID:2 N:teste2;teste;;; FN:teste teste2 X-QTPROJECT-FAVORITE:false;0 TEL:111111 CATEGORIES:T X-QTPROJECT-EXTENDED-DETAIL:X-REMOTE-ID;[\n "qtcontacts:mock:id=remote-so urce:4"\n]\n REV:2015-06-15T08:00:00 END:VCARD ././@LongLink0000000000000000000000000000016300000000000011215 Lustar 00000000000000buteo-sync-plugins-contacts-0.1+16.04.20151211/tests/unittest/data/fast_sync_with_a_remote_change_local_result.vcfbuteo-sync-plugins-contacts-0.1+16.04.20151211/tests/unittest/data/fast_sync_with_a_remote_change_lo0000644000015300001610000000172112632560105034037 0ustar pbuserpbgroup00000000000000BEGIN:VCARD VERSION:3.0 UID:0 N:FirstName;LasteName;;; FN:FirstName LasteName TEL:1234567890 X-QTPROJECT-EXTENDED-DETAIL:X-REMOTE-ID;[\n "qtcontacts:mock:id=remote-so urce:5"\n]\n REV:2015-06-15T08:00:00 END:VCARD BEGIN:VCARD VERSION:3.0 UID:0 N:teste3;teste3;;; FN:teste3 teste3 X-QTPROJECT-FAVORITE:false;0 TEL:3333333 CATEGORIES:T X-QTPROJECT-EXTENDED-DETAIL:X-REMOTE-ID;[\n "qtcontacts:mock:id=remote-so urce:2"\n]\n REV:2015-06-15T08:00:00 END:VCARD BEGIN:VCARD VERSION:3.0 N:New;Name;;; FN:Name New X-QTPROJECT-FAVORITE:false;0 TEL:44444 CATEGORIES:T REV:2015-06-15T10:00:00 X-QTPROJECT-EXTENDED-DETAIL:X-REMOTE-ID;[\n "qtcontacts:mock:id=remote-so urce:3"\n]\n END:VCARD BEGIN:VCARD VERSION:3.0 UID:2 N:teste2;teste;;; FN:teste teste2 X-QTPROJECT-FAVORITE:false;0 TEL:111111 CATEGORIES:T X-QTPROJECT-EXTENDED-DETAIL:X-REMOTE-ID;[\n "qtcontacts:mock:id=remote-so urce:4"\n]\n REV:2015-06-15T08:00:00 END:VCARD ././@LongLink0000000000000000000000000000015500000000000011216 Lustar 00000000000000buteo-sync-plugins-contacts-0.1+16.04.20151211/tests/unittest/data/fast_sync_with_a_local_removal_remote.vcfbuteo-sync-plugins-contacts-0.1+16.04.20151211/tests/unittest/data/fast_sync_with_a_local_removal_re0000644000015300001610000000115412632560105034052 0ustar pbuserpbgroup00000000000000BEGIN:VCARD VERSION:3.0 UID:0 N:teste3;teste3;;; FN:teste3 teste3 X-QTPROJECT-FAVORITE:false;0 TEL:3333333 CATEGORIES:T REV:2015-06-15T08:00:00 END:VCARD BEGIN:VCARD VERSION:3.0 UID:0 N:test34;teste;;; FN:teste test34 X-QTPROJECT-FAVORITE:false;0 TEL:44444 CATEGORIES:T REV:2015-06-15T08:00:00 END:VCARD BEGIN:VCARD VERSION:3.0 UID:2 N:teste2;teste;;; FN:teste teste2 X-QTPROJECT-FAVORITE:false;0 TEL:111111 CATEGORIES:T REV:2015-06-15T08:00:00 END:VCARD BEGIN:VCARD VERSION:3.0 UID:0 N:FirstName;LasteName;;; FN:FirstName LasteName TEL:1234567890 REV:2015-06-15T08:00:00 END:VCARD ././@LongLink0000000000000000000000000000014700000000000011217 Lustar 00000000000000buteo-sync-plugins-contacts-0.1+16.04.20151211/tests/unittest/data/fast_sync_without_changes_local.vcfbuteo-sync-plugins-contacts-0.1+16.04.20151211/tests/unittest/data/fast_sync_without_changes_local.v0000644000015300001610000000174012632560105034024 0ustar pbuserpbgroup00000000000000BEGIN:VCARD VERSION:3.0 UID:0 N:FirstName;LasteName;;; FN:FirstName LasteName TEL:1234567890 X-QTPROJECT-EXTENDED-DETAIL:X-REMOTE-ID;[\n "qtcontacts:mock:id=remote-so urce:5"\n]\n REV:2015-06-15T08:00:00 END:VCARD BEGIN:VCARD VERSION:3.0 UID:0 N:teste3;teste3;;; FN:teste3 teste3 X-QTPROJECT-FAVORITE:false;0 TEL:3333333 CATEGORIES:T X-QTPROJECT-EXTENDED-DETAIL:X-REMOTE-ID;[\n "qtcontacts:mock:id=remote-so urce:2"\n]\n REV:2015-06-15T08:00:00 END:VCARD BEGIN:VCARD VERSION:3.0 UID:0 N:test34;teste;;; FN:teste test34 X-QTPROJECT-FAVORITE:false;0 TEL:44444 CATEGORIES:T X-QTPROJECT-EXTENDED-DETAIL:X-REMOTE-ID;[\n "qtcontacts:mock:id=remote-so urce:3"\n]\n REV:2015-06-15T08:00:00 END:VCARD BEGIN:VCARD VERSION:3.0 UID:2 N:teste2;teste;;; FN:teste teste2 X-QTPROJECT-FAVORITE:false;0 TEL:111111 CATEGORIES:T X-QTPROJECT-EXTENDED-DETAIL:X-REMOTE-ID;[\n "qtcontacts:mock:id=remote-so urce:4"\n]\n REV:2015-06-15T08:00:00 END:VCARD ././@LongLink0000000000000000000000000000015500000000000011216 Lustar 00000000000000buteo-sync-plugins-contacts-0.1+16.04.20151211/tests/unittest/data/fast_sync_with_a_remote_change_remote.vcfbuteo-sync-plugins-contacts-0.1+16.04.20151211/tests/unittest/data/fast_sync_with_a_remote_change_re0000644000015300001610000000114412632560105034032 0ustar pbuserpbgroup00000000000000BEGIN:VCARD VERSION:3.0 UID:0 N:teste3;teste3;;; FN:teste3 teste3 X-QTPROJECT-FAVORITE:false;0 TEL:3333333 CATEGORIES:T REV:2015-06-15T08:00:00 END:VCARD BEGIN:VCARD VERSION:3.0 UID:0 N:New;Name;;; FN:Name New X-QTPROJECT-FAVORITE:false;0 TEL:44444 CATEGORIES:T REV:2015-06-15T10:00:00 END:VCARD BEGIN:VCARD VERSION:3.0 UID:2 N:teste2;teste;;; FN:teste teste2 X-QTPROJECT-FAVORITE:false;0 TEL:111111 CATEGORIES:T REV:2015-06-15T08:00:00 END:VCARD BEGIN:VCARD VERSION:3.0 UID:0 N:FirstName;LasteName;;; FN:FirstName LasteName TEL:1234567890 REV:2015-06-15T08:00:00 END:VCARD ././@LongLink0000000000000000000000000000016500000000000011217 Lustar 00000000000000buteo-sync-plugins-contacts-0.1+16.04.20151211/tests/unittest/data/fast_sync_with_new_local_contact_local_result.vcfbuteo-sync-plugins-contacts-0.1+16.04.20151211/tests/unittest/data/fast_sync_with_new_local_contact_0000644000015300001610000000073112632560105034062 0ustar pbuserpbgroup00000000000000BEGIN:VCARD VERSION:3.0 UID:0 N:FirstName;LasteName;;; FN:FirstName LasteName TEL:1234567890 X-QTPROJECT-EXTENDED-DETAIL:X-REMOTE-ID;[\n "qtcontacts:mock:id=remote-so urce:0"\n]\n REV:2015-06-15T08:00:00 END:VCARD BEGIN:VCARD VERSION:3.0 N:New;Contact;;; FN:Contact New X-QTPROJECT-FAVORITE:false;0 TEL:111111 CATEGORIES:T REV:2015-06-15T10:00:00 X-QTPROJECT-EXTENDED-DETAIL:X-REMOTE-ID;[\n "qtcontacts:mock:id=remote-so urce:3"\n]\n END:VCARD ././@LongLink0000000000000000000000000000015400000000000011215 Lustar 00000000000000buteo-sync-plugins-contacts-0.1+16.04.20151211/tests/unittest/data/fast_sync_delete_same_contact_remote.vcfbuteo-sync-plugins-contacts-0.1+16.04.20151211/tests/unittest/data/fast_sync_delete_same_contact_rem0000644000015300001610000000127012632560105034036 0ustar pbuserpbgroup00000000000000BEGIN:VCARD VERSION:3.0 UID:0 N:teste3;teste3;;; FN:teste3 teste3 X-QTPROJECT-FAVORITE:false;0 TEL:3333333 CATEGORIES:T REV:2015-06-15T08:00:00 END:VCARD BEGIN:VCARD VERSION:3.0 UID:0 N:test34;teste;;; FN:teste test34 X-QTPROJECT-FAVORITE:false;0 TEL:44444 CATEGORIES:T REV:2015-06-15T10:00:00 X-QTPROJECT-EXTENDED-DETAIL:X-DELETED-AT;[\n "2015-06-15T10:00:00"\n]\n END:VCARD BEGIN:VCARD VERSION:3.0 UID:2 N:teste2;teste;;; FN:teste teste2 X-QTPROJECT-FAVORITE:false;0 TEL:111111 CATEGORIES:T REV:2015-06-15T08:00:00 END:VCARD BEGIN:VCARD VERSION:3.0 UID:0 N:FirstName;LasteName;;; FN:FirstName LasteName TEL:1234567890 REV:2015-06-15T08:00:00 END:VCARD buteo-sync-plugins-contacts-0.1+16.04.20151211/tests/unittest/data/new_database_remote.vcf0000644000015300001610000000063212632560105031706 0ustar pbuserpbgroup00000000000000BEGIN:VCARD VERSION:3.0 UID:0 N:teste3;teste3;;; FN:teste3 teste3 X-QTPROJECT-FAVORITE:false;0 TEL:3333333 CATEGORIES:T END:VCARD BEGIN:VCARD VERSION:3.0 UID:0 N:test34;teste;;; FN:teste test34 X-QTPROJECT-FAVORITE:false;0 TEL:44444 CATEGORIES:T END:VCARD BEGIN:VCARD VERSION:3.0 UID:2 N:teste2;teste;;; FN:teste teste2 X-QTPROJECT-FAVORITE:false;0 TEL:111111 CATEGORIES:T END:VCARD buteo-sync-plugins-contacts-0.1+16.04.20151211/tests/unittest/data/local_previous_synced_remote.vcf0000644000015300001610000000063212632560105033664 0ustar pbuserpbgroup00000000000000BEGIN:VCARD VERSION:3.0 UID:0 N:teste3;teste3;;; FN:teste3 teste3 X-QTPROJECT-FAVORITE:false;0 TEL:3333333 CATEGORIES:T END:VCARD BEGIN:VCARD VERSION:3.0 UID:0 N:test34;teste;;; FN:teste test34 X-QTPROJECT-FAVORITE:false;0 TEL:44444 CATEGORIES:T END:VCARD BEGIN:VCARD VERSION:3.0 UID:2 N:teste2;teste;;; FN:teste teste2 X-QTPROJECT-FAVORITE:false;0 TEL:111111 CATEGORIES:T END:VCARD ././@LongLink0000000000000000000000000000016400000000000011216 Lustar 00000000000000buteo-sync-plugins-contacts-0.1+16.04.20151211/tests/unittest/data/fast_sync_with_a_remote_change_remote_result.vcfbuteo-sync-plugins-contacts-0.1+16.04.20151211/tests/unittest/data/fast_sync_with_a_remote_change_re0000644000015300001610000000114412632560105034032 0ustar pbuserpbgroup00000000000000BEGIN:VCARD VERSION:3.0 UID:0 N:teste3;teste3;;; FN:teste3 teste3 X-QTPROJECT-FAVORITE:false;0 TEL:3333333 CATEGORIES:T REV:2015-06-15T08:00:00 END:VCARD BEGIN:VCARD VERSION:3.0 UID:0 N:New;Name;;; FN:Name New X-QTPROJECT-FAVORITE:false;0 TEL:44444 CATEGORIES:T REV:2015-06-15T10:00:00 END:VCARD BEGIN:VCARD VERSION:3.0 UID:2 N:teste2;teste;;; FN:teste teste2 X-QTPROJECT-FAVORITE:false;0 TEL:111111 CATEGORIES:T REV:2015-06-15T08:00:00 END:VCARD BEGIN:VCARD VERSION:3.0 UID:0 N:FirstName;LasteName;;; FN:FirstName LasteName TEL:1234567890 REV:2015-06-15T08:00:00 END:VCARD ././@LongLink0000000000000000000000000000015000000000000011211 Lustar 00000000000000buteo-sync-plugins-contacts-0.1+16.04.20151211/tests/unittest/data/fast_sync_without_changes_remote.vcfbuteo-sync-plugins-contacts-0.1+16.04.20151211/tests/unittest/data/fast_sync_without_changes_remote.0000644000015300001610000000115412632560105034036 0ustar pbuserpbgroup00000000000000BEGIN:VCARD VERSION:3.0 UID:0 N:teste3;teste3;;; FN:teste3 teste3 X-QTPROJECT-FAVORITE:false;0 TEL:3333333 CATEGORIES:T REV:2015-06-15T08:00:00 END:VCARD BEGIN:VCARD VERSION:3.0 UID:0 N:test34;teste;;; FN:teste test34 X-QTPROJECT-FAVORITE:false;0 TEL:44444 CATEGORIES:T REV:2015-06-15T08:00:00 END:VCARD BEGIN:VCARD VERSION:3.0 UID:2 N:teste2;teste;;; FN:teste teste2 X-QTPROJECT-FAVORITE:false;0 TEL:111111 CATEGORIES:T REV:2015-06-15T08:00:00 END:VCARD BEGIN:VCARD VERSION:3.0 UID:0 N:FirstName;LasteName;;; FN:FirstName LasteName TEL:1234567890 REV:2015-06-15T08:00:00 END:VCARD ././@LongLink0000000000000000000000000000015100000000000011212 Lustar 00000000000000buteo-sync-plugins-contacts-0.1+16.04.20151211/tests/unittest/data/google_not_found_contact_response.txtbuteo-sync-plugins-contacts-0.1+16.04.20151211/tests/unittest/data/google_not_found_contact_response0000644000015300001610000000404412632560105034122 0ustar pbuserpbgroup00000000000000 https://www.google.com/m8/feeds/contacts/bfiller%40gmail.com/full/batch/1437413415201000 2015-07-20T17:30:15.201Z Batch Feed qtcontacts:galera::df8fd2e011e64624459c66f8d72417f7559d9c1d http://www.google.com/m8/feeds/contacts/bfiller@gmail.com/full/415f25f8a58b972 qtcontacts:galera::f55c2eeb760ffd6843d2e98319d3544ff3e987b5 http://www.google.com/m8/feeds/contacts/renato.teste2%40gmail.com/base/5f66bbf78bed2765 2015-07-20T17:30:15.201Z My Contact My Contact My Contact buteo-sync-plugins-contacts-0.1+16.04.20151211/tests/unittest/data/google_single_entry.txt0000644000015300001610000002250412632560105032017 0ustar pbuserpbgroup00000000000000 test@gmail.com 2015-06-18T19:25:40.490Z Contacts Ubuntu test@gmail.com Contacts 1 1 10 http://www.google.com/m8/feeds/contacts/test%40gmail.com/base/948ac108f03db45 2015-06-18T16:13:40.822Z 2015-06-18T16:13:40.822Z Aaron Rossler Aaron Rossler Aaron Rossler Mark Sr. Jr. +55-8704-0000 +55-8704-0001 +55-8704-0002 +55-8704-0003 +55-8704-0004 +55-8704-0005 +55-8704-0006 +55-8704-0007 +55-8704-0008 +55-8704-0009 +55-8704-0010 +55-8704-0011 +55-8704-0012 +55-8704-0013 Other street 00 00000 Brazil Other street 00 00000 Brazil PE Other POBOX0 Other street 01 11111 Brazil Work street 01 11111 Brazil MA Work POBOX1 Other street 02 22222 Brazil Home street 02 22222 Brazil MG Home POBOX2 Google, Inc. Tech Writer Writes documentation Software Development GOOG My house cleaner Clean the house Room HOUSE Lived in Orange County Borrowed my copy of "Alice's Adventures in Wonderland" Paragliding Dragon Katherine Mike ././@LongLink0000000000000000000000000000015000000000000011211 Lustar 00000000000000buteo-sync-plugins-contacts-0.1+16.04.20151211/tests/unittest/data/google_contact_full_fetch_page_1.txtbuteo-sync-plugins-contacts-0.1+16.04.20151211/tests/unittest/data/google_contact_full_fetch_page_1.0000644000015300001610000002520112632560105033614 0ustar pbuserpbgroup00000000000000 renato.teste2@gmail.com 2015-06-18T19:25:41.425Z Filho Renato's Contacts Filho Renato renato.teste2@gmail.com Contacts 15 11 10 http://www.google.com/m8/feeds/contacts/renato.teste2%40gmail.com/base/604d67ec8adf79bc 2015-06-18T19:11:47.368Z 2015-06-18T19:11:47.368Z Aaron Hentschke Aaron Hentschke Aaron Hentschke +49-178-2572849 (05530) 8188983 Moosweg 33c 81232 Deutschland Moosweg 33c 81232 Deutschland http://www.google.com/m8/feeds/contacts/renato.teste2%40gmail.com/base/763e9f2d08921a16 2015-06-18T16:13:40.822Z 2015-06-18T16:13:40.822Z Abdul Feld Abdul Feld Abdul Feld +49-7197-68234855 +49-198-5948206 Albin-Edelmann-Str. 98c 37466 Deutschland Albin-Edelmann-Str. 98c 37466 Deutschland http://www.google.com/m8/feeds/contacts/renato.teste2%40gmail.com/base/76af90190bfd1a3f 2015-06-18T16:13:40.822Z 2015-06-18T16:13:40.822Z Abby Burkhard Abby Burkhard Abby Burkhard +49-1270-18191872 (0165) 571583823 Bergische Landstr. 31a 63108 Deutschland Bergische Landstr. 31a 63108 Deutschland http://www.google.com/m8/feeds/contacts/renato.teste2%40gmail.com/base/7848b97f0879cfdc 2015-06-18T19:11:24.575Z 2015-06-18T19:11:24.575Z Arved Menga Arved Menga Arved Menga +49-5499-63405807 +49-1134-87938626 Gezelinallee 33 West Miekeburg 62583 Deutschland Gezelinallee 33 West Miekeburg 62583 Deutschland http://www.google.com/m8/feeds/contacts/renato.teste2%40gmail.com/base/7da271258ea03b89 2015-06-18T19:11:24.575Z 2015-06-18T19:11:24.575Z Arvid Herzenberg Arvid Herzenberg Arvid Herzenberg +49-7780-75716285 +49-1402-29654218 Schäfershütte 124 Kühneldorf 15713 Deutschland Schäfershütte 124 Kühneldorf 15713 Deutschland buteo-sync-plugins-contacts-0.1+16.04.20151211/tests/unittest/data/new_database_local.vcf0000644000015300001610000000015612632560105031506 0ustar pbuserpbgroup00000000000000BEGIN:VCARD VERSION:3.0 UID:0 N:FirstName;LasteName;;; FN:FirstName LasteName TEL:1234567890 END:VCARD ././@LongLink0000000000000000000000000000015500000000000011216 Lustar 00000000000000buteo-sync-plugins-contacts-0.1+16.04.20151211/tests/unittest/data/fast_sync_with_a_remote_removal_local.vcfbuteo-sync-plugins-contacts-0.1+16.04.20151211/tests/unittest/data/fast_sync_with_a_remote_removal_l0000644000015300001610000000174012632560105034101 0ustar pbuserpbgroup00000000000000BEGIN:VCARD VERSION:3.0 UID:0 N:FirstName;LasteName;;; FN:FirstName LasteName TEL:1234567890 X-QTPROJECT-EXTENDED-DETAIL:X-REMOTE-ID;[\n "qtcontacts:mock:id=remote-so urce:5"\n]\n REV:2015-06-15T08:00:00 END:VCARD BEGIN:VCARD VERSION:3.0 UID:0 N:teste3;teste3;;; FN:teste3 teste3 X-QTPROJECT-FAVORITE:false;0 TEL:3333333 CATEGORIES:T X-QTPROJECT-EXTENDED-DETAIL:X-REMOTE-ID;[\n "qtcontacts:mock:id=remote-so urce:2"\n]\n REV:2015-06-15T08:00:00 END:VCARD BEGIN:VCARD VERSION:3.0 UID:0 N:test34;teste;;; FN:teste test34 X-QTPROJECT-FAVORITE:false;0 TEL:44444 CATEGORIES:T X-QTPROJECT-EXTENDED-DETAIL:X-REMOTE-ID;[\n "qtcontacts:mock:id=remote-so urce:3"\n]\n REV:2015-06-15T08:00:00 END:VCARD BEGIN:VCARD VERSION:3.0 UID:2 N:teste2;teste;;; FN:teste teste2 X-QTPROJECT-FAVORITE:false;0 TEL:111111 CATEGORIES:T X-QTPROJECT-EXTENDED-DETAIL:X-REMOTE-ID;[\n "qtcontacts:mock:id=remote-so urce:4"\n]\n REV:2015-06-15T08:00:00 END:VCARD ././@LongLink0000000000000000000000000000016500000000000011217 Lustar 00000000000000buteo-sync-plugins-contacts-0.1+16.04.20151211/tests/unittest/data/fast_sync_with_a_remote_removal_remote_result.vcfbuteo-sync-plugins-contacts-0.1+16.04.20151211/tests/unittest/data/fast_sync_with_a_remote_removal_r0000644000015300001610000000071412632560105034107 0ustar pbuserpbgroup00000000000000BEGIN:VCARD VERSION:3.0 UID:0 N:teste3;teste3;;; FN:teste3 teste3 X-QTPROJECT-FAVORITE:false;0 TEL:3333333 CATEGORIES:T REV:2015-06-15T08:00:00 END:VCARD BEGIN:VCARD VERSION:3.0 UID:2 N:teste2;teste;;; FN:teste teste2 X-QTPROJECT-FAVORITE:false;0 TEL:111111 CATEGORIES:T REV:2015-06-15T08:00:00 END:VCARD BEGIN:VCARD VERSION:3.0 UID:0 N:FirstName;LasteName;;; FN:FirstName LasteName TEL:1234567890 REV:2015-06-15T08:00:00 END:VCARD ././@LongLink0000000000000000000000000000016400000000000011216 Lustar 00000000000000buteo-sync-plugins-contacts-0.1+16.04.20151211/tests/unittest/data/fast_sync_with_a_local_removal_remote_result.vcfbuteo-sync-plugins-contacts-0.1+16.04.20151211/tests/unittest/data/fast_sync_with_a_local_removal_re0000644000015300001610000000071412632560105034053 0ustar pbuserpbgroup00000000000000BEGIN:VCARD VERSION:3.0 UID:0 N:teste3;teste3;;; FN:teste3 teste3 X-QTPROJECT-FAVORITE:false;0 TEL:3333333 CATEGORIES:T REV:2015-06-15T08:00:00 END:VCARD BEGIN:VCARD VERSION:3.0 UID:2 N:teste2;teste;;; FN:teste teste2 X-QTPROJECT-FAVORITE:false;0 TEL:111111 CATEGORIES:T REV:2015-06-15T08:00:00 END:VCARD BEGIN:VCARD VERSION:3.0 UID:0 N:FirstName;LasteName;;; FN:FirstName LasteName TEL:1234567890 REV:2015-06-15T08:00:00 END:VCARD buteo-sync-plugins-contacts-0.1+16.04.20151211/tests/unittest/PluginClientTest.h0000644000015300001610000000000012632560105027700 0ustar pbuserpbgroup00000000000000buteo-sync-plugins-contacts-0.1+16.04.20151211/tests/unittest/PluginClientTest.cpp0000644000015300001610000000000012632560105030233 0ustar pbuserpbgroup00000000000000buteo-sync-plugins-contacts-0.1+16.04.20151211/tests/unittest/profile-test.xml0000644000015300001610000000206312632560105027444 0ustar pbuserpbgroup00000000000000 buteo-sync-plugins-contacts-0.1+16.04.20151211/tests/unittest/GTransportMock.cpp0000644000015300001610000000000012632560105027713 0ustar pbuserpbgroup00000000000000buteo-sync-plugins-contacts-0.1+16.04.20151211/tests/unittest/config-tests.h.in0000644000015300001610000000045212632560105027470 0ustar pbuserpbgroup00000000000000#ifndef __CONFIG_TESTS_H__ #define __CONFIG_TESTS_H__ #include const QString PROFILE_TEST_FN ("@PROFILE_TEST_FN@"); const QString TEST_DATA_DIR ("@TEST_DATA_DIR@"); const QString MOCK_PLUGIN_PATH ("@MOCK_PLUGIN_PATH@"); #endif buteo-sync-plugins-contacts-0.1+16.04.20151211/tests/unittest/MockAuthenticator.h0000644000015300001610000000245212632560105030104 0ustar pbuserpbgroup00000000000000/* * This file is part of buteo-gcontact-plugin package * * Copyright (C) 2015 Canonical Ltd. * * Contributors: Renato Araujo Oliveira Filho * * 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 MOCK_AUTH_H #define MOCK_AUTH_H #include #include #include class MockAuthenticator : public UAuth { Q_OBJECT public: explicit MockAuthenticator(QObject *parent = 0); ~MockAuthenticator(); bool authenticate(); bool init(const quint32 accountId, const QString serviceName); //test helpers bool m_initialized; quint32 m_accountId; QString m_serviceName; }; #endif // MOCK_AUTH_H buteo-sync-plugins-contacts-0.1+16.04.20151211/tests/unittest/MockRemoteSource.h0000644000015300001610000000460012632560105027703 0ustar pbuserpbgroup00000000000000/* * This file is part of buteo-gcontact-plugin package * * Copyright (C) 2015 Canonical Ltd. * * Contributors: Renato Araujo Oliveira Filho * * 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 #include #include #include QTCONTACTS_USE_NAMESPACE class MockRemoteSource : public UAbstractRemoteSource { Q_OBJECT public: MockRemoteSource(QObject *parent = 0); ~MockRemoteSource(); // UAbstractRemoteSource bool init(const QVariantMap &properties); void abort(); void fetchContacts(const QDateTime &since, bool includeDeleted, bool fetchAvatar = true); //test helpers bool m_initialized; QVariantMap m_properties; QtContacts::QContactManager *manager() const; void setPageSize(int pageSize); int count() const; protected: void saveContactsNonBatch(const QList contacts); void removeContactsNonBatch(const QList contacts); void batch(const QList &contactsToCreate, const QList &contactsToUpdate, const QList &contactsToRemove); private slots: void onContactChanged(const QList &); void onContactRemoved(const QList &); void onContactCreated(const QList &); private: QScopedPointer m_manager; int m_pageSize; QList toLocalContacts(const QList contacts) const; QList toRemoteContact(const QList contacts) const; bool exixts(const QContactId &remoteId) const; }; buteo-sync-plugins-contacts-0.1+16.04.20151211/tests/unittest/TestContactsClient.h0000644000015300001610000000344512632560105030240 0ustar pbuserpbgroup00000000000000/* * This file is part of buteo-gcontact-plugin package * * Copyright (C) 2015 Canonical Ltd. * * Contributors: Renato Araujo Oliveira Filho * * 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 TEST_CONTACTSCLIENT_H #define TEST_CONTACTSCLIENT_H #include #include class MockRemoteSource; class MockAuthenticator; class TestContactsClient : public UContactsClient { Q_OBJECT public: TestContactsClient(const QString& aPluginName, const Buteo::SyncProfile &aProfile, Buteo::PluginCbInterface *aCbInterface); virtual ~TestContactsClient(); // test helpers QPointer m_remoteSource; QPointer m_localSource; QPointer m_authenticator; QDateTime m_lastSyncTime; protected: QVariantMap remoteSourceProperties() const; UAbstractRemoteSource* createRemoteSource(QObject *parent) const; UContactsBackend* createContactsBackend(QObject *parent) const; UAuth* crateAuthenticator(QObject *parent) const; virtual const QDateTime lastSyncTime() const; }; #endif // TEST_CONTACTSCLIENT_H buteo-sync-plugins-contacts-0.1+16.04.20151211/tests/unittest/CMakeLists.txt0000644000015300001610000000504312632560105027046 0ustar pbuserpbgroup00000000000000# Buteo oop plugin #add_definitions(-DCLASSNAME=TestContactsClient) #add_definitions(-DCLASSNAME_H=\"TestContactsClient.h\") #add_definitions(-DCLIENT_PLUGIN) # config file # used on avatar class to avoid network connection during tests set(PROFILE_TEST_FN "${CMAKE_CURRENT_SOURCE_DIR}/profile-test.xml") set(TEST_DATA_DIR "${CMAKE_CURRENT_SOURCE_DIR}/data/") configure_file("${CMAKE_CURRENT_SOURCE_DIR}/config-tests.h.in" "${CMAKE_CURRENT_BINARY_DIR}/config-tests.h" IMMEDIATE @ONLY) add_executable(test-contact-sync TestContactsClient.h TestContactsClient.cpp TestContactsMain.cpp MockAuthenticator.h MockAuthenticator.cpp MockRemoteSource.h MockRemoteSource.cpp ) target_link_libraries(test-contact-sync ${ACCOUNTS_LIBRARIES} ${BUTEOSYNCFW_LIBRARIES} ${LIBSIGNON_LIBRARIES} ubuntu-contact-client ) include_directories( ${buteo-contact-client_SOURCE_DIR} ${buteo-contact-google_SOURCE_DIR} ${ACCOUNTS_INCLUDE_DIRS} ${BUTEOSYNCFW_INCLUDE_DIRS} ${LIBSIGNON_INCLUDE_DIRS} ) qt5_use_modules(test-contact-sync Core Versit Contacts Test) add_test(test-contact-sync test-contact-sync) set_tests_properties(test-contact-sync PROPERTIES ENVIRONMENT "MSYNCD_LOGGING_LEVEL=10" ) # Google sync test add_executable(test-gremotesource TestGRemoteSource.cpp ${buteo-contact-google_SOURCE_DIR}/GTransport.h GTransport.cpp ${buteo-contact-google_SOURCE_DIR}/GContactImageUploader.h GContactImageUploader.cpp ) target_link_libraries(test-gremotesource ${ACCOUNTS_LIBRARIES} ${BUTEOSYNCFW_LIBRARIES} ${LIBSIGNON_LIBRARIES} ubuntu-contact-client googlecontacts-lib ) qt5_use_modules(test-gremotesource Core Versit Network Contacts Test) add_test(test-gremotesource test-gremotesource) set_tests_properties(test-gremotesource PROPERTIES ENVIRONMENT "MSYNCD_LOGGING_LEVEL=10" ) # Google contact parser add_executable(test-google-contact-parser TestGoogleContactParser.cpp ${buteo-contact-google_SOURCE_DIR}/GTransport.h GTransport.cpp ${buteo-contact-google_SOURCE_DIR}/GContactImageUploader.h GContactImageUploader.cpp ) target_link_libraries(test-google-contact-parser ${ACCOUNTS_LIBRARIES} ${BUTEOSYNCFW_LIBRARIES} ${LIBSIGNON_LIBRARIES} ubuntu-contact-client googlecontacts-lib ) qt5_use_modules(test-google-contact-parser Core Contacts Xml Test) add_test(test-google-contact-parser test-google-contact-parser) set_tests_properties(test-google-contact-parser PROPERTIES ENVIRONMENT "MSYNCD_LOGGING_LEVEL=10" ) buteo-sync-plugins-contacts-0.1+16.04.20151211/tests/unittest/TestGRemoteSource.cpp0000644000015300001610000006301712632560105030402 0ustar pbuserpbgroup00000000000000/* * This file is part of buteo-gcontact-plugin package * * Copyright (C) 2015 Canonical Ltd. * * Contributors: Renato Araujo Oliveira Filho * * 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-tests.h" #include "GRemoteSource.h" #include "GTransport.h" #include "GConfig.h" #include #include #include #include #include #include #include #include QTVERSIT_USE_NAMESPACE QTCONTACTS_USE_NAMESPACE class GRemoteSourceTest : public QObject { Q_OBJECT private: int mGooglePage; QList fullContacts() { QFile vcardFile(TEST_DATA_DIR + QStringLiteral("google_contact_full_fetch.vcf")); if (!vcardFile.open(QIODevice::ReadOnly)) { qWarning() << "Fail to open:" << vcardFile.fileName(); return QList(); } QVersitReader reader(vcardFile.readAll()); reader.startReading(); reader.waitForFinished(); if (reader.results().count() > 0) { QList documents = reader.results(); QVersitContactImporter contactImporter; if (!contactImporter.importDocuments(documents)) { qWarning() << "Fail to import contacts"; return QList(); } return contactImporter.contacts(); } return QList(); } private Q_SLOTS: void onFetchContactsRequested(const QUrl &url, QByteArray *data) { Q_UNUSED(url); // populate data with the contacts data QFile fetchFile(TEST_DATA_DIR + QString("google_contact_full_fetch_page_%1.txt").arg(mGooglePage)); if (fetchFile.open(QIODevice::ReadOnly)) { data->append(fetchFile.readAll()); fetchFile.close(); } mGooglePage++; } void onSaveNotFoundContactsRequested(const QUrl &url, QByteArray *data) { Q_UNUSED(url); // populate data with the contacts data QFile fetchFile(TEST_DATA_DIR + QString("google_not_found_contact_response.txt")); if (fetchFile.open(QIODevice::ReadOnly)) { data->append(fetchFile.readAll()); fetchFile.close(); } } void onCreateContactRequested(const QUrl &url, QByteArray *data) { Q_UNUSED(url); // populate data with the contacts data QFile fetchFile(TEST_DATA_DIR + QStringLiteral("google_contact_created_page.txt")); if (fetchFile.open(QIODevice::ReadOnly)) { data->append(fetchFile.readAll()); fetchFile.close(); } } void onUpdatedContactRequested(const QUrl &url, QByteArray *data) { Q_UNUSED(url); // populate data with the contacts data QFile fetchFile(TEST_DATA_DIR + QStringLiteral("google_contact_updated_reply.txt")); if (fetchFile.open(QIODevice::ReadOnly)) { data->append(fetchFile.readAll()); fetchFile.close(); } } void initTestCase() { qRegisterMetaType >("QMap"); } void testInitialization() { QScopedPointer src(new GRemoteSource()); QVariantMap props; props.insert(Buteo::KEY_REMOTE_DATABASE, "http://google.com/contacts"); props.insert(Buteo::KEY_HTTP_PROXY_HOST, "http://proxy.com"); props.insert(Buteo::KEY_HTTP_PROXY_PORT, 8080); src->init(props); QCOMPARE(src->state(), 0); QCOMPARE(src->transport()->property("URL").toString(), QStringLiteral("http://google.com/contacts")); QCOMPARE(src->transport()->property("ProxyHost").toString(), QStringLiteral("http://proxy.com")); QCOMPARE(src->transport()->property("ProxyPort").toInt(), 8080); } void testFetchContacts() { mGooglePage = 0; QList remoteContacts; QScopedPointer src(new GRemoteSource()); QVariantMap props; props.insert(Buteo::KEY_REMOTE_DATABASE, "http://google.com/contacts"); props.insert("AUTH-TOKEN", "1234567890"); src->init(props); QSignalSpy contactsFetched(src.data(), SIGNAL(contactsFetched(QList,Sync::SyncStatus, qreal))); connect(src->transport(), SIGNAL(requested(QUrl,QByteArray*)), SLOT(onFetchContactsRequested(QUrl,QByteArray*))); src->fetchContacts(QDateTime(), false, false); QTRY_COMPARE(contactsFetched.count(), 2); QVERIFY(!src->transport()->property("UpdatedMin").toDateTime().isValid()); QCOMPARE(src->transport()->property("MaxResults").toInt(), GConfig::MAX_RESULTS); QVariantMap headers = src->transport()->property("Headers").value(); QCOMPARE(headers["Authorization"].toString(), QStringLiteral("Bearer 1234567890")); QCOMPARE(headers["GData-Version"].toString(), QStringLiteral("3.0")); QTRY_COMPARE(contactsFetched.count(), 2); // first page signal QList arguments = contactsFetched.takeFirst(); QList contacts = arguments.at(0).value >(); QCOMPARE(arguments.at(1).toInt(), int(Sync::SYNC_PROGRESS)); QCOMPARE(contacts.size(), 10); remoteContacts += contacts; // second page signal arguments = contactsFetched.takeFirst(); contacts = arguments.at(0).value >(); QCOMPARE(arguments.at(1).toInt(), int(Sync::SYNC_DONE)); QCOMPARE(contacts.size(), 5); remoteContacts += contacts; QList expectedContacts = fullContacts(); QCOMPARE(remoteContacts.size(), expectedContacts.size()); foreach(const QContact &c, remoteContacts) { QString cRID = UContactsCustomDetail::getCustomField(c, UContactsCustomDetail::FieldRemoteId).data().toString(); bool found = false; foreach(const QContact &e, expectedContacts) { QString eRID = UContactsCustomDetail::getCustomField(e, UContactsCustomDetail::FieldRemoteId).data().toString(); if (cRID == eRID) { found = true; QList detA = c.details(); QList detB = e.details(); if (detA.size() != detB.size()) { qDebug() << detA; qDebug() << detB; } QCOMPARE(detA.size(), detB.size()); for(int i = 0; i < detA.size(); i++) { // skip timestamp since this will be update to the current time while importing if (detA[i].type() == QContactTimestamp::Type) { QContactTimestamp tA(detA[i]); QContactTimestamp tB(detB[i]); qDebug() << detA[i] << detB[i]; QCOMPARE(tA.created(), tB.created()); } else { if (detA[i] != detB[i]) { qWarning() << "DETAILS NOT EQUAL\n\t" << detA[i] << "\n\t" << detB[i]; } QCOMPARE(detA[i], detB[i]); } } } } QVERIFY2(found, "remote contact not found in local contact list"); } } void testCreateContact() { mGooglePage = 0; QScopedPointer src(new GRemoteSource()); QVariantMap props; props.insert(Buteo::KEY_REMOTE_DATABASE, "http://google.com/contacts"); props.insert("AUTH-TOKEN", "1234567890"); props.insert("ACCOUNT-NAME", "renato_teste_2@gmail.com"); src->init(props); connect(src->transport(), SIGNAL(requested(QUrl,QByteArray*)), SLOT(onCreateContactRequested(QUrl,QByteArray*))); QContact c; QContactName nm; nm.setFirstName("Renato"); nm.setLastName("Oliveira Filho"); c.saveDetail(&nm); QList lc; lc << c; QSignalSpy onTransactionCommited(src.data(), SIGNAL(transactionCommited(QList, QList, QStringList, QMap, Sync::SyncStatus))); QSignalSpy onContactCreated(src.data(), SIGNAL(contactsCreated(QList,Sync::SyncStatus))); src->transaction(); src->saveContacts(lc); src->commit(); // check create command data QCOMPARE(QString::fromLatin1(src->transport()->property("DATA").toByteArray()), QStringLiteral("" "" "qtcontacts:::" "" "" "" "Renato" "Oliveira Filho" "" "" "\n")); QTRY_COMPARE(onContactCreated.count(), 1); QTRY_COMPARE(onTransactionCommited.count(), 1); QList transactionCommitedArgs = onTransactionCommited.first(); QCOMPARE(transactionCommitedArgs.size(), 5); QList createdContacts = transactionCommitedArgs.at(0).value >(); QList updatedContacts = transactionCommitedArgs.at(1).value >(); QStringList removedContacts = transactionCommitedArgs.at(2).value(); QMap errorMap = transactionCommitedArgs.at(3).value >(); Sync::SyncStatus syncStatus = transactionCommitedArgs.at(4).value(); QCOMPARE(createdContacts.size(), 1); QCOMPARE(createdContacts.at(0).detail().guid(), QStringLiteral("qtcontacts:galera::f55c2eeb760ffd6843d2e98319d3544ff3e987b5")); QCOMPARE(UContactsCustomDetail::getCustomField(createdContacts.at(0), UContactsCustomDetail::FieldRemoteId).data().toString(), QStringLiteral("5f66bbf78bed2765")); QCOMPARE(updatedContacts.size(), 0); QCOMPARE(removedContacts.size(), 0); QCOMPARE(errorMap.size(), 0); QCOMPARE(syncStatus, Sync::SYNC_DONE); } void testModifyAContact() { mGooglePage = 0; QDateTime currentDateTime = QDateTime::fromString(QStringLiteral("2015-07-06T20:17:06.117Z"), Qt::ISODate); QScopedPointer src(new GRemoteSource()); QVariantMap props; props.insert(Buteo::KEY_REMOTE_DATABASE, "http://google.com/contacts"); props.insert("AUTH-TOKEN", "1234567890"); props.insert("ACCOUNT-NAME", "renato_teste_2@gmail.com"); src->init(props); connect(src->transport(), SIGNAL(requested(QUrl,QByteArray*)), SLOT(onUpdatedContactRequested(QUrl,QByteArray*))); QContact c; c.setId(QContactId::fromString("qtcontacts::memory:99999")); QContactGuid guid; guid.setGuid("2c79456ba52fb29ecab9afedc4068f3421b77779"); c.saveDetail(&guid); QContactTimestamp ts; ts.setLastModified(currentDateTime); c.saveDetail(&ts); QContactName nm; nm.setFirstName("Renato"); nm.setLastName("Oliveira Filho"); c.saveDetail(&nm); QContactAvatar avatar; avatar.setImageUrl(QUrl::fromLocalFile("/tmp/avatar.png")); c.saveDetail(&avatar); UContactsCustomDetail::setCustomField(c, UContactsCustomDetail::FieldRemoteId, QVariant("012345")); QList lc; lc << c; QSignalSpy onTransactionCommited(src.data(), SIGNAL(transactionCommited(QList, QList, QStringList, QMap, Sync::SyncStatus))); QSignalSpy onContactChanged(src.data(), SIGNAL(contactsChanged(QList,Sync::SyncStatus))); src->transaction(); src->saveContacts(lc); src->commit(); // check modify command data QCOMPARE(QString::fromLatin1(src->transport()->property("DATA").toByteArray()), QStringLiteral("" "" "qtcontacts:::" "" "" "http://www.google.com/m8/feeds/contacts/renato_teste_2@gmail.com/full/012345" "2015-07-06T20:17:06.117Z" "" "Renato" "Oliveira Filho" "" "" "\n")); QTRY_COMPARE(onContactChanged.count(), 1); QTRY_COMPARE(onTransactionCommited.count(), 1); QList transactionCommitedArgs = onTransactionCommited.first(); QCOMPARE(transactionCommitedArgs.size(), 5); QList createdContacts = transactionCommitedArgs.at(0).value >(); QList updatedContacts = transactionCommitedArgs.at(1).value >(); QStringList removedContacts = transactionCommitedArgs.at(2).value(); QMap errorMap = transactionCommitedArgs.at(3).value >(); Sync::SyncStatus syncStatus = transactionCommitedArgs.at(4).value(); QCOMPARE(createdContacts.size(), 0); QCOMPARE(updatedContacts.size(), 1); QCOMPARE(removedContacts.size(), 0); QCOMPARE(errorMap.size(), 0); QCOMPARE(syncStatus, Sync::SYNC_DONE); QContact newContact(updatedContacts.at(0)); QList exDetails = newContact.details(); QCOMPARE(exDetails.size(), 4); QCOMPARE(newContact.detail().guid(), QStringLiteral("qtcontacts:galera::2c79456ba52fb29ecab9afedc4068f3421b77779")); QCOMPARE(UContactsCustomDetail::getCustomField(newContact, UContactsCustomDetail::FieldRemoteId).data().toString(), QStringLiteral("5b56e6f60f3d43d3")); QCOMPARE(UContactsCustomDetail::getCustomField(newContact, UContactsCustomDetail::FieldContactETag).data().toString(), QStringLiteral("5b56e6f60f3d43d3-new")); // check avatar QList avatars = newContact.details(); QCOMPARE(avatars.size(), 1); QCOMPARE(avatars.at(0).imageUrl(), QUrl::fromLocalFile("/tmp/avatar.png")); QCOMPARE(UContactsCustomDetail::getCustomField(newContact, UContactsCustomDetail::FieldContactAvatarETag).data().toString(), QString("%1-avatar").arg("5b56e6f60f3d43d3")); } void testModifyAContactWithoutChangeAvatar() { QScopedPointer src(new GRemoteSource()); QVariantMap props; props.insert(Buteo::KEY_REMOTE_DATABASE, "http://google.com/contacts"); props.insert("AUTH-TOKEN", "1234567890"); props.insert("ACCOUNT-NAME", "renato_teste_2@gmail.com"); src->init(props); connect(src->transport(), SIGNAL(requested(QUrl,QByteArray*)), SLOT(onUpdatedContactRequested(QUrl,QByteArray*))); // prepare contact QContact c; c.setId(QContactId::fromString("qtcontacts::memory:99999")); QContactGuid guid; guid.setGuid("2c79456ba52fb29ecab9afedc4068f3421b77779"); c.saveDetail(&guid); QContactName nm; nm.setFirstName("Renato"); nm.setLastName("Oliveira Filho"); c.saveDetail(&nm); QContactAvatar avatar; avatar.setImageUrl(QUrl::fromLocalFile("/tmp/avatar.png")); c.saveDetail(&avatar); UContactsCustomDetail::setCustomField(c, UContactsCustomDetail::FieldContactAvatarETag, QStringLiteral("\"TTt0ZQQ3Sit7I2BfHWQlUURPIEwjCVMZdT0.\"")); UContactsCustomDetail::setCustomField(c, UContactsCustomDetail::FieldRemoteId, QVariant("012345")); // save contact QList lc; lc << c; QSignalSpy onTransactionCommited(src.data(), SIGNAL(transactionCommited(QList, QList, QStringList, QMap, Sync::SyncStatus))); src->transaction(); src->saveContacts(lc); src->commit(); // wait for result QTRY_COMPARE(onTransactionCommited.count(), 1); // check result QList transactionCommitedArgs = onTransactionCommited.first(); QCOMPARE(transactionCommitedArgs.size(), 5); QList createdContacts = transactionCommitedArgs.at(0).value >(); QList updatedContacts = transactionCommitedArgs.at(1).value >(); QStringList removedContacts = transactionCommitedArgs.at(2).value(); QMap errorMap = transactionCommitedArgs.at(3).value >(); Sync::SyncStatus syncStatus = transactionCommitedArgs.at(4).value(); QCOMPARE(createdContacts.size(), 0); QCOMPARE(updatedContacts.size(), 1); QCOMPARE(removedContacts.size(), 0); QCOMPARE(errorMap.size(), 0); QCOMPARE(syncStatus, Sync::SYNC_DONE); // check if avatar still the same QContact newContact(updatedContacts.at(0)); QList exDetails = newContact.details(); QCOMPARE(exDetails.size(), 4); QCOMPARE(newContact.detail().guid(), QStringLiteral("qtcontacts:galera::2c79456ba52fb29ecab9afedc4068f3421b77779")); QCOMPARE(UContactsCustomDetail::getCustomField(newContact, UContactsCustomDetail::FieldRemoteId).data().toString(), QStringLiteral("5b56e6f60f3d43d3")); QCOMPARE(UContactsCustomDetail::getCustomField(newContact, UContactsCustomDetail::FieldContactETag).data().toString(), QStringLiteral("\"SXg5ejVSLit7I2A9XRVVEkwOTgQ.\"")); QCOMPARE(UContactsCustomDetail::getCustomField(newContact, UContactsCustomDetail::FieldContactAvatarETag).data().toString(), QStringLiteral("\"TTt0ZQQ3Sit7I2BfHWQlUURPIEwjCVMZdT0.\"")); // check avatar url QList avatars = newContact.details(); QCOMPARE(avatars.size(), 1); QCOMPARE(avatars.at(0).imageUrl(), QUrl::fromLocalFile("/tmp/avatar.png")); } void testSaveNotFoundContact() { QScopedPointer src(new GRemoteSource()); QVariantMap props; props.insert(Buteo::KEY_REMOTE_DATABASE, "http://google.com/contacts"); props.insert("AUTH-TOKEN", "1234567890"); props.insert("ACCOUNT-NAME", "renato_teste_2@gmail.com"); src->init(props); connect(src->transport(), SIGNAL(requested(QUrl,QByteArray*)), SLOT(onSaveNotFoundContactsRequested(QUrl,QByteArray*))); // prepare contacts QList contacts; // Contact to Update QContact c; c.setId(QContactId::fromString("qtcontacts::memory:1")); QContactGuid guid; guid.setGuid("df8fd2e011e64624459c66f8d72417f7559d9c1d"); c.saveDetail(&guid); UContactsCustomDetail::setCustomField(c, UContactsCustomDetail::FieldRemoteId, QStringLiteral("415f25f8a58b972")); UContactsCustomDetail::setCustomField(c, UContactsCustomDetail::FieldContactETag, QStringLiteral("415f25f8a58b972-ETAG")); contacts << c; // Contact to create c = QContact(); c.setId(QContactId::fromString("qtcontacts::memory:2")); guid = QContactGuid(); guid.setGuid("f55c2eeb760ffd6843d2e98319d3544ff3e987b5"); c.saveDetail(&guid); QContactName name; name.setFirstName("My"); name.setLastName("Contact"); c.saveDetail(&name); contacts << c; QSignalSpy onTransactionCommited(src.data(), SIGNAL(transactionCommited(QList, QList, QStringList, QMap, Sync::SyncStatus))); src->transaction(); src->saveContacts(contacts); src->commit(); QTRY_COMPARE(onTransactionCommited.count(), 1); QList transactionCommitedArgs = onTransactionCommited.first(); QCOMPARE(transactionCommitedArgs.size(), 5); QList createdContacts = transactionCommitedArgs.at(0).value >(); QList updatedContacts = transactionCommitedArgs.at(1).value >(); QStringList removedContacts = transactionCommitedArgs.at(2).value(); QMap errorMap = transactionCommitedArgs.at(3).value< QMap >(); Sync::SyncStatus syncStatus = transactionCommitedArgs.at(4).value(); QCOMPARE(createdContacts.size(), 1); QCOMPARE(updatedContacts.size(), 0); QCOMPARE(removedContacts.size(), 0); QCOMPARE(errorMap.size(), 1); QCOMPARE(syncStatus, Sync::SYNC_DONE); // check if the reported error is correct QCOMPARE(errorMap.begin().key(), QStringLiteral("qtcontacts:galera::df8fd2e011e64624459c66f8d72417f7559d9c1d")); QCOMPARE(errorMap.begin().value(), (int) QContactManager::DoesNotExistError); } }; QTEST_MAIN(GRemoteSourceTest) #include "TestGRemoteSource.moc" buteo-sync-plugins-contacts-0.1+16.04.20151211/tests/plugins/0000755000015300001610000000000012632561656024122 5ustar pbuserpbgroup00000000000000buteo-sync-plugins-contacts-0.1+16.04.20151211/tests/plugins/CMakeLists.txt0000644000015300001610000000003312632560105026642 0ustar pbuserpbgroup00000000000000add_subdirectory(contacts) buteo-sync-plugins-contacts-0.1+16.04.20151211/tests/plugins/contacts/0000755000015300001610000000000012632561656025740 5ustar pbuserpbgroup00000000000000buteo-sync-plugins-contacts-0.1+16.04.20151211/tests/plugins/contacts/qcontactmemorybackend.cpp0000644000015300001610000012375112632560105033016 0ustar pbuserpbgroup00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the QtContacts module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #include "qcontactmemorybackend_p.h" #ifndef QT_NO_DEBUG_STREAM #include #endif #include #include #include #include #include #include #include #include #include #include #include #define ENGINE_NAME "mock" #define X_DELETED_AT "X-DELETED-AT" QT_BEGIN_NAMESPACE_CONTACTS QContactManagerEngine* QContactMemoryEngineFactory::engine(const QMap ¶meters, QContactManager::Error *error) { Q_UNUSED(error); QContactMemoryEngine *ret = QContactMemoryEngine::createMemoryEngine(parameters); return ret; } QContactEngineId* QContactMemoryEngineFactory::createContactEngineId(const QMap ¶meters, const QString &engineIdString) const { Q_UNUSED(parameters); QContactMemoryEngineId *retn = new QContactMemoryEngineId(parameters, engineIdString); return retn; } QString QContactMemoryEngineFactory::managerName() const { return QString::fromLatin1(ENGINE_NAME); } /*! \class QContactMemoryEngine \inmodule QtContacts \brief The QContactMemoryEngine class provides an in-memory implementation of a contacts backend. \internal It may be used as a reference implementation, or when persistent storage is not required. During construction, it will load the in-memory data associated with the memory store identified by the "id" parameter from the given parameters if it exists, or a new, anonymous store if it does not. Data stored in this engine is only available in the current process. This engine supports sharing, so an internal reference count is increased whenever a manager uses this backend, and is decreased when the manager no longer requires this engine. */ /* static data for manager class */ QMap QContactMemoryEngine::engineDatas; /*! * Factory function for creating a new in-memory backend, based * on the given \a parameters. * * The same engine will be returned for multiple calls with the * same value for the "id" parameter, while one of them is in scope. */ QContactMemoryEngine* QContactMemoryEngine::createMemoryEngine(const QMap ¶meters) { bool anonymous = false; QString idValue = parameters.value(QStringLiteral("id")); if (idValue.isNull() || idValue.isEmpty()) { // no store given? new, anonymous store. idValue = "mock"; //QUuid::createUuid().toString(); anonymous = true; } QContactMemoryEngineData *data = engineDatas.value(idValue); if (data) { data->m_refCount.ref(); } else { data = new QContactMemoryEngineData(); data->m_id = idValue; data->m_anonymous = anonymous; engineDatas.insert(idValue, data); } return new QContactMemoryEngine(data); } /*! * Constructs a new in-memory backend which shares the given \a data with * other shared memory engines. */ QContactMemoryEngine::QContactMemoryEngine(QContactMemoryEngineData *data) : d(data) { qRegisterMetaType("QContactAbstractRequest::State"); qRegisterMetaType >("QList"); qRegisterMetaType("QContactId"); d->m_managerUri = managerUri(); d->m_sharedEngines.append(this); } /*! Frees any memory used by this engine */ QContactMemoryEngine::~QContactMemoryEngine() { d->m_sharedEngines.removeAll(this); if (!d->m_refCount.deref()) { engineDatas.remove(d->m_id); delete d; } } /*! \reimp */ QString QContactMemoryEngine::managerName() const { return QStringLiteral(ENGINE_NAME); } /*! \reimp */ QMap QContactMemoryEngine::managerParameters() const { QMap params; params.insert(QStringLiteral("id"), d->m_id); return params; } /*! \reimp */ bool QContactMemoryEngine::setSelfContactId(const QContactId &contactId, QContactManager::Error *error) { if (contactId.isNull() || d->m_contactIds.contains(contactId)) { *error = QContactManager::NoError; QContactId oldId = d->m_selfContactId; d->m_selfContactId = contactId; QContactChangeSet changeSet; changeSet.setOldAndNewSelfContactId(QPair(oldId, contactId)); d->emitSharedSignals(&changeSet); return true; } *error = QContactManager::DoesNotExistError; return false; } /*! \reimp */ QContactId QContactMemoryEngine::selfContactId(QContactManager::Error *error) const { *error = QContactManager::DoesNotExistError; if (!d->m_selfContactId.isNull()) *error = QContactManager::NoError; return d->m_selfContactId; } /*! \reimp */ QContact QContactMemoryEngine::contact(const QContactId &contactId, const QContactFetchHint &fetchHint, QContactManager::Error *error) const { Q_UNUSED(fetchHint); // no optimizations are possible in the memory backend; ignore the fetch hint. int index = d->m_contactIds.indexOf(contactId); if (index != -1) { // found the contact successfully. *error = QContactManager::NoError; return d->m_contacts.at(index); } *error = QContactManager::DoesNotExistError; return QContact(); } /*! \reimp */ QList QContactMemoryEngine::contactIds(const QContactFilter &filter, const QList &sortOrders, QContactManager::Error *error) const { QList clist = contacts(filter, sortOrders, QContactFetchHint(), error); /* Extract the ids */ QList ids; foreach (const QContact &c, clist) ids.append(c.id()); return ids; } bool QContactMemoryEngine::testDeletedContact(const QContactFilter &filter, const QContact &contact, const QDateTime &deletedAt) const { switch(filter.type()) { case QContactFilter::ChangeLogFilter: { const QContactChangeLogFilter bf(filter); if (bf.eventType() == QContactChangeLogFilter::EventRemoved) { return (deletedAt >= bf.since()); } break; } case QContactFilter::IdFilter: { const QContactIdFilter idF(filter); return (idF.ids().contains(contact.id())); } case QContactFilter::IntersectionFilter: { const QContactIntersectionFilter bf(filter); const QList& terms = bf.filters(); if (terms.count() > 0) { bool changeLogResult = false; for(int j = 0; j < terms.count(); j++) { if (terms.at(j).type() == QContactFilter::ChangeLogFilter) { changeLogResult = testDeletedContact(terms.at(j), contact, deletedAt); } else if (!QContactManagerEngine::testFilter(terms.at(j), contact)) { return false; } } return changeLogResult; } break; } case QContactFilter::UnionFilter: { return QContactManagerEngine::testFilter(filter, contact); } default: break; } return false; } /*! \reimp */ QList QContactMemoryEngine::contacts(const QContactFilter &filter, const QList &sortOrders, const QContactFetchHint &fetchHint, QContactManager::Error *error) const { Q_UNUSED(fetchHint); // no optimizations are possible in the memory backend; ignore the fetch hint. Q_UNUSED(error); QList sorted; /* First filter out contacts - check for default filter first */ if (filter.type() == QContactFilter::DefaultFilter) { foreach(const QContact&c, d->m_contacts) { if (!deleted(c).isValid()) QContactManagerEngine::addSorted(&sorted,c, sortOrders); } } else { foreach(const QContact&c, d->m_contacts) { QDateTime deletedAt = deleted(c); if (deletedAt.isValid() && testDeletedContact(filter, c, deletedAt)) QContactManagerEngine::addSorted(&sorted,c, sortOrders); else if (!deletedAt.isValid() && QContactManagerEngine::testFilter(filter, c)) QContactManagerEngine::addSorted(&sorted,c, sortOrders); } } return sorted; } /*! Saves the given contact \a theContact, storing any error to \a error and filling the \a changeSet with ids of changed contacts as required Returns true if the operation was successful otherwise false. */ bool QContactMemoryEngine::saveContact(QContact *theContact, QContactChangeSet &changeSet, QContactManager::Error *error) { return saveContact(theContact, changeSet, error, QList()); } /*! \reimp */ bool QContactMemoryEngine::saveContacts(QList *contacts, QMap *errorMap, QContactManager::Error *error) { return saveContacts(contacts, errorMap, error, QList()); } /*! Removes the contact identified by the given \a contactId, storing any error to \a error and filling the \a changeSet with ids of changed contacts and relationships as required. Returns true if the operation was successful otherwise false. */ bool QContactMemoryEngine::removeContact(const QContactId &contactId, QContactChangeSet &changeSet, QContactManager::Error *error) { int index = d->m_contactIds.indexOf(contactId); if (index == -1) { *error = QContactManager::DoesNotExistError; return false; } // remove the contact from any relationships it was in. QContact thisContact = d->m_contacts.at(index); QList allRelationships = relationships(QString(), thisContact, QContactRelationship::Either, error); if (*error != QContactManager::NoError && *error != QContactManager::DoesNotExistError) { *error = QContactManager::UnspecifiedError; // failed to clean up relationships return false; } // this is meant to be a transaction, so if any of these fail, we're in BIG TROUBLE. // a real backend will use DBMS transactions to ensure database integrity. removeRelationships(allRelationships, 0, error); #if 0 // having cleaned up the relationships, remove the contact from the lists. d->m_contacts.removeAt(index); d->m_contactIds.removeAt(index); #else QContact &c = d->m_contacts[index]; QContactExtendedDetail removedAt; foreach(QContactExtendedDetail d, c.details()) { if (d.name() == X_DELETED_AT) { removedAt = d; break; } } if (removedAt.isEmpty()) { removedAt.setName(X_DELETED_AT); } removedAt.setData(currentDateTime()); c.saveDetail(&removedAt); #endif *error = QContactManager::NoError; // and if it was the self contact, reset the self contact id if (contactId == d->m_selfContactId) { d->m_selfContactId = QContactId(); changeSet.setOldAndNewSelfContactId(QPair(contactId, QContactId())); } changeSet.insertRemovedContact(contactId); return true; } /*! \reimp */ bool QContactMemoryEngine::removeContacts(const QList &contactIds, QMap *errorMap, QContactManager::Error *error) { if (contactIds.count() == 0) { *error = QContactManager::BadArgumentError; return false; } QContactChangeSet changeSet; QContactId current; QContactManager::Error operationError = QContactManager::NoError; for (int i = 0; i < contactIds.count(); i++) { current = contactIds.at(i); if (!removeContact(current, changeSet, error)) { operationError = *error; if (errorMap) errorMap->insert(i, operationError); } } *error = operationError; d->emitSharedSignals(&changeSet); // return false if some errors occurred return (*error == QContactManager::NoError); } /*! \reimp */ QList QContactMemoryEngine::relationships(const QString &relationshipType, const QContact &participant, QContactRelationship::Role role, QContactManager::Error *error) const { QContact defaultContact; QList retn; for (int i = 0; i < d->m_relationships.size(); i++) { QContactRelationship curr = d->m_relationships.at(i); // check that the relationship type matches if (curr.relationshipType() != relationshipType && !relationshipType.isEmpty()) continue; // if the participantId argument is default constructed, then the relationship matches. if (participant == defaultContact) { retn.append(curr); continue; } // otherwise, check that the participant exists and plays the required role in the relationship. if (role == QContactRelationship::First && curr.first() == participant) { retn.append(curr); } else if (role == QContactRelationship::Second && curr.second() == participant) { retn.append(curr); } else if (role == QContactRelationship::Either && (curr.first() == participant || curr.second() == participant)) { retn.append(curr); } } *error = QContactManager::NoError; if (retn.isEmpty()) *error = QContactManager::DoesNotExistError; return retn; } /*! Saves the given relationship \a relationship, storing any error to \a error and filling the \a changeSet with ids of changed contacts and relationships as required Returns true if the operation was successful otherwise false. */ bool QContactMemoryEngine::saveRelationship(QContactRelationship *relationship, QContactChangeSet &changeSet, QContactManager::Error *error) { // Attempt to validate the relationship. // first, check that the source contact exists and is in this manager. QString myUri = managerUri(); int firstContactIndex = d->m_contactIds.indexOf(relationship->first().id()); if ((!relationship->first().id().managerUri().isEmpty() && relationship->first().id().managerUri() != myUri) ||firstContactIndex == -1) { *error = QContactManager::InvalidRelationshipError; return false; } // second, check that the second contact exists (if it's local); we cannot check other managers' contacts. QContact dest = relationship->second(); int secondContactIndex = d->m_contactIds.indexOf(dest.id()); if (dest.id().managerUri().isEmpty() || dest.id().managerUri() == myUri) { // this entry in the destination list is supposedly stored in this manager. // check that it exists, and that it isn't the source contact (circular) if (secondContactIndex == -1 || dest.id() == relationship->first().id()) { *error = QContactManager::InvalidRelationshipError; return false; } } // the relationship is valid. We need to update the manager URIs in the second contact if it is empty to our URI. if (dest.id().managerUri().isEmpty()) { // need to update the URI relationship->setSecond(dest); } // check to see if the relationship already exists in the database. If so, replace. // We do this because we don't want duplicates in our lists / maps of relationships. *error = QContactManager::NoError; QList allRelationships = d->m_relationships; for (int i = 0; i < allRelationships.size(); i++) { QContactRelationship curr = allRelationships.at(i); if (curr == *relationship) { return true; // TODO: set error to AlreadyExistsError and return false? } } // no matching relationship; must be new. append it to lists in our map of relationships where required. QList firstRelationships = d->m_orderedRelationships.value(relationship->first().id()); QList secondRelationships = d->m_orderedRelationships.value(relationship->second().id()); firstRelationships.append(*relationship); secondRelationships.append(*relationship); d->m_orderedRelationships.insert(relationship->first().id(), firstRelationships); d->m_orderedRelationships.insert(relationship->second().id(), secondRelationships); changeSet.insertAddedRelationshipsContact(relationship->first().id()); changeSet.insertAddedRelationshipsContact(relationship->second().id()); // update the contacts involved QContactManagerEngine::setContactRelationships(&d->m_contacts[firstContactIndex], firstRelationships); QContactManagerEngine::setContactRelationships(&d->m_contacts[secondContactIndex], secondRelationships); // finally, insert into our list of all relationships, and return. d->m_relationships.append(*relationship); return true; } /*! \reimp */ bool QContactMemoryEngine::saveRelationships(QList *relationships, QMap *errorMap, QContactManager::Error *error) { *error = QContactManager::NoError; QContactManager::Error functionError; QContactChangeSet changeSet; for (int i = 0; i < relationships->size(); i++) { QContactRelationship curr = relationships->at(i); saveRelationship(&curr, changeSet, &functionError); if (functionError != QContactManager::NoError && errorMap) errorMap->insert(i, functionError); // and replace the current relationship with the updated version. relationships->replace(i, curr); // also, update the total error if it did not succeed. if (functionError != QContactManager::NoError) *error = functionError; } d->emitSharedSignals(&changeSet); return (*error == QContactManager::NoError); } /*! Removes the given relationship \a relationship, storing any error to \a error and filling the \a changeSet with ids of changed contacts and relationships as required Returns true if the operation was successful otherwise false. */ bool QContactMemoryEngine::removeRelationship(const QContactRelationship &relationship, QContactChangeSet &changeSet, QContactManager::Error *error) { // attempt to remove it from our list of relationships. if (!d->m_relationships.removeOne(relationship)) { *error = QContactManager::DoesNotExistError; return false; } // if that worked, then we need to remove it from the two locations in our map, also. QList firstRelationships = d->m_orderedRelationships.value(relationship.first().id()); QList secondRelationships = d->m_orderedRelationships.value(relationship.second().id()); firstRelationships.removeOne(relationship); secondRelationships.removeOne(relationship); d->m_orderedRelationships.insert(relationship.first().id(), firstRelationships); d->m_orderedRelationships.insert(relationship.second().id(), secondRelationships); // Update the contacts as well int firstContactIndex = d->m_contactIds.indexOf(relationship.first().id()); int secondContactIndex = relationship.second().id().managerUri() == managerUri() ? d->m_contactIds.indexOf(relationship.second().id()) : -1; if (firstContactIndex != -1) QContactMemoryEngine::setContactRelationships(&d->m_contacts[firstContactIndex], firstRelationships); if (secondContactIndex != -1) QContactMemoryEngine::setContactRelationships(&d->m_contacts[secondContactIndex], secondRelationships); // set our changes, and return. changeSet.insertRemovedRelationshipsContact(relationship.first().id()); changeSet.insertRemovedRelationshipsContact(relationship.second().id()); *error = QContactManager::NoError; return true; } /*! \reimp */ bool QContactMemoryEngine::removeRelationships(const QList &relationships, QMap *errorMap, QContactManager::Error *error) { QContactManager::Error functionError; QContactChangeSet cs; for (int i = 0; i < relationships.size(); i++) { removeRelationship(relationships.at(i), cs, &functionError); // update the total error if it did not succeed. if (functionError != QContactManager::NoError) { if (errorMap) errorMap->insert(i, functionError); *error = functionError; } } d->emitSharedSignals(&cs); return (*error == QContactManager::NoError); } /*! \reimp */ void QContactMemoryEngine::requestDestroyed(QContactAbstractRequest *req) { Q_UNUSED(req); } /*! \reimp */ bool QContactMemoryEngine::startRequest(QContactAbstractRequest *req) { updateRequestState(req, QContactAbstractRequest::ActiveState); performAsynchronousOperation(req); return true; } bool QContactMemoryEngine::cancelRequest(QContactAbstractRequest *req) { Q_UNUSED(req); // we can't cancel since we complete immediately return false; } /*! \reimp */ bool QContactMemoryEngine::waitForRequestFinished(QContactAbstractRequest *req, int msecs) { // in our implementation, we always complete any operation we start. Q_UNUSED(msecs); Q_UNUSED(req); return true; } /*! * This slot is called some time after an asynchronous request is started. * It performs the required operation, sets the result and returns. */ void QContactMemoryEngine::performAsynchronousOperation(QContactAbstractRequest *currentRequest) { // store up changes, and emit signals once at the end of the (possibly batch) operation. QContactChangeSet changeSet; // Now perform the active request and emit required signals. Q_ASSERT(currentRequest->state() == QContactAbstractRequest::ActiveState); switch (currentRequest->type()) { case QContactAbstractRequest::ContactFetchRequest: { QContactFetchRequest *r = static_cast(currentRequest); QContactFilter filter = r->filter(); QList sorting = r->sorting(); QContactFetchHint fetchHint = r->fetchHint(); QContactManager::Error operationError = QContactManager::NoError; QList requestedContacts = contacts(filter, sorting, fetchHint, &operationError); // update the request with the results. if (!requestedContacts.isEmpty() || operationError != QContactManager::NoError) updateContactFetchRequest(r, requestedContacts, operationError, QContactAbstractRequest::FinishedState); else updateRequestState(currentRequest, QContactAbstractRequest::FinishedState); } break; case QContactAbstractRequest::ContactFetchByIdRequest: { QContactFetchByIdRequest *r = static_cast(currentRequest); QContactIdFilter idFilter; idFilter.setIds(r->contactIds()); QList sorting; QContactFetchHint fetchHint = r->fetchHint(); QContactManager::Error error = QContactManager::NoError; QList requestedContacts = contacts(idFilter, sorting, fetchHint, &error); // Build an index into the results QHash idMap; // value is index into unsorted if (error == QContactManager::NoError) { for (int i = 0; i < requestedContacts.size(); i++) { idMap.insert(requestedContacts[i].id(), i); } } // Find the order in which the results should be presented // Build up the results and errors QList results; QMap errorMap; int index = 0; foreach (const QContactId &id, r->contactIds()) { if (!idMap.contains(id)) { errorMap.insert(index, QContactManager::DoesNotExistError); error = QContactManager::DoesNotExistError; results.append(QContact()); } else { results.append(requestedContacts[idMap[id]]); } index++; } // update the request with the results. if (!requestedContacts.isEmpty() || error != QContactManager::NoError) QContactManagerEngine::updateContactFetchByIdRequest(r, results, error, errorMap, QContactAbstractRequest::FinishedState); else updateRequestState(currentRequest, QContactAbstractRequest::FinishedState); } break; case QContactAbstractRequest::ContactIdFetchRequest: { QContactIdFetchRequest *r = static_cast(currentRequest); QContactFilter filter = r->filter(); QList sorting = r->sorting(); QContactManager::Error operationError = QContactManager::NoError; QList requestedContactIds = contactIds(filter, sorting, &operationError); if (!requestedContactIds.isEmpty() || operationError != QContactManager::NoError) updateContactIdFetchRequest(r, requestedContactIds, operationError, QContactAbstractRequest::FinishedState); else updateRequestState(currentRequest, QContactAbstractRequest::FinishedState); } break; case QContactAbstractRequest::ContactSaveRequest: { QContactSaveRequest *r = static_cast(currentRequest); QList contacts = r->contacts(); QContactManager::Error operationError = QContactManager::NoError; QMap errorMap; saveContacts(&contacts, &errorMap, &operationError, r->typeMask()); updateContactSaveRequest(r, contacts, operationError, errorMap, QContactAbstractRequest::FinishedState); } break; case QContactAbstractRequest::ContactRemoveRequest: { // this implementation provides scant information to the user // the operation either succeeds (all contacts matching the filter were removed) // or it fails (one or more contacts matching the filter could not be removed) // if a failure occurred, the request error will be set to the most recent // error that occurred during the remove operation. QContactRemoveRequest *r = static_cast(currentRequest); QContactManager::Error operationError = QContactManager::NoError; QList contactsToRemove = r->contactIds(); QMap errorMap; for (int i = 0; i < contactsToRemove.size(); i++) { QContactManager::Error tempError; removeContact(contactsToRemove.at(i), changeSet, &tempError); if (tempError != QContactManager::NoError) { errorMap.insert(i, tempError); operationError = tempError; } } if (!errorMap.isEmpty() || operationError != QContactManager::NoError) updateContactRemoveRequest(r, operationError, errorMap, QContactAbstractRequest::FinishedState); else updateRequestState(currentRequest, QContactAbstractRequest::FinishedState); } break; case QContactAbstractRequest::RelationshipFetchRequest: { QContactRelationshipFetchRequest *r = static_cast(currentRequest); QContactManager::Error operationError = QContactManager::NoError; QList operationErrors; QList allRelationships = relationships(QString(), QContact(), QContactRelationship::Either, &operationError); QList requestedRelationships; // select the requested relationships. for (int i = 0; i < allRelationships.size(); i++) { QContactRelationship currRel = allRelationships.at(i); if (r->first() != QContact() && r->first() != currRel.first()) continue; if (r->second() != QContact() && r->second() != currRel.second()) continue; if (!r->relationshipType().isEmpty() && r->relationshipType() != currRel.relationshipType()) continue; requestedRelationships.append(currRel); } // update the request with the results. if (!requestedRelationships.isEmpty() || operationError != QContactManager::NoError) updateRelationshipFetchRequest(r, requestedRelationships, operationError, QContactAbstractRequest::FinishedState); else updateRequestState(currentRequest, QContactAbstractRequest::FinishedState); } break; case QContactAbstractRequest::RelationshipRemoveRequest: { QContactRelationshipRemoveRequest *r = static_cast(currentRequest); QContactManager::Error operationError = QContactManager::NoError; QList relationshipsToRemove = r->relationships(); QMap errorMap; removeRelationships(r->relationships(), &errorMap, &operationError); if (!errorMap.isEmpty() || operationError != QContactManager::NoError) updateRelationshipRemoveRequest(r, operationError, errorMap, QContactAbstractRequest::FinishedState); else updateRequestState(currentRequest, QContactAbstractRequest::FinishedState); } break; case QContactAbstractRequest::RelationshipSaveRequest: { QContactRelationshipSaveRequest *r = static_cast(currentRequest); QContactManager::Error operationError = QContactManager::NoError; QMap errorMap; QList requestRelationships = r->relationships(); saveRelationships(&requestRelationships, &errorMap, &operationError); // update the request with the results. updateRelationshipSaveRequest(r, requestRelationships, operationError, errorMap, QContactAbstractRequest::FinishedState); } break; default: // unknown request type. break; } // now emit any signals we have to emit d->emitSharedSignals(&changeSet); } QDateTime QContactMemoryEngine::deleted(const QContact &c) const { foreach(const QContactExtendedDetail &xd, c.details()) { if (xd.name() == X_DELETED_AT) { return xd.data().toDateTime(); } } return QDateTime(); } QDateTime QContactMemoryEngine::currentDateTime() const { QDateTime dateTime = property("CURRENT_DATE_TIME").toDateTime(); if (dateTime.isValid()) { return dateTime; } else { return QDateTime::currentDateTime(); } } void QContactMemoryEngine::partiallySyncDetails(QContact *to, const QContact &from, const QList &mask) { // these details in old contact QList fromDetails; // these details in new contact QList toDetails; // Collect details that match mask foreach (QContactDetail::DetailType type, mask) { fromDetails.append(from.details(type)); toDetails.append(to->details(type)); } // check details to remove foreach (QContactDetail detail, toDetails) { if (!fromDetails.contains(detail)) to->removeDetail(&detail); } // check details to save foreach (QContactDetail detail, fromDetails) { if (!toDetails.contains(detail)) to->saveDetail(&detail); } } /*! * \reimp */ bool QContactMemoryEngine::isRelationshipTypeSupported(const QString& relationshipType, QContactType::TypeValues contactType) const { // the memory backend supports arbitrary relationship types // but some relationship types don't make sense for groups. if (contactType == QContactType::TypeGroup) { if (relationshipType == QContactRelationship::HasSpouse() || relationshipType == QContactRelationship::HasAssistant()) { return false; } } // all other relationship types for all contact types are supported. return true; } /*! * \reimp */ QList QContactMemoryEngine::supportedDataTypes() const { QList st; st.append(QVariant::String); st.append(QVariant::Date); st.append(QVariant::DateTime); st.append(QVariant::Time); st.append(QVariant::Bool); st.append(QVariant::Char); st.append(QVariant::Int); st.append(QVariant::UInt); st.append(QVariant::LongLong); st.append(QVariant::ULongLong); st.append(QVariant::Double); return st; } /*! * The function returns true if the backend natively supports the given filter \a filter, otherwise false. */ bool QContactMemoryEngine::isFilterSupported(const QContactFilter &filter) const { Q_UNUSED(filter); // Until we add hashes for common stuff, fall back to slow code return false; } bool QContactMemoryEngine::saveContacts(QList *contacts, QMap *errorMap, QContactManager::Error *error, const QList &mask) { if (!contacts) { *error = QContactManager::BadArgumentError; return false; } QContactChangeSet changeSet; QContact current; QContactManager::Error operationError = QContactManager::NoError; for (int i = 0; i < contacts->count(); i++) { current = contacts->at(i); if (!saveContact(¤t, changeSet, error, mask)) { operationError = *error; if (errorMap) errorMap->insert(i, operationError); } else { (*contacts)[i] = current; } } *error = operationError; d->emitSharedSignals(&changeSet); // return false if some error occurred return (*error == QContactManager::NoError); } bool QContactMemoryEngine::saveContact(QContact *theContact, QContactChangeSet &changeSet, QContactManager::Error *error, const QList &mask) { // ensure that the contact's details conform to their definitions if (!validateContact(*theContact, error)) { return false; } QContactId id(theContact->id()); if (!id.managerUri().isEmpty() && id.managerUri() != managerUri()) { // the contact doesn't belong to this manager *error = QContactManager::DoesNotExistError; return false; } // check to see if this contact already exists int index = d->m_contactIds.indexOf(id); if (index != -1) { /* We also need to check that there are no modified create only details */ QContact oldContact = d->m_contacts.at(index); if (oldContact.type() != theContact->type()) { *error = QContactManager::AlreadyExistsError; return false; } // check if this is partial save if (!mask.isEmpty()) { QContact tempContact = oldContact; partiallySyncDetails(&tempContact, *theContact, mask); *theContact = tempContact; } // Keep the value from contact // =========================== // QContactTimestamp ts = theContact->detail(QContactTimestamp::Type); // ts.setLastModified(QDateTime::currentDateTime()); // QContactManagerEngine::setDetailAccessConstraints(&ts, QContactDetail::ReadOnly | QContactDetail::Irremovable); // theContact->saveDetail(&ts); // Looks ok, so continue d->m_contacts.replace(index, *theContact); changeSet.insertChangedContact(theContact->id()); } else { // id does not exist; if not zero, fail. QContactId newId; if (theContact->id() != QContactId() && theContact->id() != newId) { // the ID is not empty, and it doesn't identify an existing contact in our database either. *error = QContactManager::DoesNotExistError; return false; } // check if this is partial save if (!mask.isEmpty()) { QContact tempContact; partiallySyncDetails(&tempContact, *theContact, mask); *theContact = tempContact; } /* New contact */ // Keep the value from contact // =========================== // QContactTimestamp ts = theContact->detail(QContactTimestamp::Type); // ts.setLastModified(QDateTime::currentDateTime()); // ts.setCreated(ts.lastModified()); // setDetailAccessConstraints(&ts, QContactDetail::ReadOnly | QContactDetail::Irremovable); // theContact->saveDetail(&ts); // update the contact item - set its ID quint32 nextContactId = d->m_nextContactId; // don't increment the persistent version until we're successful or we know it collides. nextContactId += 1; // but do increment the temporary version to check for collision QContactMemoryEngineId *newMemoryEngineId = new QContactMemoryEngineId; newMemoryEngineId->m_contactId = nextContactId; newMemoryEngineId->m_managerUri = d->m_managerUri; QContactId newContactId = QContactId(newMemoryEngineId); theContact->setId(newContactId); // note: do NOT delete the QContactMemoryEngineId -- the QContactId ctor takes ownership of it. // finally, add the contact to our internal lists and return d->m_contacts.append(*theContact); // add contact to list d->m_contactIds.append(theContact->id()); // track the contact id. changeSet.insertAddedContact(theContact->id()); // successful, now increment the persistent version of the next item id. d->m_nextContactId += 1; } *error = QContactManager::NoError; // successful. return true; } /*! \class QContactMemoryEngineId \brief The QContactMemoryEngineId class provides an id which uniquely identifies a QContact stored within a QContactMemoryEngine. \internal It may be used as a reference implementation, although since different platforms have different semantics for ids the precise implementation required may differ. */ QContactMemoryEngineId::QContactMemoryEngineId() : QContactEngineId(), m_contactId(0) { } QContactMemoryEngineId::QContactMemoryEngineId(quint32 contactId, const QString &managerUri) : QContactEngineId(), m_contactId(contactId), m_managerUri(managerUri) { } QContactMemoryEngineId::~QContactMemoryEngineId() { } QContactMemoryEngineId::QContactMemoryEngineId(const QContactMemoryEngineId &other) : QContactEngineId(), m_contactId(other.m_contactId), m_managerUri(other.m_managerUri) { } QContactMemoryEngineId::QContactMemoryEngineId(const QMap ¶meters, const QString &engineIdString) : QContactEngineId() { m_contactId = engineIdString.toInt(); m_managerUri = QContactManager::buildUri(ENGINE_NAME, parameters); } bool QContactMemoryEngineId::isEqualTo(const QContactEngineId *other) const { if (m_contactId != static_cast(other)->m_contactId) return false; return true; } bool QContactMemoryEngineId::isLessThan(const QContactEngineId *other) const { const QContactMemoryEngineId *otherPtr = static_cast(other); if (m_managerUri < otherPtr->m_managerUri) return true; if (m_contactId < otherPtr->m_contactId) return true; return false; } QString QContactMemoryEngineId::managerUri() const { return m_managerUri; } QString QContactMemoryEngineId::toString() const { return QString::number(m_contactId); } QContactEngineId* QContactMemoryEngineId::clone() const { return new QContactMemoryEngineId(m_contactId, m_managerUri); } #ifndef QT_NO_DEBUG_STREAM QDebug& QContactMemoryEngineId::debugStreamOut(QDebug &dbg) const { dbg.nospace() << "QContactMemoryEngineId(" << m_managerUri << "," << m_contactId << ")"; return dbg.maybeSpace(); } #endif uint QContactMemoryEngineId::hash() const { return m_contactId; } #include "moc_qcontactmemorybackend_p.cpp" QT_END_NAMESPACE_CONTACTS buteo-sync-plugins-contacts-0.1+16.04.20151211/tests/plugins/contacts/qcontactmemorybackend_p.h0000644000015300001610000002404012632560105032771 0ustar pbuserpbgroup00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the QtContacts module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #ifndef QCONTACTMEMORYBACKEND_P_H #define QCONTACTMEMORYBACKEND_P_H // // W A R N I N G // ------------- // // This file is not part of the Qt API. It exists purely as an // implementation detail. This header file may change from version to // version without notice, or even be removed. // // We mean it. // #include #include #include #include #include #include #include QT_BEGIN_NAMESPACE_CONTACTS class QContactMemoryEngine; class QContactMemoryEngineFactory : public QContactManagerEngineFactory { Q_OBJECT Q_PLUGIN_METADATA(IID "org.qt-project.Qt.QContactManagerEngineFactoryInterface" FILE "memory.json") public: QContactManagerEngine* engine(const QMap ¶meters, QContactManager::Error*); QString managerName() const; QContactEngineId* createContactEngineId(const QMap ¶meters, const QString &engineIdString) const; }; class QContactMemoryEngineData : public QSharedData { public: QContactMemoryEngineData() : QSharedData() , m_refCount(QAtomicInt(1)) , m_selfContactId() , m_nextContactId(1) , m_anonymous(false) { } QContactMemoryEngineData(const QContactMemoryEngineData &other) : QSharedData(other), m_refCount(QAtomicInt(1)), m_selfContactId(other.m_selfContactId), m_nextContactId(other.m_nextContactId), m_anonymous(other.m_anonymous) { } ~QContactMemoryEngineData() { } static QContactMemoryEngineData *data(QContactMemoryEngine *engine); QAtomicInt m_refCount; QString m_id; // the id parameter value QContactId m_selfContactId; // the "MyCard" contact id QList m_contacts; // list of contacts QList m_contactIds; // list of contact Id's QList m_relationships; // list of contact relationships QMap > m_orderedRelationships; // map of ordered lists of contact relationships QList m_definitionIds; // list of definition types (id's) quint32 m_nextContactId; bool m_anonymous; // Is this backend ever shared? QString m_managerUri; // for faster lookup. void emitSharedSignals(QContactChangeSet *cs) { foreach(QContactManagerEngine* engine, m_sharedEngines) cs->emitSignals(engine); } QList m_sharedEngines; // The list of engines that share this data }; class QContactMemoryEngineId : public QContactEngineId { public: QContactMemoryEngineId(); ~QContactMemoryEngineId(); QContactMemoryEngineId(quint32 contactId, const QString &managerUri); QContactMemoryEngineId(const QContactMemoryEngineId &other); QContactMemoryEngineId(const QMap ¶meters, const QString &engineIdString); bool isEqualTo(const QContactEngineId *other) const; bool isLessThan(const QContactEngineId *other) const; QString managerUri() const; QContactEngineId* clone() const; QString toString() const; #ifndef QT_NO_DEBUG_STREAM QDebug& debugStreamOut(QDebug &dbg) const; #endif uint hash() const; private: quint32 m_contactId; QString m_managerUri; friend class QContactMemoryEngine; }; class QContactMemoryEngine : public QContactManagerEngine { Q_OBJECT public: static QContactMemoryEngine *createMemoryEngine(const QMap ¶meters); ~QContactMemoryEngine(); /* URI reporting */ QString managerName() const; QMap managerParameters() const; /*! \reimp */ int managerVersion() const {return 1;} virtual QList contactIds(const QContactFilter &filter, const QList &sortOrders, QContactManager::Error *error) const; virtual QList contacts(const QContactFilter &filter, const QList &sortOrders, const QContactFetchHint &fetchHint, QContactManager::Error *error) const; virtual QContact contact(const QContactId &contactId, const QContactFetchHint &fetchHint, QContactManager::Error *error) const; virtual bool saveContacts(QList *contacts, QMap *errorMap, QContactManager::Error *error); virtual bool removeContacts(const QList &contactIds, QMap *errorMap, QContactManager::Error *error); /* "Self" contact id (MyCard) */ virtual bool setSelfContactId(const QContactId &contactId, QContactManager::Error *error); virtual QContactId selfContactId(QContactManager::Error *error) const; /* Relationships between contacts */ virtual QList relationships(const QString &relationshipType, const QContact &participant, QContactRelationship::Role role, QContactManager::Error *error) const; virtual bool saveRelationships(QList *relationships, QMap *errorMap, QContactManager::Error *error); virtual bool removeRelationships(const QList &relationships, QMap *errorMap, QContactManager::Error *error); /*! \reimp */ virtual bool validateContact(const QContact &contact, QContactManager::Error *error) const { return QContactManagerEngine::validateContact(contact, error); } /* Asynchronous Request Support */ virtual void requestDestroyed(QContactAbstractRequest *req); virtual bool startRequest(QContactAbstractRequest *req); virtual bool cancelRequest(QContactAbstractRequest *req); virtual bool waitForRequestFinished(QContactAbstractRequest *req, int msecs); /* Capabilities reporting */ virtual bool isRelationshipTypeSupported(const QString &relationshipType, QContactType::TypeValues contactType) const; virtual bool isFilterSupported(const QContactFilter &filter) const; virtual QList supportedDataTypes() const; /*! \reimp */ virtual QList supportedContactTypes() const { return QContactManagerEngine::supportedContactTypes(); } virtual QList supportedContactDetailTypes() const { return QContactManagerEngine::supportedContactDetailTypes(); } protected: QContactMemoryEngine(QContactMemoryEngineData *data); protected: /* Implement "signal coalescing" for batch functions via change set */ virtual bool saveContact(QContact *theContact, QContactChangeSet &changeSet, QContactManager::Error *error); virtual bool removeContact(const QContactId &contactId, QContactChangeSet &changeSet, QContactManager::Error *error); virtual bool saveRelationship(QContactRelationship *relationship, QContactChangeSet &changeSet, QContactManager::Error *error); virtual bool removeRelationship(const QContactRelationship &relationship, QContactChangeSet &changeSet, QContactManager::Error *error); private: /* For partial save */ bool saveContacts(QList *contacts, QMap *errorMap, QContactManager::Error *error, const QList &mask); bool saveContact(QContact *theContact, QContactChangeSet &changeSet, QContactManager::Error *error, const QList &mask); void partiallySyncDetails(QContact *to, const QContact &from, const QList &mask); void performAsynchronousOperation(QContactAbstractRequest *request); bool testDeletedContact(const QContactFilter &filter, const QContact &contact, const QDateTime &deletedAt) const; QDateTime deleted(const QContact &c) const; QDateTime currentDateTime() const; QContactMemoryEngineData *d; static QMap engineDatas; friend class QContactMemoryEngineData; }; QT_END_NAMESPACE_CONTACTS #endif // QCONTACTMEMORYBACKEND_P_H buteo-sync-plugins-contacts-0.1+16.04.20151211/tests/plugins/contacts/memory.json0000644000015300001610000000003312632560105030123 0ustar pbuserpbgroup00000000000000{ "Keys": [ "mock" ] } buteo-sync-plugins-contacts-0.1+16.04.20151211/tests/plugins/contacts/CMakeLists.txt0000644000015300001610000000037712632560105030473 0ustar pbuserpbgroup00000000000000set(QCONTACTS_BACKEND qtcontacts_mock) set(QCONTACTS_BACKEND_SRCS qcontactmemorybackend_p.h qcontactmemorybackend.cpp ) add_library(${QCONTACTS_BACKEND} MODULE ${QCONTACTS_BACKEND_SRCS} ) qt5_use_modules(${QCONTACTS_BACKEND} Core Contacts) buteo-sync-plugins-contacts-0.1+16.04.20151211/tests/CMakeLists.txt0000644000015300001610000000016112632560105025163 0ustar pbuserpbgroup00000000000000set(MOCK_PLUGIN_PATH ${CMAKE_CURRENT_BINARY_DIR}/plugins/) add_subdirectory(plugins) add_subdirectory(unittest) buteo-sync-plugins-contacts-0.1+16.04.20151211/google/0000755000015300001610000000000012632561656022553 5ustar pbuserpbgroup00000000000000buteo-sync-plugins-contacts-0.1+16.04.20151211/google/GContactsClient.cpp0000644000015300001610000000555312632560105026277 0ustar pbuserpbgroup00000000000000/* * This file is part of buteo-sync-plugins-contacts package * * Copyright (C) 2013 Jolla Ltd. and/or its subsidiary(-ies). * 2015 Canonical Ltd * * Contributors: Sateesh Kavuri * Mani Chandrasekar * Renato Araujo Oliveira Filho * * 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 "GContactsClient.h" #include "GTransport.h" #include "GConfig.h" #include "GRemoteSource.h" #include #include #include #include #include #include #include "buteosyncfw_p.h" static const QString GOOGLE_CONTACTS_SERVICE ("google-buteo-contacts"); extern "C" GContactsClient* createPlugin(const QString& aPluginName, const Buteo::SyncProfile& aProfile, Buteo::PluginCbInterface *aCbInterface) { return new GContactsClient(aPluginName, aProfile, aCbInterface); } extern "C" void destroyPlugin(GContactsClient *aClient) { delete aClient; } GContactsClient::GContactsClient(const QString& aPluginName, const Buteo::SyncProfile& aProfile, Buteo::PluginCbInterface *aCbInterface) : UContactsClient(aPluginName, aProfile, aCbInterface, GOOGLE_CONTACTS_SERVICE) { FUNCTION_CALL_TRACE; } GContactsClient::~GContactsClient() { FUNCTION_CALL_TRACE; } QVariantMap GContactsClient::remoteSourceProperties() const { QVariantMap remoteProperties; remoteProperties.insert("AUTH-TOKEN", authToken()); remoteProperties.insert("SYNC-TARGET", syncTargetId()); remoteProperties.insert("ACCOUNT-NAME", accountName()); remoteProperties.insert(Buteo::KEY_REMOTE_DATABASE, iProfile.key(Buteo::KEY_REMOTE_DATABASE)); remoteProperties.insert(Buteo::KEY_HTTP_PROXY_HOST, iProfile.key(Buteo::KEY_HTTP_PROXY_HOST)); remoteProperties.insert(Buteo::KEY_HTTP_PROXY_PORT, iProfile.key(Buteo::KEY_HTTP_PROXY_PORT)); return remoteProperties; } UAbstractRemoteSource *GContactsClient::createRemoteSource(QObject *parent) const { return qobject_cast(new GRemoteSource(parent)); } buteo-sync-plugins-contacts-0.1+16.04.20151211/google/GContactImageDownloader.h0000644000015300001610000000400012632560105027366 0ustar pbuserpbgroup00000000000000/**************************************************************************** ** ** Copyright (C) 2015 Canonical Ltd. ** ** Contact: Renato Araujo Oliveira Filho ** ** This program/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 program/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 program/library; if not, write to the Free ** Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA ** 02110-1301 USA ** ****************************************************************************/ #ifndef GOOGLECONTACTIMAGEDOWNLOADER_H #define GOOGLECONTACTIMAGEDOWNLOADER_H #include #include #include #include #include #include #include #include #include class GTransport; class GContactImageDownloader: public QObject { Q_OBJECT public: explicit GContactImageDownloader(const QString &authToken, QObject *parent = 0); ~GContactImageDownloader(); void push(const QUrl &imgUrl); QMap donwloaded(); void exec(); signals: void downloadFinished(const QUrl &imgUrl, const QUrl &localFile); void donwloadError(const QUrl &imgUrl, const QString &error); private slots: void onRequestFinished(QNetworkReply *reply); private: QEventLoop *mEventLoop; QQueue mQueue; QString mAuthToken; QMap mResults; bool mAbort; QStringList mTempFiles; QUrl saveImage(const QUrl &remoteFile, const QByteArray &imgData); }; #endif // GOOGLECONTACTIMAGEDOWNLOADER_H buteo-sync-plugins-contacts-0.1+16.04.20151211/google/GConfig.h0000644000015300001610000000411012632560105024220 0ustar pbuserpbgroup00000000000000/* * This file is part of buteo-sync-plugins-contacts package * * Copyright (C) 2013 Jolla Ltd. and/or its subsidiary(-ies). * 2015 Canonical Ltd * * Contributors: Sateesh Kavuri * Mani Chandrasekar * Renato Araujo Oliveira Filho * * 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 GCONFIG_H #define GCONFIG_H #include class GConfig { public: static const int MAX_RESULTS; static const QString SCOPE_URL; static const QString GCONTACT_URL; static const QString GDATA_VERSION_TAG; static const QString GDATA_VERSION; static const QString G_DELETE_OVERRIDE_HEADER; static const QString G_ETAG_HEADER; static const QString G_AUTH_HEADER; /* My Contacts Group */ static const QString GROUP_MY_CONTACTS_ID; /* Query parameters */ static const QString QUERY_TAG; static const QString MAX_RESULTS_TAG; static const QString START_INDEX_TAG; static const QString UPDATED_MIN_TAG; static const QString ORDERBY_TAG; static const QString SHOW_DELETED_TAG; static const QString REQUIRE_ALL_DELETED; static const QString SORTORDER_TAG; static const QString PHOTO_TAG; static const QString MEDIA_TAG; static const QString BATCH_TAG; typedef enum { NONE = 0, ADD, UPDATE, DELETE } TRANSACTION_TYPE; }; #endif // GCONFIG_H buteo-sync-plugins-contacts-0.1+16.04.20151211/google/GContactStream.cpp0000644000015300001610000016572212632560105026136 0ustar pbuserpbgroup00000000000000/**************************************************************************** ** ** Copyright (C) 2013-2014 Jolla Ltd. and/or its subsidiary(-ies). ** 2015 Canonical Ltd. ** ** Contributors: Sateesh Kavuri ** Mani Chandrasekar ** Chris Adams ** Renato Araujo Oliveira Filho ** ** This program/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 program/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 program/library; if not, write to the Free ** Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA ** 02110-1301 USA ** ****************************************************************************/ #include "GConfig.h" #include "GContactStream.h" #include "GContactAtom.h" #include "UContactsCustomDetail.h" #include "buteosyncfw_p.h" #include #include GoogleContactStream::GoogleContactStream(bool response, const QString &accountEmail, QObject* parent) : QObject(parent) , mXmlReader(0) , mAtom(0) , mXmlWriter(0) , mAccountEmail(accountEmail) { if (response == true) { initResponseFunctionMap(); } else { initFunctionMap(); } } GoogleContactStream::~GoogleContactStream() { } GoogleContactAtom *GoogleContactStream::parse(const QByteArray &xmlBuffer) { mXmlReader = new QXmlStreamReader(xmlBuffer); mAtom = new GoogleContactAtom; Q_CHECK_PTR(mXmlReader); Q_CHECK_PTR(mAtom); while (!mXmlReader->atEnd() && !mXmlReader->hasError()) { if (mXmlReader->readNextStartElement()) { Handler handler = mAtomFunctionMap.value(mXmlReader->name().toString()); if (handler) { (*this.*handler)(); } } } delete mXmlReader; return mAtom; } QByteArray GoogleContactStream::encode(const QMultiMap > &updates) { QByteArray xmlBuffer; mXmlWriter = new QXmlStreamWriter(&xmlBuffer); startBatchFeed(); QList > removedContacts = updates.values(GoogleContactStream::Remove); for (int i = 0; i < removedContacts.size(); ++i) { encodeContactUpdate(removedContacts[i].first, removedContacts[i].second, GoogleContactStream::Remove, true); // batchmode = true } QList > addedContacts = updates.values(GoogleContactStream::Add); for (int i = 0; i < addedContacts.size(); ++i) { encodeContactUpdate(addedContacts[i].first, addedContacts[i].second, GoogleContactStream::Add, true); // batchmode = true } QList > modifiedContacts = updates.values(GoogleContactStream::Modify); for (int i = 0; i < modifiedContacts.size(); ++i) { encodeContactUpdate(modifiedContacts[i].first, modifiedContacts[i].second, GoogleContactStream::Modify, true); // batchmode = true } endBatchFeed(); mXmlWriter->writeEndDocument(); delete mXmlWriter; return xmlBuffer; } // ---------------------------------------- void GoogleContactStream::initAtomFunctionMap() { mAtomFunctionMap.insert("updated", &GoogleContactStream::handleAtomUpdated); mAtomFunctionMap.insert("category", &GoogleContactStream::handleAtomCategory); mAtomFunctionMap.insert("author", &GoogleContactStream::handleAtomAuthor); mAtomFunctionMap.insert("id", &GoogleContactStream::handleAtomId); mAtomFunctionMap.insert("totalResults", &GoogleContactStream::handleAtomOpenSearch); mAtomFunctionMap.insert("startIndex", &GoogleContactStream::handleAtomOpenSearch); mAtomFunctionMap.insert("itemsPerPage", &GoogleContactStream::handleAtomOpenSearch); mAtomFunctionMap.insert("link", &GoogleContactStream::handleAtomLink); mAtomFunctionMap.insert("entry", &GoogleContactStream::handleAtomEntry); mAtomFunctionMap.insert("generator", &GoogleContactStream::handleAtomGenerator); mAtomFunctionMap.insert("title", &GoogleContactStream::handleAtomTitle); } void GoogleContactStream::initResponseFunctionMap() { initAtomFunctionMap(); // TODO: move the batch request response handling stuff here. } void GoogleContactStream::initFunctionMap() { initAtomFunctionMap(); mContactFunctionMap.insert("updated", &GoogleContactStream::handleEntryUpdated); //mContactFunctionMap.insert("app:edited", &GoogleContactStream::handleEntryUpdated); mContactFunctionMap.insert("gContact:birthday", &GoogleContactStream::handleEntryBirthday); mContactFunctionMap.insert("gContact:gender", &GoogleContactStream::handleEntryGender); mContactFunctionMap.insert("gContact:hobby", &GoogleContactStream::handleEntryHobby); mContactFunctionMap.insert("gContact:nickname", &GoogleContactStream::handleEntryNickname); mContactFunctionMap.insert("gContact:occupation", &GoogleContactStream::handleEntryOccupation); mContactFunctionMap.insert("gContact:website", &GoogleContactStream::handleEntryWebsite); mContactFunctionMap.insert("gContact:groupMembershipInfo", &GoogleContactStream::handleEntryGroup); mContactFunctionMap.insert("gContact:event", &GoogleContactStream::handleEntryEvent); mContactFunctionMap.insert("gContact:jot", &GoogleContactStream::handleEntryJot); mContactFunctionMap.insert("gContact:relation", &GoogleContactStream::handleRelation); mContactFunctionMap.insert("gd:email", &GoogleContactStream::handleEntryEmail); mContactFunctionMap.insert("gd:im", &GoogleContactStream::handleEntryIm); mContactFunctionMap.insert("gd:name", &GoogleContactStream::handleEntryName); mContactFunctionMap.insert("gd:organization", &GoogleContactStream::handleEntryOrganization); mContactFunctionMap.insert("gd:phoneNumber", &GoogleContactStream::handleEntryPhoneNumber); mContactFunctionMap.insert("gd:structuredPostalAddress", &GoogleContactStream::handleEntryStructuredPostalAddress); mContactFunctionMap.insert("gd:extendedProperty", &GoogleContactStream::handleEntryExtendedProperty); } // ---------------------------------------- void GoogleContactStream::handleAtomUpdated() { Q_ASSERT(mXmlReader->isStartElement() && mXmlReader->name() == "updated"); mAtom->setUpdated(mXmlReader->readElementText()); } void GoogleContactStream::handleAtomCategory() { Q_ASSERT(mXmlReader->isStartElement() && mXmlReader->name() == "category"); QXmlStreamAttributes attributes = mXmlReader->attributes(); QString scheme, term; if (attributes.hasAttribute("scheme")) { scheme = attributes.value("scheme").toString(); } if (attributes.hasAttribute("term")) { term = attributes.value("term").toString(); } mAtom->setCategory(scheme, term); } void GoogleContactStream::handleAtomAuthor() { Q_ASSERT(mXmlReader->isStartElement() && mXmlReader->name() == "author"); while (!(mXmlReader->tokenType() == QXmlStreamReader::EndElement && mXmlReader->name() == "author")) { if (mXmlReader->tokenType() == QXmlStreamReader::StartElement) { if (mXmlReader->name() == "name") { mAtom->setAuthorName(mXmlReader->readElementText()); } else if (mXmlReader->name() == "email") { mAtom->setAuthorEmail(mXmlReader->readElementText()); } } mXmlReader->readNextStartElement(); } } void GoogleContactStream::handleAtomOpenSearch() { Q_ASSERT(mXmlReader->isStartElement() && mXmlReader->prefix() == "openSearch"); if (mXmlReader->name() == "totalResults") { mAtom->setTotalResults(mXmlReader->readElementText().toInt()); } else if (mXmlReader->name() == "startIndex") { mAtom->setStartIndex(mXmlReader->readElementText().toInt()); } else if (mXmlReader->name() == "itemsPerPage") { mAtom->setItemsPerPage(mXmlReader->readElementText().toInt()); } } void GoogleContactStream::handleAtomLink() { Q_ASSERT(mXmlReader->isStartElement() && mXmlReader->name() == "link"); if (mXmlReader->attributes().hasAttribute("rel") && (mXmlReader->attributes().value("rel") == "next")) { mAtom->setNextEntriesUrl(mXmlReader->attributes().value("href").toString()); } } void GoogleContactStream::handleAtomId() { Q_ASSERT(mXmlReader->isStartElement() && mXmlReader->name() == "id"); mAtom->setId(mXmlReader->readElementText()); } void GoogleContactStream::handleAtomGenerator() { Q_ASSERT(mXmlReader->isStartElement() && mXmlReader->name() == "generator"); QXmlStreamAttributes attributes = mXmlReader->attributes(); QString name, version, uri; if (attributes.hasAttribute("version")) { version = attributes.value("version").toString(); } if (attributes.hasAttribute("uri")) { uri = attributes.value("uri").toString(); } name = mXmlReader->readElementText(); mAtom->setGenerator(name, version, uri); } void GoogleContactStream::handleAtomTitle() { Q_ASSERT(mXmlReader->isStartElement() && mXmlReader->name() == "title"); mAtom->setTitle(mXmlReader->readElementText()); } void GoogleContactStream::handleAtomEntry() { Q_ASSERT(mXmlReader->isStartElement() && mXmlReader->name() == "entry"); // the entry will either be a contact, a group, or a response to a batch update request. // if it's a group, we need to store some information about it. QString systemGroupId; QString systemGroupAtomId; // the entry will be a contact if this is a response to a "read" request QContact entryContact; QStringList unsupportedElements; bool isInGroup = false; bool isDeleted = false; // or it will be a series of batch operation success/fail info // if this xml is the response to a batch update/delete request. bool isBatchOperationResponse = false; GoogleContactAtom::BatchOperationResponse response; while (!((mXmlReader->tokenType() == QXmlStreamReader::EndElement) && (mXmlReader->name() == "entry"))) { if (mXmlReader->tokenType() == QXmlStreamReader::StartElement) { isInGroup |= (mXmlReader->qualifiedName().toString() == QStringLiteral("gContact:groupMembershipInfo")); DetailHandler handler = mContactFunctionMap.value(mXmlReader->qualifiedName().toString()); if (handler) { QContactDetail convertedDetail = (*this.*handler)(); if (convertedDetail != QContactDetail()) { entryContact.saveDetail(&convertedDetail); } else { LOG_WARNING("Handle not found for " << mXmlReader->qualifiedName().toString()); } } else if (mXmlReader->qualifiedName().toString() == QStringLiteral("gd:deleted")) { isDeleted = true; } else if (mXmlReader->qualifiedName().toString() == QStringLiteral("batch:id")) { isBatchOperationResponse = true; handleEntryBatchId(&response); } else if (mXmlReader->qualifiedName().toString() == QStringLiteral("batch:operation")) { isBatchOperationResponse = true; handleEntryBatchOperation(&response); } else if (mXmlReader->qualifiedName().toString() == QStringLiteral("batch:status")) { isBatchOperationResponse = true; handleEntryBatchStatus(&response); } else if (mXmlReader->qualifiedName().toString() == QStringLiteral("link")) { // There are several possible links: // Avatar Photo link // Self query link // Edit link // Batch link etc. // If it's an avatar, we grab it as a QContactAvatar detail bool isAvatar = false; QContactAvatar googleAvatar; QString etag; QString unsupportedElement = handleEntryLink(&googleAvatar, &isAvatar, &etag); if (isAvatar) { // check if we have already a google avatar entryContact.saveDetail(&googleAvatar); UContactsCustomDetail::setCustomField(entryContact, UContactsCustomDetail::FieldContactAvatarETag, etag); } // Whether it's an avatar or not, we also store the element text. if (!unsupportedElement.isEmpty()) { unsupportedElements.append(unsupportedElement); } } else if (mXmlReader->name().toString() == QStringLiteral("entry")) { // read the etag out of the entry. response.eTag = mXmlReader->attributes().value("gd:etag").toString(); } else if (mXmlReader->qualifiedName().toString() == QStringLiteral("gContact:systemGroup")) { systemGroupId = mXmlReader->attributes().value("id").toString(); } else if (mXmlReader->qualifiedName().toString() == QStringLiteral("id")) { // either a contact id or a group id. QContactDetail guidDetail = handleEntryId(&systemGroupAtomId); entryContact.saveDetail(&guidDetail); } else { // This is some XML element which we don't handle. // We should store it, so that we can send it back when we upload changes. QString unsupportedElement = handleEntryUnknownElement(); if (!unsupportedElement.isEmpty()) { unsupportedElements.append(unsupportedElement); } } } mXmlReader->readNextStartElement(); } if (!systemGroupId.isEmpty()) { // this entry was a group mAtom->addEntrySystemGroup(systemGroupId, systemGroupAtomId); } else { // this entry was a contact. // the etag is the "version identifier". Save it into the QCOM detail. if (!response.eTag.isEmpty()) { QtContacts::QContactExtendedDetail etagDetail = UContactsCustomDetail::getCustomField(entryContact, UContactsCustomDetail::FieldContactETag); etagDetail.setData(response.eTag); entryContact.saveDetail(&etagDetail); } if (isInGroup) { // Only sync the contact if it is in a "real" group // as otherwise we get hundreds of "Other Contacts" // (random email addresses etc). if (isDeleted) { // if contact is deleted set the deletedAt value QContactExtendedDetail deletedAt = UContactsCustomDetail::getCustomField(entryContact, UContactsCustomDetail::FieldDeletedAt); deletedAt.setData(QDateTime::currentDateTime()); entryContact.saveDetail(&deletedAt); mAtom->addDeletedEntryContact(entryContact); } else { mAtom->addEntryContact(entryContact, unsupportedElements); } } } if (isBatchOperationResponse) { if (!entryContact.detail().guid().isEmpty()) { response.contactGuid = entryContact.detail().guid(); } mAtom->addBatchOperationResponse(response.operationId, response); } } QString GoogleContactStream::handleEntryLink(QContactAvatar *avatar, bool *isAvatar, QString *etag) { Q_ASSERT(mXmlReader->isStartElement() && mXmlReader->name() == "link"); QXmlStreamAttributes attributes = mXmlReader->attributes(); // if a contact does not have a photo, then the photo link element has no gd:etag attribute. *isAvatar = ((attributes.value("rel") == "http://schemas.google.com/contacts/2008/rel#photo") && attributes.hasAttribute("gd:etag")); if (*isAvatar) { // this is an avatar photo for the contact entry avatar->setImageUrl(attributes.value("href").toString()); *etag = attributes.value("gd:etag").toString(); } return handleEntryUnknownElement(); } QContactDetail GoogleContactStream::handleEntryExtendedProperty() { Q_ASSERT(mXmlReader->isStartElement() && mXmlReader->name() == "gd:extendedProperty"); QContactExtendedDetail xd; QXmlStreamAttributes attributes = mXmlReader->attributes(); QString propName = attributes.value("name").toString(); // FIXME: // We use extendedProperty to store favorite property until we implment support // for google groups sync if (propName == "X-FAVORITE") { QContactFavorite fav; fav.setFavorite(attributes.value("value").toString() == "true"); return fav; } else if (propName == "SOUND") { QContactRingtone ring; ring.setAudioRingtoneUrl(QUrl(attributes.value("value").toString())); return ring; } // handle as generic type xd.setName(attributes.value("name").toString()); xd.setData(attributes.value("value").toString()); return xd; } QContactDetail GoogleContactStream::handleEntryEvent() { Q_ASSERT(mXmlReader->isStartElement() && mXmlReader->name() == "gContact:event"); // // // static QMap anniversaryTypes; if (anniversaryTypes.isEmpty()) { anniversaryTypes.insert(QString::fromLatin1("engagement"), QContactAnniversary::SubTypeEngagement); anniversaryTypes.insert(QString::fromLatin1("employment"), QContactAnniversary::SubTypeEmployment); anniversaryTypes.insert(QString::fromLatin1("memorial"), QContactAnniversary::SubTypeMemorial); anniversaryTypes.insert(QString::fromLatin1("house"), QContactAnniversary::SubTypeHouse); anniversaryTypes.insert(QString::fromLatin1("wedding"), QContactAnniversary::SubTypeWedding); } QXmlStreamAttributes attributes = mXmlReader->attributes(); if (attributes.value("rel") == "anniversary") { QContactAnniversary anniversary; anniversary.setSubType(anniversaryTypes.value(attributes.value("label").toString(), QContactAnniversary::SubTypeWedding)); mXmlReader->readNextStartElement(); if (mXmlReader->qualifiedName() == "gd:when") { attributes = mXmlReader->attributes(); anniversary.setOriginalDateTime(QDateTime::fromString(attributes.value("startTime").toString(), Qt::ISODate)); anniversary.setEvent(attributes.value("valueString").toString()); // FIXME: missing endTime // QContactAnniversary API does not support endTime } return anniversary; } return QContactDetail(); } QContactDetail GoogleContactStream::handleRelation() { Q_ASSERT(mXmlReader->isStartElement() && mXmlReader->name() == "gContact:relation"); QXmlStreamAttributes attributes = mXmlReader->attributes(); QString rel = attributes.hasAttribute("rel") ? attributes.value("rel").toString() : QString(); QContactFamily family; if (rel == "spouse") { family.setSpouse(mXmlReader->readElementText()); } else if (rel == "child") { family.setChildren(QStringList() << mXmlReader->readElementText()); } else { LOG_WARNING("Family relation type not supported" << rel); return QContactDetail(); } return family; } QString GoogleContactStream::handleEntryUnknownElement() { Q_ASSERT(mXmlReader->isStartElement()); QXmlStreamAttributes attributes = mXmlReader->attributes(); QString attributesString; for (int i = 0; i < attributes.size(); ++i) { QString extra = QStringLiteral(" %1=\"%2\"") .arg(attributes[i].qualifiedName().toString()) .arg(attributes[i].value().toString().toHtmlEscaped()); attributesString.append(extra); } QString unknownElement = QStringLiteral("<%1%2>%3") .arg(mXmlReader->qualifiedName().toString()) .arg(attributesString) .arg(mXmlReader->text().toString()); return unknownElement; } QList GoogleContactStream::handleContext(const QString &rel) const { QList contexts; QString name = rel.split("#").last(); if (name == QStringLiteral("home")) { contexts << QContactDetail::ContextHome; } else if (name == QStringLiteral("work")) { contexts << QContactDetail::ContextWork; } else if (!name.isEmpty()) { contexts << QContactDetail::ContextOther; } return contexts; } void GoogleContactStream::handleEntryBatchStatus(GoogleContactAtom::BatchOperationResponse *response) { Q_ASSERT(mXmlReader->isStartElement() && mXmlReader->name() == "status"); response->code = mXmlReader->attributes().value("code").toString(); response->reason = mXmlReader->attributes().value("reason").toString(); response->reasonDescription = mXmlReader->readElementText(); response->isError = true; if (response->code == QStringLiteral("200") // No error. || response->code == QStringLiteral("201") // Created without error. || response->code == QStringLiteral("304")) { // Not modified (no change since time specified) // according to Google Data API these response codes signify success cases. response->isError = false; } } void GoogleContactStream::handleEntryBatchOperation(GoogleContactAtom::BatchOperationResponse *response) { Q_ASSERT(mXmlReader->isStartElement() && mXmlReader->name() == "operation"); response->type = mXmlReader->attributes().value("type").toString(); } void GoogleContactStream::handleEntryBatchId(GoogleContactAtom::BatchOperationResponse *response) { Q_ASSERT(mXmlReader->isStartElement() && mXmlReader->name() == "id"); response->operationId = mXmlReader->readElementText(); } QContactDetail GoogleContactStream::handleEntryId(QString *rawId) { *rawId = mXmlReader->readElementText(); QString idUrl = *rawId; QContactGuid guid; guid.setGuid(idUrl.split('/').last()); return guid; } QContactDetail GoogleContactStream::handleEntryBirthday() { Q_ASSERT(mXmlReader->isStartElement() && mXmlReader->qualifiedName() == "gContact:birthday"); QContactBirthday birthday; birthday.setDate(QDate::fromString(mXmlReader->attributes().value("when").toString(), Qt::ISODate)); if (birthday.dateTime().isValid()) { return birthday; } else { LOG_WARNING("Birthday date not supported:" << mXmlReader->attributes().value("when").toString()); return QContactDetail(); } } QContactDetail GoogleContactStream::handleEntryGender() { Q_ASSERT(mXmlReader->isStartElement() && mXmlReader->qualifiedName() == "gContact:gender"); QString genderStr = mXmlReader->attributes().value("value").toString().toLower(); QContactGender gender; if (genderStr.startsWith('m')) { gender.setGender(QContactGender::GenderMale); } else if (genderStr.startsWith('f')) { gender.setGender(QContactGender::GenderFemale); } else { gender.setGender(QContactGender::GenderUnspecified); } return gender; } QContactDetail GoogleContactStream::handleEntryHobby() { Q_ASSERT(mXmlReader->isStartElement() && mXmlReader->qualifiedName() == "gContact:hobby"); QContactHobby hobby; hobby.setHobby(mXmlReader->readElementText()); return hobby; } QContactDetail GoogleContactStream::handleEntryNickname() { Q_ASSERT(mXmlReader->isStartElement() && mXmlReader->qualifiedName() == "gContact:nickname"); QContactNickname nickname; nickname.setNickname(mXmlReader->readElementText()); return nickname; } QContactDetail GoogleContactStream::handleEntryOccupation() { Q_ASSERT(mXmlReader->isStartElement() && mXmlReader->qualifiedName() == "gContact:occupation"); QContactOrganization org; org.setRole(mXmlReader->readElementText()); return org; } QContactDetail GoogleContactStream::handleEntryWebsite() { Q_ASSERT(mXmlReader->isStartElement() && mXmlReader->qualifiedName() == "gContact:website"); QContactUrl url; QXmlStreamAttributes attributes = mXmlReader->attributes(); QString rel = attributes.hasAttribute("rel") ? attributes.value("rel").toString() : QString(); if (rel == "home-page") { url.setSubType(QContactUrl::SubTypeHomePage); } else if (rel == "blog") { url.setSubType(QContactUrl::SubTypeBlog); } else { url.setSubType(QContactUrl::SubTypeFavourite); } url.setContexts(handleContext(rel)); url.setUrl(attributes.value("href").toString()); return url; } QContactDetail GoogleContactStream::handleEntryJot() { Q_ASSERT(mXmlReader->isStartElement() && mXmlReader->qualifiedName() == "gContact:jot"); QString rel = mXmlReader->attributes().hasAttribute("rel") ? mXmlReader->attributes().value("rel").toString() : QString(); QContactNote note; note.setContexts(handleContext(rel)); note.setNote(mXmlReader->readElementText()); return note; } QContactDetail GoogleContactStream::handleEntryEmail() { Q_ASSERT(mXmlReader->isStartElement() && mXmlReader->qualifiedName() == "gd:email"); QContactEmailAddress email; email.setEmailAddress(mXmlReader->attributes().value("address").toString()); QString rel = mXmlReader->attributes().hasAttribute("rel") ? mXmlReader->attributes().value("rel").toString() : QString(); email.setContexts(handleContext(rel)); return email; } QContactDetail GoogleContactStream::handleEntryIm() { Q_ASSERT(mXmlReader->isStartElement () && mXmlReader->qualifiedName () == "gd:im"); static QMap protocolMap; if (protocolMap.isEmpty()) { protocolMap.insert("AIM", QContactOnlineAccount::ProtocolAim); protocolMap.insert("MSN", QContactOnlineAccount::ProtocolMsn); protocolMap.insert("YAHOO", QContactOnlineAccount::ProtocolYahoo); protocolMap.insert("SKYPE", QContactOnlineAccount::ProtocolSkype); protocolMap.insert("ICQ", QContactOnlineAccount::ProtocolIcq); protocolMap.insert("JABBER", QContactOnlineAccount::ProtocolJabber); protocolMap.insert("QQ", QContactOnlineAccount::ProtocolQq); protocolMap.insert("IRC", QContactOnlineAccount::ProtocolIrc); } QString rel, protocol; if (mXmlReader->attributes().hasAttribute("rel")) { rel = mXmlReader->attributes().value("rel").toString(); } if (mXmlReader->attributes().hasAttribute ("protocol")) { QString protocolUrl = mXmlReader->attributes().value("protocol").toString(); protocol = protocolUrl.split("#").last(); } QContactOnlineAccount imAccount; imAccount.setAccountUri(mXmlReader->attributes().value("address").toString()); imAccount.setProtocol(protocolMap.value(protocol, QContactOnlineAccount::ProtocolUnknown)); if (!protocolMap.contains(protocol)) { imAccount.setServiceProvider(protocol); } imAccount.setContexts(handleContext(rel)); return imAccount; } QContactDetail GoogleContactStream::handleEntryName() { Q_ASSERT(mXmlReader->isStartElement() && mXmlReader->qualifiedName() == "gd:name"); QContactName name; while (!(mXmlReader->tokenType() == QXmlStreamReader::EndElement && mXmlReader->qualifiedName() == "gd:name")) { if (mXmlReader->tokenType() == QXmlStreamReader::StartElement) { if (mXmlReader->qualifiedName() == "gd:givenName") { name.setFirstName(mXmlReader->readElementText()); } else if (mXmlReader->qualifiedName() == "gd:additionalName") { name.setMiddleName(mXmlReader->readElementText()); } else if (mXmlReader->qualifiedName() == "gd:familyName") { name.setLastName(mXmlReader->readElementText()); } else if (mXmlReader->qualifiedName() == "gd:namePrefix") { name.setPrefix(mXmlReader->readElementText()); } else if (mXmlReader->qualifiedName() == "gd:nameSuffix") { name.setSuffix(mXmlReader->readElementText()); } } mXmlReader->readNextStartElement(); } return name; } QContactDetail GoogleContactStream::handleEntryOrganization() { Q_ASSERT(mXmlReader->isStartElement() && mXmlReader->qualifiedName() == "gd:organization"); QContactOrganization org; QString rel = mXmlReader->attributes().hasAttribute("rel") ? mXmlReader->attributes().value("rel").toString() : QString(); org.setContexts(handleContext(rel)); while (!(mXmlReader->tokenType() == QXmlStreamReader::EndElement && mXmlReader->qualifiedName() == "gd:organization")) { if (mXmlReader->tokenType() == QXmlStreamReader::StartElement) { if (mXmlReader->qualifiedName() == "gd:orgDepartment") { QStringList dept = org.department(); dept.append(mXmlReader->readElementText()); org.setDepartment(dept); } else if (mXmlReader->qualifiedName() == "gd:orgJobDescription") { org.setRole(mXmlReader->readElementText()); } else if (mXmlReader->qualifiedName() == "gd:orgName") { org.setName(mXmlReader->readElementText()); } else if (mXmlReader->qualifiedName() == "gd:orgSymbol") { org.setLogoUrl(mXmlReader->readElementText()); } else if (mXmlReader->qualifiedName() == "gd:orgTitle") { org.setTitle(mXmlReader->readElementText()); } } mXmlReader->readNextStartElement(); } return org; } QContactDetail GoogleContactStream::handleEntryPhoneNumber() { Q_ASSERT(mXmlReader->isStartElement() && mXmlReader->qualifiedName() == "gd:phoneNumber"); QContactPhoneNumber phone; QString rel = mXmlReader->attributes().hasAttribute("rel") ? mXmlReader->attributes().value("rel").toString() : QString(); if (rel == QStringLiteral("http://schemas.google.com/g/2005#home")) { phone.setSubTypes(QList() << QContactPhoneNumber::SubTypeVoice); phone.setContexts(QContactDetail::ContextHome); } else if (rel == QStringLiteral("http://schemas.google.com/g/2005#work")) { phone.setSubTypes(QList() << QContactPhoneNumber::SubTypeVoice); phone.setContexts(QContactDetail::ContextWork); } else if (rel == QStringLiteral("http://schemas.google.com/g/2005#mobile")) { phone.setSubTypes(QList() << QContactPhoneNumber::SubTypeMobile); } else if (rel == QStringLiteral("http://schemas.google.com/g/2005#work_mobile")) { phone.setSubTypes(QList() << QContactPhoneNumber::SubTypeMobile); phone.setContexts(QContactDetail::ContextWork); } else if (rel == QStringLiteral("http://schemas.google.com/g/2005#home_fax")) { phone.setSubTypes(QList() << QContactPhoneNumber::SubTypeFax); phone.setContexts(QContactDetail::ContextHome); } else if (rel == QStringLiteral("http://schemas.google.com/g/2005#work_fax")) { phone.setSubTypes(QList() << QContactPhoneNumber::SubTypeFax); phone.setContexts(QContactDetail::ContextWork); } else if (rel == QStringLiteral("http://schemas.google.com/g/2005#other_fax")) { phone.setSubTypes(QList() << QContactPhoneNumber::SubTypeFax); phone.setContexts(QContactDetail::ContextOther); } else if (rel == QStringLiteral("http://schemas.google.com/g/2005#pager")) { phone.setSubTypes(QList() << QContactPhoneNumber::SubTypePager); phone.setContexts(QContactDetail::ContextHome); } else if (rel == QStringLiteral("http://schemas.google.com/g/2005#work_pager")) { phone.setSubTypes(QList() << QContactPhoneNumber::SubTypePager); phone.setContexts(QContactDetail::ContextWork); } else if (rel == QStringLiteral("http://schemas.google.com/g/2005#tty_tdd")) { phone.setSubTypes(QList() << QContactPhoneNumber::SubTypeModem); } else if (rel == QStringLiteral("http://schemas.google.com/g/2005#car")) { phone.setSubTypes(QList() << QContactPhoneNumber::SubTypeCar); } else if (rel == QStringLiteral("http://schemas.google.com/g/2005#telex")) { phone.setSubTypes(QList() << QContactPhoneNumber::SubTypeBulletinBoardSystem); } else if (rel == QStringLiteral("http://schemas.google.com/g/2005#assistant")) { phone.setSubTypes(QList() << QContactPhoneNumber::SubTypeAssistant); } else if (rel == QStringLiteral("http://schemas.google.com/g/2005#other")) { phone.setSubTypes(QList() << QContactPhoneNumber::SubTypeVoice); phone.setContexts(QContactDetail::ContextOther); } else { // else ignore it, malformed output from Google. LOG_WARNING("Malformated phone tag: " << rel); phone.setSubTypes(QList() << QContactPhoneNumber::SubTypeVoice); phone.setContexts(QContactDetail::ContextOther); } phone.setNumber(mXmlReader->readElementText()); return phone; } QContactDetail GoogleContactStream::handleEntryStructuredPostalAddress() { Q_ASSERT(mXmlReader->isStartElement() && mXmlReader->qualifiedName() == "gd:structuredPostalAddress"); QContactAddress address; QString rel = mXmlReader->attributes().hasAttribute("rel") ? mXmlReader->attributes().value("rel").toString() : QString(); address.setContexts(handleContext(rel)); while (!(mXmlReader->tokenType() == QXmlStreamReader::EndElement && mXmlReader->qualifiedName() == "gd:structuredPostalAddress")) { if (mXmlReader->tokenType() == QXmlStreamReader::StartElement) { if (mXmlReader->qualifiedName() == "gd:street") { address.setStreet(mXmlReader->readElementText()); } else if (mXmlReader->qualifiedName() == "gd:pobox") { address.setPostOfficeBox(mXmlReader->readElementText()); } else if (mXmlReader->qualifiedName() == "gd:neighborhood") { address.setLocality(mXmlReader->readElementText()); } else if (mXmlReader->qualifiedName() == "gd:city") { address.setLocality(mXmlReader->readElementText()); } else if (mXmlReader->qualifiedName() == "gd:region") { address.setRegion(mXmlReader->readElementText()); } else if (mXmlReader->qualifiedName() == "gd:postcode") { address.setPostcode(mXmlReader->readElementText()); } else if (mXmlReader->qualifiedName() == "gd:country") { address.setCountry(mXmlReader->readElementText()); } } mXmlReader->readNextStartElement(); } return address; } QContactDetail GoogleContactStream::handleEntryUpdated() { Q_ASSERT(mXmlReader->isStartElement() && (mXmlReader->qualifiedName() == "updated" || mXmlReader->qualifiedName() == "app:edited")); QDateTime modTs = QDateTime::fromString(mXmlReader->readElementText(), Qt::ISODate); if (modTs.isValid()) { QContactTimestamp ts; ts.setLastModified(modTs); return ts; } return QContactDetail(); } QContactDetail GoogleContactStream::handleEntryGroup() { Q_ASSERT(mXmlReader->isStartElement() && mXmlReader->qualifiedName() == "gContact:groupMembershipInfo"); QXmlStreamAttributes attrs = mXmlReader->attributes(); QString groupId = attrs.value("href").toString().split('/').last(); QContactExtendedDetail membershipDetail; membershipDetail.setName(UContactsCustomDetail::FieldGroupMembershipInfo); membershipDetail.setData(groupId); return membershipDetail; } // ---------------------------------------- void GoogleContactStream::encodeContactUpdate(const QContact &qContact, const QStringList &unsupportedElements, const GoogleContactStream::UpdateType updateType, const bool batch) { QList allDetails = qContact.details (); mXmlWriter->writeStartElement("atom:entry"); if (batch == true) { // Etag encoding has to immediately succeed writeStartElement("atom:entry"), // since etag is an attribute of this element. encodeEtag(qContact, updateType == GoogleContactStream::Remove || updateType == GoogleContactStream::Modify); encodeBatchTag(updateType, qContact.id().toString()); } else { mXmlWriter->writeAttribute("xmlns:atom", "http://www.w3.org/2005/Atom"); mXmlWriter->writeAttribute("xmlns:gd", "http://schemas.google.com/g/2005"); mXmlWriter->writeAttribute("xmlns:gContact", "http://schemas.google.com/contact/2008"); } if (updateType == GoogleContactStream::Remove) { encodeId(qContact, true); mXmlWriter->writeEndElement(); return; } encodeCategory(); if (updateType == GoogleContactStream::Modify) { encodeId(qContact, true); encodeUpdatedTimestamp(qContact); } encodeUnknownElements(unsupportedElements); // for an Add, this is just group membership. bool hasGroup = false; Q_FOREACH (const QContactDetail &detail, allDetails) { switch(detail.type()) { case QContactDetail::TypeName: { encodeName(detail); } break; case QContactDetail::TypePhoneNumber: { encodePhoneNumber(detail); } break; case QContactDetail::TypeEmailAddress: { encodeEmailAddress(detail); } break; case QContactDetail::TypeAddress: { encodeAddress(detail); } break; case QContactDetail::TypeUrl: { encodeUrl(detail); } break; case QContactDetail::TypeBirthday: { encodeBirthday(detail); } break; case QContactDetail::TypeNote: { encodeNote(detail); } break; case QContactDetail::TypeHobby: { encodeHobby(detail); } break; case QContactDetail::TypeOrganization: { encodeOrganization(detail); } break; case QContactDetail::TypeAvatar: { encodeAvatar(detail, qContact); } break; case QContactDetail::TypeAnniversary: { encodeAnniversary(detail); } break; case QContactDetail::TypeNickname: { encodeNickname(detail); } break; case QContactDetail::TypeGender: { encodeGender(detail); } break; case QContactDetail::TypeOnlineAccount: { encodeOnlineAccount(detail); } break; case QContactDetail::TypeFamily: { encodeFamily(detail); } break; case QContactDetail::TypeFavorite: { encodeFavorite(detail); } break; case QContactDetail::TypeExtendedDetail: { encodeExtendedProperty(detail, &hasGroup); } break; case QContactDetail::TypeRingtone: { encodeRingTone(detail); } break; // TODO: handle the custom detail fields. default: { } break; } } if (!hasGroup) { // Make sure that the contact has at least one group QContactExtendedDetail group; group.setName(UContactsCustomDetail::FieldGroupMembershipInfo); group.setData(GConfig::GROUP_MY_CONTACTS_ID); encodeGroup(group); } mXmlWriter->writeEndElement(); } void GoogleContactStream::startBatchFeed() { mXmlWriter->writeStartElement("atom:feed"); mXmlWriter->writeAttribute("xmlns:atom", "http://www.w3.org/2005/Atom"); mXmlWriter->writeAttribute("xmlns:gContact", "http://schemas.google.com/contact/2008"); mXmlWriter->writeAttribute("xmlns:gd", "http://schemas.google.com/g/2005"); mXmlWriter->writeAttribute("xmlns:batch", "http://schemas.google.com/gdata/batch"); } void GoogleContactStream::endBatchFeed() { mXmlWriter->writeEndElement (); } void GoogleContactStream::encodeBatchTag(const GoogleContactStream::UpdateType type, const QString &batchElementId) { mXmlWriter->writeTextElement("batch:id", batchElementId); if (type == GoogleContactStream::Add) { mXmlWriter->writeEmptyElement("batch:operation"); mXmlWriter->writeAttribute("type", "insert"); } else if (type == GoogleContactStream::Modify) { mXmlWriter->writeEmptyElement("batch:operation"); mXmlWriter->writeAttribute("type", "update"); } else if (type == GoogleContactStream::Remove) { mXmlWriter->writeEmptyElement("batch:operation"); mXmlWriter->writeAttribute("type", "delete"); } } void GoogleContactStream::encodeCategory() { mXmlWriter->writeEmptyElement("atom:category"); mXmlWriter->writeAttribute("schema", "http://schemas.google.com/g/2005#kind"); mXmlWriter->writeAttribute("term", "http://schemas.google.com/contact/2008#contact"); } void GoogleContactStream::encodeId(const QContact &qContact, bool isUpdate) { QString remoteId = UContactsCustomDetail::getCustomField(qContact, UContactsCustomDetail::FieldRemoteId).data().toString(); if (!remoteId.isEmpty()) { remoteId = remoteId.mid(remoteId.indexOf(":")+1); if (isUpdate) { // according to the docs, this should be "base" instead of "full" -- but that actually fails. if (mAccountEmail.isEmpty()) { LOG_WARNING("account email not known - unable to build batch edit id!"); } else { mXmlWriter->writeTextElement("atom:id", "http://www.google.com/m8/feeds/contacts/" + mAccountEmail + "/full/" + remoteId); } } else { mXmlWriter->writeTextElement("atom:id", "http://www.google.com/m8/feeds/contacts/default/full/" + remoteId); } } } void GoogleContactStream::encodeUpdatedTimestamp(const QContact &qContact) { QContactTimestamp timestamp = qContact.detail(); QDateTime updatedTimestamp = timestamp.lastModified(); // will be UTC from database. if (!updatedTimestamp.isValid()) { updatedTimestamp = QDateTime::currentDateTimeUtc(); } QString updatedStr = updatedTimestamp.toString(QStringLiteral("yyyy-MM-ddThh:mm:ss.zzzZ")); mXmlWriter->writeTextElement("updated", updatedStr); } void GoogleContactStream::encodeEtag(const QContact &qContact, bool needed) { QContactExtendedDetail etagDetail = UContactsCustomDetail::getCustomField(qContact, UContactsCustomDetail::FieldContactETag); QString etag = etagDetail.data().toString(); if (!etag.isEmpty()) { mXmlWriter->writeAttribute("gd:etag", etag); } else if (needed) { // we're trying to delete a contact in a batch operation // but we don't know the etag of the deleted contact. LOG_WARNING("etag needed but not available! caller needs to prefill for deletion updates!"); } } void GoogleContactStream::encodeName(const QContactName &name) { mXmlWriter->writeStartElement("gd:name"); if (!name.firstName().isEmpty()) mXmlWriter->writeTextElement("gd:givenName", name.firstName()); if (!name.middleName().isEmpty()) mXmlWriter->writeTextElement("gd:additionalName", name.middleName()); if (!name.lastName().isEmpty()) mXmlWriter->writeTextElement("gd:familyName", name.lastName()); if (!name.prefix().isEmpty()) mXmlWriter->writeTextElement("gd:namePrefix", name.prefix()); if (!name.suffix().isEmpty()) mXmlWriter->writeTextElement("gd:nameSuffix", name.suffix()); mXmlWriter->writeEndElement(); } void GoogleContactStream::encodePhoneNumber(const QContactPhoneNumber &phoneNumber) { if (phoneNumber.number().isEmpty()) { return; } bool isHome = phoneNumber.contexts().contains(QContactDetail::ContextHome); bool isWork = phoneNumber.contexts().contains(QContactDetail::ContextWork); int subType = phoneNumber.subTypes().isEmpty() ? QContactPhoneNumber::SubTypeMobile // default to mobile : phoneNumber.subTypes().first(); QString rel = "http://schemas.google.com/g/2005#"; switch (subType) { case QContactPhoneNumber::SubTypeVoice: case QContactPhoneNumber::SubTypeLandline: { if (isHome) { rel += QString::fromLatin1("home"); } else if (isWork) { rel += QString::fromLatin1("work"); } else { rel += QString::fromLatin1("other"); } } break; case QContactPhoneNumber::SubTypeMobile: { if (isHome) { rel += QString::fromLatin1("mobile"); } else if (isWork) { rel += QString::fromLatin1("work_mobile"); } else { rel += QString::fromLatin1("mobile"); // we lose the non-homeness in roundtrip. } } break; case QContactPhoneNumber::SubTypeFax: { if (isHome) { rel += QString::fromLatin1("home_fax"); } else if (isWork) { rel += QString::fromLatin1("work_fax"); } else { rel += QString::fromLatin1("other_fax"); } } break; case QContactPhoneNumber::SubTypePager: { if (isHome) { rel += QString::fromLatin1("pager"); } else if (isWork) { rel += QString::fromLatin1("work_pager"); } else { rel += QString::fromLatin1("pager"); // we lose the non-homeness in roundtrip. } } break; case QContactPhoneNumber::SubTypeModem: { rel += QString::fromLatin1("tty_tdd"); // we lose context in roundtrip. } break; case QContactPhoneNumber::SubTypeCar: { rel += QString::fromLatin1("car"); // we lose context in roundtrip. } break; case QContactPhoneNumber::SubTypeBulletinBoardSystem: { rel += QString::fromLatin1("telex"); // we lose context in roundtrip. } break; case QContactPhoneNumber::SubTypeAssistant: { rel += QString::fromLatin1("assistant"); } break; default: { rel += QString::fromLatin1("other"); } break; } mXmlWriter->writeStartElement("gd:phoneNumber"); mXmlWriter->writeAttribute("rel", rel); mXmlWriter->writeCharacters(phoneNumber.number()); mXmlWriter->writeEndElement(); } void GoogleContactStream::encodeEmailAddress(const QContactEmailAddress &emailAddress) { if (!emailAddress.emailAddress().isEmpty()) { mXmlWriter->writeEmptyElement("gd:email"); if (emailAddress.contexts().contains(QContactDetail::ContextHome)) { mXmlWriter->writeAttribute("rel", "http://schemas.google.com/g/2005#home"); } else if (emailAddress.contexts().contains(QContactDetail::ContextWork)) { mXmlWriter->writeAttribute("rel", "http://schemas.google.com/g/2005#work"); } else { mXmlWriter->writeAttribute("rel", "http://schemas.google.com/g/2005#other"); } mXmlWriter->writeAttribute("address", emailAddress.emailAddress()); } } void GoogleContactStream::encodeAddress(const QContactAddress &address) { mXmlWriter->writeStartElement("gd:structuredPostalAddress"); // https://developers.google.com/google-apps/contacts/v3/reference#structuredPostalAddressRestrictions // we cannot use mailClass attribute (for postal/parcel etc) mXmlWriter->writeAttribute("rel", QString("http://schemas.google.com/g/2005#%1").arg(encodeContext(address.contexts()))); if (!address.street().isEmpty()) mXmlWriter->writeTextElement("gd:street", address.street()); if (!address.locality().isEmpty()) mXmlWriter->writeTextElement("gd:neighborhood", address.locality()); if (!address.postOfficeBox().isEmpty()) mXmlWriter->writeTextElement("gd:pobox", address.postOfficeBox()); if (!address.region().isEmpty()) mXmlWriter->writeTextElement("gd:region", address.region()); if (!address.postcode().isEmpty()) mXmlWriter->writeTextElement("gd:postcode", address.postcode()); if (!address.country().isEmpty()) mXmlWriter->writeTextElement("gd:country", address.country()); mXmlWriter->writeEndElement(); } void GoogleContactStream::encodeUrl(const QContactUrl &url) { if (!url.url().isEmpty()) { mXmlWriter->writeEmptyElement("gContact:website"); switch (url.subType()) { case QContactUrl::SubTypeHomePage: { mXmlWriter->writeAttribute("rel", "home-page"); } break; case QContactUrl::SubTypeBlog: { mXmlWriter->writeAttribute("rel", "blog"); } break; default: { mXmlWriter->writeAttribute("rel", encodeContext(url.contexts())); } break; } mXmlWriter->writeAttribute("href", url.url()); } } void GoogleContactStream::encodeBirthday(const QContactBirthday &birthday) { if (birthday.date().isValid()) { mXmlWriter->writeEmptyElement("gContact:birthday"); mXmlWriter->writeAttribute("when", birthday.date().toString(Qt::ISODate)); } } void GoogleContactStream::encodeNote(const QContactNote ¬e) { if (!note.note().isEmpty()) { mXmlWriter->writeStartElement("gContact:jot"); mXmlWriter->writeAttribute("rel", encodeContext(note.contexts())); mXmlWriter->writeCharacters(note.note()); mXmlWriter->writeEndElement(); } } void GoogleContactStream::encodeHobby(const QContactHobby &hobby) { if (!hobby.hobby().isEmpty()) { mXmlWriter->writeTextElement ("gContact:hobby", hobby.hobby()); } } void GoogleContactStream::encodeGeoLocation(const QContactGeoLocation &geolocation) { Q_UNUSED(geolocation); LOG_DEBUG("skipping geolocation"); } void GoogleContactStream::encodeOrganization(const QContactOrganization &organization) { mXmlWriter->writeStartElement("gd:organization"); mXmlWriter->writeAttribute("rel", "http://schemas.google.com/g/2005#" + encodeContext(organization.contexts())); if (!organization.title().isEmpty()) mXmlWriter->writeTextElement("gd:orgTitle", organization.title()); if (!organization.name().isEmpty()) mXmlWriter->writeTextElement("gd:orgName", organization.name()); if (!organization.department().isEmpty()) mXmlWriter->writeTextElement("gd:orgDepartment", organization.department().join(',')); if (!organization.role().isEmpty()) mXmlWriter->writeTextElement("gd:orgJobDescription", organization.role()); mXmlWriter->writeEndElement(); } void GoogleContactStream::encodeAvatar(const QContactAvatar &avatar, const QContact &qContact) { // XXX TODO: determine if it's a new local avatar, if so, push it up. QUrl imageUrl(avatar.imageUrl()); if (imageUrl.isLocalFile()) { LOG_DEBUG("have avatar:" << imageUrl << "but not upsyncing avatars"); mEncodedContactsWithAvatars << qContact.id(); } } void GoogleContactStream::encodeGender(const QContactGender &gender) { mXmlWriter->writeEmptyElement ("gContact:gender"); mXmlWriter->writeAttribute ("value", gender.gender() == QContactGender::GenderMale ? "male" : "female"); } void GoogleContactStream::encodeNickname(const QContactNickname &nickname) { if (!nickname.nickname().isEmpty()) { mXmlWriter->writeTextElement("gContact:nickname", nickname.nickname()); } } void GoogleContactStream::encodeAnniversary(const QContactAnniversary &anniversary) { static QMap anniversaryTypes; if (anniversaryTypes.isEmpty()) { anniversaryTypes.insert(QContactAnniversary::SubTypeEngagement, QString::fromLatin1("engagement")); anniversaryTypes.insert(QContactAnniversary::SubTypeEmployment, QString::fromLatin1("employment")); anniversaryTypes.insert(QContactAnniversary::SubTypeMemorial, QString::fromLatin1("memorial")); anniversaryTypes.insert(QContactAnniversary::SubTypeHouse, QString::fromLatin1("house")); anniversaryTypes.insert(QContactAnniversary::SubTypeWedding, QString::fromLatin1("wedding")); } if (!anniversary.event().isEmpty() && !anniversary.originalDate().isNull()) { mXmlWriter->writeStartElement ("gContact:event"); mXmlWriter->writeAttribute("rel", "anniversary"); mXmlWriter->writeAttribute("label", anniversaryTypes.value(anniversary.subType(), QString::fromLatin1("wedding"))); mXmlWriter->writeEmptyElement("gd:when"); mXmlWriter->writeAttribute("startTime", anniversary.originalDateTime().toString(Qt::ISODate)); mXmlWriter->writeAttribute("valueString", anniversary.event()); mXmlWriter->writeEndElement(); } } void GoogleContactStream::encodeOnlineAccount(const QContactOnlineAccount &onlineAccount) { static QMap protocolMap; if (onlineAccount.accountUri().isEmpty()) { return; } if (protocolMap.isEmpty()) { protocolMap.insert(QContactOnlineAccount::ProtocolJabber, "JABBER"); protocolMap.insert(QContactOnlineAccount::ProtocolAim, "AIM"); protocolMap.insert(QContactOnlineAccount::ProtocolIcq, "ICQ"); protocolMap.insert(QContactOnlineAccount::ProtocolMsn, "MSN"); protocolMap.insert(QContactOnlineAccount::ProtocolQq, "QQ"); protocolMap.insert(QContactOnlineAccount::ProtocolYahoo, "YAHOO"); protocolMap.insert(QContactOnlineAccount::ProtocolSkype, "SKYPE"); protocolMap.insert(QContactOnlineAccount::ProtocolIrc, "IRC"); } QString protocolName = protocolMap.value(onlineAccount.protocol(), onlineAccount.serviceProvider()); if (protocolName.isEmpty()) { LOG_WARNING("Fail to parse online account protcol:" + onlineAccount.accountUri()); return; } QString context = encodeContext(onlineAccount.contexts()); mXmlWriter->writeEmptyElement("gd:im"); mXmlWriter->writeAttribute("protocol", "http://schemas.google.com/g/2005#" + protocolName); mXmlWriter->writeAttribute("rel", "http://schemas.google.com/g/2005#" + context); mXmlWriter->writeAttribute("address", onlineAccount.accountUri()); } void GoogleContactStream::encodeFamily(const QContactFamily &family) { if (family.spouse().length() > 0) { mXmlWriter->writeStartElement("gContact:relation"); mXmlWriter->writeAttribute("rel", "spouse"); mXmlWriter->writeCharacters(family.spouse()); mXmlWriter->writeEndElement(); } Q_FOREACH (const QString member, family.children()) { mXmlWriter->writeStartElement("gContact:relation"); mXmlWriter->writeAttribute("rel", "child"); mXmlWriter->writeCharacters(member); mXmlWriter->writeEndElement(); } } void GoogleContactStream::encodeGroup(const QContactExtendedDetail &group) { static const QString groupHref = QStringLiteral("http://www.google.com/m8/feeds/groups/%1/base/%2"); QString groupId = group.data().toString(); QString accountName = mAccountEmail.isEmpty() ? QStringLiteral("default") : mAccountEmail; if (groupId.isEmpty()) { groupId = GConfig::GROUP_MY_CONTACTS_ID; } mXmlWriter->writeEmptyElement("gContact:groupMembershipInfo"); mXmlWriter->writeAttribute("deleted", "false"); mXmlWriter->writeAttribute("href", QString(groupHref).arg(accountName).arg(groupId)); } void GoogleContactStream::encodeFavorite(const QContactFavorite &favorite) { QContactExtendedDetail det; det.setName("X-FAVORITE"); det.setData(favorite.isFavorite() ? "true" : "false"); encodeExtendedProperty(det, 0); } void GoogleContactStream::encodeExtendedProperty(const QContactExtendedDetail &detail, bool *isGroup) { static QStringList blackList; QString detailName = detail.value(QContactExtendedDetail::FieldName).toString(); // Avoid duplicate info in the remote side // These fields are used locally to store remote info if (blackList.isEmpty()) { blackList << "X-REMOTE-ID" << "X-AVATAR-REV" << "X-GOOGLE-ETAG" << "X-GROUP-ID" << "X-CREATED-AT" << "X-NORMALIZED_FN"; } if (detailName == UContactsCustomDetail::FieldGroupMembershipInfo) { encodeGroup(detail); if (isGroup) { *isGroup |= true; } } else if (!detailName.isEmpty() && !blackList.contains(detailName)) { mXmlWriter->writeEmptyElement("gd:extendedProperty"); mXmlWriter->writeAttribute("name", detail.name()); mXmlWriter->writeAttribute("value", detail.data().toString()); } } void GoogleContactStream::encodeRingTone(const QContactRingtone &ringTone) { if (!ringTone.audioRingtoneUrl().isEmpty()) { QContactExtendedDetail det; det.setName("SOUND"); det.setData(ringTone.audioRingtoneUrl()); encodeExtendedProperty(det, 0); } } void GoogleContactStream::encodeUnknownElements(const QStringList &unknownElements) { // ugly hack to get the QXmlStreamWriter to write a pre-formatted element... foreach (const QString &unknownElement, unknownElements) { QString concat; concat.append(""); concat.append(""); concat.append(unknownElement); concat.append(""); QXmlStreamReader tokenizer(concat); tokenizer.readNextStartElement(); // read past the xml document element start. QString text = tokenizer.readElementText(); mXmlWriter->writeStartElement(tokenizer.qualifiedName().toString()); mXmlWriter->writeAttributes(tokenizer.attributes()); if (!text.isEmpty()) { mXmlWriter->writeCharacters(text); } mXmlWriter->writeEndElement(); } } QString GoogleContactStream::encodeContext(const QList context) const { // TOOD: check if a list of context is necessary switch(context.value(0, QContactDetail::ContextOther)) { case QContactDetail::ContextHome: return "home"; case QContactDetail::ContextWork: return "work"; case QContactDetail::ContextOther: default: return "other"; } } buteo-sync-plugins-contacts-0.1+16.04.20151211/google/GRemoteSource.cpp0000644000015300001610000006544412632560105026003 0ustar pbuserpbgroup00000000000000/* * This file is part of buteo-sync-plugins-contacts package * * Copyright (C) 2013 Jolla Ltd. and/or its subsidiary(-ies). * 2015 Canonical Ltd * * Contributors: Sateesh Kavuri * Mani Chandrasekar * Renato Araujo Oliveira Filho * * 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 "GRemoteSource.h" #include "GTransport.h" #include "GConfig.h" #include "GContactStream.h" #include "GContactImageDownloader.h" #include "GContactImageUploader.h" #include "buteosyncfw_p.h" #include #include #include #include #include #include #include QTCONTACTS_USE_NAMESPACE GRemoteSource::GRemoteSource(QObject *parent) : UAbstractRemoteSource(parent), mTransport(new GTransport), mState(GRemoteSource::STATE_IDLE), mStartIndex(0), mFetchAvatars(true) { connect(mTransport.data(), SIGNAL(finishedRequest()), SLOT(networkRequestFinished())); connect(mTransport.data(), SIGNAL(error(int)), SLOT(networkError(int))); } GRemoteSource::~GRemoteSource() { } bool GRemoteSource::init(const QVariantMap &properties) { FUNCTION_CALL_TRACE; LOG_DEBUG("Creating HTTP transport"); if (mState != GRemoteSource::STATE_IDLE) { LOG_WARNING("Init called with wrong state" << mState); return false; } mAccountName = properties.value("ACCOUNT-NAME").toString(); mAuthToken = properties.value("AUTH-TOKEN").toString(); mSyncTarget = properties.value("SYNC-TARGET").toString(); mRemoteUri = properties.value(Buteo::KEY_REMOTE_DATABASE).toString(); mStartIndex = 1; mPendingBatchOps.clear(); mState = GRemoteSource::STATE_IDLE; if (mRemoteUri.isEmpty()) { // Set to the default value mRemoteUri = QStringLiteral("https://www.google.com/m8/feeds/contacts/default/full/"); } LOG_DEBUG("Setting remote URI to" << mRemoteUri); mTransport->setUrl(mRemoteUri); QString proxyHost = properties.value(Buteo::KEY_HTTP_PROXY_HOST).toString(); // Set proxy, if available if (!proxyHost.isEmpty()) { QString proxyPort = properties.value(Buteo::KEY_HTTP_PROXY_PORT).toString(); mTransport->setProxy(proxyHost, proxyPort); LOG_DEBUG("Proxy host:" << proxyHost); LOG_DEBUG("Proxy port:" << proxyPort); } else { LOG_DEBUG("Not using proxy"); } return true; } void GRemoteSource::abort() { disconnect(mTransport.data()); mState = STATE_ABORTED; } void GRemoteSource::fetchContacts(const QDateTime &since, bool includeDeleted, bool fetchAvatar) { FUNCTION_CALL_TRACE; if (mState != GRemoteSource::STATE_IDLE) { LOG_WARNING("GRemote source is not in idle state, current state is" << mState); return; } mFetchAvatars = fetchAvatar; mState = GRemoteSource::STATE_FETCHING_CONTACTS; fetchRemoteContacts(since, includeDeleted, 1); } void GRemoteSource::fetchAvatars(QList *contacts) { // keep downloader object live while GRemoteSource exists to avoid removing // the temporary files used to store avatars. // The files will be removed when the object get destroyed GContactImageDownloader *downloader = new GContactImageDownloader(mAuthToken, this); QMap > avatars; for(int i=0; i < contacts->size(); i++) { QContact &c = (*contacts)[i]; foreach (const QContactAvatar &avatar, c.details()) { if (!avatar.imageUrl().isLocalFile()) { LOG_DEBUG("Download avatar:" << avatar.imageUrl()); avatars.insert(avatar.imageUrl(), qMakePair(avatar, &c)); downloader->push(avatar.imageUrl()); } } } downloader->exec(); QMap downloaded = downloader->donwloaded(); foreach (const QUrl &avatarUrl, avatars.keys()) { QPair &p = avatars[avatarUrl]; LOG_DEBUG("Replace avatar image:" << p.first.imageUrl() << downloaded.value(avatarUrl)); p.first.setImageUrl(downloaded.value(avatarUrl)); p.second->saveDetail(&p.first); } } void GRemoteSource::uploadAvatars(QList *contacts) { GContactImageUploader uploader(mAuthToken, mAccountName); foreach(const QContact &c, *contacts) { QString localId = UContactsBackend::getLocalId(c); LOG_DEBUG("Will upload avatar for:" << localId); if (mLocalIdToAvatar.contains(localId)) { QContactExtendedDetail rEtag = UContactsCustomDetail::getCustomField(c, UContactsCustomDetail::FieldContactAvatarETag); QPair avatar = mLocalIdToAvatar.value(localId); LOG_DEBUG("Current avatar:" << "\n\tlocal-etag:" << avatar.first << "\n\tremote-etag:" << rEtag.data().toString() << "\n\tlocal-url:" << avatar.second); // check if the remote etag has changed if (avatar.second.isLocalFile() && (avatar.first.isEmpty() || (avatar.first != rEtag.data().toString()))) { QString remoteId = UContactsBackend::getRemoteId(c); LOG_DEBUG("Avatar revision changed:" << "\n\tRemote version:" << rEtag.data().toString() << "\n\tLocal version:" << avatar.first); LOG_DEBUG("Uploade avatar:" << remoteId << avatar.second); uploader.push(remoteId, avatar.second); } else if (!avatar.second.isLocalFile()) { LOG_DEBUG("Contact avatar is not local" << avatar.second); } else { LOG_DEBUG("Avatar did not change"); } } else { LOG_WARNING("Local id not found found on avatar map:" << localId); } } uploader.exec(); QMap result = uploader.result(); for(int i =0; i < contacts->size(); i++) { QContact &c = (*contacts)[i]; GContactImageUploader::UploaderReply reply = result.value(UContactsBackend::getRemoteId(c)); // update contact e-tag if necessary if (!reply.newEtag.isEmpty()) { UContactsCustomDetail::setCustomField(c, UContactsCustomDetail::FieldContactETag, reply.newEtag); } // update avatar e-tag if necessary if (!reply.newAvatarEtag.isEmpty()) { UContactsCustomDetail::setCustomField(c, UContactsCustomDetail::FieldContactAvatarETag, reply.newAvatarEtag); } QString localId = UContactsBackend::getLocalId(c); // copy local url to new remote avatar QContactAvatar avatar = c.detail(); avatar.setImageUrl(mLocalIdToAvatar.value(localId).second); c.saveDetail(&avatar); } } void GRemoteSource::saveContactsNonBatch(const QList contacts) { FUNCTION_CALL_TRACE; if (mState != GRemoteSource::STATE_IDLE) { LOG_WARNING("GRemote source is not in idle state, current state is" << mState); return; } mState = GRemoteSource::STATE_BATCH_RUNNING; foreach (const QContact &contact, contacts) { QString remoteId = UContactsBackend::getRemoteId(contact); if (remoteId.isEmpty()) { mPendingBatchOps.insertMulti(GoogleContactStream::Add, qMakePair(contact, QStringList())); } else { mPendingBatchOps.insertMulti(GoogleContactStream::Modify, qMakePair(contact, QStringList())); } } batchOperationContinue(); } void GRemoteSource::removeContactsNonBatch(const QList contacts) { FUNCTION_CALL_TRACE; if (mState != GRemoteSource::STATE_IDLE) { LOG_WARNING("GRemote source is not in idle state, current state is" << mState); return; } mState = GRemoteSource::STATE_BATCH_RUNNING; foreach (const QContact &contact, contacts) { mPendingBatchOps.insertMulti(GoogleContactStream::Remove, qMakePair(contact, QStringList())); } batchOperationContinue(); } void GRemoteSource::batch(const QList &contactsToCreate, const QList &contactsToUpdate, const QList &contactsToRemove) { FUNCTION_CALL_TRACE; if (mState != GRemoteSource::STATE_IDLE) { LOG_WARNING("GRemote source is not in idle state, current state is" << mState); emit transactionCommited(QList(), QList(), QStringList(), QMap(), Sync::SYNC_ERROR); return; } mLocalIdToAvatar.clear(); mState = GRemoteSource::STATE_BATCH_RUNNING; foreach (const QContact &contact, contactsToCreate) { QString localID = UContactsBackend::getLocalId(contact); QString avatarEtag = UContactsCustomDetail::getCustomField(contact, UContactsCustomDetail::FieldContactAvatarETag).data().toString(); mLocalIdToAvatar.insert(QString("qtcontacts:galera::%1").arg(localID), qMakePair(avatarEtag, contact.detail().imageUrl())); mPendingBatchOps.insertMulti(GoogleContactStream::Add, qMakePair(contact, QStringList())); } foreach (const QContact &contact, contactsToUpdate) { QString localID = UContactsBackend::getLocalId(contact); QString avatarEtag = UContactsCustomDetail::getCustomField(contact, UContactsCustomDetail::FieldContactAvatarETag).data().toString(); mLocalIdToAvatar.insert(QString("qtcontacts:galera::%1").arg(localID), qMakePair(avatarEtag, contact.detail().imageUrl())); mPendingBatchOps.insertMulti(GoogleContactStream::Modify, qMakePair(contact, QStringList())); } foreach (const QContact &contact, contactsToRemove) { mPendingBatchOps.insertMulti(GoogleContactStream::Remove, qMakePair(contact, QStringList())); } batchOperationContinue(); } void GRemoteSource::batchOperationContinue() { FUNCTION_CALL_TRACE; if (mState == GRemoteSource::STATE_ABORTED) { LOG_WARNING("Operation aborted"); return; } int limit = qMin(mPendingBatchOps.size(), GConfig::MAX_RESULTS); // no pending batch ops if (limit < 1) { LOG_DEBUG ("No pending operations"); emit transactionCommited(QList(), QList(), QStringList(), QMap(), Sync::SYNC_DONE); return; } QMultiMap > batchPage; QPair value; while (batchPage.size() < limit) { GoogleContactStream::UpdateType type; if (!mPendingBatchOps.values(GoogleContactStream::Add).isEmpty()) { type = GoogleContactStream::Add; } else if (!mPendingBatchOps.values(GoogleContactStream::Modify).isEmpty()) { type = GoogleContactStream::Modify; } else if (!mPendingBatchOps.values(GoogleContactStream::Remove).isEmpty()) { type = GoogleContactStream::Remove; } value = mPendingBatchOps.values(type).first(); mPendingBatchOps.remove(type, value); batchPage.insertMulti(type, value); } GoogleContactStream encoder(false, mAccountName); QByteArray encodedContacts = encoder.encode(batchPage); mTransport->reset(); mTransport->setUrl(mRemoteUri + "batch"); mTransport->setGDataVersionHeader(); mTransport->setAuthToken(mAuthToken); mTransport->setData(encodedContacts); mTransport->addHeader("Content-Type", "application/atom+xml; charset=UTF-8; type=feed"); LOG_TRACE("POST DATA:" << encodedContacts); mTransport->request(GTransport::POST); } int GRemoteSource::parseErrorReponse(const GoogleContactAtom::BatchOperationResponse &response) { if ((response.code == "404") && (response.type == "update")) { // contact not found on remote side: return QContactManager::DoesNotExistError; } return QContactManager::UnspecifiedError; } void GRemoteSource::emitTransactionCommited(const QList &created, const QList &changed, const QList &removed, const QMap &errorMap, Sync::SyncStatus status) { FUNCTION_CALL_TRACE; LOG_INFO("ADDED:" << created.size() << "CHANGED" << changed.size() << "REMOVED" << removed.size()); if (!created.isEmpty()) { emit contactsCreated(created, status); } if (!changed.isEmpty()) { emit contactsChanged(changed, status); } QStringList removedIds; foreach(const QContact &c, removed) { removedIds << UContactsCustomDetail::getCustomField(c, UContactsCustomDetail::FieldRemoteId).data().toString(); } if (!removedIds.isEmpty()) { emit contactsRemoved(removedIds, status); } emit transactionCommited(created, changed, removedIds, errorMap, status); } void GRemoteSource::fetchRemoteContacts(const QDateTime &since, bool includeDeleted, int startIndex) { FUNCTION_CALL_TRACE; if (mState == GRemoteSource::STATE_ABORTED) { LOG_WARNING("Operation aborted"); return; } /** o Get last sync time o Get etag value from local file system (this is a soft etag) o Connect finishedRequest to parseResults & network error slots o Use mTransport to perform network fetch */ if (since.isValid()) { mTransport->setUpdatedMin(since); } if (startIndex > 1) { mTransport->setStartIndex(startIndex); } mTransport->setMaxResults(GConfig::MAX_RESULTS); if (includeDeleted) { mTransport->setShowDeleted(); } // TODO: only fetch contacts from "My Contacts" group for now // we should implement support for all groups mTransport->setGroupFilter(mAccountName, GConfig::GROUP_MY_CONTACTS_ID); mTransport->setGDataVersionHeader(); mTransport->addHeader(QByteArray("Authorization"), QString("Bearer " + mAuthToken).toUtf8()); mTransport->request(GTransport::GET); } /** * The state machine is pretty much maintained in this method. * Maybe it is better to create a separate class that can handle * the sync state. It can act of Qt signals that will be emitted * by this method */ void GRemoteSource::networkRequestFinished() { FUNCTION_CALL_TRACE; if (mState == GRemoteSource::STATE_ABORTED) { LOG_WARNING("Operation aborted"); return; } // o Error - if network error, set the sync results with the code // o Call uninit // o Stop sync // o If success, invoke the mParser->parse () Sync::SyncStatus syncStatus = Sync::SYNC_ERROR; GTransport::HTTP_REQUEST_TYPE requestType = mTransport->requestType(); if (mTransport->hasReply()) { QByteArray data = mTransport->replyBody(); LOG_TRACE(data); if (data.isNull () || data.isEmpty()) { LOG_INFO("Nothing returned from server"); syncStatus = Sync::SYNC_CONNECTION_ERROR; goto operationFailed; } GoogleContactStream parser(false); GoogleContactAtom *atom = parser.parse(data); if (!atom) { LOG_CRITICAL("NULL atom object. Something wrong with parsing"); goto operationFailed; } if ((requestType == GTransport::POST) || (requestType == GTransport::PUT)) { QList addedContacts; QList modContacts; QList delContacts; QMap errorMap; LOG_DEBUG("@@@PREVIOUS REQUEST TYPE=POST"); QMap operationResponses = atom->batchOperationResponses(); QMap batchOperationRemoteIdToType; QMap batchOperationRemoteToLocalId; LOG_DEBUG("RESPONSE SIZE:" << operationResponses.size()); foreach (const GoogleContactAtom::BatchOperationResponse &response, operationResponses) { if (response.isError) { LOG_CRITICAL("batch operation error:\n" " id: " << response.operationId << "\n" " type: " << response.type << "\n" " code: " << response.code << "\n" " reason: " << response.reason << "\n" " descr: " << response.reasonDescription << "\n"); errorMap.insert(response.operationId, parseErrorReponse(response)); } else { LOG_DEBUG("RESPONSE" << response.contactGuid << response.type); batchOperationRemoteToLocalId.insert(response.contactGuid, response.operationId); batchOperationRemoteIdToType.insert(response.contactGuid, response.type); } } QList > remoteAddModContacts = atom->entryContacts(); LOG_DEBUG("Number of changed contacts:" << remoteAddModContacts.size()); for (int i = 0; i < remoteAddModContacts.size(); i++) { QContact &c = remoteAddModContacts[i].first; QContactGuid guid = c.detail(); QString cRemoteId = guid.guid(); if (cRemoteId.isEmpty()) { LOG_WARNING("Remote contact without remote id"); continue; } UContactsBackend::setRemoteId(c, cRemoteId); UContactsBackend::setLocalId(c, batchOperationRemoteToLocalId.value(cRemoteId, "")); QString opType = batchOperationRemoteIdToType.value(cRemoteId, ""); c.removeDetail(&guid); if (opType == QStringLiteral("insert")) { addedContacts << c; } else { modContacts << c; } } delContacts += atom->deletedEntryContacts(); LOG_DEBUG("Number of deleted contacts:" << delContacts.size()); if (!atom->nextEntriesUrl().isEmpty()) { syncStatus = Sync::SYNC_PROGRESS; } else { //TODO: avatars syncStatus = Sync::SYNC_DONE; mState = GRemoteSource::STATE_IDLE; } uploadAvatars(&addedContacts); uploadAvatars(&modContacts); emitTransactionCommited(addedContacts, modContacts, delContacts, errorMap, syncStatus); if (syncStatus == Sync::SYNC_PROGRESS) { batchOperationContinue(); } else { mLocalIdToAvatar.clear(); } } else if (requestType == GTransport::GET) { LOG_DEBUG ("@@@PREVIOUS REQUEST TYPE=GET"); if (mState != GRemoteSource::STATE_FETCHING_CONTACTS) { LOG_WARNING("Received a network request finish but the state is not fetching contacts" << mState); return; } LOG_INFO("received information about" << atom->entryContacts().size() << "add/mod contacts and " << atom->deletedEntryContacts().size() << "del contacts"); QList remoteContacts; // for each remote contact, there are some associated XML elements which // could not be stored in QContactDetail form (eg, link URIs etc). // build up some datastructures to help us retrieve that information // when we need it. // we also store the etag data out-of-band to avoid spurious contact saves // when the etag changes are reported by the remote server. // finally, we can set the id of the contact. QList > remoteAddModContacts = atom->entryContacts(); for (int i = 0; i < remoteAddModContacts.size(); ++i) { QContact c = remoteAddModContacts[i].first; QContactGuid guid = c.detail(); UContactsBackend::setRemoteId(c, guid.guid()); c.removeDetail(&guid); // FIXME: This code came from the meego implementation, until now we did not face // any unsupported xml element. Keep the code here in case some unsupported element // apper. Then we should store it some how on our backend. // m_unsupportedXmlElements[accountId].insert( // c.detail().guid(), // remoteAddModContacts[i].second); // m_contactEtags[accountId].insert(c.detail().guid(), c.detail().id()); // c.setId(QContactId::fromString(m_contactIds[accountId].value(c.detail().guid()))); // m_remoteAddMods[accountId].append(c); remoteContacts << c; } if (mFetchAvatars) { fetchAvatars(&remoteContacts); } QList remoteDelContacts = atom->deletedEntryContacts(); for (int i = 0; i < remoteDelContacts.size(); ++i) { QContact c = remoteDelContacts[i]; QContactGuid guid = c.detail(); UContactsBackend::setRemoteId(c, guid.guid()); c.removeDetail(&guid); // FIXME // c.setId(QContactId::fromString(m_contactIds[accountId].value(c.detail().guid()))); // m_contactAvatars[accountId].remove(c.detail().guid()); // just in case the avatar was outstanding. // m_remoteDels[accountId].append(c); remoteContacts << c; } bool hasMore = (!atom->nextEntriesUrl().isNull() || !atom->nextEntriesUrl().isEmpty()); if (hasMore) { // Request for the next batch // This condition will make this slot to be // called again and again until there are no more // entries left to be fetched from the server mStartIndex += GConfig::MAX_RESULTS; mTransport->setUrl(atom->nextEntriesUrl()); syncStatus = Sync::SYNC_PROGRESS; LOG_DEBUG("Has more contacts to retrieve"); } else { LOG_DEBUG("NO contacts to retrieve"); syncStatus = Sync::SYNC_DONE; mState = GRemoteSource::STATE_IDLE; } // progress qreal progress = -1.0; if (hasMore && (atom->totalResults() > 0)) { progress = (mStartIndex / (qreal) atom->totalResults()); } else if (!hasMore) { progress = 1.0; } LOG_TRACE("NOTIFY CONTACTS FETCHED:" << remoteContacts.size() << "Progress" << progress); emit contactsFetched(remoteContacts, syncStatus, progress); if (hasMore) { LOG_TRACE("FETCH MORE CONTACTS FROM INDEX:" << mStartIndex); fetchRemoteContacts(QDateTime(), mTransport->showDeleted(), mStartIndex); } } delete atom; } return; operationFailed: switch(mState) { case GRemoteSource::STATE_FETCHING_CONTACTS: contactsFetched(QList(), syncStatus, -1.0); break; case GRemoteSource::STATE_BATCH_RUNNING: emitTransactionCommited(QList(), QList(), QList(), QMap(), syncStatus); break; default: break; } mState = GRemoteSource::STATE_IDLE; } void GRemoteSource::networkError(int errorCode) { FUNCTION_CALL_TRACE; Sync::SyncStatus syncStatus = Sync::SYNC_ERROR; switch (errorCode) { case 400: // Bad request. Better to bail out, since it could be a problem with the // data format of the request/response syncStatus = Sync::SYNC_BAD_REQUEST; break; case 401: syncStatus = Sync::SYNC_AUTHENTICATION_FAILURE; break; case 403: case 408: syncStatus = Sync::SYNC_ERROR; break; case 500: case 503: case 504: // Server failures syncStatus = Sync::SYNC_SERVER_FAILURE; break; default: break; }; switch(mState) { case GRemoteSource::STATE_FETCHING_CONTACTS: contactsFetched(QList(), syncStatus, -1.0); break; case GRemoteSource::STATE_BATCH_RUNNING: emitTransactionCommited(QList(), QList(), QList(), QMap(), syncStatus); break; default: break; } mState = GRemoteSource::STATE_IDLE; } const GTransport *GRemoteSource::transport() const { return mTransport.data(); } int GRemoteSource::state() const { return mState; } buteo-sync-plugins-contacts-0.1+16.04.20151211/google/buteo-gcontact-plugin_global.h0000644000015300001610000000241312632560105030442 0ustar pbuserpbgroup00000000000000/* * This file is part of buteo-gcontact-plugin package * * Copyright (C) 2013 Jolla Ltd. and/or its subsidiary(-ies). * 2015 Canonical Ltd. * * Contributors: Sateesh Kavuri * Renato Araujo Oliveira Filho * * 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 BUTEOGCONTACTPLUGIN_GLOBAL_H #define BUTEOGCONTACTPLUGIN_GLOBAL_H #include #if defined(BUTEOGCONTACTPLUGIN_LIBRARY) # define BUTEOGCONTACTPLUGINSHARED_EXPORT Q_DECL_EXPORT #else # define BUTEOGCONTACTPLUGINSHARED_EXPORT Q_DECL_IMPORT #endif #endif // BUTEOGCONTACTPLUGIN_GLOBAL_H buteo-sync-plugins-contacts-0.1+16.04.20151211/google/GContactImageUploader.cpp0000644000015300001610000001572312632560105027414 0ustar pbuserpbgroup00000000000000/**************************************************************************** ** ** Copyright (C) 2015 Canonical Ltd. ** ** Contact: Renato Araujo Oliveira Filho ** ** This program/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 program/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 program/library; if not, write to the Free ** Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA ** 02110-1301 USA ** ****************************************************************************/ #include "GContactImageUploader.h" #include "GContactStream.h" #include "GContactAtom.h" #include "UContactsCustomDetail.h" #include #include #include #include #include #include #define GOOGLE_URL "https://www.google.com/m8/feeds/contacts/default/full" #define GOOGLE_PHOTO_URL "https://www.google.com/m8/feeds/photos/media/%1/%2" GContactImageUploader::GContactImageUploader(const QString &authToken, const QString &accountId, QObject *parent) : QObject(parent), mEventLoop(0), mAuthToken(authToken), mAccountId(accountId), mAbort(false) { } void GContactImageUploader::push(const QString &remoteId, const QUrl &imgUrl) { mQueue.push_back(qMakePair(remoteId, imgUrl)); } QMap GContactImageUploader::result() { return mResults; } /* * This is a sync function since the contact sync process happen in a different * thread we do not need this function to be async */ void GContactImageUploader::exec() { if (mQueue.isEmpty()) { return; } QDateTime startTime = QDateTime::currentDateTime().toUTC(); QScopedPointer networkAccessManager(new QNetworkAccessManager); connect(networkAccessManager.data(), SIGNAL(finished(QNetworkReply*)), SLOT(onRequestFinished(QNetworkReply*)), Qt::QueuedConnection); QEventLoop eventLoop; mEventLoop = &eventLoop; mUploadCompleted = false; while(!mQueue.isEmpty()) { QPair data = mQueue.takeFirst(); QByteArray imgData; QFile imgFile(data.second.toLocalFile()); if (!imgFile.open(QIODevice::ReadOnly)) { LOG_WARNING("Fail to open file:" << data.second.toLocalFile()); continue; } imgData = imgFile.readAll(); imgFile.close();; mCurrentRemoteId = data.first; // upload photo QString requestUrl = QString(GOOGLE_PHOTO_URL) .arg(mAccountId) .arg(mCurrentRemoteId); QNetworkRequest request(requestUrl); request.setRawHeader(QStringLiteral("GData-Version").toUtf8(), QStringLiteral("3.0").toUtf8()); request.setRawHeader(QStringLiteral("Authorization").toUtf8(), QStringLiteral("Bearer %1").arg(mAuthToken).toUtf8()); request.setRawHeader(QStringLiteral("Content-Type").toUtf8(), QStringLiteral("image/*").toUtf8()); request.setRawHeader(QStringLiteral("If-Match").toUtf8(), QStringLiteral("*").toUtf8()); networkAccessManager->put(request, imgData); // wait for the upload to finish eventLoop.exec(); // should we abort? if (mAbort) { break; } } mUploadCompleted = true; if (mAbort) { return; } // After upload the pictures the contact etag get updated we need to retrieve // the new ones QString requestUrl = QString("%1?updated-min=%2&fields=entry(@gd:etag, id, link(@rel, @gd:etag))") .arg(GOOGLE_URL) .arg(startTime.toString(Qt::ISODate)); QNetworkRequest request(requestUrl); request.setRawHeader(QStringLiteral("GData-Version").toUtf8(), QStringLiteral("3.0").toUtf8()); request.setRawHeader(QStringLiteral("Authorization").toUtf8(), QStringLiteral("Bearer %1").arg(mAuthToken).toUtf8()); networkAccessManager->get(request); // wait for the reply to finish eventLoop.exec(); } void GContactImageUploader::onRequestFinished(QNetworkReply *reply) { if (mUploadCompleted) { if (reply->error() != QNetworkReply::NoError) { LOG_WARNING("Fail to retrieve new etags:" << reply->errorString()); } else { QByteArray data = reply->readAll(); LOG_TRACE("After avatar upload query result:" << data); QMap entries = parseEntryList(data); foreach(const QString &remoteId, entries.keys()) { if (mResults.contains(remoteId)) { mResults[remoteId] = entries[remoteId]; emit uploadFinished(remoteId, entries[remoteId]); } } } } else { mResults.insert(mCurrentRemoteId, UploaderReply()); if (reply->error() != QNetworkReply::NoError) { LOG_WARNING("Fail to upload avatar:" << reply->errorString()); emit uploadError(mCurrentRemoteId, reply->errorString()); } else { LOG_TRACE("Avatar upload result" << reply->readAll()); } mCurrentRemoteId.clear(); } if (mEventLoop) { mEventLoop->quit(); } } QMap GContactImageUploader::parseEntryList(const QByteArray &data) const { QMap result; QDomDocument doc; QString errorMsg; if (!doc.setContent(data, &errorMsg)) { LOG_WARNING("Fail to parse etag list" << errorMsg); return result; } QDomNodeList entries = doc.elementsByTagName("entry"); for (int i = 0; i < entries.size(); i++) { UploaderReply reply; QDomElement entry = entries.item(i).toElement(); QDomElement id = entry.firstChildElement("id"); reply.newEtag = entry.attribute("gd:etag"); if (!id.isNull()) { reply.remoteId = id.text().split('/').last(); } QDomNodeList links = entry.elementsByTagName("link"); for (int l = 0; l < links.size(); l++) { QDomElement link = links.item(l).toElement(); if (link.attribute("rel") == "http://schemas.google.com/contacts/2008/rel#photo") { reply.newAvatarEtag = link.attribute("gd:etag"); break; } } result.insert(reply.remoteId, reply); } return result; } buteo-sync-plugins-contacts-0.1+16.04.20151211/google/xmls/0000755000015300001610000000000012632561656023536 5ustar pbuserpbgroup00000000000000buteo-sync-plugins-contacts-0.1+16.04.20151211/google/xmls/sync/0000755000015300001610000000000012632561656024512 5ustar pbuserpbgroup00000000000000buteo-sync-plugins-contacts-0.1+16.04.20151211/google/xmls/sync/google-buteo-contacts.xml0000644000015300001610000000160112632560105031422 0ustar pbuserpbgroup00000000000000 buteo-sync-plugins-contacts-0.1+16.04.20151211/google/xmls/sync/CMakeLists.txt0000644000015300001610000000012512632560105027234 0ustar pbuserpbgroup00000000000000install(FILES google-buteo-contacts.xml DESTINATION "/etc/buteo/profiles/sync" ) buteo-sync-plugins-contacts-0.1+16.04.20151211/google/xmls/CMakeLists.txt0000644000015300001610000000006012632560105026256 0ustar pbuserpbgroup00000000000000add_subdirectory(client) add_subdirectory(sync) buteo-sync-plugins-contacts-0.1+16.04.20151211/google/xmls/client/0000755000015300001610000000000012632561656025014 5ustar pbuserpbgroup00000000000000buteo-sync-plugins-contacts-0.1+16.04.20151211/google/xmls/client/googlecontacts.xml0000644000015300001610000000036112632560105030535 0ustar pbuserpbgroup00000000000000 buteo-sync-plugins-contacts-0.1+16.04.20151211/google/xmls/client/CMakeLists.txt0000644000015300001610000000012012632560105027531 0ustar pbuserpbgroup00000000000000install(FILES googlecontacts.xml DESTINATION "/etc/buteo/profiles/client" ) buteo-sync-plugins-contacts-0.1+16.04.20151211/google/atom_global.h0000644000015300001610000000226112632560105025171 0ustar pbuserpbgroup00000000000000/* * This file is part of buteo-gcontact-plugin package * * Copyright (C) 2013 Jolla Ltd. and/or its subsidiary(-ies). * 2015 Canonical Ltd. * * Contributors: Sateesh Kavuri * Renato Araujo Oliveira Filho * * 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 ATOM_GLOBAL_H #define ATOM_GLOBAL_H #include #if defined(ATOM_LIBRARY) # define ATOMSHARED_EXPORT Q_DECL_EXPORT #else # define ATOMSHARED_EXPORT Q_DECL_IMPORT #endif #endif // ATOM_GLOBAL_H buteo-sync-plugins-contacts-0.1+16.04.20151211/google/GContactImageUploader.h0000644000015300001610000000443312632560105027055 0ustar pbuserpbgroup00000000000000/**************************************************************************** ** ** Copyright (C) 2015 Canonical Ltd. ** ** Contact: Renato Araujo Oliveira Filho ** ** This program/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 program/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 program/library; if not, write to the Free ** Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA ** 02110-1301 USA ** ****************************************************************************/ #ifndef GOOGLECONTACTIMAGEUPLOADER_H #define GOOGLECONTACTIMAGEUPLOADER_H #include #include #include #include #include #include #include #include class GContactImageUploader: public QObject { Q_OBJECT public: class UploaderReply { public: QString remoteId; QString newEtag; QString newAvatarEtag; }; explicit GContactImageUploader(const QString &authToken, const QString &accountId, QObject *parent = 0); void push(const QString &remoteId, const QUrl &imgUrl); QMap result(); void exec(); signals: void uploadFinished(const QUrl &remoteId, const GContactImageUploader::UploaderReply &eTag); void uploadError(const QUrl &remoteId, const QString &error); private slots: void onRequestFinished(QNetworkReply *reply); private: QEventLoop *mEventLoop; QQueue > mQueue; QString mAuthToken; QString mAccountId; QString mCurrentRemoteId; QMap mResults; bool mAbort; bool mUploadCompleted; QMap parseEntryList(const QByteArray &data) const; }; #endif // GOOGLECONTACTIMAGEUPLOADER_H buteo-sync-plugins-contacts-0.1+16.04.20151211/google/LICENSE0000644000015300001610000000172612632560105023552 0ustar pbuserpbgroup00000000000000/* * This file is part of buteo-sync-plugins-contacts package * * Copyright (C) 2013 Jolla Ltd. and/or its subsidiary(-ies). * 2015 Canonical Ltd * * Contributors: Sateesh Kavuri * Renato Araujo Oliveira Filho * * 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 * * buteo-sync-plugins-contacts-0.1+16.04.20151211/google/GContactAtom.h0000644000015300001610000001044512632560105025237 0ustar pbuserpbgroup00000000000000/**************************************************************************** ** ** Copyright (C) 2013-2014 Jolla Ltd. and/or its subsidiary(-ies). ** Contact: Chris Adams ** ** Contributors: Sateesh Kavuri ** Chris Adams ** ** This program/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 program/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 program/library; if not, write to the Free ** Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA ** 02110-1301 USA ** ****************************************************************************/ #ifndef GOOGLECONTACTATOM_H #define GOOGLECONTACTATOM_H #include #include #include #include #include QTCONTACTS_USE_NAMESPACE class GoogleContactAtom { public: GoogleContactAtom(); void setAuthorName(const QString &authorName); QString authorName() const; void setAuthorEmail(const QString &authorEmail); QString authorEmail() const; void setId(const QString &id); QString id() const; void setUpdated(const QString &updated); QString updated() const; void setCategory(const QString &schema = QStringLiteral("http://schemas.google.com/g/2005#kind"), const QString &term = QStringLiteral("http://schemas.google.com/contact/2008#contact")); QString categorySchema() const; QString categoryTerm() const; void setTitle(const QString &title); QString title() const; void setContent(const QString ¬e, const QString &type = QStringLiteral("text")); void setGenerator(const QString &name = QStringLiteral("Contacts"), const QString &version = QStringLiteral("1.0"), const QString &uri = QStringLiteral("https://sailfishos.org")); QString generatorName() const; QString generatorVersion() const; QString generatorUri() const; void setTotalResults(int totalResults); int totalResults() const; void setStartIndex(int startIndex); int startIndex() const; void setItemsPerPage(int itemsPerPage); int itemsPerPage() const; void addEntryContact(const QContact &contact, const QStringList &unsupportedElements); QList > entryContacts() const; void addDeletedEntryContact(const QContact &contact); QList deletedEntryContacts() const; void addEntrySystemGroup(const QString &systemGroupId, const QString &systemGroupAtomId); QMap entrySystemGroups() const; void setNextEntriesUrl(const QString &nextUrl); QString nextEntriesUrl() const; class BatchOperationResponse { public: BatchOperationResponse(); QString operationId; QString type; QString code; QString reason; QString reasonDescription; QString contactGuid; QString eTag; bool isError; }; void addBatchOperationResponse(const QString &operationId, BatchOperationResponse response); QMap batchOperationResponses() const; private: QString mAuthorEmail; QString mAuthorName; QString mCategory; QString mCategorySchema; QString mCategoryTerm; QString mContributor; QString mGeneratorName; QString mGeneratorVersion; QString mGeneratorUri; QString mIcon; QString mId; QString mLink; QString mLogo; QString mRights; QString mSubtitle; QString mTitle; QString mUpdated; int mTotalResults; int mStartIndex; int mItemsPerPage; QMap mBatchOperationResponses; QList mDeletedContactList; QList > mContactList; QMap mSystemGroupAtomIds; QString mNextEntriesUrl; }; #endif // GOOGLECONTACTATOM_H buteo-sync-plugins-contacts-0.1+16.04.20151211/google/GContactAtom.cpp0000644000015300001610000001133112632560105025565 0ustar pbuserpbgroup00000000000000/**************************************************************************** ** ** Copyright (C) 2013-2014 Jolla Ltd. and/or its subsidiary(-ies). ** Contact: Chris Adams ** ** Contributors: Sateesh Kavuri ** Mani Chandrasekar ** Chris Adams ** ** This program/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 program/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 program/library; if not, write to the Free ** Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA ** 02110-1301 USA ** ****************************************************************************/ #include "GContactAtom.h" GoogleContactAtom::BatchOperationResponse::BatchOperationResponse() : isError(false) { } GoogleContactAtom::GoogleContactAtom() { } void GoogleContactAtom::setAuthorEmail(const QString &authorEmail) { mAuthorEmail = authorEmail; } QString GoogleContactAtom::authorEmail() const { return mAuthorEmail; } void GoogleContactAtom::setAuthorName(const QString &authorName) { mAuthorName = authorName; } QString GoogleContactAtom::authorName() const { return mAuthorName; } void GoogleContactAtom::setId(const QString &id) { mId = id; } QString GoogleContactAtom::id() const { return mId; } void GoogleContactAtom::setUpdated(const QString &updated) { mUpdated = updated; } QString GoogleContactAtom::updated() const { return mUpdated; } void GoogleContactAtom::setCategory(const QString &schema, const QString &term) { mCategorySchema = schema; mCategoryTerm = term; } QString GoogleContactAtom::categorySchema() const { return mCategorySchema; } QString GoogleContactAtom::categoryTerm() const { return mCategoryTerm; } void GoogleContactAtom::setTitle(const QString &title) { mTitle = title; } QString GoogleContactAtom::title() const { return mTitle; } void GoogleContactAtom::setGenerator(const QString &name, const QString &version, const QString &uri) { mGeneratorName = name; mGeneratorVersion = version; mGeneratorUri = uri; } void GoogleContactAtom::setContent(const QString ¬e, const QString &type) { Q_UNUSED(note) Q_UNUSED(type) } QString GoogleContactAtom::generatorName() const { return mGeneratorName; } QString GoogleContactAtom::generatorVersion() const { return mGeneratorVersion; } QString GoogleContactAtom::generatorUri() const { return mGeneratorUri; } void GoogleContactAtom::setTotalResults(int totalResults) { mTotalResults = totalResults; } int GoogleContactAtom::totalResults() const { return mTotalResults; } void GoogleContactAtom::setStartIndex(int startIndex) { mStartIndex = startIndex; } int GoogleContactAtom::startIndex() const { return mStartIndex; } void GoogleContactAtom::setItemsPerPage(int itemsPerPage) { mItemsPerPage = itemsPerPage; } int GoogleContactAtom::itemsPerPage() const { return mItemsPerPage; } void GoogleContactAtom::addBatchOperationResponse(const QString &operationId, GoogleContactAtom::BatchOperationResponse response) { mBatchOperationResponses.insert(operationId, response); } QMap GoogleContactAtom::batchOperationResponses() const { return mBatchOperationResponses; } void GoogleContactAtom::addEntryContact(const QContact &entryContact, const QStringList &unsupportedElements) { mContactList.append(qMakePair(entryContact, unsupportedElements)); } QList > GoogleContactAtom::entryContacts() const { return mContactList; } void GoogleContactAtom::addDeletedEntryContact(const QContact &deletedContact) { mDeletedContactList.append(deletedContact); } QList GoogleContactAtom::deletedEntryContacts() const { return mDeletedContactList; } void GoogleContactAtom::addEntrySystemGroup(const QString &systemGroupId, const QString &systemGroupAtomId) { mSystemGroupAtomIds.insert(systemGroupId, systemGroupAtomId); } QMap GoogleContactAtom::entrySystemGroups() const { return mSystemGroupAtomIds; } void GoogleContactAtom::setNextEntriesUrl(const QString &nextUrl) { mNextEntriesUrl = nextUrl; } QString GoogleContactAtom::nextEntriesUrl() const { return mNextEntriesUrl; } buteo-sync-plugins-contacts-0.1+16.04.20151211/google/GTransport.h0000644000015300001610000000555412632560105025024 0ustar pbuserpbgroup00000000000000/* * This file is part of buteo-gcontact-plugin package * * Copyright (C) 2013 Jolla Ltd. and/or its subsidiary(-ies). * 2015 Canonical Ltd. * * Contributors: Sateesh Kavuri * Renato Araujo Oliveira Filho * * 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 GTRANSPORT_H #define GTRANSPORT_H #include #include #include #include #include #include #include #include class GTransportPrivate; class GTransport : public QObject { Q_OBJECT Q_DECLARE_PRIVATE(GTransport) public: typedef enum { GET, POST, DELETE, PUT, HEAD } HTTP_REQUEST_TYPE; explicit GTransport(QObject *parent = 0); GTransport (QUrl url, QList > headers); GTransport (QUrl url, QList > headers, QByteArray data); virtual ~GTransport(); void setUrl(const QString &url); void setData(QByteArray data); void setHeaders(); void addHeader(const QByteArray first, const QByteArray second); void setAuthToken(const QString token); void setGDataVersionHeader(); void setProxy(QString proxyHost, QString proxyPort); void request(const HTTP_REQUEST_TYPE type); bool hasReply() const; const QByteArray replyBody() const; void setUpdatedMin(const QDateTime datetime); void setMaxResults(unsigned int limit); void setShowDeleted(); void setGroupFilter(const QString &account, const QString &groupId); bool showDeleted() const; void setStartIndex(const int index); HTTP_REQUEST_TYPE requestType(); void reset(); typedef enum { HTTP_OK = 200, HTTP_CONTACT_CREATED = 201, HTTP_PRECONDITION_FAILED = 412 } RESPONSE_CODE; static const QString GDATA_VERSION_HEADER; signals: void finishedRequest(); void error(int errorCode); // used by tests void requested(const QUrl &url, QByteArray *result); private slots: virtual void finishedSlot(QNetworkReply* reply); virtual void readyRead(); private: QScopedPointer d_ptr; }; #endif // GTRANSPORT_H buteo-sync-plugins-contacts-0.1+16.04.20151211/google/GContactImageDownloader.cpp0000644000015300001610000000664212632560105027737 0ustar pbuserpbgroup00000000000000/**************************************************************************** ** ** Copyright (C) 2015 Canonical Ltd. ** ** Contact: Renato Araujo Oliveira Filho ** ** This program/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 program/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 program/library; if not, write to the Free ** Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA ** 02110-1301 USA ** ****************************************************************************/ #include "GContactImageDownloader.h" #include "GTransport.h" #include #include #include #include #include GContactImageDownloader::GContactImageDownloader(const QString &authToken, QObject *parent) : QObject(parent), mEventLoop(0), mAuthToken(authToken), mAbort(false) { } GContactImageDownloader::~GContactImageDownloader() { foreach(const QString &file, mTempFiles) { QFile::remove(file); } } void GContactImageDownloader::push(const QUrl &imgUrl) { mQueue.push_back(imgUrl); } QMap GContactImageDownloader::donwloaded() { return mResults; } void GContactImageDownloader::exec() { QNetworkAccessManager *networkAccessManager = new QNetworkAccessManager; connect(networkAccessManager, SIGNAL(finished(QNetworkReply*)), SLOT(onRequestFinished(QNetworkReply*)), Qt::QueuedConnection); QEventLoop eventLoop; mEventLoop = &eventLoop; while(!mQueue.isEmpty()) { QNetworkRequest request(mQueue.takeFirst()); request.setRawHeader(QStringLiteral("GData-Version").toUtf8(), QStringLiteral("3.0").toUtf8()); request.setRawHeader(QStringLiteral("Authorization").toUtf8(), QStringLiteral("Bearer %1").arg(mAuthToken).toUtf8()); networkAccessManager->get(request); // wait for the download to finish eventLoop.exec(); // should we abort? if (mAbort) { break; } } delete networkAccessManager; } void GContactImageDownloader::onRequestFinished(QNetworkReply *reply) { if (reply->error() != QNetworkReply::NoError) { LOG_WARNING("Fail to download avatar:" << reply->errorString()); emit donwloadError(reply->url(), reply->errorString()); } else { QUrl localFile(saveImage(reply->url(), reply->readAll())); mResults.insert(reply->url(), localFile); emit downloadFinished(reply->url(), localFile); } if (mEventLoop) { mEventLoop->quit(); } } QUrl GContactImageDownloader::saveImage(const QUrl &remoteFile, const QByteArray &imgData) { Q_UNUSED(remoteFile); QTemporaryFile tmp; if (tmp.open()) { tmp.write(imgData); tmp.setAutoRemove(false); tmp.close(); mTempFiles << tmp.fileName(); return QUrl::fromLocalFile(tmp.fileName()); } return QUrl(); } buteo-sync-plugins-contacts-0.1+16.04.20151211/google/GContactStream.h0000644000015300001610000001570012632560105025571 0ustar pbuserpbgroup00000000000000/**************************************************************************** ** ** Copyright (C) 2013-2014 Jolla Ltd. and/or its subsidiary(-ies). ** 2015 Canonical Ltd. ** ** Contributors: Sateesh Kavuri ** Mani Chandrasekar ** Chris Adams ** Renato Araujo Oliveira Filho ** ** This program/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 program/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 program/library; if not, write to the Free ** Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA ** 02110-1301 USA ** ****************************************************************************/ #ifndef GOOGLECONTACTSTREAM_H #define GOOGLECONTACTSTREAM_H #include "GContactAtom.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include QTCONTACTS_USE_NAMESPACE class GoogleContactStream : public QObject { Q_OBJECT public: enum UpdateType { Add, Modify, Remove }; public: explicit GoogleContactStream(bool response, const QString &accountEmail = QString(), QObject* parent = 0); ~GoogleContactStream(); QByteArray encode(const QMultiMap > &updates); GoogleContactAtom* parse(const QByteArray &xmlBuffer); signals: void parseDone(bool); // Decoding XML stream to QContacts private: void initAtomFunctionMap(); void initResponseFunctionMap(); void initFunctionMap(); // Atom feed elements handler methods void handleAtomUpdated(); void handleAtomCategory(); void handleAtomAuthor(); void handleAtomOpenSearch(); void handleAtomLink(); void handleAtomId(); void handleAtomGenerator(); void handleAtomTitle(); void handleAtomEntry(); // Following are for the response received from the server in case of failures void handleEntryBatchStatus(GoogleContactAtom::BatchOperationResponse *response); void handleEntryBatchOperation(GoogleContactAtom::BatchOperationResponse *response); void handleEntryBatchId(GoogleContactAtom::BatchOperationResponse *response); // gContact:xxx schema handler methods QContactDetail handleEntryBirthday(); QContactDetail handleEntryGender(); QContactDetail handleEntryHobby(); QContactDetail handleEntryNickname(); QContactDetail handleEntryOccupation(); QContactDetail handleEntryWebsite(); QContactDetail handleEntryJot(); QContactDetail handleEntryEmail(); QContactDetail handleEntryIm(); QContactDetail handleEntryName(); QContactDetail handleEntryOrganization(); QContactDetail handleEntryPhoneNumber(); QContactDetail handleEntryStructuredPostalAddress(); QContactDetail handleEntryUpdated(); QContactDetail handleEntryGroup(); QContactDetail handleEntryExtendedProperty(); QContactDetail handleEntryEvent(); QContactDetail handleRelation(); // handle the id specially QContactDetail handleEntryId(QString *rawId); // unknown / unsupported element handler methods QString handleEntryLink(QContactAvatar *avatar, bool *isAvatar, QString *etag); QString handleEntryUnknownElement(); // generic context parse QList handleContext(const QString &rel) const; typedef void (GoogleContactStream::*Handler)(); typedef QContactDetail (GoogleContactStream::*DetailHandler)(); QMap mAtomFunctionMap; QMap mContactFunctionMap; QXmlStreamReader *mXmlReader; GoogleContactAtom *mAtom; // Encoding QContacts to XML stream private: void encodeContactUpdate(const QContact &qContact, const QStringList &unsupportedElements, const UpdateType updateType, const bool batch); void startBatchFeed(); void endBatchFeed(); void encodeBatchTag(const UpdateType updateType, const QString &batchElementId); void encodeId(const QContact &qContact, bool isUpdate = false); void encodeUpdatedTimestamp(const QContact &qContact); void encodeEtag(const QContact &qContact, bool needed); void encodeCategory(); void encodeName(const QContactName &name); void encodePhoneNumber(const QContactPhoneNumber &phoneNumber); void encodeEmailAddress(const QContactEmailAddress &emailAddress); void encodeAddress(const QContactAddress &address); void encodeUrl(const QContactUrl &url); void encodeBirthday(const QContactBirthday &birthday); void encodeNote(const QContactNote ¬e); void encodeHobby(const QContactHobby &hobby); void encodeGeoLocation(const QContactGeoLocation &geolocation); void encodeOrganization(const QContactOrganization &organization); void encodeAvatar(const QContactAvatar &avatar, const QContact &qContact); void encodeGender(const QContactGender &gender); void encodeNickname(const QContactNickname &nickname); void encodeAnniversary(const QContactAnniversary &anniversary); void encodeOnlineAccount(const QContactOnlineAccount &onlineAccount); void encodeFamily(const QContactFamily &family); void encodeDisplayLabel(const QContactDisplayLabel &displayLabel); void encodeGroup(const QContactExtendedDetail &group); void encodeFavorite(const QContactFavorite &favorite); void encodeExtendedProperty(const QContactExtendedDetail &detail, bool *isGroup); void encodeRingTone(const QContactRingtone &ringTone); void encodeUnknownElements(const QStringList &unknownElements); QString encodeContext(const QList context) const; QXmlStreamWriter *mXmlWriter; QList mEncodedContactsWithAvatars; QString mAccountEmail; }; #endif // GOOGLECONTACTSTREAM_H buteo-sync-plugins-contacts-0.1+16.04.20151211/google/GContactGroupsMap.h0000644000015300001610000000327212632560105026254 0ustar pbuserpbgroup00000000000000/**************************************************************************** ** ** Copyright (C) 2015 Canonical Ltd. ** ** Contact: Renato Araujo Oliveira Filho ** ** This program/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 program/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 program/library; if not, write to the Free ** Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA ** 02110-1301 USA ** ****************************************************************************/ #ifndef GOOGLECONTACTGRUOPMAP_H #define GOOGLECONTACTGRUOPMAP_H #include #include #include #include #include #include #include #include #include class GTransport; class GContactGroupMap: public QObject { Q_OBJECT public: explicit GContactGroupMap(const QString &authToken, QObject *parent = 0); ~GContactGroupMap(); void reload(); signals: void updated(); private slots: void onRequestFinished(QNetworkReply *reply); private: QScopedPointer mNetworkAccessManager; QString mAuthToken; QMap mGroups; }; #endif // GOOGLECONTACTGRUOPMAP_H buteo-sync-plugins-contacts-0.1+16.04.20151211/google/GContactsClient.h0000644000015300001610000000360312632560105025736 0ustar pbuserpbgroup00000000000000/* * This file is part of buteo-gcontact-plugin package * * Copyright (C) 2013 Jolla Ltd. and/or its subsidiary(-ies). * 2015 Canonical Ltd. * * Contributors: Sateesh Kavuri * Renato Araujo Oliveira Filho * * 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 GCONTACTSCLIENT_H #define GCONTACTSCLIENT_H #include #include "buteosyncfw_p.h" #include "buteo-gcontact-plugin_global.h" class GContactEntry; class GTransport; class BUTEOGCONTACTPLUGINSHARED_EXPORT GContactsClient : public UContactsClient { Q_OBJECT public: GContactsClient(const QString& aPluginName, const Buteo::SyncProfile &aProfile, Buteo::PluginCbInterface *aCbInterface); virtual ~GContactsClient(); protected: QVariantMap remoteSourceProperties() const; UAbstractRemoteSource* createRemoteSource(QObject *parent) const; }; extern "C" GContactsClient* createPlugin(const QString& aPluginName, const Buteo::SyncProfile& aProfile, Buteo::PluginCbInterface *aCbInterface); extern "C" void destroyPlugin(GContactsClient *aClient ); #endif // GCONTACTSCLIENT_H buteo-sync-plugins-contacts-0.1+16.04.20151211/google/GTransport.cpp0000644000015300001610000002741212632560105025354 0ustar pbuserpbgroup00000000000000/* * This file is part of buteo-gcontact-plugin package * * Copyright (C) 2013 Jolla Ltd. and/or its subsidiary(-ies). * 2015 Canonical Ltd. * * Contributors: Sateesh Kavuri * Renato Araujo Oliveira Filho * * 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 "GTransport.h" #include #include #include #include #include #include const int MAX_RESULTS = 10; const QString SCOPE_URL("https://www.google.com/m8/feeds/"); const QString GCONTACT_URL(SCOPE_URL + "/contacts/default/"); const QString GCONTACT_GROUP_FEED_URL = "http://www.google.com/m8/feeds/groups"; const QString GDATA_VERSION_TAG = "GData-Version"; const QString GDATA_VERSION = "3.1"; const QString G_DELETE_OVERRIDE_HEADER("X-HTTP-Method-Override: DELETE"); const QString G_ETAG_HEADER("If-Match "); const QString G_AUTH_HEADER ("Authorization"); const QString G_CONTENT_TYPE_HEADER = "application/atom+xml"; /* Query parameters */ const QString QUERY_TAG("q"); const QString MAX_RESULTS_TAG("max-results"); const QString START_INDEX_TAG("start-index"); const QString UPDATED_MIN_TAG("updated-min"); const QString ORDERBY_TAG("orderby"); const QString SHOW_DELETED_TAG("showdeleted"); const QString REQUIRE_ALL_DELETED("requirealldeleted"); const QString SORTORDER_TAG("sortorder"); const QString GROUP_QUERY_TAG("group"); const QString PHOTO_TAG("photos"); const QString MEDIA_TAG("media"); const QString BATCH_TAG("batch"); class GTransportPrivate { public: GTransportPrivate(QObject *parent) : mNetworkRequest(0), mNetworkReply(0), mNetworkMgr(new QNetworkAccessManager(parent)) { } void construct(const QUrl& url) { mUrl = url; QUrlQuery urlQuery(mUrl); QList > queryList = urlQuery.queryItems(); for (int i=0; i < queryList.size(); i++) { QPair pair = queryList.at(i); QByteArray leftEncode = QUrl::toPercentEncoding(pair.first); QByteArray rightEncode = QUrl::toPercentEncoding(pair.second); urlQuery.removeQueryItem(pair.first); urlQuery.addQueryItem(leftEncode, rightEncode); } mUrl.setQuery(urlQuery); } QNetworkRequest *mNetworkRequest; QNetworkReply *mNetworkReply; QScopedPointer mNetworkMgr; QUrl mUrl; QList > mHeaders; QByteArray mPostData; QByteArray mNetworkReplyBody; QNetworkReply::NetworkError mNetworkError; int mResponseCode; QString mAuthToken; QDateTime mUpdatedMin; GTransport::HTTP_REQUEST_TYPE mRequestType; }; GTransport::GTransport(QObject *parent) : QObject(parent), d_ptr(new GTransportPrivate(this)) { FUNCTION_CALL_TRACE; Q_D(GTransport); connect(d->mNetworkMgr.data(), SIGNAL(finished(QNetworkReply*)), SLOT(finishedSlot(QNetworkReply*)), Qt::QueuedConnection); } GTransport::GTransport (QUrl url, QList > headers) : d_ptr(new GTransportPrivate(this)) { FUNCTION_CALL_TRACE; Q_D(GTransport); d->mHeaders = headers; d->construct(url); connect(d->mNetworkMgr.data(), SIGNAL(finished(QNetworkReply*)), SLOT(finishedSlot(QNetworkReply*)), Qt::QueuedConnection); } GTransport::GTransport (QUrl url, QList > headers, QByteArray data) : d_ptr(new GTransportPrivate(this)) { FUNCTION_CALL_TRACE; Q_D(GTransport); d->mHeaders = headers; d->mPostData = data; d->construct(url); connect(d->mNetworkMgr.data(), SIGNAL(finished(QNetworkReply*)), SLOT(finishedSlot(QNetworkReply*)), Qt::QueuedConnection); } GTransport::~GTransport() { FUNCTION_CALL_TRACE; Q_D(GTransport); delete d->mNetworkRequest; d->mNetworkRequest = 0; // TODO: Should I delete network reply?? d->mNetworkReply = 0; } void GTransport::setUrl(const QString &url) { FUNCTION_CALL_TRACE; Q_D(GTransport); d->mUrl.setUrl(url, QUrl::StrictMode); d->construct(d->mUrl); } void GTransport::setData(QByteArray data) { FUNCTION_CALL_TRACE; Q_D(GTransport); //FIXME: this is really necessary?? if (d->mPostData.isEmpty()) { d->mPostData.clear(); } d->mPostData = data; } void GTransport::setHeaders() { FUNCTION_CALL_TRACE; Q_D(GTransport); /* * A laughable bug in Google (bug#3397). If the "GDataVersion:3.0" tag is not * the first header in the list, then google does not consider * it as a 3.0 version, and just returns the default format * So, the headers order is very sensitive */ for (int i=0; i < d->mHeaders.count (); i++) { QPair headerPair = d->mHeaders[i]; d->mNetworkRequest->setRawHeader(headerPair.first, headerPair.second); } } void GTransport::addHeader(const QByteArray first, const QByteArray second) { FUNCTION_CALL_TRACE; Q_D(GTransport); d->mHeaders.append(QPair (first, second)); } void GTransport::setAuthToken(const QString token) { FUNCTION_CALL_TRACE; Q_D(GTransport); d->mAuthToken = token; QByteArray header1 = QString(G_AUTH_HEADER).toUtf8(); addHeader(header1, ("Bearer " + token).toUtf8()); } void GTransport::setGDataVersionHeader() { Q_D(GTransport); d->mHeaders.append(QPair (QByteArray ("GData-Version"), QByteArray ("3.0"))); } void GTransport::setProxy (QString proxyHost, QString proxyPort) { FUNCTION_CALL_TRACE; Q_D(GTransport); QNetworkProxy proxy = d->mNetworkMgr->proxy(); proxy.setType (QNetworkProxy::HttpProxy); proxy.setHostName (proxyHost); proxy.setPort (proxyPort.toInt ()); d->mNetworkMgr->setProxy(proxy); } void GTransport::request(const HTTP_REQUEST_TYPE type) { FUNCTION_CALL_TRACE; Q_D(GTransport); LOG_DEBUG ("Request type:" << type); if (d->mNetworkRequest) { delete d->mNetworkRequest; d->mNetworkRequest = 0; } d->mNetworkReplyBody = ""; d->mNetworkRequest = new QNetworkRequest(); d->mNetworkRequest->setUrl(d->mUrl); setHeaders(); d->mRequestType = type; LOG_DEBUG("++URL:" << d->mNetworkRequest->url().toString ()); switch (type) { case GET: d->mNetworkReply = d->mNetworkMgr->get(*(d->mNetworkRequest)); LOG_DEBUG ("--- FINISHED GET REQUEST ---"); break; case POST: d->mNetworkRequest->setHeader (QNetworkRequest::ContentLengthHeader, d->mPostData.size ()); d->mNetworkReply = d->mNetworkMgr->post(*(d->mNetworkRequest), d->mPostData); LOG_DEBUG ("--- FINISHED POST REQUEST ---"); break; case PUT: d->mNetworkRequest->setHeader (QNetworkRequest::ContentLengthHeader, d->mPostData.size ()); d->mNetworkReply = d->mNetworkMgr->put(*(d->mNetworkRequest), d->mPostData); LOG_DEBUG ("--- FINISHED PUT REQUEST ---"); break; case DELETE: d->mNetworkReply = d->mNetworkMgr->deleteResource(*(d->mNetworkRequest)); break; case HEAD: // Nothing break; default: // FIXME: signal the error break; } d->mNetworkRequest->setRawHeader(QStringLiteral("If-Match").toUtf8(), QStringLiteral("*").toUtf8()); QList headerList = d->mNetworkRequest->rawHeaderList(); for (int i=0; imNetworkRequest->rawHeader(headerList.at (i))); } connect(d->mNetworkReply, SIGNAL(readyRead()), SLOT(readyRead())); } bool GTransport::hasReply() const { FUNCTION_CALL_TRACE; const Q_D(GTransport); return (d->mNetworkReply); } const QByteArray GTransport::replyBody() const { FUNCTION_CALL_TRACE; const Q_D(GTransport); return d->mNetworkReplyBody; } void GTransport::readyRead() { FUNCTION_CALL_TRACE; Q_D(GTransport); d->mResponseCode = d->mNetworkReply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(); LOG_DEBUG ("++RESPONSE CODE:" << d->mResponseCode); QByteArray bytes = d->mNetworkReply->readAll(); if (d->mResponseCode >= 200 && d->mResponseCode <= 300) { d->mNetworkReplyBody += bytes; } else { LOG_DEBUG ("SERVER ERROR:" << bytes); emit error(d->mResponseCode); } } void GTransport::finishedSlot(QNetworkReply *reply) { FUNCTION_CALL_TRACE; Q_D(GTransport); // QVariant statusCode = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute); // QVariant redirectionUrl = reply->attribute(QNetworkRequest::RedirectionTargetAttribute); d->mNetworkError = reply->error(); if (d->mNetworkError != QNetworkReply::NoError) { emit error(d->mNetworkError); } emit finishedRequest(); } void GTransport::setUpdatedMin (const QDateTime datetime) { FUNCTION_CALL_TRACE; Q_D(GTransport); d->mUpdatedMin = datetime; QUrlQuery urlQuery(d->mUrl); if (!urlQuery.hasQueryItem(UPDATED_MIN_TAG)) { urlQuery.addQueryItem(UPDATED_MIN_TAG, d->mUpdatedMin.toString(Qt::ISODate)); d->mUrl.setQuery(urlQuery); } } void GTransport::setMaxResults (unsigned int limit) { FUNCTION_CALL_TRACE; Q_D(GTransport); QUrlQuery urlQuery(d->mUrl); if (!urlQuery.hasQueryItem(MAX_RESULTS_TAG)) { urlQuery.addQueryItem(MAX_RESULTS_TAG, QString::number(limit)); d->mUrl.setQuery(urlQuery); } } void GTransport::setShowDeleted() { FUNCTION_CALL_TRACE; Q_D(GTransport); QUrlQuery urlQuery(d->mUrl); if (!urlQuery.hasQueryItem(SHOW_DELETED_TAG)) { urlQuery.addQueryItem(SHOW_DELETED_TAG, "true"); d->mUrl.setQuery(urlQuery); } } void GTransport::setGroupFilter(const QString &account, const QString &groupId) { FUNCTION_CALL_TRACE; Q_D(GTransport); QUrlQuery urlQuery(d->mUrl); if (urlQuery.hasQueryItem(GROUP_QUERY_TAG)) { urlQuery.removeQueryItem(GROUP_QUERY_TAG); } QString groupIdValue = QString("%1/%2/base/%3") .arg(GCONTACT_GROUP_FEED_URL) .arg(QString(QUrl::toPercentEncoding(account))) .arg(groupId); urlQuery.addQueryItem(GROUP_QUERY_TAG, groupIdValue); d->mUrl.setQuery(urlQuery); } bool GTransport::showDeleted() const { const Q_D(GTransport); QUrlQuery urlQuery(d->mUrl); return urlQuery.hasQueryItem(SHOW_DELETED_TAG); } void GTransport::setStartIndex(const int index) { FUNCTION_CALL_TRACE; Q_D(GTransport); QUrlQuery urlQuery(d->mUrl); if (urlQuery.hasQueryItem ("start-index")) { urlQuery.removeQueryItem ("start-index"); } urlQuery.addQueryItem ("start-index", QString::number (index)); d->mUrl.setQuery(urlQuery); } GTransport::HTTP_REQUEST_TYPE GTransport::requestType () { Q_D(GTransport); return d->mRequestType; } void GTransport::reset() { Q_D(GTransport); d->mUrl.clear (); d->mHeaders.clear (); d->mPostData.clear (); d->mNetworkReplyBody.clear(); } buteo-sync-plugins-contacts-0.1+16.04.20151211/google/GRemoteSource.h0000644000015300001610000000632212632560105025436 0ustar pbuserpbgroup00000000000000/* * This file is part of buteo-sync-plugins-contacts package * * Copyright (C) 2013 Jolla Ltd. and/or its subsidiary(-ies). * 2015 Canonical Ltd * * Contributors: Sateesh Kavuri * Mani Chandrasekar * Renato Araujo Oliveira Filho * * 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 "GContactStream.h" #include #include #include class GTransport; class GRemoteSource : public UAbstractRemoteSource { Q_OBJECT public: GRemoteSource(QObject *parent = 0); ~GRemoteSource(); // UAbstractRemoteSource bool init(const QVariantMap &properties); void abort(); void fetchContacts(const QDateTime &since, bool includeDeleted, bool fetchAvatar = true); // help on tests const GTransport *transport() const; int state() const; protected: void saveContactsNonBatch(const QList contacts); void removeContactsNonBatch(const QList contacts); void batch(const QList &contactsToCreate, const QList &contactsToUpdate, const QList &contactsToRemove); private slots: void networkRequestFinished(); void networkError(int errorCode); private: enum SyncState { STATE_IDLE = 0, STATE_FETCHING_CONTACTS, STATE_BATCH_RUNNING, STATE_ABORTED }; QScopedPointer mTransport; QString mRemoteUri; QString mAuthToken; QString mSyncTarget; QString mAccountName; SyncState mState; int mStartIndex; bool mFetchAvatars; QMap > mLocalIdToAvatar; QMap mLocalIdToContact; QMultiMap > mPendingBatchOps; void fetchAvatars(QList *contacts); void uploadAvatars(QList *contacts); void fetchRemoteContacts(const QDateTime &since, bool includeDeleted, int startIndex); void batchOperationContinue(); int parseErrorReponse(const GoogleContactAtom::BatchOperationResponse &response); void emitTransactionCommited(const QList &created, const QList &changed, const QList &removed, const QMap &errorMap, Sync::SyncStatus status); }; buteo-sync-plugins-contacts-0.1+16.04.20151211/google/README.md0000644000015300001610000000013012632560105024010 0ustar pbuserpbgroup00000000000000buteo-gcontact-plugin ===================== Buteo Google contact synchronization pluginbuteo-sync-plugins-contacts-0.1+16.04.20151211/google/GContactGroupsMap.cpp0000644000015300001610000000716312632560105026612 0ustar pbuserpbgroup00000000000000/**************************************************************************** ** ** Copyright (C) 2015 Canonical Ltd. ** ** Contact: Renato Araujo Oliveira Filho ** ** This program/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 program/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 program/library; if not, write to the Free ** Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA ** 02110-1301 USA ** ****************************************************************************/ #include "GContactGroupsMap.h" #include #include #include #include #include GContactGroupMap::GContactGroupMap(const QString &authToken, QObject *parent) : QObject(parent), mNetworkAccessManager(new QNetworkAccessManager), mAuthToken(authToken) { } GContactGroupMap::~GContactGroupMap() { } void GContactGroupMap::reload() { connect(networkAccessManager, SIGNAL(finished(QNetworkReply*)), SLOT(onRequestFinished(QNetworkReply*)), Qt::QueuedConnection); QNetworkRequest request(mQueue.takeFirst()); request.setRawHeader("GData-Version", "3.0"); request.setRawHeader(QString(QLatin1String("Authorization")).toUtf8(), QString(QLatin1String("Bearer ") + mAuthToken).toUtf8()); QNetworkReply *reply = networkAccessManager->get(request); } void GContactGroupMap::push(const QUrl &imgUrl) { mQueue.push_back(imgUrl); } QMap GContactImageDownloader::donwloaded() { return mResults; } void GContactImageDownloader::exec() { QNetworkAccessManager *networkAccessManager = new QNetworkAccessManager; QEventLoop eventLoop; mEventLoop = &eventLoop; while(!mQueue.isEmpty()) { QNetworkRequest request(mQueue.takeFirst()); request.setRawHeader("GData-Version", "3.0"); request.setRawHeader(QString(QLatin1String("Authorization")).toUtf8(), QString(QLatin1String("Bearer ") + mAuthToken).toUtf8()); QNetworkReply *reply = networkAccessManager->get(request); // wait for the download to finish eventLoop.exec(); // should we abort? if (mAbort) { break; } } delete networkAccessManager; } void GContactImageDownloader::onRequestFinished(QNetworkReply *reply) { if (reply->error() != QNetworkReply::NoError) { LOG_WARNING("Fail to download avatar:" << reply->errorString()); emit donwloadError(reply->url(), reply->errorString()); } else { QUrl localFile(saveImage(reply->url(), reply->readAll())); mResults.insert(reply->url(), localFile); emit downloadFinished(reply->url(), localFile); } if (mEventLoop) { mEventLoop->quit(); } } QUrl GContactImageDownloader::saveImage(const QUrl &remoteFile, const QByteArray &imgData) { Q_UNUSED(remoteFile); QTemporaryFile tmp; if (tmp.open()) { tmp.write(imgData); tmp.setAutoRemove(false); tmp.close(); mTempFiles << tmp.fileName(); return QUrl::fromLocalFile(tmp.fileName()); } return QUrl(); } buteo-sync-plugins-contacts-0.1+16.04.20151211/google/buteosyncfw_p.h0000644000015300001610000000277212632560105025607 0ustar pbuserpbgroup00000000000000/**************************************************************************** ** ** Copyright (C) 2014 Jolla Ltd. ** Contact: Chris Adams ** ** This program/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 program/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 program/library; if not, write to the Free ** Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA ** 02110-1301 USA ** ****************************************************************************/ #ifndef SOCIALD_BUTEOSYNCFW_P_H #define SOCIALD_BUTEOSYNCFW_P_H // Several headers from Buteo SyncFW produce warnings // This means we cannot use -Werror without wrapping them // in GCC-specific pragmas to ignore warnings from Buteo. #pragma GCC system_header #include #include #include #include #include #include #include #include #include #include #endif // SOCIALD_BUTEOSYNCFW_P_H buteo-sync-plugins-contacts-0.1+16.04.20151211/google/GConfig.cpp0000644000015300001610000000420212632560105024555 0ustar pbuserpbgroup00000000000000/* * This file is part of buteo-sync-plugins-contacts package * * Copyright (C) 2013 Jolla Ltd. and/or its subsidiary(-ies). * 2015 Canonical Ltd * * Contributors: Sateesh Kavuri * Mani Chandrasekar * Renato Araujo Oliveira Filho * * 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 "GConfig.h" const int GConfig::MAX_RESULTS = 30; const QString GConfig::SCOPE_URL = "https://www.google.com/m8/feeds/"; const QString GConfig::GCONTACT_URL = SCOPE_URL + "/contacts/default/"; const QString GConfig::GDATA_VERSION_TAG = "GData-Version"; const QString GConfig::GDATA_VERSION = "3.0"; const QString GConfig::G_DELETE_OVERRIDE_HEADER = "X-HTTP-Method-Override: DELETE"; const QString GConfig::G_ETAG_HEADER = "If-Match"; const QString GConfig::G_AUTH_HEADER = "Authorization"; /* Query parameters */ const QString GConfig::QUERY_TAG = "q"; const QString GConfig::MAX_RESULTS_TAG = "max-results"; const QString GConfig::START_INDEX_TAG = "start-index"; const QString GConfig::UPDATED_MIN_TAG = "updated-min"; const QString GConfig::ORDERBY_TAG = "orderby"; const QString GConfig::SHOW_DELETED_TAG = "showdeleted"; const QString GConfig::REQUIRE_ALL_DELETED = "requirealldeleted"; const QString GConfig::SORTORDER_TAG = "sortorder"; const QString GConfig::GROUP_MY_CONTACTS_ID = "6"; const QString GConfig::PHOTO_TAG = "photos"; const QString GConfig::MEDIA_TAG = "media"; const QString GConfig::BATCH_TAG = "batch"; buteo-sync-plugins-contacts-0.1+16.04.20151211/google/CMakeLists.txt0000644000015300001610000000415612632560105025305 0ustar pbuserpbgroup00000000000000project(buteo-contact-google) set(GOOGLE_CONTACTS_CLIENT googlecontacts-client) set(GOOGLE_CONTACTS_LIB googlecontacts-lib) include_directories( ${CMAKE_BINARY_DIR} ${buteo-contact-client_SOURCE_DIR} ${ACCOUNTS_INCLUDE_DIRS} ${BUTEOSYNCFW_INCLUDE_DIRS} ${LIBSIGNON_INCLUDE_DIRS} ) set(GOOGLE_CONTACTS_LIB_SRCS atom_global.h buteo-gcontact-plugin_global.h buteosyncfw_p.h GConfig.h GConfig.cpp GContactAtom.h GContactAtom.cpp GContactImageDownloader.h GContactImageDownloader.cpp GContactStream.h GContactStream.cpp GRemoteSource.h GRemoteSource.cpp ) add_library(${GOOGLE_CONTACTS_LIB} STATIC ${GOOGLE_CONTACTS_LIB_SRCS} ) target_link_libraries(${GOOGLE_CONTACTS_LIB} ${ACCOUNTS_LIBRARIES} ${BUTEOSYNCFW_LIBRARIES} ${LIBSIGNON_LIBRARIES} ubuntu-contact-client ) qt5_use_modules(${GOOGLE_CONTACTS_LIB} Core Network Contacts) # Buteo oop plugin add_definitions(-DCLASSNAME=GContactsClient) add_definitions(-DCLASSNAME_H=\"GContactsClient.h\") add_definitions(-DCLIENT_PLUGIN) set(GOOGLE_CONTACT_BUTEO_SRCS ${BUTEOSYNCFW_INCLUDEDIR}/ButeoPluginIfaceAdaptor.h ${BUTEOSYNCFW_INCLUDEDIR}/PluginCbImpl.h ${BUTEOSYNCFW_INCLUDEDIR}/PluginServiceObj.h ${BUTEOSYNCFW_INCLUDEDIR}/ButeoPluginIfaceAdaptor.cpp ${BUTEOSYNCFW_INCLUDEDIR}/PluginCbImpl.cpp ${BUTEOSYNCFW_INCLUDEDIR}/PluginServiceObj.cpp ${BUTEOSYNCFW_INCLUDEDIR}/plugin_main.cpp ) set(GOOGLE_CONTACTS_CLIENT_SRCS GContactsClient.h GContactsClient.cpp # real implementation GTransport.h GTransport.cpp GContactImageUploader.cpp GContactImageUploader.h ${GOOGLE_CONTACT_BUTEO_SRCS} ) add_executable(${GOOGLE_CONTACTS_CLIENT} ${GOOGLE_CONTACTS_CLIENT_SRCS} ) target_link_libraries(${GOOGLE_CONTACTS_CLIENT} ${ACCOUNTS_LIBRARIES} ${BUTEOSYNCFW_LIBRARIES} ${LIBSIGNON_LIBRARIES} ${GOOGLE_CONTACTS_LIB} ubuntu-contact-client ) qt5_use_modules(${GOOGLE_CONTACTS_CLIENT} Core DBus Network Contacts) install(TARGETS ${GOOGLE_CONTACTS_CLIENT} RUNTIME DESTINATION "${BUTEOSYNCFW_PLUGIN_PATH}/oopp" ) add_subdirectory(xmls) buteo-sync-plugins-contacts-0.1+16.04.20151211/config.h.in0000644000015300001610000000031012632560105023300 0ustar pbuserpbgroup00000000000000#ifndef __CONFIG_H__ #define __CONFIG_H__ #include const QString GOOGLE_ETAG_DETAIL ("X-GOOGLE-ETAG"); const QString QCONTACTS_BACKEND_NAME ("galera"); #endif buteo-sync-plugins-contacts-0.1+16.04.20151211/cmake_uninstall.cmake.in0000644000015300001610000000165412632560105026051 0ustar pbuserpbgroup00000000000000IF(NOT EXISTS "@CMAKE_CURRENT_BINARY_DIR@/install_manifest.txt") MESSAGE(FATAL_ERROR "Cannot find install manifest: \"@CMAKE_CURRENT_BINARY_DIR@/install_manifest.txt\"") ENDIF(NOT EXISTS "@CMAKE_CURRENT_BINARY_DIR@/install_manifest.txt") FILE(READ "@CMAKE_CURRENT_BINARY_DIR@/install_manifest.txt" files) STRING(REGEX REPLACE "\n" ";" files "${files}") FOREACH(file ${files}) MESSAGE(STATUS "Uninstalling \"$ENV{DESTDIR}${file}\"") IF(EXISTS "$ENV{DESTDIR}${file}") EXEC_PROGRAM( "@CMAKE_COMMAND@" ARGS "-E remove \"$ENV{DESTDIR}${file}\"" OUTPUT_VARIABLE rm_out RETURN_VALUE rm_retval ) IF(NOT "${rm_retval}" STREQUAL 0) MESSAGE(FATAL_ERROR "Problem when removing \"$ENV{DESTDIR}${file}\"") ENDIF(NOT "${rm_retval}" STREQUAL 0) ELSE(EXISTS "$ENV{DESTDIR}${file}") MESSAGE(STATUS "File \"$ENV{DESTDIR}${file}\" does not exist.") ENDIF(EXISTS "$ENV{DESTDIR}${file}") ENDFOREACH(file) buteo-sync-plugins-contacts-0.1+16.04.20151211/storage-change-notifier/0000755000015300001610000000000012632561656026003 5ustar pbuserpbgroup00000000000000buteo-sync-plugins-contacts-0.1+16.04.20151211/storage-change-notifier/CMakeLists.txt0000644000015300001610000000003312632560105030523 0ustar pbuserpbgroup00000000000000add_subdirectory(contacts) buteo-sync-plugins-contacts-0.1+16.04.20151211/storage-change-notifier/contacts/0000755000015300001610000000000012632561656027621 5ustar pbuserpbgroup00000000000000././@LongLink0000000000000000000000000000016100000000000011213 Lustar 00000000000000buteo-sync-plugins-contacts-0.1+16.04.20151211/storage-change-notifier/contacts/ContactsChangeNotifierPlugin.cppbuteo-sync-plugins-contacts-0.1+16.04.20151211/storage-change-notifier/contacts/ContactsChangeNotifi0000644000015300001610000000535412632560105033574 0ustar pbuserpbgroup00000000000000/**************************************************************************** ** ** Copyright (C) 2015 Canonical Ltd. ** ** Contact: Renato Araujo Oliveira Filho ** ** This program/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 program/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 program/library; if not, write to the Free ** Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA ** 02110-1301 USA ** ****************************************************************************/ #include "ContactsChangeNotifierPlugin.h" #include "ContactsChangeNotifier.h" #include "LogMacros.h" #include using namespace Buteo; extern "C" StorageChangeNotifierPlugin* createPlugin(const QString& aStorageName) { return new ContactsChangeNotifierPlugin(aStorageName); } extern "C" void destroyPlugin(StorageChangeNotifierPlugin* plugin) { delete plugin; } ContactsChangeNotifierPlugin::ContactsChangeNotifierPlugin(const QString& aStorageName) : StorageChangeNotifierPlugin(aStorageName), ihasChanges(false), iDisableLater(false) { FUNCTION_CALL_TRACE; icontactsChangeNotifier = new ContactsChangeNotifier; connect(icontactsChangeNotifier, SIGNAL(change()), SLOT(onChange())); } ContactsChangeNotifierPlugin::~ContactsChangeNotifierPlugin() { FUNCTION_CALL_TRACE; delete icontactsChangeNotifier; } QString ContactsChangeNotifierPlugin::name() const { FUNCTION_CALL_TRACE; return iStorageName; } bool ContactsChangeNotifierPlugin::hasChanges() const { FUNCTION_CALL_TRACE; return ihasChanges; } void ContactsChangeNotifierPlugin::changesReceived() { FUNCTION_CALL_TRACE; ihasChanges = false; } void ContactsChangeNotifierPlugin::onChange() { FUNCTION_CALL_TRACE; ihasChanges = true; if(iDisableLater) { icontactsChangeNotifier->disable(); } else { emit storageChange(); } } void ContactsChangeNotifierPlugin::enable() { FUNCTION_CALL_TRACE; icontactsChangeNotifier->enable(); iDisableLater = false; } void ContactsChangeNotifierPlugin::disable(bool disableAfterNextChange) { FUNCTION_CALL_TRACE; if(disableAfterNextChange) { iDisableLater = true; } else { icontactsChangeNotifier->disable(); } } ././@LongLink0000000000000000000000000000015700000000000011220 Lustar 00000000000000buteo-sync-plugins-contacts-0.1+16.04.20151211/storage-change-notifier/contacts/ContactsChangeNotifierPlugin.hbuteo-sync-plugins-contacts-0.1+16.04.20151211/storage-change-notifier/contacts/ContactsChangeNotifi0000644000015300001610000000420412632560105033565 0ustar pbuserpbgroup00000000000000/**************************************************************************** ** ** Copyright (C) 2015 Canonical Ltd. ** ** Contact: Renato Araujo Oliveira Filho ** ** This program/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 program/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 program/library; if not, write to the Free ** Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA ** 02110-1301 USA ** ****************************************************************************/ #ifndef CONTACTSCHANGENOTIFIERPLUGIN_H #define CONTACTSCHANGENOTIFIERPLUGIN_H #include "StorageChangeNotifierPlugin.h" class ContactsChangeNotifier; class ContactsChangeNotifierPlugin : public Buteo::StorageChangeNotifierPlugin { Q_OBJECT public: /*! \brief constructor * see StorageChangeNotifierPlugin */ ContactsChangeNotifierPlugin(const QString& aStorageName); /*! \brief destructor */ ~ContactsChangeNotifierPlugin(); /*! \brief see StorageChangeNotifierPlugin::name */ QString name() const; /*! \brief see StorageChangeNotifierPlugin::hasChanges */ bool hasChanges() const; /*! \brief see StorageChangeNotifierPlugin::changesReceived */ void changesReceived(); /*! \brief see StorageChangeNotifierPlugin::enable */ void enable(); /*! \brief see StorageChangeNotifierPlugin::disable */ void disable(bool disableAfterNextChange = false); private Q_SLOTS: /*! \brief handles a change notification from contacts notifier */ void onChange(); private: ContactsChangeNotifier* icontactsChangeNotifier; bool ihasChanges; bool iDisableLater; }; #endif ././@LongLink0000000000000000000000000000015300000000000011214 Lustar 00000000000000buteo-sync-plugins-contacts-0.1+16.04.20151211/storage-change-notifier/contacts/ContactsChangeNotifier.cppbuteo-sync-plugins-contacts-0.1+16.04.20151211/storage-change-notifier/contacts/ContactsChangeNotifi0000644000015300001610000000550512632560105033572 0ustar pbuserpbgroup00000000000000/**************************************************************************** ** ** Copyright (C) 2015 Canonical Ltd. ** ** Contact: Renato Araujo Oliveira Filho ** ** This program/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 program/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 program/library; if not, write to the Free ** Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA ** 02110-1301 USA ** ****************************************************************************/ #include "ContactsChangeNotifier.h" #include "LogMacros.h" #include const QString DEFAULT_CONTACTS_MANAGER("galera"); ContactsChangeNotifier::ContactsChangeNotifier() : iDisabled(true) { FUNCTION_CALL_TRACE; iManager = new QContactManager(DEFAULT_CONTACTS_MANAGER); } ContactsChangeNotifier::~ContactsChangeNotifier() { disable(); delete iManager; } void ContactsChangeNotifier::enable() { if(iManager && iDisabled) { connect(iManager, SIGNAL(contactsAdded(const QList&)), SLOT(onContactsAdded(const QList&))); connect(iManager, SIGNAL(contactsRemoved(const QList&)), SLOT(onContactsRemoved(const QList&))); connect(iManager, SIGNAL(contactsChanged(const QList&)), SLOT(onContactsChanged(const QList&))); iDisabled = false; } } void ContactsChangeNotifier::onContactsAdded(const QList& ids) { FUNCTION_CALL_TRACE; if(ids.count()) { foreach(const QContactId &id, ids) { LOG_DEBUG("Added contact with id" << id); } emit change(); } } void ContactsChangeNotifier::onContactsRemoved(const QList& ids) { FUNCTION_CALL_TRACE; if(ids.count()) { foreach(const QContactId &id, ids) { LOG_DEBUG("Removed contact with id" << id); } emit change(); } } void ContactsChangeNotifier::onContactsChanged(const QList& ids) { FUNCTION_CALL_TRACE; if(ids.count()) { foreach(const QContactId &id, ids) { LOG_DEBUG("Changed contact with id" << id); } emit change(); } } void ContactsChangeNotifier::disable() { FUNCTION_CALL_TRACE; iDisabled = true; QObject::disconnect(iManager, 0, this, 0); } ././@LongLink0000000000000000000000000000015100000000000011212 Lustar 00000000000000buteo-sync-plugins-contacts-0.1+16.04.20151211/storage-change-notifier/contacts/ContactsChangeNotifier.hbuteo-sync-plugins-contacts-0.1+16.04.20151211/storage-change-notifier/contacts/ContactsChangeNotifi0000644000015300001610000000360012632560105033564 0ustar pbuserpbgroup00000000000000/**************************************************************************** ** ** Copyright (C) 2015 Canonical Ltd. ** ** Contact: Renato Araujo Oliveira Filho ** ** This program/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 program/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 program/library; if not, write to the Free ** Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA ** 02110-1301 USA ** ****************************************************************************/ #ifndef CONTACTSCHANGENOTIFIER_H #define CONTACTSCHANGENOTIFIER_H #include #include #include #include QTCONTACTS_USE_NAMESPACE class ContactsChangeNotifier : public QObject { Q_OBJECT public: /*! \brief constructor */ ContactsChangeNotifier(); /*! \brief destructor */ ~ContactsChangeNotifier(); /*! \brief start listening to changes from QContactManager */ void enable(); /*! \brief stop listening to changes from QContactManager */ void disable(); Q_SIGNALS: /*! emit this signal to notify a change in contacts backend */ void change(); private Q_SLOTS: void onContactsAdded(const QList& ids); void onContactsRemoved(const QList& ids); void onContactsChanged(const QList& ids); private: QContactManager* iManager; bool iDisabled; }; #endif buteo-sync-plugins-contacts-0.1+16.04.20151211/storage-change-notifier/contacts/CMakeLists.txt0000644000015300001610000000126412632560105032350 0ustar pbuserpbgroup00000000000000project(buteo-storagechangenotifier) set(STORAGE_CHANGE_NOTIFIER "hcontacts-changenotifier") set(STORAGE_CHANGE_NOTIFIER_SRCS ContactsChangeNotifier.cpp ContactsChangeNotifier.h ContactsChangeNotifierPlugin.cpp ContactsChangeNotifierPlugin.h ) add_library(${STORAGE_CHANGE_NOTIFIER} MODULE ${STORAGE_CHANGE_NOTIFIER_SRCS} ) target_link_libraries(${STORAGE_CHANGE_NOTIFIER} ${BUTEOSYNCFW_LIBRARIES} Qt5::Core Qt5::Contacts ) #set_target_properties(${STORAGE_CHANGE_NOTIFIER} # PROPERTIES PREFIX "" #) include_directories( ${BUTEOSYNCFW_INCLUDE_DIRS} ) install(TARGETS ${STORAGE_CHANGE_NOTIFIER} LIBRARY DESTINATION ${BUTEOSYNCFW_PLUGIN_PATH} ) buteo-sync-plugins-contacts-0.1+16.04.20151211/COPYING.LGPL-2.10000644000015300001610000006364212632560105023364 0ustar pbuserpbgroup00000000000000 GNU LESSER GENERAL PUBLIC LICENSE Version 2.1, February 1999 Copyright (C) 1991, 1999 Free Software Foundation, Inc. 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. [This is the first released version of the Lesser GPL. It also counts as the successor of the GNU Library Public License, version 2, hence the version number 2.1.] Preamble The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public Licenses are intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This license, the Lesser General Public License, applies to some specially designated software packages--typically libraries--of the Free Software Foundation and other authors who decide to use it. You can use it too, but we suggest you first think carefully about whether this license or the ordinary General Public License is the better strategy to use in any particular case, based on the explanations below. When we speak of free software, we are referring to freedom of use, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish); that you receive source code or can get it if you want it; that you can change the software and use pieces of it in new free programs; and that you are informed that you can do these things. To protect your rights, we need to make restrictions that forbid distributors to deny you these rights or to ask you to surrender these rights. These restrictions translate to certain responsibilities for you if you distribute copies of the library or if you modify it. For example, if you distribute copies of the library, whether gratis or for a fee, you must give the recipients all the rights that we gave you. You must make sure that they, too, receive or can get the source code. If you link other code with the library, you must provide complete object files to the recipients, so that they can relink them with the library after making changes to the library and recompiling it. And you must show them these terms so they know their rights. We protect your rights with a two-step method: (1) we copyright the library, and (2) we offer you this license, which gives you legal permission to copy, distribute and/or modify the library. To protect each distributor, we want to make it very clear that there is no warranty for the free library. Also, if the library is modified by someone else and passed on, the recipients should know that what they have is not the original version, so that the original author's reputation will not be affected by problems that might be introduced by others. Finally, software patents pose a constant threat to the existence of any free program. We wish to make sure that a company cannot effectively restrict the users of a free program by obtaining a restrictive license from a patent holder. Therefore, we insist that any patent license obtained for a version of the library must be consistent with the full freedom of use specified in this license. Most GNU software, including some libraries, is covered by the ordinary GNU General Public License. This license, the GNU Lesser General Public License, applies to certain designated libraries, and is quite different from the ordinary General Public License. We use this license for certain libraries in order to permit linking those libraries into non-free programs. When a program is linked with a library, whether statically or using a shared library, the combination of the two is legally speaking a combined work, a derivative of the original library. The ordinary General Public License therefore permits such linking only if the entire combination fits its criteria of freedom. The Lesser General Public License permits more lax criteria for linking other code with the library. We call this license the "Lesser" General Public License because it does Less to protect the user's freedom than the ordinary General Public License. It also provides other free software developers Less of an advantage over competing non-free programs. These disadvantages are the reason we use the ordinary General Public License for many libraries. However, the Lesser license provides advantages in certain special circumstances. For example, on rare occasions, there may be a special need to encourage the widest possible use of a certain library, so that it becomes a de-facto standard. To achieve this, non-free programs must be allowed to use the library. A more frequent case is that a free library does the same job as widely used non-free libraries. In this case, there is little to gain by limiting the free library to free software only, so we use the Lesser General Public License. In other cases, permission to use a particular library in non-free programs enables a greater number of people to use a large body of free software. For example, permission to use the GNU C Library in non-free programs enables many more people to use the whole GNU operating system, as well as its variant, the GNU/Linux operating system. Although the Lesser General Public License is Less protective of the users' freedom, it does ensure that the user of a program that is linked with the Library has the freedom and the wherewithal to run that program using a modified version of the Library. The precise terms and conditions for copying, distribution and modification follow. Pay close attention to the difference between a "work based on the library" and a "work that uses the library". The former contains code derived from the library, whereas the latter must be combined with the library in order to run. GNU LESSER GENERAL PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. This License Agreement applies to any software library or other program which contains a notice placed by the copyright holder or other authorized party saying it may be distributed under the terms of this Lesser General Public License (also called "this License"). Each licensee is addressed as "you". A "library" means a collection of software functions and/or data prepared so as to be conveniently linked with application programs (which use some of those functions and data) to form executables. The "Library", below, refers to any such software library or work which has been distributed under these terms. A "work based on the Library" means either the Library or any derivative work under copyright law: that is to say, a work containing the Library or a portion of it, either verbatim or with modifications and/or translated straightforwardly into another language. (Hereinafter, translation is included without limitation in the term "modification".) "Source code" for a work means the preferred form of the work for making modifications to it. For a library, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the library. Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running a program using the Library is not restricted, and output from such a program is covered only if its contents constitute a work based on the Library (independent of the use of the Library in a tool for writing it). Whether that is true depends on what the Library does and what the program that uses the Library does. 1. You may copy and distribute verbatim copies of the Library's complete source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and distribute a copy of this License along with the Library. You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. 2. You may modify your copy or copies of the Library or any portion of it, thus forming a work based on the Library, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: a) The modified work must itself be a software library. b) You must cause the files modified to carry prominent notices stating that you changed the files and the date of any change. c) You must cause the whole of the work to be licensed at no charge to all third parties under the terms of this License. d) If a facility in the modified Library refers to a function or a table of data to be supplied by an application program that uses the facility, other than as an argument passed when the facility is invoked, then you must make a good faith effort to ensure that, in the event an application does not supply such function or table, the facility still operates, and performs whatever part of its purpose remains meaningful. (For example, a function in a library to compute square roots has a purpose that is entirely well-defined independent of the application. Therefore, Subsection 2d requires that any application-supplied function or table used by this function must be optional: if the application does not supply it, the square root function must still compute square roots.) These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Library, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Library, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Library. In addition, mere aggregation of another work not based on the Library with the Library (or with a work based on the Library) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. 3. You may opt to apply the terms of the ordinary GNU General Public License instead of this License to a given copy of the Library. To do this, you must alter all the notices that refer to this License, so that they refer to the ordinary GNU General Public License, version 2, instead of to this License. (If a newer version than version 2 of the ordinary GNU General Public License has appeared, then you can specify that version instead if you wish.) Do not make any other change in these notices. Once this change is made in a given copy, it is irreversible for that copy, so the ordinary GNU General Public License applies to all subsequent copies and derivative works made from that copy. This option is useful when you wish to copy part of the code of the Library into a program that is not a library. 4. You may copy and distribute the Library (or a portion or derivative of it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange. If distribution of object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place satisfies the requirement to distribute the source code, even though third parties are not compelled to copy the source along with the object code. 5. A program that contains no derivative of any portion of the Library, but is designed to work with the Library by being compiled or linked with it, is called a "work that uses the Library". Such a work, in isolation, is not a derivative work of the Library, and therefore falls outside the scope of this License. However, linking a "work that uses the Library" with the Library creates an executable that is a derivative of the Library (because it contains portions of the Library), rather than a "work that uses the library". The executable is therefore covered by this License. Section 6 states terms for distribution of such executables. When a "work that uses the Library" uses material from a header file that is part of the Library, the object code for the work may be a derivative work of the Library even though the source code is not. Whether this is true is especially significant if the work can be linked without the Library, or if the work is itself a library. The threshold for this to be true is not precisely defined by law. If such an object file uses only numerical parameters, data structure layouts and accessors, and small macros and small inline functions (ten lines or less in length), then the use of the object file is unrestricted, regardless of whether it is legally a derivative work. (Executables containing this object code plus portions of the Library will still fall under Section 6.) Otherwise, if the work is a derivative of the Library, you may distribute the object code for the work under the terms of Section 6. Any executables containing that work also fall under Section 6, whether or not they are linked directly with the Library itself. 6. As an exception to the Sections above, you may also combine or link a "work that uses the Library" with the Library to produce a work containing portions of the Library, and distribute that work under terms of your choice, provided that the terms permit modification of the work for the customer's own use and reverse engineering for debugging such modifications. You must give prominent notice with each copy of the work that the Library is used in it and that the Library and its use are covered by this License. You must supply a copy of this License. If the work during execution displays copyright notices, you must include the copyright notice for the Library among them, as well as a reference directing the user to the copy of this License. Also, you must do one of these things: a) Accompany the work with the complete corresponding machine-readable source code for the Library including whatever changes were used in the work (which must be distributed under Sections 1 and 2 above); and, if the work is an executable linked with the Library, with the complete machine-readable "work that uses the Library", as object code and/or source code, so that the user can modify the Library and then relink to produce a modified executable containing the modified Library. (It is understood that the user who changes the contents of definitions files in the Library will not necessarily be able to recompile the application to use the modified definitions.) b) Use a suitable shared library mechanism for linking with the Library. A suitable mechanism is one that (1) uses at run time a copy of the library already present on the user's computer system, rather than copying library functions into the executable, and (2) will operate properly with a modified version of the library, if the user installs one, as long as the modified version is interface-compatible with the version that the work was made with. c) Accompany the work with a written offer, valid for at least three years, to give the same user the materials specified in Subsection 6a, above, for a charge no more than the cost of performing this distribution. d) If distribution of the work is made by offering access to copy from a designated place, offer equivalent access to copy the above specified materials from the same place. e) Verify that the user has already received a copy of these materials or that you have already sent this user a copy. For an executable, the required form of the "work that uses the Library" must include any data and utility programs needed for reproducing the executable from it. However, as a special exception, the materials to be distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable. It may happen that this requirement contradicts the license restrictions of other proprietary libraries that do not normally accompany the operating system. Such a contradiction means you cannot use both them and the Library together in an executable that you distribute. 7. You may place library facilities that are a work based on the Library side-by-side in a single library together with other library facilities not covered by this License, and distribute such a combined library, provided that the separate distribution of the work based on the Library and of the other library facilities is otherwise permitted, and provided that you do these two things: a) Accompany the combined library with a copy of the same work based on the Library, uncombined with any other library facilities. This must be distributed under the terms of the Sections above. b) Give prominent notice with the combined library of the fact that part of it is a work based on the Library, and explaining where to find the accompanying uncombined form of the same work. 8. You may not copy, modify, sublicense, link with, or distribute the Library except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense, link with, or distribute the Library is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance. 9. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Library or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Library (or any work based on the Library), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Library or works based on it. 10. Each time you redistribute the Library (or any work based on the Library), the recipient automatically receives a license from the original licensor to copy, distribute, link with or modify the Library subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties with this License. 11. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Library at all. For example, if a patent license would not permit royalty-free redistribution of the Library by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Library. If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply, and the section as a whole is intended to apply in other circumstances. It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice. This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. 12. If the distribution and/or use of the Library is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Library under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License. 13. The Free Software Foundation may publish revised and/or new versions of the Lesser General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Library specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Library does not specify a license version number, you may choose any version ever published by the Free Software Foundation. 14. If you wish to incorporate parts of the Library into other free programs whose distribution conditions are incompatible with these, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. NO WARRANTY 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Libraries If you develop a new library, and you want it to be of the greatest possible use to the public, we recommend making it free software that everyone can redistribute and change. You can do so by permitting redistribution under these terms (or, alternatively, under the terms of the ordinary General Public License). To apply these terms, attach the following notices to the library. It is safest to attach them to the start of each source file to most effectively convey the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. Copyright (C) This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 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 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 Street, Fifth Floor, Boston, MA 02110-1301 USA Also add information on how to contact you by electronic and paper mail. You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for the library, if necessary. Here is a sample; alter the names: Yoyodyne, Inc., hereby disclaims all copyright interest in the library `Frob' (a library for tweaking knobs) written by James Random Hacker. , 1 April 1990 Ty Coon, President of Vice That's all there is to it! buteo-sync-plugins-contacts-0.1+16.04.20151211/accounts/0000755000015300001610000000000012632561656023116 5ustar pbuserpbgroup00000000000000buteo-sync-plugins-contacts-0.1+16.04.20151211/accounts/google-buteo-contacts.service0000644000015300001610000000026212632560105030670 0ustar pbuserpbgroup00000000000000 buteo-contacts Google Contacts google buteo-sync-plugins-contacts-0.1+16.04.20151211/accounts/CMakeLists.txt0000644000015300001610000000047312632560105025646 0ustar pbuserpbgroup00000000000000set(SERVICE_FILES google-buteo-contacts.service ) set(APPLICATION_FILES address-book-app.application ) install(FILES ${SERVICE_FILES} DESTINATION ${CMAKE_INSTALL_FULL_DATADIR}/accounts/services ) install(FILES ${APPLICATION_FILES} DESTINATION ${CMAKE_INSTALL_FULL_DATADIR}/accounts/applications ) buteo-sync-plugins-contacts-0.1+16.04.20151211/accounts/address-book-app.application0000644000015300001610000000052612632560105030465 0ustar pbuserpbgroup00000000000000 Contacts address-book-app.desktop Synchronize your contacts buteo-sync-plugins-contacts-0.1+16.04.20151211/CMakeLists.txt0000644000015300001610000000361212632560105024025 0ustar pbuserpbgroup00000000000000project(buteo-sync-plugins-google) cmake_minimum_required(VERSION 2.8.9) # Find includes in corresponding build directories set(CMAKE_INCLUDE_CURRENT_DIR ON) # Instruct CMake to run moc automatically when needed. set(CMAKE_AUTOMOC ON) # Standard install paths include(GNUInstallDirs) find_package(PkgConfig REQUIRED) find_package(Qt5Core REQUIRED) find_package(Qt5DBus REQUIRED) find_package(Qt5Contacts REQUIRED) find_package(Qt5Network REQUIRED) find_package(Qt5Versit REQUIRED) pkg_check_modules(BUTEOSYNCFW REQUIRED buteosyncfw5) pkg_check_modules(ACCOUNTS REQUIRED accounts-qt5>=1.10) pkg_check_modules(LIBSIGNON REQUIRED libsignon-qt5) set(BUTEOSYNCFW_PLUGIN_PATH "/usr/lib/buteo-plugins-qt5") # config file configure_file("${CMAKE_CURRENT_SOURCE_DIR}/config.h.in" "${CMAKE_CURRENT_BINARY_DIR}/config.h" IMMEDIATE @ONLY) # Coverage tools OPTION(ENABLE_COVERAGE "Build with coverage analysis support" OFF) if(ENABLE_COVERAGE) message(STATUS "Using coverage flags") find_program(COVERAGE_COMMAND gcov) if(NOT COVERAGE_COMMAND) message(FATAL_ERROR "gcov command not found") endif() SET(CMAKE_C_FLAGS "-g -O0 -Wall -fprofile-arcs -ftest-coverage") SET(CMAKE_CXX_FLAGS "-g -O0 -Wall -fprofile-arcs -ftest-coverage") SET(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -fprofile-arcs -ftest-coverage -lgcov") include(${CMAKE_SOURCE_DIR}/cmake/lcov.cmake) endif() enable_testing() add_subdirectory(storage-change-notifier) add_subdirectory(buteo-contact-client) add_subdirectory(google) add_subdirectory(accounts) add_subdirectory(tests) # uninstall target configure_file("${CMAKE_CURRENT_SOURCE_DIR}/cmake_uninstall.cmake.in" "${CMAKE_CURRENT_BINARY_DIR}/cmake_uninstall.cmake" IMMEDIATE @ONLY) add_custom_target(uninstall "${CMAKE_COMMAND}" -P "${CMAKE_CURRENT_BINARY_DIR}/cmake_uninstall.cmake") buteo-sync-plugins-contacts-0.1+16.04.20151211/buteo-contact-client/0000755000015300001610000000000012632561656025322 5ustar pbuserpbgroup00000000000000buteo-sync-plugins-contacts-0.1+16.04.20151211/buteo-contact-client/UContactsCustomDetail.h0000644000015300001610000000333512632560105031704 0ustar pbuserpbgroup00000000000000/* * This file is part of buteo-sync-plugins-contacts package * * Copyright (C) 2013 Jolla Ltd. and/or its subsidiary(-ies). * 2015 Canonical Ltd * * Contributors: Sateesh Kavuri * Mani Chandrasekar * Renato Araujo Oliveira Filho * * 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 #include #include 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 buteo-sync-plugins-contacts-0.1+16.04.20151211/buteo-contact-client/UAuth.h0000644000015300001610000000367512632560115026521 0ustar pbuserpbgroup00000000000000/* * This file is part of buteo-sync-plugins-contacts package * * Copyright (C) 2013 Jolla Ltd. and/or its subsidiary(-ies). * 2015 Canonical Ltd * * Contributors: Sateesh Kavuri * Mani Chandrasekar * Renato Araujo Oliveira Filho * * 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 UAUTH_H #define UAUTH_H #include #include #include #include #include #include class UAuthPrivate; class UAuth : public QObject { Q_OBJECT Q_DECLARE_PRIVATE(UAuth) public: explicit UAuth(QObject *parent = 0); ~UAuth(); virtual bool authenticate(); virtual bool init(const quint32 accountId, const QString serviceName); inline QString accountDisplayName() const { return mDisplayName; } inline QString token() const { return mToken; } signals: void success(); void failed(); void accountRemoved(); protected: QString mToken; QString mDisplayName; private: QScopedPointer d_ptr; private slots: void credentialsStored(const quint32); void error(const SignOn::Error &); void sessionResponse(const SignOn::SessionData &); }; #endif // UAUTH_H buteo-sync-plugins-contacts-0.1+16.04.20151211/buteo-contact-client/UContactsClient.cpp0000644000015300001610000010503512632560115031061 0ustar pbuserpbgroup00000000000000/* * This file is part of buteo-sync-plugins-contacts package * * Copyright (C) 2013 Jolla Ltd. and/or its subsidiary(-ies). * 2015 Canonical Ltd * * Contributors: Sateesh Kavuri * Mani Chandrasekar * Renato Araujo Oliveira Filho * * 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 #include #include #include #include #include #include #include #include class UContactsClientPrivate { public: UContactsClientPrivate(const QString &serviceName) : mAuth(0), mContactBackend(0), mRemoteSource(0), mSlowSync(false), mAborted(false), mServiceName(serviceName), mProgress(0), mAccountId(0), mSyncDirection(Buteo::SyncProfile::SYNC_DIRECTION_TWO_WAY), mConflictResPolicy(Buteo::SyncProfile::CR_POLICY_PREFER_REMOTE_CHANGES) { } UAuth* mAuth; UContactsBackend* mContactBackend; UAbstractRemoteSource* mRemoteSource; bool mSlowSync; bool mAborted; QString mServiceName; // local database information QSet mAllLocalContactIds; RemoteToLocalIdMap mAddedContactIds; RemoteToLocalIdMap mModifiedContactIds; RemoteToLocalIdMap mDeletedContactIds; // sync report QMap mItemResults; Buteo::SyncResults mResults; qreal mProgress; // 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); d->mProgress = 0.0; d->mAborted = false; 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; } LOG_INFO ("Init sync for account:" << d->mAccountId << d->mServiceName); 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) { 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(accountRemoved()), SLOT(onAccountRemoved())); 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(int)), SLOT(onStateChanged(int))); // Take necessary action when sync is finished connect(this, SIGNAL(syncFinished(Sync::SyncStatus)), SLOT(onSyncFinished(Sync::SyncStatus)), Qt::QueuedConnection); 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"); stateChanged(Sync::SYNC_PROGRESS_INITIALISING); return d->mAuth->authenticate(); } void UContactsClient::abortSync(Sync::SyncStatus aStatus) { FUNCTION_CALL_TRACE; Q_D(UContactsClient); d->mAborted = true; 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(); return true; } void UContactsClient::onAuthenticationError() { LOG_WARNING("Fail to authenticate with account"); emit syncFinished (Sync::SYNC_AUTHENTICATION_FAILURE); } void UContactsClient::onAccountRemoved() { Q_D(UContactsClient); LOG_WARNING("ABORT: Account removed while syncing"); d->mAborted = true; d->mRemoteSource->abort(); d->mContactBackend->removeSyncTarget(); emit syncFinished(Sync::SYNC_ABORTED); } 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..) LOG_INFO("Sync Started at:" << QDateTime::currentDateTime().toUTC().toString(Qt::ISODate)); if (d->mAborted) { LOG_WARNING("Sync aborted"); return false; } stateChanged(Sync::SYNC_PROGRESS_RECEIVING_ITEMS); if (!d->mRemoteSource->init(remoteSourceProperties())) { LOG_WARNING("Fail to init remote source"); return false; } if (!d->mContactBackend->init(d->mAccountId, d->mAuth->accountDisplayName())) { LOG_WARNING("Fail to init contact backend"); return false; } d->mItemResults.insert(syncTargetId(), Buteo::DatabaseResults()); 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,Sync::SyncStatus,qreal)), SLOT(onRemoteContactsFetchedForSlowSync(QList,Sync::SyncStatus,qreal))); } else { connect(d->mRemoteSource, SIGNAL(contactsFetched(QList,Sync::SyncStatus,qreal)), SLOT(onRemoteContactsFetchedForFastSync(QList,Sync::SyncStatus, qreal))); } 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 UContactsClient::prepareContactsToUpload(UContactsBackend *backend, const QSet &ids) { QList 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(); } } return toUpdate; } void UContactsClient::onRemoteContactsFetchedForSlowSync(const QList contacts, Sync::SyncStatus status, qreal progress) { FUNCTION_CALL_TRACE; Q_D(UContactsClient); if (d->mAborted) { LOG_WARNING("Sync aborted"); return; } 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) { stateChanged(Sync::SYNC_PROGRESS_SENDING_ITEMS); QList toUpload = prepareContactsToUpload(d->mContactBackend, d->mAllLocalContactIds); connect(d->mRemoteSource, SIGNAL(transactionCommited(QList, QList, QStringList, QMap, Sync::SyncStatus)), SLOT(onContactsSavedForSlowSync(QList, QList, QStringList, QMap, Sync::SyncStatus))); d->mRemoteSource->transaction(); d->mRemoteSource->saveContacts(toUpload); d->mRemoteSource->commit(); } else { stateChanged(qRound(progress * 100)); } } else { emit syncFinished(status); } } void UContactsClient::onContactsSavedForSlowSync(const QList &createdContacts, const QList &updatedContacts, const QStringList &removedContacts, const QMap errorMap, Sync::SyncStatus status) { FUNCTION_CALL_TRACE; Q_D(UContactsClient); if (d->mAborted) { LOG_WARNING("Sync aborted"); return; } LOG_DEBUG("AFTER UPLOAD(Slow sync):" << "\n\tCreated on remote:" << createdContacts.size() << "\n\tUpdated on remote:" << updatedContacts.size() << "\n\tRemoved from remote:" << removedContacts.size() << "\n\tError reported:" << errorMap.size()); if ((status != Sync::SYNC_PROGRESS) && (status != Sync::SYNC_STARTED)) { disconnect(d->mRemoteSource); } if ((status == Sync::SYNC_PROGRESS) || (status == Sync::SYNC_DONE)) { QList changedContacts; changedContacts += createdContacts; changedContacts += updatedContacts; updateIdsToLocal(changedContacts); handleError(errorMap); // sync report addProcessedItem(Sync::ITEM_ADDED, Sync::REMOTE_DATABASE, syncTargetId(), createdContacts.size()); if (status == Sync::SYNC_PROGRESS) { // sync still in progress return; } else { stateChanged(Sync::SYNC_PROGRESS_FINALISING); // 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 contacts, Sync::SyncStatus status, qreal progress) { FUNCTION_CALL_TRACE; Q_D(UContactsClient); if (d->mAborted) { LOG_WARNING("Sync aborted"); return; } 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) { stateChanged(Sync::SYNC_PROGRESS_SENDING_ITEMS); QList contactsToUpload; QList 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, QList, QStringList, QMap, Sync::SyncStatus)), SLOT(onContactsSavedForFastSync(QList, QList, QStringList, QMap, Sync::SyncStatus))); d->mRemoteSource->transaction(); d->mRemoteSource->saveContacts(contactsToUpload); d->mRemoteSource->removeContacts(contactsToRemove); d->mRemoteSource->commit(); } else { stateChanged(qRound(progress*100)); } } else { emit syncFinished(status); } } void UContactsClient::onContactsSavedForFastSync(const QList &createdContacts, const QList &updatedContacts, const QStringList &removedContacts, const QMap errorMap, Sync::SyncStatus status) { Q_UNUSED(updatedContacts) Q_UNUSED(removedContacts) FUNCTION_CALL_TRACE; Q_D(UContactsClient); if (d->mAborted) { LOG_WARNING("Sync aborted"); return; } 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() << "\n\tError reported:" << errorMap.size()); if ((status != Sync::SYNC_PROGRESS) && (status != Sync::SYNC_STARTED)) { disconnect(d->mRemoteSource); } if ((status == Sync::SYNC_PROGRESS) || (status == Sync::SYNC_DONE)) { QList changedContacts; changedContacts += createdContacts; changedContacts += updatedContacts; updateIdsToLocal(changedContacts); handleError(errorMap); // we need to call delete again because of error handling d->mContactBackend->deleteContacts(removedContacts); // 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 { stateChanged(Sync::SYNC_PROGRESS_FINALISING); // 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::fireSyncFinishedSucessfully() { emit syncFinished(Sync::SYNC_DONE); } void UContactsClient::handleError(const QMap &errorMap) { Q_D(UContactsClient); QStringList contactToRemove; QMap::const_iterator i = errorMap.begin(); while(i != errorMap.end()) { switch(i.value()) { case QContactManager::DoesNotExistError: // if the contact does not exists on remote side we will remove it locally LOG_DEBUG("Romoving contact locally due the remote error:" << i.key()); contactToRemove << i.key(); break; default: LOG_WARNING("Unexpected error:" << i.value()); break; } i++; } if (!contactToRemove.isEmpty()) { d->mContactBackend->deleteContacts(contactToRemove); } } bool UContactsClient::storeToLocalForSlowSync(const QList &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 statusMap; QList cpyContacts(remoteContacts); if (d->mContactBackend->addContacts(cpyContacts, &statusMap)) { // TODO: Saving succeeded. Update sync results 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 syncSuccess = false; } } return syncSuccess; } bool UContactsClient::storeToLocalForFastSync(const QList &remoteContacts) { FUNCTION_CALL_TRACE; Q_D(UContactsClient); Q_ASSERT(!d->mSlowSync); bool syncSuccess = false; LOG_DEBUG ("@@@storeToLocal#FAST SYNC"); QList remoteAddedContacts, remoteModifiedContacts, remoteDeletedContacts; filterRemoteAddedModifiedDeletedContacts(remoteContacts, remoteAddedContacts, remoteModifiedContacts, remoteDeletedContacts); resolveConflicts(remoteModifiedContacts, remoteDeletedContacts); if (!remoteAddedContacts.isEmpty()) { LOG_DEBUG ("***Adding " << remoteAddedContacts.size() << " contacts"); QMap 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 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; imContactBackend->localIds(guidList); QMap 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 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(int aState) { FUNCTION_CALL_TRACE; emit syncProgressDetail(getProfileName(), aState); } void UContactsClient::onSyncFinished(Sync::SyncStatus aState) { FUNCTION_CALL_TRACE; Q_D(UContactsClient); LOG_INFO("Sync finished with state:" << aState); 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(lastSyncTime()); 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; } } } 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(3).toUTC(); } else { return lastTime; } } void UContactsClient::filterRemoteAddedModifiedDeletedContacts(const QList remoteContacts, QList &remoteAddedContacts, QList &remoteModifiedContacts, QList &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 &modifiedRemoteContacts, QList &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 //QListIterator iter (modifiedRemoteContacts); QList::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 &contacts) { FUNCTION_CALL_TRACE; Q_D(UContactsClient); QList 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; } } emit transferProgress(getProfileName(), database, modificationType, "text/vcard", count); } void UContactsClient::generateResults(bool aSuccessful) { FUNCTION_CALL_TRACE; Q_D(UContactsClient); d->mResults = Buteo::SyncResults(); d->mResults.setMajorCode(aSuccessful ? Buteo::SyncResults::SYNC_RESULT_SUCCESS : Buteo::SyncResults::SYNC_RESULT_FAILED ); d->mResults.setTargetId(iProfile.name()); if (d->mItemResults.isEmpty()) { LOG_INFO("No items transferred"); } else { QMapIterator 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_INFO("Sync finished at:" << d->mResults.syncTime().toUTC().toString(Qt::ISODate)); LOG_INFO("Items for" << targetResults.targetName() << ":"); LOG_INFO("LA:" << targetResults.localItems().added << "LD:" << targetResults.localItems().deleted << "LM:" << targetResults.localItems().modified << "RA:" << targetResults.remoteItems().added << "RD:" << targetResults.remoteItems().deleted << "RM:" << targetResults.remoteItems().modified); } } } buteo-sync-plugins-contacts-0.1+16.04.20151211/buteo-contact-client/UContactsBackend.h0000644000015300001610000001777312632560115030652 0ustar pbuserpbgroup00000000000000/* * This file is part of buteo-sync-plugins-contacts package * * Copyright (C) 2013 Jolla Ltd. and/or its subsidiary(-ies). * 2015 Canonical Ltd * * Contributors: Sateesh Kavuri * Mani Chandrasekar * Renato Araujo Oliveira Filho * * 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 UCONTACTSBACKEND_H_ #define UCONTACTSBACKEND_H_ #include #include #include #include #include #include #include QTCONTACTS_USE_NAMESPACE struct UContactsStatus { int id; QContactManager::Error errorCode; }; typedef QMultiMap RemoteToLocalIdMap; //! \brief Harmattan Contact storage plugin backend interface class /// /// This class interfaces with the QtContact backend implementation class UContactsBackend : public QObject { public: explicit UContactsBackend(const QString &managerName = "", QObject* parent = 0); /*! * \brief Destructor */ ~UContactsBackend(); /*! * \brief Initialize the backend, must be called before any other function * \param syncTarget The name of the collection used to store contacts * \return Returns true if initialized with sucess false otherwise */ bool init(uint syncAccount, const QString &syncTarget); /*! * \brief releases the resources held. * @returnReturns true if sucess false otherwise */ bool uninit(); /*! * \brief Return ids of all contacts stored locally * @return List of contact IDs */ QList getAllContactIds(); /*! * \brief Return all new contacts ids in a QList of QStrings * @param aTimeStamp Timestamp of the oldest contact ID to be returned * @return List of contact IDs */ RemoteToLocalIdMap getAllNewContactIds(const QDateTime& aTimeStamp); /*! * \brief Return all modified contact ids in a QList of QStrings * @param aTimeStamp Timestamp of the oldest contact ID to be returned * @return List of contact IDs */ RemoteToLocalIdMap getAllModifiedContactIds(const QDateTime& aTimeStamp); /*! * \brief Return all deleted contacts ids in a QList of QStrings * @param aTimeStamp Timestamp of the oldest contact ID to be returned * @return List of contact IDs */ RemoteToLocalIdMap getAllDeletedContactIds(const QDateTime& aTimeStamp); /*! * \brief Get contact data for a given contact ID as a QContact object * @param aContactId The ID of the contact * @return The data of the contact */ QContact getContact(const QContactId& aContactId); /*! * \brief Returns a contact for the specified remoteId * @param remoteId The remote id of the contact to be returned * @return The data of the contact */ QContact getContact(const QString& remoteId); /*! * \brief Batch addition of contacts * @param aContactDataList Contact data * @param aStatusMap Returned status data * @return Errors */ bool addContacts(QList& aContactList, QMap *aStatusMap ); // Functions for modifying contacts /*! * \brief Batch modification * @param aContactDataList Contact data * @param aContactsIdList Contact IDs * @return Errors */ QMap modifyContacts(QList *aContactList); /*! * \brief Batch deletion of contacts * @param aContactIDList Contact IDs * @return Errors */ QMap deleteContacts(const QStringList &aContactIDList); /*! * \brief Batch deletion of contacts * @param aContactIDList Contact IDs * @return Errors */ QMap deleteContacts(const QList &aContactIDList); /*! * \brief Check if a contact exists * \param remoteId The remoteId of the contact * \return The localId of the contact */ QContactId entryExists(const QString remoteId); /*! * \brief Retrieve the current address book used by the backend * \return The address book id */ QString syncTargetId() const; /*! * \brief Retrieve the local id of a list of remote ids * \param remoteIds A list with remote ids * \return A list with local ids */ const QStringList localIds(const QStringList remoteIds); /*! * \brief Return the value of the remote id field in a contact * \param contact The contact object * \return A string with the remoteId */ static QString getRemoteId(const QContact &contact); /*! * \brief Update the value of the remote id field in a contact * \param contact The contact object * \param remoteId A string with the remoteId */ static void setRemoteId(QContact &contact, const QString &remoteId); /*! * \brief Return the valueof the local id field in a contact * \param contact The contact object * \return A string with the localId */ static QString getLocalId(const QContact &contact); /*! * \brief Update the value of the local id field in a contact * \param contact The contact object * \param remoteId A string with the localId */ static void setLocalId(QContact &contact, const QString &localId); /*! * \brief Check if the contact is marked as deleted * \param contact The contact object * \return Returns true if the contact is marked as deleted, otherwise returns false */ static bool deleted(const QContact &contact); /*! * \brief Purge all deleted contacts from the server */ void purgecontacts(const QDateTime &date); /*! * \brief Reload contact id cache */ void reloadCache(); /*! * \brief Remove backend source */ void removeSyncTarget(); QContactManager *manager() const; private: // functions /*! * \brief Returns contact IDs specified by event type and timestamp * @param aEventType Added/changed/removed contacts * @param aTimeStamp Contacts older than aTimeStamp are filtered out * @param aIdList Returned contact IDs */ void getSpecifiedContactIds(const QContactChangeLogFilter::EventType aEventType, const QDateTime &aTimeStamp, RemoteToLocalIdMap *aIdList); /*! * \brief Constructs and returns the filter for accessing only contacts allowed to be synchronized * Contacts not allowed to be synchronized are Instant messaging contacts and contacts with origin from other sync backends; * those contacts have QContactSyncTarget::SyncTarget value different from address book or buteo sync clients. * It is designed that buteo sync clients don't restrict access to contacts among themselves * - value for QContactSyncTarget::SyncTarget written by this backend is "buteo". */ QContactFilter getSyncTargetFilter() const; private: // data // if there is more than one Manager we need to have a list of Managers QContactManager *iMgr; ///< A pointer to contact manager QString mSyncTargetId; QMap mRemoteIdToLocalId; void createSourceForAccount(uint accountId, const QString &label); }; #endif /* CONTACTSBACKEND_H_ */ buteo-sync-plugins-contacts-0.1+16.04.20151211/buteo-contact-client/UAuth.cpp0000644000015300001610000001267512632560115027054 0ustar pbuserpbgroup00000000000000/* * This file is part of buteo-sync-plugins-contacts package * * Copyright (C) 2013 Jolla Ltd. and/or its subsidiary(-ies). * 2015 Canonical Ltd * * Contributors: Sateesh Kavuri * Mani Chandrasekar * Renato Araujo Oliveira Filho * * 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 "UAuth.h" #include #include #include #include #include #include #include #include using namespace Accounts; using namespace SignOn; class UAuthPrivate { public: UAuthPrivate() {} ~UAuthPrivate() {} QPointer mAccountManager; QPointer mIdentity; QPointer mSession; QPointer mAccount; QString mServiceName; }; UAuth::UAuth(QObject *parent) : QObject(parent), d_ptr(new UAuthPrivate) { } UAuth::~UAuth() { } bool UAuth::init(const quint32 accountId, const QString serviceName) { Q_D(UAuth); if ((accountId == 0) || serviceName.isEmpty()) { LOG_DEBUG("Invalid account id or service name:" << accountId << serviceName); return false; } d->mServiceName = serviceName; if (d->mAccountManager && d_ptr->mAccount) { LOG_DEBUG("GAuth already initialized"); return false; } if (!d->mAccountManager) { d->mAccountManager = new Accounts::Manager(); if (d->mAccountManager == NULL) { LOG_DEBUG("Account manager is not created... Cannot authenticate"); return false; } } if (!d->mAccount) { d->mAccount = Accounts::Account::fromId(d->mAccountManager.data(), accountId, this); if (d->mAccount == NULL) { LOG_DEBUG("Account is not created... Cannot authenticate"); return false; } connect(d->mAccount.data(), SIGNAL(removed()), SIGNAL(accountRemoved())); mDisplayName = d->mAccount->displayName(); } return true; } void UAuth::sessionResponse(const SessionData &sessionData) { SignOn::AuthSession *session = qobject_cast(sender()); Q_ASSERT(session); session->disconnect(this); mToken = sessionData.getProperty(QStringLiteral("AccessToken")).toString(); LOG_DEBUG("Authenticated !!!"); emit success(); } bool UAuth::authenticate() { Q_D(UAuth); if (d->mSession) { LOG_WARNING(QString("error: Account %1 Authenticate already requested") .arg(d->mAccount->displayName())); return true; } Accounts::Service srv(d->mAccountManager->service(d->mServiceName)); if (!srv.isValid()) { LOG_WARNING(QString("error: Service [%1] not found for account [%2].") .arg(d->mServiceName) .arg(d->mAccount->displayName())); return false; } d->mAccount->selectService(srv); Accounts::AccountService *accSrv = new Accounts::AccountService(d->mAccount, srv); if (!accSrv) { LOG_WARNING(QString("error: Account %1 has no valid account service") .arg(d->mAccount->displayName())); return false; } if (!accSrv->isEnabled()) { LOG_WARNING(QString("error: Service %1 not enabled for account %2.") .arg(d->mServiceName) .arg(d->mAccount->displayName())); return false; } AuthData authData = accSrv->authData(); d->mIdentity = SignOn::Identity::existingIdentity(authData.credentialsId()); if (!d->mIdentity) { LOG_WARNING(QString("error: Account %1 has no valid credentials") .arg(d->mAccount->displayName())); return false; } d->mSession = d->mIdentity->createSession(authData.method()); if (!d->mSession) { LOG_WARNING(QString("error: could not create signon session for Google account %1") .arg(d->mAccount->displayName())); accSrv->deleteLater(); return false; } connect(d->mSession.data(),SIGNAL(response(SignOn::SessionData)), SLOT(sessionResponse(SignOn::SessionData)), Qt::QueuedConnection); connect(d->mSession.data(), SIGNAL(error(SignOn::Error)), SLOT(error(SignOn::Error)), Qt::QueuedConnection); QVariantMap signonSessionData = authData.parameters(); signonSessionData.insert("UiPolicy", SignOn::NoUserInteractionPolicy); d->mSession->process(signonSessionData, authData.mechanism()); accSrv->deleteLater(); return true; } void UAuth::credentialsStored(const quint32 id) { Q_D(UAuth); d->mAccount->setCredentialsId(id); d->mAccount->sync(); } void UAuth::error(const SignOn::Error & error) { LOG_WARNING("LOGIN ERROR:" << error.message()); emit failed(); } buteo-sync-plugins-contacts-0.1+16.04.20151211/buteo-contact-client/UContactsClient.h0000644000015300001610000001476612632560115030540 0ustar pbuserpbgroup00000000000000/* * This file is part of buteo-sync-plugins-contacts package * * Copyright (C) 2013 Jolla Ltd. and/or its subsidiary(-ies). * 2015 Canonical Ltd * * Contributors: Sateesh Kavuri * Mani Chandrasekar * Renato Araujo Oliveira Filho * * 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 #define UCONTACSTCLIENT_H #include #include #include #include #include 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(int progress); void itemProcessed(Sync::TransferType type, Sync::TransferDatabase db, int committedItems); void syncFinished(Sync::SyncStatus); private: QScopedPointer d_ptr; void loadLocalContacts(const QDateTime &since); bool initConfig(); void generateResults(bool aSuccessful); void updateIdsToLocal(const QList &contacts); void filterRemoteAddedModifiedDeletedContacts(const QList remoteContacts, QList &remoteAddedContacts, QList &remoteModifiedContacts, QList &remoteDeletedContacts); void resolveConflicts(QList &modifiedRemoteContacts, QList &deletedRemoteContacts); void addProcessedItem(Sync::TransferType modificationType, Sync::TransferDatabase database, const QString &modifiedDatabase, int count = 1); QList prepareContactsToUpload(UContactsBackend *backend, const QSet &ids); /* slow sync */ bool storeToLocalForSlowSync(const QList &remoteContacts); /* fast sync */ bool storeToLocalForFastSync(const QList &remoteContacts); private slots: bool start(); void onAuthenticationError(); void onAccountRemoved(); void onStateChanged(int progress); void onSyncFinished(Sync::SyncStatus status); void fireSyncFinishedSucessfully(); void handleError(const QMap &errorMap); /* slow sync */ void onRemoteContactsFetchedForSlowSync(const QList contacts, Sync::SyncStatus status, qreal progress); void onContactsSavedForSlowSync(const QList &createdContacts, const QList &updatedContacts, const QStringList &removedContacts, const QMap errorList, Sync::SyncStatus status); /* fast sync */ void onRemoteContactsFetchedForFastSync(const QList contacts, Sync::SyncStatus status, qreal progress); void onContactsSavedForFastSync(const QList &createdContacts, const QList &updatedContacts, const QStringList &removedContacts, const QMap errorMap, Sync::SyncStatus status); }; #endif // UCONTACTCLIENT_H buteo-sync-plugins-contacts-0.1+16.04.20151211/buteo-contact-client/UAbstractRemoteSource.cpp0000644000015300001610000001056412632560105032245 0ustar pbuserpbgroup00000000000000/* * This file is part of buteo-sync-plugins-contacts package * * Copyright (C) 2015 Canonical Ltd * * Contributors: Renato Araujo Oliveira Filho * * 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 "UAbstractRemoteSource.h" #include "UContactsBackend.h" #include QTCONTACTS_USE_NAMESPACE class UContactsBackendBatchOperation { public: enum BatchOperation { BATCH_UPDATE = 0, BATCH_DELETE, BATCH_CREATE, BATCH_NONE, }; UContactsBackendBatchOperation() : m_op(BATCH_NONE) { } UContactsBackendBatchOperation(BatchOperation op, const QContact &contact) : m_op(op), m_contact(contact) { } BatchOperation operation() const { return m_op; } QContact contact() const { return m_contact; } bool isValid() const { return (m_op != BATCH_NONE); } private: BatchOperation m_op; QContact m_contact; }; class UAbstractRemoteSourcePrivate { public: UAbstractRemoteSourcePrivate() : m_batchMode(false) { } bool m_batchMode; QList m_operations; }; UAbstractRemoteSource::UAbstractRemoteSource(QObject *parent) : QObject(parent), d_ptr(new UAbstractRemoteSourcePrivate) { } UAbstractRemoteSource::~UAbstractRemoteSource() { } void UAbstractRemoteSource::transaction() { Q_D(UAbstractRemoteSource); d->m_batchMode = true; } bool UAbstractRemoteSource::commit() { Q_D(UAbstractRemoteSource); if (d->m_operations.isEmpty()) { transactionCommited(QList(), QList(), QStringList(), QMap(), Sync::SYNC_DONE); return true; } QList create; QList update; QList remove; foreach(const UContactsBackendBatchOperation &op, d->m_operations) { switch(op.operation()) { case UContactsBackendBatchOperation::BATCH_CREATE: create << op.contact(); break; case UContactsBackendBatchOperation::BATCH_UPDATE: update << op.contact(); break; case UContactsBackendBatchOperation::BATCH_DELETE: remove << op.contact(); break; default: qWarning() << "Invalid operation"; } } batch(create, update, remove); d->m_operations.clear(); d->m_batchMode = false; return true; } bool UAbstractRemoteSource::rollback() { Q_D(UAbstractRemoteSource); d->m_operations.clear(); d->m_batchMode = false; return true; } void UAbstractRemoteSource::saveContacts(const QList &contacts) { Q_D(UAbstractRemoteSource); if (d->m_batchMode) { foreach(const QContact &contact, contacts) { UContactsBackendBatchOperation::BatchOperation op; QString remoteId = UContactsBackend::getRemoteId(contact); op = remoteId.isEmpty() ? UContactsBackendBatchOperation::BATCH_CREATE : UContactsBackendBatchOperation::BATCH_UPDATE; d->m_operations << UContactsBackendBatchOperation(op, contact); } } else { saveContactsNonBatch(contacts); } } void UAbstractRemoteSource::removeContacts(const QList &contacts) { Q_D(UAbstractRemoteSource); if (d->m_batchMode) { foreach(const QContact &contact, contacts) { d->m_operations << UContactsBackendBatchOperation(UContactsBackendBatchOperation::BATCH_DELETE, contact); } } else { removeContactsNonBatch(contacts); } } buteo-sync-plugins-contacts-0.1+16.04.20151211/buteo-contact-client/UContactsCustomDetail.cpp0000644000015300001610000000407012632560105032234 0ustar pbuserpbgroup00000000000000/* * This file is part of buteo-sync-plugins-contacts package * * Copyright (C) 2013 Jolla Ltd. and/or its subsidiary(-ies). * 2015 Canonical Ltd * * Contributors: Sateesh Kavuri * Mani Chandrasekar * Renato Araujo Oliveira Filho * * 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 "UContactsCustomDetail.h" // Ubuntu fields const QString UContactsCustomDetail::FieldContactETag = "X-GOOGLE-ETAG"; const QString UContactsCustomDetail::FieldGroupMembershipInfo = "X-GROUP-ID"; const QString UContactsCustomDetail::FieldRemoteId = "X-REMOTE-ID"; const QString UContactsCustomDetail::FieldDeletedAt = "X-DELETED-AT"; const QString UContactsCustomDetail::FieldCreatedAt = "X-CREATED-AT"; const QString UContactsCustomDetail::FieldContactAvatarETag = "X-AVATAR-REV"; QContactExtendedDetail UContactsCustomDetail::getCustomField(const QContact &contact, const QString &name) { foreach (QContactExtendedDetail xd, contact.details()) { if (xd.name() == name) { return xd; } } QContactExtendedDetail xd; xd.setName(name); return xd; } void UContactsCustomDetail::setCustomField(QtContacts::QContact &contact, const QString &name, const QVariant &value) { QContactExtendedDetail xd = getCustomField(contact, name); xd.setData(value); contact.saveDetail(&xd); } buteo-sync-plugins-contacts-0.1+16.04.20151211/buteo-contact-client/CMakeLists.txt0000644000015300001610000000140212632560105030043 0ustar pbuserpbgroup00000000000000project(buteo-contact-client) set(UBUNTU_CONTACTS_CLIENT ubuntu-contact-client) set(UBUNTU_CONTACTS_CLIENT_SRCS UAbstractRemoteSource.h UAbstractRemoteSource.cpp UAuth.cpp UAuth.h UContactsBackend.cpp UContactsBackend.h UContactsClient.cpp UContactsClient.h UContactsCustomDetail.cpp UContactsCustomDetail.h ) add_library(${UBUNTU_CONTACTS_CLIENT} STATIC ${UBUNTU_CONTACTS_CLIENT_SRCS} ) include_directories( ${CMAKE_BINARY_DIR} ${ACCOUNTS_INCLUDE_DIRS} ${BUTEOSYNCFW_INCLUDE_DIRS} ${LIBSIGNON_INCLUDE_DIRS} ) target_link_libraries(${UBUNTU_CONTACTS_CLIENT} Qt5::Core Qt5::Network Qt5::DBus Qt5::Contacts ${ACCOUNTS_LIBRARIES} ${BUTEOSYNCFW_LIBRARIES} ${LIBSIGNON_LIBRARIES} ) buteo-sync-plugins-contacts-0.1+16.04.20151211/buteo-contact-client/UAbstractRemoteSource.h0000644000015300001610000001040612632560105031705 0ustar pbuserpbgroup00000000000000/* * This file is part of buteo-sync-plugins-contacts package * * Copyright (C) 2015 Canonical Ltd * * Contributors: Renato Araujo Oliveira Filho * * 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 UABSTRACTREMOTESOURCE_H #define UABSTRACTREMOTESOURCE_H #include #include #include #include #include class UAbstractRemoteSourcePrivate; class UAbstractRemoteSource : public QObject { Q_OBJECT Q_DECLARE_PRIVATE(UAbstractRemoteSource) public: UAbstractRemoteSource(QObject *parent = 0); ~UAbstractRemoteSource(); virtual void abort() = 0; virtual bool init(const QVariantMap &properties) = 0; virtual void fetchContacts(const QDateTime &since, bool includeDeleted, bool fetchAvatar = true) = 0; /*! * \brief Begins a transaction on the remote database. */ void transaction(); /*! * \brief Commits a transaction to the remote database. */ bool commit(); /*! * \brief Rolls back a transaction on the remote database. */ bool rollback(); virtual void saveContacts(const QList &contacts); virtual void removeContacts(const QList &contacts); signals: void contactsFetched(const QList &contacts, Sync::SyncStatus status, qreal progress); /*! * \brief This signal is emitted, when a remote contact is created * \param contacts A list of created contacts * \param status The operation status */ void contactsCreated(const QList &contacts, Sync::SyncStatus status); /*! * \brief This signal is emitted, when a remote contact is changed * \param contacts A list of changed contacts * \param status The operation status */ void contactsChanged(const QList &contacts, Sync::SyncStatus status); /*! * \brief This signal is emitted, when a remote contact is removed * \param ids A list with remoteId of removed contacts * \param status The operation status */ void contactsRemoved(const QStringList &ids, Sync::SyncStatus status); /*! * \brief This signal is emitted, when a batch operation finishes * \param createdContacts A list of created contacts * \param updatedContacts A list of updated contacts * \param removedContacts A list with remoteId of removed contacts * \param errorMap A map with the list of errors found during the batch operation * the key value contains the contact local id and the value is a int based * on QContactManager::Error enum * \param status The operation status */ void transactionCommited(const QList &createdContacts, const QList &updatedContacts, const QStringList &removedContacts, const QMap &errorMap, Sync::SyncStatus status); protected: virtual void batch(const QList &contactsToCreate, const QList &contactsToUpdate, const QList &contactsToRemove) = 0; virtual void saveContactsNonBatch(const QList contacts) = 0; virtual void removeContactsNonBatch(const QList contacts) = 0; private: QScopedPointer d_ptr; }; Q_DECLARE_METATYPE(QList) #endif // UABSTRACTREMOTESOURCE_H buteo-sync-plugins-contacts-0.1+16.04.20151211/buteo-contact-client/UContactsBackend.cpp0000644000015300001610000004515612632560115031201 0ustar pbuserpbgroup00000000000000/* * This file is part of buteo-sync-plugins-contacts package * * Copyright (C) 2013 Jolla Ltd. and/or its subsidiary(-ies). * 2015 Canonical Ltd * * Contributors: Sateesh Kavuri * Mani Chandrasekar * Renato Araujo Oliveira Filho * * 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 #include #include #include #include #include #include #include #include #include #include #include #include #include #include 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) { QMap parameters; parameters.insert("show-invisible", "true"); iMgr = new QContactManager(managerName, parameters); 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 sources = iMgr->contacts(filter); // WORKAROUND: sometimes EDS crash while querying for sources we use a second // query to make sure that the source does not exists. // The crash happens on sqlite3 library, check bug #1517252 for more info // FIXME: Remove this when bug #1517252 get fixed. if (sources.isEmpty()) { 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().guid(); reloadCache(); 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; //WORKAROUND: append a extra space in the end of the source name //this is necessary to avoid conflict with old sources used by syncevolution //SyncEvolution sync profile sync contacts from gooble based on the source name //creating a new source with the same name could cause the sync to happen in the //wrong source //FIXME: remove this when syncevolution get removed from image label.setLabel(syncTarget + " "); contact.saveDetail(&label); // 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().guid(); } return true; } bool UContactsBackend::uninit() { FUNCTION_CALL_TRACE; mRemoteIdToLocalId.clear(); return true; } QList 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& aContactList, QMap *aStatusMap) { FUNCTION_CALL_TRACE; Q_ASSERT(iMgr); Q_ASSERT(aStatusMap); QMap 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(); syncTarget.setSyncTarget(syncTargetId()); c.saveDetail(&syncTarget); } // remove guid field if it exists QContactGuid guid = c.detail(); 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; // update remote id map const QContact &c = aContactList.at(i); QString remoteId = getRemoteId(c); mRemoteIdToLocalId.insert(remoteId, c.id()); } 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 UContactsBackend::modifyContacts(QList *aContactList) { FUNCTION_CALL_TRACE; Q_ASSERT (iMgr); UContactsStatus status; QMap errors; QMap 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 QContactGuid guid = newContact.detail(); 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++) { const QContact &c = aContactList->at(i); QContactId contactId = c.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); // update remote it map QString oldRemoteId = mRemoteIdToLocalId.key(c.id()); mRemoteIdToLocalId.remove(oldRemoteId); QString remoteId = getRemoteId(c); mRemoteIdToLocalId.insert(remoteId, c.id()); } 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 UContactsBackend::deleteContacts(const QStringList &aContactIDList) { FUNCTION_CALL_TRACE; QList qContactIdList; foreach (QString id, aContactIDList) { qContactIdList.append(QContactId::fromString(id)); } return deleteContacts(qContactIdList); } QMap UContactsBackend::deleteContacts(const QList &aContactIDList) { FUNCTION_CALL_TRACE; Q_ASSERT (iMgr); UContactsStatus status; QMap errors; QMap statusMap; if(aContactIDList.isEmpty() || iMgr->removeContacts(aContactIDList , &errors)) { LOG_DEBUG("Successfully Removed all contacts "); } else { LOG_WARNING("Failed Removing Contacts" << errors); } // 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++) { const QContactId &contactId = aContactIDList.at(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); // remove from remote id map QString remoteId = mRemoteIdToLocalId.key(contactId); mRemoteIdToLocalId.remove(remoteId); } else { QContactManager::Error errorCode = errors.value(i); LOG_WARNING("Removing contact with id " << contactId << " and index " << i <<" is an error" << errorCode); 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 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 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 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 detailTypes; detailTypes << QContactExtendedDetail::Type; remoteIdHint.setDetailTypesHint(detailTypes); QList 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 returnedContacts; LOG_DEBUG("Contact ID to be retreived = " << aContactId.toString()); returnedContacts = iMgr->contacts(QList() << 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); // use contact id if possible QContactId cId = entryExists(remoteId); if (!cId.isNull()) { return iMgr->contact(cId); } return QContact(); } QContactId UContactsBackend::entryExists(const QString remoteId) { if (remoteId.isEmpty()) { return QContactId(); } // check cache return mRemoteIdToLocalId.value(remoteId); } 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; } void UContactsBackend::reloadCache() { FUNCTION_CALL_TRACE; QContactFetchHint hint; QList sortOrder; QContactFilter sourceFilter; if (!mSyncTargetId.isEmpty()) { sourceFilter = getSyncTargetFilter(); } mRemoteIdToLocalId.clear(); hint.setDetailTypesHint(QList() << QContactExtendedDetail::Type); Q_FOREACH(const QContact &c, iMgr->contacts(sourceFilter, sortOrder, hint)) { QString remoteId = getRemoteId(c); if (!remoteId.isEmpty()) { mRemoteIdToLocalId.insert(remoteId, c.id()); } } } void UContactsBackend::removeSyncTarget() { if (iMgr && !mSyncTargetId.isEmpty()) { iMgr->removeContact(QContactId::fromString(QString("qtcontacts:galera::source@%1").arg(mSyncTargetId))); } } 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(); return guid.guid(); } void UContactsBackend::setLocalId(QContact &contact, const QString &localId) { QContactGuid guid = contact.detail(); 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(const QDateTime &date) { QDBusInterface iface(CPIM_SERVICE_NAME, CPIM_ADDRESSBOOK_OBJECT_PATH, CPIM_ADDRESSBOOK_IFACE_NAME); QDBusReply reply = iface.call("purgeContacts", date.toString(Qt::ISODate), 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 detailFilterDefaultSyncTarget; } QtContacts::QContactManager *UContactsBackend::manager() const { return iMgr; }