diff options
Diffstat (limited to 'transferengine-plugins/fediverseshareservicestatus.cpp')
| -rw-r--r-- | transferengine-plugins/fediverseshareservicestatus.cpp | 305 |
1 files changed, 305 insertions, 0 deletions
diff --git a/transferengine-plugins/fediverseshareservicestatus.cpp b/transferengine-plugins/fediverseshareservicestatus.cpp new file mode 100644 index 0000000..0ab1460 --- /dev/null +++ b/transferengine-plugins/fediverseshareservicestatus.cpp @@ -0,0 +1,305 @@ +/* + * SPDX-FileCopyrightText: 2013 - 2026 Jolla Ltd. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include "fediverseshareservicestatus.h" +#include "fediverseauthutils.h" + +#include <Accounts/Account> +#include <Accounts/AccountService> +#include <Accounts/Manager> +#include <Accounts/Service> + +#include <SignOn/AuthSession> +#include <SignOn/Error> +#include <SignOn/Identity> +#include <SignOn/SessionData> + +#include <QtCore/QUrl> +#include <QtCore/QVariantMap> +#include <QtDebug> + +FediverseShareServiceStatus::FediverseShareServiceStatus(QObject *parent) + : QObject(parent) + , m_auth(new AccountAuthenticator(this)) + , m_accountManager(new Accounts::Manager(this)) + , m_serviceName(QStringLiteral("fediverse-sharing")) +{ +} + +QString FediverseShareServiceStatus::authServiceName() const +{ + return QStringLiteral("fediverse-microblog"); +} + +void FediverseShareServiceStatus::signIn(int accountId) +{ + Accounts::Account *account = Accounts::Account::fromId(m_accountManager, accountId, this); + if (!account) { + qWarning() << Q_FUNC_INFO << "Failed to retrieve account for id:" << accountId; + setAccountDetailsState(accountId, Error); + return; + } + + const Accounts::Service service(m_accountManager->service(authServiceName())); + if (!service.isValid()) { + qWarning() << Q_FUNC_INFO << "Invalid auth service" << authServiceName(); + account->deleteLater(); + setAccountDetailsState(accountId, Error); + return; + } + + account->selectService(service); + + SignOn::Identity *identity = account->credentialsId() > 0 + ? SignOn::Identity::existingIdentity(account->credentialsId()) + : 0; + if (!identity) { + qWarning() << Q_FUNC_INFO << "account" << accountId << "has no valid credentials"; + account->deleteLater(); + setAccountDetailsState(accountId, Error); + return; + } + + Accounts::AccountService accountService(account, service); + const QString method = accountService.authData().method(); + const QString mechanism = accountService.authData().mechanism(); + + SignOn::AuthSession *session = identity->createSession(method); + if (!session) { + qWarning() << Q_FUNC_INFO << "could not create signon session for account" << accountId; + identity->deleteLater(); + account->deleteLater(); + setAccountDetailsState(accountId, Error); + return; + } + + QVariantMap signonSessionData = accountService.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 FediverseShareServiceStatus::signOnResponse(const SignOn::SessionData &responseData) +{ + const QVariantMap data = FediverseAuthUtils::responseDataToMap(responseData); + + 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 ? account->id() : 0; + + QString accessToken = FediverseAuthUtils::accessToken(data); + + if (accountId > 0 && m_accountIdToDetailsIdx.contains(accountId)) { + AccountDetails &accountDetails(m_accountDetails[m_accountIdToDetailsIdx[accountId]]); + accountDetails.accessToken = accessToken; + setAccountDetailsState(accountId, accessToken.isEmpty() ? Error : Populated); + } + + session->disconnect(this); + if (identity) { + identity->destroySession(session); + identity->deleteLater(); + } + if (account) { + account->deleteLater(); + } +} + +void FediverseShareServiceStatus::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 ? account->id() : 0; + + qWarning() << Q_FUNC_INFO << "failed to retrieve credentials for account" << accountId + << error.type() << error.message(); + + if (accountId > 0 && error.type() == SignOn::Error::UserInteraction) { + setCredentialsNeedUpdate(accountId, authServiceName()); + } + + session->disconnect(this); + if (identity) { + identity->destroySession(session); + identity->deleteLater(); + } + if (account) { + account->deleteLater(); + } + + if (accountId > 0) { + setAccountDetailsState(accountId, Error); + } +} + +void FediverseShareServiceStatus::setAccountDetailsState(int accountId, AccountDetailsState state) +{ + if (!m_accountIdToDetailsIdx.contains(accountId)) { + return; + } + + m_accountDetailsState[accountId] = state; + + bool anyWaiting = false; + bool anyPopulated = false; + Q_FOREACH (int id, m_accountDetailsState.keys()) { + AccountDetailsState accountState = m_accountDetailsState.value(id, Waiting); + if (accountState == Waiting) { + anyWaiting = true; + } else if (accountState == Populated) { + anyPopulated = true; + } + } + + if (!anyWaiting) { + if (anyPopulated) { + emit serviceReady(); + } else { + emit serviceError(QStringLiteral("Unable to retrieve Fediverse account credentials")); + } + } +} + +int FediverseShareServiceStatus::count() const +{ + return m_accountDetails.count(); +} + +bool FediverseShareServiceStatus::setCredentialsNeedUpdate(int accountId, const QString &serviceName) +{ + return m_auth->setCredentialsNeedUpdate(accountId, serviceName); +} + +void FediverseShareServiceStatus::queryStatus(QueryStatusMode mode) +{ + m_accountDetails.clear(); + m_accountIdToDetailsIdx.clear(); + m_accountDetailsState.clear(); + + bool signInActive = false; + Q_FOREACH (Accounts::AccountId id, m_accountManager->accountList()) { + Accounts::Account *acc = Accounts::Account::fromId(m_accountManager, id, this); + + if (!acc) { + qWarning() << Q_FUNC_INFO << "Failed to get account for id:" << id; + continue; + } + + acc->selectService(Accounts::Service()); + + const Accounts::Service service(m_accountManager->service(m_serviceName)); + const Accounts::ServiceList services = acc->services(); + bool serviceFound = false; + Q_FOREACH (const Accounts::Service &s, services) { + if (s.name() == m_serviceName) { + serviceFound = true; + break; + } + } + + if (!service.isValid() || !serviceFound) { + acc->deleteLater(); + continue; + } + + const bool accountEnabled = acc->enabled(); + acc->selectService(service); + const bool shareServiceEnabled = acc->enabled(); + if (!accountEnabled || !shareServiceEnabled) { + acc->selectService(Accounts::Service()); + acc->deleteLater(); + continue; + } + + if (acc->value(QStringLiteral("CredentialsNeedUpdate")).toBool()) { + qWarning() << Q_FUNC_INFO << "Credentials need update for account id:" << id; + acc->selectService(Accounts::Service()); + acc->deleteLater(); + continue; + } + + if (!m_accountIdToDetailsIdx.contains(id)) { + AccountDetails details; + details.accountId = id; + acc->selectService(Accounts::Service()); + details.apiHost = FediverseAuthUtils::normalizeApiHost(acc->value(QStringLiteral("api/Host")).toString()); + const QString instanceTitle = acc->value(QStringLiteral("instance/Title")).toString().trimmed(); + details.iconPath = acc->value(QStringLiteral("iconPath")).toString().trimmed(); + acc->selectService(service); + + QUrl apiUrl(details.apiHost); + details.providerName = instanceTitle; + if (details.providerName.isEmpty()) { + details.providerName = apiUrl.host(); + } + if (details.providerName.isEmpty()) { + details.providerName = details.apiHost; + if (details.providerName.startsWith(QLatin1String("https://"))) { + details.providerName.remove(0, 8); + } else if (details.providerName.startsWith(QLatin1String("http://"))) { + details.providerName.remove(0, 7); + } + const int separator = details.providerName.indexOf(QLatin1Char('/')); + if (separator > 0) { + details.providerName.truncate(separator); + } + } + + details.displayName = acc->value(QStringLiteral("description")).toString().trimmed(); + if (details.displayName.isEmpty()) { + details.displayName = acc->displayName(); + } + + m_accountIdToDetailsIdx.insert(id, m_accountDetails.size()); + m_accountDetails.append(details); + } + + if (mode == SignInMode) { + signInActive = true; + m_accountDetailsState.insert(id, Waiting); + signIn(id); + } + + acc->selectService(Accounts::Service()); + acc->deleteLater(); + } + + if (!signInActive) { + emit serviceReady(); + } +} + +FediverseShareServiceStatus::AccountDetails FediverseShareServiceStatus::details(int index) const +{ + if (index < 0 || index >= m_accountDetails.size()) { + qWarning() << Q_FUNC_INFO << "Index out of range"; + return AccountDetails(); + } + + return m_accountDetails.at(index); +} + +FediverseShareServiceStatus::AccountDetails FediverseShareServiceStatus::detailsByIdentifier(int accountIdentifier) const +{ + if (!m_accountIdToDetailsIdx.contains(accountIdentifier)) { + qWarning() << Q_FUNC_INFO << "No details known for account with identifier" << accountIdentifier; + return AccountDetails(); + } + + return m_accountDetails[m_accountIdToDetailsIdx[accountIdentifier]]; +} |
