File indexing completed on 2024-05-26 05:13:54

0001 /***************************************************************************
0002  *   SPDX-FileCopyrightText: 2019 Daniel Vrátil <dvratil@kde.org>          *
0003  *                                                                         *
0004  *   SPDX-License-Identifier: LGPL-2.0-or-later                            *
0005  ***************************************************************************/
0006 
0007 #include "accountsintegration.h"
0008 #include "accountsinterface.h"
0009 #include "agentmanager.h"
0010 #include "akonadicontrol_debug.h"
0011 
0012 #include "private/dbus_p.h"
0013 
0014 #include <QDBusConnection>
0015 #include <QTimer>
0016 
0017 #include <Accounts/Account>
0018 #include <Accounts/Service>
0019 
0020 using namespace Akonadi;
0021 using namespace std::chrono_literals;
0022 
0023 namespace
0024 {
0025 const auto akonadiAgentType = QStringLiteral("akonadi/agentType");
0026 
0027 }
0028 
0029 AccountsIntegration::AccountsIntegration(AgentManager &agentManager)
0030     : mAgentManager(agentManager)
0031 {
0032     connect(&mAccountsManager, &Accounts::Manager::accountCreated, this, &AccountsIntegration::onAccountAdded);
0033     connect(&mAccountsManager, &Accounts::Manager::accountRemoved, this, &AccountsIntegration::onAccountRemoved);
0034 
0035     const auto accounts = mAccountsManager.accountList();
0036     for (const auto account : accounts) {
0037         connect(mAccountsManager.account(account), &Accounts::Account::enabledChanged, this, &AccountsIntegration::onAccountServiceEnabled);
0038     }
0039 }
0040 
0041 std::optional<QString> AccountsIntegration::agentForAccount(const QString &agentType, Accounts::AccountId accountId) const
0042 {
0043     const auto instances = mAgentManager.agentInstances();
0044     for (const auto &identifier : instances) {
0045         if (mAgentManager.agentInstanceType(identifier) == agentType) {
0046             const auto serviceName = Akonadi::DBus::agentServiceName(identifier, Akonadi::DBus::Resource);
0047             org::kde::Akonadi::Accounts accountsIface(serviceName, QStringLiteral("/Accounts"), QDBusConnection::sessionBus());
0048             if (!accountsIface.isValid()) {
0049                 continue;
0050             }
0051 
0052             if (accountsIface.getAccountId() == accountId) {
0053                 return identifier;
0054             }
0055         }
0056     }
0057     return std::nullopt;
0058 }
0059 
0060 void AccountsIntegration::configureAgentInstance(const QString &identifier, Accounts::AccountId accountId, int attempt)
0061 {
0062     const auto serviceName = Akonadi::DBus::agentServiceName(identifier, Akonadi::DBus::Resource);
0063     org::kde::Akonadi::Accounts accountsIface(serviceName, QStringLiteral("/Accounts"), QDBusConnection::sessionBus());
0064     if (!accountsIface.isValid()) {
0065         if (attempt >= 3) {
0066             qCWarning(AKONADICONTROL_LOG) << "The resource" << identifier << "does not provide the Accounts DBus interface. Will remove the agent";
0067             mAgentManager.removeAgentInstance(identifier);
0068         } else {
0069             QTimer::singleShot(2s, this, [this, identifier, accountId, attempt]() {
0070                 configureAgentInstance(identifier, accountId, attempt + 1);
0071             });
0072         }
0073         return;
0074     }
0075 
0076     accountsIface.setAccountId(accountId);
0077     qCDebug(AKONADICONTROL_LOG) << "Configured resource" << identifier << "for account" << accountId;
0078 }
0079 
0080 void AccountsIntegration::createAgent(const QString &agentType, Accounts::AccountId accountId)
0081 {
0082     const auto identifier = mAgentManager.createAgentInstance(agentType);
0083     qCDebug(AKONADICONTROL_LOG) << "Created resource" << identifier << "for account" << accountId;
0084     configureAgentInstance(identifier, accountId);
0085 }
0086 
0087 void AccountsIntegration::removeAgentInstance(const QString &identifier)
0088 {
0089     mAgentManager.removeAgentInstance(identifier);
0090 }
0091 
0092 void AccountsIntegration::onAccountAdded(Accounts::AccountId accId)
0093 {
0094     qCDebug(AKONADICONTROL_LOG) << "Online account ID" << accId << "added.";
0095     auto account = mAccountsManager.account(accId);
0096     if (!account || !account->isEnabled()) {
0097         return;
0098     }
0099 
0100     const auto enabledServices = account->enabledServices();
0101     for (const auto &service : enabledServices) {
0102         account->selectService(service);
0103         const auto agentType = account->valueAsString(akonadiAgentType);
0104         if (agentType.isEmpty()) {
0105             continue; // doesn't support Akonadi :-(
0106         }
0107         const auto agent = agentForAccount(agentType, accId);
0108         if (!agent.has_value()) {
0109             createAgent(agentType, account->id());
0110         }
0111         // Always go through all services, there may be more!
0112     }
0113     account->selectService();
0114 
0115     connect(account, &Accounts::Account::enabledChanged, this, &AccountsIntegration::onAccountServiceEnabled);
0116 }
0117 
0118 void AccountsIntegration::onAccountRemoved(Accounts::AccountId accId)
0119 {
0120     qCDebug(AKONADICONTROL_LOG) << "Online account ID" << accId << "removed.";
0121 
0122     const auto instances = mAgentManager.agentInstances();
0123     for (const auto &instance : instances) {
0124         const auto serviceName = DBus::agentServiceName(instance, DBus::Resource);
0125         org::kde::Akonadi::Accounts accountIface(serviceName, QStringLiteral("/Accounts"), QDBusConnection::sessionBus());
0126         if (!accountIface.isValid()) {
0127             continue;
0128         }
0129 
0130         if (accountIface.getAccountId() == accId) {
0131             removeAgentInstance(instance);
0132         }
0133     }
0134 }
0135 
0136 void AccountsIntegration::onAccountServiceEnabled(const QString &serviceType, bool enabled)
0137 {
0138     if (serviceType.isEmpty()) {
0139         return;
0140     }
0141 
0142     auto account = qobject_cast<Accounts::Account *>(sender());
0143     qCDebug(AKONADICONTROL_LOG) << "Online account ID" << account->id() << "service" << serviceType << "has been" << (enabled ? "enabled" : "disabled");
0144 
0145     const auto service = mAccountsManager.service(serviceType);
0146     account->selectService(service);
0147     const auto agentType = account->valueAsString(akonadiAgentType);
0148     account->selectService();
0149     if (agentType.isEmpty()) {
0150         return; // this service does not support Akonadi (yet -;)
0151     }
0152 
0153     const auto identifier = agentForAccount(agentType, account->id());
0154     if (enabled && !identifier.has_value()) {
0155         createAgent(agentType, account->id());
0156     } else if (!enabled && identifier.has_value()) {
0157         removeAgentInstance(identifier.value());
0158     }
0159 }
0160 
0161 #include "moc_accountsintegration.cpp"