Skip to content

Commit

Permalink
Overhaul connection management
Browse files Browse the repository at this point in the history
As described in #781

Roughly:
- Turn Connection into a type that only exists in an initiated state (from the API user's perspective, at least)
- To do this, pull connection setup, etc. out into a new PendingConnection type
- Add easy-to-use account login and restoration functions to AccountRegistry
- Make connections do the expected things (cache, sync loop, etc.) by default
  • Loading branch information
TobiasFella committed Aug 14, 2024
1 parent 3f39d03 commit d80de5a
Show file tree
Hide file tree
Showing 16 changed files with 709 additions and 511 deletions.
4 changes: 4 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -190,6 +190,8 @@ target_sources(${QUOTIENT_LIB_NAME} PUBLIC FILE_SET HEADERS BASE_DIRS .
Quotient/events/keyverificationevent.h
Quotient/keyimport.h
Quotient/qt_connection_util.h
Quotient/pendingconnection.h
Quotient/config.h
PRIVATE
Quotient/function_traits.cpp
Quotient/networkaccessmanager.cpp
Expand Down Expand Up @@ -247,6 +249,8 @@ target_sources(${QUOTIENT_LIB_NAME} PUBLIC FILE_SET HEADERS BASE_DIRS .
Quotient/e2ee/cryptoutils.cpp
Quotient/e2ee/sssshandler.cpp
Quotient/keyimport.cpp
Quotient/pendingconnection.cpp
Quotient/config.cpp
libquotientemojis.qrc
)

Expand Down
79 changes: 18 additions & 61 deletions Quotient/accountregistry.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,11 @@

#include "accountregistry.h"

#include "pendingconnection.h"
#include "connection.h"
#include "logging_categories_p.h"
#include "settings.h"
#include "config.h"

#include <QtCore/QCoreApplication>

Expand Down Expand Up @@ -89,69 +91,24 @@ Connection* AccountRegistry::get(const QString& userId) const
return nullptr;
}

void AccountRegistry::invokeLogin()
QStringList AccountRegistry::accountsLoading() const
{
const auto accounts = SettingsGroup("Accounts"_ls).childGroups();
for (const auto& accountId : accounts) {
AccountSettings account { accountId };

if (account.homeserver().isEmpty())
continue;

d->m_accountsLoading += accountId;
emit accountsLoadingChanged();

qCDebug(MAIN) << "Reading access token from keychain for" << accountId;
auto accessTokenLoadingJob =
new QKeychain::ReadPasswordJob(qAppName(), this);
accessTokenLoadingJob->setKey(accountId);
connect(accessTokenLoadingJob, &QKeychain::Job::finished, this,
[accountId, this, accessTokenLoadingJob]() {
if (accessTokenLoadingJob->error()
!= QKeychain::Error::NoError) {
emit keychainError(accessTokenLoadingJob->error());
d->m_accountsLoading.removeAll(accountId);
emit accountsLoadingChanged();
return;
}

AccountSettings account { accountId };
auto connection = new Connection(account.homeserver());
connect(connection, &Connection::connected, this,
[connection, this, accountId] {
connection->loadState();
connection->setLazyLoading(true);

connection->syncLoop();

d->m_accountsLoading.removeAll(accountId);
emit accountsLoadingChanged();
});
connect(connection, &Connection::loginError, this,
[this, connection, accountId](const QString& error,
const QString& details) {
emit loginError(connection, error, details);

d->m_accountsLoading.removeAll(accountId);
emit accountsLoadingChanged();
});
connect(connection, &Connection::resolveError, this,
[this, connection, accountId](const QString& error) {
emit resolveError(connection, error);

d->m_accountsLoading.removeAll(accountId);
emit accountsLoadingChanged();
});
connection->assumeIdentity(
account.userId(),
QString::fromUtf8(accessTokenLoadingJob->binaryData()));
add(connection);
});
accessTokenLoadingJob->start();
}
return d->m_accountsLoading;
}

QStringList AccountRegistry::accountsLoading() const
PendingConnection* AccountRegistry::loginWithPassword(const QString& matrixId, const QString& password, const ConnectionSettings& settings)
{
return d->m_accountsLoading;
return PendingConnection::loginWithPassword(matrixId, password, settings, this);
}

PendingConnection* AccountRegistry::restoreConnection(const QString& matrixId, const ConnectionSettings& settings)
{
return PendingConnection::restoreConnection(matrixId, settings, this);
}

QStringList AccountRegistry::availableConnections() const
{
//TODO change accounts -> QuotientAccounts?
return Config::instance()->childGroups("Accounts"_ls, Config::Data);
//TODO check whether we have plausible data?
}
19 changes: 16 additions & 3 deletions Quotient/accountregistry.h
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,13 @@

namespace Quotient {
class Connection;
class PendingConnection;

struct ConnectionSettings
{
QString deviceId;
QString initialDeviceName;
};

class QUOTIENT_API AccountRegistry : public QAbstractListModel,
private QVector<Connection*> {
Expand Down Expand Up @@ -63,9 +70,15 @@ class QUOTIENT_API AccountRegistry : public QAbstractListModel,

QStringList accountsLoading() const;

[[deprecated("This may leak Connection objects when failing and cannot be"
"fixed without breaking the API; do not use it")]] //
void invokeLogin();
Quotient::PendingConnection *loginWithPassword(const QString& matrixId, const QString& password, const ConnectionSettings& settings = {});
Quotient::PendingConnection *restoreConnection(const QString& matrixId, const ConnectionSettings& settings = {});

Quotient::PendingConnection *mockConnection(const QString& userId) {
//TODO
return nullptr;
}

QStringList availableConnections() const;

Q_SIGNALS:
void accountCountChanged();
Expand Down
55 changes: 55 additions & 0 deletions Quotient/config.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
// SPDX-FileCopyrightText: 2024 Tobias Fella <[email protected]>
// SPDX-License-Identifier: LGPL-2.0-or-later

#include "config.h"

#include <QSettings>

class QSettingsConfig : public Config
{
//TODO this backend currently ignores the type and stores everything in the same backend
QString load(const QString& group, const QString& childGroup, const QString& key, Type type) override
{
settings.beginGroup(group);
settings.beginGroup(childGroup);
auto value = settings.value(key).toString();
settings.endGroup();
settings.endGroup();
return value;
}

void store(const QString& group, const QString& childGroup, const QString& key, const QString& value, Type type) override
{
settings.beginGroup(group);
settings.beginGroup(childGroup);
settings.setValue(key, value);
settings.endGroup();
settings.endGroup();
settings.sync();
}

QStringList childGroups(const QString& group, Type type) override
{
settings.beginGroup(group);
auto childGroups = settings.childGroups();
settings.endGroup();
return childGroups;
}

QSettings settings;
};

Config* Config::s_instance = nullptr;

Config* Config::instance()
{
if (!s_instance) {
s_instance = new QSettingsConfig();
}
return s_instance;
}

void Config::setInstance(Config* config)
{
s_instance = config;
}
25 changes: 25 additions & 0 deletions Quotient/config.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
// SPDX-FileCopyrightText: 2024 Tobias Fella <[email protected]>
// SPDX-License-Identifier: LGPL-2.0-or-later

#pragma once

#include <QString>

class Config
{
public:
enum Type
{
Settings,
Data,
};
virtual QString load(const QString& group, const QString& childGroup, const QString& key, Type type) = 0;
virtual void store(const QString& group, const QString& childGroup, const QString& key, const QString& value, Type type) = 0;
virtual QStringList childGroups(const QString& group, Type type) = 0;

static Config* instance();
static void setInstance(Config* config);

private:
static Config* s_instance;
};
Loading

0 comments on commit d80de5a

Please sign in to comment.