Page MenuHomePhorge

D311.1783865924.diff
No OneTemporary

Size
116 KB
Referenced Files
None
Subscribers
None

D311.1783865924.diff

diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -36,6 +36,8 @@
qt-job.cpp
${CMAKE_CURRENT_BINARY_DIR}/kazv-version.cpp
matrix-sdk.cpp
+ matrix-session.cpp
+ matrix-session-controller.cpp
sso-login-process.cpp
kazv-session-lock-guard.cpp
matrix-room.cpp
diff --git a/src/db-store.hpp b/src/db-store.hpp
--- a/src/db-store.hpp
+++ b/src/db-store.hpp
@@ -4,6 +4,7 @@
* SPDX-License-Identifier: AGPL-3.0-or-later
*/
+#pragma once
#include <kazv-defs.hpp>
#include <tuple>
#include <functional>
diff --git a/src/helper.hpp b/src/helper.hpp
--- a/src/helper.hpp
+++ b/src/helper.hpp
@@ -11,7 +11,7 @@
#include <QString>
#include <QStringList>
-
+#include <QObject>
#include <zug/transducer/map.hpp>
#include <cursorutil.hpp>
@@ -72,3 +72,14 @@
constexpr auto qStringToTrustLevel = zug::map(qStringToTrustLevelFunc);
inline auto roomMemberToAvatar = Kazv::eventContent | Kazv::jsonAtOr(std::string("avatar_url"), std::string(""));
+
+struct QObjectDeleterDeleteLater
+{
+ void operator()(QObject *obj) const
+ {
+ obj->deleteLater();
+ }
+};
+
+template<class T>
+using UniquePtrDL = std::unique_ptr<T, QObjectDeleterDeleteLater>;
diff --git a/src/matrix-sdk.hpp b/src/matrix-sdk.hpp
--- a/src/matrix-sdk.hpp
+++ b/src/matrix-sdk.hpp
@@ -22,25 +22,16 @@
#include <random-generator.hpp>
#include "meta-types.hpp"
-Q_MOC_INCLUDE("matrix-room-list.hpp")
-Q_MOC_INCLUDE("matrix-device-list.hpp")
Q_MOC_INCLUDE("matrix-promise.hpp")
Q_MOC_INCLUDE("matrix-event.hpp")
-Q_MOC_INCLUDE("matrix-sticker-pack-list.hpp")
-Q_MOC_INCLUDE("matrix-user-given-attrs-map.hpp")
-Q_MOC_INCLUDE("matrix-verification-list.hpp")
+Q_MOC_INCLUDE("matrix-session.hpp")
-class MatrixRoomList;
-class MatrixDeviceList;
class MatrixPromise;
class MatrixSdkTest;
class MatrixSdkSessionsTest;
-class MatrixEvent;
-class MatrixStickerPackList;
-class MatrixUserGivenAttrsMap;
class KazvSessionLockGuard;
class DbStore;
-class MatrixVerificationList;
+class MatrixSession;
struct MatrixSdkPrivate;
std::filesystem::path sessionDirForUserAndDeviceId(std::filesystem::path userDataDir, std::string userId, std::string deviceId);
@@ -59,14 +50,6 @@
void init();
public:
- enum CreateRoomPreset {
- PrivateChat = Kazv::CreateRoomPreset::PrivateChat,
- PublicChat = Kazv::CreateRoomPreset::PublicChat,
- TrustedPrivateChat = Kazv::CreateRoomPreset::TrustedPrivateChat,
- };
-
- Q_ENUM(CreateRoomPreset);
-
enum LoadSessionResult {
/// Successfully loaded the session
SessionLoadSuccess,
@@ -86,94 +69,27 @@
Q_ENUM(LoadSessionResult);
- enum MediaDownloadEndpointVersion {
- UnauthenticatedMediaV3, // Classical endpoint series
- AuthenticatedMediaV1, // Endpoints that needs authorization
- };
-
- Q_ENUM(MediaDownloadEndpointVersion);
-
explicit MatrixSdk(QObject *parent = 0);
~MatrixSdk() override;
- LAGER_QT_READER(QString, serverUrl);
- LAGER_QT_READER(QString, userId);
- LAGER_QT_READER(QString, token);
- LAGER_QT_READER(QString, deviceId);
- LAGER_QT_READER(immer::array<std::string>, specVersions); // The versions of the Matrix Spec supported by the server.
-
- Q_INVOKABLE MatrixRoomList *roomList() const;
-
- Q_INVOKABLE QString mxcUriToHttp(QString mxcUri) const;
-
- Q_INVOKABLE QString mxcUriToHttpAuthenticatedV1(QString mxcUri) const;
-
- Q_INVOKABLE MatrixDeviceList *devicesOfUser(QString userId) const;
-
- // Return true if version is at least as new as the spec version of server
- Q_INVOKABLE bool checkSpecVersion(QString version) const;
-
- // Return true if the spec version of server in the range [minVer, maxVer]
- Q_INVOKABLE bool checkSpecVersionRange(QString minVer, QString maxVer) const;
-
- Q_INVOKABLE QStringList directRoomIds(QString userId) const;
-
- Q_INVOKABLE MatrixVerificationList *verificationList() const;
-
- Kazv::RandomInterface &randomGenerator() const;
+ /**
+ * Get the current session.
+ */
+ Q_INVOKABLE MatrixSession *session() const;
private:
// Replaces the store with another one
void emplace(std::optional<Kazv::SdkModel> model, std::unique_ptr<KazvSessionLockGuard> lockGuard, std::unique_ptr<DbStore> dbStore);
- static std::string validateHomeserverUrl(const QString &url);
-
Q_SIGNALS:
void trigger(Kazv::KazvEvent e);
- void loginSuccessful(Kazv::KazvEvent e);
- void loginFailed(QString errorCode, QString errorMsg);
- void discoverFailed(QString errorCode, QString errorMsg);
- void discoverSuccessful(QString serverUrl);
- void getLoginFlowsFailed(QString errorCode, QString errorMsg);
- void getLoginFlowsSuccessful(QJsonValue flows);
- void ssoLoginTokenAvailable();
- void logoutSuccessful();
- void logoutFailed(QString errorCode, QString errorMsg);
-
- void receivedMessage(QString roomId, QString eventId);
-
void sessionChanged();
void loadSessionFinished(QString sessionName, MatrixSdk::LoadSessionResult result);
void dbResolved(bool success);
public Q_SLOTS:
- void login(const QString &userId, const QString &password, const QString &homeserverUrl);
-
- /**
- * Auto-discover the server url and then get the login flows from the server.
- *
- * If homeserverUrl is not provided, try to get it from auto-discovery.
- * If discovery is successful, or it is already provided, get the login flows
- * by calling getLoginFlows.
- */
- void discoverAndGetLoginFlows(const QString &userId, const QString &homeserverUrl);
- void getLoginFlows(const QString &serverUrl);
-
- /**
- * Start SSO login flow.
- *
- * It will start an http server on a local port and pass it as
- * the redirect url to the SSO login link.
- *
- * When the user completes the SSO login flow, we get the login token
- * for us to login via the token flow.
- *
- * @return The SSO login link for the user to open.
- */
- QUrl ssoLoginStart(const QString &homeserverUrl);
- void logout();
/**
* Serialize data to <AppDataDir>/sessions/<userid>/<deviceid>/
@@ -217,174 +133,6 @@
*/
QStringList allSessions() const;
- /**
- * Create a new room.
- *
- * @param isPrivate Whether the room is private.
- * @param name The room's name.
- * @param alias The alias of the room.
- * @param invite List of matrix ids of users to invite.
- * @param isDirect Whether it is a direct message room.
- * @param allowFederate Whether to allow users on other servers to join.
- * @param topic The topic of the room.
- * @param powerLevelContentOverride The content to override m.room.power_levels event.
- * @param preset The preset to create the room with.
- * @param encrypted Whether to enable encryption for this room.
- */
- MatrixPromise *createRoom(
- bool isPrivate,
- const QString &name,
- const QString &alias,
- const QStringList &invite,
- bool isDirect,
- bool allowFederate,
- const QString &topic,
- const QJsonValue &powerLevelContentOverride,
- CreateRoomPreset preset,
- bool encrypted
- );
-
- /**
- * Join a room.
- * @param idOrAlias The id or alias of the room to join.
- * @param servers The servers to use when joining the room.
- */
- MatrixPromise *joinRoom(
- const QString &idOrAlias,
- const QStringList &servers
- );
-
- /**
- * Change the trust level of a device.
- *
- * @param userId The user id that owns the device.
- * @param deviceId The device id to set the trust level.
- * @param trustLevel The trust level.
- *
- * @return A MatrixPromise representing the progress.
- */
- MatrixPromise *setDeviceTrustLevel(QString userId, QString deviceId, QString trustLevel);
-
- /**
- * Get the profile of the current user.
- *
- * @return A MatrixPromise representing the progress.
- */
- MatrixPromise *getSelfProfile();
-
- /**
- * Set the display name of the current user.
- *
- * @return A MatrixPromise representing the progress.
- */
- MatrixPromise *setDisplayName(QString displayName);
-
- /**
- * Set the avatar url of the current user.
- *
- * @return A MatrixPromise representing the progress.
- */
- MatrixPromise *setAvatarUrl(QString avatarUrl);
-
- /**
- * Check if an event should be notified.
- *
- * @param event The event to check.
- * @return Whether `event` should be notified.
- */
- bool shouldNotify(MatrixEvent *event) const;
-
- /**
- * Check if an event should be notified with sound.
- *
- * You should only call this method when `shouldNotify(event)`
- * returns true.
- *
- * @param event The event to check.
- * @return Whether `event` should be notified with sound.
- */
- bool shouldPlaySound(MatrixEvent *event) const;
-
- /**
- * Get the sticker pack list for the current account.
- *
- * @return A list of sticker packs associated with the current account.
- */
- MatrixStickerPackList *stickerPackList() const;
-
- /**
- * Get the sticker rooms account data event for the current account.
- *
- * @return A MatrixEvent representing the sticker rooms account data event.
- */
- MatrixEvent *stickerRoomsEvent() const;
-
- /**
- * Update the sticker pack from source.
- *
- * @param source The source of the sticker pack to update.
- * @return A promise that resolves when the sticker pack is updated,
- * or when there is an error.
- */
- MatrixPromise *updateStickerPack(MatrixStickerPackSource source);
-
- MatrixUserGivenAttrsMap *userGivenNicknameMap() const;
-
- MatrixPromise *sendAccountData(const QString &type, const QJsonObject &content);
-
- /**
- * Get all Matrix Spec versions supported by the server.
- * Use MatrixSdk::supportSpecVersion() to check if a version is supported.
- */
- MatrixPromise *getSpecVersions();
-
- MatrixPromise *addDirectRoom(const QString &userId, const QString &roomId);
-
- MatrixPromise *getRoomIdByAlias(const QString &roomAlias);
-
- /**
- * Purge events in all rooms except those specified in roomIds
- *
- * @param roomIds The ids for rooms to NOT be purged. This allows
- * keeping the rooms that are already open intact.
- * @return A MatrixPromise that resolves when the events are purged.
- */
- MatrixPromise *purgeEventsExceptRooms(const QStringList &roomIds);
-
- /**
- * Backfill from the event storage from an existing event in a room.
- *
- * @param roomId The id of the room to operate on.
- * @param eventId The id of the event to backfill from.
- * @return A MatrixPromise that resolves when the events are backfilled.
- * The Promise is considered successful if and only if at least one event
- * is loaded.
- */
- MatrixPromise *backfillRoomFromEvent(const QString &roomId, const QString &eventId);
-
- /**
- * Import from key backup file.
- *
- * @param fileUrl The url of the key backup file. Will be passed to
- * QUrl::toLocalFile(), and must be a local file.
- * @param password The password to decrypt the key backup file.
- * @return A MatrixPromise that resolves when the keys are imported.
- * If successful, `data["imported"]` contains the number of imported keys.
- * If unsuccessful, `data` contains a standard error structure.
- */
- MatrixPromise *importFromKeyBackupFile(QUrl fileUrl, QString password);
-
- /**
- * Send an outbound device verification request.
- *
- * @param userId The user id of the device to verify.
- * @param deviceId The device id of the device to verify.
- */
- MatrixPromise *requestVerifyDevice(QString userId, QString deviceId);
-
-private:
- MatrixPromise *sendAccountDataImpl(Kazv::Event event);
-
private: // Testing
friend MatrixSdkTest;
friend MatrixSdkSessionsTest;
diff --git a/src/matrix-sdk.cpp b/src/matrix-sdk.cpp
--- a/src/matrix-sdk.cpp
+++ b/src/matrix-sdk.cpp
@@ -6,21 +6,7 @@
#include <kazv-defs.hpp>
-#include <boost/archive/text_oarchive.hpp>
-#include <boost/archive/text_iarchive.hpp>
-#include <fstream>
-#include <filesystem>
-#include <chrono>
-
-#include <QMutex>
-#include <QMutexLocker>
#include <QtConcurrent>
-#include <QThreadPool>
-#include <KConfig>
-#include <KConfigGroup>
-#include <QCoroTask>
-#include <eventemitter/lagerstoreeventemitter.hpp>
-#include <client/sdk.hpp>
#include <client/notification-handler.hpp>
#include <crypto/base64.hpp>
#include <csapi/directory.hpp>
@@ -30,75 +16,24 @@
#include <lager/event_loop/qt.hpp>
#include "matrix-sdk.hpp"
-#include "matrix-room-list.hpp"
#include "matrix-promise.hpp"
#include "matrix-event.hpp"
+#include "matrix-session-types.hpp"
#include "helper.hpp"
#include "kazv-path-config.hpp"
#include "kazv-version.hpp"
#include "qt-json.hpp"
#include "qt-rand-adapter.hpp"
-#include "qt-promise-handler.hpp"
#include "qt-job-handler.hpp"
-#include "device-mgmt/matrix-device-list.hpp"
-#include "matrix-sticker-pack-list.hpp"
-#include "matrix-sticker-pack-list-p.hpp"
-#include "matrix-user-given-attrs-map.hpp"
#include "kazv-log.hpp"
#include "matrix-utils.hpp"
+#include "matrix-session-controller.hpp"
#include "kazv-session-lock-guard.hpp"
#include "db-store.hpp"
-#include "sso-login-process.hpp"
-#include "matrix-verification-list.hpp"
using namespace Qt::Literals::StringLiterals;
using namespace Kazv;
-static const std::string clientName = "kazv";
-
-// Sdk with qt event loop, identity transform and no enhancers
-using SdkT =
- decltype(makeSdk(
- SdkModel{},
- detail::declref<JobInterface>(),
- detail::declref<EventInterface>(),
- QtPromiseHandler(detail::declref<QObject>()),
- zug::identity,
- withRandomGenerator(detail::declref<RandomInterface>())));
-
-struct QtEventLoop
-{
- QObject *m_obj;
-
- template<class Fn>
- void async(Fn &&) { throw std::runtime_error{"not implemented!"}; }
-
- template<class Fn>
- void post(Fn &&fn)
- {
- QMetaObject::invokeMethod(
- m_obj, std::forward<Fn>(fn), Qt::QueuedConnection
- );
- }
-
- void finish() {}
- void pause() { throw std::runtime_error{"not implemented!"}; }
- void resume() { throw std::runtime_error{"not implemented!"}; }
-};
-
-QCoro::Task<std::pair<bool, QString>> setupAndImport(SdkModel model, DbStore *dbStore, std::string userDataDir)
-{
- auto res = co_await dbStore->setup(
- userDataDir,
- model.c().userId,
- model.c().deviceId
- );
- if (!res.first) {
- co_return res;
- }
- co_return co_await dbStore->importAllFrom(model);
-}
-
std::filesystem::path sessionDirForUserAndDeviceId(std::filesystem::path userDataDir, std::string userId, std::string deviceId)
{
auto encodedUserId = encodeBase64(userId, Base64Opts::urlSafe);
@@ -107,60 +42,20 @@
return sessionDir;
}
-struct PendingSaveEvents : public SaveEventsRequested
+struct MatrixSdkPrivate
{
- using DataT = immer::map<std::string, EventList>;
- void add(SaveEventsRequested s)
- {
- timelineEvents = addOneType(std::move(timelineEvents), s.timelineEvents);
- nonTimelineEvents = addOneType(std::move(nonTimelineEvents), s.nonTimelineEvents);
- }
-
- static DataT addOneType(DataT orig, DataT addon)
+ MatrixSdkPrivate(MatrixSdk *q, bool testing)
+ : testing(testing)
+ , userDataDir(kazvUserDataDir().toStdString())
+ , thread(new QThread(q))
+ , controller(new MatrixSessionController(thread, userDataDir))
{
- for (const auto &[roomId, el]: addon) {
- orig = std::move(orig).update(roomId, [&el](EventList origEl) {
- // This works without explicit dedupe because we do not do parallel
- // inserts and the insert order is the same as list order.
- return origEl + el;
- });
- }
- return orig;
}
-};
-
-enum DbStatus
-{
- Pending,
- Ready,
- Failed,
-};
-
-struct MatrixSdkPrivate
-{
- MatrixSdkPrivate(MatrixSdk *q, bool testing, std::unique_ptr<KazvSessionLockGuard> lockGuard, std::unique_ptr<DbStore> dbStore);
- MatrixSdkPrivate(MatrixSdk *q, bool testing, SdkModel model, std::unique_ptr<KazvSessionLockGuard> lockGuard, std::unique_ptr<DbStore> dbStore);
bool testing;
std::string userDataDir;
- std::unique_ptr<KazvSessionLockGuard> lockGuard;
- RandomInterface randomGenerator;
- std::unique_ptr<DbStore> dbStore;
QThread *thread;
- QObject *obj;
- QtJobHandler *jobHandler;
- LagerStoreEventEmitter ee;
- LagerStoreEventEmitter::Watchable watchable;
- SdkT sdk;
+ UniquePtrDL<MatrixSessionController> controller;
QTimer saveTimer;
- QPointer<SsoLoginProcess> ssoLoginProcess{};
-
- using SecondaryRootT = decltype(sdk.createSecondaryRoot(std::declval<lager::with_qt_event_loop>()));
- SecondaryRootT secondaryRoot;
- Client clientOnSecondaryRoot;
- NotificationHandler notificationHandler;
- DbStatus dbStatus;
- PendingSaveEvents pendingSaveEvents{};
- lager::state<VerificationTrackerModel> verificationTrackerState;
void runIoContext() {
thread->start();
@@ -171,290 +66,22 @@
void maybeSerialize()
{
- if (!testing) {
- serializeClientToFile(clientOnSecondaryRoot);
- }
- }
-
- void serializeClientToFile(Client c);
-
- void saveOrQueueEvents(SaveEventsRequested s)
- {
- if (dbStatus == Ready) {
- saveEvents(std::move(s));
- } else if (dbStatus == Pending) {
- pendingSaveEvents.add(std::move(s));
- }
- // Database open failed, do not do anything
- }
-
- void saveEvents(SaveEventsRequested s)
- {
- dbStore->saveEvents(std::move(s))
- .then([](const auto &stat) {
- if (!stat.first) {
- qCWarning(kazvLog) << "Cannot save events:" << stat.second;
- }
- });
- }
-};
-
-// Cleaning up notes:
-// 0. Callback functions may store the context for an indefinite time
-// 1. The QThread event loop can be stopped
-// 2. QtJobHandler::submit() should only happen in the primary event loop thread
-// 3. QtJobHandler lives in the primary event loop thread
-// 4. QtJobs live in the primary event loop thread
-// 5. Job callbacks are called in the primary event loop thread
-// 6. QtPromise::then() callbacks are called in the primary event loop thread
-// 7. When the QThread event loop stops, no more callbacks will be executed (there is nothing to post to)
-// 8. The QThread should stop before obj is deleted
-class CleanupHelper : public QObject
-{
- Q_OBJECT
-public:
- explicit CleanupHelper(std::unique_ptr<MatrixSdkPrivate> d)
- : oldD(std::move(d))
- {
- }
-
- std::unique_ptr<MatrixSdkPrivate> oldD;
-
- void cleanup()
- {
- qCInfo(kazvLog) << "start to clean up everything";
- if (oldD->ssoLoginProcess) {
- oldD->ssoLoginProcess->deleteLater();
- }
-
- oldD->clientOnSecondaryRoot.stopSyncing()
- .then([obj=oldD->obj, thread=oldD->thread](auto &&) {
- qCDebug(kazvLog) << "stopped syncing";
- QMetaObject::invokeMethod(obj, [thread]() {
- thread->quit();
- });
- });
- oldD->thread->wait();
- oldD->thread->deleteLater();
- // After the thread's event loop is finished, we can delete the root object
- oldD->obj->deleteLater();
- this->deleteLater();
- qCInfo(kazvLog) << "thread is done";
+ // TODO: restore
+ // if (!testing) {
+ // serializeClientToFile(clientOnSecondaryRoot);
+ // }
}
};
-void MatrixSdkPrivate::serializeClientToFile(Client c)
-{
- using namespace Kazv::CursorOp;
- auto userId = +c.userId();
- auto deviceId = +c.deviceId();
- if (userId.empty() || deviceId.empty()) {
- qDebug() << "Not logged in, nothing to serialize";
- return;
- }
-
- using StdPath = std::filesystem::path;
- auto userDataDir = StdPath(this->userDataDir);
- auto sessionDir = sessionDirForUserAndDeviceId(userDataDir, userId, deviceId);
-
- auto storeFile = sessionDir / "store";
- auto storeFileNew = sessionDir / "store.new";
-
- auto metadataFile = sessionDir / "metadata";
-
- qDebug() << "storeFile=" << QString::fromStdString(storeFile.string());
-
- std::error_code err;
- if ((! std::filesystem::create_directories(sessionDir, err))
- && err) {
- qDebug() << "Unable to create sessionDir";
- return;
- }
-
- if (!lockGuard) {
- try {
- lockGuard = std::make_unique<KazvSessionLockGuard>(sessionDir);
- } catch (const std::runtime_error &e) {
- qCWarning(kazvLog) << "Error locking session: " << e.what();
- return;
- }
- }
-
- try {
- auto storeStream = std::ofstream(storeFileNew);
- if (! storeStream) {
- qCWarning(kazvLog) << "Unable to open storeFile";
- return;
- }
- using OAr = boost::archive::text_oarchive;
- auto archive = OAr{storeStream};
- c.serializeTo(archive);
- } catch (const std::exception &e) {
- qCWarning(kazvLog) << "Cannot write to store file: " << e.what();
- return;
- }
-
- err.clear();
- std::filesystem::rename(storeFileNew, storeFile, err);
- if (err) {
- qCWarning(kazvLog) << "Cannot move storeFile into place: " << QString::fromStdString(err.message());
- return;
- }
- qDebug() << "Serialization done";
-
-
- // store metadata
- {
- KConfig metadata(QString::fromStdString(metadataFile.string()));
- KConfigGroup mdGroup(&metadata, u"Metadata"_s);
- mdGroup.writeEntry("kazvVersion", QString::fromStdString(kazvVersionString()));
- mdGroup.writeEntry("archiveFormat", "text-with-sqlite");
- }
-}
-
-MatrixSdkPrivate::MatrixSdkPrivate(MatrixSdk *q, bool testing, std::unique_ptr<KazvSessionLockGuard> lockGuard, std::unique_ptr<DbStore> dbStore)
- : testing(testing)
- , userDataDir{kazvUserDataDir().toStdString()}
- , lockGuard(std::move(lockGuard))
- , randomGenerator(QtRandAdapter{})
- , dbStore(std::move(dbStore))
- , thread(new QThread())
- , obj(new QObject())
- , jobHandler(new QtJobHandler(obj))
- , ee{QtEventLoop{obj}}
- , watchable(ee.watchable())
- , sdk(makeDefaultSdkWithCryptoRandom(
- randomGenerator.generateRange<std::string>(makeDefaultSdkWithCryptoRandomSize()),
- static_cast<JobInterface &>(*jobHandler),
- static_cast<EventInterface &>(ee),
- QtPromiseHandler(*obj),
- zug::identity,
- withRandomGenerator(randomGenerator)))
- , secondaryRoot(sdk.createSecondaryRoot(QtEventLoop{q}))
- , clientOnSecondaryRoot(sdk.clientFromSecondaryRoot(secondaryRoot))
- , notificationHandler(clientOnSecondaryRoot.notificationHandler())
- , dbStatus(this->dbStore ? Ready : Pending)
-{
- obj->moveToThread(thread);
-}
-
-MatrixSdkPrivate::MatrixSdkPrivate(MatrixSdk *q, bool testing, SdkModel model, std::unique_ptr<KazvSessionLockGuard> lockGuard, std::unique_ptr<DbStore> dbStore)
- : testing(testing)
- , userDataDir{kazvUserDataDir().toStdString()}
- , lockGuard(std::move(lockGuard))
- , randomGenerator(QtRandAdapter{})
- , dbStore(std::move(dbStore))
- , thread(new QThread())
- , obj(new QObject())
- , jobHandler(new QtJobHandler(obj))
- , ee{QtEventLoop{obj}}
- , watchable(ee.watchable())
- , sdk(makeSdk(
- model,
- static_cast<JobInterface &>(*jobHandler),
- static_cast<EventInterface &>(ee),
- QtPromiseHandler(*obj),
- zug::identity,
- withRandomGenerator(randomGenerator)))
- , secondaryRoot(sdk.createSecondaryRoot(QtEventLoop{q}, std::move(model)))
- , clientOnSecondaryRoot(sdk.clientFromSecondaryRoot(secondaryRoot))
- , notificationHandler(clientOnSecondaryRoot.notificationHandler())
- , dbStatus(this->dbStore ? Ready : Pending)
-{
- obj->moveToThread(thread);
-}
-
MatrixSdk::MatrixSdk(std::unique_ptr<MatrixSdkPrivate> d, QObject *parent)
: QObject(parent)
, m_d(std::move(d))
{
init();
- connect(this, &MatrixSdk::trigger,
- this, [this](KazvEvent e) {
- if (std::holds_alternative<SaveEventsRequested>(e)) {
- auto s = std::get<SaveEventsRequested>(e);
- m_d->saveOrQueueEvents(s);
- }
- });
-
- // loginSuccessful will be emitted only for new sessions (not for loaded
- // sessions),
- connect(this, &MatrixSdk::loginSuccessful, this, [this]() {
- if (m_d->dbStore) {
- qCWarning(kazvLog) << "Trying to replace current db store, ignoring";
- return;
- }
- m_d->dbStore = std::make_unique<DbStore>();
- // This must be called from the GUI thread
- auto model = m_d->secondaryRoot.get();
- setupAndImport(std::move(model), m_d->dbStore.get(), m_d->userDataDir)
- .then([this](const auto &stat) {
- if (!stat.first) {
- qCWarning(kazvLog) << "Cannot set up database:" << stat.second;
- }
- Q_EMIT dbResolved(stat.first);
- });
- });
-
- connect(this, &MatrixSdk::dbResolved, this, [this](bool success) {
- if (!success) {
- m_d->dbStatus = Failed;
- } else {
- qCDebug(kazvLog) << "db loading succeeded";
- auto pendingSaveEvents = std::move(m_d->pendingSaveEvents);
- m_d->pendingSaveEvents = {};
- m_d->saveEvents(std::move(pendingSaveEvents));
- m_d->dbStatus = Ready;
- }
- });
}
void MatrixSdk::init()
{
- LAGER_QT(serverUrl) = m_d->clientOnSecondaryRoot.serverUrl().xform(strToQt); Q_EMIT serverUrlChanged(serverUrl());
- LAGER_QT(userId) = m_d->clientOnSecondaryRoot.userId().xform(strToQt); Q_EMIT userIdChanged(userId());
- LAGER_QT(token) = m_d->clientOnSecondaryRoot.token().xform(strToQt); Q_EMIT tokenChanged(token());
- LAGER_QT(deviceId) = m_d->clientOnSecondaryRoot.deviceId().xform(strToQt); Q_EMIT deviceIdChanged(deviceId());
- LAGER_QT(specVersions) = m_d->clientOnSecondaryRoot.supportVersions(); Q_EMIT specVersionsChanged(specVersions());
-
- m_d->watchable.afterAll(
- [this](KazvEvent e) {
- Q_EMIT this->trigger(e);
- });
-
- m_d->watchable.after<LoginSuccessful>(
- [this](LoginSuccessful e) {
- Q_EMIT this->loginSuccessful(e);
- });
-
- m_d->watchable.after<LoginFailed>(
- [this](LoginFailed e) {
- Q_EMIT this->loginFailed(
- QString::fromStdString(e.errorCode),
- QString::fromStdString(e.error)
- );
- });
-
- m_d->watchable.after<ReceivingRoomTimelineEvent>(
- [this](ReceivingRoomTimelineEvent e) {
- Q_EMIT this->receivedMessage(
- QString::fromStdString(e.roomId),
- QString::fromStdString(e.event.id())
- );
- });
-
- m_d->watchable.after<VerificationTrackerModelChanged>(
- [this](VerificationTrackerModelChanged) {
- auto model = lager::get<VerificationTracker>(m_d->sdk.context()).model;
- QMetaObject::invokeMethod(
- this, [this, model=std::move(model)]() {
- qCDebug(kazvLog) << "Verification tracker model changed" << model.processes.size();
- m_d->verificationTrackerState.set(model);
- lager::commit(m_d->verificationTrackerState);
- });
- }
- );
-
connect(&m_d->saveTimer, &QTimer::timeout, &m_d->saveTimer, [m_d=m_d.get()]() {
m_d->maybeSerialize();
});
@@ -465,9 +92,7 @@
MatrixSdk::MatrixSdk(QObject *parent)
: MatrixSdk(std::make_unique<MatrixSdkPrivate>(
this,
- /* testing = */ false,
- std::unique_ptr<KazvSessionLockGuard>(),
- std::unique_ptr<DbStore>()
+ /* testing = */ false
), parent)
{
}
@@ -475,315 +100,13 @@
MatrixSdk::MatrixSdk(SdkModel model, bool testing, QObject *parent)
: MatrixSdk(std::make_unique<MatrixSdkPrivate>(
this,
- testing,
- std::move(model),
- std::unique_ptr<KazvSessionLockGuard>(),
- std::unique_ptr<DbStore>()
+ testing
), parent)
{
}
-static void cleanupDPointer(std::unique_ptr<MatrixSdkPrivate> oldD)
-{
- oldD->saveTimer.disconnect();
- oldD->saveTimer.stop();
-
- auto helper = new CleanupHelper(std::move(oldD));
- helper->cleanup();
-}
-
-MatrixSdk::~MatrixSdk()
-{
- if (m_d) {
- serializeToFile();
- cleanupDPointer(std::move(m_d));
- }
-}
-
-QString MatrixSdk::mxcUriToHttp(QString mxcUri) const
-{
- return QString::fromStdString(m_d->clientOnSecondaryRoot.mxcUriToHttp(mxcUri.toStdString()));
-}
-
-QString MatrixSdk::mxcUriToHttpAuthenticatedV1(QString mxcUri) const
-{
- return QString::fromStdString(m_d->clientOnSecondaryRoot.mxcUriToHttpV1(mxcUri.toStdString()));
-}
-
-MatrixDeviceList *MatrixSdk::devicesOfUser(QString userId) const
-{
- return new MatrixDeviceList(m_d->clientOnSecondaryRoot.devicesOfUser(userId.toStdString()));
-}
-
-bool isIllFormatSpecVersion(const QString &version)
-{
- const bool isLegacy = version.startsWith(u"r"_s);
- if (isLegacy && version.split(u'.').size() == 3) {
- return false;
- }
- if (!isLegacy && version.split(u'.').size() == 2) {
- return false;
- }
- return true;
-}
-
-// Return true if v1 is at least as new as v2, false if v1 is older than v2
-// Return false if v1 or v2 is ill-format
-bool compareSpecVersion(QString v1, QString v2)
-{
- // Check parameters format
- if (isIllFormatSpecVersion(v1) || isIllFormatSpecVersion(v2)) {
- return false;
- }
-
- const bool v1IsLegacy = v1.startsWith(u"r"_s);
- const bool v2IsLegacy = v2.startsWith(u"r"_s);
- if (v1IsLegacy != v2IsLegacy) {
- return v2IsLegacy;
- }
-
- v1.remove(0, 1);
- v2.remove(0, 1);
- auto v1VersionNumbers = v1.split(u'.');
- auto v2VersionNumbers = v2.split(u'.');
-
- for (int i = 0; i < v1VersionNumbers.size(); i++) {
- auto v1VerNum = v1VersionNumbers[i].toInt();
- auto v2VerNum = v2VersionNumbers[i].toInt();
- if (v1VerNum != v2VerNum) {
- return v1VerNum > v2VerNum;
- }
- }
- // v1 is equal to v2
- return true;
-}
-
-// Return true if version in the range [minVer, maxVer]
-// Return false if any parameter is ill-format
-bool compareSpecVersionRange(const QString &version,
- const QString &minVer, const QString &maxVer)
-{
- if (isIllFormatSpecVersion(version)
- || isIllFormatSpecVersion(minVer)
- || isIllFormatSpecVersion(maxVer)) {
- return false;
- }
-
- if (compareSpecVersion(version, minVer) && compareSpecVersion(maxVer, version)) {
- return true;
- }
-
- return false;
-}
-
-bool MatrixSdk::checkSpecVersion(QString version) const
-{
- return std::find_if(specVersions().begin(), specVersions().end(),
- [&version](auto v) {
- return compareSpecVersion(QString::fromStdString(v), version);
- }) != specVersions().end();
-}
-
-bool MatrixSdk::checkSpecVersionRange(QString minVer, QString maxVer) const
-{
- return std::find_if(specVersions().begin(), specVersions().end(),
- [&minVer, maxVer](auto v) {
- return compareSpecVersionRange(QString::fromStdString(v), minVer, maxVer);
- }) != specVersions().end();
-}
-
-QStringList MatrixSdk::directRoomIds(QString userId) const
-{
- auto content = m_d->clientOnSecondaryRoot.accountData().get()["m.direct"].content().get();
- auto roomIds = QStringList{};
- for (auto i : content[userId.toStdString()]) {
- roomIds.push_back(QString::fromStdString(i.get<std::string>()));
- }
- return roomIds;
-}
-
-MatrixVerificationList *MatrixSdk::verificationList() const
-{
- return new MatrixVerificationList(m_d->clientOnSecondaryRoot, m_d->verificationTrackerState);
-}
-
-std::string MatrixSdk::validateHomeserverUrl(const QString &url)
-{
- if (url.isEmpty()) {
- return std::string();
- }
-
- auto u = QUrl::fromUserInput(url);
- if (!u.isValid()) {
- return std::string();
- }
-
- if (u.scheme() == u"http"_s) {
- qCInfo(kazvLog) << "url" << u << "is http. Force switching to https.";
- u.setScheme(u"https"_s);
- } else if (u.scheme() != u"https"_s) {
- qCWarning(kazvLog) << "url" << u << "is not http/https.";
- return std::string();
- }
- return u.toString().toStdString();
-}
-
-void MatrixSdk::login(const QString &userId, const QString &password, const QString &homeserverUrl)
-{
- auto loginFunc = [userId, password](const Client &client, const std::string &serverUrl) {
- client.passwordLogin(
- serverUrl,
- userId.toStdString(),
- password.toStdString(),
- clientName
- );
- };
-
- auto validated = validateHomeserverUrl(homeserverUrl);
- if (!validated.empty()) {
- loginFunc(m_d->clientOnSecondaryRoot, validated);
- } else {
- m_d->clientOnSecondaryRoot
- .autoDiscover(userId.toStdString()) // autoDiscover() will dispatch GetVersionAction to get supported versions of the server
- .then([
- this,
- client=m_d->clientOnSecondaryRoot.toEventLoop(),
- userId,
- password,
- loginFunc
- ](auto res) {
- if (!res.success()) {
- // FIXME use real error codes and msgs when available in libkazv
- Q_EMIT this->discoverFailed(u""_s, u""_s);
- return res;
- }
-
- auto serverUrl = res.dataStr("homeserverUrl");
-
- loginFunc(client, serverUrl);
-
- return res;
- });
- }
-
- m_d->runIoContext();
-}
-
-void MatrixSdk::discoverAndGetLoginFlows(const QString &userId, const QString &homeserverUrl)
-{
- auto validated = validateHomeserverUrl(homeserverUrl);
- if (!validated.empty()) {
- getLoginFlows(homeserverUrl);
- } else {
- m_d->clientOnSecondaryRoot
- .autoDiscover(userId.toStdString())
- .then([
- this
- ](const EffectStatus &res) {
- if (!res.success()) {
- Q_EMIT discoverFailed(
- QString::fromStdString(res.dataStr("errorCode")),
- QString::fromStdString(res.dataStr("error"))
- );
- return;
- }
- auto serverUrl = QString::fromStdString(res.dataStr("homeserverUrl"));
- Q_EMIT discoverSuccessful(serverUrl);
- getLoginFlows(serverUrl);
- });
- }
-
- m_d->runIoContext();
-}
-
-void MatrixSdk::getLoginFlows(const QString &serverUrl)
-{
- m_d->jobHandler->submit(
- Api::GetLoginFlowsJob(serverUrl.toStdString()),
- [this](Api::GetLoginFlowsResponse r) {
- if (!r.success()) {
- Q_EMIT getLoginFlowsFailed(
- QString::fromStdString(r.errorCode()),
- QString::fromStdString(r.errorMessage())
- );
- return;
- }
- auto v = std::move(r).jsonBody().get().at("flows").template get<QJsonValue>();
- Q_EMIT getLoginFlowsSuccessful(v);
- }
- );
-}
-
-QUrl MatrixSdk::ssoLoginStart(const QString &homeserverUrl)
-{
- if (m_d->ssoLoginProcess) {
- m_d->ssoLoginProcess->deleteLater();
- }
- m_d->ssoLoginProcess = new SsoLoginProcess();
- if (!m_d->ssoLoginProcess->startServer()) {
- return QUrl();
- }
- auto link = m_d->ssoLoginProcess->getSsoLink(homeserverUrl);
- connect(m_d->ssoLoginProcess.get(), &SsoLoginProcess::loginTokenAvailable,
- this, [this, homeserverUrl](const QString &loginToken) {
- Q_EMIT ssoLoginTokenAvailable();
- qCInfo(kazvLog) << "Got SSO login token";
- m_d->ssoLoginProcess->deleteLater();
- m_d->clientOnSecondaryRoot.mLoginTokenLogin(
- homeserverUrl.toStdString(),
- loginToken.toStdString(),
- clientName
- );
- });
- return link;
-}
-
-void MatrixSdk::logout()
-{
- m_d->clientOnSecondaryRoot.logout()
- .then([&] (EffectStatus stat) {
- if (stat.success()) {
- m_d->stopIoContext();
- Q_EMIT this->logoutSuccessful();
- } else {
- Q_EMIT this->logoutFailed(QString::fromStdString(stat.dataStr("errorCode")), QString::fromStdString(stat.dataStr("error")));
- }
- });
-}
-
-MatrixRoomList *MatrixSdk::roomList() const
-{
- return new MatrixRoomList(m_d->clientOnSecondaryRoot);
-}
-
-void MatrixSdk::emplace(std::optional<SdkModel> model, std::unique_ptr<KazvSessionLockGuard> lockGuard, std::unique_ptr<DbStore> dbStore)
-{
- auto testing = m_d->testing;
- auto userDataDir = m_d->userDataDir;
-
- if (m_d) {
- cleanupDPointer(std::move(m_d));
- }
-
- m_d = model.has_value()
- ? std::make_unique<MatrixSdkPrivate>(
- this, testing, std::move(model.value()),
- std::move(lockGuard), std::move(dbStore))
- : std::make_unique<MatrixSdkPrivate>(
- this, testing, std::move(lockGuard), std::move(dbStore)
- );
- m_d->userDataDir = userDataDir;
-
- // Re-initialize lager-qt cursors and watchable connections
- init();
-
- m_d->runIoContext();
-
- m_d->clientOnSecondaryRoot.startSyncing();
-
- Q_EMIT sessionChanged();
-}
+MatrixSdk::~MatrixSdk() = default;
QStringList MatrixSdk::allSessions() const
{
@@ -825,100 +148,10 @@
void MatrixSdk::loadSession(QString sessionName)
{
- using StdPath = std::filesystem::path;
- auto loadFromSession = [this, sessionName](StdPath sessionDir, std::unique_ptr<KazvSessionLockGuard> lockGuard) {
- auto storeFile = sessionDir / "store";
-
- auto metadataFile = sessionDir / "metadata";
-
- if (! std::filesystem::exists(storeFile)) {
- qDebug() << "storeFile does not exist, skip loading session " << sessionName;
- Q_EMIT loadSessionFinished(sessionName, SessionNotFound);
- return;
- }
-
- if (std::filesystem::exists(metadataFile)) {
- KConfig metadata(QString::fromStdString(metadataFile.string()));
- KConfigGroup mdGroup(&metadata, u"Metadata"_s);
- auto format = mdGroup.readEntry(u"archiveFormat"_s);
- if (format != u"text"_s && format != u"text-with-sqlite"_s) {
- qDebug() << "Unknown archive format:" << format;
- Q_EMIT loadSessionFinished(sessionName, SessionFormatUnknown);
- return;
- }
- auto version = mdGroup.readEntry(u"kazvVersion"_s);
- auto curVersion = kazvVersionString();
- if (version != QString::fromStdString(curVersion)) {
- qDebug() << "A different version from the current one, making a backup";
- std::error_code err;
- auto now = std::chrono::system_clock::now();
- auto backupName =
- std::to_string(std::chrono::duration_cast<std::chrono::seconds>(now.time_since_epoch()).count());
- auto backupDir = sessionDir / "backup" / backupName;
- if (! std::filesystem::create_directories(backupDir, err)
- && err) {
- qDebug() << "Cannot create backup directory";
- Q_EMIT loadSessionFinished(sessionName, SessionCannotBackup);
- return;
- }
- std::filesystem::copy_file(storeFile, backupDir / "store");
- std::filesystem::copy_file(metadataFile, backupDir / "metadata");
- }
- }
-
- SdkModel model;
- try {
- auto storeStream = std::ifstream(storeFile);
- if (! storeStream) {
- qDebug() << "Unable to open storeFile";
- Q_EMIT loadSessionFinished(sessionName, SessionCannotOpenFile);
- return;
- }
- using IAr = boost::archive::text_iarchive;
- auto archive = IAr{storeStream};
- archive >> model;
- qDebug() << "Finished loading session store file";
- } catch (const std::exception &e) {
- qDebug() << "Error when loading session:" << QString::fromStdString(e.what());
- Q_EMIT loadSessionFinished(sessionName, SessionDeserializeFailed);
- return;
- }
-
- auto dbStore = std::make_unique<DbStore>();
- auto stat = QCoro::waitFor(setupAndImport(model, dbStore.get(), m_d->userDataDir));
- if (!stat.first) {
- qCWarning(kazvLog) << "Unable to open db store:" << stat.second;
- Q_EMIT loadSessionFinished(sessionName, SessionDeserializeFailed);
- return;
- }
- QMetaObject::invokeMethod(this, [this, model=std::move(model), lockGuard=std::move(lockGuard), dbStore=std::move(dbStore)]() mutable {
- emplace(std::move(model), std::move(lockGuard), std::move(dbStore));
- });
- Q_EMIT loadSessionFinished(sessionName, SessionLoadSuccess);
- };
-
- qDebug() << "in loadSession(), sessionName=" << sessionName;
- auto userDataDir = StdPath(m_d->userDataDir);
- auto parts = sessionName.split(u'/');
- if (parts.size() == 2) {
- auto userId = parts[0].toStdString();
- auto deviceId = parts[1].toStdString();
- auto sessionDir = sessionDirForUserAndDeviceId(userDataDir, userId, deviceId);
- std::unique_ptr<KazvSessionLockGuard> lockGuard;
- try {
- lockGuard = std::make_unique<KazvSessionLockGuard>(sessionDir);
- } catch (const std::runtime_error &e) {
- qCWarning(kazvLog) << "Error locking session: " << e.what();
- Q_EMIT loadSessionFinished(sessionName, SessionLockFailed);
- return;
- }
- QThreadPool::globalInstance()->start([loadFromSession, sessionDir, lockGuard=std::move(lockGuard)]() mutable {
- loadFromSession(sessionDir, std::move(lockGuard));
- });
- return;
- }
- qDebug(kazvLog) << "no session found for" << sessionName;
- Q_EMIT loadSessionFinished(sessionName, SessionNotFound);
+ m_d->controller.reset(
+ new MatrixSessionController(m_d->thread, m_d->userDataDir)
+ );
+ m_d->controller->load(sessionName);
}
bool MatrixSdk::deleteSession(QString sessionName) {
@@ -947,288 +180,20 @@
bool MatrixSdk::startNewSession()
{
- emplace(std::nullopt, std::unique_ptr<KazvSessionLockGuard>(), std::unique_ptr<DbStore>());
return true;
}
-static std::optional<std::string> optMaybe(QString s)
-{
- if (s.isEmpty()) {
- return std::nullopt;
- } else {
- return s.toStdString();
- }
-}
-
-MatrixPromise *MatrixSdk::createRoom(
- bool isPrivate,
- const QString &name,
- const QString &alias,
- const QStringList &invite,
- bool isDirect,
- bool allowFederate,
- const QString &topic,
- const QJsonValue &powerLevelContentOverride,
- CreateRoomPreset preset,
- bool encrypted
-)
-{
- immer::array<Event> initialState;
-
- if (encrypted) {
- initialState = {Event{json{
- {"type", "m.room.encryption"},
- {"state_key", ""},
- {"content", {
- {"algorithm", "m.megolm.v1.aes-sha2"},
- }},
- }}};
- }
-
- return new MatrixPromise(m_d->clientOnSecondaryRoot.createRoom(
- isPrivate ? Kazv::RoomVisibility::Private : Kazv::RoomVisibility::Public,
- optMaybe(name),
- optMaybe(alias),
- qStringListToStdF(invite),
- isDirect,
- allowFederate,
- optMaybe(topic),
- nlohmann::json(powerLevelContentOverride),
- static_cast<Kazv::CreateRoomPreset>(preset),
- initialState
- ));
-}
-
-MatrixPromise *MatrixSdk::joinRoom(const QString &idOrAlias, const QStringList &servers)
-{
- return new MatrixPromise(m_d->clientOnSecondaryRoot.joinRoom(
- idOrAlias.toStdString(),
- qStringListToStdF(servers)
- ));
-}
-
-MatrixPromise *MatrixSdk::setDeviceTrustLevel(QString userId, QString deviceId, QString trustLevel)
-{
- return new MatrixPromise(
- m_d->clientOnSecondaryRoot.setDeviceTrustLevel(
- userId.toStdString(),
- deviceId.toStdString(),
- qStringToTrustLevelFunc(trustLevel)
- )
- );
-}
-
-MatrixPromise *MatrixSdk::getSelfProfile()
-{
- return new MatrixPromise(
- m_d->clientOnSecondaryRoot.getProfile(userId().toStdString())
- );
-}
-
-MatrixPromise *MatrixSdk::setDisplayName(QString displayName)
-{
- return new MatrixPromise(
- m_d->clientOnSecondaryRoot.setDisplayName(
- displayName.isEmpty() ? std::nullopt : std::optional<std::string>(displayName.toStdString())
- )
- );
-}
-
-MatrixPromise *MatrixSdk::setAvatarUrl(QString avatarUrl)
-{
- return new MatrixPromise(
- m_d->clientOnSecondaryRoot.setAvatarUrl(
- avatarUrl.isEmpty() ? std::nullopt : std::optional<std::string>(avatarUrl.toStdString())
- )
- );
-}
-
void MatrixSdk::startThread()
{
m_d->runIoContext();
}
-RandomInterface &MatrixSdk::randomGenerator() const
-{
- return m_d->randomGenerator;
-}
-
-bool MatrixSdk::shouldNotify(MatrixEvent *event) const
-{
- // Do not notify own event
- if (event->sender() == userId()) {
- return false;
- }
- return m_d->notificationHandler.handleNotification(event->underlyingEvent()).shouldNotify;
-}
-
-bool MatrixSdk::shouldPlaySound(MatrixEvent *event) const
-{
- return m_d->notificationHandler.handleNotification(event->underlyingEvent()).sound.has_value();
-}
-
-MatrixStickerPackList *MatrixSdk::stickerPackList() const
-{
- return new MatrixStickerPackList(m_d->clientOnSecondaryRoot);
-}
-
-MatrixEvent *MatrixSdk::stickerRoomsEvent() const
-{
- return new MatrixEvent(m_d->clientOnSecondaryRoot.accountData()[imagePackRoomsEventType][lager::lenses::or_default]);
-}
-
-MatrixPromise *MatrixSdk::updateStickerPack(MatrixStickerPackSource source)
-{
- if (source.source == MatrixStickerPackSource::AccountData) {
- auto eventJson = std::move(source.event).raw().get();
- eventJson["type"] = source.eventType;
- return sendAccountDataImpl(Event(std::move(eventJson)));
- } else if (source.source == MatrixStickerPackSource::RoomState) {
- auto eventJson = std::move(source.event).raw().get();
- eventJson["type"] = source.eventType;
- eventJson["state_key"] = source.stateKey;
- return new MatrixPromise(
- m_d->clientOnSecondaryRoot
- .room(source.roomId)
- .sendStateEvent(Event(std::move(eventJson))));
- } else {
- return 0;
- }
-}
-
-MatrixUserGivenAttrsMap *MatrixSdk::userGivenNicknameMap() const
-{
- return new MatrixUserGivenAttrsMap(
- userGivenNicknameMapFor(m_d->clientOnSecondaryRoot),
- [client=m_d->clientOnSecondaryRoot](json content) {
- return client.setAccountData(json{
- {"type", USER_GIVEN_NICKNAME_EVENT_TYPES[0]},
- {"content", std::move(content)},
- });
- }
- );
-}
-
-MatrixPromise *MatrixSdk::sendAccountData(const QString &type, const QJsonObject &content)
-{
- Event e = json{
- {"type", type.toStdString()},
- {"content", content},
- };
- return sendAccountDataImpl(std::move(e));
-}
-
-MatrixPromise *MatrixSdk::sendAccountDataImpl(Event event)
-{
- return new MatrixPromise(m_d->clientOnSecondaryRoot.setAccountData(event));
-}
-
-MatrixPromise *MatrixSdk::getSpecVersions()
-{
- return new MatrixPromise(m_d->clientOnSecondaryRoot.getVersions(LAGER_QT(serverUrl).get().toStdString()));
-}
-
-MatrixPromise *MatrixSdk::addDirectRoom(const QString &userId, const QString &roomId)
-{
- return new MatrixPromise(m_d->clientOnSecondaryRoot.addDirectRoom(userId.toStdString(), roomId.toStdString()));
-}
-
-MatrixPromise *MatrixSdk::getRoomIdByAlias(const QString &roomAlias)
-{
- auto job = m_d->clientOnSecondaryRoot
- .getRoomIdByAliasJob(roomAlias.toStdString());
- auto jh = m_d->jobHandler;
- return new MatrixPromise(
- m_d->sdk.context().createPromise([jh, job](auto resolve) {
- jh->submit(job, [resolve](GetRoomIdByAliasResponse r) {
- resolve(parseGetRoomIdByAliasResponse(r));
- });
- }));
-}
-
-inline constexpr std::size_t purgeEventsKeepNumber = 5;
-MatrixPromise *MatrixSdk::purgeEventsExceptRooms(const QStringList &roomIds)
-{
- return new MatrixPromise(
- m_d->sdk.context().createResolvedPromise({})
- .then([client=m_d->sdk.client(), roomIds=qStringListToStdF(roomIds)
- ]([[maybe_unused]] auto &&stat) {
- auto map = intoImmer(immer::map<std::string, std::size_t>{},
- zug::filter([&roomIds](const std::string &roomId) {
- return std::find(roomIds.begin(), roomIds.end(), roomId) == roomIds.end();
- })
- | zug::map([](const std::string &roomId) {
- return std::make_pair(roomId, purgeEventsKeepNumber);
- }),
- client.roomIds().make().get()
- );
- return client.purgeRoomEvents(map);
- }).then([](const auto &stat) {
- qCDebug(kazvLog) << "Purge room events stat:" << !!stat;
- return stat;
- })
- );
-}
-
-MatrixPromise *MatrixSdk::backfillRoomFromEvent(const QString &roomId, const QString &eventId)
-{
- return new MatrixPromise(
- m_d->sdk.context().createPromise([
- dbStore=m_d->dbStore.get(),
- client=m_d->sdk.client(),
- roomId,
- eventId
- ](auto resolve) {
- dbStore->getEventsBefore(roomId, eventId).then([client, resolve, roomId](auto &&res) {
- auto [timelineEvents, relatedEvents] = std::move(res);
- auto loadedCount = timelineEvents[roomId.toStdString()].size();
- client.loadEventsFromStorage(timelineEvents, relatedEvents)
- .then([resolve, loadedCount](auto &&) {
- resolve(EffectStatus(
- !!loadedCount,
- json{{"loadedCount", loadedCount}}
- ));
- });
- });
- })
- );
-}
-
-MatrixPromise *MatrixSdk::importFromKeyBackupFile(QUrl fileUrl, QString password)
-{
- if (!fileUrl.isLocalFile()) {
- return new MatrixPromise(m_d->sdk.context().createResolvedPromise({
- /* succ = */ false,
- json{{"error", "Not a local file"}, {"errorCode", "NOT_LOCAL_FILE"}},
- }));
- }
- auto filename = fileUrl.toLocalFile();
- auto f = QFile(filename);
- if (!f.open(QFile::ReadOnly)) {
- return new MatrixPromise(m_d->sdk.context().createResolvedPromise({
- /* succ = */ false,
- json{{"error", "File open failed"}, {"errorCode", "FILE_OPEN_FAILED"}},
- }));
- }
- auto ba = f.readAll();
- auto content = std::string(ba.begin(), ba.end());
- auto passwordStd = std::move(password).toStdString();
- return new MatrixPromise(m_d->clientOnSecondaryRoot.importFromKeyBackupFile(
- std::move(content), std::move(passwordStd)
- ));
-}
-
-MatrixPromise *MatrixSdk::requestVerifyDevice(QString userId, QString deviceId)
-{
- return new MatrixPromise(m_d->clientOnSecondaryRoot.requestOutgoingToDeviceVerification(
- std::move(userId).toStdString(),
- std::move(deviceId).toStdString()
- ));
-}
-
void MatrixSdk::setUserDataDir(const std::string &userDataDir)
{
m_d->userDataDir = userDataDir;
}
-#include "matrix-sdk.moc"
+MatrixSession *MatrixSdk::session() const
+{
+ return nullptr;
+}
diff --git a/src/matrix-session-controller-p.hpp b/src/matrix-session-controller-p.hpp
new file mode 100644
--- /dev/null
+++ b/src/matrix-session-controller-p.hpp
@@ -0,0 +1,111 @@
+/*
+ * This file is part of kazv.
+ * SPDX-FileCopyrightText: 2020-2026 tusooa <tusooa@kazv.moe>
+ * SPDX-License-Identifier: AGPL-3.0-or-later
+ */
+
+#pragma once
+#include <kazv-defs.hpp>
+#include "matrix-session-controller.hpp"
+#include "matrix-session-types.hpp"
+#include "kazv-session-lock-guard.hpp"
+#include "helper.hpp"
+#include "qt-rand-adapter.hpp"
+#include "qt-job-handler.hpp"
+#include "db-store.hpp"
+#include <lager/event_loop/qt.hpp>
+#include <QPointer>
+
+using namespace Kazv;
+
+struct QtEventLoop
+{
+ QObject *m_obj;
+
+ template<class Fn>
+ void async(Fn &&) { throw std::runtime_error{"not implemented!"}; }
+
+ template<class Fn>
+ void post(Fn &&fn)
+ {
+ QMetaObject::invokeMethod(
+ m_obj, std::forward<Fn>(fn), Qt::QueuedConnection
+ );
+ }
+
+ inline void finish() {}
+ inline void pause() { throw std::runtime_error{"not implemented!"}; }
+ inline void resume() { throw std::runtime_error{"not implemented!"}; }
+};
+
+struct PendingSaveEvents : public SaveEventsRequested
+{
+ using DataT = immer::map<std::string, EventList>;
+ void add(SaveEventsRequested s)
+ {
+ timelineEvents = addOneType(std::move(timelineEvents), s.timelineEvents);
+ nonTimelineEvents = addOneType(std::move(nonTimelineEvents), s.nonTimelineEvents);
+ }
+
+ static DataT addOneType(DataT orig, DataT addon)
+ {
+ for (const auto &[roomId, el]: addon) {
+ orig = std::move(orig).update(roomId, [&el](EventList origEl) {
+ // This works without explicit dedupe because we do not do parallel
+ // inserts and the insert order is the same as list order.
+ return origEl + el;
+ });
+ }
+ return orig;
+ }
+};
+
+enum DbStatus
+{
+ Pending,
+ Ready,
+ Failed,
+};
+
+class MatrixSessionControllerPrivate : public QObject
+{
+ Q_OBJECT
+
+public:
+ MatrixSessionControllerPrivate(
+ QThread *thread,
+ std::string userDataDir,
+ SdkModel model,
+ std::unique_ptr<KazvSessionLockGuard> lockGuard,
+ std::unique_ptr<DbStore> dbStore,
+ QObject *parent = nullptr
+ );
+
+ ~MatrixSessionControllerPrivate();
+
+ void saveSession();
+
+ void saveOrQueueEvents(SaveEventsRequested s);
+
+ void saveEvents(SaveEventsRequested s);
+
+Q_SIGNALS:
+ void dbResolved(bool success);
+
+private:
+ QPointer<QThread> thread;
+ std::string userDataDir;
+ UniquePtrDL<QObject> obj;
+ std::unique_ptr<KazvSessionLockGuard> lockGuard;
+ RandomInterface randomGenerator;
+ std::unique_ptr<DbStore> dbStore;
+ QPointer<QtJobHandler> jobHandler;
+ LagerStoreEventEmitter ee;
+ LagerStoreEventEmitter::Watchable watchable;
+ MatrixSessionSdkT sdk;
+ SecondaryRootT secondaryRoot;
+ Client clientOnSecondaryRoot;
+ DbStatus dbStatus;
+ PendingSaveEvents pendingSaveEvents{};
+ lager::state<VerificationTrackerModel> verificationTrackerState;
+};
diff --git a/src/matrix-session-controller.hpp b/src/matrix-session-controller.hpp
new file mode 100644
--- /dev/null
+++ b/src/matrix-session-controller.hpp
@@ -0,0 +1,64 @@
+/*
+ * This file is part of kazv.
+ * SPDX-FileCopyrightText: 2020-2026 tusooa <tusooa@kazv.moe>
+ * SPDX-License-Identifier: AGPL-3.0-or-later
+ */
+
+#pragma once
+#include <kazv-defs.hpp>
+#include "matrix-sdk.hpp"
+#include <QObject>
+#include <QThread>
+#include <QPointer>
+#include <memory>
+
+class MatrixSession;
+class MatrixSessionControllerPrivate;
+
+/**
+ * Control the creation/loading/saving of a session.
+ *
+ * The controller object should be in the GUI thread.
+ * However, it owns objects that are in worker thread
+ * passed in as the argument of the constructor.
+ */
+class MatrixSessionController : public QObject
+{
+ Q_OBJECT
+
+public:
+ MatrixSessionController(
+ QThread *workerThread,
+ std::string userDataDir,
+ QObject *parent = nullptr
+ );
+
+ ~MatrixSessionController();
+
+ Q_INVOKABLE MatrixSession *session() const;
+
+Q_SIGNALS:
+ void loadSessionFinished(
+ QString sessionName,
+ MatrixSdk::LoadSessionResult result
+ );
+
+public Q_SLOTS:
+ /**
+ * Load session at <userDataDir>/sessions/<sessionName> asynchronously.
+ *
+ * When the session is loaded or when there is an error, loadSessionFinished
+ * will be called with the result.
+ *
+ * @param sessionName A string in the form of <userid>/<deviceid> .
+ */
+ void load(QString sessionName);
+
+ void save() const;
+
+private:
+ QPointer<QThread> m_thread;
+ std::string m_userDataDir;
+
+ std::unique_ptr<MatrixSessionControllerPrivate> m_d;
+};
diff --git a/src/matrix-session-controller.cpp b/src/matrix-session-controller.cpp
new file mode 100644
--- /dev/null
+++ b/src/matrix-session-controller.cpp
@@ -0,0 +1,387 @@
+/*
+ * This file is part of kazv.
+ * SPDX-FileCopyrightText: 2020-2026 tusooa <tusooa@kazv.moe>
+ * SPDX-License-Identifier: AGPL-3.0-or-later
+ */
+
+#include <kazv-defs.hpp>
+#include "matrix-session-controller-p.hpp"
+#include "kazv-log.hpp"
+#include "kazv-version.hpp"
+#include <KConfig>
+#include <KConfigGroup>
+#include <QCoroTask>
+#include <boost/archive/text_oarchive.hpp>
+#include <boost/archive/text_iarchive.hpp>
+#include <fstream>
+#include <filesystem>
+#include <chrono>
+
+using namespace Qt::Literals::StringLiterals;
+using namespace Kazv;
+
+QCoro::Task<std::pair<bool, QString>> setupAndImport(SdkModel model, DbStore *dbStore, std::string userDataDir)
+{
+ auto res = co_await dbStore->setup(
+ userDataDir,
+ model.c().userId,
+ model.c().deviceId
+ );
+ if (!res.first) {
+ co_return res;
+ }
+ co_return co_await dbStore->importAllFrom(model);
+}
+
+MatrixSessionControllerPrivate::MatrixSessionControllerPrivate(
+ QThread *threadArg,
+ std::string userDataDirArg,
+ SdkModel model,
+ std::unique_ptr<KazvSessionLockGuard> lockGuardArg,
+ std::unique_ptr<DbStore> dbStoreArg,
+ QObject *parent
+)
+ : QObject(parent)
+ , thread(threadArg)
+ , userDataDir(userDataDirArg)
+ , obj(new QObject())
+ , lockGuard(std::move(lockGuardArg))
+ , randomGenerator(QtRandAdapter{})
+ , dbStore(std::move(dbStoreArg))
+ , jobHandler(new QtJobHandler(obj.get()))
+ , ee{QtEventLoop{obj.get()}}
+ , watchable(ee.watchable())
+ , sdk(makeSdk(
+ model,
+ static_cast<JobInterface &>(*jobHandler),
+ static_cast<EventInterface &>(ee),
+ QtPromiseHandler(*obj),
+ zug::identity,
+ withRandomGenerator(randomGenerator)))
+ , secondaryRoot(sdk.createSecondaryRoot(QtEventLoop{this}, std::move(model)))
+ , clientOnSecondaryRoot(sdk.clientFromSecondaryRoot(secondaryRoot))
+ , dbStatus(this->dbStore ? Ready : Pending)
+{
+ obj->moveToThread(thread);
+
+ watchable.after<SaveEventsRequested>([this](SaveEventsRequested e) {
+ QMetaObject::invokeMethod(this, [this, e=std::move(e)]() mutable {
+ saveOrQueueEvents(std::move(e));
+ });
+ });
+
+ watchable.after<LoginSuccessful>([this](LoginSuccessful) {
+ QMetaObject::invokeMethod(this, [this]() {
+ if (dbStore) {
+ qCWarning(kazvLog) << "Trying to replace current db store, ignoring";
+ return;
+ }
+ dbStore = std::make_unique<DbStore>();
+ // This must be called from the GUI thread
+ auto model = secondaryRoot.get();
+ setupAndImport(std::move(model), dbStore.get(), userDataDir)
+ .then([this](const auto &stat) {
+ if (!stat.first) {
+ qCWarning(kazvLog) << "Cannot set up database:" << stat.second;
+ }
+ Q_EMIT dbResolved(stat.first);
+ });
+ });
+ });
+
+ watchable.after<VerificationTrackerModelChanged>([this](VerificationTrackerModelChanged) {
+ auto model = lager::get<VerificationTracker>(sdk.context()).model;
+ QMetaObject::invokeMethod(
+ this, [this, model=std::move(model)]() {
+ qCDebug(kazvLog) << "Verification tracker model changed" << model.processes.size();
+ verificationTrackerState.set(model);
+ lager::commit(verificationTrackerState);
+ });
+ });
+
+
+ connect(this, &MatrixSessionControllerPrivate::dbResolved, this, [this](bool success) {
+ if (!success) {
+ dbStatus = Failed;
+ } else {
+ qCDebug(kazvLog) << "db loading succeeded";
+ auto origPendingSaveEvents = std::move(pendingSaveEvents);
+ pendingSaveEvents = {};
+ saveEvents(std::move(pendingSaveEvents));
+ dbStatus = Ready;
+ }
+ });
+}
+
+void MatrixSessionControllerPrivate::saveSession()
+{
+ using namespace Kazv::CursorOp;
+ auto &c = clientOnSecondaryRoot;
+ auto userId = +c.userId();
+ auto deviceId = +c.deviceId();
+ if (userId.empty() || deviceId.empty()) {
+ qDebug() << "Not logged in, nothing to serialize";
+ return;
+ }
+
+ using StdPath = std::filesystem::path;
+ auto userDataDir = StdPath(this->userDataDir);
+ auto sessionDir = sessionDirForUserAndDeviceId(userDataDir, userId, deviceId);
+
+ auto storeFile = sessionDir / "store";
+ auto storeFileNew = sessionDir / "store.new";
+
+ auto metadataFile = sessionDir / "metadata";
+
+ qDebug() << "storeFile=" << QString::fromStdString(storeFile.string());
+
+ std::error_code err;
+ if ((! std::filesystem::create_directories(sessionDir, err))
+ && err) {
+ qDebug() << "Unable to create sessionDir";
+ return;
+ }
+
+ if (!lockGuard) {
+ try {
+ lockGuard = std::make_unique<KazvSessionLockGuard>(sessionDir);
+ } catch (const std::runtime_error &e) {
+ qCWarning(kazvLog) << "Error locking session: " << e.what();
+ return;
+ }
+ }
+
+ try {
+ auto storeStream = std::ofstream(storeFileNew);
+ if (! storeStream) {
+ qCWarning(kazvLog) << "Unable to open storeFile";
+ return;
+ }
+ using OAr = boost::archive::text_oarchive;
+ auto archive = OAr{storeStream};
+ c.serializeTo(archive);
+ } catch (const std::exception &e) {
+ qCWarning(kazvLog) << "Cannot write to store file: " << e.what();
+ return;
+ }
+
+ err.clear();
+ std::filesystem::rename(storeFileNew, storeFile, err);
+ if (err) {
+ qCWarning(kazvLog) << "Cannot move storeFile into place: " << QString::fromStdString(err.message());
+ return;
+ }
+ qDebug() << "Serialization done";
+
+
+ // store metadata
+ {
+ KConfig metadata(QString::fromStdString(metadataFile.string()));
+ KConfigGroup mdGroup(&metadata, u"Metadata"_s);
+ mdGroup.writeEntry("kazvVersion", QString::fromStdString(kazvVersionString()));
+ mdGroup.writeEntry("archiveFormat", "text-with-sqlite");
+ }
+}
+
+void MatrixSessionControllerPrivate::saveOrQueueEvents(SaveEventsRequested s)
+{
+ if (dbStatus == Ready) {
+ saveEvents(std::move(s));
+ } else if (dbStatus == Pending) {
+ pendingSaveEvents.add(std::move(s));
+ }
+ // Database open failed, do not do anything
+}
+
+void MatrixSessionControllerPrivate::saveEvents(SaveEventsRequested s)
+{
+ dbStore->saveEvents(std::move(s))
+ .then([](const auto &stat) {
+ if (!stat.first) {
+ qCWarning(kazvLog) << "Cannot save events:" << stat.second;
+ }
+ });
+}
+
+MatrixSessionControllerPrivate::~MatrixSessionControllerPrivate()
+{
+}
+
+MatrixSessionController::MatrixSessionController(
+ QThread *workerThread,
+ std::string userDataDir,
+ QObject *parent
+)
+ : QObject(parent)
+ , m_thread(workerThread)
+ , m_userDataDir(userDataDir)
+ , m_d(nullptr)
+{
+}
+
+MatrixSessionController::~MatrixSessionController() = default;
+
+// Cleaning up notes:
+// 0. Callback functions may store the context for an indefinite time
+// 1. The QThread event loop can be stopped
+// 2. QtJobHandler::submit() should only happen in the primary event loop thread
+// 3. QtJobHandler lives in the primary event loop thread
+// 4. QtJobs live in the primary event loop thread
+// 5. Job callbacks are called in the primary event loop thread
+// 6. QtPromise::then() callbacks are called in the primary event loop thread
+// 7. When the QThread event loop stops, no more callbacks will be executed (there is nothing to post to)
+// 8. The QThread should stop before obj is deleted
+// class CleanupHelper : public QObject
+// {
+// Q_OBJECT
+// public:
+// explicit CleanupHelper(std::unique_ptr<MatrixSdkPrivate> d)
+// : oldD(std::move(d))
+// {
+// }
+
+// std::unique_ptr<MatrixSdkPrivate> oldD;
+
+// void cleanup()
+// {
+// qCInfo(kazvLog) << "start to clean up everything";
+
+// oldD->clientOnSecondaryRoot.stopSyncing()
+// .then([obj=oldD->obj, thread=oldD->thread](auto &&) {
+// qCDebug(kazvLog) << "stopped syncing";
+// QMetaObject::invokeMethod(obj, [thread]() {
+// thread->quit();
+// });
+// });
+// oldD->thread->wait();
+// oldD->thread->deleteLater();
+// // After the thread's event loop is finished, we can delete the root object
+// oldD->obj->deleteLater();
+// this->deleteLater();
+// qCInfo(kazvLog) << "thread is done";
+// }
+// };
+
+// static void cleanupDPointer(std::unique_ptr<MatrixSdkPrivate> oldD)
+// {
+// oldD->saveTimer.disconnect();
+// oldD->saveTimer.stop();
+
+// auto helper = new CleanupHelper(std::move(oldD));
+// helper->cleanup();
+// }
+
+void MatrixSessionController::load(QString sessionName)
+{
+ using StdPath = std::filesystem::path;
+ auto loadFromSession = [this, sessionName](StdPath sessionDir, std::unique_ptr<KazvSessionLockGuard> lockGuard) {
+ auto storeFile = sessionDir / "store";
+
+ auto metadataFile = sessionDir / "metadata";
+
+ if (! std::filesystem::exists(storeFile)) {
+ qDebug() << "storeFile does not exist, skip loading session " << sessionName;
+ Q_EMIT loadSessionFinished(sessionName, MatrixSdk::SessionNotFound);
+ return;
+ }
+
+ if (std::filesystem::exists(metadataFile)) {
+ KConfig metadata(QString::fromStdString(metadataFile.string()));
+ KConfigGroup mdGroup(&metadata, u"Metadata"_s);
+ auto format = mdGroup.readEntry(u"archiveFormat"_s);
+ if (format != u"text"_s && format != u"text-with-sqlite"_s) {
+ qDebug() << "Unknown archive format:" << format;
+ Q_EMIT loadSessionFinished(sessionName, MatrixSdk::SessionFormatUnknown);
+ return;
+ }
+ auto version = mdGroup.readEntry(u"kazvVersion"_s);
+ auto curVersion = kazvVersionString();
+ if (version != QString::fromStdString(curVersion)) {
+ qDebug() << "A different version from the current one, making a backup";
+ std::error_code err;
+ auto now = std::chrono::system_clock::now();
+ auto backupName =
+ std::to_string(std::chrono::duration_cast<std::chrono::seconds>(now.time_since_epoch()).count());
+ auto backupDir = sessionDir / "backup" / backupName;
+ if (! std::filesystem::create_directories(backupDir, err)
+ && err) {
+ qDebug() << "Cannot create backup directory";
+ Q_EMIT loadSessionFinished(sessionName, MatrixSdk::SessionCannotBackup);
+ return;
+ }
+ std::filesystem::copy_file(storeFile, backupDir / "store");
+ std::filesystem::copy_file(metadataFile, backupDir / "metadata");
+ }
+ }
+
+ SdkModel model;
+ try {
+ auto storeStream = std::ifstream(storeFile);
+ if (! storeStream) {
+ qDebug() << "Unable to open storeFile";
+ Q_EMIT loadSessionFinished(sessionName, MatrixSdk::SessionCannotOpenFile);
+ return;
+ }
+ using IAr = boost::archive::text_iarchive;
+ auto archive = IAr{storeStream};
+ archive >> model;
+ qDebug() << "Finished loading session store file";
+ } catch (const std::exception &e) {
+ qDebug() << "Error when loading session:" << QString::fromStdString(e.what());
+ Q_EMIT loadSessionFinished(sessionName, MatrixSdk::SessionDeserializeFailed);
+ return;
+ }
+
+ auto dbStore = std::make_unique<DbStore>();
+ auto stat = QCoro::waitFor(setupAndImport(model, dbStore.get(), m_userDataDir));
+ if (!stat.first) {
+ qCWarning(kazvLog) << "Unable to open db store:" << stat.second;
+ Q_EMIT loadSessionFinished(sessionName, MatrixSdk::SessionDeserializeFailed);
+ return;
+ }
+ QMetaObject::invokeMethod(this, [this, model=std::move(model), lockGuard=std::move(lockGuard), dbStore=std::move(dbStore)]() mutable {
+ m_d = std::make_unique<MatrixSessionControllerPrivate>(
+ m_thread, m_userDataDir,
+ std::move(model), std::move(lockGuard), std::move(dbStore)
+ );
+ });
+ Q_EMIT loadSessionFinished(sessionName, MatrixSdk::SessionLoadSuccess);
+ };
+
+ qDebug() << "in loadSession(), sessionName=" << sessionName;
+ auto userDataDir = StdPath(m_userDataDir);
+ auto parts = sessionName.split(u'/');
+ if (parts.size() == 2) {
+ auto userId = parts[0].toStdString();
+ auto deviceId = parts[1].toStdString();
+ auto sessionDir = sessionDirForUserAndDeviceId(userDataDir, userId, deviceId);
+ std::unique_ptr<KazvSessionLockGuard> lockGuard;
+ try {
+ lockGuard = std::make_unique<KazvSessionLockGuard>(sessionDir);
+ } catch (const std::runtime_error &e) {
+ qCWarning(kazvLog) << "Error locking session: " << e.what();
+ Q_EMIT loadSessionFinished(sessionName, MatrixSdk::SessionLockFailed);
+ return;
+ }
+ QThreadPool::globalInstance()->start([loadFromSession, sessionDir, lockGuard=std::move(lockGuard)]() mutable {
+ loadFromSession(sessionDir, std::move(lockGuard));
+ });
+ return;
+ }
+ qDebug(kazvLog) << "no session found for" << sessionName;
+ Q_EMIT loadSessionFinished(sessionName, MatrixSdk::SessionNotFound);
+}
+
+void MatrixSessionController::save() const
+{
+ if (m_d) {
+ m_d->saveSession();
+ }
+}
+
+MatrixSession *MatrixSessionController::session() const
+{
+ return nullptr;
+}
+
+#include "moc_matrix-session-controller-p.cpp"
diff --git a/src/matrix-session-types.hpp b/src/matrix-session-types.hpp
new file mode 100644
--- /dev/null
+++ b/src/matrix-session-types.hpp
@@ -0,0 +1,27 @@
+/*
+ * This file is part of kazv.
+ * SPDX-FileCopyrightText: 2020-2024 tusooa <tusooa@kazv.moe>
+ * SPDX-License-Identifier: AGPL-3.0-or-later
+ */
+
+#pragma once
+#include <kazv-defs.hpp>
+#include "qt-promise-handler.hpp"
+#include <lagerstoreeventemitter.hpp>
+#include <sdk.hpp>
+#include <lager/event_loop/qt.hpp>
+#include <QObject>
+
+// Sdk with qt event loop, identity transform and no enhancers
+using MatrixSessionSdkT =
+ decltype(Kazv::makeSdk(
+ Kazv::SdkModel{},
+ Kazv::detail::declref<Kazv::JobInterface>(),
+ Kazv::detail::declref<Kazv::EventInterface>(),
+ QtPromiseHandler(Kazv::detail::declref<QObject>()),
+ zug::identity,
+ withRandomGenerator(Kazv::detail::declref<Kazv::RandomInterface>())));
+
+using MatrixSessionContextT = decltype(std::declval<MatrixSessionSdkT>().context());
+
+using SecondaryRootT = decltype(std::declval<MatrixSessionSdkT>().createSecondaryRoot(std::declval<lager::with_qt_event_loop>()));
diff --git a/src/matrix-sdk.hpp b/src/matrix-session.hpp
copy from src/matrix-sdk.hpp
copy to src/matrix-session.hpp
--- a/src/matrix-sdk.hpp
+++ b/src/matrix-session.hpp
@@ -1,27 +1,21 @@
/*
* This file is part of kazv.
- * SPDX-FileCopyrightText: 2020-2023 tusooa <tusooa@kazv.moe>
+ * SPDX-FileCopyrightText: 2020-2026 tusooa <tusooa@kazv.moe>
* SPDX-License-Identifier: AGPL-3.0-or-later
*/
#pragma once
#include <kazv-defs.hpp>
-
+#include "helper.hpp"
+#include "meta-types.hpp"
+#include "matrix-session-types.hpp"
+#include <client.hpp>
+#include <notification-handler.hpp>
+#include <lagerstoreeventemitter.hpp>
+#include <lager/extra/qt.hpp>
#include <QObject>
#include <QQmlEngine>
-#include <QString>
-
-#include <string>
-#include <memory>
-#include <filesystem>
-
-#include <lager/extra/qt.hpp>
-#include <immer/map.hpp>
-
-#include <sdk-model.hpp>
-#include <random-generator.hpp>
-#include "meta-types.hpp"
Q_MOC_INCLUDE("matrix-room-list.hpp")
Q_MOC_INCLUDE("matrix-device-list.hpp")
Q_MOC_INCLUDE("matrix-promise.hpp")
@@ -38,25 +32,26 @@
class MatrixEvent;
class MatrixStickerPackList;
class MatrixUserGivenAttrsMap;
-class KazvSessionLockGuard;
-class DbStore;
class MatrixVerificationList;
-struct MatrixSdkPrivate;
-
-std::filesystem::path sessionDirForUserAndDeviceId(std::filesystem::path userDataDir, std::string userId, std::string deviceId);
+class SsoLoginProcess;
+class DbStore;
-class MatrixSdk : public QObject
+/**
+ * Represent all operations that can be taken within a Matrix session.
+ */
+class MatrixSession : public QObject
{
Q_OBJECT
QML_ELEMENT
+ QML_UNCREATABLE("")
- std::unique_ptr<MatrixSdkPrivate> m_d;
-
- /// @param d A dynamically allocated d-pointer, whose ownership
- /// will be transferred to this MatrixSdk.
- explicit MatrixSdk(std::unique_ptr<MatrixSdkPrivate> d, QObject *parent);
-
- void init();
+ MatrixSessionContextT m_context;
+ Kazv::Client m_clientOnSecondaryRoot;
+ Kazv::LagerStoreEventEmitter::Watchable m_watchable;
+ Kazv::NotificationHandler m_notificationHandler;
+ lager::reader<Kazv::VerificationTrackerModel> m_verificationTrackerState;
+ UniquePtrDL<SsoLoginProcess> m_ssoLoginProcess;
+ std::function<DbStore &()> m_dbStoreGetter;
public:
enum CreateRoomPreset {
@@ -67,25 +62,6 @@
Q_ENUM(CreateRoomPreset);
- enum LoadSessionResult {
- /// Successfully loaded the session
- SessionLoadSuccess,
- /// There is no store file
- SessionNotFound,
- /// The format of the store file is not supported
- SessionFormatUnknown,
- /// The store file cannot be backed up
- SessionCannotBackup,
- /// Cannot grab the lock on the session file
- SessionLockFailed,
- /// Cannot open store file
- SessionCannotOpenFile,
- /// Cannot deserialize the store file
- SessionDeserializeFailed,
- };
-
- Q_ENUM(LoadSessionResult);
-
enum MediaDownloadEndpointVersion {
UnauthenticatedMediaV3, // Classical endpoint series
AuthenticatedMediaV1, // Endpoints that needs authorization
@@ -93,8 +69,17 @@
Q_ENUM(MediaDownloadEndpointVersion);
- explicit MatrixSdk(QObject *parent = 0);
- ~MatrixSdk() override;
+ MatrixSession(
+ MatrixSessionContextT context,
+ Kazv::Client client,
+ Kazv::LagerStoreEventEmitter::Watchable watchable,
+ lager::reader<Kazv::VerificationTrackerModel> verificationTrackerState,
+ std::function<DbStore &()> dbStoreGetter,
+ QObject *parent = nullptr
+ );
+ ~MatrixSession() override;
+
+ static std::string validateHomeserverUrl(const QString &url);
LAGER_QT_READER(QString, serverUrl);
LAGER_QT_READER(QString, userId);
@@ -120,14 +105,6 @@
Q_INVOKABLE MatrixVerificationList *verificationList() const;
- Kazv::RandomInterface &randomGenerator() const;
-
-private:
- // Replaces the store with another one
- void emplace(std::optional<Kazv::SdkModel> model, std::unique_ptr<KazvSessionLockGuard> lockGuard, std::unique_ptr<DbStore> dbStore);
-
- static std::string validateHomeserverUrl(const QString &url);
-
Q_SIGNALS:
void trigger(Kazv::KazvEvent e);
@@ -143,11 +120,6 @@
void receivedMessage(QString roomId, QString eventId);
- void sessionChanged();
-
- void loadSessionFinished(QString sessionName, MatrixSdk::LoadSessionResult result);
- void dbResolved(bool success);
-
public Q_SLOTS:
void login(const QString &userId, const QString &password, const QString &homeserverUrl);
@@ -175,48 +147,6 @@
QUrl ssoLoginStart(const QString &homeserverUrl);
void logout();
- /**
- * Serialize data to <AppDataDir>/sessions/<userid>/<deviceid>/
- *
- * If not logged in, do nothing.
- */
- void serializeToFile() const;
-
- /**
- * Load session at <AppDataDir>/sessions/<sessionName> asynchronously.
- *
- * When the session is loaded or when there is an error, loadSessionFinished
- * will be called with the result.
- *
- * @param sessionName A string in the form of <userid>/<deviceid> .
- */
- void loadSession(QString sessionName);
-
- /**
- * Delete session at <AppDataDir>/sessions/<sessionName> .
- *
- * @param sessionName A string in the form of <userid>/<deviceid> .
- *
- * @return true if successful, false otherwise.
- */
- bool deleteSession(QString sessionName);
-
- /**
- * Start an empty session.
- *
- * The new session is not logged in, and need to call login().
- *
- * @return true if successful, false otherwise.
- */
- bool startNewSession();
-
- /**
- * Get all saved sessions.
- *
- * @return A list of session names in the form of <userid>/<deviceid> .
- */
- QStringList allSessions() const;
-
/**
* Create a new room.
*
@@ -384,14 +314,4 @@
private:
MatrixPromise *sendAccountDataImpl(Kazv::Event event);
-
-private: // Testing
- friend MatrixSdkTest;
- friend MatrixSdkSessionsTest;
- friend MatrixSdk *makeTestSdk(Kazv::SdkModel model);
-
- void setUserDataDir(const std::string &userDataDir);
-
- explicit MatrixSdk(Kazv::SdkModel model, bool testing = false, QObject *parent = 0);
- void startThread();
};
diff --git a/src/matrix-session.cpp b/src/matrix-session.cpp
new file mode 100644
--- /dev/null
+++ b/src/matrix-session.cpp
@@ -0,0 +1,600 @@
+/*
+ * This file is part of kazv.
+ * SPDX-FileCopyrightText: 2020-2026 tusooa <tusooa@kazv.moe>
+ * SPDX-License-Identifier: AGPL-3.0-or-later
+ */
+
+#include <kazv-defs.hpp>
+#include "matrix-session.hpp"
+#include "matrix-utils.hpp"
+#include "matrix-room-list.hpp"
+#include "matrix-promise.hpp"
+#include "matrix-event.hpp"
+#include "device-mgmt/matrix-device-list.hpp"
+#include "matrix-sticker-pack-list.hpp"
+#include "matrix-sticker-pack-list-p.hpp"
+#include "matrix-user-given-attrs-map.hpp"
+#include "matrix-verification-list.hpp"
+#include "sso-login-process.hpp"
+#include "db-store.hpp"
+#include "helper.hpp"
+#include "kazv-log.hpp"
+#include <QFile>
+
+using namespace Qt::Literals::StringLiterals;
+using namespace Kazv;
+
+static const std::string clientName = "kazv";
+
+MatrixSession::MatrixSession(
+ MatrixSessionContextT context,
+ Client client,
+ LagerStoreEventEmitter::Watchable watchable,
+ lager::reader<VerificationTrackerModel> verificationTrackerState,
+ std::function<DbStore &()> dbStoreGetter,
+ QObject *parent
+)
+ : QObject(parent)
+ , m_context(std::move(context))
+ , m_clientOnSecondaryRoot(std::move(client))
+ , m_watchable(std::move(watchable))
+ , m_notificationHandler(m_clientOnSecondaryRoot.notificationHandler())
+ , m_verificationTrackerState(std::move(verificationTrackerState))
+ , m_ssoLoginProcess()
+ , m_dbStoreGetter(std::move(dbStoreGetter))
+ , LAGER_QT(serverUrl)(m_clientOnSecondaryRoot.serverUrl().xform(strToQt))
+ , LAGER_QT(userId)(m_clientOnSecondaryRoot.userId().xform(strToQt))
+ , LAGER_QT(token)(m_clientOnSecondaryRoot.token().xform(strToQt))
+ , LAGER_QT(deviceId)(m_clientOnSecondaryRoot.deviceId().xform(strToQt))
+ , LAGER_QT(specVersions)(m_clientOnSecondaryRoot.supportVersions())
+{
+ m_watchable.afterAll(
+ [this](KazvEvent e) {
+ Q_EMIT this->trigger(e);
+ });
+
+ m_watchable.after<LoginSuccessful>(
+ [this](LoginSuccessful e) {
+ Q_EMIT this->loginSuccessful(e);
+ });
+
+ m_watchable.after<LoginFailed>(
+ [this](LoginFailed e) {
+ Q_EMIT this->loginFailed(
+ QString::fromStdString(e.errorCode),
+ QString::fromStdString(e.error)
+ );
+ });
+
+ m_watchable.after<ReceivingRoomTimelineEvent>(
+ [this](ReceivingRoomTimelineEvent e) {
+ Q_EMIT this->receivedMessage(
+ QString::fromStdString(e.roomId),
+ QString::fromStdString(e.event.id())
+ );
+ });
+}
+
+MatrixSession::~MatrixSession() = default;
+
+QString MatrixSession::mxcUriToHttp(QString mxcUri) const
+{
+ return QString::fromStdString(m_clientOnSecondaryRoot.mxcUriToHttp(mxcUri.toStdString()));
+}
+
+QString MatrixSession::mxcUriToHttpAuthenticatedV1(QString mxcUri) const
+{
+ return QString::fromStdString(m_clientOnSecondaryRoot.mxcUriToHttpV1(mxcUri.toStdString()));
+}
+
+MatrixDeviceList *MatrixSession::devicesOfUser(QString userId) const
+{
+ return new MatrixDeviceList(m_clientOnSecondaryRoot.devicesOfUser(userId.toStdString()));
+}
+
+bool isIllFormatSpecVersion(const QString &version)
+{
+ const bool isLegacy = version.startsWith(u"r"_s);
+ if (isLegacy && version.split(u'.').size() == 3) {
+ return false;
+ }
+ if (!isLegacy && version.split(u'.').size() == 2) {
+ return false;
+ }
+ return true;
+}
+
+// Return true if v1 is at least as new as v2, false if v1 is older than v2
+// Return false if v1 or v2 is ill-format
+bool compareSpecVersion(QString v1, QString v2)
+{
+ // Check parameters format
+ if (isIllFormatSpecVersion(v1) || isIllFormatSpecVersion(v2)) {
+ return false;
+ }
+
+ const bool v1IsLegacy = v1.startsWith(u"r"_s);
+ const bool v2IsLegacy = v2.startsWith(u"r"_s);
+ if (v1IsLegacy != v2IsLegacy) {
+ return v2IsLegacy;
+ }
+
+ v1.remove(0, 1);
+ v2.remove(0, 1);
+ auto v1VersionNumbers = v1.split(u'.');
+ auto v2VersionNumbers = v2.split(u'.');
+
+ for (int i = 0; i < v1VersionNumbers.size(); i++) {
+ auto v1VerNum = v1VersionNumbers[i].toInt();
+ auto v2VerNum = v2VersionNumbers[i].toInt();
+ if (v1VerNum != v2VerNum) {
+ return v1VerNum > v2VerNum;
+ }
+ }
+ // v1 is equal to v2
+ return true;
+}
+
+// Return true if version in the range [minVer, maxVer]
+// Return false if any parameter is ill-format
+bool compareSpecVersionRange(const QString &version,
+ const QString &minVer, const QString &maxVer)
+{
+ if (isIllFormatSpecVersion(version)
+ || isIllFormatSpecVersion(minVer)
+ || isIllFormatSpecVersion(maxVer)) {
+ return false;
+ }
+
+ if (compareSpecVersion(version, minVer) && compareSpecVersion(maxVer, version)) {
+ return true;
+ }
+
+ return false;
+}
+
+bool MatrixSession::checkSpecVersion(QString version) const
+{
+ return std::find_if(specVersions().begin(), specVersions().end(),
+ [&version](auto v) {
+ return compareSpecVersion(QString::fromStdString(v), version);
+ }) != specVersions().end();
+}
+
+bool MatrixSession::checkSpecVersionRange(QString minVer, QString maxVer) const
+{
+ return std::find_if(specVersions().begin(), specVersions().end(),
+ [&minVer, maxVer](auto v) {
+ return compareSpecVersionRange(QString::fromStdString(v), minVer, maxVer);
+ }) != specVersions().end();
+}
+
+QStringList MatrixSession::directRoomIds(QString userId) const
+{
+ auto content = m_clientOnSecondaryRoot.accountData().get()["m.direct"].content().get();
+ auto roomIds = QStringList{};
+ for (auto i : content[userId.toStdString()]) {
+ roomIds.push_back(QString::fromStdString(i.get<std::string>()));
+ }
+ return roomIds;
+}
+
+MatrixVerificationList *MatrixSession::verificationList() const
+{
+ return new MatrixVerificationList(m_clientOnSecondaryRoot, m_verificationTrackerState);
+}
+
+std::string MatrixSession::validateHomeserverUrl(const QString &url)
+{
+ if (url.isEmpty()) {
+ return std::string();
+ }
+
+ auto u = QUrl::fromUserInput(url);
+ if (!u.isValid()) {
+ return std::string();
+ }
+
+ if (u.scheme() == u"http"_s) {
+ qCInfo(kazvLog) << "url" << u << "is http. Force switching to https.";
+ u.setScheme(u"https"_s);
+ } else if (u.scheme() != u"https"_s) {
+ qCWarning(kazvLog) << "url" << u << "is not http/https.";
+ return std::string();
+ }
+
+ return u.toString().toStdString();
+}
+
+void MatrixSession::login(const QString &userId, const QString &password, const QString &homeserverUrl)
+{
+ auto loginFunc = [userId, password](const Client &client, const std::string &serverUrl) {
+ client.passwordLogin(
+ serverUrl,
+ userId.toStdString(),
+ password.toStdString(),
+ clientName
+ );
+ };
+
+ auto validated = validateHomeserverUrl(homeserverUrl);
+ if (!validated.empty()) {
+ loginFunc(m_clientOnSecondaryRoot, validated);
+ } else {
+ m_clientOnSecondaryRoot
+ .autoDiscover(userId.toStdString()) // autoDiscover() will dispatch GetVersionAction to get supported versions of the server
+ .then([
+ this,
+ client=m_clientOnSecondaryRoot.toEventLoop(),
+ userId,
+ password,
+ loginFunc
+ ](auto res) {
+ if (!res.success()) {
+ // FIXME use real error codes and msgs when available in libkazv
+ Q_EMIT this->discoverFailed(u""_s, u""_s);
+ return res;
+ }
+
+ auto serverUrl = res.dataStr("homeserverUrl");
+
+ loginFunc(client, serverUrl);
+
+ return res;
+ });
+ }
+}
+
+void MatrixSession::discoverAndGetLoginFlows(const QString &userId, const QString &homeserverUrl)
+{
+ auto validated = validateHomeserverUrl(homeserverUrl);
+ if (!validated.empty()) {
+ getLoginFlows(homeserverUrl);
+ } else {
+ m_clientOnSecondaryRoot
+ .autoDiscover(userId.toStdString())
+ .then([
+ this
+ ](const EffectStatus &res) {
+ if (!res.success()) {
+ Q_EMIT discoverFailed(
+ QString::fromStdString(res.dataStr("errorCode")),
+ QString::fromStdString(res.dataStr("error"))
+ );
+ return;
+ }
+ auto serverUrl = QString::fromStdString(res.dataStr("homeserverUrl"));
+ Q_EMIT discoverSuccessful(serverUrl);
+ getLoginFlows(serverUrl);
+ });
+ }
+}
+
+void MatrixSession::getLoginFlows(const QString &serverUrl)
+{
+ // TODO: restore
+
+ // m_d->jobHandler->submit(
+ // Api::GetLoginFlowsJob(serverUrl.toStdString()),
+ // [this](Api::GetLoginFlowsResponse r) {
+ // if (!r.success()) {
+ // Q_EMIT getLoginFlowsFailed(
+ // QString::fromStdString(r.errorCode()),
+ // QString::fromStdString(r.errorMessage())
+ // );
+ // return;
+ // }
+ // auto v = std::move(r).jsonBody().get().at("flows").template get<QJsonValue>();
+ // Q_EMIT getLoginFlowsSuccessful(v);
+ // }
+ // );
+}
+
+QUrl MatrixSession::ssoLoginStart(const QString &homeserverUrl)
+{
+ if (m_ssoLoginProcess) {
+ m_ssoLoginProcess.reset();
+ }
+ m_ssoLoginProcess.reset(new SsoLoginProcess());
+ if (!m_ssoLoginProcess->startServer()) {
+ return QUrl();
+ }
+ auto link = m_ssoLoginProcess->getSsoLink(homeserverUrl);
+ connect(m_ssoLoginProcess.get(), &SsoLoginProcess::loginTokenAvailable,
+ this, [this, homeserverUrl](const QString &loginToken) {
+ Q_EMIT ssoLoginTokenAvailable();
+ qCInfo(kazvLog) << "Got SSO login token";
+ m_ssoLoginProcess.reset();
+ m_clientOnSecondaryRoot.mLoginTokenLogin(
+ homeserverUrl.toStdString(),
+ loginToken.toStdString(),
+ clientName
+ );
+ });
+ return link;
+}
+
+void MatrixSession::logout()
+{
+ m_clientOnSecondaryRoot.logout()
+ .then([&] (EffectStatus stat) {
+ if (stat.success()) {
+ Q_EMIT this->logoutSuccessful();
+ } else {
+ Q_EMIT this->logoutFailed(QString::fromStdString(stat.dataStr("errorCode")), QString::fromStdString(stat.dataStr("error")));
+ }
+ });
+}
+
+MatrixRoomList *MatrixSession::roomList() const
+{
+ return new MatrixRoomList(m_clientOnSecondaryRoot);
+}
+
+static std::optional<std::string> optMaybe(QString s)
+{
+ if (s.isEmpty()) {
+ return std::nullopt;
+ } else {
+ return s.toStdString();
+ }
+}
+
+MatrixPromise *MatrixSession::createRoom(
+ bool isPrivate,
+ const QString &name,
+ const QString &alias,
+ const QStringList &invite,
+ bool isDirect,
+ bool allowFederate,
+ const QString &topic,
+ const QJsonValue &powerLevelContentOverride,
+ CreateRoomPreset preset,
+ bool encrypted
+)
+{
+ immer::array<Event> initialState;
+
+ if (encrypted) {
+ initialState = {Event{json{
+ {"type", "m.room.encryption"},
+ {"state_key", ""},
+ {"content", {
+ {"algorithm", "m.megolm.v1.aes-sha2"},
+ }},
+ }}};
+ }
+
+ return new MatrixPromise(m_clientOnSecondaryRoot.createRoom(
+ isPrivate ? Kazv::RoomVisibility::Private : Kazv::RoomVisibility::Public,
+ optMaybe(name),
+ optMaybe(alias),
+ qStringListToStdF(invite),
+ isDirect,
+ allowFederate,
+ optMaybe(topic),
+ nlohmann::json(powerLevelContentOverride),
+ static_cast<Kazv::CreateRoomPreset>(preset),
+ initialState
+ ));
+}
+
+MatrixPromise *MatrixSession::joinRoom(const QString &idOrAlias, const QStringList &servers)
+{
+ return new MatrixPromise(m_clientOnSecondaryRoot.joinRoom(
+ idOrAlias.toStdString(),
+ qStringListToStdF(servers)
+ ));
+}
+
+MatrixPromise *MatrixSession::setDeviceTrustLevel(QString userId, QString deviceId, QString trustLevel)
+{
+ return new MatrixPromise(
+ m_clientOnSecondaryRoot.setDeviceTrustLevel(
+ userId.toStdString(),
+ deviceId.toStdString(),
+ qStringToTrustLevelFunc(trustLevel)
+ )
+ );
+}
+
+MatrixPromise *MatrixSession::getSelfProfile()
+{
+ return new MatrixPromise(
+ m_clientOnSecondaryRoot.getProfile(userId().toStdString())
+ );
+}
+
+MatrixPromise *MatrixSession::setDisplayName(QString displayName)
+{
+ return new MatrixPromise(
+ m_clientOnSecondaryRoot.setDisplayName(
+ displayName.isEmpty() ? std::nullopt : std::optional<std::string>(displayName.toStdString())
+ )
+ );
+}
+
+MatrixPromise *MatrixSession::setAvatarUrl(QString avatarUrl)
+{
+ return new MatrixPromise(
+ m_clientOnSecondaryRoot.setAvatarUrl(
+ avatarUrl.isEmpty() ? std::nullopt : std::optional<std::string>(avatarUrl.toStdString())
+ )
+ );
+}
+
+bool MatrixSession::shouldNotify(MatrixEvent *event) const
+{
+ // Do not notify own event
+ if (event->sender() == userId()) {
+ return false;
+ }
+ return m_notificationHandler.handleNotification(event->underlyingEvent()).shouldNotify;
+}
+
+bool MatrixSession::shouldPlaySound(MatrixEvent *event) const
+{
+ return m_notificationHandler.handleNotification(event->underlyingEvent()).sound.has_value();
+}
+
+MatrixStickerPackList *MatrixSession::stickerPackList() const
+{
+ return new MatrixStickerPackList(m_clientOnSecondaryRoot);
+}
+
+MatrixEvent *MatrixSession::stickerRoomsEvent() const
+{
+ return new MatrixEvent(m_clientOnSecondaryRoot.accountData()[imagePackRoomsEventType][lager::lenses::or_default]);
+}
+
+MatrixPromise *MatrixSession::updateStickerPack(MatrixStickerPackSource source)
+{
+ if (source.source == MatrixStickerPackSource::AccountData) {
+ auto eventJson = std::move(source.event).raw().get();
+ eventJson["type"] = source.eventType;
+ return sendAccountDataImpl(Event(std::move(eventJson)));
+ } else if (source.source == MatrixStickerPackSource::RoomState) {
+ auto eventJson = std::move(source.event).raw().get();
+ eventJson["type"] = source.eventType;
+ eventJson["state_key"] = source.stateKey;
+ return new MatrixPromise(
+ m_clientOnSecondaryRoot
+ .room(source.roomId)
+ .sendStateEvent(Event(std::move(eventJson))));
+ } else {
+ return 0;
+ }
+}
+
+MatrixUserGivenAttrsMap *MatrixSession::userGivenNicknameMap() const
+{
+ return new MatrixUserGivenAttrsMap(
+ userGivenNicknameMapFor(m_clientOnSecondaryRoot),
+ [client=m_clientOnSecondaryRoot](json content) {
+ return client.setAccountData(json{
+ {"type", USER_GIVEN_NICKNAME_EVENT_TYPES[0]},
+ {"content", std::move(content)},
+ });
+ }
+ );
+}
+
+MatrixPromise *MatrixSession::sendAccountData(const QString &type, const QJsonObject &content)
+{
+ Event e = json{
+ {"type", type.toStdString()},
+ {"content", content},
+ };
+ return sendAccountDataImpl(std::move(e));
+}
+
+MatrixPromise *MatrixSession::sendAccountDataImpl(Event event)
+{
+ return new MatrixPromise(m_clientOnSecondaryRoot.setAccountData(event));
+}
+
+MatrixPromise *MatrixSession::getSpecVersions()
+{
+ return new MatrixPromise(m_clientOnSecondaryRoot.getVersions(LAGER_QT(serverUrl).get().toStdString()));
+}
+
+MatrixPromise *MatrixSession::addDirectRoom(const QString &userId, const QString &roomId)
+{
+ return new MatrixPromise(m_clientOnSecondaryRoot.addDirectRoom(userId.toStdString(), roomId.toStdString()));
+}
+
+MatrixPromise *MatrixSession::getRoomIdByAlias(const QString &roomAlias)
+{
+ // TODO: restore
+
+ // auto job = m_clientOnSecondaryRoot
+ // .getRoomIdByAliasJob(roomAlias.toStdString());
+ // auto jh = m_d->jobHandler;
+ // return new MatrixPromise(
+ // m_context.createPromise([jh, job](auto resolve) {
+ // jh->submit(job, [resolve](GetRoomIdByAliasResponse r) {
+ // resolve(parseGetRoomIdByAliasResponse(r));
+ // });
+ // }));
+ return nullptr;
+}
+
+inline constexpr std::size_t purgeEventsKeepNumber = 5;
+MatrixPromise *MatrixSession::purgeEventsExceptRooms(const QStringList &roomIds)
+{
+ return new MatrixPromise(
+ m_context.createResolvedPromise({})
+ .then([client=m_clientOnSecondaryRoot.toEventLoop(), roomIds=qStringListToStdF(roomIds)
+ ]([[maybe_unused]] auto &&stat) {
+ auto map = intoImmer(immer::map<std::string, std::size_t>{},
+ zug::filter([&roomIds](const std::string &roomId) {
+ return std::find(roomIds.begin(), roomIds.end(), roomId) == roomIds.end();
+ })
+ | zug::map([](const std::string &roomId) {
+ return std::make_pair(roomId, purgeEventsKeepNumber);
+ }),
+ client.roomIds().make().get()
+ );
+ return client.purgeRoomEvents(map);
+ }).then([](const auto &stat) {
+ qCDebug(kazvLog) << "Purge room events stat:" << !!stat;
+ return stat;
+ })
+ );
+}
+
+MatrixPromise *MatrixSession::backfillRoomFromEvent(const QString &roomId, const QString &eventId)
+{
+ return new MatrixPromise(
+ m_context.createPromise([
+ dbStoreGetter=m_dbStoreGetter,
+ client=m_clientOnSecondaryRoot.toEventLoop(),
+ roomId,
+ eventId
+ ](auto resolve) {
+ dbStoreGetter().getEventsBefore(roomId, eventId).then([client, resolve, roomId](auto &&res) {
+ auto [timelineEvents, relatedEvents] = std::move(res);
+ auto loadedCount = timelineEvents[roomId.toStdString()].size();
+ client.loadEventsFromStorage(timelineEvents, relatedEvents)
+ .then([resolve, loadedCount](auto &&) {
+ resolve(EffectStatus(
+ !!loadedCount,
+ json{{"loadedCount", loadedCount}}
+ ));
+ });
+ });
+ })
+ );
+}
+
+MatrixPromise *MatrixSession::importFromKeyBackupFile(QUrl fileUrl, QString password)
+{
+ if (!fileUrl.isLocalFile()) {
+ return new MatrixPromise(m_context.createResolvedPromise({
+ /* succ = */ false,
+ json{{"error", "Not a local file"}, {"errorCode", "NOT_LOCAL_FILE"}},
+ }));
+ }
+ auto filename = fileUrl.toLocalFile();
+ auto f = QFile(filename);
+ if (!f.open(QFile::ReadOnly)) {
+ return new MatrixPromise(m_context.createResolvedPromise({
+ /* succ = */ false,
+ json{{"error", "File open failed"}, {"errorCode", "FILE_OPEN_FAILED"}},
+ }));
+ }
+ auto ba = f.readAll();
+ auto content = std::string(ba.begin(), ba.end());
+ auto passwordStd = std::move(password).toStdString();
+ return new MatrixPromise(m_clientOnSecondaryRoot.importFromKeyBackupFile(
+ std::move(content), std::move(passwordStd)
+ ));
+}
+
+MatrixPromise *MatrixSession::requestVerifyDevice(QString userId, QString deviceId)
+{
+ return new MatrixPromise(m_clientOnSecondaryRoot.requestOutgoingToDeviceVerification(
+ std::move(userId).toStdString(),
+ std::move(deviceId).toStdString()
+ ));
+}
diff --git a/src/tests/CMakeLists.txt b/src/tests/CMakeLists.txt
--- a/src/tests/CMakeLists.txt
+++ b/src/tests/CMakeLists.txt
@@ -18,7 +18,7 @@
qt-promise-handler-test.cpp
qt-json-test.cpp
device-mgmt-test.cpp
- matrix-sdk-test.cpp
+ matrix-session-test.cpp
matrix-sdk-sessions-test.cpp
matrix-promise-test.cpp
matrix-room-timeline-test.cpp
diff --git a/src/tests/kazv-io-manager-test.cpp b/src/tests/kazv-io-manager-test.cpp
--- a/src/tests/kazv-io-manager-test.cpp
+++ b/src/tests/kazv-io-manager-test.cpp
@@ -128,8 +128,8 @@
model.client.roomList.rooms =
model.client.roomList.rooms.set(room.roomId, room);
- std::unique_ptr<MatrixSdk> sdk{makeTestSdk(model)};
- auto roomList = toUniquePtr(sdk->roomList());
+ SessionSetup s(model);
+ auto roomList = toUniquePtr(s.session.roomList());
KazvIOManager manager;
auto job = manager.startNewUploadJob(QUrl{u"serverUrl"_s}, QUrl{u"localFileUrl"_s},
@@ -172,8 +172,8 @@
model.client.roomList.rooms =
model.client.roomList.rooms.set(room.roomId, room);
- std::unique_ptr<MatrixSdk> sdk{makeTestSdk(model)};
- auto roomList = toUniquePtr(sdk->roomList());
+ SessionSetup s(model);
+ auto roomList = toUniquePtr(s.session.roomList());
manager.startNewUploadJob(QUrl{u"serverUrl"_s}, QUrl{u"localFileUrl"_s},
u"token"_s, QString::fromStdString(room.roomId), roomList.get(), false,
u"relType"_s, u"relatedTo"_s);
diff --git a/src/tests/matrix-event-test.cpp b/src/tests/matrix-event-test.cpp
--- a/src/tests/matrix-event-test.cpp
+++ b/src/tests/matrix-event-test.cpp
@@ -46,8 +46,8 @@
}));
auto model = SdkModel{makeClient(withRoom(r))};
- std::unique_ptr<MatrixSdk> sdk{makeTestSdk(model)};
- auto roomList = toUniquePtr(sdk->roomList());
+ SessionSetup s(model);
+ auto roomList = toUniquePtr(s.session.roomList());
auto room = toUniquePtr(roomList->room(QString::fromStdString(r.roomId)));
auto timeline = toUniquePtr(room->timeline());
@@ -90,8 +90,8 @@
withRoomTimeline(events)(r);
auto model = SdkModel{makeClient(withRoom(r))};
- std::unique_ptr<MatrixSdk> sdk{makeTestSdk(model)};
- auto roomList = toUniquePtr(sdk->roomList());
+ SessionSetup s(model);
+ auto roomList = toUniquePtr(s.session.roomList());
auto room = toUniquePtr(roomList->room(QString::fromStdString(r.roomId)));
auto timeline = toUniquePtr(room->timeline());
@@ -147,8 +147,8 @@
withRoomTimeline(events)(r);
auto model = SdkModel{makeClient(withRoom(r))};
- std::unique_ptr<MatrixSdk> sdk{makeTestSdk(model)};
- auto roomList = toUniquePtr(sdk->roomList());
+ SessionSetup s(model);
+ auto roomList = toUniquePtr(s.session.roomList());
auto room = toUniquePtr(roomList->room(QString::fromStdString(r.roomId)));
auto timeline = toUniquePtr(room->timeline());
diff --git a/src/tests/matrix-room-list-test.cpp b/src/tests/matrix-room-list-test.cpp
--- a/src/tests/matrix-room-list-test.cpp
+++ b/src/tests/matrix-room-list-test.cpp
@@ -52,8 +52,8 @@
room.accountData = room.accountData.set("m.tag", tagEvent);
model.client.roomList.rooms = model.client.roomList.rooms.set(room.roomId, room);
- std::unique_ptr<MatrixSdk> sdk{makeTestSdk(model)};
- auto roomList = toUniquePtr(sdk->roomList());
+ SessionSetup s(model);
+ auto roomList = toUniquePtr(s.session.roomList());
QCOMPARE(roomList->count(), 2);
@@ -102,8 +102,8 @@
withRoom(room3)(model.client);
withRoom(room4)(model.client);
- std::unique_ptr<MatrixSdk> sdk{makeTestSdk(model)};
- auto roomList = toUniquePtr(sdk->roomList());
+ SessionSetup s(model);
+ auto roomList = toUniquePtr(s.session.roomList());
QCOMPARE(roomList->roomIdAt(0), QString::fromStdString(room4.roomId));
QCOMPARE(roomList->roomIdAt(1), QString::fromStdString(room3.roomId));
@@ -141,8 +141,8 @@
withRoom(room2)(model.client);
withRoom(room3)(model.client);
- std::unique_ptr<MatrixSdk> sdk{makeTestSdk(model)};
- auto roomList = toUniquePtr(sdk->roomList());
+ SessionSetup s(model);
+ auto roomList = toUniquePtr(s.session.roomList());
QCOMPARE(roomList->roomIdAt(0), QString::fromStdString(room3.roomId));
QCOMPARE(roomList->roomIdAt(1), QString::fromStdString(room1.roomId));
@@ -177,8 +177,8 @@
);
withAccountData({ nicknameEvent })(model.client);
- std::unique_ptr<MatrixSdk> sdk{makeTestSdk(model)};
- auto roomList = toUniquePtr(sdk->roomList());
+ SessionSetup s(model);
+ auto roomList = toUniquePtr(s.session.roomList());
roomList->setfilter(u"some"_s);
QCOMPARE(roomList->count(), 2);
@@ -222,8 +222,8 @@
model.client.roomList.rooms = model.client.roomList.rooms.set(
roomFavourited.roomId, roomFavourited);
- std::unique_ptr<MatrixSdk> sdk{makeTestSdk(model)};
- auto roomList = toUniquePtr(sdk->roomList());
+ SessionSetup s(model);
+ auto roomList = toUniquePtr(s.session.roomList());
QCOMPARE(roomList->count(), 3);
// Priority: Invite > Favourite
@@ -238,8 +238,8 @@
room.roomId = "!test:tusooa.xyz";
model.client.roomList.rooms = model.client.roomList.rooms.set(room.roomId, room);
- std::unique_ptr<MatrixSdk> sdk{makeTestSdk(model)};
- auto roomList = toUniquePtr(sdk->roomList());
+ SessionSetup s(model);
+ auto roomList = toUniquePtr(s.session.roomList());
QVERIFY(roomList->contains(u"!test:tusooa.xyz"_s));
QVERIFY(!roomList->contains(u"!notexist:tusooa.xyz"_s));
}
diff --git a/src/tests/matrix-room-state-test.cpp b/src/tests/matrix-room-state-test.cpp
--- a/src/tests/matrix-room-state-test.cpp
+++ b/src/tests/matrix-room-state-test.cpp
@@ -40,8 +40,8 @@
auto r = makeRoom(withRoomState(state));
auto c = makeClient(withRoom(r));
- std::unique_ptr<MatrixSdk> sdk{makeTestSdk(SdkModel{c})};
- auto roomList = toUniquePtr(sdk->roomList());
+ SessionSetup s(SdkModel{c});
+ auto roomList = toUniquePtr(s.session.roomList());
auto room = toUniquePtr(roomList->room(QString::fromStdString(r.roomId)));
auto allState = toUniquePtr(room->allState());
diff --git a/src/tests/matrix-room-timeline-benchmark-test.cpp b/src/tests/matrix-room-timeline-benchmark-test.cpp
--- a/src/tests/matrix-room-timeline-benchmark-test.cpp
+++ b/src/tests/matrix-room-timeline-benchmark-test.cpp
@@ -73,8 +73,8 @@
};
auto model = SdkModel{makeClient(withRoom(r))};
- std::unique_ptr<MatrixSdk> sdk{makeTestSdk(model)};
- auto roomList = toUniquePtr(sdk->roomList());
+ SessionSetup s(model);
+ auto roomList = toUniquePtr(s.session.roomList());
auto room = toUniquePtr(roomList->room(QString::fromStdString(r.roomId)));
auto timeline = toUniquePtr(room->timeline());
diff --git a/src/tests/matrix-room-timeline-test.cpp b/src/tests/matrix-room-timeline-test.cpp
--- a/src/tests/matrix-room-timeline-test.cpp
+++ b/src/tests/matrix-room-timeline-test.cpp
@@ -41,8 +41,8 @@
void MatrixRoomTimelineTest::testLocalEcho()
{
auto model = makeTestModel();
- std::unique_ptr<MatrixSdk> sdk{makeTestSdk(model)};
- auto roomList = toUniquePtr(sdk->roomList());
+ SessionSetup s(model);
+ auto roomList = toUniquePtr(s.session.roomList());
auto room = toUniquePtr(roomList->room(u"!foo:tusooa.xyz"_s));
auto timeline = toUniquePtr(room->timeline());
@@ -68,8 +68,8 @@
r.eventReadUsers = {{"$1", {"@a:example.com", "@b:example.com"}}};
r.readReceipts = {{"@a:example.com", {"$1", 1234}}, {"@b:example.com", {"$2", 5678}}};
auto model = SdkModel{makeClient(withRoom(r))};
- std::unique_ptr<MatrixSdk> sdk{makeTestSdk(model)};
- auto roomList = toUniquePtr(sdk->roomList());
+ SessionSetup s(model);
+ auto roomList = toUniquePtr(s.session.roomList());
auto room = toUniquePtr(roomList->room(QString::fromStdString(r.roomId)));
auto timeline = toUniquePtr(room->timeline());
{
@@ -113,8 +113,8 @@
makeEvent(withEventId("$8") | withEventContent(json{{"body", "seventh"}}) | withEventRelationship("m.replace", "$1") | withEventType("moe.kazv.mxc.some-other-type")),
}));
auto model = SdkModel{makeClient(withRoom(r))};
- std::unique_ptr<MatrixSdk> sdk{makeTestSdk(model)};
- auto roomList = toUniquePtr(sdk->roomList());
+ SessionSetup s(model);
+ auto roomList = toUniquePtr(s.session.roomList());
auto room = toUniquePtr(roomList->room(QString::fromStdString(r.roomId)));
auto timeline = toUniquePtr(room->timeline());
@@ -146,8 +146,8 @@
};
auto model = SdkModel{makeClient(withRoom(r))};
- std::unique_ptr<MatrixSdk> sdk{makeTestSdk(model)};
- auto roomList = toUniquePtr(sdk->roomList());
+ SessionSetup s(model);
+ auto roomList = toUniquePtr(s.session.roomList());
auto room = toUniquePtr(roomList->room(QString::fromStdString(r.roomId)));
auto timeline = toUniquePtr(room->timeline());
diff --git a/src/tests/matrix-sdk-sessions-test.cpp b/src/tests/matrix-sdk-sessions-test.cpp
--- a/src/tests/matrix-sdk-sessions-test.cpp
+++ b/src/tests/matrix-sdk-sessions-test.cpp
@@ -107,8 +107,9 @@
spy.wait();
auto res = spy.first().at(1);
QCOMPARE(res, MatrixSdk::SessionLoadSuccess);
- QCOMPARE(sdk->userId(), QStringLiteral("@mew:example.com"));
- QCOMPARE(sdk->deviceId(), QStringLiteral("device4"));
+ // TODO: restore
+ // QCOMPARE(sdk->userId(), QStringLiteral("@mew:example.com"));
+ // QCOMPARE(sdk->deviceId(), QStringLiteral("device4"));
}
void MatrixSdkSessionsTest::testSessionLock()
@@ -193,7 +194,8 @@
auto res = spy.first().at(1);
QCOMPARE(res, MatrixSdk::SessionLoadSuccess);
// verify that the original data is kept
- QCOMPARE(sdk->serverUrl(), u""_s);
+ // TODO: restore
+ // QCOMPARE(sdk->serverUrl(), u""_s);
// before cleaning up, add write permissions back so the files can be removed
Fs::permissions(
diff --git a/src/tests/matrix-sdk-test.cpp b/src/tests/matrix-session-test.cpp
rename from src/tests/matrix-sdk-test.cpp
rename to src/tests/matrix-session-test.cpp
--- a/src/tests/matrix-sdk-test.cpp
+++ b/src/tests/matrix-session-test.cpp
@@ -5,24 +5,20 @@
*/
#include <kazv-defs.hpp>
-
-#include <memory>
-
-#include <QtTest>
-#include <QSignalSpy>
-
-#include <matrix-sdk.hpp>
+#include "test-model.hpp"
+#include "test-utils.hpp"
#include <matrix-device-list.hpp>
#include <matrix-device.hpp>
#include <matrix-promise.hpp>
-
-#include "test-model.hpp"
-#include "test-utils.hpp"
+#include <lager/event_loop/qt.hpp>
+#include <QtTest>
+#include <QSignalSpy>
+#include <memory>
using namespace Qt::Literals::StringLiterals;
using namespace Kazv;
-class MatrixSdkTest : public QObject
+class MatrixSessionTest : public QObject
{
Q_OBJECT
@@ -34,27 +30,25 @@
void testDirectRoomIds();
};
-void MatrixSdkTest::testDevicesOfUser()
+void MatrixSessionTest::testDevicesOfUser()
{
auto model = makeTestModel();
- std::unique_ptr<MatrixSdk> sdk{new MatrixSdk(model, /* testing = */ true)};
+ SessionSetup s(model);
- auto devices = toUniquePtr(sdk->devicesOfUser(QStringLiteral("@test1:test1.org")));
+ auto devices = toUniquePtr(s.session.devicesOfUser(QStringLiteral("@test1:test1.org")));
QVERIFY(devices->count() == 2);
auto dev = toUniquePtr(devices->at(0));
QVERIFY(dev->deviceId() == u"device1"_s);
QVERIFY(dev->trustLevel() == u"unseen"_s);
}
-void MatrixSdkTest::testSetDeviceTrustLevel()
+void MatrixSessionTest::testSetDeviceTrustLevel()
{
auto model = makeTestModel();
- std::unique_ptr<MatrixSdk> sdk{new MatrixSdk(model, /* testing = */ true)};
-
- sdk->startThread();
+ SessionSetup s(model);
auto promise = toUniquePtr(
- sdk->setDeviceTrustLevel(
+ s.session.setDeviceTrustLevel(
QStringLiteral("@test1:test1.org"),
QStringLiteral("device1"),
QStringLiteral("verified")
@@ -64,17 +58,17 @@
auto spy = QSignalSpy(promise.get(), &MatrixPromise::succeeded);
spy.wait();
- auto devices = toUniquePtr(sdk->devicesOfUser(QStringLiteral("@test1:test1.org")));
+ auto devices = toUniquePtr(s.session.devicesOfUser(QStringLiteral("@test1:test1.org")));
QVERIFY(devices->count() == 2);
auto dev = toUniquePtr(devices->at(0));
QVERIFY(dev->deviceId() == u"device1"_s);
QTRY_VERIFY(dev->trustLevel() == u"verified"_s); // wait for the change to propagate to this thread
}
-void MatrixSdkTest::testValidateHomeserverUrl()
+void MatrixSessionTest::testValidateHomeserverUrl()
{
auto v = [](const QString &u) {
- return QString::fromStdString(MatrixSdk::validateHomeserverUrl(u));
+ return QString::fromStdString(MatrixSession::validateHomeserverUrl(u));
};
QCOMPARE(v(u"example.com"_s), u"https://example.com"_s);
@@ -84,32 +78,38 @@
QCOMPARE(v(u"ea.pl"_s), u"https://ea.pl"_s);
}
-void MatrixSdkTest::testSpecVersion()
+void MatrixSessionTest::testSpecVersion()
{
auto model = makeTestModel();
model.client.versions = immer::array{"v1.11"s, "v1.12"s};
- std::unique_ptr<MatrixSdk> sdk{new MatrixSdk(model, /* testing = */ true)};
- QVERIFY(sdk->checkSpecVersion(u"r0.5.0"_s));
- QVERIFY(sdk->checkSpecVersion(u"v1.10"_s));
- QVERIFY(sdk->checkSpecVersion(u"v1.11"_s));
- QVERIFY(!sdk->checkSpecVersion(u"v1.13"_s));
- QVERIFY(!sdk->checkSpecVersion(u"mew"_s));
- QVERIFY(sdk->checkSpecVersionRange(u"r0.5.0"_s, u"v1.12"_s));
- QVERIFY(sdk->checkSpecVersionRange(u"v1.10"_s, u"v1.12"_s));
- QVERIFY(sdk->checkSpecVersionRange(u"v1.11"_s, u"v1.13"_s));
- QVERIFY(!sdk->checkSpecVersionRange(u"r0.4.0"_s, u"v1.9"_s));
- QVERIFY(!sdk->checkSpecVersionRange(u"v1.13"_s, u"v1.14"_s));
+
+ {
+ SessionSetup s(model);
+
+ QVERIFY(s.session.checkSpecVersion(u"r0.5.0"_s));
+ QVERIFY(s.session.checkSpecVersion(u"v1.10"_s));
+ QVERIFY(s.session.checkSpecVersion(u"v1.11"_s));
+ QVERIFY(!s.session.checkSpecVersion(u"v1.13"_s));
+ QVERIFY(!s.session.checkSpecVersion(u"mew"_s));
+ QVERIFY(s.session.checkSpecVersionRange(u"r0.5.0"_s, u"v1.12"_s));
+ QVERIFY(s.session.checkSpecVersionRange(u"v1.10"_s, u"v1.12"_s));
+ QVERIFY(s.session.checkSpecVersionRange(u"v1.11"_s, u"v1.13"_s));
+ QVERIFY(!s.session.checkSpecVersionRange(u"r0.4.0"_s, u"v1.9"_s));
+ QVERIFY(!s.session.checkSpecVersionRange(u"v1.13"_s, u"v1.14"_s));
+ }
model.client.versions = immer::array{"r0.4.0"s};
- sdk.reset(new MatrixSdk(model, /* testing = */ true));
- QVERIFY(sdk->checkSpecVersion(u"r0.4.0"_s));
- QVERIFY(!sdk->checkSpecVersion(u"v1.1"_s));
- QVERIFY(sdk->checkSpecVersionRange(u"r0.3.5"_s, u"r0.4.0"_s));
- QVERIFY(!sdk->checkSpecVersionRange(u"r0.3.0"_s, u"r0.3.5"_s));
- QVERIFY(!sdk->checkSpecVersionRange(u"r0.4.1"_s, u"r0.4.5"_s));
- QVERIFY(!sdk->checkSpecVersionRange(u"v1.1"_s, u"v1.2"_s));
+ {
+ SessionSetup s(model);
+ QVERIFY(s.session.checkSpecVersion(u"r0.4.0"_s));
+ QVERIFY(!s.session.checkSpecVersion(u"v1.1"_s));
+ QVERIFY(s.session.checkSpecVersionRange(u"r0.3.5"_s, u"r0.4.0"_s));
+ QVERIFY(!s.session.checkSpecVersionRange(u"r0.3.0"_s, u"r0.3.5"_s));
+ QVERIFY(!s.session.checkSpecVersionRange(u"r0.4.1"_s, u"r0.4.5"_s));
+ QVERIFY(!s.session.checkSpecVersionRange(u"v1.1"_s, u"v1.2"_s));
+ }
}
-void MatrixSdkTest::testDirectRoomIds()
+void MatrixSessionTest::testDirectRoomIds()
{
auto model = makeTestModel();
auto j = R"({
@@ -122,13 +122,13 @@
}
})"_json;
model.client.accountData = model.client.accountData.set("m.direct", j);
- std::unique_ptr<MatrixSdk> sdk{new MatrixSdk(model, /* testing = */ true)};
+ SessionSetup s(model);
const auto rooms = QStringList{u"!somewhere1:example.org"_s, u"!somewhere2:example.org"_s};
- const auto directRoomIds = sdk->directRoomIds(u"@mew:example.org"_s);
+ const auto directRoomIds = s.session.directRoomIds(u"@mew:example.org"_s);
QCOMPARE(QSet(directRoomIds.begin(), directRoomIds.end()),
QSet(rooms.begin(), rooms.end()));
}
-QTEST_MAIN(MatrixSdkTest)
+QTEST_MAIN(MatrixSessionTest)
-#include "matrix-sdk-test.moc"
+#include "matrix-session-test.moc"
diff --git a/src/tests/matrix-sticker-pack-test.cpp b/src/tests/matrix-sticker-pack-test.cpp
--- a/src/tests/matrix-sticker-pack-test.cpp
+++ b/src/tests/matrix-sticker-pack-test.cpp
@@ -179,8 +179,8 @@
| withRoomState({getRoomStickersEvent("", "Pack 3")})
))
);
- std::unique_ptr<MatrixSdk> sdk{makeTestSdk(SdkModel{model})};
- auto stickerPackList = toUniquePtr(sdk->stickerPackList());
+ SessionSetup s(SdkModel{model});
+ auto stickerPackList = toUniquePtr(s.session.stickerPackList());
QCOMPARE(stickerPackList->rowCount(QModelIndex()), 4);
QCOMPARE(stickerPackList->count(), 4);
@@ -267,8 +267,8 @@
})
))
);
- std::unique_ptr<MatrixSdk> sdk{makeTestSdk(SdkModel{model})};
- auto roomList = toUniquePtr(sdk->roomList());
+ SessionSetup s(SdkModel{model});
+ auto roomList = toUniquePtr(s.session.roomList());
auto room = toUniquePtr(roomList->room(u"!someroom:example.org"_s));
auto stickerPackList = toUniquePtr(room->stickerPackList());
QCOMPARE(stickerPackList->count(), 2);
diff --git a/src/tests/test-model.hpp b/src/tests/test-model.hpp
--- a/src/tests/test-model.hpp
+++ b/src/tests/test-model.hpp
@@ -6,13 +6,53 @@
#pragma once
#include <kazv-defs.hpp>
-
+#include <db-store.hpp>
+#include <matrix-session.hpp>
+#include <matrix-session-controller-p.hpp>
+#include <qt-rand-adapter.hpp>
+#include <qt-job-handler.hpp>
#include <sdk-model.hpp>
class MatrixSdk;
Kazv::SdkModel makeTestModel();
-MatrixSdk *makeTestSdk(Kazv::SdkModel model);
+struct SessionSetup
+{
+ RandomInterface randomGenerator;
+ QObject obj;
+ // QObject objSecondary;
+ QtJobHandler jobHandler;
+ LagerStoreEventEmitter ee;
+ MatrixSessionSdkT sdk;
+ lager::state<VerificationTrackerModel> verificationTrackerState;
+ // SecondaryRootT secondaryRoot;
+ DbStore dbStore;
+ MatrixSession session;
+
+ inline SessionSetup(SdkModel m)
+ : randomGenerator(QtRandAdapter{})
+ , obj()
+ // , objSecondary()
+ , jobHandler(&obj)
+ , ee{lager::with_qt_event_loop{obj}}
+ , sdk(makeSdk(
+ m,
+ jobHandler,
+ ee,
+ QtPromiseHandler(obj),
+ zug::identity,
+ withRandomGenerator(randomGenerator)
+ ))
+ // , secondaryRoot(sdk.createSecondaryRoot(QtEventLoop{this}, std::move(m)))
+ , session(
+ sdk.context(),
+ sdk.client(),
+ ee.watchable(),
+ verificationTrackerState,
+ [this]() -> DbStore & { return dbStore; }
+ )
+ {}
+};
Kazv::Event makeRoomMember(std::string userId, std::string displayName = "", std::string avatarUrl = "");
diff --git a/src/tests/test-model.cpp b/src/tests/test-model.cpp
--- a/src/tests/test-model.cpp
+++ b/src/tests/test-model.cpp
@@ -55,11 +55,6 @@
return model;
}
-MatrixSdk *makeTestSdk(SdkModel model)
-{
- return new MatrixSdk(model, /* testing = */ true);
-}
-
Event makeRoomMember(std::string userId, std::string displayName, std::string avatarUrl)
{
auto content = json::object({{"membership", "join"}});

File Metadata

Mime Type
text/plain
Expires
Sun, Jul 12, 7:18 AM (19 h, 16 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
1685213
Default Alt Text
D311.1783865924.diff (116 KB)

Event Timeline