Page MenuHomePhorge

No OneTemporary

Size
18 KB
Referenced Files
None
Subscribers
None
diff --git a/src/matrix-sdk.cpp b/src/matrix-sdk.cpp
index 7e829a0..4941744 100644
--- a/src/matrix-sdk.cpp
+++ b/src/matrix-sdk.cpp
@@ -1,398 +1,408 @@
/*
* Copyright (C) 2020-2021 Tusooa Zhu <tusooa@kazv.moe>
*
* This file is part of kazv.
*
* kazv is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* kazv is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with kazv. If not, see <https://www.gnu.org/licenses/>.
*/
#include <libkazv-config.hpp>
#include <immer/config.hpp> // https://github.com/arximboldi/immer/issues/168
#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 <KConfig>
#include <KConfigGroup>
#include <eventemitter/lagerstoreeventemitter.hpp>
#include <job/cprjobhandler.hpp>
#include <asio-promise-handler.hpp>
#include <client/sdk.hpp>
#include <base/descendent.hpp>
#include <zug/util.hpp>
#include <boost/asio.hpp>
#include <lager/event_loop/boost_asio.hpp>
+#include <lager/event_loop/qt.hpp>
#include "matrix-sdk.hpp"
#include "matrix-room-list.hpp"
#include "helper.hpp"
#include "kazv-path-config.hpp"
#include "kazv-version.hpp"
using namespace Kazv;
// Sdk with Boost event loop, identity transform and no enhancers
using ExecT = decltype(std::declval<boost::asio::io_context>().get_executor());
using SdkT =
decltype(makeSdk(
SdkModel{},
detail::declref<JobInterface>(),
detail::declref<EventInterface>(),
AsioPromiseHandler{std::declval<boost::asio::io_context>().get_executor()},
zug::identity));
struct MatrixSdkPrivate
{
- MatrixSdkPrivate();
- MatrixSdkPrivate(SdkModel model);
+ MatrixSdkPrivate(MatrixSdk *q);
+ MatrixSdkPrivate(MatrixSdk *q, SdkModel model);
boost::asio::io_context io;
CprJobHandler jobHandler;
LagerStoreEventEmitter ee;
LagerStoreEventEmitter::Watchable watchable;
SdkT sdk;
QTimer saveTimer;
QMutex ioContextLock;
+
+ using SecondaryRootT = decltype(sdk.createSecondaryRoot(std::declval<lager::with_qt_event_loop>()));
+ SecondaryRootT secondaryRoot;
+ Client clientOnSecondaryRoot;
void runIoContext() {
std::thread([this] {
// This lock is to persist as long as io.run() does not return.
// Together with the measures in emplace(), it ensures that
// the io context is not destructed prematurely.
QMutexLocker l(&ioContextLock);
qDebug() << "running io context...";
io.run();
qDebug() << "io context is done";
}).detach();
}
};
static void 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(kazvUserDataDir().toStdString());
auto sessionDir = userDataDir / "sessions"
/ userId / deviceId;
auto storeFile = sessionDir / "store";
auto metadataFile = sessionDir / "metadata";
qDebug() << "storeFile=" << QString::fromStdString(storeFile.native());
std::error_code err;
if ((! std::filesystem::create_directories(sessionDir, err))
&& err) {
qDebug() << "Unable to create sessionDir";
return;
}
{
auto storeStream = std::ofstream(storeFile);
if (! storeStream) {
qDebug() << "Unable to open storeFile";
return;
}
using OAr = boost::archive::text_oarchive;
auto archive = OAr{storeStream};
c.serializeTo(archive);
qDebug() << "Serialization done";
}
// store metadata
{
KConfig metadata(QString::fromStdString(metadataFile.native()));
KConfigGroup mdGroup(&metadata, "Metadata");
mdGroup.writeEntry("kazvVersion", QString::fromStdString(kazvVersionString()));
mdGroup.writeEntry("archiveFormat", "text");
}
}
-MatrixSdkPrivate::MatrixSdkPrivate()
+MatrixSdkPrivate::MatrixSdkPrivate(MatrixSdk *q)
: io{}
, jobHandler{io.get_executor()}
, ee{lager::with_boost_asio_event_loop{io.get_executor()}}
, watchable(ee.watchable())
, sdk(makeDefaultEncryptedSdk(
static_cast<JobInterface &>(jobHandler),
static_cast<EventInterface &>(ee),
AsioPromiseHandler{io.get_executor()},
zug::identity))
+ , secondaryRoot(sdk.createSecondaryRoot(lager::with_qt_event_loop{*q}))
+ , clientOnSecondaryRoot(sdk.clientFromSecondaryRoot(secondaryRoot))
{
}
-MatrixSdkPrivate::MatrixSdkPrivate(SdkModel model)
+MatrixSdkPrivate::MatrixSdkPrivate(MatrixSdk *q, SdkModel model)
: io{}
, jobHandler{io.get_executor()}
, ee{lager::with_boost_asio_event_loop{io.get_executor()}}
, watchable(ee.watchable())
, sdk(makeSdk(
std::move(model),
static_cast<JobInterface &>(jobHandler),
static_cast<EventInterface &>(ee),
AsioPromiseHandler{io.get_executor()},
zug::identity))
+ , secondaryRoot(sdk.createSecondaryRoot(lager::with_qt_event_loop{*q}))
+ , clientOnSecondaryRoot(sdk.clientFromSecondaryRoot(secondaryRoot))
{
}
-MatrixSdk::MatrixSdk(MatrixSdkPrivate *d, QObject *parent)
+MatrixSdk::MatrixSdk(std::unique_ptr<MatrixSdkPrivate> d, QObject *parent)
: QObject(parent)
- , m_d(d)
+ , m_d(std::move(d))
{
init();
connect(this, &MatrixSdk::trigger,
this, [](KazvEvent e) {
qDebug() << "receiving trigger:";
if (std::holds_alternative<LoginSuccessful>(e)) {
qDebug() << "Login successful";
}
});
}
void MatrixSdk::init()
{
- LAGER_QT(userId) = m_d->sdk.client().userId().xform(strToQt); Q_EMIT userIdChanged(userId());
- LAGER_QT(token) = m_d->sdk.client().token().xform(strToQt); Q_EMIT tokenChanged(token());
- LAGER_QT(deviceId) = m_d->sdk.client().deviceId().xform(strToQt); Q_EMIT deviceIdChanged(deviceId());
+ 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());
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(e);
});
connect(&m_d->saveTimer, &QTimer::timeout, &m_d->saveTimer,
- [m_d=m_d.data()]() {
- serializeClientToFile(m_d->sdk.client());
+ [m_d=m_d.get()]() {
+ serializeClientToFile(m_d->clientOnSecondaryRoot);
});
const int saveIntervalMs = 1000 * 60 * 5;
m_d->saveTimer.start(std::chrono::milliseconds{saveIntervalMs});
}
MatrixSdk::MatrixSdk(QObject *parent)
- : MatrixSdk(new MatrixSdkPrivate, parent)
+ : MatrixSdk(std::make_unique<MatrixSdkPrivate>(this), parent)
{
}
MatrixSdk::MatrixSdk(SdkModel model, QObject *parent)
- : MatrixSdk(new MatrixSdkPrivate(std::move(model)), parent)
+ : MatrixSdk(std::make_unique<MatrixSdkPrivate>(this, std::move(model)), parent)
{
}
-static void cleanupDPointer(MatrixSdkPrivate *d)
+static void cleanupDPointer(std::unique_ptr<MatrixSdkPrivate> oldD)
{
- QSharedPointer<MatrixSdkPrivate> oldD(d);
oldD->saveTimer.disconnect();
oldD->saveTimer.stop();
QtConcurrent::run(
- [oldD] {
+ [oldD=std::shared_ptr(std::move(oldD))] {
qDebug() << "start to clean up everything";
// Not running io_context::stop() because it will
// disregard any remaining work guards. Here we just
// want to end any periodic jobs but still wait for
// current remaining jobs.
- oldD->sdk.client().stopSyncing();
+ oldD->clientOnSecondaryRoot.stopSyncing();
oldD->jobHandler.stop();
+
// This lock will not be unlocked as long as io.run()
// in runIoContext() does not return.
QMutexLocker l(&(oldD->ioContextLock));
- qDebug() << "serialize final client state";
- serializeClientToFile(oldD->sdk.client());
+ // qDebug() << "serialize final client state";
+ // serializeClientToFile(oldD->sdk.client());
qDebug() << "from here, the old data can be destructed";
});
}
MatrixSdk::~MatrixSdk()
{
if (m_d) {
serializeToFile();
- cleanupDPointer(m_d.take());
+ cleanupDPointer(std::move(m_d));
}
}
QString MatrixSdk::mxcUriToHttp(QString mxcUri) const
{
- return QString::fromStdString(m_d->sdk.client().mxcUriToHttp(mxcUri.toStdString()));
+ return QString::fromStdString(m_d->clientOnSecondaryRoot.mxcUriToHttp(mxcUri.toStdString()));
}
void MatrixSdk::login(const QString &userId, const QString &password)
{
auto l = userId.split(':');
if (l.size() != 2) {
qDebug() << "User id format error";
return;
}
auto serverUrl = QString("https://") + l[1];
- m_d->sdk.client()
+ m_d->clientOnSecondaryRoot
.passwordLogin(
serverUrl.toStdString(),
userId.toStdString(),
password.toStdString(),
"kazv 0.0.0"
);
m_d->runIoContext();
}
MatrixRoomList *MatrixSdk::roomList() const
{
- return new MatrixRoomList(m_d->sdk.client());
+ return new MatrixRoomList(m_d->clientOnSecondaryRoot);
}
void MatrixSdk::emplace(std::optional<SdkModel> model)
{
if (m_d) {
- cleanupDPointer(m_d.take());
+ cleanupDPointer(std::move(m_d));
}
- m_d.reset(model.has_value()
- ? new MatrixSdkPrivate(std::move(model.value()))
- : new MatrixSdkPrivate());
+
+ m_d = (model.has_value()
+ ? std::make_unique<MatrixSdkPrivate>(this, std::move(model.value()))
+ : std::make_unique<MatrixSdkPrivate>(this));
// Re-initialize lager-qt cursors and watchable connections
init();
m_d->runIoContext();
- m_d->sdk.client().startSyncing();
+ m_d->clientOnSecondaryRoot.startSyncing();
Q_EMIT sessionChanged();
}
QStringList MatrixSdk::allSessions() const
{
using StdPath = std::filesystem::path;
auto userDataDir = StdPath(kazvUserDataDir().toStdString());
auto allSessionsDir = userDataDir / "sessions";
QStringList sessionNames;
try {
for (const auto &p : std::filesystem::directory_iterator(allSessionsDir)) {
if (p.is_directory()) {
auto userId = p.path().filename().native();
for (const auto &q : std::filesystem::directory_iterator(p.path())) {
auto path = q.path();
auto deviceId = path.filename().native();
std::error_code err;
if (std::filesystem::exists(path / "store", err)) {
sessionNames.append(QString::fromStdString(userId + "/" + deviceId));
}
}
}
}
} catch (const std::filesystem::filesystem_error &) {
qDebug() << "sessionDir not available, ignoring";
}
return sessionNames;
}
void MatrixSdk::serializeToFile() const
{
- serializeClientToFile(m_d->sdk.client());
+ serializeClientToFile(m_d->clientOnSecondaryRoot);
}
bool MatrixSdk::loadSession(QString sessionName)
{
qDebug() << "in loadSession(), sessionName=" << sessionName;
using StdPath = std::filesystem::path;
auto userDataDir = StdPath(kazvUserDataDir().toStdString());
auto sessionDir = userDataDir / "sessions" / sessionName.toStdString();
auto storeFile = sessionDir / "store";
auto metadataFile = sessionDir / "metadata";
if (! std::filesystem::exists(storeFile)) {
qDebug() << "storeFile does not exist, skip loading session " << sessionName;
return false;
}
if (std::filesystem::exists(metadataFile)) {
KConfig metadata(QString::fromStdString(metadataFile.native()));
KConfigGroup mdGroup(&metadata, "Metadata");
auto format = mdGroup.readEntry("archiveFormat");
if (format != QStringLiteral("text")) {
qDebug() << "Unknown archive format:" << format;
return false;
}
auto version = mdGroup.readEntry("kazvVersion");
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";
return false;
}
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";
return false;
}
using IAr = boost::archive::text_iarchive;
auto archive = IAr{storeStream};
archive >> model;
qDebug() << "Finished loading session";
} catch (const std::exception &e) {
qDebug() << "Error when loading session:" << QString::fromStdString(e.what());
return false;
}
emplace(std::move(model));
return true;
}
bool MatrixSdk::startNewSession()
{
emplace(std::nullopt);
return true;
}
diff --git a/src/matrix-sdk.hpp b/src/matrix-sdk.hpp
index 21e495f..95602e8 100644
--- a/src/matrix-sdk.hpp
+++ b/src/matrix-sdk.hpp
@@ -1,112 +1,113 @@
/*
* Copyright (C) 2020-2021 Tusooa Zhu <tusooa@kazv.moe>
*
* This file is part of kazv.
*
* kazv is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* kazv is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with kazv. If not, see <https://www.gnu.org/licenses/>.
*/
#pragma once
#include <libkazv-config.hpp>
#include <immer/config.hpp> // https://github.com/arximboldi/immer/issues/168
#include <QObject>
#include <QQmlEngine>
#include <QString>
-#include <QScopedPointer>
+
+#include <memory>
#include <lager/extra/qt.hpp>
#include <sdk-model.hpp>
#include "meta-types.hpp"
class MatrixRoomList;
struct MatrixSdkPrivate;
class MatrixSdk : public QObject
{
Q_OBJECT
QML_ELEMENT
- QScopedPointer<MatrixSdkPrivate> m_d;
+ std::unique_ptr<MatrixSdkPrivate> m_d;
/// @param d A dynamically allocated d-pointer, whose ownership
/// will be transferred to this MatrixSdk.
- explicit MatrixSdk(MatrixSdkPrivate *d, QObject *parent);
+ explicit MatrixSdk(std::unique_ptr<MatrixSdkPrivate> d, QObject *parent);
void init();
public:
explicit MatrixSdk(QObject *parent = 0);
explicit MatrixSdk(Kazv::SdkModel model, QObject *parent = 0);
~MatrixSdk() override;
LAGER_QT_READER(QString, userId);
LAGER_QT_READER(QString, token);
LAGER_QT_READER(QString, deviceId);
Q_INVOKABLE MatrixRoomList *roomList() const;
Q_INVOKABLE QString mxcUriToHttp(QString mxcUri) const;
private:
/// Replaces the store with another one
void emplace(std::optional<Kazv::SdkModel> model);
Q_SIGNALS:
void trigger(Kazv::KazvEvent e);
void loginSuccessful(Kazv::KazvEvent e);
void loginFailed(Kazv::KazvEvent e);
void sessionChanged();
public Q_SLOTS:
void login(const QString &userId, const QString &password);
/**
* Serialize data to <AppDataDir>/sessions/<userid>/<deviceid>/
*
* If not logged in, do nothing.
*/
void serializeToFile() const;
/**
* Load session at <AppDataDir>/sessions/<sessionName> .
*
* @param sessionName A string in the form of <userid>/<deviceid> .
*
* @return true if successful, false otherwise.
*/
bool loadSession(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;
};

File Metadata

Mime Type
text/x-diff
Expires
Sun, Jan 19, 3:10 PM (19 h, 42 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
55250
Default Alt Text
(18 KB)

Event Timeline