2019-07-17 13:37:18 -04:00
# include <cstring>
# include <protocol/buffers.h>
# include "client/voice/VoiceClient.h"
# include "client/InternalClient.h"
2020-01-26 12:04:38 -05:00
# include "VirtualServer.h"
2019-07-17 13:37:18 -04:00
# include <misc/timer.h>
# include <log/LogUtils.h>
# include <misc/sassert.h>
2020-06-28 08:01:14 -04:00
# include <src/manager/ActionLogger.h>
2019-07-17 13:37:18 -04:00
# include "InstanceHandler.h"
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 12:04:38 -05:00
bool VirtualServer : : registerClient ( shared_ptr < ConnectedClient > client ) {
2020-01-23 20:57:58 -05:00
sassert ( client ) ;
{
lock_guard lock ( this - > clients . lock ) ;
if ( client - > getClientId ( ) > 0 ) {
logCritical ( this - > getServerId ( ) , " Client {} ({}|{}) has been already registered! " , client - > getDisplayName ( ) , client - > getClientId ( ) , client - > getUid ( ) ) ;
return false ;
}
ClientId client_id = 0 ;
ClientId max_client_id = this - > clients . clients . size ( ) ;
while ( client_id < max_client_id & & this - > clients . clients [ client_id ] )
client_id + + ;
if ( client_id = = max_client_id )
this - > clients . clients . push_back ( client ) ;
else
this - > clients . clients [ client_id ] = client ;
this - > clients . count + + ;
client - > setClientId ( client_id ) ;
}
{
lock_guard lock ( this - > client_nickname_lock ) ;
auto login_name = client - > getDisplayName ( ) ;
while ( login_name . length ( ) < 3 )
login_name + = " . " ;
if ( client - > getExternalType ( ) = = ClientType : : CLIENT_TEAMSPEAK )
client - > properties ( ) [ property : : CLIENT_LOGIN_NAME ] = login_name ;
std : : shared_ptr < ConnectedClient > found_client = nullptr ;
auto client_name = login_name ;
size_t counter = 0 ;
{
lock_guard clients_lock ( this - > clients . lock ) ;
while ( true ) {
for ( auto & _client : this - > clients . clients ) {
if ( ! _client ) continue ;
if ( _client - > getDisplayName ( ) = = client_name & & _client ! = client )
goto increase_name ;
}
goto nickname_valid ;
increase_name :
client_name = login_name + to_string ( + + counter ) ;
}
}
nickname_valid :
client - > setDisplayName ( client_name ) ;
}
if ( client - > getType ( ) = = ClientType : : CLIENT_TEAMSPEAK | | client - > getType ( ) = = ClientType : : CLIENT_WEB ) {
this - > properties ( ) [ property : : VIRTUALSERVER_CLIENT_CONNECTIONS ] + + ; //increase manager connections
this - > properties ( ) [ property : : VIRTUALSERVER_LAST_CLIENT_CONNECT ] = duration_cast < seconds > ( system_clock : : now ( ) . time_since_epoch ( ) ) . count ( ) ;
}
else if ( client - > getType ( ) = = 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 ] + + ; //increase manager connections
}
2019-07-17 13:37:18 -04:00
return true ;
}
2020-01-26 12:04:38 -05:00
bool VirtualServer : : unregisterClient ( shared_ptr < ConnectedClient > cl , std : : string reason , std : : unique_lock < std : : shared_mutex > & chan_tree_lock ) {
2020-01-23 20:57:58 -05:00
if ( cl - > getType ( ) = = ClientType : : CLIENT_TEAMSPEAK & & cl - > getType ( ) = = ClientType : : CLIENT_WEB ) {
sassert ( cl - > state = = ConnectionState : : DISCONNECTED ) ;
}
auto client_id = cl - > getClientId ( ) ;
if ( client_id = = 0 ) return false ; /* not registered */
{
lock_guard lock ( this - > clients . lock ) ;
if ( client_id > = this - > clients . clients . size ( ) ) {
logCritical ( this - > getServerId ( ) , " Client {} ({}|{}) has been registered, but client id exceed client id! Failed to unregister client. " , cl - > getDisplayName ( ) , client_id , cl - > getUid ( ) ) ;
} else {
auto & client_container = this - > clients . clients [ client_id ] ;
if ( client_container ! = cl ) {
logCritical ( this - > getServerId ( ) , " Client {} ({}|{}) has been registered, but container hasn't client set! Failed to unregister client. " , cl - > getDisplayName ( ) , client_id , cl - > getUid ( ) ) ;
} else {
client_container . reset ( ) ;
this - > clients . count - - ;
}
}
}
if ( cl - > getType ( ) = = ClientType : : CLIENT_TEAMSPEAK | | cl - > getType ( ) = = ClientType : : CLIENT_WEB )
this - > properties ( ) [ property : : VIRTUALSERVER_LAST_CLIENT_DISCONNECT ] = duration_cast < seconds > ( system_clock : : now ( ) . time_since_epoch ( ) ) . count ( ) ;
else if ( cl - > getType ( ) = = ClientType : : CLIENT_QUERY )
this - > properties ( ) [ property : : VIRTUALSERVER_LAST_QUERY_DISCONNECT ] = duration_cast < seconds > ( system_clock : : now ( ) . time_since_epoch ( ) ) . count ( ) ;
{
if ( ! chan_tree_lock . owns_lock ( ) )
chan_tree_lock . lock ( ) ;
if ( cl - > currentChannel ) //We dont have to make him invisible if he hasnt even a channel
this - > client_move ( cl , nullptr , nullptr , reason , ViewReasonId : : VREASON_SERVER_LEFT , false , chan_tree_lock ) ;
}
serverInstance - > databaseHelper ( ) - > saveClientPermissions ( this - > ref ( ) , cl - > getClientDatabaseId ( ) , cl - > clientPermissions ) ;
cl - > setClientId ( 0 ) ;
return true ;
2019-07-17 13:37:18 -04:00
}
2020-01-26 12:04:38 -05:00
void VirtualServer : : registerInternalClient ( std : : shared_ptr < ConnectedClient > client ) {
2019-07-17 13:37:18 -04:00
client - > state = ConnectionState : : CONNECTED ;
2020-01-23 20:57:58 -05:00
{
lock_guard lock ( this - > clients . lock ) ;
if ( client - > getClientId ( ) > 0 ) {
logCritical ( this - > getServerId ( ) , " Internal client {} ({}|{}) has been already registered! " , client - > getDisplayName ( ) , client - > getClientId ( ) , client - > getUid ( ) ) ;
return ;
}
ClientId client_id = 0 ;
ClientId max_client_id = this - > clients . clients . size ( ) ;
while ( client_id < max_client_id & & this - > clients . clients [ client_id ] )
client_id + + ;
if ( client_id = = max_client_id )
this - > clients . clients . push_back ( client ) ;
else
this - > clients . clients [ client_id ] = client ;
this - > clients . clients [ client_id ] = client ;
this - > clients . count + + ;
client - > setClientId ( client_id ) ;
}
2019-07-17 13:37:18 -04:00
}
2020-01-26 12:04:38 -05:00
void VirtualServer : : unregisterInternalClient ( std : : shared_ptr < ConnectedClient > client ) {
2019-07-17 13:37:18 -04:00
client - > state = ConnectionState : : DISCONNECTED ;
2020-01-23 20:57:58 -05:00
{
auto client_id = client - > getClientId ( ) ;
lock_guard lock ( this - > clients . lock ) ;
if ( client_id > = this - > clients . clients . size ( ) ) {
logCritical ( this - > getServerId ( ) , " Client {} ({}|{}) has been registered, but client id exceed client id! Failed to unregister internal client. " , client - > getDisplayName ( ) , client_id , client - > getUid ( ) ) ;
} else {
auto & client_container = this - > clients . clients [ client_id ] ;
if ( client_container ! = client ) {
logCritical ( this - > getServerId ( ) , " Client {} ({}|{}) has been registered, but container hasn't client set! Failed to unregister internal client. " , client - > getDisplayName ( ) , client_id , client - > getUid ( ) ) ;
} else {
this - > clients . count - - ;
client_container . reset ( ) ;
}
}
}
2019-07-17 13:37:18 -04:00
}
2020-01-26 12:04:38 -05:00
bool VirtualServer : : assignDefaultChannel ( const shared_ptr < ConnectedClient > & client , bool join ) {
2020-01-23 20:57:58 -05:00
shared_lock server_channel_lock ( this - > channel_tree_lock ) ;
2019-07-17 13:37:18 -04:00
std : : shared_ptr < BasicChannel > channel = nullptr ;
if ( client - > properties ( ) - > hasProperty ( property : : CLIENT_DEFAULT_CHANNEL ) & & ! client - > properties ( ) [ property : : CLIENT_DEFAULT_CHANNEL ] . as < string > ( ) . empty ( ) ) {
auto str = client - > properties ( ) [ property : : CLIENT_DEFAULT_CHANNEL ] . as < std : : string > ( ) ;
if ( str [ 0 ] = = ' / ' & & str . find_first_not_of ( " 0123456789 " , 1 ) = = string : : npos )
channel = this - > channelTree - > findChannel ( static_cast < ChannelId > ( stoll ( str . substr ( 1 ) ) ) ) ;
else
channel = this - > channelTree - > findChannelByPath ( str ) ;
if ( channel ) {
2020-01-26 08:21:34 -05:00
if ( ! channel - > permission_granted ( permission : : i_channel_needed_join_power , client - > calculate_permission ( permission : : i_channel_join_power , channel - > channelId ( ) ) , false ) ) {
2020-01-23 20:57:58 -05:00
logMessage ( this - > serverId , " {} Client tried to connect to a channel which he hasn't permission for. Channel: {} ({}) " , CLIENT_STR_LOG_PREFIX_ ( client ) , channel - > channelId ( ) , channel - > name ( ) ) ;
channel = nullptr ;
2020-01-26 08:21:34 -05:00
} else if ( ! channel - > passwordMatch ( client - > properties ( ) [ property : : CLIENT_DEFAULT_CHANNEL_PASSWORD ] , true ) & & ! permission : : v2 : : permission_granted ( 1 , client - > calculate_permission ( permission : : b_channel_join_ignore_password , channel - > channelId ( ) ) ) ) {
2020-01-23 20:57:58 -05:00
logMessage ( this - > serverId , " {} Client tried to connect to a channel which is password protected and he hasn't the right password. Channel: {} ({}) " , CLIENT_STR_LOG_PREFIX_ ( client ) , channel - > channelId ( ) , channel - > name ( ) ) ;
channel = nullptr ;
2019-07-17 13:37:18 -04:00
}
} else
2019-11-23 15:16:55 -05:00
logMessage ( this - > serverId , " {} Client {}/{} tried to join on a not existing channel. Name: {} " ,
2020-01-23 20:57:58 -05:00
CLIENT_STR_LOG_PREFIX_ ( client ) ,
client - > getDisplayName ( ) , client - > getUid ( ) ,
client - > properties ( ) [ property : : CLIENT_DEFAULT_CHANNEL ] . as < std : : string > ( ) ) ;
2019-07-17 13:37:18 -04:00
}
if ( ! channel ) channel = this - > channelTree - > getDefaultChannel ( ) ;
2020-01-23 20:57:58 -05:00
if ( ! channel ) return false ;
if ( join ) {
server_channel_lock . unlock ( ) ;
unique_lock server_channel_w_lock ( this - > channel_tree_lock ) ;
this - > client_move ( client , channel , nullptr , " " , ViewReasonId : : VREASON_USER_ACTION , false , server_channel_w_lock ) ;
} else {
client - > currentChannel = channel ;
}
2019-07-17 13:37:18 -04:00
return true ;
}
2020-01-26 12:04:38 -05:00
void VirtualServer : : testBanStateChange ( const std : : shared_ptr < ConnectedClient > & invoker ) {
2020-01-23 20:57:58 -05: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 08:32:16 -05:00
client - > close_connection ( system_clock : : now ( ) + seconds ( 1 ) ) ;
2020-01-23 20:57:58 -05:00
}
} ) ;
2019-07-17 13:37:18 -04:00
}
2020-01-26 12:04:38 -05:00
bool VirtualServer : : could_default_create_channel ( ) {
2020-01-23 20:57:58 -05:00
{
auto default_group = this - > getGroupManager ( ) - > defaultGroup ( GroupTarget : : GROUPTARGET_SERVER ) ;
if ( default_group ) {
auto flag = default_group - > permissions ( ) - > permission_value_flagged ( permission : : b_channel_create_temporary ) . value = = 1 ;
flag = flag ? flag : default_group - > permissions ( ) - > permission_value_flagged ( permission : : b_channel_create_semi_permanent ) . value = = 1 ;
flag = flag ? flag : default_group - > permissions ( ) - > permission_value_flagged ( permission : : b_channel_create_permanent ) . value = = 1 ;
if ( flag )
return true ;
}
}
{
auto default_group = this - > getGroupManager ( ) - > defaultGroup ( GroupTarget : : GROUPTARGET_CHANNEL ) ;
if ( default_group ) {
auto flag = default_group - > permissions ( ) - > permission_value_flagged ( permission : : b_channel_create_temporary ) . value = = 1 ;
flag = flag ? flag : default_group - > permissions ( ) - > permission_value_flagged ( permission : : b_channel_create_semi_permanent ) . value = = 1 ;
flag = flag ? flag : default_group - > permissions ( ) - > permission_value_flagged ( permission : : b_channel_create_permanent ) . value = = 1 ;
if ( flag )
return true ;
}
}
return false ;
2019-07-17 13:37:18 -04:00
}
/*
*
for ( auto & cl : this - > server - > getClients ( ) )
if ( cl - > isClientVisible ( client ) | | client = = cl )
cl - > notifyClientLeftViewKicked ( client , client - > currentChannel , nullptr , cmd [ " reasonmsg " ] . as < std : : string > ( ) , this ) ;
*/
2020-01-26 12:04:38 -05: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-23 20:57:58 -05: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 ) ;
unique_lock server_channel_lock ( this - > channel_tree_lock ) ; /* we're "moving" a client! */
if ( target - > currentChannel ) {
for ( const auto & client : this - > getClients ( ) ) {
if ( ! client | | client = = target )
continue ;
unique_lock client_channel_lock ( client - > channel_lock ) ;
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 */
unique_lock client_channel_lock ( target - > channel_lock ) ;
target - > notifyClientLeftViewBanned ( target , reason , invoker , time , false ) ;
target - > currentChannel = nullptr ;
2019-07-17 13:37:18 -04:00
}
2020-01-26 12:04:38 -05:00
void VirtualServer : : notify_client_kick (
2020-01-23 20:57:58 -05:00
const std : : shared_ptr < ts : : server : : ConnectedClient > & target ,
2019-07-17 13:37:18 -04:00
const std : : shared_ptr < ts : : server : : ConnectedClient > & invoker ,
const std : : string & reason ,
const std : : shared_ptr < ts : : BasicChannel > & target_channel ) {
2020-01-23 20:57:58 -05:00
if ( target_channel ) {
/* use the move! */
unique_lock server_channel_lock ( this - > channel_tree_lock , defer_lock ) ;
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 ) ;
unique_lock server_channel_lock ( this - > channel_tree_lock ) ; /* we're "moving" a client! */
if ( target - > currentChannel ) {
for ( const auto & client : this - > getClients ( ) ) {
if ( ! client | | client = = target )
continue ;
unique_lock client_channel_lock ( client - > channel_lock ) ;
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 ) ;
}
/* now disconnect the target itself */
unique_lock client_channel_lock ( target - > channel_lock ) ;
target - > notifyClientLeftViewKicked ( target , nullptr , reason , invoker , false ) ;
target - > currentChannel = nullptr ;
}
2019-07-17 13:37:18 -04: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 08:01:14 -04: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 ) {
2020-01-23 20:57:58 -05:00
if ( ! tree_lock . owns_lock ( ) )
tree_lock . lock ( ) ;
if ( channel - > deleted )
return ;
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 ( ) ;
tree_lock . unlock ( ) ;
deque < unique_lock < threads : : Mutex > > command_locks ;
for ( const auto & client : clients ) {
command_locks . push_back ( move ( unique_lock ( client - > command_lock ) ) ) ;
}
for ( const auto & client : clients )
this - > client_move ( client , default_channel , invoker , kick_message , ViewReasonId : : VREASON_CHANNEL_KICK , true , tree_lock ) ;
if ( ! tree_lock . owns_lock ( ) )
tree_lock . lock ( ) ; /* no clients left within that tree */
command_locks . clear ( ) ;
2020-06-28 08:01:14 -04: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-23 20:57:58 -05:00
this - > forEachClient ( [ & ] ( const shared_ptr < ConnectedClient > & client ) {
unique_lock client_channel_lock ( client - > channel_lock ) ;
client - > notifyChannelDeleted ( client - > channels - > delete_channel_root ( channel ) , invoker ) ;
} ) ;
2019-07-17 13:37:18 -04:00
}
2020-01-26 12:04:38 -05:00
void VirtualServer : : client_move (
2020-01-23 20:57:58 -05:00
const shared_ptr < ts : : server : : ConnectedClient > & target ,
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 13:37:18 -04:00
bool notify_client ,
std : : unique_lock < std : : shared_mutex > & server_channel_write_lock ) {
2020-01-23 20:57:58 -05:00
TIMING_START ( timings ) ;
if ( server_channel_write_lock . owns_lock ( ) )
server_channel_write_lock . unlock ( ) ;
lock_guard client_command_lock ( target - > command_lock ) ;
server_channel_write_lock . lock ( ) ;
TIMING_STEP ( timings , " chan tree l " ) ;
if ( target - > currentChannel = = target_channel )
return ;
/* first step: resolve the target channel / or fix missing */
auto s_target_channel = dynamic_pointer_cast < ServerChannel > ( target_channel ) ;
auto s_source_channel = dynamic_pointer_cast < ServerChannel > ( target - > currentChannel ) ;
assert ( ! target - > currentChannel | | s_source_channel ! = nullptr ) ;
deque < property : : ClientProperties > client_updates ;
std : : deque < property : : ClientProperties > changed_groups { } ;
if ( target_channel ) {
assert ( s_target_channel ) ;
if ( s_target_channel - > deleted ) {
target_channel = this - > channelTree - > getDefaultChannel ( ) ;
s_target_channel = dynamic_pointer_cast < ServerChannel > ( target_channel ) ;
assert ( s_target_channel ) ;
}
/* update the group properties here already, so for all enter views we could just send the new props directly */
changed_groups = this - > groups - > update_server_group_property ( target , true , s_target_channel ) ;
client_updates . insert ( client_updates . end ( ) , changed_groups . begin ( ) , changed_groups . end ( ) ) ; //TODO: Only update for clients which have no enter?
}
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 " ) ;
/* second step: show the target channel to the client if its not shown and let him subscibe to the channel */
if ( target_channel & & notify_client ) {
unique_lock client_channel_lock ( target - > channel_lock ) ;
bool success = false ;
/* TODO: Use a bunk here and not a notify for every single */
for ( const auto & channel : target - > channels - > show_channel ( l_target_channel , success ) )
target - > notifyChannelShow ( channel - > channel ( ) , channel - > previous_channel ) ;
sassert ( success ) ;
if ( ! success )
return ;
target - > subscribeChannel ( { target_channel } , false , true ) ;
}
TIMING_STEP ( timings , " target show " ) ;
if ( target_channel ) {
this - > forEachClient ( [ & ] ( const shared_ptr < ConnectedClient > & client ) {
if ( ! notify_client & & client = = target ) return ;
unique_lock client_channel_lock ( client - > channel_lock ) ;
auto chan_target = client - > channels - > find_channel ( target_channel ) ;
if ( chan_target ) {
auto chan_source = client - > channels - > find_channel ( s_source_channel ) ;
if ( chan_source ) {
if ( chan_target - > subscribed | | client = = target ) {
if ( client = = target | | client - > isClientVisible ( target , false ) ) {
client - > notifyClientMoved ( target , s_target_channel , reason_id , reason_message , invoker , false ) ;
} else {
client - > notifyClientEnterView ( target , invoker , reason_message , s_target_channel , reason_id , s_source_channel , false ) ;
}
} else if ( client - > isClientVisible ( target , false ) ) {
//Client got out of view
client - > notifyClientLeftView ( target , s_target_channel , reason_id , reason_message . empty ( ) ? string ( " view left " ) : reason_message , invoker , false ) ;
}
} else {
if ( client = = target & & client - > getType ( ) ! = ClientType : : CLIENT_INTERNAL & & client - > getType ( ) ! = ClientType : : CLIENT_MUSIC )
logCritical ( this - > getServerId ( ) , " {} Client enters visibility twice! " , CLIENT_STR_LOG_PREFIX_ ( client ) ) ;
//Client entered view
if ( chan_target - > subscribed )
client - > notifyClientEnterView ( target , invoker , reason_message , s_target_channel , ViewReasonId : : VREASON_USER_ACTION , nullptr , false ) ;
}
} else {
/* target channel isn't visible => so client gone out of view */
if ( client = = target & & client - > getType ( ) ! = ClientType : : CLIENT_INTERNAL & & client - > getType ( ) ! = ClientType : : CLIENT_MUSIC )
logCritical ( this - > getServerId ( ) , " {} Moving own client into a not visible channel! This shall not happen! " , CLIENT_STR_LOG_PREFIX_ ( client ) ) ;
//Test for in view? (Notify already does but nvm)
if ( client - > isClientVisible ( target , false ) ) {
//Client got out of view
if ( reason_id = = ViewReasonId : : VREASON_USER_ACTION )
client - > notifyClientLeftView ( target , nullptr , ViewReasonId : : VREASON_SERVER_LEFT , reason_message . empty ( ) ? " joined a hidden channel " : reason_message , invoker , false ) ;
else
client - > notifyClientLeftView ( target , nullptr , ViewReasonId : : VREASON_SERVER_LEFT , reason_message . empty ( ) ? " moved to a hidden channel " : reason_message , invoker , false ) ;
}
}
} ) ;
if ( s_source_channel )
s_source_channel - > unregister_client ( target ) ;
s_target_channel - > register_client ( target ) ;
} else {
/* client left the server */
if ( target - > currentChannel ) {
for ( const auto & client : this - > getClients ( ) ) {
if ( ! client | | client = = target )
continue ;
unique_lock client_channel_lock ( client - > channel_lock ) ;
if ( client - > isClientVisible ( target , false ) )
client - > notifyClientLeftView ( target , nullptr , reason_id , reason_message , invoker , false ) ;
}
s_source_channel - > unregister_client ( target ) ;
}
}
TIMING_STEP ( timings , " notify view " ) ;
target - > currentChannel = target_channel ;
server_channel_write_lock . unlock ( ) ;
/* third step: update stuff for the client (remember: the client cant execute anything at the moment!) */
shared_lock server_channel_read_lock ( this - > channel_tree_lock ) ;
unique_lock client_channel_lock ( target - > channel_lock ) ;
TIMING_STEP ( timings , " lock own tr " ) ;
if ( s_source_channel ) {
s_source_channel - > properties ( ) [ property : : CHANNEL_LAST_LEFT ] = chrono : : duration_cast < chrono : : milliseconds > ( chrono : : system_clock : : now ( ) . time_since_epoch ( ) ) . count ( ) ;
auto source_channel_group = this - > groups - > getChannelGroupExact ( target - > getClientDatabaseId ( ) , s_source_channel , false ) ;
if ( source_channel_group ) {
auto default_data = source_channel_group - > group - > permissions ( ) - > permission_value_flagged ( permission : : b_group_is_permanent ) ;
if ( ! default_data . has_value | | default_data . value ! = 1 )
this - > groups - > setChannelGroup ( target - > getClientDatabaseId ( ) , nullptr , s_source_channel ) ;
}
auto update = target - > properties ( ) [ property : : CLIENT_IS_TALKER ] . as < bool > ( ) | | target - > properties ( ) [ property : : CLIENT_TALK_REQUEST ] . as < int64_t > ( ) > 0 ;
if ( update ) {
target - > properties ( ) [ property : : CLIENT_IS_TALKER ] = 0 ;
target - > properties ( ) [ property : : CLIENT_TALK_REQUEST ] = 0 ;
target - > properties ( ) [ property : : CLIENT_TALK_REQUEST_MSG ] = " " ;
client_updates . push_back ( property : : CLIENT_IS_TALKER ) ;
client_updates . push_back ( property : : CLIENT_TALK_REQUEST ) ;
client_updates . push_back ( property : : CLIENT_TALK_REQUEST_MSG ) ;
}
TIMING_STEP ( timings , " src chan up " ) ;
}
if ( s_target_channel ) {
if ( target - > update_cached_permissions ( ) ) /* update cached calculated permissions */
target - > sendNeededPermissions ( false ) ;
TIMING_STEP ( timings , " perm gr upd " ) ;
if ( s_source_channel ) {
deque < ChannelId > deleted ;
for ( const auto & channel : target - > channels - > test_channel ( l_source_channel , l_target_channel ) )
deleted . push_back ( channel - > channelId ( ) ) ;
if ( ! deleted . empty ( ) )
target - > notifyChannelHide ( deleted , false ) ;
auto i_source_channel = s_source_channel - > channelId ( ) ;
if ( std : : find ( deleted . begin ( ) , deleted . end ( ) , i_source_channel ) = = deleted . end ( ) ) {
2020-01-26 08:21:34 -05:00
auto source_channel_sub_power = target - > calculate_permission ( permission : : i_channel_subscribe_power , i_source_channel ) ;
2020-01-23 20:57:58 -05:00
if ( ! s_source_channel - > permission_granted ( permission : : i_channel_needed_subscribe_power , source_channel_sub_power , false ) ) {
2020-01-26 08:21:34 -05:00
auto source_channel_sub_power_ignore = target - > calculate_permission ( permission : : b_channel_ignore_subscribe_power , i_source_channel ) ;
if ( ! permission : : v2 : : permission_granted ( 1 , source_channel_sub_power_ignore , true ) ) {
2020-01-23 20:57:58 -05:00
logTrace ( this - > serverId , " Force unsubscribing of client {} for channel {}/{}. (Channel switch and no permissions) " ,
CLIENT_STR_LOG_PREFIX_ ( target ) , s_source_channel - > name ( ) ,
i_source_channel
) ;
target - > unsubscribeChannel ( { s_source_channel } , false ) ; //Unsubscribe last channel (hasn't permissions)
}
}
}
TIMING_STEP ( timings , " src hide ts " ) ;
}
}
client_channel_lock . unlock ( ) ;
/* both methods lock if they require stuff */
this - > notifyClientPropertyUpdates ( target , client_updates , s_source_channel ? true : false ) ;
TIMING_STEP ( timings , " notify cpro " ) ;
if ( s_target_channel ) {
target - > updateChannelClientProperties ( false , s_source_channel ? true : false ) ;
TIMING_STEP ( timings , " notify_t_pr " ) ;
}
debugMessage ( this - > getServerId ( ) , " {} Client move timings: {} " , CLIENT_STR_LOG_PREFIX_ ( target ) , TIMING_FINISH ( timings ) ) ;
2019-07-17 13:37:18 -04:00
}