#pragma once #include #include #include #include #include #include #include #include #include #include "./ExecuteResponse.h" #define TRANSFER_KEY_LENGTH (32) namespace ts::server::file { class VirtualFileServer; 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 }; 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, FAILED_TO_CREATE_DIRECTORIES, SOME_FILES_ARE_LOCKED }; typedef DetailedError FileModifyError; enum struct FileDeleteErrorType { UNKNOWN, }; typedef DetailedError 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)} {} }; std::vector delete_results{}; }; enum struct ServerCommandErrorType { UNKNOWN, FAILED_TO_CREATE_DIRECTORIES, FAILED_TO_DELETE_DIRECTORIES }; typedef DetailedError 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)} {} }; std::vector file_info{}; }; enum struct FileInfoErrorType { UNKNOWN, }; typedef DetailedError FileInfoError; class AbstractProvider { public: typedef ExecuteResponse> directory_query_response_t; /* server */ [[nodiscard]] virtual std::shared_ptr> initialize_server(const std::shared_ptr &/* server */) = 0; [[nodiscard]] virtual std::shared_ptr> delete_server(const std::shared_ptr &/* server */) = 0; /* channels */ [[nodiscard]] virtual std::shared_ptr> query_channel_info(const std::shared_ptr &/* server */, ChannelId /* channel */, const std::vector& /* names */) = 0; [[nodiscard]] virtual std::shared_ptr query_channel_directory(const std::shared_ptr &/* server */, ChannelId /* channel */, const std::string& /* path */) = 0; [[nodiscard]] virtual std::shared_ptr> create_channel_directory(const std::shared_ptr &/* server */, ChannelId /* channel */, const std::string& /* path */) = 0; [[nodiscard]] virtual std::shared_ptr> delete_channel_files(const std::shared_ptr &/* server */, ChannelId /* channel */, const std::vector& /* paths */) = 0; [[nodiscard]] virtual std::shared_ptr> rename_channel_file(const std::shared_ptr &/* server */, ChannelId /* channel */, const std::string& /* path */, ChannelId /* target channel */, const std::string& /* target */) = 0; /* icons */ [[nodiscard]] virtual std::shared_ptr> query_icon_info(const std::shared_ptr &/* server */, const std::vector& /* names */) = 0; [[nodiscard]] virtual std::shared_ptr query_icon_directory(const std::shared_ptr &/* server */) = 0; [[nodiscard]] virtual std::shared_ptr> delete_icons(const std::shared_ptr &/* server */, const std::vector& /* names */) = 0; /* avatars */ [[nodiscard]] virtual std::shared_ptr> query_avatar_info(const std::shared_ptr &/* server */, const std::vector& /* names */) = 0; [[nodiscard]] virtual std::shared_ptr query_avatar_directory(const std::shared_ptr &/* server */) = 0; [[nodiscard]] virtual std::shared_ptr> delete_avatars(const std::shared_ptr &/* server */, const std::vector& /* names */) = 0; private: }; } namespace transfer { typedef uint32_t transfer_id; struct Transfer { transfer_id server_transfer_id{0}; transfer_id client_transfer_id{0}; std::shared_ptr server{nullptr}; ChannelId channel_id{0}; ClientId client_id{0}; std::string client_unique_id{}; char transfer_key[TRANSFER_KEY_LENGTH]{}; 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
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}; }; 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>> initialize_channel_transfer(Transfer::Direction /* direction */, const std::shared_ptr& /* server */, ChannelId /* channel */, const TransferInfo& /* info */) = 0; virtual std::shared_ptr>> initialize_icon_transfer(Transfer::Direction /* direction */, const std::shared_ptr& /* server */, const TransferInfo& /* info */) = 0; virtual std::shared_ptr>> initialize_avatar_transfer(Transfer::Direction /* direction */, const std::shared_ptr& /* server */, const TransferInfo& /* info */) = 0; virtual std::shared_ptr>> list_transfer() = 0; virtual std::shared_ptr> stop_transfer(const std::shared_ptr& /* server */, transfer_id /* id */, bool /* flush */) = 0; std::function&)> callback_transfer_registered{}; /* transfer has been registered */ std::function&)> callback_transfer_started{}; /* transfer has been started */ std::function&)> callback_transfer_finished{}; /* transfer has been finished */ std::function&, const TransferStatistics&)> callback_transfer_statistics{}; std::function&, 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 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::lock_guard slock{this->servers_mutex}; return this->servers_; } [[nodiscard]] inline auto find_virtual_server(ServerId server_id) const -> std::shared_ptr { std::lock_guard slock{this->servers_mutex}; auto it = std::find_if(this->servers_.begin(), this->servers_.end(), [&](const std::shared_ptr& server) { return server->server_id() == server_id; }); return it == this->servers_.end() ? nullptr : *it; } virtual std::shared_ptr register_server(ServerId /* server id */) = 0; virtual void unregister_server(ServerId /* server id */) = 0; protected: mutable std::mutex servers_mutex{}; std::deque> servers_{}; }; extern bool initialize(std::string& /* error */); extern void finalize(); extern std::shared_ptr server(); }