summaryrefslogtreecommitdiff
path: root/buteo-plugins/buteo-common
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
Initial commit
Diffstat (limited to 'buteo-plugins/buteo-common')
-rw-r--r--buteo-plugins/buteo-common/buteo-common.pri9
-rw-r--r--buteo-plugins/buteo-common/buteo-common.pro29
-rw-r--r--buteo-plugins/buteo-common/buteosyncfw_p.h39
-rw-r--r--buteo-plugins/buteo-common/socialdbuteoplugin.cpp338
-rw-r--r--buteo-plugins/buteo-common/socialdbuteoplugin.h75
-rw-r--r--buteo-plugins/buteo-common/socialdnetworkaccessmanager_p.cpp37
-rw-r--r--buteo-plugins/buteo-common/socialdnetworkaccessmanager_p.h40
-rw-r--r--buteo-plugins/buteo-common/socialnetworksyncadaptor.cpp470
-rw-r--r--buteo-plugins/buteo-common/socialnetworksyncadaptor.h155
-rw-r--r--buteo-plugins/buteo-common/trace.cpp25
-rw-r--r--buteo-plugins/buteo-common/trace.h30
11 files changed, 1247 insertions, 0 deletions
diff --git a/buteo-plugins/buteo-common/buteo-common.pri b/buteo-plugins/buteo-common/buteo-common.pri
new file mode 100644
index 0000000..192da35
--- /dev/null
+++ b/buteo-plugins/buteo-common/buteo-common.pri
@@ -0,0 +1,9 @@
+INCLUDEPATH += $$PWD
+DEPENDPATH += .
+
+QT += dbus
+
+CONFIG += link_pkgconfig
+PKGCONFIG += accounts-qt5 buteosyncfw5 socialcache libsignon-qt5 libsailfishkeyprovider
+
+LIBS += -L$$PWD -lmastodonbuteocommon
diff --git a/buteo-plugins/buteo-common/buteo-common.pro b/buteo-plugins/buteo-common/buteo-common.pro
new file mode 100644
index 0000000..c1aa569
--- /dev/null
+++ b/buteo-plugins/buteo-common/buteo-common.pro
@@ -0,0 +1,29 @@
+TEMPLATE = lib
+
+TARGET = mastodonbuteocommon
+TARGET = $$qtLibraryTarget($$TARGET)
+
+QT -= gui
+QT += network dbus
+CONFIG += link_pkgconfig
+PKGCONFIG += accounts-qt5 buteosyncfw5 socialcache
+
+INCLUDEPATH += $$PWD
+
+HEADERS += \
+ $$PWD/buteosyncfw_p.h \
+ $$PWD/socialdbuteoplugin.h \
+ $$PWD/socialnetworksyncadaptor.h \
+ $$PWD/socialdnetworkaccessmanager_p.h \
+ $$PWD/trace.h
+
+SOURCES += \
+ $$PWD/socialdbuteoplugin.cpp \
+ $$PWD/socialnetworksyncadaptor.cpp \
+ $$PWD/socialdnetworkaccessmanager_p.cpp \
+ $$PWD/trace.cpp
+
+TARGETPATH = $$[QT_INSTALL_LIBS]
+target.path = $$TARGETPATH
+
+INSTALLS += target
diff --git a/buteo-plugins/buteo-common/buteosyncfw_p.h b/buteo-plugins/buteo-common/buteosyncfw_p.h
new file mode 100644
index 0000000..5924731
--- /dev/null
+++ b/buteo-plugins/buteo-common/buteosyncfw_p.h
@@ -0,0 +1,39 @@
+/****************************************************************************
+ **
+ ** Copyright (C) 2014 Jolla Ltd.
+ ** Contact: Chris Adams <chris.adams@jollamobile.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
+ **
+ ****************************************************************************/
+
+#ifndef SOCIALD_BUTEOSYNCFW_P_H
+#define SOCIALD_BUTEOSYNCFW_P_H
+
+#include <SyncCommonDefs.h>
+#include <SyncPluginBase.h>
+#include <ProfileManager.h>
+#include <ClientPlugin.h>
+#include <SyncResults.h>
+#include <ProfileEngineDefs.h>
+#include <SyncProfile.h>
+#include <Profile.h>
+#include <PluginCbInterface.h>
+
+#ifndef SOCIALD_TEST_DEFINE
+#define PRIVILEGED_DATA_DIR QStandardPaths::writableLocation(QStandardPaths::HomeLocation) + QLatin1String("/.local/share/system/privileged")
+#endif
+
+#endif // SOCIALD_BUTEOSYNCFW_P_H
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;
+}
diff --git a/buteo-plugins/buteo-common/socialdbuteoplugin.h b/buteo-plugins/buteo-common/socialdbuteoplugin.h
new file mode 100644
index 0000000..57de171
--- /dev/null
+++ b/buteo-plugins/buteo-common/socialdbuteoplugin.h
@@ -0,0 +1,75 @@
+/****************************************************************************
+ **
+ ** Copyright (C) 2013-2014 Jolla Ltd.
+ ** Contact: Raine Makelainen <raine.makelainen@jollamobile.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
+ **
+ ****************************************************************************/
+
+#ifndef SOCIALDBUTEOPLUGIN_H
+#define SOCIALDBUTEOPLUGIN_H
+
+#include <QtCore/qglobal.h>
+#include "buteosyncfw_p.h"
+
+/*
+ Datatype-specific implementations of this class
+ allow per-account sync profiles for that data type.
+*/
+
+class SocialNetworkSyncAdaptor;
+class Q_DECL_EXPORT SocialdButeoPlugin : public Buteo::ClientPlugin
+{
+ Q_OBJECT
+
+protected:
+ virtual SocialNetworkSyncAdaptor *createSocialNetworkSyncAdaptor() = 0;
+
+public:
+ SocialdButeoPlugin(const QString& pluginName,
+ const Buteo::SyncProfile& profile,
+ Buteo::PluginCbInterface *cbInterface,
+ const QString &socialServiceName,
+ const QString &dataTypeName);
+ virtual ~SocialdButeoPlugin();
+
+ bool init() override;
+ bool uninit() override;
+ bool startSync() override;
+ void abortSync(Sync::SyncStatus status = Sync::SYNC_ABORTED) override;
+ Buteo::SyncResults getSyncResults() const override;
+ bool cleanUp() override;
+
+public Q_SLOTS:
+ void connectivityStateChanged(Sync::ConnectivityType type, bool state) override;
+
+private Q_SLOTS:
+ void syncStatusChanged();
+
+protected:
+ QList<Buteo::SyncProfile*> ensurePerAccountSyncProfilesExist();
+
+private:
+ void updateResults(const Buteo::SyncResults &results);
+ Buteo::SyncResults m_syncResults;
+ Buteo::ProfileManager m_profileManager;
+ SocialNetworkSyncAdaptor *m_socialNetworkSyncAdaptor;
+ QString m_socialServiceName;
+ QString m_dataTypeName;
+ int m_profileAccountId;
+};
+
+#endif // SOCIALDBUTEOPLUGIN_H
diff --git a/buteo-plugins/buteo-common/socialdnetworkaccessmanager_p.cpp b/buteo-plugins/buteo-common/socialdnetworkaccessmanager_p.cpp
new file mode 100644
index 0000000..7030ce3
--- /dev/null
+++ b/buteo-plugins/buteo-common/socialdnetworkaccessmanager_p.cpp
@@ -0,0 +1,37 @@
+/****************************************************************************
+ **
+ ** Copyright (C) 2013-2014 Jolla Ltd.
+ ** Contact: Chris Adams <chris.adams@jollamobile.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 "socialdnetworkaccessmanager_p.h"
+
+/* The default implementation is just a normal QNetworkAccessManager */
+
+SocialdNetworkAccessManager::SocialdNetworkAccessManager(QObject *parent)
+ : QNetworkAccessManager(parent)
+{
+}
+
+QNetworkReply *SocialdNetworkAccessManager::createRequest(
+ QNetworkAccessManager::Operation op,
+ const QNetworkRequest &req,
+ QIODevice *outgoingData)
+{
+ return QNetworkAccessManager::createRequest(op, req, outgoingData);
+}
diff --git a/buteo-plugins/buteo-common/socialdnetworkaccessmanager_p.h b/buteo-plugins/buteo-common/socialdnetworkaccessmanager_p.h
new file mode 100644
index 0000000..846489b
--- /dev/null
+++ b/buteo-plugins/buteo-common/socialdnetworkaccessmanager_p.h
@@ -0,0 +1,40 @@
+/****************************************************************************
+ **
+ ** Copyright (C) 2013-2014 Jolla Ltd.
+ ** Contact: Chris Adams <chris.adams@jollamobile.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
+ **
+ ****************************************************************************/
+
+#ifndef SOCIALD_QNAMFACTORY_P_H
+#define SOCIALD_QNAMFACTORY_P_H
+
+#include <QNetworkAccessManager>
+
+class SocialdNetworkAccessManager : public QNetworkAccessManager
+{
+ Q_OBJECT
+
+public:
+ SocialdNetworkAccessManager(QObject *parent = 0);
+
+protected:
+ QNetworkReply *createRequest(QNetworkAccessManager::Operation op,
+ const QNetworkRequest &req,
+ QIODevice *outgoingData = 0) override;
+};
+
+#endif
diff --git a/buteo-plugins/buteo-common/socialnetworksyncadaptor.cpp b/buteo-plugins/buteo-common/socialnetworksyncadaptor.cpp
new file mode 100644
index 0000000..4451d5f
--- /dev/null
+++ b/buteo-plugins/buteo-common/socialnetworksyncadaptor.cpp
@@ -0,0 +1,470 @@
+/****************************************************************************
+ **
+ ** Copyright (C) 2013-2014 Jolla Ltd.
+ ** Contact: Chris Adams <chris.adams@jollamobile.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 "socialnetworksyncadaptor.h"
+#include "socialdnetworkaccessmanager_p.h"
+#include "trace.h"
+
+#include <QtCore/QJsonDocument>
+#include <QtCore/QTimer>
+#include <QtSql/QSqlDatabase>
+#include <QtSql/QSqlQuery>
+#include <QtSql/QSqlError>
+#include <QtSql/QSqlRecord>
+
+#include <QtNetwork/QNetworkAccessManager>
+#include <QtNetwork/QNetworkReply>
+
+#include "buteosyncfw_p.h"
+
+// libaccounts-qt5
+#include <Accounts/Manager>
+#include <Accounts/Account>
+#include <Accounts/Service>
+
+// libsocialcache
+#include <socialcache/socialimagesdatabase.h>
+#include <socialcache/socialnetworksyncdatabase.h>
+
+namespace {
+ QStringList validDataTypesInitialiser()
+ {
+ return QStringList()
+ << QStringLiteral("Contacts")
+ << QStringLiteral("Calendars")
+ << QStringLiteral("Notifications")
+ << QStringLiteral("Images")
+ << QStringLiteral("Videos")
+ << QStringLiteral("Posts")
+ << QStringLiteral("Messages")
+ << QStringLiteral("Emails")
+ << QStringLiteral("Signon")
+ << QStringLiteral("Backup")
+ << QStringLiteral("BackupQuery")
+ << QStringLiteral("BackupRestore");
+ }
+}
+
+SocialNetworkSyncAdaptor::SocialNetworkSyncAdaptor(const QString &serviceName,
+ SocialNetworkSyncAdaptor::DataType dataType,
+ QNetworkAccessManager *qnam,
+ QObject *parent)
+ : QObject(parent)
+ , m_dataType(dataType)
+ , m_accountManager(new Accounts::Manager(this))
+ , m_networkAccessManager(qnam != 0 ? qnam : new SocialdNetworkAccessManager)
+ , m_accountSyncProfile(NULL)
+ , m_syncDb(new SocialNetworkSyncDatabase())
+ , m_status(SocialNetworkSyncAdaptor::Invalid)
+ , m_enabled(false)
+ , m_syncAborted(false)
+ , m_serviceName(serviceName)
+{
+}
+
+SocialNetworkSyncAdaptor::~SocialNetworkSyncAdaptor()
+{
+ delete m_networkAccessManager;
+ delete m_accountSyncProfile;
+ delete m_syncDb;
+}
+
+// The SocialNetworkSyncAdaptor takes ownership of the sync profiles.
+void SocialNetworkSyncAdaptor::setAccountSyncProfile(Buteo::SyncProfile* perAccountSyncProfile)
+{
+ delete m_accountSyncProfile;
+ m_accountSyncProfile = perAccountSyncProfile;
+}
+
+SocialNetworkSyncAdaptor::Status SocialNetworkSyncAdaptor::status() const
+{
+ return m_status;
+}
+
+bool SocialNetworkSyncAdaptor::enabled() const
+{
+ return m_enabled;
+}
+
+QString SocialNetworkSyncAdaptor::serviceName() const
+{
+ return m_serviceName;
+}
+
+bool SocialNetworkSyncAdaptor::syncAborted() const
+{
+ return m_syncAborted;
+}
+
+void SocialNetworkSyncAdaptor::sync(const QString &dataType, int accountId)
+{
+ Q_UNUSED(dataType)
+ Q_UNUSED(accountId)
+ qCWarning(lcSocialPlugin) << "sync() must be overridden by derived types";
+}
+
+void SocialNetworkSyncAdaptor::abortSync(Sync::SyncStatus status)
+{
+ qCInfo(lcSocialPlugin) << "forcing timeout of outstanding replies due to abort:" << status;
+ m_syncAborted = true;
+ triggerReplyTimeouts();
+}
+
+/*!
+ * \brief SocialNetworkSyncAdaptor::checkAccount
+ * \param account
+ * \return true if synchronization of this adaptor's datatype is enabled for the account
+ *
+ * The default implementation checks that the account is enabled
+ * with the accounts&sso service associated with this sync adaptor.
+ */
+bool SocialNetworkSyncAdaptor::checkAccount(Accounts::Account *account)
+{
+ bool globallyEnabled = account->enabled();
+ Accounts::Service srv(m_accountManager->service(syncServiceName()));
+ if (!srv.isValid()) {
+ qCInfo(lcSocialPlugin) << "invalid service" << syncServiceName() << "specified, account" << account->id()
+ << "will be disabled for" << m_serviceName << dataTypeName(m_dataType) << "sync";
+ return false;
+ }
+ account->selectService(srv);
+ bool serviceEnabled = account->enabled();
+ account->selectService(Accounts::Service());
+ return globallyEnabled && serviceEnabled;
+}
+
+/*!
+ \internal
+ Called when the semaphores for all accounts have been decreased
+ to zero. This is the final function which is called prior to
+ telling buteo that the sync plugin can be destroyed.
+ The implementation MUST be synchronous.
+*/
+void SocialNetworkSyncAdaptor::finalCleanup()
+{
+}
+
+/*!
+ \internal
+ Called when the semaphores decreased to 0, this method is used
+ to finalize something, like saving all data to a database.
+
+ You can call incrementSemaphore to perform asynchronous tasks
+ in this method. finalize will then be called again when the
+ asynchronous task is finished (and when decrementSemaphore is
+ called), be sure to have a condition check in order not to run
+ into an infinite loop.
+
+ It is unsafe to call decrementSemaphore in this method, as
+ the semaphore handling method will find that the semaphore
+ went to 0 twice and will perform cleanup operations twice.
+ Please call decrementSemaphore at the end of the asynchronous
+ task (preferably in a slot), and only call incrementSemaphore
+ for asynchronous tasks.
+ */
+void SocialNetworkSyncAdaptor::finalize(int accountId)
+{
+ Q_UNUSED(accountId)
+}
+
+/*!
+ \internal
+ Returns the last sync timestamp for the given service, account and data type.
+ If data from prior to this timestamp is received in subsequent requests, it does not need to be synced.
+ This function will return an invalid QDateTime if no synchronisation has occurred.
+*/
+QDateTime SocialNetworkSyncAdaptor::lastSyncTimestamp(const QString &serviceName,
+ const QString &dataType,
+ int accountId) const
+{
+ return m_syncDb->lastSyncTimestamp(serviceName, dataType, accountId);
+}
+
+/*!
+ \internal
+ Updates the last sync timestamp for the given service, account and data type to the given \a timestamp.
+*/
+bool SocialNetworkSyncAdaptor::updateLastSyncTimestamp(const QString &serviceName,
+ const QString &dataType,
+ int accountId,
+ const QDateTime &timestamp)
+{
+ // Workaround
+ // TODO: do better, with a queue
+ m_syncDb->addSyncTimestamp(serviceName, dataType, accountId, timestamp);
+ m_syncDb->commit();
+ m_syncDb->wait();
+ return m_syncDb->writeStatus() == AbstractSocialCacheDatabase::Finished;
+}
+
+/*!
+ \internal
+ Returns the list of identifiers of accounts which have been synced for
+ the given \a dataType.
+*/
+QList<int> SocialNetworkSyncAdaptor::syncedAccounts(const QString &dataType)
+{
+ return m_syncDb->syncedAccounts(m_serviceName, dataType);
+}
+
+/*!
+ * \internal
+ * Changes status if there is real change and emits statusChanged() signal.
+ */
+void SocialNetworkSyncAdaptor::setStatus(Status status)
+{
+ if (m_status != status) {
+ m_status = status;
+ emit statusChanged();
+ }
+}
+
+/*!
+ * \internal
+ * Should be used in constructors to set the initial state
+ * of enabled and status, without emitting signals
+ *
+ */
+void SocialNetworkSyncAdaptor::setInitialActive(bool enabled)
+{
+ m_enabled = enabled;
+ if (enabled) {
+ m_status = Inactive;
+ } else {
+ m_status = Invalid;
+ }
+}
+
+/*!
+ * \internal
+ * Should be called by any specific sync adapter when
+ * they've finished syncing data. The transition from
+ * busy status to inactive status is what causes the
+ * Buteo plugin to emit the sync results (and allows
+ * subsequent syncs to occur).
+ */
+void SocialNetworkSyncAdaptor::setFinishedInactive()
+{
+ finalCleanup();
+ qCInfo(lcSocialPlugin) << "Finished" << m_serviceName << SocialNetworkSyncAdaptor::dataTypeName(m_dataType)
+ << "sync at:" << QDateTime::currentDateTime().toString(Qt::ISODate);
+ setStatus(SocialNetworkSyncAdaptor::Inactive);
+}
+
+void SocialNetworkSyncAdaptor::incrementSemaphore(int accountId)
+{
+ int semaphoreValue = m_accountSyncSemaphores.value(accountId);
+ semaphoreValue += 1;
+ m_accountSyncSemaphores.insert(accountId, semaphoreValue);
+ qCDebug(lcSocialPlugin) << "incremented busy semaphore for account" << accountId << "to:" << semaphoreValue;
+}
+
+void SocialNetworkSyncAdaptor::decrementSemaphore(int accountId)
+{
+ if (!m_accountSyncSemaphores.contains(accountId)) {
+ qCWarning(lcSocialPlugin) << "no such semaphore for account" << accountId;
+ return;
+ }
+
+ int semaphoreValue = m_accountSyncSemaphores.value(accountId);
+ semaphoreValue -= 1;
+ qCDebug(lcSocialPlugin) << "decremented busy semaphore for account" << accountId << "to:" << semaphoreValue;
+ if (semaphoreValue < 0) {
+ qCWarning(lcSocialPlugin) << "busy semaphore is negative for account" << accountId;
+ return;
+ }
+ m_accountSyncSemaphores.insert(accountId, semaphoreValue);
+
+ if (semaphoreValue == 0) {
+ finalize(accountId);
+
+ // With the newer implementation, in finalize we can raise semaphores,
+ // so if after calling finalize, the semaphore count is not the same anymore,
+ // we shouldn't update the sync timestamp
+ if (m_accountSyncSemaphores.value(accountId) > 0) {
+ return;
+ }
+
+ // finished all outstanding sync requests for this account.
+ // update the sync time in the global sociald database.
+ updateLastSyncTimestamp(m_serviceName,
+ SocialNetworkSyncAdaptor::dataTypeName(m_dataType), accountId,
+ QDateTime::currentDateTime().toTimeSpec(Qt::UTC));
+
+ // if all outstanding requests for all accounts have finished,
+ // then update our status to Inactive / ready to handle more sync requests.
+ bool allAreZero = true;
+ QList<int> semaphores = m_accountSyncSemaphores.values();
+ foreach (int sv, semaphores) {
+ if (sv != 0) {
+ allAreZero = false;
+ break;
+ }
+ }
+
+ if (allAreZero) {
+ setFinishedInactive(); // Finished!
+ }
+ }
+}
+
+void SocialNetworkSyncAdaptor::timeoutReply()
+{
+ QTimer *timer = qobject_cast<QTimer*>(sender());
+ QNetworkReply *reply = timer->property("networkReply").value<QNetworkReply*>();
+ int accountId = timer->property("accountId").toInt();
+
+ qCWarning(lcSocialPlugin) << "network request timed out while performing sync with account" << accountId;
+
+ m_networkReplyTimeouts[accountId].remove(reply);
+ reply->setProperty("isError", QVariant::fromValue<bool>(true));
+ reply->finished(); // invoke finished, so that the error handling there decrements the semaphore etc.
+ reply->disconnect();
+}
+
+void SocialNetworkSyncAdaptor::setupReplyTimeout(int accountId, QNetworkReply *reply, int msecs)
+{
+ // this function should be called whenever a new network request is performed.
+ QTimer *timer = new QTimer(this);
+ timer->setSingleShot(true);
+ timer->setInterval(msecs);
+ timer->setProperty("accountId", accountId);
+ timer->setProperty("networkReply", QVariant::fromValue<QNetworkReply*>(reply));
+ connect(timer, &QTimer::timeout, this, &SocialNetworkSyncAdaptor::timeoutReply);
+ timer->start();
+ m_networkReplyTimeouts[accountId].insert(reply, timer);
+}
+
+void SocialNetworkSyncAdaptor::removeReplyTimeout(int accountId, QNetworkReply *reply)
+{
+ // this function should be called by the finished() handler for the reply.
+ QTimer *timer = m_networkReplyTimeouts[accountId].value(reply);
+ if (!reply) {
+ return;
+ }
+
+ delete timer;
+ m_networkReplyTimeouts[accountId].remove(reply);
+}
+
+void SocialNetworkSyncAdaptor::triggerReplyTimeouts()
+{
+ // if we've lost network connectivity, we should immediately timeout all replies.
+ Q_FOREACH (int accountId, m_networkReplyTimeouts.keys()) {
+ Q_FOREACH (QTimer *timer, m_networkReplyTimeouts[accountId]) {
+ timer->stop();
+ timer->setInterval(1);
+ timer->start();
+ }
+ }
+}
+
+QJsonObject SocialNetworkSyncAdaptor::parseJsonObjectReplyData(const QByteArray &replyData, bool *ok)
+{
+ QJsonDocument jsonDocument = QJsonDocument::fromJson(replyData);
+ *ok = !jsonDocument.isEmpty();
+ if (*ok && jsonDocument.isObject()) {
+ return jsonDocument.object();
+ }
+ *ok = false;
+ return QJsonObject();
+}
+
+QJsonArray SocialNetworkSyncAdaptor::parseJsonArrayReplyData(const QByteArray &replyData, bool *ok)
+{
+ QJsonDocument jsonDocument = QJsonDocument::fromJson(replyData);
+ *ok = !jsonDocument.isEmpty();
+ if (*ok && jsonDocument.isArray()) {
+ return jsonDocument.array();
+ }
+ *ok = false;
+ return QJsonArray();
+}
+
+/*
+ Valid data types are data types which are known to the API.
+ Note that just because a data type is valid does not mean
+ that it will necessarily be supported by a given social network
+ sync adaptor.
+*/
+QStringList SocialNetworkSyncAdaptor::validDataTypes()
+{
+ static QStringList retn(validDataTypesInitialiser());
+ return retn;
+}
+
+/*
+ String for Enum since the DBus API uses strings
+*/
+QString SocialNetworkSyncAdaptor::dataTypeName(SocialNetworkSyncAdaptor::DataType t)
+{
+ switch (t) {
+ case SocialNetworkSyncAdaptor::Contacts: return QStringLiteral("Contacts");
+ case SocialNetworkSyncAdaptor::Calendars: return QStringLiteral("Calendars");
+ case SocialNetworkSyncAdaptor::Notifications: return QStringLiteral("Notifications");
+ case SocialNetworkSyncAdaptor::Images: return QStringLiteral("Images");
+ case SocialNetworkSyncAdaptor::Videos: return QStringLiteral("Videos");
+ case SocialNetworkSyncAdaptor::Posts: return QStringLiteral("Posts");
+ case SocialNetworkSyncAdaptor::Messages: return QStringLiteral("Messages");
+ case SocialNetworkSyncAdaptor::Emails: return QStringLiteral("Emails");
+ case SocialNetworkSyncAdaptor::Signon: return QStringLiteral("Signon");
+ case SocialNetworkSyncAdaptor::Backup: return QStringLiteral("Backup");
+ case SocialNetworkSyncAdaptor::BackupQuery: return QStringLiteral("BackupQuery");
+ case SocialNetworkSyncAdaptor::BackupRestore: return QStringLiteral("BackupRestore");
+ default: break;
+ }
+
+ return QString();
+}
+
+void SocialNetworkSyncAdaptor::purgeCachedImages(SocialImagesDatabase *database,
+ int accountId)
+{
+ database->queryImages(accountId);
+ database->wait();
+
+ QList<SocialImage::ConstPtr> images = database->images();
+ foreach (SocialImage::ConstPtr image, images) {
+ qCDebug(lcSocialPlugin) << "Purge cached image " << image->imageFile() << " for account " << image->accountId();
+ QFile::remove(image->imageFile());
+ }
+
+ database->removeImages(images);
+ database->commit();
+ database->wait();
+}
+
+void SocialNetworkSyncAdaptor::purgeExpiredImages(SocialImagesDatabase *database,
+ int accountId)
+{
+ database->queryExpired(accountId);
+ database->wait();
+
+ QList<SocialImage::ConstPtr> images = database->images();
+ foreach (SocialImage::ConstPtr image, images) {
+ qCDebug(lcSocialPlugin) << "Purge expired image " << image->imageFile() << " for account " << image->accountId();
+ QFile::remove(image->imageFile());
+ }
+
+ database->removeImages(images);
+ database->commit();
+ database->wait();
+}
diff --git a/buteo-plugins/buteo-common/socialnetworksyncadaptor.h b/buteo-plugins/buteo-common/socialnetworksyncadaptor.h
new file mode 100644
index 0000000..99adeb3
--- /dev/null
+++ b/buteo-plugins/buteo-common/socialnetworksyncadaptor.h
@@ -0,0 +1,155 @@
+/****************************************************************************
+ **
+ ** Copyright (C) 2013-2014 Jolla Ltd.
+ ** Contact: Chris Adams <chris.adams@jollamobile.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
+ **
+ ****************************************************************************/
+
+#ifndef SOCIALNETWORKSYNCADAPTOR_H
+#define SOCIALNETWORKSYNCADAPTOR_H
+
+#include <QtCore/QObject>
+#include <QtCore/QDateTime>
+#include <QtCore/QString>
+#include <QtCore/QJsonObject>
+#include <QtCore/QJsonArray>
+#include <QtCore/QMap>
+#include <QtCore/QList>
+
+#include "buteosyncfw_p.h"
+
+class QSqlDatabase;
+class QNetworkAccessManager;
+class QTimer;
+class QNetworkReply;
+class SocialNetworkSyncDatabase;
+class SocialImagesDatabase;
+
+namespace Accounts {
+ class Account;
+ class Manager;
+}
+
+class SocialNetworkSyncAdaptor : public QObject
+{
+ Q_OBJECT
+ Q_PROPERTY(Status status READ status NOTIFY statusChanged)
+ Q_PROPERTY(bool enabled READ enabled NOTIFY enabledChanged)
+
+public:
+ enum Status {
+ Initializing = 0,
+ Inactive,
+ Busy,
+ Error,
+ Invalid
+ };
+
+ enum PurgeMode {
+ SyncPurge = 0,
+ CleanUpPurge
+ };
+
+ enum DataType {
+ Contacts = 1, // "Contacts"
+ Calendars, // "Calendars"
+ Notifications, // "Notifications"
+ Images, // "Images"
+ Videos, // "Videos"
+ Posts, // "Posts"
+ Messages, // "Messages"
+ Emails, // "Emails"
+ Signon, // "Signon" -- for refreshing AccessTokens etc.
+ Backup, // "Backup"
+ BackupQuery, // "BackupQuery"
+ BackupRestore // "BackupRestore"
+ };
+ static QStringList validDataTypes();
+ static QString dataTypeName(DataType t);
+
+public:
+ SocialNetworkSyncAdaptor(const QString &serviceName, SocialNetworkSyncAdaptor::DataType dataType,
+ QNetworkAccessManager *qnam, QObject *parent);
+ virtual ~SocialNetworkSyncAdaptor();
+
+ virtual QString syncServiceName() const = 0;
+ void setAccountSyncProfile(Buteo::SyncProfile* perAccountSyncProfile);
+
+ Status status() const;
+ bool enabled() const;
+ QString serviceName() const;
+
+ virtual void sync(const QString &dataType, int accountId = 0);
+ virtual void purgeDataForOldAccount(int accountId, PurgeMode mode = SyncPurge) = 0;
+ virtual void abortSync(Sync::SyncStatus status);
+
+Q_SIGNALS:
+ void statusChanged();
+ void enabledChanged();
+
+protected:
+ virtual bool checkAccount(Accounts::Account *account);
+ virtual void finalCleanup();
+ virtual void finalize(int accountId);
+ QDateTime lastSyncTimestamp(const QString &serviceName, const QString &dataType,
+ int accountId) const;
+ bool updateLastSyncTimestamp(const QString &serviceName, const QString &dataType,
+ int accountId, const QDateTime &timestamp);
+ QList<int> syncedAccounts(const QString &dataType);
+ void setStatus(Status status);
+ void setInitialActive(bool enabled);
+ void setFinishedInactive();
+
+ // whether the sync has been aborted (perhaps due to network connection loss)
+ bool syncAborted() const;
+
+ // Semaphore system
+ void incrementSemaphore(int accountId);
+ void decrementSemaphore(int accountId);
+
+ // network reply timeouts
+ void setupReplyTimeout(int accountId, QNetworkReply *reply, int msecs = 60000);
+ void removeReplyTimeout(int accountId, QNetworkReply *reply);
+ void triggerReplyTimeouts();
+
+ // Parsing methods
+ static QJsonObject parseJsonObjectReplyData(const QByteArray &replyData, bool *ok);
+ static QJsonArray parseJsonArrayReplyData(const QByteArray &replyData, bool *ok);
+
+ // Cache management
+ void purgeCachedImages(SocialImagesDatabase *database, int accountId);
+ void purgeExpiredImages(SocialImagesDatabase *database, int accountId);
+
+ const SocialNetworkSyncAdaptor::DataType m_dataType;
+ Accounts::Manager * const m_accountManager;
+ QNetworkAccessManager * const m_networkAccessManager;
+ Buteo::SyncProfile *m_accountSyncProfile;
+
+protected Q_SLOTS:
+ virtual void timeoutReply();
+
+private:
+ SocialNetworkSyncDatabase *m_syncDb;
+ SocialNetworkSyncAdaptor::Status m_status;
+ bool m_enabled;
+ bool m_syncAborted;
+ QString m_serviceName;
+ QMap<int, int> m_accountSyncSemaphores;
+ QMap<int, QMap<QNetworkReply*, QTimer *> > m_networkReplyTimeouts;
+};
+
+#endif // SOCIALNETWORKSYNCADAPTOR_H
diff --git a/buteo-plugins/buteo-common/trace.cpp b/buteo-plugins/buteo-common/trace.cpp
new file mode 100644
index 0000000..ffcdf5b
--- /dev/null
+++ b/buteo-plugins/buteo-common/trace.cpp
@@ -0,0 +1,25 @@
+/****************************************************************************
+ **
+ ** Copyright (C) 2021 Jolla Ltd.
+ **
+ ** 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 "trace.h"
+
+Q_LOGGING_CATEGORY(lcSocialPlugin, "buteo.plugin.social", QtWarningMsg)
+Q_LOGGING_CATEGORY(lcSocialPluginTrace, "buteo.plugin.social.trace", QtWarningMsg)
+
diff --git a/buteo-plugins/buteo-common/trace.h b/buteo-plugins/buteo-common/trace.h
new file mode 100644
index 0000000..b0aeeeb
--- /dev/null
+++ b/buteo-plugins/buteo-common/trace.h
@@ -0,0 +1,30 @@
+/****************************************************************************
+ **
+ ** Copyright (C) 2013-2014 Jolla Ltd.
+ ** Contact: Chris Adams <chris.adams@jollamobile.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
+ **
+ ****************************************************************************/
+
+#ifndef TRACE_H
+#define TRACE_H
+
+#include <QLoggingCategory>
+
+Q_DECLARE_LOGGING_CATEGORY(lcSocialPlugin)
+Q_DECLARE_LOGGING_CATEGORY(lcSocialPluginTrace)
+
+#endif // TRACE_H