Akonadi

akonadicontrol/accountsintegration.cpp
1 /***************************************************************************
2  * Copyright (c) 2019 Daniel Vrátil <[email protected]> *
3  * *
4  * This program is free software; you can redistribute it and/or modify *
5  * it under the terms of the GNU Library General Public License as *
6  * published by the Free Software Foundation; either version 2 of the *
7  * License, or (at your option) any later version. *
8  * *
9  * This program is distributed in the hope that it will be useful, *
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of *
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
12  * GNU General Public License for more details. *
13  * *
14  * You should have received a copy of the GNU Library General Public *
15  * License along with this program; if not, write to the *
16  * Free Software Foundation, Inc., *
17  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
18  ***************************************************************************/
19 
20 #include "accountsintegration.h"
21 #include "agentmanager.h"
22 #include "akonadicontrol_debug.h"
23 #include "accountsinterface.h"
24 
25 #include <private/dbus_p.h>
26 
27 #include <QDBusConnection>
28 #include <QTimer>
29 
30 #include <Accounts/Account>
31 #include <Accounts/Service>
32 
33 using namespace Akonadi;
34 using namespace std::chrono_literals;
35 
36 namespace {
37 
38 static const auto akonadiAgentType = QStringLiteral("akonadi/agentType");
39 
40 }
41 
42 AccountsIntegration::AccountsIntegration(AgentManager &agentManager)
43  : QObject()
44  , mAgentManager(agentManager)
45 {
46  connect(&mAccountsManager, &Accounts::Manager::accountCreated,
47  this, &AccountsIntegration::onAccountAdded);
48  connect(&mAccountsManager, &Accounts::Manager::accountRemoved,
49  this, &AccountsIntegration::onAccountRemoved);
50 
51  const auto accounts = mAccountsManager.accountList();
52  for (const auto account : accounts) {
53  connect(mAccountsManager.account(account), &Accounts::Account::enabledChanged,
54  this, &AccountsIntegration::onAccountServiceEnabled);
55  }
56 }
57 
58 std::optional<QString> AccountsIntegration::agentForAccount(const QString &agentType, Accounts::AccountId accountId) const
59 {
60  const auto instances = mAgentManager.agentInstances();
61  for (const auto &identifier : instances) {
62  if (mAgentManager.agentInstanceType(identifier) == agentType) {
63  const auto serviceName = Akonadi::DBus::agentServiceName(identifier, Akonadi::DBus::Resource);
64  org::kde::Akonadi::Accounts accountsIface(serviceName, QStringLiteral("/Accounts"), QDBusConnection::sessionBus());
65  if (!accountsIface.isValid()) {
66  continue;
67  }
68 
69  if (accountsIface.getAccountId() == accountId) {
70  return identifier;
71  }
72  }
73  }
74  return std::nullopt;
75 }
76 
77 void AccountsIntegration::configureAgentInstance(const QString &identifier, Accounts::AccountId accountId, int attempt)
78 {
79  const auto serviceName = Akonadi::DBus::agentServiceName(identifier, Akonadi::DBus::Resource);
80  org::kde::Akonadi::Accounts accountsIface(serviceName, QStringLiteral("/Accounts"), QDBusConnection::sessionBus());
81  if (!accountsIface.isValid()) {
82  if (attempt >= 3) {
83  qCWarning(AKONADICONTROL_LOG) << "The resource" << identifier << "does not provide the Accounts DBus interface. Will remove the agent";
84  mAgentManager.removeAgentInstance(identifier);
85  } else {
86  QTimer::singleShot(2s, this, [this, identifier, accountId, attempt]() {
87  configureAgentInstance(identifier, accountId, attempt + 1);
88  });
89  }
90  return;
91  }
92 
93  accountsIface.setAccountId(accountId);
94  qCDebug(AKONADICONTROL_LOG) << "Configured resource" << identifier << "for account" << accountId;
95 }
96 
97 void AccountsIntegration::createAgent(const QString &agentType, Accounts::AccountId accountId)
98 {
99  const auto identifier = mAgentManager.createAgentInstance(agentType);
100  qCDebug(AKONADICONTROL_LOG) << "Created resource" << identifier << "for account" << accountId;
101  configureAgentInstance(identifier, accountId);
102 }
103 
104 void AccountsIntegration::removeAgentInstance(const QString &identifier)
105 {
106  mAgentManager.removeAgentInstance(identifier);
107 }
108 
109 void AccountsIntegration::onAccountAdded(Accounts::AccountId accId)
110 {
111  qCDebug(AKONADICONTROL_LOG) << "Online account ID" << accId << "added.";
112  auto account = mAccountsManager.account(accId);
113  if (!account || !account->isEnabled()) {
114  return;
115  }
116 
117  const auto enabledServices = account->enabledServices();
118  for (const auto &service : enabledServices) {
119  account->selectService(service);
120  const auto agentType = account->valueAsString(akonadiAgentType);
121  if (agentType.isEmpty()) {
122  continue; // doesn't support Akonadi :-(
123  }
124  const auto agent = agentForAccount(agentType, accId);
125  if (!agent.has_value()) {
126  createAgent(agentType, account->id());
127  }
128  // Always go through all services, there may be more!
129  }
130  account->selectService();
131 
132  connect(account, &Accounts::Account::enabledChanged,
133  this, &AccountsIntegration::onAccountServiceEnabled);
134 }
135 
136 void AccountsIntegration::onAccountRemoved(Accounts::AccountId accId)
137 {
138  qCDebug(AKONADICONTROL_LOG) << "Online account ID" << accId << "removed.";
139 
140  const auto instances = mAgentManager.agentInstances();
141  for (const auto &instance : instances) {
142  const auto serviceName = DBus::agentServiceName(instance, DBus::Resource);
143  org::kde::Akonadi::Accounts accountIface(serviceName, QStringLiteral("/Accounts"), QDBusConnection::sessionBus());
144  if (!accountIface.isValid()) {
145  continue;
146  }
147 
148  if (accountIface.getAccountId() == accId) {
149  removeAgentInstance(instance);
150  }
151  }
152 }
153 
154 void AccountsIntegration::onAccountServiceEnabled(const QString &serviceType, bool enabled)
155 {
156  if (serviceType.isEmpty()) {
157  return;
158  }
159 
160  auto account = qobject_cast<Accounts::Account*>(sender());
161  qCDebug(AKONADICONTROL_LOG) << "Online account ID" << account->id() << "service" << serviceType << "has been" << (enabled ? "enabled" : "disabled");
162 
163  const auto service = mAccountsManager.service(serviceType);
164  account->selectService(service);
165  const auto agentType = account->valueAsString(akonadiAgentType);
166  account->selectService();
167  if (agentType.isEmpty()) {
168  return; // this service does not support Akonadi (yet -;)
169  }
170 
171  const auto identifier = agentForAccount(agentType, account->id());
172  if (enabled && !identifier.has_value()) {
173  createAgent(agentType, account->id());
174  } else if (!enabled && identifier.has_value()) {
175  removeAgentInstance(identifier.value());
176  }
177 }
Provides an interface to retrieve agent types and manage agent instances.
QDBusConnection sessionBus()
bool isEmpty() const const
Helper integration between Akonadi and Qt.
This file is part of the KDE documentation.
Documentation copyright © 1996-2020 The KDE developers.
Generated on Sat May 30 2020 22:46:06 by doxygen 1.8.11 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.