Starting attempt to implement the new rust time transfer server
This commit is contained in:
parent
45229e1e8e
commit
f7039187b2
3
.gitmodules
vendored
3
.gitmodules
vendored
@ -11,3 +11,6 @@
|
||||
[submodule "rtclib"]
|
||||
path = rtclib
|
||||
url = https://git.did.science/TeaSpeak/Server/rtc.git
|
||||
[submodule "file-rust"]
|
||||
path = file-rust
|
||||
url = https://git.did.science/TeaSpeak/Server/teafile.git
|
||||
|
1
file-rust
Submodule
1
file-rust
Submodule
@ -0,0 +1 @@
|
||||
Subproject commit 29f95794358201811692f7e7b60f78fe9605f2a1
|
@ -17,6 +17,14 @@ add_library(TeaSpeak-FileServer STATIC
|
||||
local_server/HTTPUtils.cpp
|
||||
)
|
||||
|
||||
add_library(TeaSpeak-FileRust STATIC
|
||||
local_rust/provider.cpp
|
||||
local_rust/lib.cpp
|
||||
)
|
||||
target_include_directories(TeaSpeak-FileRust PUBLIC include/)
|
||||
target_link_libraries(TeaSpeak-FileRust PUBLIC TeaSpeak ${CMAKE_SOURCE_DIR}/file-rust/target/debug/libteaspeak_file_capi.so)
|
||||
# target_link_directories(TeaSpeak-FileRust PUBLIC ${CMAKE_SOURCE_DIR}/file-rust/target/debug/)
|
||||
|
||||
target_link_libraries(TeaSpeak-FileServer PUBLIC TeaSpeak ${StringVariable_LIBRARIES_STATIC} stdc++fs
|
||||
libevent::core libevent::pthreads
|
||||
# We're not linking this here, since we may later use DataPipes::shared linking
|
||||
|
@ -2,13 +2,12 @@
|
||||
|
||||
#include <string>
|
||||
#include <chrono>
|
||||
#include <Definitions.h>
|
||||
#include <condition_variable>
|
||||
#include <utility>
|
||||
#include <variant>
|
||||
#include <deque>
|
||||
#include <functional>
|
||||
#include <atomic>
|
||||
#include <condition_variable>
|
||||
|
||||
#include "./ExecuteResponse.h"
|
||||
|
||||
@ -18,6 +17,10 @@
|
||||
namespace ts::server::file {
|
||||
class VirtualFileServer;
|
||||
|
||||
typedef uint64_t ChannelId;
|
||||
typedef uint64_t ServerId;
|
||||
typedef uint64_t ClientId;
|
||||
|
||||
namespace filesystem {
|
||||
template <typename ErrorCodes>
|
||||
struct DetailedError {
|
||||
@ -102,6 +105,7 @@ namespace ts::server::file {
|
||||
|
||||
DeleteResult(StatusType status, std::string errorDetail) : status{status},
|
||||
error_detail{std::move(errorDetail)} {}
|
||||
DeleteResult() = default;
|
||||
};
|
||||
|
||||
std::vector<DeleteResult> delete_results{};
|
||||
@ -133,6 +137,7 @@ namespace ts::server::file {
|
||||
|
||||
FileInfo(StatusType status, std::string errorDetail, DirectoryEntry info) : status{status},
|
||||
error_detail{std::move(errorDetail)}, info{std::move(info)} {}
|
||||
FileInfo() = default;
|
||||
};
|
||||
|
||||
std::vector<FileInfo> file_info{};
|
||||
@ -149,7 +154,6 @@ namespace ts::server::file {
|
||||
|
||||
/* server */
|
||||
[[nodiscard]] virtual std::shared_ptr<ExecuteResponse<ServerCommandError>> initialize_server(const std::shared_ptr<VirtualFileServer> &/* server */) = 0;
|
||||
[[nodiscard]] virtual std::shared_ptr<ExecuteResponse<ServerCommandError>> delete_server(const std::shared_ptr<VirtualFileServer> &/* server */) = 0;
|
||||
|
||||
/* channels */
|
||||
[[nodiscard]] virtual std::shared_ptr<ExecuteResponse<FileInfoError, FileInfoResponse>> query_channel_info(const std::shared_ptr<VirtualFileServer> &/* server */, const std::vector<std::tuple<ChannelId, std::string>>& /* files */) = 0;
|
||||
@ -184,7 +188,7 @@ namespace ts::server::file {
|
||||
ClientId client_id{0};
|
||||
std::string client_unique_id{};
|
||||
|
||||
char transfer_key[TRANSFER_KEY_LENGTH]{};
|
||||
std::string transfer_key{};
|
||||
std::chrono::system_clock::time_point initialized_timestamp{};
|
||||
enum Direction {
|
||||
DIRECTION_UNKNOWN,
|
||||
@ -407,7 +411,7 @@ namespace ts::server::file {
|
||||
}
|
||||
|
||||
virtual std::shared_ptr<VirtualFileServer> register_server(ServerId /* server id */) = 0;
|
||||
virtual void unregister_server(ServerId /* server id */) = 0;
|
||||
virtual void unregister_server(ServerId /* server id */, bool /* delete tiles */) = 0;
|
||||
protected:
|
||||
mutable std::mutex servers_mutex{};
|
||||
std::deque<std::shared_ptr<VirtualFileServer>> servers_{};
|
||||
|
54
file/local_rust/imports.h
Normal file
54
file/local_rust/imports.h
Normal file
@ -0,0 +1,54 @@
|
||||
//
|
||||
// Created by WolverinDEV on 20/04/2021.
|
||||
//
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
#include <cstddef>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/* Attention: Do not call any libteaspeak_file_* functions within being in the callback! */
|
||||
struct TeaFileNativeCallbacks {
|
||||
uint32_t version;
|
||||
|
||||
void(*log)(uint8_t /* level */, const void* /* callback data */, const char* /* message */, uint32_t /* length */);
|
||||
};
|
||||
|
||||
struct TeaFilePath {
|
||||
uint32_t type;
|
||||
uint64_t channel_id;
|
||||
const char* path;
|
||||
};
|
||||
|
||||
struct TeaFilePathInfo {
|
||||
int32_t query_result;
|
||||
uint32_t file_type;
|
||||
const char* name;
|
||||
uint64_t modify_timestamp;
|
||||
uint64_t file_size;
|
||||
bool directory_empty;
|
||||
};
|
||||
|
||||
extern const char* libteaspeak_file_version();
|
||||
extern void libteaspeak_free_str(const char* /* ptr */);
|
||||
|
||||
extern const char* libteaspeak_file_initialize(const TeaFileNativeCallbacks* /* */, size_t /* size of the callback struct */);
|
||||
extern void libteaspeak_file_finalize();
|
||||
|
||||
extern void libteaspeak_file_system_register_server(const char* /* server unique id */);
|
||||
extern void libteaspeak_file_system_unregister_server(const char* /* server unique id */, bool /* delete files */);
|
||||
|
||||
extern void libteaspeak_file_free_file_info(const TeaFilePathInfo*);
|
||||
extern const char* libteaspeak_file_system_query_file_info(const char* /* server unique id */, const TeaFilePath* /* file path */, size_t /* path count */, const TeaFilePathInfo ** /* result */);
|
||||
extern const char* libteaspeak_file_system_query_directory(const char* /* server unique id */, const TeaFilePath* /* file path */, const TeaFilePathInfo ** /* result */);
|
||||
extern const char* libteaspeak_file_system_delete_files(const char* /* server unique id */, const TeaFilePath* /* file path */, size_t /* path count */, const TeaFilePathInfo ** /* result */);
|
||||
extern uint32_t libteaspeak_file_system_create_channel_directory(const char* /* server unique id */, uint32_t /* channel id */, const char* /* path */);
|
||||
extern uint32_t libteaspeak_file_system_rename_channel_file(const char* /* server unique id */, uint32_t /* old channel id */, const char* /* old path */, uint32_t /* new channel id */, const char* /* new path */);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
6
file/local_rust/lib.cpp
Normal file
6
file/local_rust/lib.cpp
Normal file
@ -0,0 +1,6 @@
|
||||
//
|
||||
// Created by WolverinDEV on 20/04/2021.
|
||||
//
|
||||
|
||||
#include "./lib.h"
|
||||
#include "./imports.h"
|
5
file/local_rust/lib.h
Normal file
5
file/local_rust/lib.h
Normal file
@ -0,0 +1,5 @@
|
||||
//
|
||||
// Created by WolverinDEV on 20/04/2021.
|
||||
//
|
||||
|
||||
#pragma once
|
587
file/local_rust/provider.cpp
Normal file
587
file/local_rust/provider.cpp
Normal file
@ -0,0 +1,587 @@
|
||||
//
|
||||
// Created by WolverinDEV on 20/04/2021.
|
||||
//
|
||||
|
||||
#include <cassert>
|
||||
#include <log/LogUtils.h>
|
||||
#include "./provider.h"
|
||||
#include "./imports.h"
|
||||
|
||||
using namespace ts::server;
|
||||
using namespace ts::server::file;
|
||||
using namespace ts::server::file::transfer;
|
||||
using namespace ts::server::file::filesystem;
|
||||
|
||||
RustFileSystem::~RustFileSystem() = default;
|
||||
|
||||
std::shared_ptr<ExecuteResponse<ServerCommandError>> RustFileSystem::initialize_server(
|
||||
const std::shared_ptr<VirtualFileServer> &) {
|
||||
/* Nothing to do. */
|
||||
auto response = this->create_execute_response<ServerCommandError>();
|
||||
response->emplace_success();
|
||||
return response;
|
||||
}
|
||||
|
||||
std::shared_ptr<ExecuteResponse<FileInfoError, FileInfoResponse>> RustFileSystem::query_channel_info(
|
||||
const std::shared_ptr<VirtualFileServer> &virtual_server, const std::vector<std::tuple<ChannelId, std::string>> &files) {
|
||||
std::vector<TeaFilePath> file_paths{};
|
||||
file_paths.reserve(files.size());
|
||||
for(const auto& [ channel_id, path ] : files) {
|
||||
file_paths.push_back(TeaFilePath{
|
||||
.type = 0,
|
||||
.channel_id = channel_id,
|
||||
.path = path.c_str()
|
||||
});
|
||||
}
|
||||
|
||||
return this->execute_info(virtual_server->unique_id().c_str(), file_paths.data(), file_paths.size());
|
||||
}
|
||||
|
||||
std::shared_ptr<RustFileSystem::directory_query_response_t> RustFileSystem::query_channel_directory(
|
||||
const std::shared_ptr<VirtualFileServer> &virtual_server, ChannelId channelId, const std::string &directory) {
|
||||
TeaFilePath path{
|
||||
.type = 0,
|
||||
.channel_id = channelId,
|
||||
.path = directory.c_str()
|
||||
};
|
||||
return this->execute_query(virtual_server->unique_id().c_str(), &path);
|
||||
}
|
||||
|
||||
std::shared_ptr<ExecuteResponse<FileInfoError, FileInfoResponse>> RustFileSystem::query_icon_info(
|
||||
const std::shared_ptr<VirtualFileServer> &virtual_server, const std::vector<std::string> &files) {
|
||||
std::vector<TeaFilePath> file_paths{};
|
||||
file_paths.reserve(files.size());
|
||||
for(const auto& path : files) {
|
||||
file_paths.push_back(TeaFilePath{
|
||||
.type = 1,
|
||||
.channel_id = 0,
|
||||
.path = path.c_str()
|
||||
});
|
||||
}
|
||||
|
||||
return this->execute_info(virtual_server->unique_id().c_str(), file_paths.data(), file_paths.size());
|
||||
}
|
||||
|
||||
std::shared_ptr<RustFileSystem::directory_query_response_t> RustFileSystem::query_icon_directory(
|
||||
const std::shared_ptr<VirtualFileServer> &virtual_server) {
|
||||
TeaFilePath path{
|
||||
.type = 1,
|
||||
.channel_id = 0,
|
||||
.path = nullptr
|
||||
};
|
||||
return this->execute_query(virtual_server->unique_id().c_str(), &path);
|
||||
}
|
||||
|
||||
std::shared_ptr<ExecuteResponse<FileInfoError, FileInfoResponse>> RustFileSystem::query_avatar_info(
|
||||
const std::shared_ptr<VirtualFileServer> &virtual_server, const std::vector<std::string> &files) {
|
||||
std::vector<TeaFilePath> file_paths{};
|
||||
file_paths.reserve(files.size());
|
||||
for(const auto& path : files) {
|
||||
file_paths.push_back(TeaFilePath{
|
||||
.type = 2,
|
||||
.channel_id = 0,
|
||||
.path = path.c_str()
|
||||
});
|
||||
}
|
||||
|
||||
return this->execute_info(virtual_server->unique_id().c_str(), file_paths.data(), file_paths.size());
|
||||
}
|
||||
|
||||
std::shared_ptr<RustFileSystem::directory_query_response_t> RustFileSystem::query_avatar_directory(
|
||||
const std::shared_ptr<VirtualFileServer> &virtual_server) {
|
||||
TeaFilePath path{
|
||||
.type = 2,
|
||||
.channel_id = 0,
|
||||
.path = nullptr
|
||||
};
|
||||
return this->execute_query(virtual_server->unique_id().c_str(), &path);
|
||||
}
|
||||
|
||||
inline void path_info_to_directory_entry(DirectoryEntry& target, const TeaFilePathInfo& info) {
|
||||
switch (info.file_type) {
|
||||
case 1:
|
||||
target.type = DirectoryEntry::FILE;
|
||||
break;
|
||||
case 2:
|
||||
target.type = DirectoryEntry::DIRECTORY;
|
||||
break;
|
||||
|
||||
default:
|
||||
target.type = DirectoryEntry::UNKNOWN;
|
||||
return;
|
||||
}
|
||||
|
||||
assert(info.name);
|
||||
target.name = std::string{info.name};
|
||||
target.modified_at = std::chrono::system_clock::time_point{} + std::chrono::seconds{info.modify_timestamp};
|
||||
target.size = info.file_size;
|
||||
target.empty = info.directory_empty;
|
||||
}
|
||||
|
||||
std::shared_ptr<ExecuteResponse<FileInfoError, FileInfoResponse>> RustFileSystem::execute_info(const char *server_unique_id, void *paths,
|
||||
size_t paths_count) {
|
||||
auto response = this->create_execute_response<FileInfoError, FileInfoResponse>();
|
||||
|
||||
const TeaFilePathInfo* result{nullptr};
|
||||
auto error_ptr = libteaspeak_file_system_query_file_info(server_unique_id, (const TeaFilePath*) paths, paths_count, &result);
|
||||
if(error_ptr) {
|
||||
libteaspeak_file_free_file_info(result);
|
||||
|
||||
response->emplace_fail(FileInfoErrorType::UNKNOWN, error_ptr);
|
||||
libteaspeak_free_str(error_ptr);
|
||||
} else {
|
||||
assert(result);
|
||||
std::vector<FileInfoResponse::FileInfo> file_infos{};
|
||||
file_infos.reserve(paths_count);
|
||||
|
||||
auto info_ptr{result};
|
||||
while(info_ptr->query_result >= 0) {
|
||||
auto& info = file_infos.emplace_back();
|
||||
switch (info_ptr->query_result) {
|
||||
case 0:
|
||||
/* success */
|
||||
info.status = FileInfoResponse::StatusType::SUCCESS;
|
||||
path_info_to_directory_entry(info.info, *info_ptr);
|
||||
break;
|
||||
|
||||
case 1:
|
||||
info.status = FileInfoResponse::StatusType::PATH_EXCEEDS_ROOT_PATH;
|
||||
break;
|
||||
|
||||
case 2:
|
||||
info.status = FileInfoResponse::StatusType::PATH_DOES_NOT_EXISTS;
|
||||
break;
|
||||
|
||||
case 3:
|
||||
info.status = FileInfoResponse::StatusType::UNKNOWN_FILE_TYPE;
|
||||
break;
|
||||
|
||||
default:
|
||||
info.status = FileInfoResponse::StatusType::FAILED_TO_QUERY_INFO;
|
||||
info.error_detail = std::string{"invalid query result "} + std::to_string(info_ptr->query_result);
|
||||
break;
|
||||
}
|
||||
|
||||
info_ptr++;
|
||||
}
|
||||
|
||||
assert(file_infos.size() == paths_count);
|
||||
libteaspeak_file_free_file_info(result);
|
||||
response->emplace_success(FileInfoResponse{file_infos});
|
||||
}
|
||||
|
||||
return response;
|
||||
}
|
||||
|
||||
std::shared_ptr<RustFileSystem::directory_query_response_t> RustFileSystem::execute_query(const char *server_unique_id, void *path) {
|
||||
auto response = this->create_execute_response<DirectoryQueryError, std::deque<DirectoryEntry>>();
|
||||
|
||||
const TeaFilePathInfo* result{nullptr};
|
||||
auto error_ptr = libteaspeak_file_system_query_directory(server_unique_id, (const TeaFilePath*) path, &result);
|
||||
if(error_ptr) {
|
||||
libteaspeak_file_free_file_info(result);
|
||||
|
||||
response->emplace_fail(DirectoryQueryErrorType::UNKNOWN, error_ptr);
|
||||
libteaspeak_free_str(error_ptr);
|
||||
} else {
|
||||
assert(result);
|
||||
/* result->query_result could be zero or minus one in both cases we're iterating the query result */
|
||||
if(result->query_result > 0) {
|
||||
/* An error occurred */
|
||||
switch(result->query_result) {
|
||||
case 1:
|
||||
response->emplace_fail(DirectoryQueryErrorType::PATH_EXCEEDS_ROOT_PATH, "");
|
||||
break;
|
||||
|
||||
case 2:
|
||||
response->emplace_fail(DirectoryQueryErrorType::PATH_DOES_NOT_EXISTS, "");
|
||||
break;
|
||||
|
||||
case 4:
|
||||
response->emplace_fail(DirectoryQueryErrorType::PATH_IS_A_FILE, "");
|
||||
break;
|
||||
|
||||
case 5:
|
||||
response->emplace_fail(DirectoryQueryErrorType::UNKNOWN, "io error");
|
||||
break;
|
||||
|
||||
default:
|
||||
response->emplace_fail(DirectoryQueryErrorType::UNKNOWN, std::string{"unknown error result "} + std::to_string(result->query_result));
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
std::deque<DirectoryEntry> entries{};
|
||||
|
||||
auto info_ptr{result};
|
||||
while(info_ptr->query_result >= 0) {
|
||||
assert(info_ptr->query_result == 0);
|
||||
path_info_to_directory_entry(entries.emplace_back(), *info_ptr);
|
||||
info_ptr++;
|
||||
}
|
||||
|
||||
response->emplace_success(std::move(entries));
|
||||
}
|
||||
libteaspeak_file_free_file_info(result);
|
||||
}
|
||||
return response;
|
||||
}
|
||||
|
||||
std::shared_ptr<ExecuteResponse<FileDeleteError, FileDeleteResponse>> RustFileSystem::delete_channel_files(
|
||||
const std::shared_ptr<VirtualFileServer> &virtual_server, ChannelId channel_id, const std::vector<std::string> &files) {
|
||||
std::vector<TeaFilePath> file_paths{};
|
||||
file_paths.reserve(files.size());
|
||||
for(const auto& path : files) {
|
||||
file_paths.push_back(TeaFilePath{
|
||||
.type = 0,
|
||||
.channel_id = channel_id,
|
||||
.path = path.c_str()
|
||||
});
|
||||
}
|
||||
|
||||
return this->execute_delete(virtual_server->unique_id().c_str(), file_paths.data(), file_paths.size());
|
||||
}
|
||||
|
||||
std::shared_ptr<ExecuteResponse<FileDeleteError, FileDeleteResponse>> RustFileSystem::delete_icons(
|
||||
const std::shared_ptr<VirtualFileServer> &virtual_server, const std::vector<std::string> &files) {
|
||||
std::vector<TeaFilePath> file_paths{};
|
||||
file_paths.reserve(files.size());
|
||||
for(const auto& path : files) {
|
||||
file_paths.push_back(TeaFilePath{
|
||||
.type = 1,
|
||||
.channel_id = 0,
|
||||
.path = path.c_str()
|
||||
});
|
||||
}
|
||||
|
||||
return this->execute_delete(virtual_server->unique_id().c_str(), file_paths.data(), file_paths.size());
|
||||
}
|
||||
|
||||
std::shared_ptr<ExecuteResponse<FileDeleteError, FileDeleteResponse>> RustFileSystem::delete_avatars(
|
||||
const std::shared_ptr<VirtualFileServer> &virtual_server, const std::vector<std::string> &files) {
|
||||
std::vector<TeaFilePath> file_paths{};
|
||||
file_paths.reserve(files.size());
|
||||
for(const auto& path : files) {
|
||||
file_paths.push_back(TeaFilePath{
|
||||
.type = 2,
|
||||
.channel_id = 0,
|
||||
.path = path.c_str()
|
||||
});
|
||||
}
|
||||
|
||||
return this->execute_delete(virtual_server->unique_id().c_str(), file_paths.data(), file_paths.size());
|
||||
}
|
||||
|
||||
inline void path_info_to_delete_result(FileDeleteResponse::DeleteResult& result, const TeaFilePathInfo& info) {
|
||||
switch (info.query_result) {
|
||||
case 0:
|
||||
result.status = FileDeleteResponse::StatusType::SUCCESS;
|
||||
break;
|
||||
|
||||
case 1:
|
||||
result.status = FileDeleteResponse::StatusType::PATH_EXCEEDS_ROOT_PATH;
|
||||
break;
|
||||
|
||||
case 2:
|
||||
result.status = FileDeleteResponse::StatusType::PATH_DOES_NOT_EXISTS;
|
||||
break;
|
||||
|
||||
case 5:
|
||||
result.status = FileDeleteResponse::StatusType::FAILED_TO_DELETE_FILES;
|
||||
break;
|
||||
|
||||
case 6:
|
||||
result.status = FileDeleteResponse::StatusType::SOME_FILES_ARE_LOCKED;
|
||||
break;
|
||||
|
||||
default:
|
||||
result.status = FileDeleteResponse::StatusType::FAILED_TO_DELETE_FILES;
|
||||
result.error_detail = std::string{"unknown delete error "} + std::to_string(info.query_result);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
std::shared_ptr<ExecuteResponse<FileDeleteError, FileDeleteResponse>> RustFileSystem::execute_delete(const char *server_unique_id, void *paths,
|
||||
size_t paths_count) {
|
||||
auto response = this->create_execute_response<FileDeleteError, FileDeleteResponse>();
|
||||
|
||||
const TeaFilePathInfo* result{nullptr};
|
||||
auto error_ptr = libteaspeak_file_system_delete_files(server_unique_id, (const TeaFilePath*) paths, paths_count, &result);
|
||||
if(error_ptr) {
|
||||
libteaspeak_file_free_file_info(result);
|
||||
|
||||
response->emplace_fail(FileDeleteErrorType::UNKNOWN, error_ptr);
|
||||
libteaspeak_free_str(error_ptr);
|
||||
} else {
|
||||
assert(result);
|
||||
std::vector<FileDeleteResponse::DeleteResult> results{};
|
||||
results.reserve(paths_count);
|
||||
|
||||
auto info_ptr{result};
|
||||
while(info_ptr->query_result >= 0) {
|
||||
path_info_to_delete_result(results.emplace_back(), *info_ptr);
|
||||
info_ptr++;
|
||||
}
|
||||
|
||||
assert(results.size() == paths_count);
|
||||
libteaspeak_file_free_file_info(result);
|
||||
response->emplace_success(FileDeleteResponse{results});
|
||||
}
|
||||
|
||||
return response;
|
||||
}
|
||||
|
||||
std::shared_ptr<ExecuteResponse<DirectoryModifyError>> RustFileSystem::create_channel_directory(
|
||||
const std::shared_ptr<VirtualFileServer> &virtual_server, ChannelId channelId, const std::string &path) {
|
||||
auto response = this->create_execute_response<DirectoryModifyError>();
|
||||
auto result = libteaspeak_file_system_create_channel_directory(virtual_server->unique_id().c_str(), channelId, path.c_str());
|
||||
switch (result) {
|
||||
case 0:
|
||||
response->emplace_success();
|
||||
break;
|
||||
|
||||
case 1:
|
||||
response->emplace_fail(DirectoryModifyErrorType::UNKNOWN, "server not found");
|
||||
break;
|
||||
|
||||
case 2:
|
||||
response->emplace_fail(DirectoryModifyErrorType::PATH_EXCEEDS_ROOT_PATH, "");
|
||||
break;
|
||||
|
||||
case 3:
|
||||
response->emplace_fail(DirectoryModifyErrorType::PATH_ALREADY_EXISTS, "");
|
||||
break;
|
||||
|
||||
case 4:
|
||||
response->emplace_fail(DirectoryModifyErrorType::FAILED_TO_CREATE_DIRECTORIES, "");
|
||||
break;
|
||||
|
||||
default:
|
||||
response->emplace_fail(DirectoryModifyErrorType::UNKNOWN, std::string{"invalid return code "} + std::to_string(result));
|
||||
break;
|
||||
}
|
||||
|
||||
return response;
|
||||
}
|
||||
|
||||
std::shared_ptr<ExecuteResponse<FileModifyError>> RustFileSystem::rename_channel_file(
|
||||
const std::shared_ptr<VirtualFileServer> &virtual_server, ChannelId old_channel_id, const std::string &old_path, ChannelId new_channel_id,
|
||||
const std::string &new_path) {
|
||||
auto response = this->create_execute_response<FileModifyError>();
|
||||
auto result = libteaspeak_file_system_rename_channel_file(virtual_server->unique_id().c_str(), old_channel_id, old_path.c_str(), new_channel_id, new_path.c_str());
|
||||
switch (result) {
|
||||
case 0:
|
||||
response->emplace_success();
|
||||
break;
|
||||
|
||||
case 1:
|
||||
response->emplace_fail(FileModifyErrorType::UNKNOWN, "server not found");
|
||||
break;
|
||||
|
||||
case 2:
|
||||
response->emplace_fail(FileModifyErrorType::UNKNOWN, "invalid path types");
|
||||
break;
|
||||
|
||||
case 3:
|
||||
response->emplace_fail(FileModifyErrorType::PATH_EXCEEDS_ROOT_PATH, "");
|
||||
break;
|
||||
|
||||
case 4:
|
||||
response->emplace_fail(FileModifyErrorType::TARGET_PATH_EXCEEDS_ROOT_PATH, "");
|
||||
break;
|
||||
|
||||
case 5:
|
||||
response->emplace_fail(FileModifyErrorType::PATH_DOES_NOT_EXISTS, "");
|
||||
break;
|
||||
|
||||
case 6:
|
||||
response->emplace_fail(FileModifyErrorType::SOME_FILES_ARE_LOCKED, "");
|
||||
break;
|
||||
|
||||
case 7:
|
||||
response->emplace_fail(FileModifyErrorType::TARGET_PATH_ALREADY_EXISTS, "");
|
||||
break;
|
||||
|
||||
case 8:
|
||||
response->emplace_fail(FileModifyErrorType::FAILED_TO_RENAME_FILE, "");
|
||||
break;
|
||||
|
||||
default:
|
||||
response->emplace_fail(FileModifyErrorType::UNKNOWN, std::string{"invalid return code "} + std::to_string(result));
|
||||
break;
|
||||
}
|
||||
|
||||
return response;
|
||||
}
|
||||
|
||||
std::shared_ptr<ExecuteResponse<TransferInitError, std::shared_ptr<Transfer>>>
|
||||
RustFileTransfer::initialize_channel_transfer(transfer::Transfer::Direction direction,
|
||||
const std::shared_ptr<VirtualFileServer> &ptr, ChannelId id,
|
||||
const transfer::AbstractProvider::TransferInfo &info) {
|
||||
auto result = this->create_execute_response<TransferInitError, std::shared_ptr<Transfer>>();
|
||||
result->emplace_fail(TransferInitError::Type::UNKNOWN, "");
|
||||
return result;
|
||||
}
|
||||
|
||||
std::shared_ptr<ExecuteResponse<TransferInitError, std::shared_ptr<Transfer>>>
|
||||
RustFileTransfer::initialize_icon_transfer(transfer::Transfer::Direction direction,
|
||||
const std::shared_ptr<VirtualFileServer> &ptr,
|
||||
const transfer::AbstractProvider::TransferInfo &info) {
|
||||
auto result = this->create_execute_response<TransferInitError, std::shared_ptr<Transfer>>();
|
||||
result->emplace_fail(TransferInitError::Type::UNKNOWN, "");
|
||||
return result;
|
||||
}
|
||||
|
||||
std::shared_ptr<ExecuteResponse<TransferInitError, std::shared_ptr<Transfer>>>
|
||||
RustFileTransfer::initialize_avatar_transfer(transfer::Transfer::Direction direction,
|
||||
const std::shared_ptr<VirtualFileServer> &ptr,
|
||||
const transfer::AbstractProvider::TransferInfo &info) {
|
||||
auto result = this->create_execute_response<TransferInitError, std::shared_ptr<Transfer>>();
|
||||
result->emplace_fail(TransferInitError::Type::UNKNOWN, "");
|
||||
return result;
|
||||
}
|
||||
|
||||
std::shared_ptr<ExecuteResponse<TransferListError, std::vector<ActiveFileTransfer>>>
|
||||
RustFileTransfer::list_transfer() {
|
||||
auto result = this->create_execute_response<TransferListError, std::vector<ActiveFileTransfer>>();
|
||||
result->emplace_fail(TransferListError::UNKNOWN);
|
||||
return result;
|
||||
}
|
||||
|
||||
std::shared_ptr<ExecuteResponse<TransferActionError>>
|
||||
RustFileTransfer::stop_transfer(const std::shared_ptr<VirtualFileServer> &ptr, transfer::transfer_id id,
|
||||
bool b) {
|
||||
auto result = this->create_execute_response<TransferActionError>();
|
||||
result->emplace_fail(TransferActionError{TransferActionError::Type::UNKNOWN_TRANSFER});
|
||||
return result;
|
||||
}
|
||||
|
||||
RustFileProvider::RustFileProvider() :
|
||||
file_system_{new RustFileSystem{}},
|
||||
file_transfer_{new RustFileTransfer{}}
|
||||
{}
|
||||
|
||||
RustFileProvider::~RustFileProvider() {
|
||||
delete this->file_transfer_;
|
||||
delete this->file_system_;
|
||||
}
|
||||
|
||||
std::string RustFileProvider::file_base_path() const {
|
||||
return "TODO!"; /* FIXME! */
|
||||
}
|
||||
|
||||
filesystem::AbstractProvider & RustFileProvider::file_system() {
|
||||
return *this->file_system_;
|
||||
}
|
||||
|
||||
transfer::AbstractProvider & RustFileProvider::file_transfer() {
|
||||
return *this->file_transfer_;
|
||||
}
|
||||
|
||||
std::shared_ptr<VirtualFileServer> RustFileProvider::register_server(ServerId server_id) {
|
||||
auto server = this->find_virtual_server(server_id);
|
||||
if(server) return server;
|
||||
|
||||
server = std::make_shared<file::RustVirtualFileServer>(server_id, std::to_string(server_id));
|
||||
libteaspeak_file_system_register_server(server->unique_id().c_str());
|
||||
{
|
||||
std::lock_guard slock{this->servers_mutex};
|
||||
this->servers_.push_back(server);
|
||||
}
|
||||
|
||||
return server;
|
||||
}
|
||||
|
||||
void RustFileProvider::unregister_server(ServerId server_id, bool delete_files) {
|
||||
auto server_unique_id = std::to_string(server_id);
|
||||
std::shared_ptr<VirtualFileServer> server{};
|
||||
|
||||
{
|
||||
std::lock_guard slock{this->servers_mutex};
|
||||
auto it = std::find_if(this->servers_.begin(), this->servers_.end(), [&](const std::shared_ptr<VirtualFileServer>& server) {
|
||||
return server->unique_id() == server_unique_id;
|
||||
});
|
||||
|
||||
if(it == this->servers_.end()) {
|
||||
return;
|
||||
}
|
||||
|
||||
server = *it;
|
||||
this->servers_.erase(it);
|
||||
}
|
||||
|
||||
libteaspeak_file_system_unregister_server(server->unique_id().c_str(), delete_files);
|
||||
}
|
||||
|
||||
void RustVirtualFileServer::max_networking_download_bandwidth(int64_t value) {
|
||||
VirtualFileServer::max_networking_download_bandwidth(value);
|
||||
}
|
||||
|
||||
void RustVirtualFileServer::max_networking_upload_bandwidth(int64_t value) {
|
||||
VirtualFileServer::max_networking_upload_bandwidth(value);
|
||||
}
|
||||
|
||||
|
||||
|
||||
#define log(...) \
|
||||
switch(level) { \
|
||||
case 0: \
|
||||
logTrace(__VA_ARGS__); \
|
||||
break; \
|
||||
case 1: \
|
||||
debugMessage(__VA_ARGS__); \
|
||||
break; \
|
||||
case 2: \
|
||||
logMessage(__VA_ARGS__); \
|
||||
break; \
|
||||
case 3: \
|
||||
logWarning(__VA_ARGS__); \
|
||||
break; \
|
||||
case 4: \
|
||||
logError(__VA_ARGS__); \
|
||||
break; \
|
||||
case 5: \
|
||||
logCritical(__VA_ARGS__); \
|
||||
break; \
|
||||
default: \
|
||||
debugMessage(__VA_ARGS__); \
|
||||
break; \
|
||||
}
|
||||
|
||||
void libteaspeak_file_callback_log(uint8_t level, const void* callback_data_ptr, const char* message_ptr, uint32_t length) {
|
||||
std::string_view message{message_ptr, length};
|
||||
log(LOG_GENERAL, "{}", message);
|
||||
}
|
||||
|
||||
#undef log
|
||||
|
||||
static TeaFileNativeCallbacks native_callbacks{
|
||||
.version = 1,
|
||||
|
||||
.log = libteaspeak_file_callback_log,
|
||||
};
|
||||
|
||||
std::shared_ptr<AbstractFileServer> file_instance{};
|
||||
bool file::initialize(std::string& error, const std::string& host_names, uint16_t port) {
|
||||
logMessage(LOG_FT, "Initializing file server with version {}", libteaspeak_file_version());
|
||||
|
||||
auto error_ptr = libteaspeak_file_initialize(&native_callbacks, sizeof native_callbacks);
|
||||
if(error_ptr) {
|
||||
error = error_ptr;
|
||||
libteaspeak_free_str(error_ptr);
|
||||
return false;
|
||||
}
|
||||
|
||||
/* FIXME: Start server */
|
||||
|
||||
file_instance = std::make_shared<RustFileProvider>();
|
||||
return true;
|
||||
}
|
||||
|
||||
void file::finalize() {
|
||||
file_instance = nullptr;
|
||||
libteaspeak_file_finalize();
|
||||
}
|
||||
|
||||
std::shared_ptr<AbstractFileServer> file::server() {
|
||||
return file_instance;
|
||||
}
|
123
file/local_rust/provider.h
Normal file
123
file/local_rust/provider.h
Normal file
@ -0,0 +1,123 @@
|
||||
//
|
||||
// Created by WolverinDEV on 20/04/2021.
|
||||
//
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <files/FileServer.h>
|
||||
|
||||
namespace ts::server::file {
|
||||
namespace filesystem {
|
||||
class RustFileSystem : public filesystem::AbstractProvider {
|
||||
using FileModifyError = filesystem::FileModifyError;
|
||||
using DirectoryModifyError = filesystem::DirectoryModifyError;
|
||||
public:
|
||||
virtual ~RustFileSystem();
|
||||
|
||||
std::shared_ptr<ExecuteResponse<ServerCommandError>> initialize_server(const std::shared_ptr<VirtualFileServer> & /* server */) override;
|
||||
|
||||
std::shared_ptr<ExecuteResponse<FileInfoError, FileInfoResponse>>
|
||||
query_channel_info(const std::shared_ptr<VirtualFileServer> & /* server */, const std::vector<std::tuple<ChannelId, std::string>>& /* names */) override;
|
||||
|
||||
std::shared_ptr<directory_query_response_t>
|
||||
query_channel_directory(const std::shared_ptr<VirtualFileServer> & id, ChannelId channelId, const std::string &string) override;
|
||||
|
||||
std::shared_ptr<ExecuteResponse<DirectoryModifyError>>
|
||||
create_channel_directory(const std::shared_ptr<VirtualFileServer> & id, ChannelId channelId, const std::string &string) override;
|
||||
|
||||
std::shared_ptr<ExecuteResponse<FileDeleteError, FileDeleteResponse>>
|
||||
delete_channel_files(const std::shared_ptr<VirtualFileServer> & id, ChannelId channelId, const std::vector<std::string> &string) override;
|
||||
|
||||
std::shared_ptr<ExecuteResponse<FileModifyError>>
|
||||
rename_channel_file(const std::shared_ptr<VirtualFileServer> & id, ChannelId channelId, const std::string &, ChannelId, const std::string &) override;
|
||||
|
||||
std::shared_ptr<ExecuteResponse<FileInfoError, FileInfoResponse>>
|
||||
query_icon_info(const std::shared_ptr<VirtualFileServer> & /* server */, const std::vector<std::string>& /* names */) override;
|
||||
|
||||
std::shared_ptr<directory_query_response_t> query_icon_directory(const std::shared_ptr<VirtualFileServer> & id) override;
|
||||
|
||||
std::shared_ptr<ExecuteResponse<FileDeleteError, FileDeleteResponse>>
|
||||
delete_icons(const std::shared_ptr<VirtualFileServer> & id, const std::vector<std::string> &string) override;
|
||||
|
||||
std::shared_ptr<ExecuteResponse<FileInfoError, FileInfoResponse>>
|
||||
query_avatar_info(const std::shared_ptr<VirtualFileServer> & /* server */, const std::vector<std::string>& /* names */) override;
|
||||
|
||||
std::shared_ptr<directory_query_response_t> query_avatar_directory(const std::shared_ptr<VirtualFileServer> & id) override;
|
||||
|
||||
std::shared_ptr<ExecuteResponse<FileDeleteError, FileDeleteResponse>>
|
||||
delete_avatars(const std::shared_ptr<VirtualFileServer> & id, const std::vector<std::string> &string) override;
|
||||
|
||||
private:
|
||||
template <typename error_t, typename result_t = EmptyExecuteResponse>
|
||||
std::shared_ptr<ExecuteResponse<error_t, result_t>> create_execute_response() {
|
||||
return std::make_shared<ExecuteResponse<error_t, result_t>>(this->result_notify_mutex, this->result_notify_cv);
|
||||
}
|
||||
|
||||
std::shared_ptr<ExecuteResponse<FileInfoError, FileInfoResponse>> execute_info(const char* /* server unique id */, void* /* query */, size_t /* query count */);
|
||||
std::shared_ptr<directory_query_response_t> execute_query(const char* /* server unique id */, void* /* directory */);
|
||||
std::shared_ptr<ExecuteResponse<FileDeleteError, FileDeleteResponse>> execute_delete(const char* /* server unique id */, void* /* files */, size_t /* file count */);
|
||||
|
||||
std::mutex result_notify_mutex{};
|
||||
std::condition_variable result_notify_cv{};
|
||||
};
|
||||
}
|
||||
|
||||
namespace transfer {
|
||||
class RustFileTransfer : public AbstractProvider {
|
||||
public:
|
||||
|
||||
std::shared_ptr<ExecuteResponse<TransferInitError, std::shared_ptr<Transfer>>>
|
||||
initialize_channel_transfer(Transfer::Direction direction,
|
||||
const std::shared_ptr<VirtualFileServer> &ptr, ChannelId id,
|
||||
const TransferInfo &info) override;
|
||||
|
||||
std::shared_ptr<ExecuteResponse<TransferInitError, std::shared_ptr<Transfer>>>
|
||||
initialize_icon_transfer(Transfer::Direction direction, const std::shared_ptr<VirtualFileServer> &ptr,
|
||||
const TransferInfo &info) override;
|
||||
|
||||
std::shared_ptr<ExecuteResponse<TransferInitError, std::shared_ptr<Transfer>>>
|
||||
initialize_avatar_transfer(Transfer::Direction direction, const std::shared_ptr<VirtualFileServer> &ptr,
|
||||
const TransferInfo &info) override;
|
||||
|
||||
std::shared_ptr<ExecuteResponse<TransferListError, std::vector<ActiveFileTransfer>>>
|
||||
list_transfer() override;
|
||||
|
||||
std::shared_ptr<ExecuteResponse<TransferActionError>>
|
||||
stop_transfer(const std::shared_ptr<VirtualFileServer> &ptr, transfer_id id, bool b) override;
|
||||
|
||||
private:
|
||||
template <typename error_t, typename result_t = EmptyExecuteResponse>
|
||||
std::shared_ptr<ExecuteResponse<error_t, result_t>> create_execute_response() {
|
||||
return std::make_shared<ExecuteResponse<error_t, result_t>>(this->result_notify_mutex, this->result_notify_cv);
|
||||
}
|
||||
|
||||
std::mutex result_notify_mutex{};
|
||||
std::condition_variable result_notify_cv{};
|
||||
};
|
||||
}
|
||||
|
||||
class RustVirtualFileServer : public VirtualFileServer {
|
||||
public:
|
||||
explicit RustVirtualFileServer(ServerId server_id, std::string unique_id) : VirtualFileServer{server_id, std::move(unique_id)} {}
|
||||
|
||||
void max_networking_upload_bandwidth(int64_t value) override;
|
||||
void max_networking_download_bandwidth(int64_t value) override;
|
||||
};
|
||||
|
||||
class RustFileProvider : public AbstractFileServer {
|
||||
public:
|
||||
RustFileProvider();
|
||||
virtual ~RustFileProvider();
|
||||
|
||||
[[nodiscard]] std::string file_base_path() const override;
|
||||
|
||||
filesystem::AbstractProvider &file_system() override;
|
||||
transfer::AbstractProvider &file_transfer() override;
|
||||
|
||||
std::shared_ptr<VirtualFileServer> register_server(ServerId /* server id */) override;
|
||||
void unregister_server(ServerId /* server id */, bool /* delete files */) override;
|
||||
private:
|
||||
filesystem::RustFileSystem* file_system_;
|
||||
transfer::RustFileTransfer* file_transfer_;
|
||||
};
|
||||
}
|
@ -125,18 +125,44 @@ std::shared_ptr<file::VirtualFileServer> LocalFileServer::register_server(Server
|
||||
return server;
|
||||
}
|
||||
|
||||
void LocalFileServer::unregister_server(ServerId server_id) {
|
||||
void LocalFileServer::unregister_server(ServerId server_id, bool delete_files) {
|
||||
auto server_unique_id = std::to_string(server_id);
|
||||
std::shared_ptr<VirtualFileServer> server{};
|
||||
|
||||
{
|
||||
std::lock_guard slock{this->servers_mutex};
|
||||
auto it = std::find_if(this->servers_.begin(), this->servers_.end(), [&](const std::shared_ptr<VirtualFileServer>& server) {
|
||||
return server->unique_id() == server_unique_id;
|
||||
});
|
||||
|
||||
if(it == this->servers_.end()) return;
|
||||
if(it == this->servers_.end()) {
|
||||
return;
|
||||
}
|
||||
|
||||
server = *it;
|
||||
this->servers_.erase(it);
|
||||
}
|
||||
|
||||
using ErrorType = file::filesystem::ServerCommandErrorType;
|
||||
|
||||
auto delete_result = this->file_system_->delete_server(server);
|
||||
if(!delete_result->wait_for(std::chrono::seconds{5})) {
|
||||
logError(LOG_INSTANCE, "Failed to wait for file directory deletion.");
|
||||
} else if(!delete_result->succeeded()) {
|
||||
switch (delete_result->error().error_type) {
|
||||
case ErrorType::FAILED_TO_DELETE_DIRECTORIES:
|
||||
logError(LOG_INSTANCE, "Failed to delete server {} file directories ({}).", server->server_id(), delete_result->error().error_message);
|
||||
break;
|
||||
|
||||
case ErrorType::UNKNOWN:
|
||||
case ErrorType::FAILED_TO_CREATE_DIRECTORIES:
|
||||
logError(LOG_INSTANCE, "Failed to delete server {} file directory due to an unknown error: {}/{}",
|
||||
server->server_id(), (int) delete_result->error().error_type, delete_result->error().error_message);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void LocalVirtualFileServer::max_networking_upload_bandwidth(int64_t value) {
|
||||
VirtualFileServer::max_networking_upload_bandwidth(value);
|
||||
this->upload_throttle.set_max_bandwidth(value);
|
||||
|
@ -44,7 +44,7 @@ namespace ts::server::file {
|
||||
|
||||
|
||||
std::shared_ptr<VirtualFileServer> register_server(ServerId /* server id */) override;
|
||||
void unregister_server(ServerId /* server id */) override;
|
||||
void unregister_server(ServerId /* server id */, bool /* delete server */) override;
|
||||
private:
|
||||
filesystem::LocalFileSystem* file_system_;
|
||||
transfer::LocalFileTransfer* file_transfer_;
|
||||
|
@ -50,7 +50,7 @@ namespace ts::server::file::filesystem {
|
||||
[[nodiscard]] std::string absolute_channel_path(const std::shared_ptr<VirtualFileServer> &, ChannelId, const std::string&);
|
||||
|
||||
std::shared_ptr<ExecuteResponse<ServerCommandError>> initialize_server(const std::shared_ptr<VirtualFileServer> & /* server */) override;
|
||||
std::shared_ptr<ExecuteResponse<ServerCommandError>> delete_server(const std::shared_ptr<VirtualFileServer> & /* server */) override;
|
||||
std::shared_ptr<ExecuteResponse<ServerCommandError>> delete_server(const std::shared_ptr<VirtualFileServer> & /* server */);
|
||||
|
||||
std::shared_ptr<ExecuteResponse<FileInfoError, FileInfoResponse>>
|
||||
query_channel_info(const std::shared_ptr<VirtualFileServer> & /* server */, const std::vector<std::tuple<ChannelId, std::string>>& /* names */) override;
|
||||
|
@ -181,8 +181,10 @@ std::shared_ptr<ExecuteResponse<TransferInitError, std::shared_ptr<Transfer>>> L
|
||||
transfer->client_id = info.client_id;
|
||||
|
||||
constexpr static std::string_view kTokenCharacters{"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"};
|
||||
for(auto& c : transfer->transfer_key)
|
||||
transfer->transfer_key.resize(TRANSFER_KEY_LENGTH);
|
||||
for(auto& c : transfer->transfer_key) {
|
||||
c = kTokenCharacters[transfer_random_token_generator() % kTokenCharacters.length()];
|
||||
}
|
||||
transfer->transfer_key[0] = (char) 'r'; /* (114) */ /* a non valid SSL header type to indicate that we're using a file transfer key and not doing a SSL handshake */
|
||||
transfer->transfer_key[1] = (char) 'a'; /* ( 97) */
|
||||
transfer->transfer_key[2] = (char) 'w'; /* (119) */
|
||||
|
@ -1223,7 +1223,7 @@ TransferKeyApplyResult LocalFileTransfer::handle_transfer_key_provided(const std
|
||||
{
|
||||
std::lock_guard tlock{this->transfers_mutex};
|
||||
for(auto it = this->pending_transfers.begin(); it != this->pending_transfers.end(); it++) {
|
||||
if(memcmp((*it)->transfer_key, client->transfer_key.key, TRANSFER_KEY_LENGTH) == 0) {
|
||||
if(memcmp((*it)->transfer_key.data(), client->transfer_key.key, std::min((size_t) TRANSFER_KEY_LENGTH, (*it)->transfer_key.length())) == 0) {
|
||||
client->transfer = *it;
|
||||
this->pending_transfers.erase(it);
|
||||
break;
|
||||
|
@ -1 +1 @@
|
||||
Subproject commit e6b2eedde9d246948684a279871e828cca6d9f90
|
||||
Subproject commit 401d248244b1d0dc82726d9baaeb375cb37f6f91
|
@ -245,6 +245,7 @@ target_link_libraries(TeaSpeakServer
|
||||
TeaMusic #Static
|
||||
CXXTerminal::static #Static
|
||||
TeaSpeak-FileServer
|
||||
# TeaSpeak-FileRust
|
||||
${StringVariable_LIBRARIES_STATIC}
|
||||
${YAML_CPP_LIBRARIES}
|
||||
pthread
|
||||
|
@ -21,9 +21,11 @@ bool FileServerHandler::initialize(std::string &error) {
|
||||
}
|
||||
|
||||
|
||||
#if 0
|
||||
file::config::ssl_option_supplier = [&]{
|
||||
return this->instance_->sslManager()->web_ssl_options();
|
||||
};
|
||||
#endif
|
||||
|
||||
auto server = file::server();
|
||||
assert(server);
|
||||
|
@ -529,6 +529,7 @@ void VirtualServer::client_move(
|
||||
if(auto client{dynamic_pointer_cast<VoiceClient>(target_client)}; client) {
|
||||
/* Start normal broadcasting, what the client expects */
|
||||
this->rtc_server().start_broadcast_audio(client->rtc_client_id, 1);
|
||||
client->clear_video_unsupported_message_flag();
|
||||
}
|
||||
} else {
|
||||
/* client left the server */
|
||||
|
@ -400,26 +400,7 @@ bool VirtualServerManager::deleteServer(shared_ptr<VirtualServer> server) {
|
||||
this->delete_server_in_db(server->serverId, false);
|
||||
this->handle->databaseHelper()->handleServerDelete(server->serverId);
|
||||
|
||||
if(auto file_vs = file::server()->find_virtual_server(server->getServerId()); file_vs) {
|
||||
using ErrorType = file::filesystem::ServerCommandErrorType;
|
||||
|
||||
auto delete_result = file::server()->file_system().delete_server(file_vs);
|
||||
if(!delete_result->wait_for(std::chrono::seconds{5})) {
|
||||
logError(LOG_INSTANCE, "Failed to wait for file directory initialisation.");
|
||||
} else if(!delete_result->succeeded()) {
|
||||
switch (delete_result->error().error_type) {
|
||||
case ErrorType::FAILED_TO_DELETE_DIRECTORIES:
|
||||
logError(LOG_INSTANCE, "Failed to delete server {} file directories ({}).", server->getServerId(), delete_result->error().error_message);
|
||||
break;
|
||||
|
||||
case ErrorType::UNKNOWN:
|
||||
case ErrorType::FAILED_TO_CREATE_DIRECTORIES:
|
||||
logError(LOG_INSTANCE, "Failed to delete server {} file directory due to an unknown error: {}/{}",
|
||||
server->getServerId(), (int) delete_result->error().error_type, delete_result->error().error_message);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
file::server()->unregister_server(server->getServerId(), true);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -756,7 +756,7 @@ command_result ConnectedClient::handleCommandFTInitUpload(ts::Command &cmd) {
|
||||
result.put_unchecked(0, "ip", used_address.hostname);
|
||||
result.put_unchecked(0, "port", used_address.port);
|
||||
|
||||
result.put_unchecked(0, "ftkey", std::string_view{transfer->transfer_key, TRANSFER_KEY_LENGTH});
|
||||
result.put_unchecked(0, "ftkey", transfer->transfer_key);
|
||||
result.put_unchecked(0, "seekpos", transfer->file_offset);
|
||||
result.put_unchecked(0, "proto", "1");
|
||||
this->sendCommand(result);
|
||||
@ -892,7 +892,7 @@ command_result ConnectedClient::handleCommandFTInitDownload(ts::Command &cmd) {
|
||||
result.put_unchecked(0, "ip", used_address.hostname);
|
||||
result.put_unchecked(0, "port", used_address.port);
|
||||
|
||||
result.put_unchecked(0, "ftkey", std::string_view{transfer->transfer_key, TRANSFER_KEY_LENGTH});
|
||||
result.put_unchecked(0, "ftkey", transfer->transfer_key);
|
||||
result.put_unchecked(0, "proto", "1");
|
||||
result.put_unchecked(0, "size", transfer->expected_file_size);
|
||||
|
||||
|
@ -463,3 +463,37 @@ void VoiceClient::processJoin() {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void VoiceClient::clear_video_unsupported_message_flag() {
|
||||
this->video_unsupported_message_send = false;
|
||||
}
|
||||
|
||||
constexpr static auto kUnsupportedMessage{"\n"
|
||||
"[b]Somebody in your channel started video sharing!\n"
|
||||
"[color=red]Your client doesn't support video sharing.[/color]\n"
|
||||
"\n"
|
||||
"Download the newest TeaSpeak client from [url]https://teaspeak.de[/url] or\n"
|
||||
"use the TeaSpeakWeb client at [url]%web-url%[/url].[/b]"};
|
||||
|
||||
void VoiceClient::send_video_unsupported_message() {
|
||||
if(this->video_unsupported_message_send) {
|
||||
return;
|
||||
}
|
||||
|
||||
this->video_unsupported_message_send = true;
|
||||
ts::command_builder notify{"notifytextmessage"};
|
||||
notify.put_unchecked(0, "targetmode", ChatMessageMode::TEXTMODE_CHANNEL);
|
||||
notify.put_unchecked(0, "target", "0");
|
||||
notify.put_unchecked(0, "invokerid", "0");
|
||||
notify.put_unchecked(0, "invokername", "TeaSpeak - Video");
|
||||
notify.put_unchecked(0, "invokeruid", "000000000000000000000000000");
|
||||
std::string message{kUnsupportedMessage};
|
||||
if(auto index{message.find("%web-url%")}; index != std::string::npos) {
|
||||
/* TODO: generate connect url */
|
||||
//this->server->getWebServer()->
|
||||
message.replace(index, 9, "https://web.teaspeak.de");
|
||||
}
|
||||
notify.put_unchecked(0, "msg", message);
|
||||
|
||||
this->sendCommand(notify, false);
|
||||
}
|
@ -80,6 +80,9 @@ namespace ts {
|
||||
[[nodiscard]] float current_packet_loss() const;
|
||||
|
||||
[[nodiscard]] const auto& server_command_queue() { return this->server_command_queue_; }
|
||||
|
||||
void send_video_unsupported_message();
|
||||
void clear_video_unsupported_message_flag();
|
||||
private:
|
||||
connection::VoiceClientConnection* connection;
|
||||
|
||||
@ -123,6 +126,7 @@ namespace ts {
|
||||
|
||||
std::unique_ptr<ServerCommandQueue> server_command_queue_{};
|
||||
|
||||
bool video_unsupported_message_send{false};
|
||||
void finalDisconnect();
|
||||
|
||||
/* Used by close_connection to determine if we've successfully flushed the connection */
|
||||
|
@ -209,6 +209,16 @@ void librtc_callback_client_video_broadcast_info(const void* const* callback_dat
|
||||
if(!client) { continue; }
|
||||
|
||||
client->sendCommand(notify);
|
||||
if(client->getType() == ClientType::CLIENT_TEAMSPEAK) {
|
||||
auto voice_client = std::dynamic_pointer_cast<VoiceClient>(client);
|
||||
assert(voice_client);
|
||||
|
||||
if(broadcast_count > 0) {
|
||||
voice_client->send_video_unsupported_message();
|
||||
} else {
|
||||
voice_client->clear_video_unsupported_message_flag();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -236,3 +236,8 @@ void WebControlServer::unregisterConnection(const std::shared_ptr<WebClient>& co
|
||||
this->clients.erase(index);
|
||||
}
|
||||
}
|
||||
|
||||
std::optional<std::string> WebControlServer::external_binding() {
|
||||
/* FIXME: TODO! */
|
||||
return std::nullopt;
|
||||
}
|
@ -35,6 +35,7 @@ namespace ts {
|
||||
return this->clients;
|
||||
}
|
||||
|
||||
[[nodiscard]] std::optional<std::string> external_binding();
|
||||
private:
|
||||
std::shared_ptr<VirtualServer> handle = nullptr;
|
||||
|
||||
|
2
shared
2
shared
@ -1 +1 @@
|
||||
Subproject commit 7a7a24ee2ce28607ec7e988053a38713c2fa3404
|
||||
Subproject commit f008d3a711307307a85bac837d124baf99d9e8ac
|
Loading…
Reference in New Issue
Block a user