2020-05-03 08:06:34 -04:00
# pragma once
# include <string>
# include <chrono>
# include <Definitions.h>
# include <condition_variable>
# include <variant>
# include <deque>
# include <functional>
2020-05-07 15:28:15 -04:00
# define TRANSFER_KEY_LENGTH (32)
2020-05-03 08:06:34 -04:00
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 ( ) ;
}
2020-05-07 15:28:15 -04:00
[ [ nodiscard ] ] inline bool succeeded ( ) const {
return this - > status = = ExecuteStatus : : SUCCESS ;
}
2020-05-03 08:06:34 -04:00
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 } ;
2020-05-07 15:28:15 -04:00
char transfer_key [ TRANSFER_KEY_LENGTH ] { } ;
2020-05-03 08:06:34 -04:00
std : : chrono : : system_clock : : time_point initialized_timestamp { } ;
enum Direction {
DIRECTION_UNKNOWN ,
DIRECTION_UPLOAD ,
DIRECTION_DOWNLOAD
} direction { DIRECTION_UNKNOWN } ;
2020-05-07 15:28:15 -04:00
struct Address {
std : : string hostname { } ;
uint16_t port { 0 } ;
} ;
std : : vector < Address > server_addresses { } ;
2020-05-03 08:06:34 -04:00
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 } ;
2020-05-07 15:28:15 -04:00
size_t expected_file_size { 0 } ; /* incl. the offset! */
2020-05-03 08:06:34 -04:00
size_t file_offset { 0 } ;
2020-05-07 15:28:15 -04:00
bool override_exiting { false } ;
2020-05-03 08:06:34 -04:00
} ;
struct TransferStatistics {
2020-05-07 15:28:15 -04:00
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 } ;
2020-05-03 08:06:34 -04:00
2020-05-07 15:28:15 -04:00
uint64_t file_bytes_transferred { 0 } ;
uint64_t delta_file_bytes_transferred { 0 } ;
2020-05-03 08:06:34 -04:00
2020-05-07 15:28:15 -04:00
size_t file_start_offset { 0 } ;
size_t file_current_offset { 0 } ;
size_t file_total_size { 0 } ;
2020-05-03 08:06:34 -04:00
} ;
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 ,
2020-05-07 15:28:15 -04:00
TRANSFER_TIMEOUT ,
2020-05-03 08:06:34 -04:00
DISK_IO_ERROR ,
2020-05-07 15:28:15 -04:00
DISK_TIMEOUT ,
DISK_INITIALIZE_ERROR ,
2020-05-03 08:06:34 -04:00
NETWORK_IO_ERROR ,
2020-05-07 15:28:15 -04:00
UNEXPECTED_CLIENT_DISCONNECT ,
UNEXPECTED_DISK_EOF
2020-05-03 08:06:34 -04:00
} 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 ;
2020-05-07 15:28:15 -04:00
virtual std : : shared_ptr < ExecuteResponse < TransferActionError > > stop_transfer ( transfer_id /* id */ , bool /* flush */ ) = 0 ;
2020-05-03 08:06:34 -04:00
2020-05-07 15:28:15 -04:00
std : : function < void ( const std : : shared_ptr < Transfer > & ) > callback_transfer_registered { } ; /* transfer has been registered */
2020-05-03 08:06:34 -04:00
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 */
2020-05-07 15:28:15 -04:00
std : : function < void ( const std : : shared_ptr < Transfer > & , const TransferError & ) > callback_transfer_aborted { } ; /* an error happened while transferring the data */
2020-05-03 08:06:34 -04:00
std : : function < void ( const std : : shared_ptr < Transfer > & , const TransferStatistics & ) > callback_transfer_statistics { } ;
} ;
}
class AbstractFileServer {
public :
[ [ nodiscard ] ] virtual filesystem : : AbstractProvider & file_system ( ) = 0 ;
2020-05-07 15:28:15 -04:00
[ [ nodiscard ] ] virtual transfer : : AbstractProvider & file_transfer ( ) = 0 ;
2020-05-03 08:06:34 -04:00
private :
} ;
extern bool initialize ( std : : string & /* error */ ) ;
extern void finalize ( ) ;
extern std : : shared_ptr < AbstractFileServer > server ( ) ;
}