diff options
Diffstat (limited to 'buteo-plugins/buteo-common/socialdbuteoplugin.cpp')
| -rw-r--r-- | buteo-plugins/buteo-common/socialdbuteoplugin.cpp | 338 |
1 files changed, 338 insertions, 0 deletions
diff --git a/buteo-plugins/buteo-common/socialdbuteoplugin.cpp b/buteo-plugins/buteo-common/socialdbuteoplugin.cpp new file mode 100644 index 0000000..8b27f84 --- /dev/null +++ b/buteo-plugins/buteo-common/socialdbuteoplugin.cpp @@ -0,0 +1,338 @@ +/**************************************************************************** + ** + ** Copyright (C) 2014 Jolla Ltd. + ** Contact: Chris Adams <chris.adams@jolla.com> + ** + ** 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 "socialdbuteoplugin.h" +#include "socialnetworksyncadaptor.h" +#include "trace.h" + +#include <QCoreApplication> +#include <QTranslator> + +#include <QDBusMessage> +#include <QDBusConnection> +#include <QDBusPendingCall> + +#include "buteosyncfw_p.h" + +#include <Accounts/Manager> +#include <Accounts/Account> +#include <Accounts/Service> + +namespace { + const QString SyncProfileTemplatesKey = QStringLiteral("sync_profile_templates"); + + QString SyncProfileIdKey(const QString &templateProfileName) + { + return QStringLiteral("%1/%2").arg(templateProfileName).arg(Buteo::KEY_PROFILE_ID); + } + + QString createProfile(Buteo::ProfileManager *profileManager, + const QString &templateProfileName, + Accounts::Account *account, + const Accounts::Service &srv, + bool enableProfile, + const QVariantMap &properties) + { + if (!account || !srv.isValid()) { + qWarning() << "Invalid account or service"; + return QString(); + } + if (templateProfileName.isEmpty()) { + qWarning() << "Invalid templateProfileName"; + return QString(); + } + + Accounts::Service prevService = account->selectedService(); + account->selectService(srv); + + Buteo::SyncProfile *templateProfile = profileManager->syncProfile(templateProfileName); + if (!templateProfile) { + account->selectService(prevService); + qWarning() << "Unable to load template profile:" << templateProfileName; + return QString(); + } + + Buteo::SyncProfile *profile = templateProfile->clone(); + if (!profile) { + delete templateProfile; + account->selectService(prevService); + qWarning() << "unable to clone template profile:" << templateProfileName; + return QString(); + } + + QString accountIdStr = QString::number(account->id()); + profile->setName(templateProfileName + "-" + accountIdStr); + profile->setKey(Buteo::KEY_DISPLAY_NAME, templateProfileName + "-" + account->displayName().toHtmlEscaped()); + profile->setKey(Buteo::KEY_ACCOUNT_ID, accountIdStr); + profile->setBoolKey(Buteo::KEY_USE_ACCOUNTS, true); + profile->setEnabled(enableProfile); + + // enable the profile schedule + Buteo::SyncSchedule schedule = profile->syncSchedule(); + schedule.setScheduleEnabled(true); + profile->setSyncSchedule(schedule); + + // set custom properties; note this may override any properties already set + Q_FOREACH (const QString &key, properties.keys()) { + profile->setKey(key, properties[key].toString()); + } + + QString profileId = profileManager->updateProfile(*profile); + if (profileId.isEmpty()) { + qWarning() << "Unable to save sync profile" << templateProfile->name(); + } else { + account->setValue(SyncProfileIdKey(templateProfile->name()), profile->name()); + } + + account->selectService(prevService); + delete profile; + delete templateProfile; + + return profileId; + } +} + +SocialdButeoPlugin::SocialdButeoPlugin(const QString& pluginName, + const Buteo::SyncProfile& profile, + Buteo::PluginCbInterface *callbackInterface, + const QString &socialServiceName, + const QString &dataTypeName) + : ClientPlugin(pluginName, profile, callbackInterface) + , m_socialNetworkSyncAdaptor(nullptr) + , m_socialServiceName(socialServiceName) + , m_dataTypeName(dataTypeName) + , m_profileAccountId(0) +{ +} + +SocialdButeoPlugin::~SocialdButeoPlugin() +{ +} + +bool SocialdButeoPlugin::init() +{ + m_profileAccountId = profile().key(Buteo::KEY_ACCOUNT_ID).toInt(); + m_socialNetworkSyncAdaptor = createSocialNetworkSyncAdaptor(); + if (m_socialNetworkSyncAdaptor) { + connect(m_socialNetworkSyncAdaptor, &SocialNetworkSyncAdaptor::statusChanged, + this, &SocialdButeoPlugin::syncStatusChanged); + return true; + } + + return false; +} + +bool SocialdButeoPlugin::uninit() +{ + delete m_socialNetworkSyncAdaptor; + m_socialNetworkSyncAdaptor = nullptr; + return true; +} + +bool SocialdButeoPlugin::startSync() +{ + // if the profile being triggered is the template profile, then we + // need to ensure that the appropriate per-account profiles exist. + if (m_profileAccountId == 0) { + QList<Buteo::SyncProfile*> perAccountProfiles = ensurePerAccountSyncProfilesExist(); + m_socialNetworkSyncAdaptor->setAccountSyncProfile(NULL); + + // we need to trigger sync with each profile separately, + // or (due to scheduling/etc) another plugin instance might + // be created to sync that profile at the same time, and + // we don't handle concurrency. + foreach (Buteo::SyncProfile *perAccountProfile, perAccountProfiles) { + QDBusMessage message = QDBusMessage::createMethodCall( + "com.meego.msyncd", "/synchronizer", "com.meego.msyncd", "startSync"); + message.setArguments(QVariantList() << perAccountProfile->name()); + QDBusConnection::sessionBus().asyncCall(message); + } + } else { + m_socialNetworkSyncAdaptor->setAccountSyncProfile(profile().clone()); + } + + // now perform sync. Note that for the template profile case, this will + // result in a purge operation occurring (checking for removed accounts and + // purging any synced data associated with those accounts). + if (m_socialNetworkSyncAdaptor && m_socialNetworkSyncAdaptor->enabled()) { + if (m_socialNetworkSyncAdaptor->status() == SocialNetworkSyncAdaptor::Inactive) { + qCDebug(lcSocialPlugin) << "performing sync of" << m_dataTypeName << "from" << m_socialServiceName + << "for account" << m_profileAccountId; + m_socialNetworkSyncAdaptor->sync(m_dataTypeName, m_profileAccountId); + return true; + } else { + qCDebug(lcSocialPlugin) << m_socialServiceName << "sync adaptor for" << m_dataTypeName + << "is still busy with last sync of account" << m_profileAccountId; + } + } else { + qCDebug(lcSocialPlugin) << "no enabled" << m_socialServiceName << "sync adaptor for" << m_dataTypeName; + } + return false; +} + +void SocialdButeoPlugin::abortSync(Sync::SyncStatus status) +{ + // note: it seems buteo automatically calls abortSync on network connectivity loss... + qCInfo(lcSocialPlugin) << "aborting sync with status:" << status; + m_socialNetworkSyncAdaptor->abortSync(status); +} + +bool SocialdButeoPlugin::cleanUp() +{ + m_profileAccountId = profile().key(Buteo::KEY_ACCOUNT_ID).toInt(); + if (!m_socialNetworkSyncAdaptor) { + // might have already been initialized by the OOP framework via init(). + m_socialNetworkSyncAdaptor = createSocialNetworkSyncAdaptor(); + } + + if (m_socialNetworkSyncAdaptor && m_profileAccountId > 0) { + m_socialNetworkSyncAdaptor->purgeDataForOldAccount(m_profileAccountId, + SocialNetworkSyncAdaptor::CleanUpPurge); + } + + return true; +} + +Buteo::SyncResults SocialdButeoPlugin::getSyncResults() const +{ + return m_syncResults; +} + +void SocialdButeoPlugin::connectivityStateChanged(Sync::ConnectivityType type, bool state) +{ + // See TransportTracker.cpp:149 for example + // Sync::CONNECTIVITY_INTERNET, true|false + qCInfo(lcSocialPlugin) << "notified of connectivity change:" << type << state; + if (type == Sync::CONNECTIVITY_INTERNET && state == false) { + // we lost connectivity during sync. + abortSync(Sync::SYNC_CONNECTION_ERROR); + } +} + +void SocialdButeoPlugin::syncStatusChanged() +{ + if (m_socialNetworkSyncAdaptor) { + SocialNetworkSyncAdaptor::Status syncStatus = m_socialNetworkSyncAdaptor->status(); + // Busy change comes when sync starts -> let's ignore that. + if (syncStatus == SocialNetworkSyncAdaptor::Inactive) { + updateResults(Buteo::SyncResults(QDateTime::currentDateTime(), + Buteo::SyncResults::SYNC_RESULT_SUCCESS, + Buteo::SyncResults::NO_ERROR)); + emit success(getProfileName(), QString("%1 update succeeded").arg(getProfileName())); + } else if (syncStatus != SocialNetworkSyncAdaptor::Busy) { + updateResults(Buteo::SyncResults(QDateTime::currentDateTime(), + Buteo::SyncResults::SYNC_RESULT_FAILED, + Buteo::SyncResults::ABORTED)); + emit error(getProfileName(), QString("%1 update failed").arg(getProfileName()), + Buteo::SyncResults::ABORTED); + } + } else { + updateResults(Buteo::SyncResults(QDateTime::currentDateTime(), + Buteo::SyncResults::SYNC_RESULT_FAILED, + Buteo::SyncResults::ABORTED)); + emit error(getProfileName(), QString("%1 update failed").arg(getProfileName()), Buteo::SyncResults::ABORTED); + } +} + +void SocialdButeoPlugin::updateResults(const Buteo::SyncResults &results) +{ + m_syncResults = results; + m_syncResults.setScheduled(true); +} + +// This function is called when the non-per-account profile is triggered. +// The implementation does: +// - get all profiles from the ProfileManager +// - get all accounts from the AccountManager +// - build a mapping of profile -> account for the current data type. (should be one-to-one for the datatype). +// - any account which doesn't have a profile, print an error. +// - check the enabled status of the account -> ensure that the enabled status is reflected in the profile. +// It then returns a list of the appropriate (per account for this data-type) sync profiles. +// The caller takes ownership of the list. +QList<Buteo::SyncProfile*> SocialdButeoPlugin::ensurePerAccountSyncProfilesExist() +{ + Accounts::Manager am; + Accounts::AccountIdList accountIds = am.accountList(); + QList<Buteo::SyncProfile*> syncProfiles = m_profileManager.allSyncProfiles(); + QMap<Accounts::Account*, Buteo::SyncProfile*> perAccountProfiles; + + Accounts::Service dataTypeSyncService = am.service(m_socialNetworkSyncAdaptor->syncServiceName()); + if (!dataTypeSyncService.isValid()) { + qWarning() << Q_FUNC_INFO << "Invalid data type sync service name specified:" + << m_socialNetworkSyncAdaptor->syncServiceName(); + return QList<Buteo::SyncProfile*>(); + } + + for (int i = 0; i < accountIds.size(); ++i) { + Accounts::Account *currAccount = Accounts::Account::fromId(&am, accountIds.at(i), this); + if (!currAccount || currAccount->id() == 0 + || m_socialNetworkSyncAdaptor->syncServiceName().split('-').first() != currAccount->providerName()) { + // we only generate per-account sync profiles for accounts which + // are provided by the provider which provides our sync service. + continue; + } + + // for the current account, find the associated sync profile. + bool foundProfile = false; + for (int j = 0; j < syncProfiles.size(); ++j) { + if (syncProfiles[j]->key(Buteo::KEY_ACCOUNT_ID).toInt() == QString::number(currAccount->id()).toInt() + && syncProfiles[j]->clientProfile() != NULL + && syncProfiles[j]->clientProfile()->name() == profile().clientProfile()->name()) { + // we have found the sync profile for this datatype for this account. + foundProfile = true; + perAccountProfiles.insert(currAccount, syncProfiles.takeAt(j)); + break; + } + } + + if (!foundProfile) { + // it should have been generated for the account when the account was added. + qCInfo(lcSocialPlugin) << "no per-account" << profile().name() + << "sync profile exists for account:" << currAccount->id(); + + // create the per-account profile... we shouldn't need to do this... + QString profileName = createProfile(&m_profileManager, profile().name(), currAccount, dataTypeSyncService, true, QVariantMap()); + Buteo::SyncProfile *newProfile = m_profileManager.syncProfile(profileName); + if (!newProfile) { + qCWarning(lcSocialPlugin) << "unable to create per-account" << profile().name() + << "sync profile for account:" << currAccount->id(); + } else { + // enable the sync schedule for the profile. + Buteo::SyncSchedule schedule = newProfile->syncSchedule(); + schedule.setScheduleEnabled(true); + newProfile->setSyncSchedule(schedule); + m_profileManager.updateProfile(*newProfile); + // and return the profile in the map. + perAccountProfiles.insert(currAccount, newProfile); + } + } + } + + // Every account now has the appropriate sync profile. + qDeleteAll(syncProfiles); // these are for the wrong data type, ignore them. + QList<Buteo::SyncProfile *> retn; + foreach (Accounts::Account *acc, perAccountProfiles.keys()) { + retn.append(perAccountProfiles[acc]); + acc->deleteLater(); + } + + return retn; +} |
