summaryrefslogtreecommitdiff
path: root/buteo-plugins/buteo-common/socialdbuteoplugin.cpp
diff options
context:
space:
mode:
authorAndrew Branson <andrew.branson@jolla.com>2026-02-10 10:41:02 +0100
committerAndrew Branson <andrew.branson@jolla.com>2026-02-10 17:09:39 +0100
commit4351f4627ba9e71775438dd26c9acddd002c7e11 (patch)
tree3c72c980c5c81507109087bda67052b7ec8216b6 /buteo-plugins/buteo-common/socialdbuteoplugin.cpp
Initial commit
Diffstat (limited to 'buteo-plugins/buteo-common/socialdbuteoplugin.cpp')
-rw-r--r--buteo-plugins/buteo-common/socialdbuteoplugin.cpp338
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;
+}