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();
|
|
} |