diff --git a/CMakeLists.txt b/CMakeLists.txt index 46e7ecf..ffbf940 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -103,4 +103,5 @@ add_definitions(-DINET -DINET6) add_subdirectory(shared/) add_subdirectory(server/) add_subdirectory(license/) -add_subdirectory(MusicBot/) \ No newline at end of file +add_subdirectory(MusicBot/) +add_subdirectory(file/) \ No newline at end of file diff --git a/file/CMakeLists.txt b/file/CMakeLists.txt new file mode 100644 index 0000000..83f9fa0 --- /dev/null +++ b/file/CMakeLists.txt @@ -0,0 +1,25 @@ +cmake_minimum_required(VERSION 3.6) +project(TeaSpeak-Files) + +#set(CMAKE_CXX_STANDARD 17) +#set(CMAKE_CXX_STANDARD_REQUIRED ON) + +add_library(TeaSpeak-FileServer STATIC + local_server/LocalFileServer.cpp + local_server/LocalFileSystem.cpp + local_server/clnpath.cpp +) + +target_link_libraries(TeaSpeak-FileServer PUBLIC TeaSpeak ${StringVariable_LIBRARIES_STATIC} stdc++fs + libevent::core libevent::pthreads) +target_include_directories(TeaSpeak-FileServer PUBLIC include/) + +add_executable(TeaSpeak-FileServerTest test/main.cpp) +target_link_libraries(TeaSpeak-FileServerTest PUBLIC TeaSpeak-FileServer + TeaMusic #Static (Must be in here, so we link against TeaMusic which uses C++11. That forbids GCC to use the newer glibc version) + CXXTerminal::static #Static + stdc++fs +) + +add_executable(FileServer-CLNText local_server/clnpath.cpp) +target_compile_definitions(FileServer-CLNText PUBLIC -DCLN_EXEC) \ No newline at end of file diff --git a/file/include/files/FileServer.h b/file/include/files/FileServer.h new file mode 100644 index 0000000..19e7d11 --- /dev/null +++ b/file/include/files/FileServer.h @@ -0,0 +1,286 @@ +#pragma once + +#include +#include +#include +#include +#include +#include +#include + +namespace ts::server::file { + enum struct ExecuteStatus { + UNKNOWN, + WAITING, + SUCCESS, + ERROR + }; + + template + constexpr std::size_t variant_index() { + if constexpr (index == std::variant_size_v) { + return index; + } else if constexpr (std::is_same_v, T>) { + return index; + } else { + return variant_index(); + } + } + + struct EmptyExecuteResponse { }; + template + class ExecuteResponse { + typedef std::variant variant_t; + public: + ExecuteStatus status{ExecuteStatus::WAITING}; + + [[nodiscard]] inline const auto& response() const { return std::get(this->response_); } + + template ::value>> + [[nodiscard]] inline const error_t& error() const { return std::get(this->response_); } + + inline void wait() const { + std::unique_lock nlock{this->notify_mutex}; + this->notify_cv.wait(nlock, [&]{ return this->status != ExecuteStatus::WAITING; }); + } + + template + [[nodiscard]] inline bool wait_for(const std::chrono::duration<_Rep, _Period>& time) const { + std::unique_lock nlock{this->notify_mutex}; + return this->notify_cv.wait_for(nlock, time, [&]{ return this->status != ExecuteStatus::WAITING; }); + } + + template + inline void emplace_success(Args&&... args) { + constexpr auto success_index = variant_index(); + + std::lock_guard rlock{this->notify_mutex}; + this->response_.template emplace(std::forward(args)...); + this->status = ExecuteStatus::SUCCESS; + this->notify_cv.notify_all(); + } + + template + inline void emplace_fail(Args&&... args) { + constexpr auto error_index = variant_index(); + + std::lock_guard rlock{this->notify_mutex}; + this->response_.template emplace(std::forward(args)...); + this->status = ExecuteStatus::ERROR; + this->notify_cv.notify_all(); + } + + ExecuteResponse(std::mutex& notify_mutex, std::condition_variable& notify_cv) + : notify_mutex{notify_mutex}, notify_cv{notify_cv} {} + private: + variant_t response_{}; /* void* as default value so we don't initialize error_t or response_t */ + + std::mutex& notify_mutex; + std::condition_variable& notify_cv; + }; + + namespace filesystem { + template + struct DetailedError { + ErrorCodes error_type{ErrorCodes::UNKNOWN}; + std::string error_message{}; + + DetailedError(ErrorCodes type, std::string extraMessage) : error_type{type}, error_message{std::move(extraMessage)} {} + }; + + enum struct DirectoryQueryErrorType { + UNKNOWN, + PATH_EXCEEDS_ROOT_PATH, + PATH_IS_A_FILE, + PATH_DOES_NOT_EXISTS, + FAILED_TO_LIST_FILES, + + MAX + }; + constexpr std::array directory_query_error_messages = { + "unknown error", + "path exceeds base path", + "path is a file", + "path does not exists", + "failed to list files" + }; + + typedef DetailedError DirectoryQueryError; + + struct DirectoryEntry { + enum Type { + UNKNOWN, + DIRECTORY, + FILE + }; + + Type type{Type::UNKNOWN}; + std::string name{}; + std::chrono::system_clock::time_point modified_at{}; + + size_t size{0}; + }; + + enum struct DirectoryModifyErrorType { + UNKNOWN, + PATH_EXCEEDS_ROOT_PATH, + PATH_ALREADY_EXISTS, + FAILED_TO_CREATE_DIRECTORIES + }; + typedef DetailedError DirectoryModifyError; + + enum struct FileModifyErrorType { + UNKNOWN, + PATH_EXCEEDS_ROOT_PATH, + TARGET_PATH_EXCEEDS_ROOT_PATH, + PATH_DOES_NOT_EXISTS, + TARGET_PATH_ALREADY_EXISTS, + FAILED_TO_DELETE_FILES, + FAILED_TO_RENAME_FILE, + + SOME_FILES_ARE_LOCKED + }; + typedef DetailedError FileModifyError; + + enum struct ServerCommandErrorType { + UNKNOWN, + FAILED_TO_CREATE_DIRECTORIES, + FAILED_TO_DELETE_DIRECTORIES + }; + typedef DetailedError ServerCommandError; + + class AbstractProvider { + public: + typedef ExecuteResponse> directory_query_response_t; + + /* server */ + [[nodiscard]] virtual std::shared_ptr> initialize_server(ServerId /* server */) = 0; + [[nodiscard]] virtual std::shared_ptr> delete_server(ServerId /* server */) = 0; + + /* channels */ + [[nodiscard]] virtual std::shared_ptr query_channel_directory(ServerId /* server */, ChannelId /* channel */, const std::string& /* path */) = 0; + [[nodiscard]] virtual std::shared_ptr> create_channel_directory(ServerId /* server */, ChannelId /* channel */, const std::string& /* path */) = 0; + [[nodiscard]] virtual std::shared_ptr> delete_channel_file(ServerId /* server */, ChannelId /* channel */, const std::string& /* path */) = 0; + [[nodiscard]] virtual std::shared_ptr> rename_channel_file(ServerId /* server */, ChannelId /* channel */, const std::string& /* path */, const std::string& /* target */) = 0; + + /* icons */ + [[nodiscard]] virtual std::shared_ptr query_icon_directory(ServerId /* server */) = 0; + [[nodiscard]] virtual std::shared_ptr> delete_icon(ServerId /* server */, const std::string& /* name */) = 0; + + /* avatars */ + [[nodiscard]] virtual std::shared_ptr query_avatar_directory(ServerId /* server */) = 0; + [[nodiscard]] virtual std::shared_ptr> delete_avatar(ServerId /* server */, const std::string& /* name */) = 0; + private: + }; + } + + namespace transfer { + typedef uint32_t transfer_id; + + struct Transfer { + transfer_id server_transfer_id{0}; + transfer_id client_transfer_id{0}; + + ServerId server_id{0}; + ClientId client_id{0}; + ChannelId channel_id{0}; + + std::string transfer_key{}; + std::chrono::system_clock::time_point initialized_timestamp{}; + enum Direction { + DIRECTION_UNKNOWN, + DIRECTION_UPLOAD, + DIRECTION_DOWNLOAD + } direction{DIRECTION_UNKNOWN}; + + std::string transfer_hosts{}; /* comma separated list */ + uint16_t transfer_port{0}; + + enum TargetType { + TARGET_TYPE_UNKNOWN, + TARGET_TYPE_CHANNEL_FILE, + TARGET_TYPE_ICON, + TARGET_TYPE_AVATAR + } target_type{TARGET_TYPE_UNKNOWN}; + std::string target_file_path{}; + + int64_t max_bandwidth{-1}; + size_t expected_file_size{0}; + size_t file_offset{0}; + }; + + struct TransferStatistics { + uint64_t bytes_send{0}; + uint64_t bytes_received{0}; + + uint64_t delta_bytes_send{0}; + uint64_t delta_bytes_received{0}; + + size_t current_offset{0}; + size_t total_size{0}; + }; + + struct TransferInitError { + enum Type { + UNKNOWN + } error_type{UNKNOWN}; + std::string error_message{}; + }; + + struct TransferActionError { + enum Type { + UNKNOWN, + + UNKNOWN_TRANSFER + } error_type{UNKNOWN}; + std::string error_message{}; + }; + + struct TransferError { + enum Type { + UNKNOWN, + DISK_IO_ERROR, + NETWORK_IO_ERROR, + + UNEXPECTED_CLIENT_EOF + } error_type{UNKNOWN}; + std::string error_message{}; + }; + + class AbstractProvider { + public: + struct TransferInfo { + std::string file_path{}; + + bool override_exiting{false}; /* only for upload valid */ + size_t file_offset{0}; + size_t expected_file_size{0}; + int64_t max_bandwidth{-1}; + }; + + virtual std::shared_ptr>> initialize_channel_transfer(Transfer::Direction /* direction */, ServerId /* server */, ChannelId /* channel */, const TransferInfo& /* info */) = 0; + virtual std::shared_ptr>> initialize_icon_transfer(Transfer::Direction /* direction */, ServerId /* server */, const TransferInfo& /* info */) = 0; + virtual std::shared_ptr>> initialize_avatar_transfer(Transfer::Direction /* direction */, ServerId /* server */, const TransferInfo& /* info */) = 0; + + virtual std::shared_ptr> stop_transfer(transfer_id) = 0; + + std::function&)> callback_transfer_timeout{}; /* client has not connected to that transfer */ + std::function&)> callback_transfer_started{}; /* transfer has been started */ + std::function&)> callback_transfer_finished{}; /* transfer has been finished */ + std::function&, const TransferError&)> callback_transfer_aborted{}; /* an error happened while transfering the data */ + std::function&, const TransferStatistics&)> callback_transfer_statistics{}; + private: + }; + } + + class AbstractFileServer { + public: + [[nodiscard]] virtual filesystem::AbstractProvider& file_system() = 0; + private: + }; + + extern bool initialize(std::string& /* error */); + extern void finalize(); + + extern std::shared_ptr server(); +} \ No newline at end of file diff --git a/file/local_server/LocalFileServer.cpp b/file/local_server/LocalFileServer.cpp new file mode 100644 index 0000000..8d77270 --- /dev/null +++ b/file/local_server/LocalFileServer.cpp @@ -0,0 +1,45 @@ +// +// Created by WolverinDEV on 28/04/2020. +// + +#include "./LocalFileServer.h" + +using namespace ts::server; +using LocalFileServer = file::LocalFileServer; + +std::shared_ptr server_instance{}; +bool file::initialize(std::string &error) { + server_instance = std::make_shared(); + if(!server_instance->initialize(error)) { + server_instance = nullptr; + return false; + } + return true; +} + +void file::finalize() { + auto server = std::exchange(server_instance, nullptr); + if(!server) return; + + server->finalize(); +} + +std::shared_ptr file::server() { + return server_instance; +} + +LocalFileServer::~LocalFileServer() {} + +bool LocalFileServer::initialize(std::string &error) { + if(!this->file_system_.initialize(error, "file-root/")) + return false; + return true; +} + +void LocalFileServer::finalize() { + +} + +file::filesystem::AbstractProvider &LocalFileServer::file_system() { + return this->file_system_; +} diff --git a/file/local_server/LocalFileServer.h b/file/local_server/LocalFileServer.h new file mode 100644 index 0000000..882f504 --- /dev/null +++ b/file/local_server/LocalFileServer.h @@ -0,0 +1,93 @@ +#pragma once + + +#include +#include + +namespace ts::server::file { + namespace filesystem { +#ifdef FS_INCLUDED + namespace fs = std::experimental::filesystem; +#endif + + class LocalFileSystem : public filesystem::AbstractProvider { + using FileModifyError = filesystem::FileModifyError; + using DirectoryModifyError = filesystem::DirectoryModifyError; + public: + virtual ~LocalFileSystem(); + + bool initialize(std::string & /* error */, const std::string & /* root path */); + void finalize(); + + void lock_file(const std::string& /* path */); + void unlock_file(const std::string& /* path */); + + [[nodiscard]] inline const auto &root_path() const { return this->root_path_; } + + std::shared_ptr> initialize_server(ServerId /* server */) override; + std::shared_ptr> delete_server(ServerId /* server */) override; + + std::shared_ptr + query_channel_directory(ServerId id, ChannelId channelId, const std::string &string) override; + + std::shared_ptr> + create_channel_directory(ServerId id, ChannelId channelId, const std::string &string) override; + + std::shared_ptr> + delete_channel_file(ServerId id, ChannelId channelId, const std::string &string) override; + + std::shared_ptr> + rename_channel_file(ServerId id, ChannelId channelId, const std::string &, const std::string &) override; + + std::shared_ptr query_icon_directory(ServerId id) override; + + std::shared_ptr> + delete_icon(ServerId id, const std::string &string) override; + + std::shared_ptr query_avatar_directory(ServerId id) override; + + std::shared_ptr> + delete_avatar(ServerId id, const std::string &string) override; + + private: +#ifdef FS_INCLUDED + [[nodiscard]] fs::path server_path(ServerId); + [[nodiscard]] fs::path server_channel_path(ServerId, ChannelId); + [[nodiscard]] static bool exceeds_base_path(const fs::path& /* base */, const fs::path& /* target */); + [[nodiscard]] bool is_any_file_locked(const fs::path& /* base */, const std::string& /* path */, std::string& /* file (relative to the base) */); + + [[nodiscard]] std::shared_ptr> + delete_file(const fs::path& /* base */, const std::string &string); + + [[nodiscard]] std::shared_ptr + query_directory(const fs::path& /* base */, const std::string &string, bool); +#endif + + 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{}; + + std::string root_path_{}; + + std::mutex locked_files_mutex{}; + std::deque locked_files_{}; + }; + } + + class LocalFileServer : public AbstractFileServer { + public: + virtual ~LocalFileServer(); + + [[nodiscard]] bool initialize(std::string& /* error */); + void finalize(); + + filesystem::AbstractProvider &file_system() override; + + private: + filesystem::LocalFileSystem file_system_{}; + }; +} \ No newline at end of file diff --git a/file/local_server/LocalFileSystem.cpp b/file/local_server/LocalFileSystem.cpp new file mode 100644 index 0000000..cc5285f --- /dev/null +++ b/file/local_server/LocalFileSystem.cpp @@ -0,0 +1,309 @@ +// +// Created by WolverinDEV on 29/04/2020. +// +#include +#define FS_INCLUDED + +#include +#include "./LocalFileServer.h" +#include "clnpath.h" + +using namespace ts::server::file; +using namespace ts::server::file::filesystem; +namespace fs = std::experimental::filesystem; +using directory_query_response_t = AbstractProvider::directory_query_response_t; + +LocalFileSystem::~LocalFileSystem() = default; + +bool LocalFileSystem::initialize(std::string &error_message, const std::string &root_path_string) { + auto root_path = fs::u8path(root_path_string); + std::error_code error{}; + + if(!fs::exists(root_path, error)) { + if(error) + logWarning(0, "Failed to check root path existence. Assuming it does not exist. ({}/{})", error.value(), error.message()); + if(!fs::create_directories(root_path, error) || error) { + error_message = "Failed to create root file system at " + root_path_string + ": " + std::to_string(error.value()) + "/" + error.message(); + return false; + } + } + + auto croot = clnpath(fs::absolute(root_path).string()); + logMessage(0, "Started file system root at {}", croot); + this->root_path_ = croot; + return true; +} + +void LocalFileSystem::finalize() {} + +fs::path LocalFileSystem::server_path(ts::ServerId id) { + return fs::u8path(this->root_path_) / fs::u8path("server_" + std::to_string(id)); +} + +fs::path LocalFileSystem::server_channel_path(ts::ServerId sid, ts::ChannelId cid) { + return this->server_path(sid) / fs::u8path("channel_" + std::to_string(cid)); +} + +bool LocalFileSystem::exceeds_base_path(const fs::path &base, const fs::path &target) { + auto rel_target = clnpath(target.string()); + if(rel_target.starts_with("..")) return true; + + auto base_string = clnpath(fs::absolute(base).string()); + auto target_string = clnpath(fs::absolute(target).string()); + return !target_string.starts_with(base_string); +} + +bool LocalFileSystem::is_any_file_locked(const fs::path &base, const std::string &path, std::string &locked_file) { + auto c_path = clnpath(fs::absolute(base / fs::u8path(path)).string()); + + for(const auto& lfile : this->locked_files_) { + if(lfile.starts_with(c_path)) { + locked_file = lfile.substr(base.string().length()); + return true; + } + } + + return false; +} + +std::shared_ptr> LocalFileSystem::initialize_server(ServerId id) { + auto path = this->server_path(id); + std::error_code error{}; + + auto response = this->create_execute_response(); + + if(!fs::exists(path, error)) { + if(!fs::create_directories(path, error) || error) { + response->emplace_fail(ServerCommandErrorType::FAILED_TO_CREATE_DIRECTORIES, std::to_string(error.value()) + "/" + error.message()); + return response; + } + } + + //TODO: Copy the default icon + + response->emplace_success(); + return response; +} + +std::shared_ptr> LocalFileSystem::delete_server(ServerId id) { + auto path = this->server_path(id); + std::error_code error{}; + + auto response = this->create_execute_response(); + + //TODO: Stop all running file transfers! + + if(fs::exists(path, error)) { + if(!fs::remove_all(path, error) || error) { + response->emplace_fail(ServerCommandErrorType::FAILED_TO_DELETE_DIRECTORIES, std::to_string(error.value()) + "/" + error.message()); + return response; + } + } + + response->emplace_success(); + return response; +} + +std::shared_ptr LocalFileSystem::query_directory(const fs::path &base, + const std::string &path, + bool allow_non_existance) { + std::error_code error{}; + auto response = this->create_execute_response>(); + auto target_path = base / fs::u8path(path); + if(this->exceeds_base_path(base, target_path)) { + response->emplace_fail(DirectoryQueryErrorType::PATH_EXCEEDS_ROOT_PATH, ""); + return response; + } + + if(!fs::exists(target_path, error)) { + if(allow_non_existance) + response->emplace_success(); + else + response->emplace_fail(DirectoryQueryErrorType::PATH_DOES_NOT_EXISTS, ""); + return response; + } else if(error) { + logWarning(0, "Failed to check for file at {}: {}. Assuming it does not exists.", target_path.string(), error.value(), error.message()); + response->emplace_fail(DirectoryQueryErrorType::PATH_DOES_NOT_EXISTS, ""); + return response; + } + + if(!fs::is_directory(target_path, error)) { + response->emplace_fail(DirectoryQueryErrorType::PATH_IS_A_FILE, ""); + return response; + } else if(error) { + logWarning(0, "Failed to check for directory at {}: {}. Assuming its not a directory.", target_path.string(), error.value(), error.message()); + response->emplace_fail(DirectoryQueryErrorType::PATH_IS_A_FILE, ""); + return response; + } + + std::deque entries{}; + for(auto& entry : fs::directory_iterator(target_path, error)) { + auto status = entry.status(error); + if(error) { + logWarning(0, "Failed to query file status for {} ({}/{}). Skipping entry for directory query.", entry.path().string(), error.value(), error.message()); + continue; + } + + if(status.type() == fs::file_type::directory) { + auto& dentry = entries.emplace_back(); + dentry.type = DirectoryEntry::DIRECTORY; + dentry.name = entry.path().filename(); + + dentry.modified_at = fs::last_write_time(entry.path(), error); + if(error) + logWarning(0, "Failed to query last write time for directory {} ({}/{})", entry.path().string(), error.value(), error.message()); + dentry.size = 0; + } else if(status.type() == fs::file_type::regular) { + auto& dentry = entries.emplace_back(); + dentry.type = DirectoryEntry::FILE; + dentry.name = entry.path().filename(); + + dentry.modified_at = fs::last_write_time(entry.path(), error); + if(error) + logWarning(0, "Failed to query last write time for file {} ({}/{}).", entry.path().string(), error.value(), error.message()); + dentry.size = fs::file_size(entry.path(), error); + if(error) + logWarning(0, "Failed to query size for file {} ({}/{}).", entry.path().string(), error.value(), error.message()); + } else { + logWarning(0, "Directory query listed an unknown file type for file {} ({}).", entry.path().string(), (int) status.type()); + } + } + if(error && entries.empty()) { + response->emplace_fail(DirectoryQueryErrorType::FAILED_TO_LIST_FILES, std::to_string(error.value()) + "/" + error.message()); + return response; + } + response->emplace_success(std::forward(entries)); + return response; +} + +std::shared_ptr LocalFileSystem::query_icon_directory(ServerId id) { + return this->query_directory(this->server_path(id) / fs::u8path("icons"), "/", true); +} + +std::shared_ptr LocalFileSystem::query_avatar_directory(ServerId id) { + return this->query_directory(this->server_path(id) / fs::u8path("avatars"), "/", true); +} + +std::shared_ptr LocalFileSystem::query_channel_directory(ServerId id, ChannelId channelId, const std::string &path) { + return this->query_directory(this->server_channel_path(id, channelId), path, false); +} + +std::shared_ptr> LocalFileSystem::create_channel_directory(ServerId id, ChannelId channelId, const std::string &path) { + auto channel_path_root = this->server_channel_path(id, channelId); + std::error_code error{}; + + auto response = this->create_execute_response(); + auto target_path = channel_path_root / fs::u8path(path); + if(this->exceeds_base_path(channel_path_root, target_path)) { + response->emplace_fail(DirectoryModifyErrorType::PATH_EXCEEDS_ROOT_PATH, ""); + return response; + } + + if(fs::exists(target_path, error)) { + response->emplace_fail(DirectoryModifyErrorType::PATH_ALREADY_EXISTS, ""); + return response; + } else if(error) { + logWarning(0, "Failed to check for file at {}: {}. Assuming it does not exists.", target_path.string(), error.value(), error.message()); + } + + if(!fs::create_directories(target_path, error) || error) { + response->emplace_fail(DirectoryModifyErrorType::FAILED_TO_CREATE_DIRECTORIES, std::to_string(error.value()) + "/" + error.message()); + return response; + } + + response->emplace_success(); + return response; +} + +std::shared_ptr> LocalFileSystem::rename_channel_file(ServerId id, ChannelId channelId, const std::string ¤t_path_string, const std::string &new_path_string) { + auto channel_path_root = this->server_channel_path(id, channelId); + std::error_code error{}; + std::string locked_file{}; + + auto response = this->create_execute_response(); + auto current_path = channel_path_root / fs::u8path(current_path_string); + auto target_path = channel_path_root / fs::u8path(new_path_string); + + if(this->exceeds_base_path(channel_path_root, current_path)) { + response->emplace_fail(FileModifyErrorType::PATH_EXCEEDS_ROOT_PATH, ""); + return response; + } + if(this->exceeds_base_path(channel_path_root, target_path)) { + response->emplace_fail(FileModifyErrorType::TARGET_PATH_EXCEEDS_ROOT_PATH, ""); + return response; + } + + if(!fs::exists(current_path, error)) { + response->emplace_fail(FileModifyErrorType::PATH_DOES_NOT_EXISTS, ""); + return response; + } else if(error) { + logWarning(0, "Failed to check for file at {}: {}. Assuming it does not exists.", current_path.string(), error.value(), error.message()); + response->emplace_fail(FileModifyErrorType::PATH_DOES_NOT_EXISTS, ""); + return response; + } + + if(fs::exists(target_path, error)) { + response->emplace_fail(FileModifyErrorType::TARGET_PATH_ALREADY_EXISTS, ""); + return response; + } else if(error) { + logWarning(0, "Failed to check for file at {}: {}. Assuming it does exists.", current_path.string(), error.value(), error.message()); + response->emplace_fail(FileModifyErrorType::TARGET_PATH_ALREADY_EXISTS, ""); + return response; + } + + if(this->is_any_file_locked(channel_path_root, current_path, locked_file)) { + response->emplace_fail(FileModifyErrorType::SOME_FILES_ARE_LOCKED, locked_file); + return response; + } + + fs::rename(current_path, target_path, error); + if(error) { + response->emplace_fail(FileModifyErrorType::FAILED_TO_RENAME_FILE, std::to_string(error.value()) + "/" + error.message()); + return response; + } + + response->emplace_success(); + return response; +} + +std::shared_ptr> LocalFileSystem::delete_file(const fs::path &base, + const std::string &path) { + std::error_code error{}; + std::string locked_file{}; + auto response = this->create_execute_response(); + auto target_path = base / fs::u8path(path); + + if(fs::exists(target_path, error)) { + response->emplace_fail(FileModifyErrorType::TARGET_PATH_ALREADY_EXISTS, ""); + return response; + } else if(error) { + logWarning(0, "Failed to check for file at {}: {}. Assuming it does exists.", target_path.string(), error.value(), error.message()); + response->emplace_fail(FileModifyErrorType::TARGET_PATH_ALREADY_EXISTS, ""); + return response; + } + + if(this->is_any_file_locked(base, path, locked_file)) { + response->emplace_fail(FileModifyErrorType::SOME_FILES_ARE_LOCKED, locked_file); + return response; + } + + if(!fs::remove(target_path, error) || error) { + response->emplace_fail(FileModifyErrorType::FAILED_TO_DELETE_FILES, std::to_string(error.value()) + "/" + error.message()); + return response; + } + + response->emplace_success(); + return response; +} + +std::shared_ptr> LocalFileSystem::delete_channel_file(ServerId id, ChannelId channelId, const std::string &path) { + return this->delete_file(this->server_channel_path(id, channelId), path); +} + +std::shared_ptr> LocalFileSystem::delete_icon(ServerId id, const std::string &icon) { + return this->delete_file(this->server_path(id) / fs::u8path("icons"), icon); +} + +std::shared_ptr> LocalFileSystem::delete_avatar(ServerId id, const std::string &avatar) { + return this->delete_file(this->server_path(id) / fs::u8path("avatars"), avatar); +} \ No newline at end of file diff --git a/file/local_server/clnpath.cpp b/file/local_server/clnpath.cpp new file mode 100644 index 0000000..f5a3792 --- /dev/null +++ b/file/local_server/clnpath.cpp @@ -0,0 +1,283 @@ +// +// Created by WolverinDEV on 29/04/2020. +// + +#include "clnpath.h" +#include + +#define MAX_PATH_ELEMENTS 128 /* Number of levels of directory */ +void ts_clnpath(char *path) +{ + char *src; + char *dst; + char c; + int slash = 0; + + /* Convert multiple adjacent slashes to single slash */ + src = dst = path; + while ((c = *dst++ = *src++) != '\0') + { + if (c == '/') + { + slash = 1; + while (*src == '/') + src++; + } + } + + if (slash == 0) + return; + + /* Remove "./" from "./xxx" but leave "./" alone. */ + /* Remove "/." from "xxx/." but reduce "/." to "/". */ + /* Reduce "xxx/./yyy" to "xxx/yyy" */ + src = dst = (*path == '/') ? path + 1 : path; + while (src[0] == '.' && src[1] == '/' && src[2] != '\0') + src += 2; + while ((c = *dst++ = *src++) != '\0') + { + if (c == '/' && src[0] == '.' && (src[1] == '\0' || src[1] == '/')) + { + src++; + dst--; + } + } + if (path[0] == '/' && path[1] == '.' && + (path[2] == '\0' || (path[2] == '/' && path[3] == '\0'))) + path[1] = '\0'; + + /* Remove trailing slash, if any. There is at most one! */ + /* dst is pointing one beyond terminating null */ + if ((dst -= 2) > path && *dst == '/') + *dst++ = '\0'; +} + +bool ts_strequal(const char* a, const char* b) { + return strcmp(a, b) == 0; +} + +int ts_tokenise(char* ostring, const char* del, char** result, int max_tokens) { + int num_tokens{0}; + + char* token, *string, *tofree; + tofree = string = strdup(ostring); + while ((token = strsep(&string, del)) != nullptr) { + result[num_tokens++] = strdup(token); + if(num_tokens > max_tokens) + break; + } + + free(tofree); + return num_tokens; +} + +/* +** clnpath2() is not part of the basic clnpath() function because it can +** change the meaning of a path name if there are symbolic links on the +** system. For example, suppose /usr/tmp is a symbolic link to /var/tmp. +** If the user supplies /usr/tmp/../abcdef as the directory name, clnpath +** would transform that to /usr/abcdef, not to /var/abcdef which is what +** the kernel would interpret it as. +*/ +void ts_clnpath2(char *path) +{ + char *token[MAX_PATH_ELEMENTS], *otoken[MAX_PATH_ELEMENTS]; + int ntok, ontok; + + ts_clnpath(path); + + /* Reduce "/.." to "/" */ + ntok = ontok = ts_tokenise(path, "/", otoken, MAX_PATH_ELEMENTS); + memcpy(token, otoken, sizeof(char*) * ntok); + + if (ntok > 1) { + for (int i = 0; i < ntok - 1; i++) + { + if (!ts_strequal(token[i], "..") && ts_strequal(token[i + 1], "..")) + { + if (*token[i] == '\0') + continue; + while (i < ntok - 1) + { + token[i] = token[i + 2]; + i++; + } + ntok -= 2; + i = -1; /* Restart enclosing for loop */ + } + } + } + + /* Reassemble string */ + char *dst = path; + if (ntok == 0) + { + *dst++ = '.'; + *dst = '\0'; + } + else + { + if (token[0][0] == '\0') + { + int i; + for (i = 1; i < ntok && ts_strequal(token[i], ".."); i++) + ; + if (i > 1) + { + int j; + for (j = 1; i < ntok; i++) + token[j++] = token[i]; + ntok = j; + } + } + if (ntok == 1 && token[0][0] == '\0') + { + *dst++ = '/'; + *dst = '\0'; + } + else + { + for (int i = 0; i < ntok; i++) + { + char *src = token[i]; + while ((*dst++ = *src++) != '\0') + ; + *(dst - 1) = '/'; + } + *(dst - 1) = '\0'; + } + } + + for(int i{0}; i < ontok; i++) + ::free(otoken[i]); +} + +std::string clnpath(const std::string_view& data) { + std::string result{data}; + ts_clnpath2(result.data()); + auto index = result.find((char) 0); + if(index != std::string::npos) + result.resize(index); + return result; +} + +#ifdef CLN_EXEC +typedef struct p1_test_case +{ + const char *input; + const char *output; +} p1_test_case; + +/* This stress tests the cleaning, concentrating on the boundaries. */ +static const p1_test_case p1_tests[] = + { + { "/", "/", }, + { "//", "/", }, + { "///", "/", }, + { "/.", "/", }, + { "/./", "/", }, + { "/./.", "/", }, + { "/././.profile", "/.profile", }, + { "./", ".", }, + { "./.", ".", }, + { "././", ".", }, + { "./././.profile", ".profile", }, + { "abc/.", "abc", }, + { "abc/./def", "abc/def", }, + { "./abc", "abc", }, + + { "//abcd///./abcd////", "/abcd/abcd", }, + { "//abcd///././../defg///ddd//.", "/abcd/../defg/ddd", }, + { "/abcd/./../././defg/./././ddd", "/abcd/../defg/ddd", }, + { "//abcd//././../defg///ddd//.///", "/abcd/../defg/ddd", }, + + /* Most of these are minimal interest in phase 1 */ + { "/usr/tmp/clnpath.c", "/usr/tmp/clnpath.c", }, + { "/usr/tmp/", "/usr/tmp", }, + { "/bin/..", "/bin/..", }, + { "bin/..", "bin/..", }, + { "/bin/.", "/bin", }, + { "sub/directory", "sub/directory", }, + { "sub/directory/file", "sub/directory/file", }, + { "/part1/part2/../.././../", "/part1/part2/../../..", }, + { "/.././../usr//.//bin/./cc", "/../../usr/bin/cc", }, + }; + +static void p1_tester(const void *data) +{ + const p1_test_case *test = (const p1_test_case *)data; + char buffer[256]; + + strcpy(buffer, test->input); + ts_clnpath(buffer); + if (strcmp(buffer, test->output) == 0) + printf("<<%s>> cleans to <<%s>>\n", test->input, buffer); + else + { + fprintf(stderr, "<<%s>> - unexpected output from clnpath()\n", test->input); + fprintf(stderr, "Wanted <<%s>>\n", test->output); + fprintf(stderr, "Actual <<%s>>\n", buffer); + } +} + +typedef struct p2_test_case +{ + const char *input; + const char *output; +} p2_test_case; + +static const p2_test_case p2_tests[] = +{ + { "/abcd/../defg/ddd", "/defg/ddd" }, + { "/bin/..", "/" }, + { "bin/..", "." }, + { "/usr/bin/..", "/usr" }, + { "/usr/bin/../..", "/" }, + { "usr/bin/../..", "." }, + { "../part/of/../the/way", "../part/the/way" }, + { "/../part/of/../the/way", "/part/the/way" }, + { "part1/part2/../../part3", "part3" }, + { "part1/part2/../../../part3", "../part3" }, + { "/part1/part2/../../../part3", "/part3" }, + { "/part1/part2/../../../", "/" }, + { "/../../usr/bin/cc", "/usr/bin/cc" }, + { "../../usr/bin/cc", "../../usr/bin/cc" }, + { "part1/./part2/../../part3", "part3" }, + { "./part1/part2/../../../part3", "../part3" }, + { "/part1/part2/.././../../part3", "/part3" }, + { "/part1/part2/../.././../", "/" }, + { "/.././..//./usr///bin/cc/", "/usr/bin/cc" }, + {nullptr, nullptr} +}; + +static void p2_tester(const void *data) +{ + auto test = (const p2_test_case *)data; + char buffer[256]; + + strcpy(buffer, test->input); + ts_clnpath2(buffer); + if (strcmp(buffer, test->output) == 0) + printf("<<%s>> cleans to <<%s>>\n", test->input, buffer); + else + { + fprintf(stderr, "<<%s>> - unexpected output from clnpath2()\n", test->input); + fprintf(stderr, "Wanted <<%s>>\n", test->output); + fprintf(stderr, "Actual <<%s>>\n", buffer); + } +} + +int main() { + for(const auto& test : p1_tests) { + if(!test.input) break; + p1_tester(&test); + } + + printf("------------------------------\n"); + for(const auto& test : p2_tests) { + if(!test.input) break; + p2_tester(&test); + } +} + +#endif \ No newline at end of file diff --git a/file/local_server/clnpath.h b/file/local_server/clnpath.h new file mode 100644 index 0000000..eb59681 --- /dev/null +++ b/file/local_server/clnpath.h @@ -0,0 +1,6 @@ +#pragma once + +#include +#include + +extern std::string clnpath(const std::string_view&); \ No newline at end of file diff --git a/file/test/main.cpp b/file/test/main.cpp new file mode 100644 index 0000000..f71d4c3 --- /dev/null +++ b/file/test/main.cpp @@ -0,0 +1,125 @@ +// +// Created by WolverinDEV on 29/04/2020. +// + +#include +#include + +#include +#include + +namespace fs = std::experimental::filesystem; + +using namespace ts::server; + +struct Nothing {}; + +template +inline void print_response(const std::string& message, const std::shared_ptr, ResponseType>>& response) { + if(response->status == file::ExecuteStatus::ERROR) + logError(0, "{}: {} => {}", message, (int) response->error().error_type, response->error().error_message); + else if(response->status == file::ExecuteStatus::SUCCESS) + logMessage(0, "{}: success", message); + else + logWarning(0, "Unknown response state ({})!", (int) response->status); +} + +inline void print_query(const std::string& message, const file::filesystem::AbstractProvider::directory_query_response_t& response) { + if(response.status == file::ExecuteStatus::ERROR) + logError(0, "{}: {} => {}", message, (int) response.error().error_type, response.error().error_message); + else if(response.status == file::ExecuteStatus::SUCCESS) { + const auto& entries = response.response(); + logMessage(0, "{}: Found {} entries", message, entries.size()); + for(auto& entry : entries) { + if(entry.type == file::filesystem::DirectoryEntry::FILE) + logMessage(0, " - File {}", entry.name); + else if(entry.type == file::filesystem::DirectoryEntry::DIRECTORY) + logMessage(0, " - Directory {}", entry.name); + else + logMessage(0, " - Unknown {}", entry.name); + logMessage(0, " Write timestamp: {}", std::chrono::floor(entry.modified_at.time_since_epoch()).count()); + logMessage(0, " Size: {}", entry.size); + } + } else + logWarning(0, "{}: Unknown response state ({})!", message, (int) response.status); +} + +int main() { + std::string error{}; + if(!file::initialize(error)) { + logError(0, "Failed to initialize file server: {}", error); + return 0; + } + logMessage(0, "File server started"); + auto instance = file::server(); + auto& fs = instance->file_system(); + + { + auto response = fs.initialize_server(0); + response->wait(); + print_response("Server init result", response); + if(response->status != file::ExecuteStatus::SUCCESS) + return 0; + } + + + { + auto response = fs.create_channel_directory(0, 2, "/"); + response->wait(); + print_response("Channel dir create A", response); + } + + + { + auto response = fs.create_channel_directory(0, 2, "/test-folder/"); + response->wait(); + print_response("Channel dir create B", response); + } + + + { + auto response = fs.create_channel_directory(0, 2, "../test-folder/"); + response->wait(); + print_response("Channel dir create C", response); + } + + + { + auto response = fs.create_channel_directory(0, 2, "./test-folder/../test-folder-2"); + response->wait(); + print_response("Channel dir create D", response); + } + + { + auto response = fs.query_channel_directory(0, 2, "/"); + response->wait(); + print_query("Channel query", *response); + } + + { + auto response = fs.query_icon_directory(0); + response->wait(); + print_query("Icons", *response); + } + + { + auto response = fs.query_avatar_directory(0); + response->wait(); + print_query("Avatars", *response); + } + + { + auto response = fs.rename_channel_file(0, 2, "./test-folder/../test-folder-2", "./test-folder/../test-folder-3"); + response->wait(); + print_response("Folder rename A", response); + } + + { + auto response = fs.rename_channel_file(0, 2, "./test-folder/../test-folder-3", "./test-folder/../test-folder-2"); + response->wait(); + print_response("Folder rename B", response); + } + + //TODO: Test file locking + return 0; +} \ No newline at end of file diff --git a/server/CMakeLists.txt b/server/CMakeLists.txt index ce31097..1b58393 100644 --- a/server/CMakeLists.txt +++ b/server/CMakeLists.txt @@ -65,7 +65,7 @@ set(SERVER_SOURCE_FILES src/client/ConnectedClientNotifyHandler.cpp src/VirtualServerManager.cpp - src/server/file/FileServer.cpp + src/server/file/LocalFileServer.cpp src/channel/ServerChannel.cpp src/channel/ClientChannelView.cpp src/client/file/FileClient.cpp @@ -82,8 +82,6 @@ set(SERVER_SOURCE_FILES src/client/query/QueryClientCommands.cpp src/client/query/QueryClientNotify.cpp - src/file/FileServer.cpp - src/manager/IpListManager.cpp src/ConnectionStatistics.cpp diff --git a/server/main.cpp b/server/main.cpp index bed9611..6713bd5 100644 --- a/server/main.cpp +++ b/server/main.cpp @@ -10,7 +10,7 @@ #include "src/VirtualServer.h" #include "src/InstanceHandler.h" #include "src/server/QueryServer.h" -#include "src/server/file/FileServer.h" +#include "src/server/file/LocalFileServer.h" #include "src/terminal/CommandHandler.h" #include "src/client/InternalClient.h" #include "src/SignalHandler.h" diff --git a/server/src/Group.cpp b/server/src/Group.cpp index c1668c6..7042f45 100644 --- a/server/src/Group.cpp +++ b/server/src/Group.cpp @@ -6,7 +6,7 @@ #include "VirtualServer.h" #include "src/client/ConnectedClient.h" #include "InstanceHandler.h" -#include "src/server/file/FileServer.h" +#include "src/server/file/LocalFileServer.h" using namespace std; using namespace std::chrono; diff --git a/server/src/InstanceHandler.cpp b/server/src/InstanceHandler.cpp index f9e30be..ffbadf3 100644 --- a/server/src/InstanceHandler.cpp +++ b/server/src/InstanceHandler.cpp @@ -9,7 +9,7 @@ #include "InstanceHandler.h" #include "src/client/InternalClient.h" #include "src/server/QueryServer.h" -#include "src/server/file/FileServer.h" +#include "src/server/file/LocalFileServer.h" #include "SignalHandler.h" #include "src/manager/PermissionNameMapper.h" #include @@ -281,19 +281,19 @@ bool InstanceHandler::startInstance() { } this->loadWebCertificate(); - fileServer = new ts::server::FileServer(); + fileServer = new ts::server::LocalFileServer(); { auto bindings_string = this->properties()[property::SERVERINSTANCE_FILETRANSFER_HOST].as(); auto port = this->properties()[property::SERVERINSTANCE_FILETRANSFER_PORT].as(); auto ft_bindings = net::resolve_bindings(bindings_string, port); - deque> bindings; + deque> bindings; for(auto& binding : ft_bindings) { if(!get<2>(binding).empty()) { logError(LOG_FT, "Failed to resolve binding for {}: {}", get<0>(binding), get<2>(binding)); continue; } - auto entry = make_shared(); + auto entry = make_shared(); memcpy(&entry->address, &get<1>(binding), sizeof(sockaddr_storage)); entry->file_descriptor = -1; diff --git a/server/src/InstanceHandler.h b/server/src/InstanceHandler.h index 8f3569d..6a3e54c 100644 --- a/server/src/InstanceHandler.h +++ b/server/src/InstanceHandler.h @@ -42,7 +42,7 @@ namespace ts { std::shared_mutex& getChannelTreeLock() { return this->default_tree_lock; } VirtualServerManager* getVoiceServerManager(){ return this->voiceServerManager; } - FileServer* getFileServer(){ return fileServer; } + LocalFileServer* getFileServer(){ return fileServer; } QueryServer* getQueryServer(){ return queryServer; } DatabaseHelper* databaseHelper(){ return this->dbHelper; } BanManager* banManager(){ return this->banMgr; } @@ -110,7 +110,7 @@ namespace ts { std::chrono::system_clock::time_point memcleanTimestamp; SqlDataManager* sql; - FileServer* fileServer = nullptr; + LocalFileServer* fileServer = nullptr; QueryServer* queryServer = nullptr; VirtualServerManager* voiceServerManager = nullptr; DatabaseHelper* dbHelper = nullptr; diff --git a/server/src/InstanceHandlerSetup.cpp b/server/src/InstanceHandlerSetup.cpp index 5792473..d1f10d6 100644 --- a/server/src/InstanceHandlerSetup.cpp +++ b/server/src/InstanceHandlerSetup.cpp @@ -6,7 +6,7 @@ #include "InstanceHandler.h" #include "src/client/InternalClient.h" #include "src/server/QueryServer.h" -#include "src/server/file/FileServer.h" +#include "src/server/file/LocalFileServer.h" using namespace std; using namespace std::chrono; diff --git a/server/src/VirtualServer.cpp b/server/src/VirtualServer.cpp index ed47edb..8b13952 100644 --- a/server/src/VirtualServer.cpp +++ b/server/src/VirtualServer.cpp @@ -18,7 +18,7 @@ #include "./client/query/QueryClient.h" #include "music/MusicBotManager.h" #include "server/VoiceServer.h" -#include "server/file/FileServer.h" +#include "src/server/file/LocalFileServer.h" #include "server/QueryServer.h" #include "InstanceHandler.h" #include "Configuration.h" diff --git a/server/src/VirtualServer.h b/server/src/VirtualServer.h index a40b68d..505417f 100644 --- a/server/src/VirtualServer.h +++ b/server/src/VirtualServer.h @@ -58,7 +58,7 @@ namespace ts { class InstanceHandler; class VoiceServer; class QueryServer; - class FileServer; + class LocalFileServer; class SpeakingClient; class WebControlServer; diff --git a/server/src/VirtualServerManager.cpp b/server/src/VirtualServerManager.cpp index 2952bee..6941f9e 100644 --- a/server/src/VirtualServerManager.cpp +++ b/server/src/VirtualServerManager.cpp @@ -4,7 +4,7 @@ #include "src/server/VoiceServer.h" #include "src/client/query/QueryClient.h" #include "InstanceHandler.h" -#include "src/server/file/FileServer.h" +#include "src/server/file/LocalFileServer.h" #include "src/client/ConnectedClient.h" #include diff --git a/server/src/channel/ServerChannel.cpp b/server/src/channel/ServerChannel.cpp index 22b65fd..198ddbd 100644 --- a/server/src/channel/ServerChannel.cpp +++ b/server/src/channel/ServerChannel.cpp @@ -5,7 +5,7 @@ #include "misc/rnd.h" #include "src/VirtualServer.h" #include "src/client/ConnectedClient.h" -#include "src/server/file/FileServer.h" +#include "src/server/file/LocalFileServer.h" #include "src/InstanceHandler.h" #include "../manager/ConversationManager.h" diff --git a/server/src/client/ConnectedClient.cpp b/server/src/client/ConnectedClient.cpp index 9f3413b..84759aa 100644 --- a/server/src/client/ConnectedClient.cpp +++ b/server/src/client/ConnectedClient.cpp @@ -11,7 +11,7 @@ #include "src/VirtualServer.h" #include "voice/VoiceClient.h" #include "../server/VoiceServer.h" -#include "../server/file/FileServer.h" +#include "src/server/file/LocalFileServer.h" #include "../InstanceHandler.h" #include "ConnectedClient.h" diff --git a/server/src/client/ConnectedClientNotifyHandler.cpp b/server/src/client/ConnectedClientNotifyHandler.cpp index 20852a9..d289d5a 100644 --- a/server/src/client/ConnectedClientNotifyHandler.cpp +++ b/server/src/client/ConnectedClientNotifyHandler.cpp @@ -3,7 +3,7 @@ #include #include "ConnectedClient.h" #include "voice/VoiceClient.h" -#include "../server/file/FileServer.h" +#include "src/server/file/LocalFileServer.h" #include "../server/VoiceServer.h" #include "../InstanceHandler.h" #include "../server/QueryServer.h" diff --git a/server/src/client/DataClient.cpp b/server/src/client/DataClient.cpp index ee92d42..ea9e734 100644 --- a/server/src/client/DataClient.cpp +++ b/server/src/client/DataClient.cpp @@ -4,7 +4,7 @@ #include #include "DataClient.h" #include "ConnectedClient.h" -#include "src/server/file/FileServer.h" +#include "src/server/file/LocalFileServer.h" #include "src/InstanceHandler.h" #include "misc/base64.h" diff --git a/server/src/client/SpeakingClient.cpp b/server/src/client/SpeakingClient.cpp index cad681f..3eaf590 100644 --- a/server/src/client/SpeakingClient.cpp +++ b/server/src/client/SpeakingClient.cpp @@ -51,6 +51,14 @@ void SpeakingClient::handlePacketVoice(const pipes::buffer_view& data, bool head return; } +#if 0 + if(rand() % 10 == 0) { + logMessage(0, "Dropping audio packet"); + return; + } + logMessage(0, "Received voice: Head: {} Fragmented: {}, length: {}", head, fragmented, data.length()); +#endif + auto current_channel = this->currentChannel; if(!current_channel) { return; } if(!this->allowedToTalk) { return; } diff --git a/server/src/client/command_handler/channel.cpp b/server/src/client/command_handler/channel.cpp index 80efa1e..7682574 100644 --- a/server/src/client/command_handler/channel.cpp +++ b/server/src/client/command_handler/channel.cpp @@ -5,7 +5,7 @@ #include "../../build.h" #include "../ConnectedClient.h" #include "../InternalClient.h" -#include "../../server/file/FileServer.h" +#include "src/server/file/LocalFileServer.h" #include "../../server/VoiceServer.h" #include "../voice/VoiceClient.h" #include "PermissionManager.h" @@ -407,7 +407,7 @@ command_result ConnectedClient::handleCommandChannelGroupAddPerm(Command &cmd) { if (!channelGroup || channelGroup->target() != GROUPTARGET_CHANNEL) return command_result{error::parameter_invalid, "invalid channel group id"}; ACTION_REQUIRES_GROUP_PERMISSION(channelGroup, permission::i_channel_group_needed_modify_power, permission::i_channel_group_modify_power, true); - auto max_value = this->calculate_permission(permission::i_permission_modify_power, 0, true); + auto max_value = this->calculate_permission(permission::i_permission_modify_power, 0, false); if(!max_value.has_value) return command_result{permission::i_permission_modify_power}; auto ignore_granted_values = permission::v2::permission_granted(1, this->calculate_permission(permission::b_permission_modify_power_ignore, 0)); @@ -1479,7 +1479,7 @@ command_result ConnectedClient::handleCommandChannelAddPerm(Command &cmd) { ACTION_REQUIRES_CHANNEL_PERMISSION(channel, permission::i_channel_needed_permission_modify_power, permission::i_channel_permission_modify_power, true); - auto max_value = this->calculate_permission(permission::i_permission_modify_power, channel_id, true); + auto max_value = this->calculate_permission(permission::i_permission_modify_power, channel_id, false); if(!max_value.has_value) return command_result{permission::i_permission_modify_power}; auto ignore_granted_values = permission::v2::permission_granted(1, this->calculate_permission(permission::b_permission_modify_power_ignore, channel_id)); @@ -1796,7 +1796,7 @@ command_result ConnectedClient::handleCommandChannelClientAddPerm(Command &cmd) ACTION_REQUIRES_PERMISSION(permission::i_client_permission_modify_power, required_permissions, channel_id); } - auto max_value = this->calculate_permission(permission::i_permission_modify_power, channel_id, true); + auto max_value = this->calculate_permission(permission::i_permission_modify_power, channel_id, false); if(!max_value.has_value) return command_result{permission::i_permission_modify_power}; auto ignore_granted_values = permission::v2::permission_granted(1, this->calculate_permission(permission::b_permission_modify_power_ignore, channel_id)); diff --git a/server/src/client/command_handler/client.cpp b/server/src/client/command_handler/client.cpp index 091f4cb..aca7cec 100644 --- a/server/src/client/command_handler/client.cpp +++ b/server/src/client/command_handler/client.cpp @@ -6,7 +6,7 @@ #include "../../build.h" #include "../ConnectedClient.h" #include "../InternalClient.h" -#include "../../server/file/FileServer.h" +#include "src/server/file/LocalFileServer.h" #include "../voice/VoiceClient.h" #include "PermissionManager.h" #include "../../InstanceHandler.h" @@ -964,7 +964,7 @@ command_result ConnectedClient::handleCommandClientAddPerm(Command &cmd) { auto mgr = serverInstance->databaseHelper()->loadClientPermissionManager(this->server, cldbid); ACTION_REQUIRES_GLOBAL_PERMISSION(permission::i_client_permission_modify_power, this->server->calculate_permission(permission::i_client_needed_permission_modify_power, cldbid, ClientType::CLIENT_TEAMSPEAK, 0)); - auto max_value = this->calculate_permission(permission::i_permission_modify_power, 0, true); + auto max_value = this->calculate_permission(permission::i_permission_modify_power, 0, false); if(!max_value.has_value) return command_result{permission::i_permission_modify_power}; auto ignore_granted_values = permission::v2::permission_granted(1, this->calculate_permission(permission::b_permission_modify_power_ignore, 0)); diff --git a/server/src/client/command_handler/file.cpp b/server/src/client/command_handler/file.cpp index 91cbf15..a743519 100644 --- a/server/src/client/command_handler/file.cpp +++ b/server/src/client/command_handler/file.cpp @@ -13,7 +13,7 @@ #include "../../build.h" #include "../ConnectedClient.h" #include "../InternalClient.h" -#include "../../server/file/FileServer.h" +#include "src/server/file/LocalFileServer.h" #include "../../server/VoiceServer.h" #include "../voice/VoiceClient.h" #include "PermissionManager.h" @@ -229,13 +229,11 @@ command_result ConnectedClient::handleCommandFTInitUpload(Command &cmd) { directory = serverInstance->getFileServer()->avatarDirectory(this->server); cmd["name"] = "/avatar_" + this->getAvatarId(); } else { - cerr << "Unknown requested directory: " << cmd["path"].as() << endl; return command_result{error::not_implemented}; } } if (!directory || directory->type != file::FileType::DIRECTORY) { //Should not happen - cerr << "Invalid upload file path!" << endl; return command_result{error::file_invalid_path, "could not resolve directory"}; } diff --git a/server/src/client/command_handler/misc.cpp b/server/src/client/command_handler/misc.cpp index 38c4717..d98e01e 100644 --- a/server/src/client/command_handler/misc.cpp +++ b/server/src/client/command_handler/misc.cpp @@ -13,7 +13,7 @@ #include "../../build.h" #include "../ConnectedClient.h" #include "../InternalClient.h" -#include "../../server/file/FileServer.h" +#include "src/server/file/LocalFileServer.h" #include "../../server/VoiceServer.h" #include "../voice/VoiceClient.h" #include "PermissionManager.h" diff --git a/server/src/client/command_handler/music.cpp b/server/src/client/command_handler/music.cpp index 67ffeb5..18ebf07 100644 --- a/server/src/client/command_handler/music.cpp +++ b/server/src/client/command_handler/music.cpp @@ -13,7 +13,7 @@ #include "../../build.h" #include "../ConnectedClient.h" #include "../InternalClient.h" -#include "../../server/file/FileServer.h" +#include "src/server/file/LocalFileServer.h" #include "../../server/VoiceServer.h" #include "../voice/VoiceClient.h" #include "PermissionManager.h" diff --git a/server/src/client/command_handler/server.cpp b/server/src/client/command_handler/server.cpp index f673e83..4a57dfc 100644 --- a/server/src/client/command_handler/server.cpp +++ b/server/src/client/command_handler/server.cpp @@ -13,7 +13,7 @@ #include "../../build.h" #include "../ConnectedClient.h" #include "../InternalClient.h" -#include "../../server/file/FileServer.h" +#include "src/server/file/LocalFileServer.h" #include "../../server/VoiceServer.h" #include "../voice/VoiceClient.h" #include "PermissionManager.h" @@ -806,7 +806,7 @@ command_result ConnectedClient::handleCommandServerGroupAddPerm(Command &cmd) { } - auto max_value = this->calculate_permission(permission::i_permission_modify_power, 0, true); + auto max_value = this->calculate_permission(permission::i_permission_modify_power, 0, false); if(!max_value.has_value) return command_result{permission::i_permission_modify_power}; auto ignore_granted_values = permission::v2::permission_granted(1, this->calculate_permission(permission::b_permission_modify_power_ignore, 0)); @@ -970,7 +970,7 @@ command_result ConnectedClient::handleCommandServerGroupAutoAddPerm(ts::Command& if(groups.empty()) return command_result{error::ok}; - auto max_value = this->calculate_permission(permission::i_permission_modify_power, 0, true); + auto max_value = this->calculate_permission(permission::i_permission_modify_power, 0, false); if(!max_value.has_value) return command_result{permission::i_permission_modify_power}; auto ignore_granted_values = permission::v2::permission_granted(1, this->calculate_permission(permission::b_permission_modify_power_ignore, 0)); diff --git a/server/src/client/file/FileClient.cpp b/server/src/client/file/FileClient.cpp index 6d7eb6d..4f2d8ed 100644 --- a/server/src/client/file/FileClient.cpp +++ b/server/src/client/file/FileClient.cpp @@ -1,5 +1,5 @@ #include -#include +#include #include #include "FileClient.h" #include @@ -15,7 +15,7 @@ using namespace ts::server; namespace fs = std::experimental::filesystem; #define BUFFER_SIZE (size_t) 2048 -FileClient::FileClient(FileServer* handle, int socketFd) : handle(handle), clientFd(socketFd) { +FileClient::FileClient(LocalFileServer* handle, int socketFd) : handle(handle), clientFd(socketFd) { memtrack::allocated(this); this->last_io_action = system_clock::now(); diff --git a/server/src/client/file/FileClient.h b/server/src/client/file/FileClient.h index 07c1b19..e845d76 100644 --- a/server/src/client/file/FileClient.h +++ b/server/src/client/file/FileClient.h @@ -3,7 +3,7 @@ #include #include #include -#include +#include #include #include #include @@ -14,7 +14,7 @@ namespace ts { class ConnectedClient; class ConnectedClient; - class FileServer; + class LocalFileServer; enum FTType { Unknown, @@ -28,7 +28,7 @@ namespace ts { }; class FileClient { - friend class FileServer; + friend class LocalFileServer; public: enum TransferState { T_INITIALIZE, @@ -45,7 +45,7 @@ namespace ts { uint16_t length = 0; }; - FileClient(FileServer* handle, int socketFd); + FileClient(LocalFileServer* handle, int socketFd); ~FileClient(); void disconnect(std::chrono::milliseconds = std::chrono::milliseconds(5000)); @@ -83,7 +83,7 @@ namespace ts { void handle_ws_message(const pipes::WSMessage&); bool handle_ts_message(); private: - FileServer* handle; + LocalFileServer* handle; std::weak_ptr _this; std::recursive_mutex bandwidth_lock; diff --git a/server/src/client/file/FileClientIO.cpp b/server/src/client/file/FileClientIO.cpp index 746a728..5e47a2b 100644 --- a/server/src/client/file/FileClientIO.cpp +++ b/server/src/client/file/FileClientIO.cpp @@ -1,6 +1,6 @@ #include #include -#include +#include #include #include #include diff --git a/server/src/client/music/internal_provider/channel_replay/ChannelProvider.cpp b/server/src/client/music/internal_provider/channel_replay/ChannelProvider.cpp index 8b7bbff..d0480ea 100644 --- a/server/src/client/music/internal_provider/channel_replay/ChannelProvider.cpp +++ b/server/src/client/music/internal_provider/channel_replay/ChannelProvider.cpp @@ -1,7 +1,7 @@ #include "ChannelProvider.h" #include "../../MusicClient.h" #include "../../../../InstanceHandler.h" -#include "../../../../server/file/FileServer.h" +#include "src/server/file/LocalFileServer.h" #include "../../../../../../music/providers/ffmpeg/FFMpegProvider.h" diff --git a/server/src/client/voice/PrecomputedPuzzles.cpp b/server/src/client/voice/PrecomputedPuzzles.cpp index d7a920a..576682c 100644 --- a/server/src/client/voice/PrecomputedPuzzles.cpp +++ b/server/src/client/voice/PrecomputedPuzzles.cpp @@ -24,8 +24,20 @@ bool PuzzleManager::precompute_puzzles(size_t amount) { } std::shared_ptr PuzzleManager::next_puzzle() { - std::lock_guard lock{this->cache_lock}; - return this->cached_puzzles[this->cache_index++ % this->cached_puzzles.size()]; + { + std::lock_guard lock{this->cache_lock}; + auto it = this->cached_puzzles.begin() + (this->cache_index++ % this->cached_puzzles.size()); + if((*it)->fail_count > 2) { + this->cached_puzzles.erase(it); + } else { + return *it; + } + } + + std::random_device rd{}; + std::mt19937 mt{rd()}; + this->generate_puzzle(mt); + return this->next_puzzle(); } inline void random_number(std::mt19937& generator, mp_int *result, int length){ @@ -74,8 +86,18 @@ void PuzzleManager::generate_puzzle(std::mt19937& random_generator) { mp_init_multi(&puzzle->x, &puzzle->n, &puzzle->result, nullptr); generate_new: - random_number(random_generator, &puzzle->x, 64); - random_number(random_generator, &puzzle->n, 64); + static int x{0}; + if(x++ == 0 || true) { + mp_set(&puzzle->x, 1); + mp_set(&puzzle->n, 1); + //random_number(random_generator, &puzzle->x, 64); + //random_number(random_generator, &puzzle->n, 64); + } else { + const static std::string_view n_{"\x01\x7a\xc5\x8d\x28\x7a\x61\x58\xf6\xe3\x98\x60\x2f\x81\x9c\x8a\x48\xc9\x20\xd1\x59\xe0\x24\x75\x91\x27\x9f\x52\x1e\x2c\x24\x85\xa9\xdc\x74\xfa\x0b\x36\xf9\x6c\x77\xa3\x7c\xf9\xbb\xf7\x04\xad\xa3\x84\x0d\x97\x25\x54\x19\x72\x4f\x8f\xfc\x66\xbe\x41\xda\x95"}; + const static std::string_view x_{"\xd1\xef\xf0\x16\x34\x48\x56\x53\x15\x97\xa0\x28\xbd\x13\xce\xbf\xc2\xd6\x79\x9d\x21\x81\x83\x37\x8c\xe8\xee\xee\xa1\x22\xa4\xf5\x63\x33\x53\x0c\x38\x2f\x0a\x00\x53\x20\xc7\x93\x52\xa9\xd0\xc2\xfb\xbc\xc5\xc4\xc3\x54\xad\xcb\x49\x52\xc0\xd8\x97\x32\x94\xee"}; + mp_read_unsigned_bin(&puzzle->x, (unsigned char*) x_.data(), x_.length()); + mp_read_unsigned_bin(&puzzle->n, (unsigned char*) n_.data(), n_.length()); + } if(!solve_puzzle(puzzle)) goto generate_new; diff --git a/server/src/client/voice/PrecomputedPuzzles.h b/server/src/client/voice/PrecomputedPuzzles.h index e1ade86..0272c05 100644 --- a/server/src/client/voice/PrecomputedPuzzles.h +++ b/server/src/client/voice/PrecomputedPuzzles.h @@ -8,15 +8,17 @@ namespace ts::server::udp { struct Puzzle { - mp_int x; - mp_int n; - int level; + mp_int x{}; + mp_int n{}; + int level{0}; - mp_int result; + mp_int result{}; - uint8_t data_x[64]; - uint8_t data_n[64]; - uint8_t data_result[64]; + uint8_t data_x[64]{0}; + uint8_t data_n[64]{0}; + uint8_t data_result[64]{0}; + + size_t fail_count{0}; }; class PuzzleManager { diff --git a/server/src/file/FileServer.cpp b/server/src/file/FileServer.cpp deleted file mode 100644 index 446f074..0000000 --- a/server/src/file/FileServer.cpp +++ /dev/null @@ -1,5 +0,0 @@ -// -// Created by WolverinDEV on 28/04/2020. -// - -#include "FileServer.h" diff --git a/server/src/file/FileServer.h b/server/src/file/FileServer.h deleted file mode 100644 index cfc9f03..0000000 --- a/server/src/file/FileServer.h +++ /dev/null @@ -1,124 +0,0 @@ -#pragma once - -#include -#include -#include -#include -#include -#include - -namespace ts::server::file { - enum struct ExecuteStatus { - UNKNOWN, - WAITING, - SUCCESS, - ERROR - }; - - template - class ExecuteResponse { - public: - ExecuteStatus status{ExecuteStatus::UNKNOWN}; - - [[nodiscard]] inline const response_t& response() const { return std::get(this->response_); } - [[nodiscard]] inline const response_t& error() const { return std::get(this->response_); } - - inline void wait() const { - std::unique_lock nlock{this->notify_mutex}; - this->notify_cv.wait(nlock, [&]{ return this->status != ExecuteStatus::WAITING; }); - } - - template - [[nodiscard]] inline bool wait_for(const std::chrono::duration<_Rep, _Period>& time) const { - std::unique_lock nlock{this->notify_mutex}; - return this->notify_cv.wait_for(nlock, time, [&]{ return this->status != ExecuteStatus::WAITING; }); - } - private: - std::variant response_{}; - - std::mutex& notify_mutex; - std::condition_variable& notify_cv; - }; - - namespace filesystem { - struct DirectoryQueryError { - enum Type { - UNKNOWN, - }; - Type type{}; - std::string extra_message{}; - }; - - struct DirectoryEntry { - enum Type { - UNKNOWN, - DIRECTORY, - FILE - }; - - Type type{Type::UNKNOWN}; - std::string name{}; - std::chrono::system_clock::time_point created_at{}; - std::chrono::system_clock::time_point modified_at{}; - - size_t size{0}; - }; - - struct DirectoryModifyError { - enum Type { - UNKNOWN, - }; - Type type{}; - std::string extra_message{}; - }; - - struct FileModifyError { - enum Type { - UNKNOWN, - }; - Type type{}; - std::string extra_message{}; - }; - - class AbstractProvider { - public: - typedef ExecuteResponse> directory_query_response_t; - - /* channels */ - [[nodiscard]] virtual std::shared_ptr query_channel_directory(ServerId /* server */, ChannelId /* channel */, const std::string& /* path */); - [[nodiscard]] virtual std::shared_ptr> create_channel_directory(ServerId /* server */, ChannelId /* channel */, const std::string& /* path */); - [[nodiscard]] virtual std::shared_ptr> delete_channel_directory(ServerId /* server */, ChannelId /* channel */, const std::string& /* path */); - - [[nodiscard]] virtual std::shared_ptr> delete_channel_file(ServerId /* server */, ChannelId /* channel */, const std::string& /* path */); - [[nodiscard]] virtual std::shared_ptr> rename_channel_file(ServerId /* server */, ChannelId /* channel */, const std::string& /* path */); - - /* icons */ - [[nodiscard]] virtual std::shared_ptr query_icon_directory(ServerId /* server */); - [[nodiscard]] virtual std::shared_ptr> delete_icon(ServerId /* server */, const std::string& /* name */); - - /* avatars */ - [[nodiscard]] virtual std::shared_ptr query_avatar_directory(ServerId /* server */); - [[nodiscard]] virtual std::shared_ptr> delete_avatar(ServerId /* server */, const std::string& /* name */); - private: - }; - } - - namespace transfer { - class AbstractProvider { - public: - enum TransferDirection { - UPLOAD, - DOWNLOAD - }; - void initialize_channel_transfer(TransferDirection /* direction */, ServerId /* server */, ChannelId /* channel */, const std::string& /* path */); - void initialize_icon_transfer(TransferDirection /* direction */, ServerId /* server */, ChannelId /* channel */, const std::string& /* path */); - void initialize_avatar_transfer(TransferDirection /* direction */, ServerId /* server */, ChannelId /* channel */, const std::string& /* path */); - private: - }; - } - - class AbstractFileServer { - public: - private: - }; -} \ No newline at end of file diff --git a/server/src/server/POWHandler.cpp b/server/src/server/POWHandler.cpp index 15a77f1..09978c0 100644 --- a/server/src/server/POWHandler.cpp +++ b/server/src/server/POWHandler.cpp @@ -252,8 +252,11 @@ void POWHandler::handle_puzzle_solve(const std::shared_ptrrsa_challenge->data_result, &buffer[4 + 1 + 2 * 64 + 04 + 100], 64) != 0) { #ifdef POW_ERROR - debugMessage(this->get_server_id(), "[POW][{}][Puzzle] Received an invalid puzzle solution! Resetting client", net::to_string(client->address)); + debugMessage(this->get_server_id(), "[POW][{}][Puzzle] Received an invalid puzzle solution! Resetting client & puzzle", net::to_string(client->address)); #endif + constexpr static uint8_t empty_result[64]{0}; + if(memcmp(empty_result, &buffer[4 + 1 + 2 * 64 + 04 + 100], 64) == 0) + client->rsa_challenge->fail_count++; client->rsa_challenge.reset(); /* get another RSA challenge */ this->reset_client(client); return; diff --git a/server/src/server/file/FileServer.cpp b/server/src/server/file/LocalFileServer.cpp similarity index 92% rename from server/src/server/file/FileServer.cpp rename to server/src/server/file/LocalFileServer.cpp index 7214fc3..16ca101 100644 --- a/server/src/server/file/FileServer.cpp +++ b/server/src/server/file/LocalFileServer.cpp @@ -1,4 +1,4 @@ -#include "FileServer.h" +#include "LocalFileServer.h" #include "src/client/file/FileClient.h" #include "src/client/ConnectedClient.h" #include @@ -20,9 +20,9 @@ namespace fs = std::experimental::filesystem; extern InstanceHandler* serverInstance; -FileServer::FileServer() {} +LocalFileServer::LocalFileServer() {} -FileServer::~FileServer() { +LocalFileServer::~LocalFileServer() { stop(); } @@ -32,7 +32,7 @@ inline fs::path buildPath(std::string rootPath, std::shared_ptr return fs::u8path(rootPath + strPath); } -std::shared_ptr FileServer::createDirectory(std::string name, std::shared_ptr parent) { +std::shared_ptr LocalFileServer::createDirectory(std::string name, std::shared_ptr parent) { auto path = buildPath(this->rootPath, parent); path += name; std::error_code code{}; @@ -44,17 +44,17 @@ std::shared_ptr FileServer::createDirectory(std::string name, s return static_pointer_cast(this->findFile(path.string())); } -bool FileServer::fileExists(std::shared_ptr dir) { +bool LocalFileServer::fileExists(std::shared_ptr dir) { std::error_code code{}; return fs::exists(buildPath(this->rootPath, dir), code); } -bool FileServer::fileExists(std::shared_ptr file) { +bool LocalFileServer::fileExists(std::shared_ptr file) { std::error_code code{}; return fs::exists(buildPath(this->rootPath, file), code); } -std::shared_ptr FileServer::findFile(std::string path, std::shared_ptr parent) { +std::shared_ptr LocalFileServer::findFile(std::string path, std::shared_ptr parent) { if(path.find(this->rootPath) != 0) { string strPath = (parent ? parent->path + "/" + parent->name : this->rootPath) + "/"; if(strPath.find(this->rootPath) != 0 && rootPath != strPath) @@ -85,7 +85,7 @@ std::shared_ptr FileServer::findFile(std::string path, std::sha return entry; } -std::vector> FileServer::listFiles(std::shared_ptr dir) { +std::vector> LocalFileServer::listFiles(std::shared_ptr dir) { if(!dir) return {}; auto directory = buildPath(this->rootPath, dir); @@ -129,7 +129,7 @@ std::vector> FileServer::listFiles(std::shared_ return result; } -bool FileServer::deleteFile(std::shared_ptr file) { +bool LocalFileServer::deleteFile(std::shared_ptr file) { std::error_code code{}; return fs::remove_all(fs::u8path(file->path + "/" + file->name), code) > 0 && !code; } @@ -142,7 +142,7 @@ inline std::string randomString(uint length = 15, std::string charIndex = "abcde return rs; } -std::shared_ptr FileServer::generateDownloadTransferKey(std::string &errorMessage, std::string targetFile, size_t offset, const shared_ptr& client) { +std::shared_ptr LocalFileServer::generateDownloadTransferKey(std::string &errorMessage, std::string targetFile, size_t offset, const shared_ptr& client) { auto file = this->findFile(targetFile, nullptr); if(!file){ errorMessage = "file does not exists"; @@ -188,7 +188,7 @@ std::shared_ptr FileServer::generateDownloadTransferKey( return result; } -std::shared_ptr FileServer::generateUploadTransferKey(std::string &errorMessage, std::string targetFile, size_t size, size_t offset, const shared_ptr& client) { +std::shared_ptr LocalFileServer::generateUploadTransferKey(std::string &errorMessage, std::string targetFile, size_t size, size_t offset, const shared_ptr& client) { shared_ptr result = make_shared(); result->owner = client; result->server = client->getServerId(); @@ -221,7 +221,7 @@ std::shared_ptr FileServer::generateUploadTransferKey(st return result; } -std::shared_ptr FileServer::resolveDirectory(const shared_ptr &server, std::shared_ptr channel, std::string subPath) { +std::shared_ptr LocalFileServer::resolveDirectory(const shared_ptr &server, std::shared_ptr channel, std::string subPath) { fs::path path = fs::u8path("server_" + to_string(server ? server->getServerId() : 0) + "/channel_" + to_string(channel->channelId())); if(!findFile(path)) this->createDirectory(path.string(), nullptr); @@ -232,7 +232,7 @@ std::shared_ptr FileServer::resolveDirectory(const shared_ptr(ffile); } -std::shared_ptr FileServer::iconDirectory(const shared_ptr &server) { +std::shared_ptr LocalFileServer::iconDirectory(const shared_ptr &server) { fs::path root = fs::u8path(this->rootPath); fs::path path = fs::u8path("server_" + to_string(server ? server->getServerId() : 0) + "/icons"); std::error_code code{}; @@ -243,13 +243,13 @@ std::shared_ptr FileServer::iconDirectory(const shared_ptr(findFile(path.string())); } -bool FileServer::iconExists(const shared_ptr &server, IconId icon) { +bool LocalFileServer::iconExists(const shared_ptr &server, IconId icon) { if(icon == 0) return false; if(icon < 1000) return true; return this->findFile("icon_" + to_string(icon), this->iconDirectory(server)) != nullptr; } -std::shared_ptr FileServer::avatarDirectory(const shared_ptr &server) { +std::shared_ptr LocalFileServer::avatarDirectory(const shared_ptr &server) { fs::path path = fs::u8path("server_" + to_string(server ? server->getServerId() : 0) + "/avatars"); if(!findFile(path)) this->createDirectory(path.string(), nullptr); @@ -257,7 +257,7 @@ std::shared_ptr FileServer::avatarDirectory(const shared_ptr &server) { +void LocalFileServer::setupServer(const shared_ptr &server) { auto dir = iconDirectory(server); if(!dir) { logError(LOG_FT,"Failed to find icon directory for server {}", server ? server->getServerId() : 0); @@ -273,11 +273,11 @@ void FileServer::setupServer(const shared_ptr &server) { } } -std::string FileServer::server_file_base(const std::shared_ptr &server) { +std::string LocalFileServer::server_file_base(const std::shared_ptr &server) { return rootPath + "/server_" + to_string(server->getServerId()); } -void FileServer::deleteServer(const shared_ptr &server) { +void LocalFileServer::deleteServer(const shared_ptr &server) { fs::path path = fs::u8path(rootPath + "/server_" + to_string(server ? server->getServerId() : 0)); std::error_code code{}; if(fs::exists(path, code) && !code) { @@ -289,7 +289,7 @@ void FileServer::deleteServer(const shared_ptr &server) { } //The actual server! -bool FileServer::start(const std::deque>& bindings, std::string& error) { +bool LocalFileServer::start(const std::deque>& bindings, std::string& error) { if(this->running()) { error = "server already running"; return false; @@ -355,7 +355,7 @@ bool FileServer::start(const std::deque>& b continue; } - binding->event_accept = event_new(this->ioLoop, binding->file_descriptor, EV_READ | EV_PERSIST, [](int a, short b, void* c){ ((FileServer *) c)->on_client_accept(a, b, c); }, this); + binding->event_accept = event_new(this->ioLoop, binding->file_descriptor, EV_READ | EV_PERSIST, [](int a, short b, void* c){ ((LocalFileServer *) c)->on_client_accept(a, b, c); }, this); event_add(binding->event_accept, nullptr); this->bindings.push_back(binding); } @@ -368,14 +368,14 @@ bool FileServer::start(const std::deque>& b } for(int index = 0; index < 2; index++){ - auto th = new threads::Thread(THREAD_SAVE_OPERATIONS | THREAD_EXECUTE_LATER, &FileServer::clientTickingExecutor, this); + auto th = new threads::Thread(THREAD_SAVE_OPERATIONS | THREAD_EXECUTE_LATER, &LocalFileServer::clientTickingExecutor, this); th->name("Ticking FT #" + to_string(index)).execute(); this->tickingThreads.push_back(th); } return true; } -void FileServer::stop() { +void LocalFileServer::stop() { if(!this->running()) return; active = false; { @@ -449,7 +449,7 @@ if(close(file_descriptor) < 0) { \ debugMessage(LOG_FT, "[{}] Failed to close socket ({} | {}).", logging_address(remote_address), errno, strerror(errno)); \ } -void FileServer::on_client_accept(int _server_file_descriptor, short ev, void *arg) { +void LocalFileServer::on_client_accept(int _server_file_descriptor, short ev, void *arg) { sockaddr_storage remote_address{}; memset(&remote_address, 0, sizeof(remote_address)); socklen_t address_length = sizeof(remote_address); @@ -559,7 +559,7 @@ void FileServer::on_client_accept(int _server_file_descriptor, short ev, void *a logMessage(LOG_FT, "[{}] Remote peer connected. Initializing session.", logging_address(remote_address)); } -void FileServer::clientTickingExecutor() { +void LocalFileServer::clientTickingExecutor() { while(this->running()){ shared_ptr client; { @@ -575,7 +575,7 @@ void FileServer::clientTickingExecutor() { } } -std::deque> FileServer::running_file_transfers(const std::shared_ptr &client) { +std::deque> LocalFileServer::running_file_transfers(const std::shared_ptr &client) { std::deque> result; { @@ -588,7 +588,7 @@ std::deque> FileServer::running_file_transfers(const return result; } -std::deque> FileServer::pending_file_transfers(const std::shared_ptr &client) { +std::deque> LocalFileServer::pending_file_transfers(const std::shared_ptr &client) { std::deque> result; for(const auto& key : this->pending_keys()) if(!client || key->owner.lock() == client) @@ -596,7 +596,7 @@ std::deque> FileServer::pending_file_tra return result; } -void FileServer::tickFileClient(std::shared_ptr cl) { +void LocalFileServer::tickFileClient(std::shared_ptr cl) { lock_guard lock(this->tickingLock); this->tickQueue.push_back(cl); this->tickingCon.notify_one(); @@ -613,7 +613,7 @@ struct TransfareGroup { ssize_t max_bandwidth = -1; }; -void FileServer::instanceTick() { +void LocalFileServer::instanceTick() { { //tickQueue auto client = this->connected_clients(); diff --git a/server/src/server/file/FileServer.h b/server/src/server/file/LocalFileServer.h similarity index 98% rename from server/src/server/file/FileServer.h rename to server/src/server/file/LocalFileServer.h index 6cb9f73..805aafb 100644 --- a/server/src/server/file/FileServer.h +++ b/server/src/server/file/LocalFileServer.h @@ -76,7 +76,7 @@ namespace ts { class FileClient; //FIXME Valid path - class FileServer { + class LocalFileServer { friend class FileClient; public: struct Binding { @@ -87,8 +87,8 @@ namespace ts { inline std::string as_string() { return net::to_string(address, true); } }; - FileServer(); - ~FileServer(); + LocalFileServer(); + ~LocalFileServer(); bool start(const std::deque>& /* bindings */, std::string& /* error */); void stop(); diff --git a/server/src/weblist/TeamSpeakWebClient.cpp b/server/src/weblist/TeamSpeakWebClient.cpp index e5f0de4..089f233 100644 --- a/server/src/weblist/TeamSpeakWebClient.cpp +++ b/server/src/weblist/TeamSpeakWebClient.cpp @@ -28,7 +28,12 @@ void TSWebClient::_handle_message_read(int file_descriptor, short, void *ptr_cli const auto read = recv(file_descriptor, buffer.data_ptr(), buffer.length(), MSG_NOSIGNAL | MSG_DONTWAIT); if(read <= 0) { logError(client->server->getServerId(), "[WebList] Failed to read weblist response. ({} | {} => {})", read, errno, strerror(errno)); - event_del_noblock(client->event_read); + + { + std::lock_guard elock{client->event_mutex}; + if(client->event_read) + event_del_noblock(client->event_read); + } client->trigger_fail_later("failed to read!", true); return; } @@ -46,8 +51,11 @@ void TSWebClient::_handle_message_write(int file_descriptor, short, void *ptr_cl buffer = std::move(client->write_buffer[0]); client->write_buffer.pop_front(); - if(!client->write_buffer.empty() && client->event_write) - event_add(client->event_write, nullptr); + if(!client->write_buffer.empty()) { + std::lock_guard elock{client->event_mutex}; + if(client->event_write) + event_add(client->event_write, nullptr); + } } const auto write = sendto(file_descriptor, buffer.data_ptr(), buffer.length(), 0, (const sockaddr *) &client->remote_address, sizeof(sockaddr_in)); @@ -60,8 +68,11 @@ void TSWebClient::_handle_message_write(int file_descriptor, short, void *ptr_cl void TSWebClient::_handle_timeout(int, short, void *ptr_client) { const auto client = (TSWebClient*) ptr_client; - if(client->event_timeout) - event_del_noblock(client->event_timeout); + { + std::lock_guard elock{client->event_mutex}; + if(client->event_timeout) + event_del_noblock(client->event_timeout); + } client->trigger_fail_later("timeout", true); } @@ -76,12 +87,15 @@ void TSWebClient::unregister_events(bool blocking) { } }; - if(this->event_timeout) - unregister(this->event_timeout); - if(this->event_read) - unregister(this->event_read); - if(this->event_write) - unregister(this->event_write); + std::unique_lock elock{this->event_mutex}; + auto tevent = std::exchange(this->event_timeout, nullptr); + auto revent = std::exchange(this->event_read, nullptr); + auto wevent = std::exchange(this->event_write, nullptr); + elock.unlock(); + + if(tevent) unregister(tevent); + if(revent) unregister(revent); + if(wevent) unregister(wevent); if(this->file_descriptor > 0) { close(this->file_descriptor); @@ -90,6 +104,7 @@ void TSWebClient::unregister_events(bool blocking) { } void TSWebClient::reset_timeout(bool reschedule) { + std::lock_guard elock{this->event_mutex}; if(this->event_timeout) { event_del(this->event_timeout); if(reschedule) { diff --git a/server/src/weblist/TeamSpeakWebClient.h b/server/src/weblist/TeamSpeakWebClient.h index 30728f1..76845c3 100644 --- a/server/src/weblist/TeamSpeakWebClient.h +++ b/server/src/weblist/TeamSpeakWebClient.h @@ -42,9 +42,10 @@ namespace ts { std::deque write_buffer{}; int file_descriptor{0}; - struct event* event_read = nullptr; - struct event* event_write = nullptr; - struct event* event_timeout = nullptr; + std::mutex event_mutex{}; + struct event* event_read{nullptr}; + struct event* event_write{nullptr}; + struct event* event_timeout{nullptr}; void unregister_events(bool /* blocking */); void reset_timeout(bool /* reschedule */); diff --git a/server/tomcryptTest.cpp b/server/tomcryptTest.cpp index 5fb1d38..e11a21b 100644 --- a/server/tomcryptTest.cpp +++ b/server/tomcryptTest.cpp @@ -1,24 +1,59 @@ #include -#include -#include +#include +#include +//#define STANDALONE + +#ifndef STANDALONE + #include +#endif + +//c++ -I. --std=c++1z test.cpp ./libtommath.a -o test +#ifdef STANDALONE +int main() { +#else void testTomMath(){ - mp_int x{}; - mp_init(&x); - mp_read_radix(&x, "2280880776330203449294339386427307168808659578661428574166839717243346815923951250209099128371839254311904649344289668000305972691071196233379180504231889", 10); +#endif + { + mp_int x{}, n{}, exp{}, r{}; + mp_init_multi(&x, &n, &exp, &r, nullptr); + mp_2expt(&exp, 1000); - mp_int n{}; - mp_init(&n); - mp_read_radix(&n, "436860662135489324843442078840868871476482593772359054106809367217662215065650065606351911592188139644751920724885335056877706082800496073391354240530016", 10); + mp_read_radix(&x, "2280880776330203449294339386427307168808659578661428574166839717243346815923951250209099128371839254311904649344289668000305972691071196233379180504231889", 10); + mp_read_radix(&n, "436860662135489324843442078840868871476482593772359054106809367217662215065650065606351911592188139644751920724885335056877706082800496073391354240530016", 10); - mp_int exp{}; - mp_init(&exp); - mp_2expt(&exp, 1000); + auto err = mp_exptmod(&x, &exp, &n, &r); +#ifdef STANDALONE + std::cout << "Series A: " << err << ", expected != 0\n"; +#else + //assert(err != MP_OKAY); //if this method succeed than tommath failed. Unknown why but it is so +#endif + mp_clear_multi(&x, &n, &exp, &r, nullptr); + } + { + mp_int x{}, n{}, exp{}, r{}; + mp_init_multi(&x, &n, &exp, &r, nullptr); + mp_2expt(&exp, 1000); +#if 0 + const static std::string_view n_{"\x01\x7a\xc5\x8d\x28\x7a\x61\x58\xf6\xe3\x98\x60\x2f\x81\x9c\x8a\x48\xc9\x20\xd1\x59\xe0\x24\x75\x91\x27\x9f\x52\x1e\x2c\x24\x85\xa9\xdc\x74\xfa\x0b\x36\xf9\x6c\x77\xa3\x7c\xf9\xbb\xf7\x04\xad\xa3\x84\x0d\x97\x25\x54\x19\x72\x4f\x8f\xfc\x66\xbe\x41\xda\x95"}; + const static std::string_view x_{"\xd1\xef\xf0\x16\x34\x48\x56\x53\x15\x97\xa0\x28\xbd\x13\xce\xbf\xc2\xd6\x79\x9d\x21\x81\x83\x37\x8c\xe8\xee\xee\xa1\x22\xa4\xf5\x63\x33\x53\x0c\x38\x2f\x0a\x00\x53\x20\xc7\x93\x52\xa9\xd0\xc2\xfb\xbc\xc5\xc4\xc3\x54\xad\xcb\x49\x52\xc0\xd8\x97\x32\x94\xee"}; + mp_read_unsigned_bin(&x, (unsigned char*) x_.data(), x_.length()); + mp_read_unsigned_bin(&n, (unsigned char*) n_.data(), n_.length()); +#else + const static std::string_view n_{"017ac58d287a6158f6e398602f819c8a48c920d159e0247591279f521e2c2485a9dc74fa0b36f96c77a37cf9bbf704ada3840d97255419724f8ffc66be41da95"}; + const static std::string_view x_{"d1eff016344856531597a028bd13cebfc2d6799d218183378ce8eeeea122a4f56333530c382f0a005320c79352a9d0c2fbbcc5c4c354adcb4952c0d8973294ee"}; + mp_read_radix(&x, x_.data(), 16); + mp_read_radix(&n, n_.data(), 16); +#endif - mp_int r{}; - mp_init(&r); - assert(mp_exptmod(&x, &exp, &n, &r) != CRYPT_OK); //if this method succeed than tommath failed. Unknown why but it is so + auto err = mp_exptmod(&x, &exp, &n, &r); +#ifdef STANDALONE + std::cout << "Series B: " << err << ", expected != 0\n"; +#else + //assert(err != MP_OKAY); //if this method succeed than tommath failed. Unknown why but it is so +#endif - mp_clear_multi(&x, &n, &exp, &r, nullptr); + mp_clear_multi(&x, &n, &exp, &r, nullptr); + } } \ No newline at end of file