307 lines
		
	
	
		
			12 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			307 lines
		
	
	
		
			12 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
| #pragma once
 | |
| 
 | |
| #include <string>
 | |
| #include <chrono>
 | |
| #include <Definitions.h>
 | |
| #include <condition_variable>
 | |
| #include <variant>
 | |
| #include <deque>
 | |
| #include <functional>
 | |
| 
 | |
| #define TRANSFER_KEY_LENGTH (32)
 | |
| 
 | |
| namespace ts::server::file {
 | |
|     enum struct ExecuteStatus {
 | |
|         UNKNOWN,
 | |
|         WAITING,
 | |
|         SUCCESS,
 | |
|         ERROR
 | |
|     };
 | |
| 
 | |
|     template<typename VariantType, typename T, std::size_t index = 0>
 | |
|     constexpr std::size_t variant_index() {
 | |
|         if constexpr (index == std::variant_size_v<VariantType>) {
 | |
|             return index;
 | |
|         } else if constexpr (std::is_same_v<std::variant_alternative_t<index, VariantType>, T>) {
 | |
|             return index;
 | |
|         } else {
 | |
|             return variant_index<VariantType, T, index + 1>();
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     struct EmptyExecuteResponse { };
 | |
|     template <class error_t, class response_t = EmptyExecuteResponse>
 | |
|     class ExecuteResponse {
 | |
|             typedef std::variant<EmptyExecuteResponse, error_t, response_t> variant_t;
 | |
|         public:
 | |
|             ExecuteStatus status{ExecuteStatus::WAITING};
 | |
| 
 | |
|             [[nodiscard]] inline const auto& response() const { return std::get<response_t>(this->response_); }
 | |
| 
 | |
|             template <typename = std::enable_if_t<!std::is_void<error_t>::value>>
 | |
|             [[nodiscard]] inline const error_t& error() const { return std::get<error_t>(this->response_); }
 | |
| 
 | |
|             inline void wait() const {
 | |
|                 std::unique_lock nlock{this->notify_mutex};
 | |
|                 this->notify_cv.wait(nlock, [&]{ return this->status != ExecuteStatus::WAITING; });
 | |
|             }
 | |
| 
 | |
|             template<typename _Rep, typename _Period>
 | |
|             [[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 <typename... Args>
 | |
|             inline void emplace_success(Args&&... args) {
 | |
|                 constexpr auto success_index = variant_index<variant_t, response_t>();
 | |
| 
 | |
|                 std::lock_guard rlock{this->notify_mutex};
 | |
|                 this->response_.template emplace<success_index, Args...>(std::forward<Args>(args)...);
 | |
|                 this->status = ExecuteStatus::SUCCESS;
 | |
|                 this->notify_cv.notify_all();
 | |
|             }
 | |
| 
 | |
|             template <typename... Args>
 | |
|             inline void emplace_fail(Args&&... args) {
 | |
|                 constexpr auto error_index = variant_index<variant_t, error_t>();
 | |
| 
 | |
|                 std::lock_guard rlock{this->notify_mutex};
 | |
|                 this->response_.template emplace<error_index, Args...>(std::forward<Args>(args)...);
 | |
|                 this->status = ExecuteStatus::ERROR;
 | |
|                 this->notify_cv.notify_all();
 | |
|             }
 | |
| 
 | |
|             [[nodiscard]] inline bool succeeded() const {
 | |
|                 return this->status == ExecuteStatus::SUCCESS;
 | |
|             }
 | |
| 
 | |
|             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 <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,
 | |
| 
 | |
|             MAX
 | |
|         };
 | |
|         constexpr std::array<std::string_view, (int) DirectoryQueryErrorType::MAX> 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};
 | |
|         };
 | |
| 
 | |
|         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,
 | |
| 
 | |
|             SOME_FILES_ARE_LOCKED
 | |
|         };
 | |
|         typedef DetailedError<FileModifyErrorType> FileModifyError;
 | |
| 
 | |
|         enum struct ServerCommandErrorType {
 | |
|             UNKNOWN,
 | |
|             FAILED_TO_CREATE_DIRECTORIES,
 | |
|             FAILED_TO_DELETE_DIRECTORIES
 | |
|         };
 | |
|         typedef DetailedError<ServerCommandErrorType> ServerCommandError;
 | |
| 
 | |
|         class AbstractProvider {
 | |
|             public:
 | |
|                 typedef ExecuteResponse<DirectoryQueryError, std::deque<DirectoryEntry>> directory_query_response_t;
 | |
| 
 | |
|                 /* server */
 | |
|                 [[nodiscard]] virtual std::shared_ptr<ExecuteResponse<ServerCommandError>> initialize_server(ServerId /* server */) = 0;
 | |
|                 [[nodiscard]] virtual std::shared_ptr<ExecuteResponse<ServerCommandError>> delete_server(ServerId /* server */) = 0;
 | |
| 
 | |
|                 /* channels */
 | |
|                 [[nodiscard]] virtual std::shared_ptr<directory_query_response_t> query_channel_directory(ServerId /* server */, ChannelId /* channel */, const std::string& /* path */) = 0;
 | |
|                 [[nodiscard]] virtual std::shared_ptr<ExecuteResponse<DirectoryModifyError>> create_channel_directory(ServerId /* server */, ChannelId /* channel */, const std::string& /* path */) = 0;
 | |
|                 [[nodiscard]] virtual std::shared_ptr<ExecuteResponse<FileModifyError>> delete_channel_file(ServerId /* server */, ChannelId /* channel */, const std::string& /* path */) = 0;
 | |
|                 [[nodiscard]] virtual std::shared_ptr<ExecuteResponse<FileModifyError>> rename_channel_file(ServerId /* server */, ChannelId /* channel */, const std::string& /* path */, const std::string& /* target */) = 0;
 | |
| 
 | |
|                 /* icons */
 | |
|                 [[nodiscard]] virtual std::shared_ptr<directory_query_response_t> query_icon_directory(ServerId /* server */) = 0;
 | |
|                 [[nodiscard]] virtual std::shared_ptr<ExecuteResponse<FileModifyError>> delete_icon(ServerId /* server */, const std::string& /* name */) = 0;
 | |
| 
 | |
|                 /* avatars */
 | |
|                 [[nodiscard]] virtual std::shared_ptr<directory_query_response_t> query_avatar_directory(ServerId /* server */) = 0;
 | |
|                 [[nodiscard]] virtual std::shared_ptr<ExecuteResponse<FileModifyError>> 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};
 | |
| 
 | |
|             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<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{};
 | |
| 
 | |
|             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
 | |
|             } 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,
 | |
| 
 | |
|                 TRANSFER_TIMEOUT,
 | |
| 
 | |
|                 DISK_IO_ERROR,
 | |
|                 DISK_TIMEOUT,
 | |
|                 DISK_INITIALIZE_ERROR,
 | |
| 
 | |
|                 NETWORK_IO_ERROR,
 | |
| 
 | |
|                 UNEXPECTED_CLIENT_DISCONNECT,
 | |
|                 UNEXPECTED_DISK_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<ExecuteResponse<TransferInitError, std::shared_ptr<Transfer>>> initialize_channel_transfer(Transfer::Direction /* direction */, ServerId /* server */, ChannelId /* channel */, const TransferInfo& /* info */) = 0;
 | |
|                 virtual std::shared_ptr<ExecuteResponse<TransferInitError, std::shared_ptr<Transfer>>> initialize_icon_transfer(Transfer::Direction /* direction */, ServerId /* server */, const TransferInfo& /* info */) = 0;
 | |
|                 virtual std::shared_ptr<ExecuteResponse<TransferInitError, std::shared_ptr<Transfer>>> initialize_avatar_transfer(Transfer::Direction /* direction */, ServerId /* server */, const TransferInfo& /* info */) = 0;
 | |
| 
 | |
|                 virtual std::shared_ptr<ExecuteResponse<TransferActionError>> stop_transfer(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 TransferError&)> callback_transfer_aborted{}; /* an error happened while transferring the data  */
 | |
|                 std::function<void(const std::shared_ptr<Transfer>&, const TransferStatistics&)> callback_transfer_statistics{};
 | |
|         };
 | |
|     }
 | |
| 
 | |
|     class AbstractFileServer {
 | |
|         public:
 | |
|             [[nodiscard]] virtual filesystem::AbstractProvider& file_system() = 0;
 | |
|             [[nodiscard]] virtual transfer::AbstractProvider& file_transfer() = 0;
 | |
|         private:
 | |
|     };
 | |
| 
 | |
|     extern bool initialize(std::string& /* error */);
 | |
|     extern void finalize();
 | |
| 
 | |
|     extern std::shared_ptr<AbstractFileServer> server();
 | |
| } |