Page MenuHomePhorge

matrix-sdk.cpp
No OneTemporary

Size
10 KB
Referenced Files
None
Subscribers
None

matrix-sdk.cpp

/*
* 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 <QMutex>
#include <QMutexLocker>
#include <QtConcurrent>
#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 "matrix-sdk.hpp"
#include "matrix-room-list.hpp"
#include "helper.hpp"
#include "kazv-path-config.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);
boost::asio::io_context io;
CprJobHandler jobHandler;
LagerStoreEventEmitter ee;
LagerStoreEventEmitter::Watchable watchable;
SdkT sdk;
QMutex ioContextLock;
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";
}
}
MatrixSdkPrivate::MatrixSdkPrivate()
: 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))
{
}
MatrixSdkPrivate::MatrixSdkPrivate(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))
{
}
MatrixSdk::MatrixSdk(MatrixSdkPrivate *d, QObject *parent)
: QObject(parent)
, m_d(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());
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);
});
}
MatrixSdk::MatrixSdk(QObject *parent)
: MatrixSdk(new MatrixSdkPrivate, parent)
{
}
MatrixSdk::MatrixSdk(SdkModel model, QObject *parent)
: MatrixSdk(new MatrixSdkPrivate(std::move(model)), parent)
{
}
static void cleanupDPointer(MatrixSdkPrivate *d)
{
QSharedPointer<MatrixSdkPrivate> oldD(d);
QtConcurrent::run(
[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->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() << "from here, the old data can be destructed";
});
}
MatrixSdk::~MatrixSdk()
{
if (m_d) {
serializeToFile();
cleanupDPointer(m_d.take());
}
}
QString MatrixSdk::mxcUriToHttp(QString mxcUri) const
{
return QString::fromStdString(m_d->sdk.client().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()
.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());
}
void MatrixSdk::emplace(std::optional<SdkModel> model)
{
if (m_d) {
cleanupDPointer(m_d.take());
}
m_d.reset(model.has_value()
? new MatrixSdkPrivate(std::move(model.value()))
: new MatrixSdkPrivate());
// Re-initialize lager-qt cursors and watchable connections
init();
m_d->runIoContext();
m_d->sdk.client().startSyncing();
Q_EMIT sessionChanged();
}
QStringList MatrixSdk::allSessions() const
{
using StdPath = std::filesystem::path;
auto userDataDir = StdPath(kazvUserDataDir().toStdString());
auto allSessionsDir = userDataDir / "sessions";
QStringList sessionNames;
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));
}
}
}
}
return sessionNames;
}
void MatrixSdk::serializeToFile() const
{
serializeClientToFile(m_d->sdk.client());
}
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;
}
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;
}

File Metadata

Mime Type
text/x-c
Expires
Thu, Oct 2, 2:58 AM (14 h, 36 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
487024
Default Alt Text
matrix-sdk.cpp (10 KB)

Event Timeline