Teaspeak-Server/file/include/files/FileServer.h

424 lines
17 KiB
C++

#pragma once
#include <string>
#include <chrono>
#include <utility>
#include <variant>
#include <deque>
#include <functional>
#include <atomic>
#include <condition_variable>
#include "./ExecuteResponse.h"
#define TRANSFER_KEY_LENGTH (32)
#define TRANSFER_MEDIA_BYTES_LENGTH (32)
namespace ts::server::file {
class VirtualFileServer;
typedef uint64_t ChannelId;
typedef uint64_t ServerId;
typedef uint64_t ClientId;
namespace filesystem {
template <typename ErrorCodes>
struct DetailedError {
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
};
constexpr std::array<std::string_view, 5> directory_query_error_messages = {
"unknown error",
"path exceeds base path",
"path is a file",
"path does not exists",
"failed to list files"
};
typedef DetailedError<DirectoryQueryErrorType> 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}; /* file only */
bool empty{false}; /* directory only */
};
enum struct DirectoryModifyErrorType {
UNKNOWN,
PATH_EXCEEDS_ROOT_PATH,
PATH_ALREADY_EXISTS,
FAILED_TO_CREATE_DIRECTORIES
};
typedef DetailedError<DirectoryModifyErrorType> 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,
FAILED_TO_CREATE_DIRECTORIES,
SOME_FILES_ARE_LOCKED
};
typedef DetailedError<FileModifyErrorType> FileModifyError;
enum struct FileDeleteErrorType {
UNKNOWN,
};
typedef DetailedError<FileDeleteErrorType> FileDeleteError;
struct FileDeleteResponse {
enum struct StatusType {
SUCCESS,
PATH_EXCEEDS_ROOT_PATH,
PATH_DOES_NOT_EXISTS,
FAILED_TO_DELETE_FILES,
SOME_FILES_ARE_LOCKED
};
struct DeleteResult {
StatusType status{StatusType::SUCCESS};
std::string error_detail{};
DeleteResult(StatusType status, std::string errorDetail) : status{status},
error_detail{std::move(errorDetail)} {}
DeleteResult() = default;
};
std::vector<DeleteResult> delete_results{};
};
enum struct ServerCommandErrorType {
UNKNOWN,
FAILED_TO_CREATE_DIRECTORIES,
FAILED_TO_DELETE_DIRECTORIES
};
typedef DetailedError<ServerCommandErrorType> ServerCommandError;
struct FileInfoResponse {
enum struct StatusType {
SUCCESS,
PATH_EXCEEDS_ROOT_PATH,
PATH_DOES_NOT_EXISTS,
FAILED_TO_QUERY_INFO,
UNKNOWN_FILE_TYPE
};
struct FileInfo {
StatusType status{StatusType::SUCCESS};
std::string error_detail{};
DirectoryEntry info{};
FileInfo(StatusType status, std::string errorDetail, DirectoryEntry info) : status{status},
error_detail{std::move(errorDetail)}, info{std::move(info)} {}
FileInfo() = default;
};
std::vector<FileInfo> file_info{};
};
enum struct FileInfoErrorType {
UNKNOWN,
};
typedef DetailedError<FileInfoErrorType> FileInfoError;
class AbstractProvider {
public:
typedef ExecuteResponse<DirectoryQueryError, std::deque<DirectoryEntry>> directory_query_response_t;
/* server */
[[nodiscard]] virtual std::shared_ptr<ExecuteResponse<ServerCommandError>> initialize_server(const std::shared_ptr<VirtualFileServer> &/* server */) = 0;
/* channels */
[[nodiscard]] virtual std::shared_ptr<ExecuteResponse<FileInfoError, FileInfoResponse>> query_channel_info(const std::shared_ptr<VirtualFileServer> &/* server */, const std::vector<std::tuple<ChannelId, std::string>>& /* files */) = 0;
[[nodiscard]] virtual std::shared_ptr<directory_query_response_t> query_channel_directory(const std::shared_ptr<VirtualFileServer> &/* server */, ChannelId /* channel */, const std::string& /* path */) = 0;
[[nodiscard]] virtual std::shared_ptr<ExecuteResponse<DirectoryModifyError>> create_channel_directory(const std::shared_ptr<VirtualFileServer> &/* server */, ChannelId /* channel */, const std::string& /* path */) = 0;
[[nodiscard]] virtual std::shared_ptr<ExecuteResponse<FileDeleteError, FileDeleteResponse>> delete_channel_files(const std::shared_ptr<VirtualFileServer> &/* server */, ChannelId /* channel */, const std::vector<std::string>& /* paths */) = 0;
[[nodiscard]] virtual std::shared_ptr<ExecuteResponse<FileModifyError>> rename_channel_file(const std::shared_ptr<VirtualFileServer> &/* server */, ChannelId /* channel */, const std::string& /* path */, ChannelId /* target channel */, const std::string& /* target */) = 0;
/* icons */
[[nodiscard]] virtual std::shared_ptr<ExecuteResponse<FileInfoError, FileInfoResponse>> query_icon_info(const std::shared_ptr<VirtualFileServer> &/* server */, const std::vector<std::string>& /* names */) = 0;
[[nodiscard]] virtual std::shared_ptr<directory_query_response_t> query_icon_directory(const std::shared_ptr<VirtualFileServer> &/* server */) = 0;
[[nodiscard]] virtual std::shared_ptr<ExecuteResponse<FileDeleteError, FileDeleteResponse>> delete_icons(const std::shared_ptr<VirtualFileServer> &/* server */, const std::vector<std::string>& /* names */) = 0;
/* avatars */
[[nodiscard]] virtual std::shared_ptr<ExecuteResponse<FileInfoError, FileInfoResponse>> query_avatar_info(const std::shared_ptr<VirtualFileServer> &/* server */, const std::vector<std::string>& /* names */) = 0;
[[nodiscard]] virtual std::shared_ptr<directory_query_response_t> query_avatar_directory(const std::shared_ptr<VirtualFileServer> &/* server */) = 0;
[[nodiscard]] virtual std::shared_ptr<ExecuteResponse<FileDeleteError, FileDeleteResponse>> delete_avatars(const std::shared_ptr<VirtualFileServer> &/* server */, const std::vector<std::string>& /* names */) = 0;
private:
};
}
namespace transfer {
typedef uint16_t transfer_id;
struct Transfer {
transfer_id server_transfer_id{0};
transfer_id client_transfer_id{0};
std::shared_ptr<VirtualFileServer> server{nullptr};
ChannelId channel_id{0};
ClientId client_id{0};
std::string client_unique_id{};
std::string transfer_key{};
std::chrono::system_clock::time_point initialized_timestamp{};
enum Direction {
DIRECTION_UNKNOWN,
DIRECTION_UPLOAD,
DIRECTION_DOWNLOAD
} direction{DIRECTION_UNKNOWN};
struct Address {
std::string hostname{};
uint16_t port{0};
};
std::vector<Address> server_addresses{};
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{};
std::string absolute_file_path{};
std::string relative_file_path{};
std::string file_name{};
int64_t max_bandwidth{-1};
size_t expected_file_size{0}; /* incl. the offset! */
size_t file_offset{0};
bool override_exiting{false};
};
struct TransferStatistics {
uint64_t network_bytes_send{0};
uint64_t network_bytes_received{0};
uint64_t delta_network_bytes_send{0};
uint64_t delta_network_bytes_received{0};
uint64_t file_bytes_transferred{0};
uint64_t delta_file_bytes_transferred{0};
size_t file_start_offset{0};
size_t file_current_offset{0};
size_t file_total_size{0};
double average_speed{0};
double current_speed{0};
};
struct TransferInitError {
enum Type {
UNKNOWN,
INVALID_FILE_TYPE,
FILE_DOES_NOT_EXISTS,
FILE_IS_NOT_A_FILE,
CLIENT_TOO_MANY_TRANSFERS,
SERVER_TOO_MANY_TRANSFERS,
SERVER_QUOTA_EXCEEDED,
CLIENT_QUOTA_EXCEEDED,
IO_ERROR
} error_type{UNKNOWN};
std::string error_message{};
TransferInitError(Type errorType, std::string errorMessage) : error_type{errorType},
error_message{std::move(errorMessage)} {}
};
struct TransferActionError {
enum Type {
UNKNOWN,
UNKNOWN_TRANSFER
} error_type{UNKNOWN};
std::string error_message{};
};
struct TransferError {
enum Type {
UNKNOWN,
TRANSFER_TIMEOUT,
DISK_IO_ERROR,
DISK_TIMEOUT,
DISK_INITIALIZE_ERROR,
NETWORK_IO_ERROR,
UNEXPECTED_CLIENT_DISCONNECT,
UNEXPECTED_DISK_EOF,
USER_REQUEST
} error_type{UNKNOWN};
std::string error_message{};
};
struct ActiveFileTransfer {
transfer_id client_transfer_id{0};
transfer_id server_transfer_id{0};
Transfer::Direction direction{Transfer::DIRECTION_UNKNOWN};
ClientId client_id{};
std::string client_unique_id{};
std::string file_path{};
std::string file_name{};
size_t expected_size{};
size_t size_done{};
enum Status {
NOT_STARTED,
RUNNING,
INACTIVE /* (not used yet) */
} status{Status::NOT_STARTED};
std::chrono::milliseconds runtime{};
double average_speed{0};
double current_speed{0};
};
enum struct TransferListError {
UNKNOWN
};
class AbstractProvider {
public:
struct TransferInfo {
std::string file_path{};
std::string client_unique_id{};
ClientId client_id{};
bool override_exiting{false}; /* only for upload valid */
size_t file_offset{0};
size_t expected_file_size{0};
int64_t max_bandwidth{-1};
int64_t max_concurrent_transfers{-1};
/* only used for upload, for download the quotas could be checked before */
size_t download_server_quota_limit{(size_t) -1};
size_t download_client_quota_limit{(size_t) -1};
};
virtual std::shared_ptr<ExecuteResponse<TransferInitError, std::shared_ptr<Transfer>>>
initialize_channel_transfer(Transfer::Direction /* direction */, const std::shared_ptr<VirtualFileServer>& /* server */, ChannelId /* channel */, const TransferInfo& /* info */) = 0;
virtual std::shared_ptr<ExecuteResponse<TransferInitError, std::shared_ptr<Transfer>>>
initialize_icon_transfer(Transfer::Direction /* direction */, const std::shared_ptr<VirtualFileServer>& /* server */, const TransferInfo& /* info */) = 0;
virtual std::shared_ptr<ExecuteResponse<TransferInitError, std::shared_ptr<Transfer>>>
initialize_avatar_transfer(Transfer::Direction /* direction */, const std::shared_ptr<VirtualFileServer>& /* server */, const TransferInfo& /* info */) = 0;
virtual std::shared_ptr<ExecuteResponse<TransferListError, std::vector<ActiveFileTransfer>>> list_transfer() = 0;
virtual std::shared_ptr<ExecuteResponse<TransferActionError>> stop_transfer(const std::shared_ptr<VirtualFileServer>& /* server */, transfer_id /* id */, bool /* flush */) = 0;
std::function<void(const std::shared_ptr<Transfer>&)> callback_transfer_registered{}; /* transfer has been registered */
std::function<void(const std::shared_ptr<Transfer>&)> callback_transfer_started{}; /* transfer has been started */
std::function<void(const std::shared_ptr<Transfer>&)> callback_transfer_finished{}; /* transfer has been finished */
std::function<void(const std::shared_ptr<Transfer>&, const TransferStatistics&)> callback_transfer_statistics{};
std::function<void(const std::shared_ptr<Transfer>&, const transfer::TransferStatistics&, const TransferError&)> callback_transfer_aborted{}; /* an error happened while transferring the data */
};
}
class VirtualFileServer {
public:
explicit VirtualFileServer(ServerId server_id, std::string unique_id) : server_id_{server_id}, unique_id_{std::move(unique_id)} {}
[[nodiscard]] inline auto unique_id() const -> const std::string& { return this->unique_id_; }
[[nodiscard]] inline auto server_id() const -> ServerId { return this->server_id_; }
[[nodiscard]] inline auto max_networking_upload_bandwidth() const -> int64_t { return this->max_networking_upload_bandwidth_; }
virtual void max_networking_upload_bandwidth(int64_t value) {
this->max_networking_upload_bandwidth_ = value;
}
[[nodiscard]] inline auto max_networking_download_bandwidth() const -> int64_t { return this->max_networking_download_bandwidth_; }
virtual void max_networking_download_bandwidth(int64_t value) {
this->max_networking_download_bandwidth_ = value;
}
[[nodiscard]] inline auto generate_transfer_id() {
return ++this->current_transfer_id;
}
private:
ServerId server_id_;
std::string unique_id_;
int64_t max_networking_upload_bandwidth_{-1};
int64_t max_networking_download_bandwidth_{-1};
std::atomic<transfer::transfer_id> current_transfer_id{0};
};
class AbstractFileServer {
public:
[[nodiscard]] virtual std::string file_base_path() const = 0;
[[nodiscard]] virtual filesystem::AbstractProvider& file_system() = 0;
[[nodiscard]] virtual transfer::AbstractProvider& file_transfer() = 0;
[[nodiscard]] inline auto virtual_servers() const -> std::deque<std::shared_ptr<VirtualFileServer>> {
std::lock_guard slock{this->servers_mutex};
return this->servers_;
}
[[nodiscard]] inline auto find_virtual_server(ServerId server_id) const -> std::shared_ptr<VirtualFileServer> {
std::lock_guard slock{this->servers_mutex};
auto it = std::find_if(this->servers_.begin(), this->servers_.end(), [&](const std::shared_ptr<VirtualFileServer>& server) {
return server->server_id() == server_id;
});
return it == this->servers_.end() ? nullptr : *it;
}
virtual std::shared_ptr<VirtualFileServer> register_server(ServerId /* server id */) = 0;
virtual void unregister_server(ServerId /* server id */, bool /* delete tiles */) = 0;
protected:
mutable std::mutex servers_mutex{};
std::deque<std::shared_ptr<VirtualFileServer>> servers_{};
};
extern bool initialize(std::string& /* error */, const std::string& /* host names */, uint16_t /* port */);
extern void finalize();
extern std::shared_ptr<AbstractFileServer> server();
}