1 | // SPDX-FileCopyrightText: Kitsune Ral <Kitsune-Ral@users.sf.net> |
2 | // SPDX-FileCopyrightText: Tobias Fella <fella@posteo.de> |
3 | // SPDX-License-Identifier: LGPL-2.1-or-later |
4 | |
5 | #include "accountregistry.h" |
6 | |
7 | #include "connection.h" |
8 | #include "logging_categories_p.h" |
9 | #include "settings.h" |
10 | |
11 | #include <QtCore/QCoreApplication> |
12 | |
13 | // TODO: remove in 0.9 |
14 | QT_IGNORE_DEPRECATIONS( |
15 | QUOTIENT_API Quotient::AccountRegistry Quotient::Accounts{};) |
16 | |
17 | using namespace Quotient; |
18 | |
19 | struct Q_DECL_HIDDEN AccountRegistry::Private { |
20 | QStringList m_accountsLoading; |
21 | }; |
22 | |
23 | AccountRegistry::AccountRegistry(QObject* parent) |
24 | : QAbstractListModel(parent), d(makeImpl<Private>()) |
25 | {} |
26 | |
27 | void AccountRegistry::add(Connection* a) |
28 | { |
29 | Q_ASSERT(a != nullptr); |
30 | if (get(userId: a->userId()) != nullptr) { |
31 | qWarning(catFunc: MAIN) << "Attempt to add another connection for the same user " |
32 | "id; skipping" ; |
33 | return; |
34 | } |
35 | beginInsertRows(parent: QModelIndex(), first: size(), last: size()); |
36 | push_back(t: a); |
37 | connect(sender: a, signal: &Connection::loggedOut, context: this, slot: [this, a] { drop(a); }); |
38 | qDebug(catFunc: MAIN) << "Added" << a->objectName() << "to the account registry" ; |
39 | endInsertRows(); |
40 | emit accountCountChanged(); |
41 | } |
42 | |
43 | void AccountRegistry::drop(Connection* a) |
44 | { |
45 | if (const auto idx = indexOf(t: a); idx != -1) { |
46 | beginRemoveRows(parent: QModelIndex(), first: idx, last: idx); |
47 | remove(i: idx); |
48 | qDebug(catFunc: MAIN) << "Removed" << a->objectName() |
49 | << "from the account registry" ; |
50 | endRemoveRows(); |
51 | } |
52 | Q_ASSERT(!contains(a)); |
53 | } |
54 | |
55 | bool AccountRegistry::isLoggedIn(const QString &userId) const |
56 | { |
57 | const auto conn = get(userId); |
58 | return conn != nullptr && conn->isLoggedIn(); |
59 | } |
60 | |
61 | QVariant AccountRegistry::data(const QModelIndex& index, int role) const |
62 | { |
63 | if (!index.isValid() || index.row() >= count()) |
64 | return {}; |
65 | |
66 | switch (role) { |
67 | case AccountRole: |
68 | return QVariant::fromValue(value: at(i: index.row())); |
69 | case UserIdRole: |
70 | return QVariant::fromValue(value: at(i: index.row())->userId()); |
71 | default: |
72 | return {}; |
73 | } |
74 | } |
75 | |
76 | int AccountRegistry::rowCount(const QModelIndex& parent) const |
77 | { |
78 | return parent.isValid() ? 0 : count(); |
79 | } |
80 | |
81 | QHash<int, QByteArray> AccountRegistry::roleNames() const |
82 | { |
83 | return { { AccountRole, QByteArrayLiteral("connection" ) }, |
84 | { UserIdRole, QByteArrayLiteral("userId" ) } }; |
85 | } |
86 | |
87 | Connection* AccountRegistry::get(const QString& userId) const |
88 | { |
89 | for (const auto& connection : accounts()) { |
90 | if (connection->userId() == userId) |
91 | return connection; |
92 | } |
93 | return nullptr; |
94 | } |
95 | |
96 | void AccountRegistry::invokeLogin() |
97 | { |
98 | const auto accounts = SettingsGroup("Accounts"_ls ).childGroups(); |
99 | for (const auto& accountId : accounts) { |
100 | AccountSettings account { accountId }; |
101 | |
102 | if (account.homeserver().isEmpty()) |
103 | continue; |
104 | |
105 | d->m_accountsLoading += accountId; |
106 | emit accountsLoadingChanged(); |
107 | |
108 | qCDebug(MAIN) << "Reading access token from keychain for" << accountId; |
109 | auto accessTokenLoadingJob = |
110 | new QKeychain::ReadPasswordJob(qAppName(), this); |
111 | accessTokenLoadingJob->setKey(accountId); |
112 | connect(sender: accessTokenLoadingJob, signal: &QKeychain::Job::finished, context: this, |
113 | slot: [accountId, this, accessTokenLoadingJob]() { |
114 | if (accessTokenLoadingJob->error() |
115 | != QKeychain::Error::NoError) { |
116 | emit keychainError(error: accessTokenLoadingJob->error()); |
117 | d->m_accountsLoading.removeAll(t: accountId); |
118 | emit accountsLoadingChanged(); |
119 | return; |
120 | } |
121 | |
122 | AccountSettings account { accountId }; |
123 | auto connection = new Connection(account.homeserver()); |
124 | connect(sender: connection, signal: &Connection::connected, context: this, |
125 | slot: [connection, this, accountId] { |
126 | connection->loadState(); |
127 | connection->setLazyLoading(true); |
128 | |
129 | connection->syncLoop(); |
130 | |
131 | d->m_accountsLoading.removeAll(t: accountId); |
132 | emit accountsLoadingChanged(); |
133 | }); |
134 | connect(sender: connection, signal: &Connection::loginError, context: this, |
135 | slot: [this, connection, accountId](const QString& error, |
136 | const QString& details) { |
137 | emit loginError(connection, message: error, details); |
138 | |
139 | d->m_accountsLoading.removeAll(t: accountId); |
140 | emit accountsLoadingChanged(); |
141 | }); |
142 | connect(sender: connection, signal: &Connection::resolveError, context: this, |
143 | slot: [this, connection, accountId](const QString& error) { |
144 | emit resolveError(connection, error); |
145 | |
146 | d->m_accountsLoading.removeAll(t: accountId); |
147 | emit accountsLoadingChanged(); |
148 | }); |
149 | connection->assumeIdentity( |
150 | mxId: account.userId(), |
151 | accessToken: QString::fromUtf8(ba: accessTokenLoadingJob->binaryData())); |
152 | add(a: connection); |
153 | }); |
154 | accessTokenLoadingJob->start(); |
155 | } |
156 | } |
157 | |
158 | QStringList AccountRegistry::accountsLoading() const |
159 | { |
160 | return d->m_accountsLoading; |
161 | } |
162 | |