From f7039187b2733f35f5a4fa3e8b7c93e40ba360b7 Mon Sep 17 00:00:00 2001 From: WolverinDEV Date: Wed, 21 Apr 2021 13:19:46 +0200 Subject: [PATCH] Starting attempt to implement the new rust time transfer server --- .gitmodules | 3 + file-rust | 1 + file/CMakeLists.txt | 8 + file/include/files/FileServer.h | 14 +- file/local_rust/imports.h | 54 ++ file/local_rust/lib.cpp | 6 + file/local_rust/lib.h | 5 + file/local_rust/provider.cpp | 587 ++++++++++++++++++ file/local_rust/provider.h | 123 ++++ file/local_server/LocalFileProvider.cpp | 40 +- file/local_server/LocalFileProvider.h | 2 +- file/local_server/LocalFileSystem.h | 2 +- file/local_server/LocalFileTransfer.cpp | 4 +- .../local_server/LocalFileTransferNetwork.cpp | 2 +- git-teaspeak | 2 +- server/CMakeLists.txt | 1 + server/src/FileServerHandler.cpp | 2 + server/src/TS3ServerClientManager.cpp | 1 + server/src/VirtualServerManager.cpp | 21 +- server/src/client/command_handler/file.cpp | 4 +- server/src/client/voice/VoiceClient.cpp | 34 + server/src/client/voice/VoiceClient.h | 4 + server/src/rtc/lib.cpp | 10 + server/src/server/WebServer.cpp | 5 + server/src/server/WebServer.h | 1 + shared | 2 +- 26 files changed, 898 insertions(+), 40 deletions(-) create mode 160000 file-rust create mode 100644 file/local_rust/imports.h create mode 100644 file/local_rust/lib.cpp create mode 100644 file/local_rust/lib.h create mode 100644 file/local_rust/provider.cpp create mode 100644 file/local_rust/provider.h diff --git a/.gitmodules b/.gitmodules index da63da2..f6e1863 100644 --- a/.gitmodules +++ b/.gitmodules @@ -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 diff --git a/file-rust b/file-rust new file mode 160000 index 0000000..29f9579 --- /dev/null +++ b/file-rust @@ -0,0 +1 @@ +Subproject commit 29f95794358201811692f7e7b60f78fe9605f2a1 diff --git a/file/CMakeLists.txt b/file/CMakeLists.txt index 4679336..75f12cf 100644 --- a/file/CMakeLists.txt +++ b/file/CMakeLists.txt @@ -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 diff --git a/file/include/files/FileServer.h b/file/include/files/FileServer.h index f702374..67337a7 100644 --- a/file/include/files/FileServer.h +++ b/file/include/files/FileServer.h @@ -2,13 +2,12 @@ #include #include -#include -#include #include #include #include #include #include +#include #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 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 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 file_info{}; @@ -149,7 +154,6 @@ namespace ts::server::file { /* server */ [[nodiscard]] virtual std::shared_ptr> initialize_server(const std::shared_ptr &/* server */) = 0; - [[nodiscard]] virtual std::shared_ptr> delete_server(const std::shared_ptr &/* server */) = 0; /* channels */ [[nodiscard]] virtual std::shared_ptr> query_channel_info(const std::shared_ptr &/* server */, const std::vector>& /* 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 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> servers_{}; diff --git a/file/local_rust/imports.h b/file/local_rust/imports.h new file mode 100644 index 0000000..a89d908 --- /dev/null +++ b/file/local_rust/imports.h @@ -0,0 +1,54 @@ +// +// Created by WolverinDEV on 20/04/2021. +// + +#pragma once + +#include +#include + +#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 \ No newline at end of file diff --git a/file/local_rust/lib.cpp b/file/local_rust/lib.cpp new file mode 100644 index 0000000..ed683d9 --- /dev/null +++ b/file/local_rust/lib.cpp @@ -0,0 +1,6 @@ +// +// Created by WolverinDEV on 20/04/2021. +// + +#include "./lib.h" +#include "./imports.h" \ No newline at end of file diff --git a/file/local_rust/lib.h b/file/local_rust/lib.h new file mode 100644 index 0000000..1de2da1 --- /dev/null +++ b/file/local_rust/lib.h @@ -0,0 +1,5 @@ +// +// Created by WolverinDEV on 20/04/2021. +// + +#pragma once \ No newline at end of file diff --git a/file/local_rust/provider.cpp b/file/local_rust/provider.cpp new file mode 100644 index 0000000..5d63aca --- /dev/null +++ b/file/local_rust/provider.cpp @@ -0,0 +1,587 @@ +// +// Created by WolverinDEV on 20/04/2021. +// + +#include +#include +#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> RustFileSystem::initialize_server( + const std::shared_ptr &) { + /* Nothing to do. */ + auto response = this->create_execute_response(); + response->emplace_success(); + return response; +} + +std::shared_ptr> RustFileSystem::query_channel_info( + const std::shared_ptr &virtual_server, const std::vector> &files) { + std::vector 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::query_channel_directory( + const std::shared_ptr &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> RustFileSystem::query_icon_info( + const std::shared_ptr &virtual_server, const std::vector &files) { + std::vector 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::query_icon_directory( + const std::shared_ptr &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> RustFileSystem::query_avatar_info( + const std::shared_ptr &virtual_server, const std::vector &files) { + std::vector 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::query_avatar_directory( + const std::shared_ptr &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> RustFileSystem::execute_info(const char *server_unique_id, void *paths, + size_t paths_count) { + auto response = this->create_execute_response(); + + 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 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::execute_query(const char *server_unique_id, void *path) { + auto response = this->create_execute_response>(); + + 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 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> RustFileSystem::delete_channel_files( + const std::shared_ptr &virtual_server, ChannelId channel_id, const std::vector &files) { + std::vector 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> RustFileSystem::delete_icons( + const std::shared_ptr &virtual_server, const std::vector &files) { + std::vector 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> RustFileSystem::delete_avatars( + const std::shared_ptr &virtual_server, const std::vector &files) { + std::vector 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> RustFileSystem::execute_delete(const char *server_unique_id, void *paths, + size_t paths_count) { + auto response = this->create_execute_response(); + + 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 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> RustFileSystem::create_channel_directory( + const std::shared_ptr &virtual_server, ChannelId channelId, const std::string &path) { + auto response = this->create_execute_response(); + 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> RustFileSystem::rename_channel_file( + const std::shared_ptr &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(); + 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>> +RustFileTransfer::initialize_channel_transfer(transfer::Transfer::Direction direction, + const std::shared_ptr &ptr, ChannelId id, + const transfer::AbstractProvider::TransferInfo &info) { + auto result = this->create_execute_response>(); + result->emplace_fail(TransferInitError::Type::UNKNOWN, ""); + return result; +} + +std::shared_ptr>> +RustFileTransfer::initialize_icon_transfer(transfer::Transfer::Direction direction, + const std::shared_ptr &ptr, + const transfer::AbstractProvider::TransferInfo &info) { + auto result = this->create_execute_response>(); + result->emplace_fail(TransferInitError::Type::UNKNOWN, ""); + return result; +} + +std::shared_ptr>> +RustFileTransfer::initialize_avatar_transfer(transfer::Transfer::Direction direction, + const std::shared_ptr &ptr, + const transfer::AbstractProvider::TransferInfo &info) { + auto result = this->create_execute_response>(); + result->emplace_fail(TransferInitError::Type::UNKNOWN, ""); + return result; +} + +std::shared_ptr>> +RustFileTransfer::list_transfer() { + auto result = this->create_execute_response>(); + result->emplace_fail(TransferListError::UNKNOWN); + return result; +} + +std::shared_ptr> +RustFileTransfer::stop_transfer(const std::shared_ptr &ptr, transfer::transfer_id id, + bool b) { + auto result = this->create_execute_response(); + 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 RustFileProvider::register_server(ServerId server_id) { + auto server = this->find_virtual_server(server_id); + if(server) return server; + + server = std::make_shared(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 server{}; + + { + std::lock_guard slock{this->servers_mutex}; + auto it = std::find_if(this->servers_.begin(), this->servers_.end(), [&](const std::shared_ptr& 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 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(); + return true; +} + +void file::finalize() { + file_instance = nullptr; + libteaspeak_file_finalize(); +} + +std::shared_ptr file::server() { + return file_instance; +} diff --git a/file/local_rust/provider.h b/file/local_rust/provider.h new file mode 100644 index 0000000..2f3d64d --- /dev/null +++ b/file/local_rust/provider.h @@ -0,0 +1,123 @@ +// +// Created by WolverinDEV on 20/04/2021. +// + +#pragma once + +#include + +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> initialize_server(const std::shared_ptr & /* server */) override; + + std::shared_ptr> + query_channel_info(const std::shared_ptr & /* server */, const std::vector>& /* names */) override; + + std::shared_ptr + query_channel_directory(const std::shared_ptr & id, ChannelId channelId, const std::string &string) override; + + std::shared_ptr> + create_channel_directory(const std::shared_ptr & id, ChannelId channelId, const std::string &string) override; + + std::shared_ptr> + delete_channel_files(const std::shared_ptr & id, ChannelId channelId, const std::vector &string) override; + + std::shared_ptr> + rename_channel_file(const std::shared_ptr & id, ChannelId channelId, const std::string &, ChannelId, const std::string &) override; + + std::shared_ptr> + query_icon_info(const std::shared_ptr & /* server */, const std::vector& /* names */) override; + + std::shared_ptr query_icon_directory(const std::shared_ptr & id) override; + + std::shared_ptr> + delete_icons(const std::shared_ptr & id, const std::vector &string) override; + + std::shared_ptr> + query_avatar_info(const std::shared_ptr & /* server */, const std::vector& /* names */) override; + + std::shared_ptr query_avatar_directory(const std::shared_ptr & id) override; + + std::shared_ptr> + delete_avatars(const std::shared_ptr & id, const std::vector &string) override; + + private: + template + std::shared_ptr> create_execute_response() { + return std::make_shared>(this->result_notify_mutex, this->result_notify_cv); + } + + std::shared_ptr> execute_info(const char* /* server unique id */, void* /* query */, size_t /* query count */); + std::shared_ptr execute_query(const char* /* server unique id */, void* /* directory */); + std::shared_ptr> 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>> + initialize_channel_transfer(Transfer::Direction direction, + const std::shared_ptr &ptr, ChannelId id, + const TransferInfo &info) override; + + std::shared_ptr>> + initialize_icon_transfer(Transfer::Direction direction, const std::shared_ptr &ptr, + const TransferInfo &info) override; + + std::shared_ptr>> + initialize_avatar_transfer(Transfer::Direction direction, const std::shared_ptr &ptr, + const TransferInfo &info) override; + + std::shared_ptr>> + list_transfer() override; + + std::shared_ptr> + stop_transfer(const std::shared_ptr &ptr, transfer_id id, bool b) override; + + private: + template + std::shared_ptr> create_execute_response() { + return std::make_shared>(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 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_; + }; +} \ No newline at end of file diff --git a/file/local_server/LocalFileProvider.cpp b/file/local_server/LocalFileProvider.cpp index d9d1920..a08c2d2 100644 --- a/file/local_server/LocalFileProvider.cpp +++ b/file/local_server/LocalFileProvider.cpp @@ -125,16 +125,42 @@ std::shared_ptr 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 server{}; - std::lock_guard slock{this->servers_mutex}; - auto it = std::find_if(this->servers_.begin(), this->servers_.end(), [&](const std::shared_ptr& server) { - return server->unique_id() == server_unique_id; - }); + { + std::lock_guard slock{this->servers_mutex}; + auto it = std::find_if(this->servers_.begin(), this->servers_.end(), [&](const std::shared_ptr& server) { + return server->unique_id() == server_unique_id; + }); - if(it == this->servers_.end()) return; - this->servers_.erase(it); + 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) { diff --git a/file/local_server/LocalFileProvider.h b/file/local_server/LocalFileProvider.h index 05db4a6..6fb0a5f 100644 --- a/file/local_server/LocalFileProvider.h +++ b/file/local_server/LocalFileProvider.h @@ -44,7 +44,7 @@ namespace ts::server::file { std::shared_ptr 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_; diff --git a/file/local_server/LocalFileSystem.h b/file/local_server/LocalFileSystem.h index 9fbe11b..05a3c00 100644 --- a/file/local_server/LocalFileSystem.h +++ b/file/local_server/LocalFileSystem.h @@ -50,7 +50,7 @@ namespace ts::server::file::filesystem { [[nodiscard]] std::string absolute_channel_path(const std::shared_ptr &, ChannelId, const std::string&); std::shared_ptr> initialize_server(const std::shared_ptr & /* server */) override; - std::shared_ptr> delete_server(const std::shared_ptr & /* server */) override; + std::shared_ptr> delete_server(const std::shared_ptr & /* server */); std::shared_ptr> query_channel_info(const std::shared_ptr & /* server */, const std::vector>& /* names */) override; diff --git a/file/local_server/LocalFileTransfer.cpp b/file/local_server/LocalFileTransfer.cpp index a02a7e9..d5af97d 100644 --- a/file/local_server/LocalFileTransfer.cpp +++ b/file/local_server/LocalFileTransfer.cpp @@ -181,8 +181,10 @@ std::shared_ptr>> 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) */ diff --git a/file/local_server/LocalFileTransferNetwork.cpp b/file/local_server/LocalFileTransferNetwork.cpp index fe576cd..c275948 100644 --- a/file/local_server/LocalFileTransferNetwork.cpp +++ b/file/local_server/LocalFileTransferNetwork.cpp @@ -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; diff --git a/git-teaspeak b/git-teaspeak index e6b2eed..401d248 160000 --- a/git-teaspeak +++ b/git-teaspeak @@ -1 +1 @@ -Subproject commit e6b2eedde9d246948684a279871e828cca6d9f90 +Subproject commit 401d248244b1d0dc82726d9baaeb375cb37f6f91 diff --git a/server/CMakeLists.txt b/server/CMakeLists.txt index da13f24..3869c17 100644 --- a/server/CMakeLists.txt +++ b/server/CMakeLists.txt @@ -245,6 +245,7 @@ target_link_libraries(TeaSpeakServer TeaMusic #Static CXXTerminal::static #Static TeaSpeak-FileServer + # TeaSpeak-FileRust ${StringVariable_LIBRARIES_STATIC} ${YAML_CPP_LIBRARIES} pthread diff --git a/server/src/FileServerHandler.cpp b/server/src/FileServerHandler.cpp index 4bccb4d..9150238 100644 --- a/server/src/FileServerHandler.cpp +++ b/server/src/FileServerHandler.cpp @@ -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); diff --git a/server/src/TS3ServerClientManager.cpp b/server/src/TS3ServerClientManager.cpp index 9b6e607..4172680 100644 --- a/server/src/TS3ServerClientManager.cpp +++ b/server/src/TS3ServerClientManager.cpp @@ -529,6 +529,7 @@ void VirtualServer::client_move( if(auto client{dynamic_pointer_cast(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 */ diff --git a/server/src/VirtualServerManager.cpp b/server/src/VirtualServerManager.cpp index 5cf4c44..4f09307 100644 --- a/server/src/VirtualServerManager.cpp +++ b/server/src/VirtualServerManager.cpp @@ -400,26 +400,7 @@ bool VirtualServerManager::deleteServer(shared_ptr 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; } diff --git a/server/src/client/command_handler/file.cpp b/server/src/client/command_handler/file.cpp index 3f38931..e59a305 100644 --- a/server/src/client/command_handler/file.cpp +++ b/server/src/client/command_handler/file.cpp @@ -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); diff --git a/server/src/client/voice/VoiceClient.cpp b/server/src/client/voice/VoiceClient.cpp index b5c99bf..f70d144 100644 --- a/server/src/client/voice/VoiceClient.cpp +++ b/server/src/client/voice/VoiceClient.cpp @@ -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); +} \ No newline at end of file diff --git a/server/src/client/voice/VoiceClient.h b/server/src/client/voice/VoiceClient.h index 1bcabcf..c27d562 100644 --- a/server/src/client/voice/VoiceClient.h +++ b/server/src/client/voice/VoiceClient.h @@ -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 server_command_queue_{}; + bool video_unsupported_message_send{false}; void finalDisconnect(); /* Used by close_connection to determine if we've successfully flushed the connection */ diff --git a/server/src/rtc/lib.cpp b/server/src/rtc/lib.cpp index 3a238e1..8d8d793 100644 --- a/server/src/rtc/lib.cpp +++ b/server/src/rtc/lib.cpp @@ -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(client); + assert(voice_client); + + if(broadcast_count > 0) { + voice_client->send_video_unsupported_message(); + } else { + voice_client->clear_video_unsupported_message_flag(); + } + } } } diff --git a/server/src/server/WebServer.cpp b/server/src/server/WebServer.cpp index 820a92c..2a08fd2 100644 --- a/server/src/server/WebServer.cpp +++ b/server/src/server/WebServer.cpp @@ -236,3 +236,8 @@ void WebControlServer::unregisterConnection(const std::shared_ptr& co this->clients.erase(index); } } + +std::optional WebControlServer::external_binding() { + /* FIXME: TODO! */ + return std::nullopt; +} \ No newline at end of file diff --git a/server/src/server/WebServer.h b/server/src/server/WebServer.h index 37d93eb..9445649 100644 --- a/server/src/server/WebServer.h +++ b/server/src/server/WebServer.h @@ -35,6 +35,7 @@ namespace ts { return this->clients; } + [[nodiscard]] std::optional external_binding(); private: std::shared_ptr handle = nullptr; diff --git a/shared b/shared index 7a7a24e..f008d3a 160000 --- a/shared +++ b/shared @@ -1 +1 @@ -Subproject commit 7a7a24ee2ce28607ec7e988053a38713c2fa3404 +Subproject commit f008d3a711307307a85bac837d124baf99d9e8ac