diff options
Diffstat (limited to 'buteo-plugins/buteo-sync-plugin-fediverse-posts/fediversedatatypesyncadaptor.cpp')
| -rw-r--r-- | buteo-plugins/buteo-sync-plugin-fediverse-posts/fediversedatatypesyncadaptor.cpp | 240 |
1 files changed, 240 insertions, 0 deletions
diff --git a/buteo-plugins/buteo-sync-plugin-fediverse-posts/fediversedatatypesyncadaptor.cpp b/buteo-plugins/buteo-sync-plugin-fediverse-posts/fediversedatatypesyncadaptor.cpp new file mode 100644 index 0000000..3ef6f35 --- /dev/null +++ b/buteo-plugins/buteo-sync-plugin-fediverse-posts/fediversedatatypesyncadaptor.cpp @@ -0,0 +1,240 @@ +/**************************************************************************** + ** + ** Copyright (C) 2013-2026 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 "fediversedatatypesyncadaptor.h" +#include "fediverseauthutils.h" + +#include <QtCore/QLoggingCategory> +#include <QtCore/QVariantMap> +#include <QtNetwork/QNetworkRequest> + +// libaccounts-qt5 +#include <Accounts/Manager> +#include <Accounts/Account> +#include <Accounts/Service> +#include <Accounts/AccountService> + +// libsignon-qt5 +#include <SignOn/Identity> +#include <SignOn/AuthSession> +#include <SignOn/SessionData> + +Q_LOGGING_CATEGORY(lcFediverseSync, "buteo.plugin.fediverse.sync", QtWarningMsg) + +FediverseDataTypeSyncAdaptor::FediverseDataTypeSyncAdaptor( + SocialNetworkSyncAdaptor::DataType dataType, + QObject *parent) + : SocialNetworkSyncAdaptor(QStringLiteral("fediverse"), dataType, 0, parent) +{ +} + +FediverseDataTypeSyncAdaptor::~FediverseDataTypeSyncAdaptor() +{ +} + +void FediverseDataTypeSyncAdaptor::sync(const QString &dataTypeString, int accountId) +{ + if (dataTypeString != SocialNetworkSyncAdaptor::dataTypeName(m_dataType)) { + qCWarning(lcFediverseSync) << "Fediverse" << SocialNetworkSyncAdaptor::dataTypeName(m_dataType) + << "sync adaptor was asked to sync" << dataTypeString; + setStatus(SocialNetworkSyncAdaptor::Error); + return; + } + + setStatus(SocialNetworkSyncAdaptor::Busy); + updateDataForAccount(accountId); + qCDebug(lcFediverseSync) << "successfully triggered sync with profile:" << m_accountSyncProfile->name(); +} + +void FediverseDataTypeSyncAdaptor::updateDataForAccount(int accountId) +{ + Accounts::Account *account = Accounts::Account::fromId(m_accountManager, accountId, this); + if (!account) { + qCWarning(lcFediverseSync) << "existing account with id" << accountId << "couldn't be retrieved"; + setStatus(SocialNetworkSyncAdaptor::Error); + return; + } + + incrementSemaphore(accountId); + signIn(account); +} + +QString FediverseDataTypeSyncAdaptor::apiHost(int accountId) const +{ + return m_apiHosts.value(accountId, FediverseAuthUtils::defaultApiHost()); +} + +QString FediverseDataTypeSyncAdaptor::iconPath(int accountId) const +{ + return m_iconPaths.value(accountId, QStringLiteral("image://theme/icon-l-fediverse")); +} + +void FediverseDataTypeSyncAdaptor::errorHandler(QNetworkReply::NetworkError err) +{ + QNetworkReply *reply = qobject_cast<QNetworkReply*>(sender()); + if (!reply) { + return; + } + + const int accountId = reply->property("accountId").toInt(); + const int httpStatus = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(); + + qCWarning(lcFediverseSync) << SocialNetworkSyncAdaptor::dataTypeName(m_dataType) + << "request with account" << accountId + << "experienced error:" << err + << "HTTP:" << httpStatus; + + reply->setProperty("isError", QVariant::fromValue<bool>(true)); + + if (httpStatus == 401 || err == QNetworkReply::AuthenticationRequiredError) { + Accounts::Account *account = Accounts::Account::fromId(m_accountManager, accountId, this); + if (account) { + setCredentialsNeedUpdate(account); + } + } +} + +void FediverseDataTypeSyncAdaptor::sslErrorsHandler(const QList<QSslError> &errs) +{ + QString sslerrs; + foreach (const QSslError &e, errs) { + sslerrs += e.errorString() + QLatin1String("; "); + } + if (!sslerrs.isEmpty()) { + sslerrs.chop(2); + } + + qCWarning(lcFediverseSync) << SocialNetworkSyncAdaptor::dataTypeName(m_dataType) + << "request with account" << sender()->property("accountId").toInt() + << "experienced ssl errors:" << sslerrs; + sender()->setProperty("isError", QVariant::fromValue<bool>(true)); +} + +void FediverseDataTypeSyncAdaptor::setCredentialsNeedUpdate(Accounts::Account *account) +{ + qCInfo(lcFediverseSync) << "sociald:Fediverse: setting CredentialsNeedUpdate to true for account:" << account->id(); + Accounts::Service srv(m_accountManager->service(syncServiceName())); + account->selectService(srv); + account->setValue(QStringLiteral("CredentialsNeedUpdate"), QVariant::fromValue<bool>(true)); + account->setValue(QStringLiteral("CredentialsNeedUpdateFrom"), QVariant::fromValue<QString>(QString::fromLatin1("sociald-fediverse"))); + account->selectService(Accounts::Service()); + account->syncAndBlock(); +} + +void FediverseDataTypeSyncAdaptor::signIn(Accounts::Account *account) +{ + const int accountId = account->id(); + if (!checkAccount(account)) { + decrementSemaphore(accountId); + return; + } + + Accounts::Service srv(m_accountManager->service(syncServiceName())); + account->selectService(srv); + SignOn::Identity *identity = account->credentialsId() > 0 + ? SignOn::Identity::existingIdentity(account->credentialsId()) + : 0; + if (!identity) { + qCWarning(lcFediverseSync) << "account" << accountId << "has no valid credentials, cannot sign in"; + decrementSemaphore(accountId); + return; + } + + Accounts::AccountService accSrv(account, srv); + const QString method = accSrv.authData().method(); + const QString mechanism = accSrv.authData().mechanism(); + SignOn::AuthSession *session = identity->createSession(method); + if (!session) { + qCWarning(lcFediverseSync) << "could not create signon session for account" << accountId; + identity->deleteLater(); + decrementSemaphore(accountId); + return; + } + + QVariantMap signonSessionData = accSrv.authData().parameters(); + FediverseAuthUtils::addSignOnSessionParameters(account, &signonSessionData); + + connect(session, SIGNAL(response(SignOn::SessionData)), + this, SLOT(signOnResponse(SignOn::SessionData)), + Qt::UniqueConnection); + connect(session, SIGNAL(error(SignOn::Error)), + this, SLOT(signOnError(SignOn::Error)), + Qt::UniqueConnection); + + session->setProperty("account", QVariant::fromValue<Accounts::Account*>(account)); + session->setProperty("identity", QVariant::fromValue<SignOn::Identity*>(identity)); + session->process(SignOn::SessionData(signonSessionData), mechanism); +} + +void FediverseDataTypeSyncAdaptor::signOnError(const SignOn::Error &error) +{ + SignOn::AuthSession *session = qobject_cast<SignOn::AuthSession*>(sender()); + Accounts::Account *account = session->property("account").value<Accounts::Account*>(); + SignOn::Identity *identity = session->property("identity").value<SignOn::Identity*>(); + const int accountId = account->id(); + + qCWarning(lcFediverseSync) << "credentials for account with id" << accountId + << "couldn't be retrieved:" << error.type() << error.message(); + + if (error.type() == SignOn::Error::UserInteraction) { + setCredentialsNeedUpdate(account); + } + + session->disconnect(this); + identity->destroySession(session); + identity->deleteLater(); + account->deleteLater(); + + setStatus(SocialNetworkSyncAdaptor::Error); + decrementSemaphore(accountId); +} + +void FediverseDataTypeSyncAdaptor::signOnResponse(const SignOn::SessionData &responseData) +{ + const QVariantMap data = FediverseAuthUtils::responseDataToMap(responseData); + + QString accessToken; + SignOn::AuthSession *session = qobject_cast<SignOn::AuthSession*>(sender()); + Accounts::Account *account = session->property("account").value<Accounts::Account*>(); + SignOn::Identity *identity = session->property("identity").value<SignOn::Identity*>(); + const int accountId = account->id(); + + accessToken = FediverseAuthUtils::accessToken(data); + if (accessToken.isEmpty()) { + qCWarning(lcFediverseSync) << "signon response for account with id" << accountId + << "contained no access token; keys:" << data.keys(); + } + + m_apiHosts.insert(accountId, FediverseAuthUtils::normalizeApiHost(account->value(QStringLiteral("api/Host")).toString())); + m_iconPaths.insert(accountId, account->value(QStringLiteral("iconPath")).toString().trimmed()); + + session->disconnect(this); + identity->destroySession(session); + identity->deleteLater(); + account->deleteLater(); + + if (!accessToken.isEmpty()) { + beginSync(accountId, accessToken); + } else { + setStatus(SocialNetworkSyncAdaptor::Error); + } + + decrementSemaphore(accountId); +} |
