2019-07-17 19:37:18 +02:00
# include <cstring>
# include <protocol/buffers.h>
2021-04-14 16:00:02 +02:00
# include "./PermissionCalculator.h"
2019-07-17 19:37:18 +02:00
# include "client/voice/VoiceClient.h"
# include "client/InternalClient.h"
2020-01-26 18:04:38 +01:00
# include "VirtualServer.h"
2019-07-17 19:37:18 +02:00
# include <misc/timer.h>
# include <log/LogUtils.h>
# include <misc/sassert.h>
2020-06-28 14:01:14 +02:00
# include <src/manager/ActionLogger.h>
2019-07-17 19:37:18 +02:00
# include "InstanceHandler.h"
2021-03-07 19:17:20 +01:00
# include "./groups/GroupManager.h"
2019-07-17 19:37:18 +02:00
using namespace std ;
using namespace ts : : server ;
using namespace ts : : protocol ;
using namespace ts : : buffer ;
using namespace ts : : permission ;
using namespace std : : chrono ;
2020-01-26 18:04:38 +01:00
bool VirtualServer : : registerClient ( shared_ptr < ConnectedClient > client ) {
2020-01-24 02:57:58 +01:00
sassert ( client ) ;
{
2021-04-15 12:54:52 +02:00
std : : lock_guard clients_lock { this - > clients_mutex } ;
2020-01-24 02:57:58 +01:00
if ( client - > getClientId ( ) > 0 ) {
logCritical ( this - > getServerId ( ) , " Client {} ({}|{}) has been already registered! " , client - > getDisplayName ( ) , client - > getClientId ( ) , client - > getUid ( ) ) ;
return false ;
}
2021-04-15 12:54:52 +02:00
ClientId client_id { 0 } ;
while ( this - > clients . count ( client_id ) ) {
2020-01-24 02:57:58 +01:00
client_id + + ;
2021-04-15 12:54:52 +02:00
}
this - > clients . emplace ( client_id , client ) ;
2020-01-24 02:57:58 +01:00
client - > setClientId ( client_id ) ;
}
{
2021-04-15 12:54:52 +02:00
std : : lock_guard lock { this - > client_nickname_lock } ;
2020-01-24 02:57:58 +01:00
auto login_name = client - > getDisplayName ( ) ;
2021-04-15 12:54:52 +02:00
if ( client - > getExternalType ( ) = = ClientType : : CLIENT_TEAMSPEAK ) {
2020-01-24 02:57:58 +01:00
client - > properties ( ) [ property : : CLIENT_LOGIN_NAME ] = login_name ;
2021-04-15 12:54:52 +02:00
}
while ( login_name . length ( ) < 3 ) {
login_name + = " . " ;
}
2020-01-24 02:57:58 +01:00
2021-04-15 12:54:52 +02:00
std : : shared_ptr < ConnectedClient > found_client { nullptr } ;
auto registered_clients = this - > getClients ( ) ;
2020-01-24 02:57:58 +01:00
2021-04-15 12:54:52 +02:00
auto client_name { login_name } ;
size_t counter { 0 } ;
2020-01-24 02:57:58 +01:00
{
while ( true ) {
2021-04-15 12:54:52 +02:00
for ( auto & _client : registered_clients ) {
if ( _client - > getDisplayName ( ) = = client_name & & _client ! = client ) {
2020-01-24 02:57:58 +01:00
goto increase_name ;
2021-04-15 12:54:52 +02:00
}
2020-01-24 02:57:58 +01:00
}
goto nickname_valid ;
increase_name :
2021-04-15 12:54:52 +02:00
client_name = login_name + std : : to_string ( + + counter ) ;
2020-01-24 02:57:58 +01:00
}
}
2021-04-15 12:54:52 +02:00
2020-01-24 02:57:58 +01:00
nickname_valid :
client - > setDisplayName ( client_name ) ;
}
2021-04-15 12:54:52 +02:00
switch ( client - > getType ( ) ) {
case ClientType : : CLIENT_TEAMSPEAK :
case ClientType : : CLIENT_TEASPEAK :
case ClientType : : CLIENT_WEB :
this - > properties ( ) [ property : : VIRTUALSERVER_CLIENT_CONNECTIONS ] . increment_by < uint64_t > ( 1 ) ; //increase manager connections
this - > properties ( ) [ property : : VIRTUALSERVER_LAST_CLIENT_CONNECT ] = duration_cast < seconds > ( system_clock : : now ( ) . time_since_epoch ( ) ) . count ( ) ;
break ;
case ClientType : : CLIENT_QUERY :
this - > properties ( ) [ property : : VIRTUALSERVER_LAST_QUERY_CONNECT ] = duration_cast < seconds > ( system_clock : : now ( ) . time_since_epoch ( ) ) . count ( ) ;
this - > properties ( ) [ property : : VIRTUALSERVER_QUERY_CLIENT_CONNECTIONS ] . increment_by < uint64_t > ( 1 ) ; //increase manager connections
break ;
case ClientType : : CLIENT_MUSIC :
case ClientType : : CLIENT_INTERNAL :
break ;
2020-01-24 02:57:58 +01:00
2021-04-15 12:54:52 +02:00
case ClientType : : MAX :
default :
assert ( false ) ;
break ;
2020-01-24 02:57:58 +01:00
}
2019-07-17 19:37:18 +02:00
return true ;
}
2021-04-15 12:54:52 +02:00
bool VirtualServer : : unregisterClient ( shared_ptr < ConnectedClient > client , std : : string reason , std : : unique_lock < std : : shared_mutex > & chan_tree_lock ) {
if ( client - > getType ( ) = = ClientType : : CLIENT_TEAMSPEAK | | client - > getType ( ) = = ClientType : : CLIENT_TEASPEAK | | client - > getType ( ) = = ClientType : : CLIENT_WEB ) {
sassert ( client - > state = = ConnectionState : : DISCONNECTED ) ;
2020-01-24 02:57:58 +01:00
}
2021-04-15 12:54:52 +02:00
{
if ( ! chan_tree_lock . owns_lock ( ) ) {
chan_tree_lock . lock ( ) ;
}
if ( client - > currentChannel ) {
//We dont have to make him invisible if he hasnt even a channel
this - > client_move ( client , nullptr , nullptr , reason , ViewReasonId : : VREASON_SERVER_LEFT , false , chan_tree_lock ) ;
}
chan_tree_lock . unlock ( ) ;
2021-04-15 03:28:08 +02:00
}
2021-04-15 12:54:52 +02:00
2020-01-24 02:57:58 +01:00
{
2021-04-15 12:54:52 +02:00
std : : lock_guard clients_lock { this - > clients_mutex } ;
auto client_id = client - > getClientId ( ) ;
if ( client_id = = 0 ) {
return false ; /* not registered */
}
2020-01-24 02:57:58 +01:00
2021-04-15 12:54:52 +02:00
if ( ! this - > clients . erase ( client_id ) ) {
client - > setClientId ( 0 ) ;
logError ( this - > getServerId ( ) , " Tried to unregister a not registered client {}/{} ({}) " , client - > getDisplayName ( ) , client - > getUid ( ) , client_id ) ;
return false ;
2020-01-24 02:57:58 +01:00
}
2021-04-15 12:54:52 +02:00
client - > setClientId ( 0 ) ;
2020-01-24 02:57:58 +01:00
}
2021-04-14 16:00:02 +02:00
auto current_time_seconds = std : : chrono : : duration_cast < seconds > ( std : : chrono : : system_clock : : now ( ) . time_since_epoch ( ) ) . count ( ) ;
2021-04-15 12:54:52 +02:00
switch ( client - > getType ( ) ) {
2021-04-14 16:00:02 +02:00
case ClientType : : CLIENT_TEAMSPEAK :
case ClientType : : CLIENT_TEASPEAK :
case ClientType : : CLIENT_WEB :
this - > properties ( ) [ property : : VIRTUALSERVER_LAST_CLIENT_DISCONNECT ] = current_time_seconds ;
break ;
case ClientType : : CLIENT_QUERY :
this - > properties ( ) [ property : : VIRTUALSERVER_LAST_QUERY_DISCONNECT ] = current_time_seconds ;
break ;
case ClientType : : CLIENT_MUSIC :
case ClientType : : CLIENT_INTERNAL :
case ClientType : : MAX :
default :
break ;
}
2020-01-24 02:57:58 +01:00
2021-04-15 12:54:52 +02:00
serverInstance - > databaseHelper ( ) - > saveClientPermissions ( this - > ref ( ) , client - > getClientDatabaseId ( ) , client - > clientPermissions ) ;
2020-01-24 02:57:58 +01:00
return true ;
2019-07-17 19:37:18 +02:00
}
2020-01-26 18:04:38 +01:00
void VirtualServer : : registerInternalClient ( std : : shared_ptr < ConnectedClient > client ) {
2019-07-17 19:37:18 +02:00
client - > state = ConnectionState : : CONNECTED ;
2021-04-15 12:54:52 +02:00
this - > registerClient ( client ) ;
2019-07-17 19:37:18 +02:00
}
2020-01-26 18:04:38 +01:00
void VirtualServer : : unregisterInternalClient ( std : : shared_ptr < ConnectedClient > client ) {
2019-07-17 19:37:18 +02:00
client - > state = ConnectionState : : DISCONNECTED ;
2021-04-15 12:54:52 +02:00
std : : unique_lock tree_lock { this - > channel_tree_mutex } ;
this - > unregisterClient ( client , " internal disconnect " , tree_lock ) ;
2019-07-17 19:37:18 +02:00
}
2020-01-26 18:04:38 +01:00
bool VirtualServer : : assignDefaultChannel ( const shared_ptr < ConnectedClient > & client , bool join ) {
2021-02-25 11:13:30 +01:00
std : : shared_ptr < BasicChannel > channel { } ;
2021-04-14 16:00:02 +02:00
std : : unique_lock server_channel_lock { this - > channel_tree_mutex } ;
2021-02-25 11:13:30 +01:00
auto requested_channel_path = client - > properties ( ) [ property : : CLIENT_DEFAULT_CHANNEL ] . value ( ) ;
if ( ! requested_channel_path . empty ( ) ) {
if ( requested_channel_path [ 0 ] = = ' / ' & & requested_channel_path . find_first_not_of ( " 0123456789 " , 1 ) = = std : : string : : npos ) {
ChannelId channel_id { 0 } ;
try {
channel_id = std : : stoull ( requested_channel_path . substr ( 1 ) ) ;
} catch ( std : : exception & ) {
logTrace ( this - > getServerId ( ) , " {} Failed to parse provided channel path as channel id. " ) ;
}
if ( channel_id > 0 ) {
channel = this - > channelTree - > findChannel ( channel_id ) ;
}
} else {
channel = this - > channelTree - > findChannelByPath ( requested_channel_path ) ;
}
}
if ( channel ) {
/* Client proposes a target channel */
auto & channel_whitelist = client - > join_whitelisted_channel ;
auto whitelist_entry = std : : find_if ( channel_whitelist . begin ( ) , channel_whitelist . end ( ) , [ & ] ( const auto & entry ) { return entry . first = = channel - > channelId ( ) ; } ) ;
auto client_channel_password = client - > properties ( ) [ property : : CLIENT_DEFAULT_CHANNEL_PASSWORD ] . value ( ) ;
if ( whitelist_entry ! = channel_whitelist . end ( ) ) {
debugMessage ( this - > getServerId ( ) , " {} Allowing client to join channel {} because the token he used explicitly allowed it. " , client - > getLoggingPrefix ( ) , channel - > channelId ( ) ) ;
if ( whitelist_entry - > second ! = " ignore " ) {
2021-04-14 14:57:04 +02:00
if ( ! channel - > verify_password ( std : : make_optional ( client_channel_password ) , true ) ) {
2021-02-25 11:13:30 +01:00
if ( ! permission : : v2 : : permission_granted ( 1 , client - > calculate_permission ( permission : : b_channel_join_ignore_password , channel - > channelId ( ) ) ) ) {
channel = nullptr ;
goto skip_permissions ;
}
}
}
goto skip_permissions ;
}
if ( ! channel - > permission_granted ( permission : : i_channel_needed_join_power , client - > calculate_permission ( permission : : i_channel_join_power , channel - > channelId ( ) ) , false ) ) {
debugMessage ( this - > getServerId ( ) , " {} Tried to join channel {} but hasn't enough join power. " , client - > getLoggingPrefix ( ) , channel - > channelId ( ) ) ;
channel = nullptr ;
goto skip_permissions ;
}
2021-04-14 14:57:04 +02:00
if ( ! channel - > verify_password ( std : : make_optional ( client - > properties ( ) [ property : : CLIENT_DEFAULT_CHANNEL_PASSWORD ] . value ( ) ) , true ) ) {
2021-02-25 11:13:30 +01:00
if ( ! permission : : v2 : : permission_granted ( 1 , client - > calculate_permission ( permission : : b_channel_join_ignore_password , channel - > channelId ( ) ) ) ) {
debugMessage ( this - > getServerId ( ) , " {} Tried to join channel {} but hasn't given the right channel password. " , client - > getLoggingPrefix ( ) , channel - > channelId ( ) ) ;
2020-01-24 02:57:58 +01:00
channel = nullptr ;
2021-02-25 11:13:30 +01:00
goto skip_permissions ;
2019-07-17 19:37:18 +02:00
}
2021-02-25 11:13:30 +01:00
}
skip_permissions : ;
2019-07-17 19:37:18 +02:00
}
2020-01-24 02:57:58 +01:00
2021-04-15 03:28:08 +02:00
/* Clear these parameters. We don't need them any more after we initially payed attention. */
client - > properties ( ) [ property : : CLIENT_DEFAULT_CHANNEL ] = " " ;
client - > properties ( ) [ property : : CLIENT_DEFAULT_CHANNEL_PASSWORD ] = " " ;
2021-02-25 11:13:30 +01:00
if ( ! channel ) {
/* Client did not propose a channel or the proposed channel got rejected */
channel = this - > channelTree - > getDefaultChannel ( ) ;
if ( ! channel ) {
logCritical ( this - > getServerId ( ) , " Channel tree is missing the default channel. " ) ;
return false ;
}
}
debugMessage ( this - > getServerId ( ) , " {} Using channel {} as default client channel. " , client - > getLoggingPrefix ( ) , channel - > channelId ( ) ) ;
2020-01-24 02:57:58 +01:00
if ( join ) {
2021-04-14 16:00:02 +02:00
this - > client_move ( client , channel , nullptr , " " , ViewReasonId : : VREASON_USER_ACTION , false , server_channel_lock ) ;
2020-01-24 02:57:58 +01:00
} else {
client - > currentChannel = channel ;
}
2021-04-14 16:00:02 +02:00
2019-07-17 19:37:18 +02:00
return true ;
}
2020-01-26 18:04:38 +01:00
void VirtualServer : : testBanStateChange ( const std : : shared_ptr < ConnectedClient > & invoker ) {
2020-01-24 02:57:58 +01:00
this - > forEachClient ( [ & ] ( shared_ptr < ConnectedClient > client ) {
auto ban = client - > resolveActiveBan ( client - > getPeerIp ( ) ) ;
if ( ban ) {
logMessage ( this - > getServerId ( ) , " Client {} was online, but had an ban whcih effect him has been registered. Disconnecting client. " , CLIENT_STR_LOG_PREFIX_ ( client ) ) ;
auto entryTime = ban - > until . time_since_epoch ( ) . count ( ) > 0 ? ( uint64_t ) chrono : : ceil < seconds > ( ban - > until - system_clock : : now ( ) ) . count ( ) : 0UL ;
this - > notify_client_ban ( client , invoker , ban - > reason , entryTime ) ;
2020-02-01 14:32:16 +01:00
client - > close_connection ( system_clock : : now ( ) + seconds ( 1 ) ) ;
2020-01-24 02:57:58 +01:00
}
} ) ;
2019-07-17 19:37:18 +02:00
}
2020-01-26 18:04:38 +01:00
void VirtualServer : : notify_client_ban ( const shared_ptr < ConnectedClient > & target , const std : : shared_ptr < ts : : server : : ConnectedClient > & invoker , const std : : string & reason , size_t time ) {
2020-01-24 02:57:58 +01:00
/* the target is not allowed to execute anything; Must before channel tree lock because the target may waits for us to finish the channel stuff */
lock_guard command_lock ( target - > command_lock ) ;
2021-04-14 14:57:04 +02:00
unique_lock server_channel_lock ( this - > channel_tree_mutex ) ; /* we're "moving" a client! */
2020-01-24 02:57:58 +01:00
if ( target - > currentChannel ) {
for ( const auto & client : this - > getClients ( ) ) {
if ( ! client | | client = = target )
continue ;
2021-04-14 14:57:04 +02:00
unique_lock client_channel_lock ( client - > channel_tree_mutex ) ;
2020-01-24 02:57:58 +01:00
if ( client - > isClientVisible ( target , false ) )
client - > notifyClientLeftViewBanned ( target , reason , invoker , time , false ) ;
}
auto s_channel = dynamic_pointer_cast < ServerChannel > ( target - > currentChannel ) ;
s_channel - > unregister_client ( target ) ;
}
/* now disconnect the target itself */
2021-04-14 14:57:04 +02:00
unique_lock client_channel_lock ( target - > channel_tree_mutex ) ;
2020-01-24 02:57:58 +01:00
target - > notifyClientLeftViewBanned ( target , reason , invoker , time , false ) ;
target - > currentChannel = nullptr ;
2019-07-17 19:37:18 +02:00
}
2020-01-26 18:04:38 +01:00
void VirtualServer : : notify_client_kick (
2020-01-24 02:57:58 +01:00
const std : : shared_ptr < ts : : server : : ConnectedClient > & target ,
2019-07-17 19:37:18 +02:00
const std : : shared_ptr < ts : : server : : ConnectedClient > & invoker ,
const std : : string & reason ,
const std : : shared_ptr < ts : : BasicChannel > & target_channel ) {
2020-01-24 02:57:58 +01:00
if ( target_channel ) {
/* use the move! */
2021-04-14 14:57:04 +02:00
unique_lock server_channel_lock ( this - > channel_tree_mutex , defer_lock ) ;
2020-01-24 02:57:58 +01:00
this - > client_move ( target , target_channel , invoker , reason , ViewReasonId : : VREASON_CHANNEL_KICK , true , server_channel_lock ) ;
} else {
/* the target is not allowed to execute anything; Must before channel tree lock because the target may waits for us to finish the channel stuff */
lock_guard command_lock ( target - > command_lock ) ;
2021-04-14 14:57:04 +02:00
unique_lock server_channel_lock ( this - > channel_tree_mutex ) ; /* we're "moving" a client! */
2020-01-24 02:57:58 +01:00
if ( target - > currentChannel ) {
for ( const auto & client : this - > getClients ( ) ) {
if ( ! client | | client = = target )
continue ;
2021-04-14 14:57:04 +02:00
unique_lock client_channel_lock ( client - > channel_tree_mutex ) ;
2020-01-24 02:57:58 +01:00
if ( client - > isClientVisible ( target , false ) )
client - > notifyClientLeftViewKicked ( target , nullptr , reason , invoker , false ) ;
}
auto s_channel = dynamic_pointer_cast < ServerChannel > ( target - > currentChannel ) ;
s_channel - > unregister_client ( target ) ;
2020-11-07 13:17:51 +01:00
if ( auto client { dynamic_pointer_cast < SpeakingClient > ( target ) } ; client ) {
this - > rtc_server ( ) . assign_channel ( client - > rtc_client_id , 0 ) ;
}
2020-01-24 02:57:58 +01:00
}
/* now disconnect the target itself */
2021-04-14 14:57:04 +02:00
unique_lock client_channel_lock ( target - > channel_tree_mutex ) ;
2020-01-24 02:57:58 +01:00
target - > notifyClientLeftViewKicked ( target , nullptr , reason , invoker , false ) ;
target - > currentChannel = nullptr ;
}
2019-07-17 19:37:18 +02:00
}
/*
* 1. flag channel as deleted ( lock channel tree so no moves )
* 2. Gather all clients within the channel ( lock their execute lock )
* 3. Unlock channel tree and lock client locks
* 4. lock channel tree again and move the clients ( No new clients should be joined because channel is flagged as deleted ! )
*
* Note : channel cant be a ref because the channel itself gets deleted !
*/
2020-06-28 14:01:14 +02:00
void VirtualServer : : delete_channel ( shared_ptr < ts : : ServerChannel > channel , const shared_ptr < ConnectedClient > & invoker , const std : : string & kick_message , unique_lock < std : : shared_mutex > & tree_lock , bool temp_delete ) {
2021-02-25 11:13:30 +01:00
if ( ! tree_lock . owns_lock ( ) ) {
2020-01-24 02:57:58 +01:00
tree_lock . lock ( ) ;
2021-02-25 11:13:30 +01:00
}
if ( channel - > deleted ) {
2020-01-24 02:57:58 +01:00
return ;
2021-02-25 11:13:30 +01:00
}
2020-01-24 02:57:58 +01:00
deque < std : : shared_ptr < ConnectedClient > > clients ;
{
for ( const auto & sub_channel : this - > channelTree - > channels ( channel ) ) {
auto s_channel = dynamic_pointer_cast < ServerChannel > ( sub_channel ) ;
assert ( s_channel ) ;
auto chan_clients = this - > getClientsByChannel ( sub_channel ) ;
clients . insert ( clients . end ( ) , chan_clients . begin ( ) , chan_clients . end ( ) ) ;
s_channel - > deleted = true ;
}
auto chan_clients = this - > getClientsByChannel ( channel ) ;
clients . insert ( clients . end ( ) , chan_clients . begin ( ) , chan_clients . end ( ) ) ;
channel - > deleted = true ;
}
auto default_channel = this - > channelTree - > getDefaultChannel ( ) ;
deque < unique_lock < threads : : Mutex > > command_locks ;
for ( const auto & client : clients ) {
command_locks . push_back ( move ( unique_lock ( client - > command_lock ) ) ) ;
}
2021-03-07 19:17:20 +01:00
for ( const auto & client : clients ) {
2020-01-24 02:57:58 +01:00
this - > client_move ( client , default_channel , invoker , kick_message , ViewReasonId : : VREASON_CHANNEL_KICK , true , tree_lock ) ;
2021-03-07 19:17:20 +01:00
}
2020-01-24 02:57:58 +01:00
2021-03-07 19:17:20 +01:00
if ( ! tree_lock . owns_lock ( ) ) {
2021-04-14 14:57:04 +02:00
/* This case should never happen. client_move should never unlock the tree lock! */
tree_lock . lock ( ) ;
2021-03-07 19:17:20 +01:00
}
2020-01-24 02:57:58 +01:00
command_locks . clear ( ) ;
2020-06-28 14:01:14 +02:00
auto deleted_channels = this - > channelTree - > delete_channel_root ( channel ) ;
log : : ChannelDeleteReason delete_reason { temp_delete ? log : : ChannelDeleteReason : : EMPTY : log : : ChannelDeleteReason : : USER_ACTION } ;
for ( const auto & deleted_channel : deleted_channels ) {
serverInstance - > action_logger ( ) - > channel_logger . log_channel_delete ( this - > serverId , invoker , deleted_channel - > channelId ( ) , channel = = deleted_channel ? delete_reason : log : : ChannelDeleteReason : : PARENT_DELETED ) ;
}
2020-01-24 02:57:58 +01:00
this - > forEachClient ( [ & ] ( const shared_ptr < ConnectedClient > & client ) {
2021-04-14 14:57:04 +02:00
unique_lock client_channel_lock ( client - > channel_tree_mutex ) ;
client - > notifyChannelDeleted ( client - > channel_tree - > delete_channel_root ( channel ) , invoker ) ;
2020-01-24 02:57:58 +01:00
} ) ;
2021-02-25 11:13:30 +01:00
2021-03-07 19:17:20 +01:00
{
std : : vector < ChannelId > deleted_channel_ids { } ;
deleted_channel_ids . reserve ( deleted_channels . size ( ) ) ;
for ( const auto & deleted_channel : deleted_channels ) {
deleted_channel_ids . push_back ( deleted_channel - > channelId ( ) ) ;
}
auto ref_self = this - > ref ( ) ;
task_id task_id { } ;
serverInstance - > general_task_executor ( ) - > schedule ( task_id , " database cleanup after channel delete " , [ ref_self , deleted_channel_ids ] {
for ( const auto & deleted_channel_id : deleted_channel_ids ) {
ref_self - > tokenManager - > handle_channel_deleted ( deleted_channel_id ) ;
}
for ( const auto & deleted_channel_id : deleted_channel_ids ) {
ref_self - > group_manager ( ) - > assignments ( ) . handle_channel_deleted ( deleted_channel_id ) ;
}
} ) ;
}
2019-07-17 19:37:18 +02:00
}
2021-04-12 19:12:02 +02:00
/*
* This method had previously owned the clients command lock but that ' s not really needed .
* Everything which is related to the server channel tree or the client channel tree should be locked with
2021-04-14 16:00:02 +02:00
* the appropriate mutexes .
2021-04-12 19:12:02 +02:00
*/
2020-01-26 18:04:38 +01:00
void VirtualServer : : client_move (
2021-04-14 16:00:02 +02:00
const shared_ptr < ts : : server : : ConnectedClient > & target_client ,
2020-01-24 02:57:58 +01:00
shared_ptr < ts : : BasicChannel > target_channel ,
const std : : shared_ptr < ts : : server : : ConnectedClient > & invoker ,
const std : : string & reason_message ,
ts : : ViewReasonId reason_id ,
2019-07-17 19:37:18 +02:00
bool notify_client ,
std : : unique_lock < std : : shared_mutex > & server_channel_write_lock ) {
2020-01-24 02:57:58 +01:00
TIMING_START ( timings ) ;
2021-04-12 19:12:02 +02:00
if ( ! server_channel_write_lock . owns_lock ( ) ) {
2021-04-18 21:36:35 +02:00
server_channel_write_lock . lock ( ) ;
2021-01-28 20:59:15 +01:00
}
2021-04-14 16:00:02 +02:00
2020-01-24 02:57:58 +01:00
TIMING_STEP ( timings , " chan tree l " ) ;
2021-04-14 16:00:02 +02:00
if ( target_client - > currentChannel = = target_channel ) {
2020-01-24 02:57:58 +01:00
return ;
2021-01-28 20:59:15 +01:00
}
2020-01-24 02:57:58 +01:00
2021-04-12 19:12:02 +02:00
/* first step: verify thew source and target channel */
2020-01-24 02:57:58 +01:00
auto s_target_channel = dynamic_pointer_cast < ServerChannel > ( target_channel ) ;
2021-04-14 16:00:02 +02:00
auto s_source_channel = dynamic_pointer_cast < ServerChannel > ( target_client - > currentChannel ) ;
assert ( ! target_client - > currentChannel | | s_source_channel ! = nullptr ) ;
2020-01-24 02:57:58 +01:00
2021-04-12 19:12:02 +02:00
std : : deque < property : : ClientProperties > updated_client_properties { } ;
2020-01-24 02:57:58 +01:00
if ( target_channel ) {
assert ( s_target_channel ) ;
if ( s_target_channel - > deleted ) {
2021-04-12 19:12:02 +02:00
return ;
2020-01-24 02:57:58 +01:00
}
}
2021-04-12 19:12:02 +02:00
2020-01-24 02:57:58 +01:00
auto l_target_channel = s_target_channel ? this - > channelTree - > findLinkedChannel ( s_target_channel - > channelId ( ) ) : nullptr ;
auto l_source_channel = s_source_channel ? this - > channelTree - > findLinkedChannel ( s_source_channel - > channelId ( ) ) : nullptr ;
TIMING_STEP ( timings , " channel res " ) ;
2021-04-14 16:00:02 +02:00
/* second step: show the target channel to the client if its not shown and let him subscribe to the channel */
2020-01-24 02:57:58 +01:00
if ( target_channel & & notify_client ) {
2021-04-14 16:00:02 +02:00
std : : unique_lock client_channel_lock { target_client - > channel_tree_mutex } ;
2020-01-24 02:57:58 +01:00
2021-04-14 16:00:02 +02:00
bool success { false } ;
2020-01-24 02:57:58 +01:00
/* TODO: Use a bunk here and not a notify for every single */
2021-04-14 16:00:02 +02:00
for ( const auto & channel : target_client - > channel_tree - > show_channel ( l_target_channel , success ) ) {
target_client - > notifyChannelShow ( channel - > channel ( ) , channel - > previous_channel ) ;
}
2020-01-24 02:57:58 +01:00
sassert ( success ) ;
2021-04-14 16:00:02 +02:00
if ( ! success ) {
2020-01-24 02:57:58 +01:00
return ;
2021-04-14 16:00:02 +02:00
}
2020-01-24 02:57:58 +01:00
2021-04-14 16:00:02 +02:00
target_client - > subscribeChannel ( { target_channel } , false , true ) ;
2020-01-24 02:57:58 +01:00
}
TIMING_STEP ( timings , " target show " ) ;
2021-04-14 16:00:02 +02:00
if ( s_source_channel ) {
s_source_channel - > unregister_client ( target_client ) ;
}
2020-01-24 02:57:58 +01:00
if ( target_channel ) {
2021-04-14 16:00:02 +02:00
ClientPermissionCalculator target_client_permissions { & * target_client , target_channel } ;
auto needed_view_power = target_client_permissions . calculate_permission ( permission : : i_client_needed_serverquery_view_power ) ;
2020-01-24 02:57:58 +01:00
2021-04-14 16:00:02 +02:00
/* ct_... is for client channel tree */
this - > forEachClient ( [ & ] ( const std : : shared_ptr < ConnectedClient > & client ) {
if ( ! notify_client & & client = = target_client ) {
return ;
}
bool move_target_client_visible { true } ;
if ( target_client - > getType ( ) = = ClientType : : CLIENT_QUERY ) {
auto query_view_power = client - > calculate_permission ( permission : : i_client_serverquery_view_power , target_channel - > channelId ( ) ) ;
move_target_client_visible = permission : : v2 : : permission_granted ( needed_view_power , query_view_power ) ;
}
std : : unique_lock client_channel_lock { client - > channel_tree_mutex } ;
auto ct_target_channel = move_target_client_visible ? client - > channel_tree - > find_channel ( target_channel ) : nullptr ;
if ( ct_target_channel ) {
auto ct_source_channel = client - > channel_tree - > find_channel ( s_source_channel ) ;
if ( ct_source_channel ) {
/* Source and target channel are visible for the client. Just a "normal" move. */
if ( ct_target_channel - > subscribed | | client = = target_client ) {
if ( client = = target_client | | client - > isClientVisible ( target_client , false ) ) {
client - > notifyClientMoved ( target_client , s_target_channel , reason_id , reason_message , invoker , false ) ;
2020-01-24 02:57:58 +01:00
} else {
2021-04-14 16:00:02 +02:00
client - > notifyClientEnterView ( target_client , invoker , reason_message , s_target_channel , reason_id , s_source_channel , false ) ;
2020-01-24 02:57:58 +01:00
}
2021-04-14 16:00:02 +02:00
} else if ( client - > isClientVisible ( target_client , false ) ) {
/* Client has been moved into an unsubscribed channel */
client - > notifyClientLeftView ( target_client , s_target_channel , reason_id , reason_message . empty ( ) ? string ( " view left " ) : reason_message , invoker , false ) ;
2020-01-24 02:57:58 +01:00
}
2021-04-14 16:00:02 +02:00
} else if ( ct_target_channel - > subscribed ) {
/* Target client entered the view from an invisible channel */
client - > notifyClientEnterView ( target_client , invoker , reason_message , s_target_channel , ViewReasonId : : VREASON_USER_ACTION , nullptr , false ) ;
2020-01-24 02:57:58 +01:00
}
} else {
2021-04-14 16:00:02 +02:00
if ( client - > isClientVisible ( target_client , false ) ) {
/* Client has been moved out of view into an invisible channel */
if ( reason_id = = ViewReasonId : : VREASON_USER_ACTION ) {
client - > notifyClientLeftView ( target_client , nullptr , ViewReasonId : : VREASON_SERVER_LEFT , reason_message . empty ( ) ? " joined a hidden channel " : reason_message , invoker , false ) ;
} else {
client - > notifyClientLeftView ( target_client , nullptr , ViewReasonId : : VREASON_SERVER_LEFT , reason_message . empty ( ) ? " moved to a hidden channel " : reason_message , invoker , false ) ;
}
2020-01-24 02:57:58 +01:00
}
}
} ) ;
2021-04-14 16:00:02 +02:00
s_target_channel - > register_client ( target_client ) ;
if ( auto client { dynamic_pointer_cast < SpeakingClient > ( target_client ) } ; client ) {
2020-11-07 13:17:51 +01:00
this - > rtc_server ( ) . assign_channel ( client - > rtc_client_id , s_target_channel - > rtc_channel_id ) ;
}
2021-04-14 16:00:02 +02:00
if ( auto client { dynamic_pointer_cast < VoiceClient > ( target_client ) } ; client ) {
2020-11-07 13:17:51 +01:00
/* Start normal broadcasting, what the client expects */
2021-01-04 20:32:29 +01:00
this - > rtc_server ( ) . start_broadcast_audio ( client - > rtc_client_id , 1 ) ;
2020-11-07 13:17:51 +01:00
}
2020-01-24 02:57:58 +01:00
} else {
/* client left the server */
2021-04-14 16:00:02 +02:00
if ( target_client - > currentChannel ) {
2020-01-24 02:57:58 +01:00
for ( const auto & client : this - > getClients ( ) ) {
2021-04-14 16:00:02 +02:00
if ( ! client | | client = = target_client )
2020-01-24 02:57:58 +01:00
continue ;
2021-04-14 14:57:04 +02:00
unique_lock client_channel_lock ( client - > channel_tree_mutex ) ;
2021-04-14 16:00:02 +02:00
if ( client - > isClientVisible ( target_client , false ) ) {
client - > notifyClientLeftView ( target_client , nullptr , reason_id , reason_message , invoker , false ) ;
}
2020-01-24 02:57:58 +01:00
}
2021-04-14 16:00:02 +02:00
if ( auto client { dynamic_pointer_cast < SpeakingClient > ( target_client ) } ; client ) {
2020-11-07 13:17:51 +01:00
this - > rtc_server ( ) . assign_channel ( client - > rtc_client_id , 0 ) ;
}
2020-01-24 02:57:58 +01:00
}
}
TIMING_STEP ( timings , " notify view " ) ;
2021-04-14 16:00:02 +02:00
target_client - > currentChannel = target_channel ;
2020-01-24 02:57:58 +01:00
/* third step: update stuff for the client (remember: the client cant execute anything at the moment!) */
2021-04-14 16:00:02 +02:00
unique_lock client_channel_lock { target_client - > channel_tree_mutex } ;
2020-01-24 02:57:58 +01:00
TIMING_STEP ( timings , " lock own tr " ) ;
if ( s_source_channel ) {
2021-04-14 16:00:02 +02:00
s_source_channel - > properties ( ) [ property : : CHANNEL_LAST_LEFT ] = std : : chrono : : duration_cast < chrono : : milliseconds > ( std : : chrono : : system_clock : : now ( ) . time_since_epoch ( ) ) . count ( ) ;
this - > group_manager ( ) - > assignments ( ) . cleanup_temporary_channel_assignment ( target_client - > getClientDatabaseId ( ) , s_source_channel - > channelId ( ) ) ;
if ( target_client - > properties ( ) [ property : : CLIENT_IS_TALKER ] . update_value ( " 0 " ) ) {
2021-04-12 19:12:02 +02:00
updated_client_properties . push_back ( property : : CLIENT_IS_TALKER ) ;
2021-04-14 16:00:02 +02:00
}
if ( target_client - > properties ( ) [ property : : CLIENT_TALK_REQUEST ] . update_value ( " 0 " ) ) {
2021-04-12 19:12:02 +02:00
updated_client_properties . push_back ( property : : CLIENT_TALK_REQUEST ) ;
2021-04-14 16:00:02 +02:00
}
if ( target_client - > properties ( ) [ property : : CLIENT_TALK_REQUEST_MSG ] . update_value ( " " ) ) {
2021-04-12 19:12:02 +02:00
updated_client_properties . push_back ( property : : CLIENT_TALK_REQUEST_MSG ) ;
2020-01-24 02:57:58 +01:00
}
TIMING_STEP ( timings , " src chan up " ) ;
}
if ( s_target_channel ) {
2021-04-14 16:00:02 +02:00
target_client - > task_update_needed_permissions . enqueue ( ) ;
target_client - > task_update_displayed_groups . enqueue ( ) ;
2020-01-24 02:57:58 +01:00
TIMING_STEP ( timings , " perm gr upd " ) ;
if ( s_source_channel ) {
deque < ChannelId > deleted ;
2021-04-14 16:00:02 +02:00
for ( const auto & channel : target_client - > channel_tree - > test_channel ( l_source_channel , l_target_channel ) ) {
2020-01-24 02:57:58 +01:00
deleted . push_back ( channel - > channelId ( ) ) ;
2021-03-07 19:17:20 +01:00
}
2020-01-24 02:57:58 +01:00
2021-03-07 19:17:20 +01:00
if ( ! deleted . empty ( ) ) {
2021-04-14 16:00:02 +02:00
target_client - > notifyChannelHide ( deleted , false ) ;
2021-03-07 19:17:20 +01:00
}
2020-01-24 02:57:58 +01:00
auto i_source_channel = s_source_channel - > channelId ( ) ;
if ( std : : find ( deleted . begin ( ) , deleted . end ( ) , i_source_channel ) = = deleted . end ( ) ) {
2021-04-14 16:00:02 +02:00
auto source_channel_sub_power = target_client - > calculate_permission ( permission : : i_channel_subscribe_power , i_source_channel ) ;
2020-01-24 02:57:58 +01:00
if ( ! s_source_channel - > permission_granted ( permission : : i_channel_needed_subscribe_power , source_channel_sub_power , false ) ) {
2021-04-14 16:00:02 +02:00
auto source_channel_sub_power_ignore = target_client - > calculate_permission ( permission : : b_channel_ignore_subscribe_power , i_source_channel ) ;
2021-04-14 14:57:04 +02:00
if ( ! permission : : v2 : : permission_granted ( 1 , source_channel_sub_power_ignore ) ) {
2020-01-24 02:57:58 +01:00
logTrace ( this - > serverId , " Force unsubscribing of client {} for channel {}/{}. (Channel switch and no permissions) " ,
2021-04-14 16:00:02 +02:00
CLIENT_STR_LOG_PREFIX_ ( target_client ) , s_source_channel - > name ( ) ,
2020-01-24 02:57:58 +01:00
i_source_channel
) ;
2021-04-14 16:00:02 +02:00
target_client - > unsubscribeChannel ( { s_source_channel } , false ) ; //Unsubscribe last channel (hasn't permissions)
2020-01-24 02:57:58 +01:00
}
}
}
TIMING_STEP ( timings , " src hide ts " ) ;
}
}
client_channel_lock . unlock ( ) ;
2021-04-14 16:00:02 +02:00
2020-01-24 02:57:58 +01:00
/* both methods lock if they require stuff */
2021-04-14 16:00:02 +02:00
this - > notifyClientPropertyUpdates ( target_client , updated_client_properties , s_source_channel ? true : false ) ;
2020-01-24 02:57:58 +01:00
TIMING_STEP ( timings , " notify cpro " ) ;
if ( s_target_channel ) {
2021-04-14 16:00:02 +02:00
target_client - > updateChannelClientProperties ( false , s_source_channel ? true : false ) ;
2020-01-24 02:57:58 +01:00
TIMING_STEP ( timings , " notify_t_pr " ) ;
}
2021-04-14 16:00:02 +02:00
debugMessage ( this - > getServerId ( ) , " {} Client move timings: {} " , CLIENT_STR_LOG_PREFIX_ ( target_client ) , TIMING_FINISH ( timings ) ) ;
2019-07-17 19:37:18 +02:00
}