summaryrefslogtreecommitdiff
path: root/transferengine-plugins/mastodonshareservicestatus.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'transferengine-plugins/mastodonshareservicestatus.cpp')
-rw-r--r--transferengine-plugins/mastodonshareservicestatus.cpp367
1 files changed, 367 insertions, 0 deletions
diff --git a/transferengine-plugins/mastodonshareservicestatus.cpp b/transferengine-plugins/mastodonshareservicestatus.cpp
new file mode 100644
index 0000000..8c70e12
--- /dev/null
+++ b/transferengine-plugins/mastodonshareservicestatus.cpp
@@ -0,0 +1,367 @@
+#include "mastodonshareservicestatus.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>
+
+MastodonShareServiceStatus::MastodonShareServiceStatus(QObject *parent)
+ : QObject(parent)
+ , m_auth(new AccountAuthenticator(this))
+ , m_accountManager(new Accounts::Manager(this))
+ , m_serviceName(QStringLiteral("mastodon-sharing"))
+{
+}
+
+QString MastodonShareServiceStatus::normalizeApiHost(const QString &rawHost)
+{
+ QString host = rawHost.trimmed();
+ if (host.isEmpty()) {
+ host = QStringLiteral("https://mastodon.social");
+ }
+
+ if (!host.startsWith(QLatin1String("https://"))
+ && !host.startsWith(QLatin1String("http://"))) {
+ host.prepend(QStringLiteral("https://"));
+ }
+
+ QUrl url(host);
+ if (!url.isValid() || url.host().isEmpty()) {
+ return QStringLiteral("https://mastodon.social");
+ }
+
+ QString normalized = QString::fromLatin1(url.toEncoded(QUrl::RemovePath
+ | QUrl::RemoveQuery
+ | QUrl::RemoveFragment));
+ if (normalized.endsWith(QLatin1Char('/'))) {
+ normalized.chop(1);
+ }
+
+ return normalized;
+}
+
+void MastodonShareServiceStatus::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(m_serviceName));
+ if (!service.isValid()) {
+ qWarning() << Q_FUNC_INFO << "Invalid auth service" << m_serviceName;
+ 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();
+
+ QString configuredHost = account->value(QStringLiteral("auth/oauth2/web_server/Host")).toString().trimmed();
+ if (configuredHost.isEmpty()) {
+ configuredHost = normalizeApiHost(account->value(QStringLiteral("api/Host")).toString());
+ }
+
+ if (configuredHost.startsWith(QLatin1String("https://"))) {
+ configuredHost.remove(0, 8);
+ } else if (configuredHost.startsWith(QLatin1String("http://"))) {
+ configuredHost.remove(0, 7);
+ }
+
+ const int separator = configuredHost.indexOf(QLatin1Char('/'));
+ if (separator > -1) {
+ configuredHost.truncate(separator);
+ }
+ while (configuredHost.endsWith(QLatin1Char('/'))) {
+ configuredHost.chop(1);
+ }
+
+ if (configuredHost.isEmpty()) {
+ configuredHost = QStringLiteral("mastodon.social");
+ }
+ signonSessionData.insert(QStringLiteral("Host"), configuredHost);
+
+ const QString authPath = account->value(QStringLiteral("auth/oauth2/web_server/AuthPath")).toString().trimmed();
+ if (!authPath.isEmpty()) {
+ signonSessionData.insert(QStringLiteral("AuthPath"), authPath);
+ }
+
+ const QString tokenPath = account->value(QStringLiteral("auth/oauth2/web_server/TokenPath")).toString().trimmed();
+ if (!tokenPath.isEmpty()) {
+ signonSessionData.insert(QStringLiteral("TokenPath"), tokenPath);
+ }
+
+ const QString responseType = account->value(QStringLiteral("auth/oauth2/web_server/ResponseType")).toString().trimmed();
+ if (!responseType.isEmpty()) {
+ signonSessionData.insert(QStringLiteral("ResponseType"), responseType);
+ }
+
+ const QString redirectUri = account->value(QStringLiteral("auth/oauth2/web_server/RedirectUri")).toString().trimmed();
+ if (!redirectUri.isEmpty()) {
+ signonSessionData.insert(QStringLiteral("RedirectUri"), redirectUri);
+ }
+
+ const QVariant scopeValue = account->value(QStringLiteral("auth/oauth2/web_server/Scope"));
+ if (scopeValue.isValid()) {
+ signonSessionData.insert(QStringLiteral("Scope"), scopeValue);
+ }
+
+ const QString clientId = account->value(QStringLiteral("auth/oauth2/web_server/ClientId")).toString().trimmed();
+ if (!clientId.isEmpty()) {
+ signonSessionData.insert(QStringLiteral("ClientId"), clientId);
+ }
+
+ const QString clientSecret = account->value(QStringLiteral("auth/oauth2/web_server/ClientSecret")).toString().trimmed();
+ if (!clientSecret.isEmpty()) {
+ signonSessionData.insert(QStringLiteral("ClientSecret"), clientSecret);
+ }
+
+ signonSessionData.insert(QStringLiteral("UiPolicy"), SignOn::NoUserInteractionPolicy);
+
+ 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 MastodonShareServiceStatus::signOnResponse(const SignOn::SessionData &responseData)
+{
+ QVariantMap data;
+ Q_FOREACH (const QString &key, responseData.propertyNames()) {
+ data.insert(key, responseData.getProperty(key));
+ }
+
+ 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 = data.value(QLatin1String("AccessToken")).toString().trimmed();
+ if (accessToken.isEmpty()) {
+ accessToken = data.value(QLatin1String("access_token")).toString().trimmed();
+ }
+
+ 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 MastodonShareServiceStatus::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, m_serviceName);
+ }
+
+ session->disconnect(this);
+ if (identity) {
+ identity->destroySession(session);
+ identity->deleteLater();
+ }
+ if (account) {
+ account->deleteLater();
+ }
+
+ if (accountId > 0) {
+ setAccountDetailsState(accountId, Error);
+ }
+}
+
+void MastodonShareServiceStatus::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 Mastodon account credentials"));
+ }
+ }
+}
+
+int MastodonShareServiceStatus::count() const
+{
+ return m_accountDetails.count();
+}
+
+bool MastodonShareServiceStatus::setCredentialsNeedUpdate(int accountId, const QString &serviceName)
+{
+ return m_auth->setCredentialsNeedUpdate(accountId, serviceName);
+}
+
+void MastodonShareServiceStatus::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 = m_accountManager->account(id);
+
+ 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 (acc->enabled() && service.isValid() && serviceFound) {
+ if (acc->value(QStringLiteral("CredentialsNeedUpdate")).toBool()) {
+ qWarning() << Q_FUNC_INFO << "Credentials need update for account id:" << id;
+ continue;
+ }
+
+ acc->selectService(service);
+ if (acc->value(QStringLiteral("CredentialsNeedUpdate")).toBool()) {
+ qWarning() << Q_FUNC_INFO << "Credentials need update for account id:" << id;
+ acc->selectService(Accounts::Service());
+ continue;
+ }
+
+ if (!m_accountIdToDetailsIdx.contains(id)) {
+ AccountDetails details;
+ details.accountId = id;
+ details.apiHost = normalizeApiHost(acc->value(QStringLiteral("api/Host")).toString());
+
+ QUrl apiUrl(details.apiHost);
+ 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->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());
+ }
+ }
+
+ if (!signInActive) {
+ emit serviceReady();
+ }
+}
+
+MastodonShareServiceStatus::AccountDetails MastodonShareServiceStatus::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);
+}
+
+MastodonShareServiceStatus::AccountDetails MastodonShareServiceStatus::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]];
+}