diff options
Diffstat (limited to 'lib')
-rw-r--r-- | lib/accountregistry.cpp | 67 | ||||
-rw-r--r-- | lib/accountregistry.h | 20 | ||||
-rw-r--r-- | lib/connection.cpp | 39 |
3 files changed, 119 insertions, 7 deletions
diff --git a/lib/accountregistry.cpp b/lib/accountregistry.cpp index 616b54b4..9b6114d9 100644 --- a/lib/accountregistry.cpp +++ b/lib/accountregistry.cpp @@ -5,7 +5,13 @@ #include "accountregistry.h" #include "connection.h" +#include <QtCore/QCoreApplication> +#if QT_VERSION_MAJOR >= 6 +# include <qt6keychain/keychain.h> +#else +# include <qt5keychain/keychain.h> +#endif using namespace Quotient; void AccountRegistry::add(Connection* a) @@ -15,6 +21,7 @@ void AccountRegistry::add(Connection* a) beginInsertRows(QModelIndex(), size(), size()); push_back(a); endInsertRows(); + Q_EMIT accountCountChanged(); } void AccountRegistry::drop(Connection* a) @@ -54,8 +61,6 @@ QHash<int, QByteArray> AccountRegistry::roleNames() const return { { AccountRole, "connection" } }; } - - Connection* AccountRegistry::get(const QString& userId) { for (const auto &connection : *this) { @@ -64,3 +69,61 @@ Connection* AccountRegistry::get(const QString& userId) } return nullptr; } + +QKeychain::ReadPasswordJob* AccountRegistry::loadAccessTokenFromKeychain(const QString& userId) +{ + qCDebug(MAIN) << "Reading access token from keychain for" << userId; + auto job = new QKeychain::ReadPasswordJob(qAppName(), this); + job->setKey(userId); + + connect(job, &QKeychain::Job::finished, this, [job]() { + if (job->error() == QKeychain::Error::NoError) { + return; + } + //TODO error handling + }); + job->start(); + + return job; +} + +void AccountRegistry::invokeLogin() +{ + const auto accounts = SettingsGroup("Accounts").childGroups(); + for (const auto& accountId : accounts) { + AccountSettings account{accountId}; + m_accountsLoading += accountId; + Q_EMIT accountsLoadingChanged(); + + if (!account.homeserver().isEmpty()) { + auto accessTokenLoadingJob = loadAccessTokenFromKeychain(account.userId()); + connect(accessTokenLoadingJob, &QKeychain::Job::finished, this, [accountId, this, accessTokenLoadingJob]() { + AccountSettings account{accountId}; + if (accessTokenLoadingJob->error() != QKeychain::Error::NoError) { + //TODO error handling + return; + } + + auto connection = new Connection(account.homeserver()); + connect(connection, &Connection::connected, this, [connection] { + connection->loadState(); + connection->setLazyLoading(true); + + connection->syncLoop(); + }); + connect(connection, &Connection::loginError, this, [](const QString& error, const QString&) { + //TODO error handling + }); + connect(connection, &Connection::networkError, this, [](const QString& error, const QString&, int, int) { + //TODO error handling + }); + connection->assumeIdentity(account.userId(), accessTokenLoadingJob->binaryData(), account.deviceId()); + }); + } + } +} + +QStringList AccountRegistry::accountsLoading() const +{ + return m_accountsLoading; +} diff --git a/lib/accountregistry.h b/lib/accountregistry.h index 2f6dffdf..ab337303 100644 --- a/lib/accountregistry.h +++ b/lib/accountregistry.h @@ -5,15 +5,25 @@ #pragma once #include "quotient_export.h" +#include "settings.h" #include <QtCore/QAbstractListModel> +namespace QKeychain { +class ReadPasswordJob; +} + namespace Quotient { class Connection; class QUOTIENT_API AccountRegistry : public QAbstractListModel, private QVector<Connection*> { Q_OBJECT + /// Number of accounts that are currently fully loaded + Q_PROPERTY(int accountCount READ rowCount NOTIFY accountCountChanged) + /// List of accounts that are currently in some stage of being loaded (Reading token from keychain, trying to contact server, etc). + /// Can be used to inform the user or to show a login screen if size() == 0 and no accounts are loaded + Q_PROPERTY(QStringList accountsLoading READ accountsLoading NOTIFY accountsLoadingChanged) public: using const_iterator = QVector::const_iterator; using const_reference = QVector::const_reference; @@ -52,6 +62,16 @@ public: [[nodiscard]] int rowCount( const QModelIndex& parent = QModelIndex()) const override; [[nodiscard]] QHash<int, QByteArray> roleNames() const override; + + QStringList accountsLoading() const; + + void invokeLogin(); +Q_SIGNALS: + void accountCountChanged(); + void accountsLoadingChanged(); +private: + QKeychain::ReadPasswordJob* loadAccessTokenFromKeychain(const QString &userId); + QStringList m_accountsLoading; }; inline QUOTIENT_API AccountRegistry Accounts {}; diff --git a/lib/connection.cpp b/lib/connection.cpp index dba18cb1..526feb26 100644 --- a/lib/connection.cpp +++ b/lib/connection.cpp @@ -41,12 +41,12 @@ # include "e2ee/qolmsession.h" # include "e2ee/qolmutils.h" -# if QT_VERSION_MAJOR >= 6 -# include <qt6keychain/keychain.h> -# else -# include <qt5keychain/keychain.h> -# endif #endif // Quotient_E2EE_ENABLED +#if QT_VERSION_MAJOR >= 6 +# include <qt6keychain/keychain.h> +#else +# include <qt5keychain/keychain.h> +#endif #if QT_VERSION >= QT_VERSION_CHECK(5, 15, 0) # include <QtCore/QCborValue> @@ -368,6 +368,32 @@ public: void saveDevicesList(); void loadDevicesList(); #endif + + void saveAccessTokenToKeychain() + { + qCDebug(MAIN) << "Saving access token to keychain for" << q->userId(); + auto job = new QKeychain::WritePasswordJob(qAppName()); + job->setAutoDelete(false); + job->setKey(q->userId()); + job->setBinaryData(data->accessToken()); + job->start(); + //TODO error handling + } + + void removeAccessTokenFromKeychain() + { + qCDebug(MAIN) << "Removing access token from keychain for" << q->userId(); + auto job = new QKeychain::DeletePasswordJob(qAppName()); + job->setAutoDelete(true); + job->setKey(q->userId()); + job->start(); + + auto pickleJob = new QKeychain::DeletePasswordJob(qAppName()); + pickleJob->setAutoDelete(true); + pickleJob->setKey(q->userId() + "-Pickle"_ls); + pickleJob->start(); + //TODO error handling + } }; Connection::Connection(const QUrl& server, QObject* parent) @@ -546,6 +572,7 @@ void Connection::Private::loginToServer(LoginArgTs&&... loginArgs) data->setToken(loginJob->accessToken().toLatin1()); data->setDeviceId(loginJob->deviceId()); completeSetup(loginJob->userId()); + saveAccessTokenToKeychain(); #ifndef Quotient_E2EE_ENABLED qCWarning(E2EE) << "End-to-end encryption (E2EE) support is turned off."; #else // Quotient_E2EE_ENABLED @@ -684,6 +711,8 @@ void Connection::logout() disconnect(d->syncLoopConnection); d->data->setToken({}); emit loggedOut(); + SettingsGroup("Accounts").remove(userId()); + d->removeAccessTokenFromKeychain(); deleteLater(); } else { // logout() somehow didn't proceed - restore the session state emit stateChanged(); |