2020-01-26 08:21:34 -05:00
//
// Created by wolverindev on 26.01.20.
//
# include <memory>
# include <bitset>
# include <algorithm>
# include <openssl/sha.h>
# include "../ConnectedClient.h"
# include "../InternalClient.h"
# include "../../server/VoiceServer.h"
# include "../voice/VoiceClient.h"
# include "../../InstanceHandler.h"
# include "../../server/QueryServer.h"
# include "../music/MusicClient.h"
# include "../query/QueryClient.h"
# include "../../manager/ConversationManager.h"
# include "../../manager/PermissionNameMapper.h"
2020-06-28 08:01:14 -04:00
# include "../../manager/ActionLogger.h"
2020-01-26 08:21:34 -05:00
# include "helpers.h"
2020-05-13 05:32:08 -04:00
# include <files/FileServer.h>
2020-01-26 08:21:34 -05:00
# include <misc/sassert.h>
# include <misc/base64.h>
# include <misc/hex.h>
# include <misc/rnd.h>
2021-02-07 06:27:40 -05:00
# include <log/LogUtils.h>
2020-01-26 08:21:34 -05:00
using namespace std : : chrono ;
using namespace std ;
using namespace ts ;
using namespace ts : : server ;
2020-05-13 05:32:08 -04:00
constexpr static auto kFileAPITimeout = std : : chrono : : milliseconds { 500 } ;
constexpr static auto kMaxClientTransfers = 10 ;
2020-01-26 08:21:34 -05:00
//ftgetfilelist cid=1 cpw path=\/ return_code=1:x
//Answer:
//1 .. n
// notifyfilelist cid=1 path=\/ return_code=1:x name=testFile size=35256 datetime=1509459767 type=1|name=testDir size=0 datetime=1509459741 type=0|name=testDir_2 size=0 datetime=1509459763 type=0
//notifyfilelistfinished cid=1 path=\/
command_result ConnectedClient : : handleCommandFTGetFileList ( Command & cmd ) {
2020-05-13 05:32:08 -04:00
using directory_query_response_t = file : : filesystem : : AbstractProvider : : directory_query_response_t ;
using DirectoryEntry = file : : filesystem : : DirectoryEntry ;
2020-01-26 08:21:34 -05:00
CMD_REQ_SERVER ;
CMD_RESET_IDLE ;
2020-05-13 05:32:08 -04:00
auto virtual_file_server = file : : server ( ) - > find_virtual_server ( this - > getServerId ( ) ) ;
if ( ! virtual_file_server ) return command_result { error : : file_virtual_server_not_registered } ;
2020-01-26 08:21:34 -05:00
2020-05-13 05:32:08 -04:00
auto & file_system = file : : server ( ) - > file_system ( ) ;
2020-01-26 08:21:34 -05:00
2020-05-13 05:32:08 -04:00
auto directory_path = cmd [ " path " ] . string ( ) ;
std : : shared_ptr < directory_query_response_t > query_result { } ;
2020-01-26 08:21:34 -05:00
if ( cmd [ 0 ] . has ( " cid " ) & & cmd [ " cid " ] ! = 0 ) { //Channel
auto channel = this - > server - > channelTree - > findChannel ( cmd [ " cid " ] . as < ChannelId > ( ) ) ;
2020-05-13 05:32:08 -04:00
if ( ! channel )
return command_result { error : : channel_invalid_id } ;
2021-04-14 08:57:04 -04:00
auto channel_password = cmd [ " cpw " ] . optional_string ( ) ;
if ( ! channel - > verify_password ( channel_password , this - > getType ( ) ! = ClientType : : CLIENT_QUERY ) & & ! permission : : v2 : : permission_granted ( 1 , this - > calculate_permission ( permission : : b_ft_ignore_password , channel - > channelId ( ) ) ) ) {
return channel_password . has_value ( ) ? command_result { error : : channel_invalid_password } : command_result { permission : : b_ft_ignore_password } ;
}
2020-01-26 08:21:34 -05:00
2020-05-13 05:32:08 -04:00
if ( ! channel - > permission_granted ( permission : : i_ft_needed_file_browse_power , this - > calculate_permission ( permission : : i_ft_file_browse_power , channel - > channelId ( ) ) , true ) )
return command_result { permission : : i_ft_file_browse_power } ;
query_result = file_system . query_channel_directory ( virtual_file_server , cmd [ " cid " ] . as < ChannelId > ( ) , cmd [ " path " ] . string ( ) ) ;
2020-01-26 08:21:34 -05:00
} else {
2020-05-13 05:32:08 -04:00
if ( directory_path = = " /icons " | | directory_path = = " /icons/ " ) {
if ( ! permission : : v2 : : permission_granted ( 1 , this - > calculate_permission ( permission : : b_icon_manage , 0 ) ) )
return command_result { permission : : b_icon_manage } ;
query_result = file_system . query_icon_directory ( virtual_file_server ) ;
} else if ( directory_path = = " / " ) {
if ( ! permission : : v2 : : permission_granted ( 1 , this - > calculate_permission ( permission : : b_icon_manage , 0 ) ) )
return command_result { permission : : b_icon_manage } ;
query_result = file_system . query_avatar_directory ( virtual_file_server ) ;
2020-01-26 08:21:34 -05:00
} else {
2020-05-13 05:32:08 -04:00
debugMessage ( this - > getServerId ( ) , " {} Requested file list for unknown path/name: path: {} name: {} " , cmd [ " path " ] . string ( ) , cmd [ " name " ] . string ( ) ) ;
return command_result { error : : parameter_invalid , " path " } ;
2020-01-26 08:21:34 -05:00
}
}
2021-01-14 16:16:56 -05:00
if ( ! query_result - > wait_for ( kFileAPITimeout ) ) {
2020-05-13 05:32:08 -04:00
return command_result { error : : file_api_timeout } ;
2021-01-14 16:16:56 -05:00
}
2020-05-13 05:32:08 -04:00
if ( ! query_result - > succeeded ( ) ) {
debugMessage ( this - > getServerId ( ) , " {} Failed to query directory: {} / {} " , CLIENT_STR_LOG_PREFIX , file : : filesystem : : directory_query_error_messages [ ( int ) query_result - > error ( ) . error_type ] , query_result - > error ( ) . error_message ) ;
using ErrorType = file : : filesystem : : DirectoryQueryErrorType ;
switch ( query_result - > error ( ) . error_type ) {
case ErrorType : : UNKNOWN :
case ErrorType : : FAILED_TO_LIST_FILES :
return command_result { error : : vs_critical , query_result - > error ( ) . error_message } ;
case ErrorType : : PATH_IS_A_FILE :
case ErrorType : : PATH_EXCEEDS_ROOT_PATH :
return command_result { error : : file_invalid_path } ;
case ErrorType : : PATH_DOES_NOT_EXISTS :
/* directory has not been created because there are no files */
if ( directory_path . empty ( ) | | directory_path = = " / " )
return command_result { error : : database_empty_result } ;
return command_result { error : : file_not_found } ;
default :
assert ( false ) ;
return command_result { error : : vs_critical } ;
}
}
const auto & files = query_result - > response ( ) ;
2021-01-14 16:16:56 -05:00
if ( files . empty ( ) ) {
2020-01-26 08:21:34 -05:00
return command_result { error : : database_empty_result } ;
2021-01-14 16:16:56 -05:00
}
2020-05-13 05:32:08 -04:00
auto return_code = cmd [ " return_code " ] . size ( ) > 0 ? cmd [ " return_code " ] . string ( ) : " " ;
{
ts : : command_builder notify_file_list { this - > notify_response_command ( " notifyfilelist " ) } ;
size_t bulk_index { 0 } ;
for ( const auto & file : files ) {
if ( bulk_index = = 0 ) {
notify_file_list . reset ( ) ;
notify_file_list . put_unchecked ( 0 , " path " , cmd [ " path " ] . string ( ) ) ;
notify_file_list . put_unchecked ( 0 , " cid " , cmd [ " cid " ] . string ( ) ) ;
2021-01-14 16:16:56 -05:00
if ( ! return_code . empty ( ) ) {
2020-05-13 05:32:08 -04:00
notify_file_list . put_unchecked ( 0 , " return_code " , return_code ) ;
2021-01-14 16:16:56 -05:00
}
2020-05-13 05:32:08 -04:00
}
auto bulk = notify_file_list . bulk ( bulk_index + + ) ;
switch ( file . type ) {
case DirectoryEntry : : DIRECTORY :
bulk . put_unchecked ( " type " , " 0 " ) ;
break ;
case DirectoryEntry : : FILE :
bulk . put_unchecked ( " type " , " 1 " ) ;
break ;
case DirectoryEntry : : UNKNOWN :
bulk . reset ( ) ;
bulk_index - - ;
continue ;
}
bulk . put_unchecked ( " name " , file . name ) ;
bulk . put_unchecked ( " size " , file . size ) ;
2020-06-10 12:17:32 -04:00
bulk . put_unchecked ( " empty " , file . empty ) ;
2020-05-13 05:32:08 -04:00
bulk . put_unchecked ( " datetime " , std : : chrono : : duration_cast < std : : chrono : : seconds > ( file . modified_at . time_since_epoch ( ) ) . count ( ) ) ;
2020-06-25 12:15:15 -04:00
if ( bulk_index > = 16 & & this - > getType ( ) ! = ClientType : : CLIENT_QUERY ) {
2020-05-13 05:32:08 -04:00
this - > sendCommand ( notify_file_list ) ;
bulk_index = 0 ;
}
}
if ( bulk_index > 0 )
this - > sendCommand ( notify_file_list ) ;
}
if ( this - > getExternalType ( ) ! = ClientType : : CLIENT_QUERY ) {
ts : : command_builder notify_file_list_finished { this - > notify_response_command ( " notifyfilelistfinished " ) } ;
notify_file_list_finished . put_unchecked ( 0 , " path " , cmd [ " path " ] . string ( ) ) ;
notify_file_list_finished . put_unchecked ( 0 , " cid " , cmd [ " cid " ] . string ( ) ) ;
if ( ! return_code . empty ( ) )
notify_file_list_finished . put_unchecked ( 0 , " return_code " , return_code ) ;
this - > sendCommand ( notify_file_list_finished ) ;
2020-01-26 08:21:34 -05:00
}
2020-05-13 05:32:08 -04:00
return command_result { error : : ok } ;
2020-01-26 08:21:34 -05:00
}
//ftcreatedir cid=4 cpw dirname=\/TestDir return_code=1:17
command_result ConnectedClient : : handleCommandFTCreateDir ( Command & cmd ) {
2020-05-13 05:32:08 -04:00
using ErrorType = file : : filesystem : : DirectoryModifyErrorType ;
2020-01-26 08:21:34 -05:00
CMD_REQ_SERVER ;
CMD_CHK_AND_INC_FLOOD_POINTS ( 5 ) ;
2020-05-13 05:32:08 -04:00
auto virtual_file_server = file : : server ( ) - > find_virtual_server ( this - > getServerId ( ) ) ;
if ( ! virtual_file_server ) return command_result { error : : file_virtual_server_not_registered } ;
auto & file_system = file : : server ( ) - > file_system ( ) ;
2020-01-26 08:21:34 -05:00
2021-04-14 08:57:04 -04:00
std : : shared_lock channel_tree_lock { this - > server - > channel_tree_mutex } ;
2020-01-26 08:21:34 -05:00
auto channel = this - > server - > channelTree - > findChannel ( cmd [ " cid " ] . as < ChannelId > ( ) ) ;
2020-05-13 05:32:08 -04:00
if ( ! channel )
return command_result { error : : channel_invalid_id } ;
2021-04-14 08:57:04 -04:00
auto channel_password = cmd [ " cpw " ] . optional_string ( ) ;
if ( ! channel - > verify_password ( channel_password , this - > getType ( ) ! = ClientType : : CLIENT_QUERY ) & & ! permission : : v2 : : permission_granted ( 1 , this - > calculate_permission ( permission : : b_ft_ignore_password , channel - > channelId ( ) ) ) ) {
return channel_password . has_value ( ) ? command_result { error : : channel_invalid_password } : command_result { permission : : b_ft_ignore_password } ;
}
2020-01-26 08:21:34 -05:00
2020-05-13 05:32:08 -04:00
if ( ! channel - > permission_granted ( permission : : i_ft_needed_directory_create_power , this - > calculate_permission ( permission : : i_ft_directory_create_power , channel - > channelId ( ) ) , true ) )
return command_result { permission : : i_ft_directory_create_power } ;
2020-06-28 08:01:14 -04:00
channel_tree_lock . unlock ( ) ;
2020-05-13 05:32:08 -04:00
auto create_result = file_system . create_channel_directory ( virtual_file_server , channel - > channelId ( ) , cmd [ " dirname " ] . string ( ) ) ;
if ( ! create_result - > wait_for ( kFileAPITimeout ) )
return command_result { error : : file_api_timeout } ;
if ( ! create_result - > succeeded ( ) ) {
debugMessage ( this - > getServerId ( ) , " {} Failed to create channel directory: {} / {} " , CLIENT_STR_LOG_PREFIX , ( int ) create_result - > error ( ) . error_type , create_result - > error ( ) . error_message ) ;
switch ( create_result - > error ( ) . error_type ) {
case ErrorType : : UNKNOWN :
case ErrorType : : FAILED_TO_CREATE_DIRECTORIES : {
auto error_detail = std : : to_string ( ( int ) create_result - > error ( ) . error_type ) ;
if ( ! create_result - > error ( ) . error_message . empty ( ) )
error_detail + = " / " + create_result - > error ( ) . error_message ;
return command_result { error : : file_io_error , error_detail } ;
}
case ErrorType : : PATH_ALREADY_EXISTS :
return command_result { error : : file_already_exists } ;
case ErrorType : : PATH_EXCEEDS_ROOT_PATH :
return command_result { error : : file_invalid_path } ;
}
}
2020-01-26 08:21:34 -05:00
2020-06-28 08:01:14 -04:00
serverInstance - > action_logger ( ) - > file_logger . log_file_directory_create ( this - > getServerId ( ) , this - > ref ( ) , channel - > channelId ( ) , cmd [ " dirname " ] . string ( ) ) ;
2020-01-26 08:21:34 -05:00
return command_result { error : : ok } ;
}
command_result ConnectedClient : : handleCommandFTDeleteFile ( Command & cmd ) {
2020-05-13 05:32:08 -04:00
using ErrorType = file : : filesystem : : FileDeleteErrorType ;
2020-01-26 08:21:34 -05:00
CMD_REQ_SERVER ;
CMD_CHK_AND_INC_FLOOD_POINTS ( 5 ) ;
2020-05-13 05:32:08 -04:00
auto virtual_file_server = file : : server ( ) - > find_virtual_server ( this - > getServerId ( ) ) ;
if ( ! virtual_file_server ) return command_result { error : : file_virtual_server_not_registered } ;
auto & file_system = file : : server ( ) - > file_system ( ) ;
2020-01-26 08:21:34 -05:00
2020-05-13 05:32:08 -04:00
ts : : command_result_bulk response { } ;
response . emplace_result_n ( cmd . bulkCount ( ) , error : : ok ) ;
2020-06-28 08:01:14 -04:00
std : : vector < std : : tuple < uint64_t , std : : string > > file_log_info { } ;
2020-05-13 05:32:08 -04:00
auto file_path = cmd [ " path " ] . string ( ) ;
std : : shared_ptr < file : : ExecuteResponse < file : : filesystem : : FileDeleteError , file : : filesystem : : FileDeleteResponse > > delete_response { } ;
if ( cmd [ 0 ] . has ( " cid " ) & & cmd [ " cid " ] ! = 0 ) {
2021-04-14 08:57:04 -04:00
std : : shared_lock channel_tree_lock { this - > server - > channel_tree_mutex } ;
2020-01-26 08:21:34 -05:00
auto channel = this - > server - > channelTree - > findChannel ( cmd [ " cid " ] . as < ChannelId > ( ) ) ;
2020-05-13 05:32:08 -04:00
if ( ! channel )
return command_result { error : : channel_invalid_id } ;
2021-04-14 08:57:04 -04:00
auto channel_password = cmd [ " cpw " ] . optional_string ( ) ;
if ( ! channel - > verify_password ( channel_password , this - > getType ( ) ! = ClientType : : CLIENT_QUERY ) & & ! permission : : v2 : : permission_granted ( 1 , this - > calculate_permission ( permission : : b_ft_ignore_password , channel - > channelId ( ) ) ) ) {
return channel_password . has_value ( ) ? command_result { error : : channel_invalid_password } : command_result { permission : : b_ft_ignore_password } ;
}
2020-01-26 08:21:34 -05:00
2020-05-13 05:32:08 -04:00
if ( ! channel - > permission_granted ( permission : : i_ft_needed_file_delete_power , this - > calculate_permission ( permission : : i_ft_file_delete_power , channel - > channelId ( ) ) , true ) )
return command_result { permission : : i_ft_file_delete_power } ;
std : : vector < std : : string > delete_files { } ;
delete_files . reserve ( cmd . bulkCount ( ) ) ;
2020-06-28 08:01:14 -04:00
file_log_info . reserve ( cmd . bulkCount ( ) ) ;
for ( size_t index { 0 } ; index < cmd . bulkCount ( ) ; index + + ) {
2020-06-10 12:17:32 -04:00
delete_files . push_back ( file_path + " / " + cmd [ index ] [ " name " ] . string ( ) ) ;
2020-06-28 08:01:14 -04:00
file_log_info . emplace_back ( channel - > channelId ( ) , file_path + " / " + cmd [ index ] [ " name " ] . string ( ) ) ;
}
2020-05-13 05:32:08 -04:00
delete_response = file_system . delete_channel_files ( virtual_file_server , channel - > channelId ( ) , delete_files ) ;
2020-01-26 08:21:34 -05:00
} else {
2020-05-13 05:32:08 -04:00
auto first_entry_name = cmd [ " name " ] . string ( ) ;
if ( first_entry_name . find ( " /icon_ " ) = = 0 & & file_path . empty ( ) ) {
if ( ! permission : : v2 : : permission_granted ( 1 , this - > calculate_permission ( permission : : b_icon_manage , 0 ) ) )
return command_result { permission : : b_icon_manage } ;
std : : vector < std : : string > delete_files { } ;
delete_files . reserve ( cmd . bulkCount ( ) ) ;
2020-06-28 08:01:14 -04:00
file_log_info . reserve ( cmd . bulkCount ( ) ) ;
2020-05-13 05:32:08 -04:00
for ( size_t index { 0 } ; index < cmd . bulkCount ( ) ; index + + ) {
auto file_name = cmd [ index ] [ " name " ] . string ( ) ;
if ( ! file_name . starts_with ( " /icon_ " ) ) {
response . set_result ( index , ts : : command_result { error : : parameter_constraint_violation } ) ;
continue ;
2020-01-26 08:21:34 -05:00
}
2020-05-13 05:32:08 -04:00
delete_files . push_back ( file_name ) ;
2020-06-28 08:01:14 -04:00
file_log_info . emplace_back ( 0 , file_name ) ;
2020-01-26 08:21:34 -05:00
}
2020-05-13 05:32:08 -04:00
delete_response = file_system . delete_icons ( virtual_file_server , delete_files ) ;
} else if ( first_entry_name . starts_with ( " /avatar_ " ) & & file_path . empty ( ) ) {
enum PermissionTestState {
SUCCEEDED ,
FAILED ,
UNSET
} permission_delete_other { PermissionTestState : : UNSET } ;
std : : vector < std : : string > delete_files { } ;
delete_files . reserve ( cmd . bulkCount ( ) ) ;
2020-06-28 08:01:14 -04:00
file_log_info . reserve ( cmd . bulkCount ( ) ) ;
2020-05-13 05:32:08 -04:00
for ( size_t index { 0 } ; index < cmd . bulkCount ( ) ; index + + ) {
auto file_name = cmd [ index ] [ " name " ] . string ( ) ;
if ( ! file_name . starts_with ( " /avatar_ " ) ) {
response . set_result ( index , ts : : command_result { error : : parameter_constraint_violation } ) ;
continue ;
}
if ( file_name ! = " /avatar_ " ) {
if ( permission_delete_other = = PermissionTestState : : UNSET ) {
if ( permission : : v2 : : permission_granted ( 1 , this - > calculate_permission ( permission : : b_client_avatar_delete_other , 0 ) ) )
permission_delete_other = PermissionTestState : : SUCCEEDED ;
else
permission_delete_other = PermissionTestState : : FAILED ;
}
if ( permission_delete_other ! = PermissionTestState : : SUCCEEDED ) {
response . set_result ( index , ts : : command_result { permission : : b_client_avatar_delete_other } ) ;
continue ;
}
auto uid = file_name . substr ( strlen ( " /avatar_ " ) ) ;
auto avId = hex : : hex ( base64 : : decode ( uid ) , ' a ' , ' q ' ) ;
auto cls = this - > server - > findClientsByUid ( uid ) ;
for ( const auto & cl : cls ) {
cl - > properties ( ) [ property : : CLIENT_FLAG_AVATAR ] = " " ;
this - > server - > notifyClientPropertyUpdates ( cl , deque < property : : ClientProperties > { property : : CLIENT_FLAG_AVATAR } ) ;
}
delete_files . push_back ( " /avatar_ " + avId ) ;
2020-06-28 08:01:14 -04:00
file_log_info . emplace_back ( 0 , " /avatar_ " + avId ) ;
2020-05-13 05:32:08 -04:00
} else {
this - > properties ( ) [ property : : CLIENT_FLAG_AVATAR ] = " " ;
2021-02-21 14:28:59 -05:00
this - > server - > notifyClientPropertyUpdates ( this - > ref ( ) , deque < property : : ClientProperties > { property : : CLIENT_FLAG_AVATAR } ) ;
2020-06-28 08:01:14 -04:00
delete_files . push_back ( " /avatar_ " + this - > getAvatarId ( ) ) ;
file_log_info . emplace_back ( 0 , " /avatar_ " + this - > getAvatarId ( ) ) ;
2020-05-13 05:32:08 -04:00
}
}
delete_response = file_system . delete_avatars ( virtual_file_server , delete_files ) ;
2020-01-26 08:21:34 -05:00
} else {
logError ( this - > getServerId ( ) , " Unknown requested file to delete: {} " , cmd [ " path " ] . as < std : : string > ( ) ) ;
return command_result { error : : not_implemented } ;
}
}
2020-05-13 05:32:08 -04:00
if ( ! delete_response - > wait_for ( kFileAPITimeout ) )
return command_result { error : : file_api_timeout } ;
if ( ! delete_response - > succeeded ( ) ) {
debugMessage ( this - > getServerId ( ) , " {} Failed to create channel directory: {} / {} " , CLIENT_STR_LOG_PREFIX , ( int ) delete_response - > error ( ) . error_type , delete_response - > error ( ) . error_message ) ;
switch ( delete_response - > error ( ) . error_type ) {
case ErrorType : : UNKNOWN : {
auto error_detail = std : : to_string ( ( int ) delete_response - > error ( ) . error_type ) ;
if ( ! delete_response - > error ( ) . error_message . empty ( ) )
error_detail + = " / " + delete_response - > error ( ) . error_message ;
return command_result { error : : vs_critical , error_detail } ;
}
2020-01-26 08:21:34 -05:00
}
}
2020-05-13 05:32:08 -04:00
const auto & file_status = delete_response - > response ( ) ;
size_t bulk_index { 0 } ;
2020-06-28 08:01:14 -04:00
for ( size_t index { 0 } ; index < file_status . delete_results . size ( ) ; index + + ) {
const auto & file = file_status . delete_results [ index ] ;
const auto & log_file_info = file_log_info [ index ] ;
2020-05-13 05:32:08 -04:00
while ( response . response ( bulk_index ) . error_code ( ) ! = error : : ok )
bulk_index + + ;
using Status = file : : filesystem : : FileDeleteResponse : : StatusType ;
switch ( file . status ) {
case Status : : SUCCESS :
2020-06-28 08:01:14 -04:00
serverInstance - > action_logger ( ) - > file_logger . log_file_delete ( this - > getServerId ( ) , this - > ref ( ) , std : : get < 0 > ( log_file_info ) , std : : get < 1 > ( log_file_info ) ) ;
2020-05-13 05:32:08 -04:00
/* we already emplaced success */
break ;
case Status : : PATH_EXCEEDS_ROOT_PATH :
response . set_result ( bulk_index , ts : : command_result { error : : file_invalid_path } ) ;
break ;
case Status : : PATH_DOES_NOT_EXISTS :
response . set_result ( bulk_index , ts : : command_result { error : : file_not_found } ) ;
break ;
case Status : : SOME_FILES_ARE_LOCKED :
response . set_result ( bulk_index , ts : : command_result { error : : file_already_in_use , file . error_detail } ) ;
break ;
case Status : : FAILED_TO_DELETE_FILES :
response . set_result ( bulk_index , ts : : command_result { error : : file_io_error , file . error_detail } ) ;
break ;
}
bulk_index + + ;
}
while ( response . length ( ) > bulk_index & & response . response ( bulk_index ) . type ( ) = = command_result_type : : error & & response . response ( bulk_index ) . error_code ( ) ! = error : : ok )
bulk_index + + ;
assert ( bulk_index = = cmd . bulkCount ( ) ) ;
return command_result { std : : move ( response ) } ;
2020-01-26 08:21:34 -05:00
}
2020-05-13 05:32:08 -04:00
/*
* Usage : ftgetfileinfo cid = { channelID } cpw = { channelPassword } name = { filePath } . . .
Permissions :
i_ft_file_browse_power
i_ft_needed_file_browse_power
Description :
Displays detailed information about one or more specified files stored in a
channels file repository .
Example :
ftgetfileinfo cid = 2 cpw = path = \ / Pic1 . PNG | cid = 2 cpw = path = \ / Pic2 . PNG
cid = 2 path = \ / name = Stuff size = 0 datetime = 1259415210 type = 0 | name = Pic1 . PNG size = 563783 datetime = 1259425462 type = 1 | name = Pic2 . PNG . . .
error id = 0 msg = ok
*/
command_result ConnectedClient : : handleCommandFTGetFileInfo ( ts : : Command & cmd ) {
using ErrorType = file : : filesystem : : FileInfoErrorType ;
using DirectoryEntry = file : : filesystem : : DirectoryEntry ;
CMD_RESET_IDLE ;
2020-01-26 08:21:34 -05:00
CMD_REQ_SERVER ;
2020-06-10 12:17:32 -04:00
CMD_CHK_AND_INC_FLOOD_POINTS ( 25 ) ;
2020-01-26 08:21:34 -05:00
2020-05-13 05:32:08 -04:00
auto virtual_file_server = file : : server ( ) - > find_virtual_server ( this - > getServerId ( ) ) ;
if ( ! virtual_file_server ) return command_result { error : : file_virtual_server_not_registered } ;
auto & file_system = file : : server ( ) - > file_system ( ) ;
2020-01-26 08:21:34 -05:00
2020-05-13 05:32:08 -04:00
ts : : command_result_bulk response { } ;
response . emplace_result_n ( cmd . bulkCount ( ) , error : : ok ) ;
auto file_path = cmd [ " path " ] . string ( ) ;
std : : shared_ptr < file : : ExecuteResponse < file : : filesystem : : FileInfoError , file : : filesystem : : FileInfoResponse > > info_response { } ;
if ( cmd [ 0 ] . has ( " cid " ) & & cmd [ " cid " ] ! = 0 ) {
2020-06-10 12:17:32 -04:00
std : : shared_ptr < BasicChannel > currentChannel { } ;
error : : type error_error { error : : ok } ;
permission : : PermissionType permission_error { permission : : ok } ;
std : : vector < std : : tuple < ChannelId , std : : string > > info_files { } ;
info_files . reserve ( cmd . bulkCount ( ) ) ;
for ( size_t index { 0 } ; index < cmd . bulkCount ( ) ; index + + ) {
if ( cmd [ index ] . has ( " cid " ) ) {
error_error = error : : ok ;
permission_error = permission : : ok ;
currentChannel = this - > server - > channelTree - > findChannel ( cmd [ index ] [ " cid " ] . as < ChannelId > ( ) ) ;
if ( currentChannel ) {
const auto password = cmd [ index ] [ " cpw " ] . string ( ) ;
2021-04-14 08:57:04 -04:00
if ( ! currentChannel - > verify_password ( std : : make_optional ( password ) , this - > getType ( ) ! = ClientType : : CLIENT_QUERY ) & & ! permission : : v2 : : permission_granted ( 1 , this - > calculate_permission ( permission : : b_ft_ignore_password , currentChannel - > channelId ( ) ) ) ) {
2020-06-10 12:17:32 -04:00
if ( password . empty ( ) )
error_error = error : : channel_invalid_password ;
else
permission_error = permission : : b_ft_ignore_password ;
} else if ( ! currentChannel - > permission_granted ( permission : : i_ft_needed_file_browse_power , this - > calculate_permission ( permission : : i_ft_file_browse_power , currentChannel - > channelId ( ) ) , true ) ) {
permission_error = permission : : i_ft_file_browse_power ;
}
} else {
error_error = error : : channel_invalid_id ;
}
}
2020-05-13 05:32:08 -04:00
2020-06-10 12:17:32 -04:00
if ( error_error ! = error : : ok ) {
response . set_result ( index , ts : : command_result { error_error } ) ;
continue ;
}
2020-05-13 05:32:08 -04:00
2020-06-10 12:17:32 -04:00
if ( permission_error ! = permission : : ok ) {
response . set_result ( index , ts : : command_result { permission_error } ) ;
continue ;
}
info_files . emplace_back ( currentChannel - > channelId ( ) , cmd [ index ] [ " name " ] . string ( ) ) ;
}
2020-05-13 05:32:08 -04:00
2020-06-10 12:17:32 -04:00
info_response = file_system . query_channel_info ( virtual_file_server , info_files ) ;
2020-01-26 08:21:34 -05:00
} else {
2020-05-13 05:32:08 -04:00
auto first_entry_name = cmd [ " name " ] . string ( ) ;
if ( first_entry_name . find ( " /icon_ " ) = = 0 & & file_path . empty ( ) ) {
if ( ! permission : : v2 : : permission_granted ( 1 , this - > calculate_permission ( permission : : b_icon_manage , 0 ) ) )
return command_result { permission : : b_icon_manage } ;
std : : vector < std : : string > delete_files { } ;
delete_files . reserve ( cmd . bulkCount ( ) ) ;
for ( size_t index { 0 } ; index < cmd . bulkCount ( ) ; index + + ) {
auto file_name = cmd [ index ] [ " name " ] . string ( ) ;
if ( ! file_name . starts_with ( " /icon_ " ) ) {
response . set_result ( index , ts : : command_result { error : : parameter_constraint_violation } ) ;
continue ;
}
2020-01-26 08:21:34 -05:00
2020-05-13 05:32:08 -04:00
delete_files . push_back ( file_name ) ;
}
info_response = file_system . query_icon_info ( virtual_file_server , delete_files ) ;
} else if ( first_entry_name . starts_with ( " /avatar_ " ) & & file_path . empty ( ) ) {
if ( ! permission : : v2 : : permission_granted ( 1 , this - > calculate_permission ( permission : : b_icon_manage , 0 ) ) )
return command_result { permission : : b_icon_manage } ;
std : : vector < std : : string > delete_files { } ;
delete_files . reserve ( cmd . bulkCount ( ) ) ;
for ( size_t index { 0 } ; index < cmd . bulkCount ( ) ; index + + ) {
auto file_name = cmd [ index ] [ " name " ] . string ( ) ;
if ( ! file_name . starts_with ( " /avatar_ " ) ) {
response . set_result ( index , ts : : command_result { error : : parameter_constraint_violation } ) ;
continue ;
}
if ( file_name ! = " /avatar_ " ) {
auto uid = file_name . substr ( strlen ( " /avatar_ " ) ) ;
auto avId = hex : : hex ( base64 : : decode ( uid ) , ' a ' , ' q ' ) ;
delete_files . push_back ( " /avatar_ " + avId ) ;
} else {
2020-06-28 08:01:14 -04:00
delete_files . push_back ( " /avatar_ " + this - > getAvatarId ( ) ) ;
2020-05-13 05:32:08 -04:00
}
}
info_response = file_system . query_avatar_info ( virtual_file_server , delete_files ) ;
2020-01-26 08:21:34 -05:00
} else {
2020-05-13 05:32:08 -04:00
logError ( this - > getServerId ( ) , " Unknown requested file to query info: {} " , cmd [ " path " ] . as < std : : string > ( ) ) ;
return command_result { error : : parameter_invalid } ;
2020-01-26 08:21:34 -05:00
}
}
2021-01-28 14:59:15 -05:00
if ( ! info_response - > wait_for ( kFileAPITimeout ) ) {
2020-05-13 05:32:08 -04:00
return command_result { error : : file_api_timeout } ;
2021-01-28 14:59:15 -05:00
}
2020-05-13 05:32:08 -04:00
if ( ! info_response - > succeeded ( ) ) {
debugMessage ( this - > getServerId ( ) , " {} Failed to execute file info query: {} / {} " , CLIENT_STR_LOG_PREFIX , ( int ) info_response - > error ( ) . error_type , info_response - > error ( ) . error_message ) ;
switch ( info_response - > error ( ) . error_type ) {
case ErrorType : : UNKNOWN : {
auto error_detail = std : : to_string ( ( int ) info_response - > error ( ) . error_type ) ;
2021-01-28 14:59:15 -05:00
if ( ! info_response - > error ( ) . error_message . empty ( ) ) {
2020-05-13 05:32:08 -04:00
error_detail + = " / " + info_response - > error ( ) . error_message ;
2021-01-28 14:59:15 -05:00
}
2020-05-13 05:32:08 -04:00
return command_result { error : : vs_critical , error_detail } ;
}
}
}
2020-06-10 12:17:32 -04:00
const auto as_list = cmd . hasParm ( " as-list " ) ;
2020-05-13 05:32:08 -04:00
const auto & file_status = info_response - > response ( ) ;
size_t bulk_index { 0 } ;
ts : : command_builder notify_file_info { this - > notify_response_command ( " notifyfileinfo " ) } ;
size_t notify_index { 0 } ;
for ( const auto & file : file_status . file_info ) {
2021-01-28 14:59:15 -05:00
while ( response . response ( bulk_index ) . error_code ( ) ! = error : : ok ) {
2020-05-13 05:32:08 -04:00
bulk_index + + ;
2021-01-28 14:59:15 -05:00
}
2020-05-13 05:32:08 -04:00
using Status = file : : filesystem : : FileInfoResponse : : StatusType ;
switch ( file . status ) {
case Status : : SUCCESS :
/* we already emplaced success */
break ;
case Status : : PATH_EXCEEDS_ROOT_PATH :
response . set_result ( bulk_index , ts : : command_result { error : : file_invalid_path } ) ;
break ;
case Status : : PATH_DOES_NOT_EXISTS :
case Status : : UNKNOWN_FILE_TYPE :
response . set_result ( bulk_index , ts : : command_result { error : : file_not_found } ) ;
break ;
case Status : : FAILED_TO_QUERY_INFO :
response . set_result ( bulk_index , ts : : command_result { error : : file_io_error , file . error_detail } ) ;
break ;
}
bulk_index + + ;
2020-06-10 12:17:32 -04:00
if ( notify_index = = 0 ) {
notify_file_info . reset ( ) ;
notify_file_info . put_unchecked ( 0 , " return_code " , cmd [ " return_code " ] . string ( ) ) ;
}
2020-05-13 05:32:08 -04:00
auto bulk = notify_file_info . bulk ( notify_index + + ) ;
switch ( file . info . type ) {
case DirectoryEntry : : DIRECTORY :
bulk . put_unchecked ( " type " , " 0 " ) ;
break ;
case DirectoryEntry : : FILE :
bulk . put_unchecked ( " type " , " 1 " ) ;
break ;
case DirectoryEntry : : UNKNOWN :
bulk . reset ( ) ;
notify_index - - ;
continue ;
}
bulk . put_unchecked ( " name " , file . info . name ) ;
bulk . put_unchecked ( " size " , file . info . size ) ;
2020-06-10 12:17:32 -04:00
bulk . put_unchecked ( " empty " , file . info . empty ) ;
2020-05-13 05:32:08 -04:00
bulk . put_unchecked ( " datetime " , std : : chrono : : duration_cast < std : : chrono : : seconds > ( file . info . modified_at . time_since_epoch ( ) ) . count ( ) ) ;
2020-06-10 12:17:32 -04:00
if ( notify_index > 20 & & as_list ) {
this - > sendCommand ( notify_file_info ) ;
notify_index = 0 ;
}
2020-01-26 08:21:34 -05:00
}
2021-01-28 14:59:15 -05:00
if ( notify_index > 0 ) {
2020-05-13 05:32:08 -04:00
this - > sendCommand ( notify_file_info ) ;
2021-01-28 14:59:15 -05:00
}
2020-06-10 12:17:32 -04:00
if ( as_list & & this - > getExternalType ( ) = = ClientType : : CLIENT_TEAMSPEAK ) {
ts : : command_builder notify { this - > notify_response_command ( " notifyfileinfofinished " ) } ;
notify . put_unchecked ( 0 , " return_code " , cmd [ " return_code " ] . string ( ) ) ;
this - > sendCommand ( notify ) ;
}
2020-05-13 05:32:08 -04:00
2021-01-28 14:59:15 -05:00
while ( response . length ( ) > bulk_index & & response . response ( bulk_index ) . type ( ) = = command_result_type : : error & & response . response ( bulk_index ) . error_code ( ) ! = error : : ok ) {
2020-05-13 05:32:08 -04:00
bulk_index + + ;
2021-01-28 14:59:15 -05:00
}
2020-05-13 05:32:08 -04:00
assert ( bulk_index = = cmd . bulkCount ( ) ) ;
return command_result { std : : move ( response ) } ;
}
/*
ftinitupload clientftfid = { clientFileTransferID } name = { filePath }
cid = { channelID } cpw = { channelPassword } size = { fileSize } overwrite = { 1 | 0 }
resume = { 1 | 0 } [ proto = 0 - 1 ]
*/
command_result ConnectedClient : : handleCommandFTInitUpload ( ts : : Command & cmd ) {
CMD_REQ_SERVER ;
auto virtual_file_server = file : : server ( ) - > find_virtual_server ( this - > getServerId ( ) ) ;
if ( ! virtual_file_server ) return command_result { error : : file_virtual_server_not_registered } ;
if ( ! cmd [ 0 ] . has ( " path " ) ) cmd [ " path " ] = " " ;
file : : transfer : : AbstractProvider : : TransferInfo info { } ;
std : : shared_ptr < file : : ExecuteResponse < file : : transfer : : TransferInitError , std : : shared_ptr < file : : transfer : : Transfer > > > transfer_response { } ;
info . max_bandwidth = - 1 ;
2020-05-14 09:08:28 -04:00
{
auto max_quota = this - > calculate_permission ( permission : : i_ft_max_bandwidth_upload , this - > getClientId ( ) ) ;
if ( max_quota . has_value )
info . max_bandwidth = max_quota . value ;
}
2020-05-13 05:32:08 -04:00
info . file_offset = 0 ;
info . expected_file_size = cmd [ " size " ] . as < size_t > ( ) ;
info . override_exiting = cmd [ " overwrite " ] . as < bool > ( ) ;
2020-06-11 05:37:04 -04:00
info . file_path = cmd [ " path " ] . string ( ) + " / " + cmd [ " name " ] . string ( ) ;
2020-05-13 05:32:08 -04:00
info . client_unique_id = this - > getUid ( ) ;
info . client_id = this - > getClientId ( ) ;
info . max_concurrent_transfers = kMaxClientTransfers ;
/* TODO: Save last file offset and resume */
if ( cmd [ " resume " ] . as < bool > ( ) & & info . override_exiting )
return ts : : command_result { error : : file_overwrite_excludes_resume } ;
2020-01-26 08:21:34 -05:00
{
2021-03-01 08:16:44 -05:00
auto server_quota = this - > server - > properties ( ) [ property : : VIRTUALSERVER_UPLOAD_QUOTA ] . as_unchecked < ssize_t > ( ) ;
auto server_used_quota = this - > server - > properties ( ) [ property : : VIRTUALSERVER_MONTH_BYTES_UPLOADED ] . as_unchecked < size_t > ( ) ;
2020-01-26 08:21:34 -05:00
server_used_quota + = cmd [ " size " ] . as < uint64_t > ( ) ;
if ( server_quota > = 0 & & server_quota * 1024 * 1024 < ( int64_t ) server_used_quota ) return command_result { error : : file_transfer_server_quota_exceeded } ;
auto client_quota = this - > calculate_permission ( permission : : i_ft_quota_mb_upload_per_client , 0 ) ;
2021-03-01 08:16:44 -05:00
auto client_used_quota = this - > properties ( ) [ property : : CLIENT_MONTH_BYTES_UPLOADED ] . as_unchecked < size_t > ( ) ;
2020-01-26 08:21:34 -05:00
client_used_quota + = cmd [ " size " ] . as < uint64_t > ( ) ;
if ( client_quota . has_value & & ! client_quota . has_infinite_power ( ) & & ( client_quota . value < 0 | | client_quota . value * 1024 * 1024 < ( int64_t ) client_used_quota ) )
return command_result { error : : file_transfer_client_quota_exceeded } ;
}
2020-06-28 08:01:14 -04:00
ChannelId log_channel_id { 0 } ;
2020-05-13 05:32:08 -04:00
if ( cmd [ 0 ] . has ( " cid " ) & & cmd [ " cid " ] ! = 0 ) { //Channel
2021-04-14 08:57:04 -04:00
std : : shared_lock channel_tree_lock { this - > server - > channel_tree_mutex } ;
2020-05-13 05:32:08 -04:00
auto channel = this - > server - > channelTree - > findChannel ( cmd [ " cid " ] . as < ChannelId > ( ) ) ;
if ( ! channel )
return command_result { error : : channel_invalid_id , " Cant resolve channel " } ;
2021-04-14 08:57:04 -04:00
auto channel_password = cmd [ " cpw " ] . optional_string ( ) ;
if ( ! channel - > verify_password ( channel_password , this - > getType ( ) ! = ClientType : : CLIENT_QUERY ) & & ! permission : : v2 : : permission_granted ( 1 , this - > calculate_permission ( permission : : b_ft_ignore_password , channel - > channelId ( ) ) ) ) {
return channel_password . has_value ( ) ? command_result { error : : channel_invalid_password } : command_result { permission : : b_ft_ignore_password } ;
}
2020-05-13 05:32:08 -04:00
ACTION_REQUIRES_CHANNEL_PERMISSION ( channel , permission : : i_ft_needed_file_upload_power , permission : : i_ft_file_upload_power , true ) ;
transfer_response = file : : server ( ) - > file_transfer ( ) . initialize_channel_transfer ( file : : transfer : : Transfer : : DIRECTION_UPLOAD , virtual_file_server , channel - > channelId ( ) , info ) ;
2020-06-28 08:01:14 -04:00
log_channel_id = channel - > channelId ( ) ;
2020-05-13 05:32:08 -04:00
} else {
2020-06-10 12:17:32 -04:00
if ( cmd [ " path " ] . string ( ) . empty ( ) & & cmd [ " name " ] . string ( ) . starts_with ( " /icon_ " ) ) {
2020-05-13 05:32:08 -04:00
auto max_size = this - > calculate_permission ( permission : : i_max_icon_filesize , 0 ) ;
if ( max_size . has_value & & ! max_size . has_infinite_power ( ) & & ( max_size . value < 0 | | max_size . value < cmd [ " size " ] . as < size_t > ( ) ) )
return command_result { permission : : i_max_icon_filesize } ;
transfer_response = file : : server ( ) - > file_transfer ( ) . initialize_icon_transfer ( file : : transfer : : Transfer : : DIRECTION_UPLOAD , virtual_file_server , info ) ;
} else if ( cmd [ " path " ] . as < std : : string > ( ) . empty ( ) & & cmd [ " name " ] . string ( ) = = " /avatar " ) {
auto max_size = this - > calculate_permission ( permission : : i_client_max_avatar_filesize , 0 ) ;
if ( max_size . has_value & & ! max_size . has_infinite_power ( ) & & ( max_size . value < 0 | | max_size . value < cmd [ " size " ] . as < size_t > ( ) ) )
return command_result { permission : : i_client_max_avatar_filesize } ;
info . file_path = " /avatar_ " + this - > getAvatarId ( ) ;
transfer_response = file : : server ( ) - > file_transfer ( ) . initialize_avatar_transfer ( file : : transfer : : Transfer : : DIRECTION_UPLOAD , virtual_file_server , info ) ;
} else {
return command_result { error : : parameter_invalid , " name " } ;
}
}
if ( ! transfer_response - > wait_for ( kFileAPITimeout ) )
return command_result { error : : file_api_timeout } ;
if ( ! transfer_response - > succeeded ( ) ) {
using ErrorType = file : : transfer : : TransferInitError ;
debugMessage ( this - > getServerId ( ) , " {} Failed to initialize file upload: {} / {} " , CLIENT_STR_LOG_PREFIX , ( int ) transfer_response - > error ( ) . error_type , transfer_response - > error ( ) . error_message ) ;
switch ( transfer_response - > error ( ) . error_type ) {
case ErrorType : : UNKNOWN : {
auto error_detail = std : : to_string ( ( int ) transfer_response - > error ( ) . error_type ) ;
if ( ! transfer_response - > error ( ) . error_message . empty ( ) )
error_detail + = " / " + transfer_response - > error ( ) . error_message ;
return command_result { error : : vs_critical , error_detail } ;
2020-01-26 08:21:34 -05:00
}
2020-05-13 05:32:08 -04:00
case ErrorType : : IO_ERROR :
return command_result { error : : file_io_error , transfer_response - > error ( ) . error_message } ;
case ErrorType : : FILE_IS_NOT_A_FILE :
case ErrorType : : INVALID_FILE_TYPE :
case ErrorType : : FILE_DOES_NOT_EXISTS :
return command_result { error : : file_not_found } ;
case ErrorType : : SERVER_QUOTA_EXCEEDED :
return command_result { error : : file_transfer_server_quota_exceeded } ;
case ErrorType : : CLIENT_QUOTA_EXCEEDED :
return command_result { error : : file_transfer_client_quota_exceeded } ;
case ErrorType : : SERVER_TOO_MANY_TRANSFERS :
return command_result { error : : file_server_transfer_limit_reached } ;
case ErrorType : : CLIENT_TOO_MANY_TRANSFERS :
return command_result { error : : file_client_transfer_limit_reached } ;
2020-01-26 08:21:34 -05:00
}
}
2020-05-13 05:32:08 -04:00
auto transfer = transfer_response - > response ( ) ;
if ( transfer - > server_addresses . empty ( ) ) {
logError ( 0 , " {} Received transfer without any addresses! " , CLIENT_STR_LOG_PREFIX ) ;
return ts : : command_result { error : : vs_critical } ;
}
transfer - > client_transfer_id = cmd [ " clientftfid " ] ;
ts : : command_builder result { this - > notify_response_command ( " notifystartupload " ) } ;
result . put_unchecked ( 0 , " clientftfid " , cmd [ " clientftfid " ] . string ( ) ) ;
result . put_unchecked ( 0 , " serverftfid " , transfer - > server_transfer_id ) ;
auto used_address = transfer - > server_addresses [ 0 ] ;
result . put_unchecked ( 0 , " ip " , used_address . hostname ) ;
result . put_unchecked ( 0 , " port " , used_address . port ) ;
2021-04-21 07:19:46 -04:00
result . put_unchecked ( 0 , " ftkey " , transfer - > transfer_key ) ;
2020-05-13 05:32:08 -04:00
result . put_unchecked ( 0 , " seekpos " , transfer - > file_offset ) ;
result . put_unchecked ( 0 , " proto " , " 1 " ) ;
2020-01-26 08:21:34 -05:00
this - > sendCommand ( result ) ;
2020-06-28 08:01:14 -04:00
serverInstance - > action_logger ( ) - > file_logger . log_file_upload ( this - > getServerId ( ) , this - > ref ( ) , log_channel_id , info . file_path ) ;
2020-01-26 08:21:34 -05:00
return command_result { error : : ok } ;
}
2020-05-13 05:32:08 -04:00
/*
ftinitdownload clientftfid = { clientFileTransferID } name = { filePath }
cid = { channelID } cpw = { channelPassword } seekpos = { seekPosition } [ proto = 0 - 1 ]
*/
command_result ConnectedClient : : handleCommandFTInitDownload ( ts : : Command & cmd ) {
2020-01-26 08:21:34 -05:00
CMD_REQ_SERVER ;
2020-05-13 05:32:08 -04:00
auto virtual_file_server = file : : server ( ) - > find_virtual_server ( this - > getServerId ( ) ) ;
if ( ! virtual_file_server ) return command_result { error : : file_virtual_server_not_registered } ;
2020-01-26 08:21:34 -05:00
if ( ! cmd [ 0 ] . has ( " path " ) ) cmd [ " path " ] = " " ;
2020-05-13 05:32:08 -04:00
file : : transfer : : AbstractProvider : : TransferInfo info { } ;
std : : shared_ptr < file : : ExecuteResponse < file : : transfer : : TransferInitError , std : : shared_ptr < file : : transfer : : Transfer > > > transfer_response { } ;
{
2021-03-01 08:16:44 -05:00
auto server_quota = this - > server - > properties ( ) [ property : : VIRTUALSERVER_DOWNLOAD_QUOTA ] . as_unchecked < ssize_t > ( ) ;
auto server_used_quota = this - > server - > properties ( ) [ property : : VIRTUALSERVER_MONTH_BYTES_DOWNLOADED ] . as_unchecked < size_t > ( ) ;
2020-05-13 05:32:08 -04:00
if ( server_quota > = 0 ) {
if ( ( size_t ) server_quota * 1024 * 1024 < = server_used_quota )
return command_result { error : : file_transfer_server_quota_exceeded } ;
info . download_server_quota_limit = server_quota * 1024 * 1024 - server_used_quota ;
}
auto client_quota = this - > calculate_permission ( permission : : i_ft_quota_mb_download_per_client , 0 ) ;
2021-03-01 08:16:44 -05:00
auto client_used_quota = this - > properties ( ) [ property : : CLIENT_MONTH_BYTES_DOWNLOADED ] . as_unchecked < size_t > ( ) ;
2020-05-13 05:32:08 -04:00
if ( client_quota . has_value ) {
if ( client_quota . value > 0 ) {
if ( ( size_t ) client_quota . value * 1024 * 1024 < = client_used_quota )
2020-06-10 12:17:32 -04:00
return command_result { error : : file_transfer_client_quota_exceeded } ;
2020-05-13 05:32:08 -04:00
info . download_client_quota_limit = client_quota . value * 1024 * 1024 - client_used_quota ;
} else if ( client_quota . value ! = - 1 ) {
return command_result { error : : file_transfer_client_quota_exceeded } ;
}
}
}
info . max_bandwidth = - 1 ;
2020-05-14 09:08:28 -04:00
{
auto max_quota = this - > calculate_permission ( permission : : i_ft_max_bandwidth_download , this - > getClientId ( ) ) ;
if ( max_quota . has_value )
info . max_bandwidth = max_quota . value ;
}
2020-05-13 05:32:08 -04:00
info . file_offset = cmd [ " seekpos " ] . as < size_t > ( ) ;
info . override_exiting = false ;
2020-06-10 12:17:32 -04:00
info . file_path = cmd [ " path " ] . string ( ) + " / " + cmd [ " name " ] . string ( ) ;
2020-05-13 05:32:08 -04:00
info . client_unique_id = this - > getUid ( ) ;
info . client_id = this - > getClientId ( ) ;
info . max_concurrent_transfers = kMaxClientTransfers ;
2020-06-28 08:01:14 -04:00
ChannelId log_channel_id { 0 } ;
2020-05-13 05:32:08 -04:00
if ( cmd [ 0 ] . has ( " cid " ) & & cmd [ " cid " ] ! = 0 ) { //Channel
2021-04-14 08:57:04 -04:00
std : : shared_lock channel_tree_lock { this - > server - > channel_tree_mutex } ;
2020-01-26 08:21:34 -05:00
auto channel = this - > server - > channelTree - > findChannel ( cmd [ " cid " ] . as < ChannelId > ( ) ) ;
2020-05-13 05:32:08 -04:00
if ( ! channel )
return command_result { error : : channel_invalid_id } ;
2020-01-26 08:21:34 -05:00
2021-04-14 08:57:04 -04:00
auto channel_password = cmd [ " cpw " ] . optional_string ( ) ;
if ( ! channel - > verify_password ( channel_password , this - > getType ( ) ! = ClientType : : CLIENT_QUERY ) & & ! permission : : v2 : : permission_granted ( 1 , this - > calculate_permission ( permission : : b_ft_ignore_password , channel - > channelId ( ) ) ) ) {
return channel_password . has_value ( ) ? command_result { error : : channel_invalid_password } : command_result { permission : : b_ft_ignore_password } ;
}
2020-01-26 08:21:34 -05:00
2020-05-13 05:32:08 -04:00
ACTION_REQUIRES_CHANNEL_PERMISSION ( channel , permission : : i_ft_needed_file_download_power , permission : : i_ft_file_download_power , true ) ;
transfer_response = file : : server ( ) - > file_transfer ( ) . initialize_channel_transfer ( file : : transfer : : Transfer : : DIRECTION_DOWNLOAD , virtual_file_server , channel - > channelId ( ) , info ) ;
2020-06-28 08:01:14 -04:00
log_channel_id = channel - > channelId ( ) ;
2020-01-26 08:21:34 -05:00
} else {
2020-06-10 12:17:32 -04:00
if ( cmd [ " path " ] . as < std : : string > ( ) . empty ( ) & & cmd [ " name " ] . string ( ) . starts_with ( " /icon_ " ) ) {
2020-05-13 05:32:08 -04:00
transfer_response = file : : server ( ) - > file_transfer ( ) . initialize_icon_transfer ( file : : transfer : : Transfer : : DIRECTION_DOWNLOAD , virtual_file_server , info ) ;
2020-06-10 12:17:32 -04:00
} else if ( cmd [ " path " ] . as < std : : string > ( ) . empty ( ) & & cmd [ " name " ] . string ( ) . starts_with ( " /avatar " ) ) {
2020-05-13 05:32:08 -04:00
transfer_response = file : : server ( ) - > file_transfer ( ) . initialize_avatar_transfer ( file : : transfer : : Transfer : : DIRECTION_DOWNLOAD , virtual_file_server , info ) ;
2020-01-26 08:21:34 -05:00
} else {
2020-05-13 05:32:08 -04:00
return command_result { error : : parameter_invalid , " name " } ;
2020-01-26 08:21:34 -05:00
}
}
2020-05-13 05:32:08 -04:00
if ( ! transfer_response - > wait_for ( kFileAPITimeout ) )
return command_result { error : : file_api_timeout } ;
2020-01-26 08:21:34 -05:00
2020-05-13 05:32:08 -04:00
if ( ! transfer_response - > succeeded ( ) ) {
using ErrorType = file : : transfer : : TransferInitError ;
2020-01-26 08:21:34 -05:00
2020-06-10 12:17:32 -04:00
debugMessage ( this - > getServerId ( ) , " {} Failed to initialize file download: {}/{} " , CLIENT_STR_LOG_PREFIX , ( int ) transfer_response - > error ( ) . error_type , transfer_response - > error ( ) . error_message ) ;
2020-05-13 05:32:08 -04:00
switch ( transfer_response - > error ( ) . error_type ) {
case ErrorType : : UNKNOWN : {
auto error_detail = std : : to_string ( ( int ) transfer_response - > error ( ) . error_type ) ;
if ( ! transfer_response - > error ( ) . error_message . empty ( ) )
error_detail + = " / " + transfer_response - > error ( ) . error_message ;
return command_result { error : : vs_critical , error_detail } ;
}
case ErrorType : : IO_ERROR :
return command_result { error : : file_io_error , transfer_response - > error ( ) . error_message } ;
2020-01-26 08:21:34 -05:00
2020-05-13 05:32:08 -04:00
case ErrorType : : FILE_IS_NOT_A_FILE :
case ErrorType : : INVALID_FILE_TYPE :
case ErrorType : : FILE_DOES_NOT_EXISTS :
return command_result { error : : file_not_found } ;
2020-01-26 08:21:34 -05:00
2020-05-13 05:32:08 -04:00
case ErrorType : : SERVER_QUOTA_EXCEEDED :
return command_result { error : : file_transfer_server_quota_exceeded } ;
2020-01-26 08:21:34 -05:00
2020-05-13 05:32:08 -04:00
case ErrorType : : CLIENT_QUOTA_EXCEEDED :
return command_result { error : : file_transfer_client_quota_exceeded } ;
2020-01-26 08:21:34 -05:00
2020-05-13 05:32:08 -04:00
case ErrorType : : SERVER_TOO_MANY_TRANSFERS :
return command_result { error : : file_server_transfer_limit_reached } ;
2020-01-26 08:21:34 -05:00
2020-05-13 05:32:08 -04:00
case ErrorType : : CLIENT_TOO_MANY_TRANSFERS :
return command_result { error : : file_client_transfer_limit_reached } ;
2020-01-26 08:21:34 -05:00
}
}
2020-05-13 05:32:08 -04:00
auto transfer = transfer_response - > response ( ) ;
if ( transfer - > server_addresses . empty ( ) ) {
logError ( 0 , " {} Received transfer without any addresses! " , CLIENT_STR_LOG_PREFIX ) ;
return ts : : command_result { error : : vs_critical } ;
}
transfer - > client_transfer_id = cmd [ " clientftfid " ] ;
ts : : command_builder result { this - > notify_response_command ( " notifystartdownload " ) } ;
result . put_unchecked ( 0 , " clientftfid " , cmd [ " clientftfid " ] . string ( ) ) ;
result . put_unchecked ( 0 , " serverftfid " , transfer - > server_transfer_id ) ;
auto used_address = transfer - > server_addresses [ 0 ] ;
result . put_unchecked ( 0 , " ip " , used_address . hostname ) ;
result . put_unchecked ( 0 , " port " , used_address . port ) ;
2021-04-21 07:19:46 -04:00
result . put_unchecked ( 0 , " ftkey " , transfer - > transfer_key ) ;
2020-05-13 05:32:08 -04:00
result . put_unchecked ( 0 , " proto " , " 1 " ) ;
result . put_unchecked ( 0 , " size " , transfer - > expected_file_size ) ;
result . put_unchecked ( 0 , " seekpos " , transfer - > file_offset ) ;
2020-01-26 08:21:34 -05:00
this - > sendCommand ( result ) ;
2020-06-28 08:01:14 -04:00
serverInstance - > action_logger ( ) - > file_logger . log_file_download ( this - > getServerId ( ) , this - > ref ( ) , log_channel_id , info . file_path ) ;
2020-01-26 08:21:34 -05:00
return command_result { error : : ok } ;
}
/*
2020-05-13 05:32:08 -04:00
* Usage : ftrenamefile cid = { channelID } cpw = { channelPassword }
[ tcid = { targetChannelID } ] [ tcpw = { targetChannelPassword } ]
oldname = { oldFilePath } newname = { newFilePath }
2020-01-26 08:21:34 -05:00
2020-05-13 05:32:08 -04:00
i_ft_file_rename_power
i_ft_needed_file_rename_power
*/
command_result ConnectedClient : : handleCommandFTRenameFile ( ts : : Command & cmd ) {
CMD_RESET_IDLE ;
CMD_REQ_SERVER ;
CMD_CHK_AND_INC_FLOOD_POINTS ( 5 ) ;
2020-01-26 08:21:34 -05:00
2020-05-13 05:32:08 -04:00
auto virtual_file_server = file : : server ( ) - > find_virtual_server ( this - > getServerId ( ) ) ;
if ( ! virtual_file_server ) return command_result { error : : file_virtual_server_not_registered } ;
2020-01-26 08:21:34 -05:00
2020-05-13 05:32:08 -04:00
auto channel_id = cmd [ " cid " ] . as < ChannelId > ( ) ;
auto target_channel_id = cmd [ 0 ] . has ( " tcid " ) ? cmd [ " tcid " ] . as < ChannelId > ( ) : channel_id ;
2020-01-26 08:21:34 -05:00
2021-04-14 08:57:04 -04:00
std : : shared_lock channel_tree_lock { this - > server - > channel_tree_mutex } ;
2020-05-13 05:32:08 -04:00
auto channel = this - > server - > channelTree - > findChannel ( channel_id ) ;
if ( ! channel )
return command_result { error : : channel_invalid_id } ;
2020-01-26 08:21:34 -05:00
2021-04-14 08:57:04 -04:00
auto channel_password = cmd [ " cpw " ] . optional_string ( ) ;
if ( ! channel - > verify_password ( channel_password , this - > getType ( ) ! = ClientType : : CLIENT_QUERY ) & & ! permission : : v2 : : permission_granted ( 1 , this - > calculate_permission ( permission : : b_ft_ignore_password , channel - > channelId ( ) ) ) ) {
return channel_password . has_value ( ) ? command_result { error : : channel_invalid_password } : command_result { permission : : b_ft_ignore_password } ;
}
2020-01-26 08:21:34 -05:00
2020-05-13 05:32:08 -04:00
ACTION_REQUIRES_CHANNEL_PERMISSION ( channel , permission : : i_ft_needed_file_rename_power , permission : : i_ft_file_rename_power , true ) ;
2020-01-26 08:21:34 -05:00
2020-05-13 05:32:08 -04:00
if ( target_channel_id ! = channel_id ) {
auto targetChannel = this - > server - > channelTree - > findChannel ( target_channel_id ) ;
if ( ! targetChannel )
return command_result { error : : channel_invalid_id } ;
2020-01-26 08:21:34 -05:00
2021-04-14 08:57:04 -04:00
auto channel_password = cmd [ " cpw " ] . optional_string ( ) ;
if ( ! channel - > verify_password ( channel_password , this - > getType ( ) ! = ClientType : : CLIENT_QUERY ) & & ! permission : : v2 : : permission_granted ( 1 , this - > calculate_permission ( permission : : b_ft_ignore_password , channel - > channelId ( ) ) ) ) {
return channel_password . has_value ( ) ? command_result { error : : channel_invalid_password } : command_result { permission : : b_ft_ignore_password } ;
}
2020-05-13 05:32:08 -04:00
ACTION_REQUIRES_CHANNEL_PERMISSION ( targetChannel , permission : : i_ft_needed_file_rename_power , permission : : i_ft_file_rename_power , true ) ;
}
2020-06-28 08:01:14 -04:00
channel_tree_lock . unlock ( ) ;
2020-05-13 05:32:08 -04:00
auto rename_response = file : : server ( ) - > file_system ( ) . rename_channel_file ( virtual_file_server , channel_id , cmd [ " oldname " ] . string ( ) , target_channel_id , cmd [ " newname " ] . string ( ) ) ;
if ( ! rename_response - > wait_for ( kFileAPITimeout ) )
return command_result { error : : file_api_timeout } ;
if ( ! rename_response - > succeeded ( ) ) {
using ErrorType = file : : filesystem : : FileModifyErrorType ;
debugMessage ( this - > getServerId ( ) , " {} Failed to rename file: {} / {} " , CLIENT_STR_LOG_PREFIX , ( int ) rename_response - > error ( ) . error_type , rename_response - > error ( ) . error_message ) ;
switch ( rename_response - > error ( ) . error_type ) {
case ErrorType : : UNKNOWN :
case ErrorType : : FAILED_TO_RENAME_FILE :
case ErrorType : : FAILED_TO_DELETE_FILES :
case ErrorType : : FAILED_TO_CREATE_DIRECTORIES : {
auto error_detail = std : : to_string ( ( int ) rename_response - > error ( ) . error_type ) ;
if ( ! rename_response - > error ( ) . error_message . empty ( ) )
error_detail + = " / " + rename_response - > error ( ) . error_message ;
return command_result { error : : vs_critical , error_detail } ;
2020-01-26 08:21:34 -05:00
}
2020-05-13 05:32:08 -04:00
case ErrorType : : PATH_EXCEEDS_ROOT_PATH :
case ErrorType : : TARGET_PATH_EXCEEDS_ROOT_PATH :
case ErrorType : : PATH_DOES_NOT_EXISTS :
return command_result { error : : file_invalid_path } ;
2020-01-26 08:21:34 -05:00
2020-05-13 05:32:08 -04:00
case ErrorType : : TARGET_PATH_ALREADY_EXISTS :
return command_result { error : : file_already_exists } ;
case ErrorType : : SOME_FILES_ARE_LOCKED :
return command_result { error : : file_already_in_use , rename_response - > error ( ) . error_message } ;
2020-01-26 08:21:34 -05:00
}
}
2020-06-28 08:01:14 -04:00
serverInstance - > action_logger ( ) - > file_logger . log_file_rename ( this - > getServerId ( ) , this - > ref ( ) , channel_id , cmd [ " oldname " ] . string ( ) , target_channel_id , cmd [ " newname " ] . string ( ) ) ;
2020-01-26 08:21:34 -05:00
return command_result { error : : ok } ;
}
2020-05-13 05:32:08 -04:00
//clid=2 path=files\/virtualserver_1\/channel_5 name=image.iso
// size=673460224 sizedone=450756 clientftfid=2
// serverftfid=6 sender=0 status=1 current_speed=60872.8 average_speed runtime
command_result ConnectedClient : : handleCommandFTList ( ts : : Command & cmd ) {
CMD_RESET_IDLE ;
CMD_REQ_SERVER ;
CMD_CHK_AND_INC_FLOOD_POINTS ( 25 ) ;
ACTION_REQUIRES_PERMISSION ( permission : : b_ft_transfer_list , 1 , 0 ) ;
auto virtual_file_server = file : : server ( ) - > find_virtual_server ( this - > getServerId ( ) ) ;
2021-01-28 14:59:15 -05:00
if ( ! virtual_file_server ) {
return command_result { error : : file_virtual_server_not_registered } ;
}
2020-05-13 05:32:08 -04:00
auto list_response = file : : server ( ) - > file_transfer ( ) . list_transfer ( ) ; //FIXME: Only for the appropriate servers!
2021-01-28 14:59:15 -05:00
if ( ! list_response - > wait_for ( kFileAPITimeout ) ) {
2020-05-13 05:32:08 -04:00
return command_result { error : : file_api_timeout } ;
2021-01-28 14:59:15 -05:00
}
2020-01-26 08:21:34 -05:00
2020-05-13 05:32:08 -04:00
if ( ! list_response - > succeeded ( ) ) {
using ErrorType = file : : transfer : : TransferListError ;
2020-01-26 08:21:34 -05:00
2020-05-13 05:32:08 -04:00
debugMessage ( this - > getServerId ( ) , " {} Failed to list current transfers: {} " , CLIENT_STR_LOG_PREFIX , ( int ) list_response - > error ( ) ) ;
switch ( list_response - > error ( ) ) {
case ErrorType : : UNKNOWN : {
auto error_detail = std : : to_string ( ( int ) list_response - > error ( ) ) ;
return command_result { error : : vs_critical , error_detail } ;
}
}
}
2020-01-26 08:21:34 -05:00
2020-05-13 05:32:08 -04:00
const auto & transfers = list_response - > response ( ) ;
2021-01-28 14:59:15 -05:00
if ( transfers . empty ( ) ) {
2020-05-13 05:32:08 -04:00
return command_result { error : : database_empty_result } ;
2021-01-28 14:59:15 -05:00
}
2020-05-13 05:32:08 -04:00
ts : : command_builder notify { this - > notify_response_command ( " notifyftlist " ) } ;
size_t bulk_index { 0 } ;
for ( const auto & transfer : transfers ) {
auto bulk = notify . bulk ( bulk_index + + ) ;
2020-01-26 08:21:34 -05:00
2020-05-13 05:32:08 -04:00
bulk . put_unchecked ( " clientftfid " , transfer . client_transfer_id ) ;
bulk . put_unchecked ( " serverftfid " , transfer . server_transfer_id ) ;
bulk . put_unchecked ( " sender " , transfer . direction = = file : : transfer : : Transfer : : DIRECTION_DOWNLOAD ) ;
2020-01-26 08:21:34 -05:00
2020-05-13 05:32:08 -04:00
bulk . put_unchecked ( " clid " , transfer . client_id ) ;
bulk . put_unchecked ( " cluid " , transfer . client_unique_id ) ;
bulk . put_unchecked ( " path " , transfer . file_path ) ;
bulk . put_unchecked ( " name " , transfer . file_name ) ;
2020-01-26 08:21:34 -05:00
2020-05-13 05:32:08 -04:00
bulk . put_unchecked ( " size " , transfer . expected_size ) ;
bulk . put_unchecked ( " sizedone " , transfer . size_done ) ;
2020-01-26 08:21:34 -05:00
2020-05-13 05:32:08 -04:00
bulk . put_unchecked ( " status " , ( int ) transfer . status ) ;
2020-01-26 08:21:34 -05:00
2020-05-13 05:32:08 -04:00
bulk . put_unchecked ( " runtime " , transfer . runtime . count ( ) ) ;
bulk . put_unchecked ( " current_speed " , transfer . current_speed ) ;
bulk . put_unchecked ( " average_speed " , transfer . average_speed ) ;
}
this - > sendCommand ( notify ) ;
return command_result { error : : ok } ;
}
//ftstop serverftfid='2' clientftfid='4096' delete='0'
command_result ConnectedClient : : handleCommandFTStop ( ts : : Command & cmd ) {
CMD_RESET_IDLE ;
CMD_REQ_SERVER ;
CMD_CHK_AND_INC_FLOOD_POINTS ( 25 ) ;
auto virtual_file_server = file : : server ( ) - > find_virtual_server ( this - > getServerId ( ) ) ;
2021-01-28 14:59:15 -05:00
if ( ! virtual_file_server ) {
return command_result { error : : file_virtual_server_not_registered } ;
}
2020-05-13 05:32:08 -04:00
auto stop_response = file : : server ( ) - > file_transfer ( ) . stop_transfer ( virtual_file_server , cmd [ " serverftfid " ] , false ) ;
2021-01-28 14:59:15 -05:00
if ( ! stop_response - > wait_for ( kFileAPITimeout ) ) {
2020-05-13 05:32:08 -04:00
return command_result { error : : file_api_timeout } ;
2021-01-28 14:59:15 -05:00
}
2020-05-13 05:32:08 -04:00
if ( ! stop_response - > succeeded ( ) ) {
using ErrorType = file : : transfer : : TransferActionError : : Type ;
switch ( stop_response - > error ( ) . error_type ) {
case ErrorType : : UNKNOWN_TRANSFER :
/* not known, so not stopped but it has the same result as it would have been stopped */
return command_result { error : : ok } ;
case ErrorType : : UNKNOWN : {
auto error_detail = std : : to_string ( ( int ) stop_response - > error ( ) . error_type ) ;
2021-01-28 14:59:15 -05:00
if ( ! stop_response - > error ( ) . error_message . empty ( ) ) {
2020-05-13 05:32:08 -04:00
error_detail + = " / " + stop_response - > error ( ) . error_message ;
2021-01-28 14:59:15 -05:00
}
2020-05-13 05:32:08 -04:00
return command_result { error : : vs_critical , error_detail } ;
}
}
}
return command_result { error : : ok } ;
}
2020-01-26 08:21:34 -05:00