Page MenuHomePhorge

No OneTemporary

Size
21 KB
Referenced Files
None
Subscribers
None
diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index 1ddbc60..5e5f612 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -1,146 +1,156 @@
# This file is part of libkazv.
# SPDX-FileCopyrightText: 2022-2023 tusooa <tusooa@kazv.moe>
# SPDX-License-Identifier: AGPL-3.0-or-later
workflow:
rules:
- if: $CI_PIPELINE_SOURCE == "merge_request_event"
- if: $CI_COMMIT_BRANCH && $CI_OPEN_MERGE_REQUESTS
when: never
- if: $CI_COMMIT_BRANCH
stages:
- lint
- prepare
- build
- check-changelog
- doc
- pages
lint:
stage: lint
script: |
echo 'Verify that no source file contains tabs'
! egrep -R --exclude-dir=resources $'\t' src
rules: &build-rules
- changes: &src-chg
- .gitlab-ci.yml
- Dockerfile
- '**/*.cpp'
- '**/*.hpp'
- '**/CMakeLists.txt'
- '**/*.in'
- 'src/tests/resources/**'
'prepare:env':
stage: prepare
script: |
tag="${CI_COMMIT_BRANCH:-$CI_COMMIT_TAG}"
tag="$(echo "$tag" | sed -e "s/[^a-zA-Z0-9.]/-/g")"
echo "tag=$tag" >> prepare.env
artifacts:
reports:
dotenv: prepare.env
rules: *build-rules
-'build:debug':
+'.build':
stage: build
image:
name: gcr.io/kaniko-project/executor:debug
entrypoint: [""]
script: |
mkdir -p /kaniko/.docker
echo "{\"auths\":{\"$CI_REGISTRY\":{\"username\":\"$CI_REGISTRY_USER\",\"password\":\"$CI_REGISTRY_PASSWORD\"}}}" > /kaniko/.docker/config.json
if [ $CI_PIPELINE_SOURCE == "merge_request_event" ]; then
- /kaniko/executor --context $CI_PROJECT_DIR --dockerfile $CI_PROJECT_DIR/Dockerfile --build-arg JOBS=1 --no-push
+ /kaniko/executor --context $CI_PROJECT_DIR --dockerfile "$DOCKERFILE" --build-arg JOBS=1 $BUILD_ARGS --no-push
else
- /kaniko/executor --context $CI_PROJECT_DIR --dockerfile $CI_PROJECT_DIR/Dockerfile --build-arg JOBS=1 --destination $CI_REGISTRY_IMAGE/libkazv:$tag --destination $CI_REGISTRY_IMAGE/libkazv:$tag-debug
+ /kaniko/executor --context $CI_PROJECT_DIR --dockerfile "$DOCKERFILE" --build-arg JOBS=1 $BUILD_ARGS $DESTINATIONS
fi
dependencies: &build-deps
- prepare:env
rules: *build-rules
-'build:production':
- stage: build
- image:
- name: gcr.io/kaniko-project/executor:debug
- entrypoint: [""]
- script: |
- mkdir -p /kaniko/.docker
- echo "{\"auths\":{\"$CI_REGISTRY\":{\"username\":\"$CI_REGISTRY_USER\",\"password\":\"$CI_REGISTRY_PASSWORD\"}}}" > /kaniko/.docker/config.json
- if [ $CI_PIPELINE_SOURCE == "merge_request_event" ]; then
- /kaniko/executor --context $CI_PROJECT_DIR --dockerfile $CI_PROJECT_DIR/Dockerfile --build-arg JOBS=1 --build-arg BUILD_TYPE=Release --no-push
- else
- /kaniko/executor --context $CI_PROJECT_DIR --dockerfile $CI_PROJECT_DIR/Dockerfile --build-arg JOBS=1 --build-arg BUILD_TYPE=Release --destination $CI_REGISTRY_IMAGE/libkazv:$tag-prod
- fi
- dependencies: *build-deps
- rules: *build-rules
+'build:debug:gnu/linux':
+ extends: '.build'
+ before_script:
+ - 'DOCKERFILE="$CI_PROJECT_DIR/Dockerfile"'
+ - 'DESTINATIONS="--destination $CI_REGISTRY_IMAGE/libkazv:$tag --destination $CI_REGISTRY_IMAGE/libkazv:$tag-debug"'
+
+'build:production:gnu/linux':
+ extends: '.build'
+ before_script:
+ - 'DOCKERFILE="$CI_PROJECT_DIR/Dockerfile"'
+ - 'DESTINATIONS="--destination $CI_REGISTRY_IMAGE/libkazv:$tag-prod"'
+ - 'BUILD_ARGS="--build-arg BUILD_TYPE=Release"'
+
+'build:debug:windows':
+ extends: '.build'
+ before_script:
+ - 'DOCKERFILE="$CI_PROJECT_DIR/Dockerfile.windows"'
+ - 'DESTINATIONS="--destination $CI_REGISTRY_IMAGE/libkazv:$tag-windows --destination $CI_REGISTRY_IMAGE/libkazv:$tag-windows-debug"'
+
+'build:production:windows':
+ extends: '.build'
+ before_script:
+ - 'DOCKERFILE="$CI_PROJECT_DIR/Dockerfile.windows"'
+ - 'DESTINATIONS="--destination $CI_REGISTRY_IMAGE/libkazv:$tag-windows-prod"'
+ - 'BUILD_ARGS="--build-arg BUILD_TYPE=Release"'
'build:coverage':
stage: build
image:
name: reg.lily.kazv.moe/kazv/libkazv-deps/deps:servant
entrypoint: [""]
script: |
export BUILD_TYPE=Debug
export JOBS=1
export DEPS_INSTALL_DIR=/opt/libkazv-deps
export LIBKAZV_INSTALL_DIR=/opt/libkazv
mkdir build && cd build && \
export CC=gcc-9 CXX=g++-9 && \
cmake .. -DCMAKE_INSTALL_PREFIX="$LIBKAZV_INSTALL_DIR" -DCMAKE_PREFIX_PATH="$DEPS_INSTALL_DIR" -DCMAKE_BUILD_TYPE=$BUILD_TYPE -Dlibkazv_BUILD_TESTS=ON \
-Dlibkazv_BUILD_EXAMPLES=ON -Dlibkazv_BUILD_KAZVJOB=ON -Dlibkazv_ENABLE_COVERAGE=ON -DCMAKE_CXX_FLAGS=-fsanitize=address && \
make -j$JOBS && \
make CTEST_OUTPUT_ON_FAILURE=1 test && \
gcovr --xml-pretty --exclude-unreachable-branches --print-summary -o coverage.xml -r "${CI_PROJECT_DIR}" --object-directory src -e '.*/api/.*'
coverage: /^\s*lines:\s*\d+.\d+\%/
artifacts:
name: ${CI_JOB_NAME}-${CI_COMMIT_REF_NAME}-${CI_COMMIT_SHA}
paths:
- build/coverage.xml
reports:
coverage_report:
coverage_format: cobertura
path: build/coverage.xml
rules: *build-rules
'doc:build':
stage: doc
image: reg.lily.kazv.moe/kazv/libkazv-deps/deps:servant
dependencies: []
script:
- doxygen Doxyfile
artifacts:
paths:
- doc/html/
rules:
- changes: *src-chg
- changes: &doc-chg
- '**/*.md'
- .gitlab-ci.yml
- public/**/*
pages:
stage: pages
image: alpine
dependencies:
- 'doc:build'
script:
- mv -v doc/html public/api
artifacts:
paths:
- public
rules:
- if: $CI_COMMIT_BRANCH != "servant"
when: never
- changes: *src-chg
- changes: *doc-chg
'check-changelog':
stage: check-changelog
image: alpine
dependencies: []
script:
- sh ./tools/check-changelog
rules:
- if: $CI_MERGE_REQUEST_TARGET_BRANCH_NAME == "servant"
diff --git a/src/client/client.cpp b/src/client/client.cpp
index 1ccc031..debb086 100644
--- a/src/client/client.cpp
+++ b/src/client/client.cpp
@@ -1,434 +1,434 @@
/*
* This file is part of libkazv.
* SPDX-FileCopyrightText: 2020-2023 tusooa <tusooa@kazv.moe>
* SPDX-License-Identifier: AGPL-3.0-or-later
*/
#include <libkazv-config.hpp>
#include <filesystem>
#include <lager/constant.hpp>
#include "client.hpp"
namespace Kazv
{
Client::Client(lager::reader<SdkModel> sdk,
ContextT ctx, std::nullopt_t)
: m_sdk(sdk)
, m_client(sdk.map(&SdkModel::c))
, m_ctx(std::move(ctx))
{
}
Client::Client(lager::reader<SdkModel> sdk,
ContextWithDepsT ctx)
: m_sdk(sdk)
, m_client(sdk.map(&SdkModel::c))
, m_ctx(ctx)
, m_deps(std::move(ctx))
{
}
Client::Client(InEventLoopTag,
ContextWithDepsT ctx)
: m_sdk(std::nullopt)
, m_client(std::nullopt)
, m_ctx(ctx)
, m_deps(std::move(ctx))
#ifdef KAZV_USE_THREAD_SAFETY_HELPER
, KAZV_ON_EVENT_LOOP_VAR(true)
#endif
{
}
Client::Client(InEventLoopTag,
ContextT ctx, DepsT deps)
: m_sdk(std::nullopt)
, m_client(std::nullopt)
, m_ctx(std::move(ctx))
, m_deps(std::move(deps))
#ifdef KAZV_USE_THREAD_SAFETY_HELPER
, KAZV_ON_EVENT_LOOP_VAR(true)
#endif
{
}
Client Client::toEventLoop() const
{
return Client(InEventLoopTag{}, m_ctx, m_deps.value());
}
Room Client::room(std::string id) const
{
if (m_deps.has_value()) {
return Room(sdkCursor(), lager::make_constant(id), m_ctx, m_deps.value());
} else {
return Room(sdkCursor(), lager::make_constant(id), m_ctx);
}
}
Room Client::roomByCursor(lager::reader<std::string> id) const
{
if (m_deps.has_value()) {
return Room(sdkCursor(), id, m_ctx, m_deps.value());
} else {
return Room(sdkCursor(), id, m_ctx);
}
}
auto Client::passwordLogin(std::string homeserver, std::string username,
std::string password, std::string deviceName) const
-> PromiseT
{
auto p1 = m_ctx.dispatch(LoginAction{
homeserver, username, password, deviceName});
p1
.then([that=toEventLoop()](auto stat) {
if (! stat.success()) {
return;
}
// It is meaningless to wait for it in a Promise
// that is never exposed to the user.
that.startSyncing();
});
return p1;
}
auto Client::tokenLogin(std::string homeserver, std::string username,
std::string token, std::string deviceId) const
-> PromiseT
{
auto p1 = m_ctx.dispatch(TokenLoginAction{
homeserver, username, token, deviceId});
p1
.then([that=toEventLoop()](auto stat) {
if (! stat.success()) {
return;
}
that.startSyncing();
});
return p1;
}
auto Client::autoDiscover(std::string userId) const
-> PromiseT
{
return m_ctx.dispatch(GetWellknownAction{userId})
.then([that=toEventLoop()](auto stat) {
if (!stat.success()) {
return that.m_ctx.createResolvedPromise(stat);
}
return that.m_ctx.dispatch(GetVersionsAction{stat.dataStr("homeserverUrl")})
.then([that, stat](auto stat2) {
if (!stat2.success()) {
return stat2;
} else {
return stat;
}
});
});
}
auto Client::createRoom(RoomVisibility v,
std::optional<std::string> name,
std::optional<std::string> alias,
immer::array<std::string> invite,
std::optional<bool> isDirect,
bool allowFederate,
std::optional<std::string> topic,
JsonWrap powerLevelContentOverride) const
-> PromiseT
{
CreateRoomAction a;
a.visibility = v;
a.name = name;
a.roomAliasName = alias;
a.invite = invite;
a.isDirect = isDirect;
a.topic = topic;
a.powerLevelContentOverride = powerLevelContentOverride;
// Synapse won't buy it if we do not provide
// a creationContent object.
a.creationContent = json{
{"m.federate", allowFederate}
};
return m_ctx.dispatch(std::move(a));
}
auto Client::joinRoomById(std::string roomId) const -> PromiseT
{
return m_ctx.dispatch(JoinRoomByIdAction{roomId});
}
auto Client::joinRoom(std::string roomId, immer::array<std::string> serverName) const
-> PromiseT
{
return m_ctx.dispatch(JoinRoomAction{roomId, serverName});
}
auto Client::uploadContent(immer::box<Bytes> content,
std::string uploadId,
std::optional<std::string> filename,
std::optional<std::string> contentType) const
-> PromiseT
{
return m_ctx.dispatch(UploadContentAction{
FileDesc(FileContent{content.get().begin(), content.get().end()}),
filename, contentType, uploadId});
}
auto Client::uploadContent(FileDesc file) const
-> PromiseT
{
auto basename = file.name()
- ? std::optional(std::filesystem::path(file.name().value()).filename().native())
+ ? std::optional(std::filesystem::path(file.name().value()).filename().string())
: std::nullopt;
return m_ctx.dispatch(UploadContentAction{
file,
// use only basename to prevent path info being leaked
basename,
file.contentType(),
// uploadId unused
std::string{}});
}
auto Client::downloadContent(std::string mxcUri, std::optional<FileDesc> downloadTo) const
-> PromiseT
{
return m_ctx.dispatch(DownloadContentAction{mxcUri, downloadTo});
}
auto Client::downloadThumbnail(
std::string mxcUri,
int width,
int height,
std::optional<ThumbnailResizingMethod> method,
std::optional<FileDesc> downloadTo) const
-> PromiseT
{
return m_ctx.dispatch(DownloadThumbnailAction{mxcUri, width, height, method, std::nullopt, downloadTo});
}
auto Client::startSyncing() const -> PromiseT
{
KAZV_VERIFY_THREAD_ID();
using namespace Kazv::CursorOp;
if (+syncing()) {
return m_ctx.createResolvedPromise(true);
}
auto p1 = m_ctx.createResolvedPromise(true)
.then([that=toEventLoop()](auto) {
// post filters, if filters are incomplete
if ((+that.clientCursor()[&ClientModel::initialSyncFilterId]).empty()
|| (+that.clientCursor()[&ClientModel::incrementalSyncFilterId]).empty()) {
return that.m_ctx.dispatch(PostInitialFiltersAction{});
}
return that.m_ctx.createResolvedPromise(true);
})
.then([that=toEventLoop()](auto stat) {
if (! stat.success()) {
return that.m_ctx.createResolvedPromise(stat);
}
// Upload identity keys if we need to
if (+that.clientCursor()[&ClientModel::crypto]
&& ! +that.clientCursor()[&ClientModel::identityKeysUploaded]) {
return that.m_ctx.dispatch(UploadIdentityKeysAction{});
} else {
return that.m_ctx.createResolvedPromise(true);
}
});
p1
.then([m_ctx=m_ctx](auto stat) {
m_ctx.dispatch(SetShouldSyncAction{true});
return stat;
})
.then([that=toEventLoop()](auto stat) {
if (stat.success()) {
that.syncForever();
}
});
return p1;
}
auto Client::syncForever(std::optional<int> retryTime) const -> void
{
KAZV_VERIFY_THREAD_ID();
// assert (m_deps);
using namespace CursorOp;
bool isInitialSync = ! (+clientCursor()[&ClientModel::syncToken]).has_value();
bool shouldSync = +clientCursor()[&ClientModel::shouldSync];
if (! shouldSync) {
return;
}
//
auto syncRes = m_ctx.dispatch(SyncAction{});
auto uploadOneTimeKeysRes = syncRes
.then([that=toEventLoop()](auto stat) {
if (! stat.success()) {
return that.m_ctx.createResolvedPromise(stat);
}
auto &rg = lager::get<RandomInterface &>(that.m_deps.value());
bool hasCrypto{+that.clientCursor()[&ClientModel::crypto]};
if (! hasCrypto) {
return that.m_ctx.createResolvedPromise(true);
}
auto numKeysToGenerate = (+that.clientCursor()).numOneTimeKeysNeeded();
return that.m_ctx.dispatch(GenerateAndUploadOneTimeKeysAction{
numKeysToGenerate,
rg.generateRange<RandomData>(GenerateAndUploadOneTimeKeysAction::randomSize(numKeysToGenerate))
});
});
auto queryKeysRes = syncRes
.then([that=toEventLoop(), isInitialSync](auto stat) {
if (! stat.success()) {
return that.m_ctx.createResolvedPromise(stat);
}
bool hasCrypto{+that.clientCursor()[&ClientModel::crypto]};
return hasCrypto
? that.m_ctx.dispatch(QueryKeysAction{isInitialSync})
: that.m_ctx.createResolvedPromise(true);
});
m_ctx.promiseInterface()
.all(std::vector<PromiseT>{uploadOneTimeKeysRes, queryKeysRes})
.then([that=toEventLoop(), retryTime](auto stat) {
if (stat.success()) {
that.syncForever(); // reset retry time
} else {
auto firstRetryTime = +that.clientCursor()[&ClientModel::firstRetryMs];
auto retryTimeFactor = +that.clientCursor()[&ClientModel::retryTimeFactor];
auto maxRetryTime = +that.clientCursor()[&ClientModel::maxRetryMs];
auto curRetryTime = retryTime ? retryTime.value() : firstRetryTime;
if (curRetryTime > maxRetryTime) { curRetryTime = maxRetryTime; }
auto nextRetryTime = curRetryTime * retryTimeFactor;
kzo.client.warn() << "Sync failed, retrying in " << curRetryTime << "ms" << std::endl;
auto &jh = getJobHandler(that.m_deps.value());
jh.setTimeout([that=that.toEventLoop(), nextRetryTime]() { that.syncForever(nextRetryTime); },
curRetryTime);
}
});
}
void Client::stopSyncing() const
{
m_ctx.dispatch(SetShouldSyncAction{false});
}
lager::reader<ClientModel> Client::clientCursor() const
{
KAZV_VERIFY_THREAD_ID();
if (m_client.has_value()) {
return m_client.value();
} else {
assert(m_deps.has_value());
return lager::get<SdkModelCursorKey>(m_deps.value())->map(&SdkModel::c);
}
}
const lager::reader<SdkModel> &Client::sdkCursor() const
{
KAZV_VERIFY_THREAD_ID();
if (m_sdk.has_value()) {
return m_sdk.value();
} else {
assert(m_deps.has_value());
return *(lager::get<SdkModelCursorKey>(m_deps.value()));
}
}
auto Client::getProfile(std::string userId) const -> PromiseT
{
return m_ctx.dispatch(GetUserProfileAction{userId});
}
auto Client::setAvatarUrl(std::optional<std::string> avatarUrl) const -> PromiseT
{
return m_ctx.dispatch(SetAvatarUrlAction{avatarUrl});
}
auto Client::setDisplayName(std::optional<std::string> displayName) const -> PromiseT
{
return m_ctx.dispatch(SetDisplayNameAction{displayName});
}
auto Client::devicesOfUser(std::string userId) const -> lager::reader<immer::flex_vector<DeviceKeyInfo>>
{
return clientCursor()
[&ClientModel::deviceLists]
[&DeviceListTracker::deviceLists]
[userId]
[lager::lenses::or_default]
.xform(containerMap(immer::flex_vector<DeviceKeyInfo>{}, zug::map([](const auto &pair) {
const auto &[deviceId, info] = pair;
(void)deviceId;
return info;
})));
}
auto Client::setDeviceTrustLevel(std::string userId, std::string deviceId, DeviceTrustLevel trustLevel) const -> PromiseT
{
return m_ctx.dispatch(SetDeviceTrustLevelAction{userId, deviceId, trustLevel});
}
auto Client::trustLevelNeededToSendKeys() const -> lager::reader<DeviceTrustLevel>
{
return clientCursor()[&ClientModel::trustLevelNeededToSendKeys];
}
auto Client::setTrustLevelNeededToSendKeys(DeviceTrustLevel trustLevel) const -> PromiseT
{
return m_ctx.dispatch(SetTrustLevelNeededToSendKeysAction{trustLevel});
}
auto Client::directRoomMap() const -> lager::reader<immer::map<std::string, std::string>>
{
return clientCursor().map(&ClientModel::directRoomMap);
}
auto Client::roomIdsUnderTag(std::string tagId) const -> lager::reader<immer::map<std::string, double>>
{
return clientCursor().map([tagId](const auto &c) {
return c.roomIdsUnderTag(tagId);
});
}
auto Client::roomIdsByTagId() const -> lager::reader<immer::map<std::string, immer::map<std::string, double>>>
{
return clientCursor().map(&ClientModel::roomIdsByTagId);
}
auto Client::accountData() const -> lager::reader<immer::map<std::string, Event>>
{
return clientCursor()[&ClientModel::accountData];
}
auto Client::setAccountData(Event accountDataEvent) const -> PromiseT
{
return m_ctx.dispatch(SetAccountDataAction{accountDataEvent});
}
NotificationHandler Client::notificationHandler() const
{
return NotificationHandler(clientCursor());
}
}

File Metadata

Mime Type
text/x-diff
Expires
Sun, Aug 9, 2:14 PM (1 d, 10 h)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
1724657
Default Alt Text
(21 KB)

Event Timeline