2019-07-17 13:37:18 -04:00
# include <memory>
2020-09-24 17:00:58 -04:00
# include <PermissionManager.h>
2019-07-17 13:37:18 -04:00
# include <misc/endianness.h>
# include <log/LogUtils.h>
# include <ThreadPool/Timer.h>
# include <regex>
# include <src/build.h>
# include <Properties.h>
2020-09-24 16:57:10 -04:00
# include <src/client/command_handler/helpers.h>
2019-07-17 13:37:18 -04:00
# include "src/channel/ClientChannelView.h"
# include "SpeakingClient.h"
# include "src/InstanceHandler.h"
# include "StringVariable.h"
# include "misc/timer.h"
2020-06-28 08:01:14 -04:00
# include "../manager/ActionLogger.h"
2019-07-17 13:37:18 -04:00
using namespace std : : chrono ;
using namespace ts ;
using namespace ts : : server ;
using namespace ts : : protocol ;
//#define PKT_LOG_VOICE
//#define PKT_LOG_WHISPER
2020-09-06 15:00:27 -04:00
constexpr static auto kMaxWhisperClientNameLength { 30 } ;
constexpr static auto kWhisperClientUniqueIdLength { 28 } ; /* base64 encoded SHA1 hash */
constexpr static auto kWhisperMaxHeaderLength { 2 + 2 + 1 + 2 + kWhisperClientUniqueIdLength + 1 + kMaxWhisperClientNameLength } ;
2019-07-17 13:37:18 -04:00
2020-11-07 07:17:51 -05:00
SpeakingClient : : SpeakingClient ( sql : : SqlManager * a , const std : : shared_ptr < VirtualServer > & b ) : ConnectedClient ( a , b ) {
speak_begin = std : : chrono : : system_clock : : now ( ) ;
speak_last_packet = std : : chrono : : system_clock : : now ( ) ;
} ;
SpeakingClient : : ~ SpeakingClient ( ) {
if ( auto server { this - > server } ; this - > rtc_client_id > 0 & & server ) {
server - > rtc_server ( ) . destroy_client ( this - > rtc_client_id ) ;
}
}
2019-07-17 13:37:18 -04:00
bool SpeakingClient : : shouldReceiveVoice ( const std : : shared_ptr < ConnectedClient > & sender ) {
2020-01-23 20:57:58 -05:00
//if(this->properties()[property::CLIENT_AWAY].as<bool>()) return false;
if ( ! this - > properties ( ) [ property : : CLIENT_OUTPUT_HARDWARE ] . as < bool > ( ) ) return false ;
if ( this - > properties ( ) [ property : : CLIENT_OUTPUT_MUTED ] . as < bool > ( ) ) return false ;
{
shared_lock client_lock ( this - > channel_lock ) ;
for ( const auto & entry : this - > mutedClients )
if ( entry . lock ( ) = = sender )
return false ;
}
return true ;
2019-07-17 13:37:18 -04:00
}
bool SpeakingClient : : shouldReceiveVoiceWhisper ( const std : : shared_ptr < ConnectedClient > & sender ) {
2020-01-23 20:57:58 -05:00
if ( ! this - > shouldReceiveVoice ( sender ) )
return false ;
2019-07-17 13:37:18 -04:00
2020-02-28 05:24:07 -05:00
return permission : : v2 : : permission_granted ( this - > cpmerission_needed_whisper_power , sender - > cpmerission_whisper_power , false ) ;
2019-07-17 13:37:18 -04:00
}
2020-11-07 07:17:51 -05:00
bool SpeakingClient : : should_handle_voice_packet ( size_t ) {
2019-07-17 13:37:18 -04:00
auto current_channel = this - > currentChannel ;
2020-11-07 07:17:51 -05:00
if ( ! current_channel ) { return false ; }
if ( ! this - > allowedToTalk ) { return false ; }
2020-01-23 20:57:58 -05:00
this - > updateSpeak ( false , system_clock : : now ( ) ) ;
this - > resetIdleTime ( ) ;
2019-07-17 13:37:18 -04:00
2020-11-07 07:17:51 -05:00
return true ;
2019-07-17 13:37:18 -04:00
}
//2 + 2 + 8
# define OUT_WHISPER_PKT_OFFSET 5
//#define PKT_LOG_WHISPER
enum WhisperType {
2020-01-23 20:57:58 -05:00
SERVER_GROUP = 0 ,
CHANNEL_GROUP = 1 ,
CHANNEL_COMMANDER = 2 ,
2020-05-10 10:23:02 -04:00
ALL = 3 ,
2020-08-13 06:58:19 -04:00
ECHO = 0x10 ,
2019-07-17 13:37:18 -04:00
} ;
enum WhisperTarget {
2020-01-23 20:57:58 -05:00
CHANNEL_ALL = 0 ,
CHANNEL_CURRENT = 1 ,
CHANNEL_PARENT = 2 ,
CHANNEL_ALL_PARENT = 3 ,
CHANNEL_FAMILY = 4 ,
CHANNEL_COMPLETE_FAMILY = 5 ,
CHANNEL_SUBCHANNELS = 6
2019-07-17 13:37:18 -04:00
} ;
2020-02-28 05:24:07 -05:00
inline bool update_whisper_error ( std : : chrono : : system_clock : : time_point & last ) {
auto now = std : : chrono : : system_clock : : now ( ) ;
if ( last + std : : chrono : : milliseconds { 500 } < now ) {
last = now ;
return true ;
}
return false ;
}
2019-07-17 13:37:18 -04:00
//All clients => type := SERVER_GROUP and target_id := 0
//Server group => type := SERVER_GROUP and target_id := <server group id>
//Channel group => type := CHANNEL_GROUP and target_id := <channel group id>
//Channel commander => type := CHANNEL_COMMANDER and target_id := 0
2020-09-06 15:00:27 -04:00
void SpeakingClient : : handlePacketVoiceWhisper ( const pipes : : buffer_view & payload , bool new_packet , bool head ) {
if ( payload . length ( ) < 5 ) {
2020-01-23 20:57:58 -05:00
this - > disconnect ( " Invalid packet (Voice whisper) " ) ;
2020-09-06 15:00:27 -04:00
logMessage ( this - > getServerId ( ) , " {} Tried to send a too short whisper packet. Length: {} " , CLIENT_STR_LOG_PREFIX , payload . length ( ) ) ;
2020-01-23 20:57:58 -05:00
return ;
}
2020-09-06 15:00:27 -04:00
uint16_t payload_offset { 0 } ;
auto voice_packet_id = be2le16 ( ( char * ) payload . data_ptr ( ) , payload_offset , & payload_offset ) ;
auto voice_codec = ( uint8_t ) payload [ payload_offset + + ] ;
2020-01-23 20:57:58 -05:00
2020-09-06 15:00:27 -04:00
std : : deque < std : : shared_ptr < SpeakingClient > > target_clients ;
2020-01-23 20:57:58 -05:00
if ( new_packet ) {
2020-09-06 15:00:27 -04:00
if ( payload . length ( ) < 7 ) {
2020-01-23 20:57:58 -05:00
this - > disconnect ( " Invalid packet (Voice whisper | New) " ) ;
2020-09-06 15:00:27 -04:00
logMessage ( this - > getServerId ( ) , " {} Tried to send a too short whisper packet. Length: {} " , CLIENT_STR_LOG_PREFIX , payload . length ( ) ) ;
2020-01-23 20:57:58 -05:00
return ;
}
2020-09-06 15:00:27 -04:00
auto type = ( WhisperType ) payload [ payload_offset + + ] ;
auto target = ( WhisperTarget ) payload [ payload_offset + + ] ;
auto type_id = be2le64 ( ( char * ) payload . data_ptr ( ) , payload_offset , & payload_offset ) ;
2020-01-23 20:57:58 -05:00
2019-07-17 13:37:18 -04:00
# ifdef PKT_LOG_WHISPER
2020-01-23 20:57:58 -05:00
logTrace ( this - > getServerId ( ) , " {} Whisper data length: {}. Type: {}. Target: {}. Target ID: {}. " , CLIENT_STR_LOG_PREFIX , data_length , type , target , type_id ) ;
2019-07-17 13:37:18 -04:00
# endif
2020-08-13 06:58:19 -04:00
if ( type = = WhisperType : : ECHO ) {
2020-09-06 15:00:27 -04:00
target_clients . push_back ( dynamic_pointer_cast < SpeakingClient > ( this - > ref ( ) ) ) ;
2020-05-10 10:23:02 -04:00
} else {
for ( const auto & client : this - > server - > getClients ( ) ) {
auto speakingClient = dynamic_pointer_cast < SpeakingClient > ( client ) ;
if ( ! speakingClient | | client = = this ) continue ;
if ( ! speakingClient - > currentChannel ) continue ;
if ( type = = WhisperType : : ALL ) {
2020-09-06 15:00:27 -04:00
target_clients . push_back ( speakingClient ) ;
2020-05-10 10:23:02 -04:00
} else if ( type = = WhisperType : : SERVER_GROUP ) {
if ( type_id = = 0 )
2020-09-06 15:00:27 -04:00
target_clients . push_back ( speakingClient ) ;
2020-05-10 10:23:02 -04:00
else {
shared_lock client_lock ( this - > channel_lock ) ;
for ( const auto & id : client - > cached_server_groups ) {
if ( id = = type_id ) {
2020-09-06 15:00:27 -04:00
target_clients . push_back ( speakingClient ) ;
2020-05-10 10:23:02 -04:00
break ;
}
2020-01-23 20:57:58 -05:00
}
}
2020-05-10 10:23:02 -04:00
} else if ( type = = WhisperType : : CHANNEL_GROUP ) {
if ( client - > cached_channel_group = = type_id )
2020-09-06 15:00:27 -04:00
target_clients . push_back ( speakingClient ) ;
2020-05-10 10:23:02 -04:00
} else if ( type = = WhisperType : : CHANNEL_COMMANDER ) {
if ( client - > properties ( ) [ property : : CLIENT_IS_CHANNEL_COMMANDER ] . as < bool > ( ) )
2020-09-06 15:00:27 -04:00
target_clients . push_back ( speakingClient ) ;
2020-01-23 20:57:58 -05:00
}
}
2020-05-10 10:23:02 -04:00
if ( target = = WhisperTarget : : CHANNEL_CURRENT ) {
2020-09-06 15:00:27 -04:00
target_clients . erase ( std : : remove_if ( target_clients . begin ( ) , target_clients . end ( ) , [ & ] ( const shared_ptr < SpeakingClient > & target ) {
2020-05-10 10:23:02 -04:00
return target - > currentChannel ! = this - > currentChannel ;
2020-09-06 15:00:27 -04:00
} ) , target_clients . end ( ) ) ;
2020-05-10 10:23:02 -04:00
} else if ( target = = WhisperTarget : : CHANNEL_PARENT ) {
auto current_parent = this - > currentChannel - > parent ( ) ;
2020-01-23 20:57:58 -05:00
if ( ! current_parent ) return ;
2020-05-10 10:23:02 -04:00
2020-09-06 15:00:27 -04:00
target_clients . erase ( std : : remove_if ( target_clients . begin ( ) , target_clients . end ( ) , [ & ] ( const shared_ptr < SpeakingClient > & target ) {
2020-05-10 10:23:02 -04:00
return target - > currentChannel ! = current_parent ;
2020-09-06 15:00:27 -04:00
} ) , target_clients . end ( ) ) ;
2020-05-10 10:23:02 -04:00
} else if ( target = = WhisperTarget : : CHANNEL_ALL_PARENT ) {
shared_ptr < BasicChannel > current_parent ;
{
current_parent = this - > currentChannel - > parent ( ) ;
if ( ! current_parent ) return ;
}
2020-09-06 15:00:27 -04:00
target_clients . erase ( std : : remove_if ( target_clients . begin ( ) , target_clients . end ( ) , [ & ] ( const shared_ptr < SpeakingClient > & target ) {
2020-05-10 10:23:02 -04:00
auto tmp_parent = current_parent ;
while ( tmp_parent & & tmp_parent ! = target - > currentChannel )
tmp_parent = tmp_parent - > parent ( ) ;
return target - > currentChannel ! = tmp_parent ;
2020-09-06 15:00:27 -04:00
} ) , target_clients . end ( ) ) ;
2020-05-10 10:23:02 -04:00
} else if ( target = = WhisperTarget : : CHANNEL_FAMILY ) {
shared_ptr < BasicChannel > current = this - > currentChannel ;
2020-09-06 15:00:27 -04:00
target_clients . erase ( std : : remove_if ( target_clients . begin ( ) , target_clients . end ( ) , [ & ] ( const shared_ptr < SpeakingClient > & target ) {
2020-05-10 10:23:02 -04:00
auto tmp_current = target - > currentChannel ;
while ( tmp_current & & tmp_current ! = current )
tmp_current = tmp_current - > parent ( ) ;
return current ! = tmp_current ;
2020-09-06 15:00:27 -04:00
} ) , target_clients . end ( ) ) ;
2020-05-10 10:23:02 -04:00
} else if ( target = = WhisperTarget : : CHANNEL_COMPLETE_FAMILY ) {
shared_ptr < BasicChannel > current = this - > currentChannel ;
while ( current & & current - > parent ( ) ) current = current - > parent ( ) ;
2020-09-06 15:00:27 -04:00
target_clients . erase ( std : : remove_if ( target_clients . begin ( ) , target_clients . end ( ) , [ & ] ( const shared_ptr < SpeakingClient > & target ) {
2020-05-10 10:23:02 -04:00
auto tmp_current = target - > currentChannel ;
while ( tmp_current & & tmp_current ! = current )
tmp_current = tmp_current - > parent ( ) ;
return current ! = tmp_current ;
2020-09-06 15:00:27 -04:00
} ) , target_clients . end ( ) ) ;
2020-05-10 10:23:02 -04:00
} else if ( target = = WhisperTarget : : CHANNEL_SUBCHANNELS ) {
shared_ptr < BasicChannel > current = this - > currentChannel ;
2020-09-06 15:00:27 -04:00
target_clients . erase ( std : : remove_if ( target_clients . begin ( ) , target_clients . end ( ) , [ & ] ( const shared_ptr < SpeakingClient > & target ) {
2020-05-10 10:23:02 -04:00
return target - > currentChannel - > parent ( ) ! = current ;
2020-09-06 15:00:27 -04:00
} ) , target_clients . end ( ) ) ;
2020-01-23 20:57:58 -05:00
}
2020-05-10 10:23:02 -04:00
auto self_lock = this - > _this . lock ( ) ;
2020-09-06 15:00:27 -04:00
target_clients . erase ( std : : remove_if ( target_clients . begin ( ) , target_clients . end ( ) , [ & ] ( const std : : shared_ptr < ConnectedClient > & cl ) {
2020-05-10 10:23:02 -04:00
auto speakingClient = dynamic_pointer_cast < SpeakingClient > ( cl ) ;
return ! speakingClient - > shouldReceiveVoiceWhisper ( self_lock ) ;
2020-09-06 15:00:27 -04:00
} ) , target_clients . end ( ) ) ;
2020-02-28 05:24:07 -05:00
2020-09-06 15:00:27 -04:00
if ( target_clients . empty ( ) ) {
2020-08-13 06:58:19 -04:00
if ( update_whisper_error ( this - > speak_last_no_whisper_target ) ) {
command_result result { error : : whisper_no_targets } ;
this - > notifyError ( result ) ;
}
return ;
2020-02-28 05:24:07 -05:00
}
2020-08-13 06:58:19 -04:00
2020-09-06 15:00:27 -04:00
if ( target_clients . size ( ) > this - > server - > properties ( ) [ property : : VIRTUALSERVER_MIN_CLIENTS_IN_CHANNEL_BEFORE_FORCED_SILENCE ] . as_save < size_t > ( ) ) {
2020-08-13 06:58:19 -04:00
if ( update_whisper_error ( this - > speak_last_too_many_whisper_targets ) ) {
command_result result { error : : whisper_too_many_targets } ;
this - > notifyError ( result ) ;
}
return ;
2020-02-28 05:24:07 -05:00
}
}
2020-09-06 15:00:27 -04:00
} else {
auto channelCount = ( uint8_t ) payload [ payload_offset + + ] ;
auto clientCount = ( uint8_t ) payload [ payload_offset + + ] ;
if ( payload . length ( ) < 5 + clientCount * 2 + channelCount * 8 ) {
logMessage ( this - > getServerId ( ) , " {} Tried to send a too short whisper packet. Length: {} Required: {} " , CLIENT_STR_LOG_PREFIX , payload . length ( ) , to_string ( 5 + channelCount * 2 + clientCount * 8 ) ) ;
return ;
}
2020-01-23 20:57:58 -05:00
2020-09-06 15:00:27 -04:00
ChannelId channelIds [ channelCount ] ;
ClientId clientIds [ clientCount ] ;
2020-01-23 20:57:58 -05:00
2020-09-06 15:00:27 -04:00
for ( uint8_t index = 0 ; index < channelCount ; index + + ) {
channelIds [ index ] = be2le64 ( ( char * ) payload . data_ptr ( ) , payload_offset , & payload_offset ) ;
2020-01-23 20:57:58 -05:00
}
2020-09-06 15:00:27 -04:00
for ( uint8_t index = 0 ; index < clientCount ; index + + ) {
clientIds [ index ] = be2le16 ( ( char * ) payload . data_ptr ( ) , payload_offset , & payload_offset ) ;
2020-01-23 20:57:58 -05:00
}
2020-09-06 15:00:27 -04:00
# ifdef PKT_LOG_WHISPER
logTrace ( this - > getServerId ( ) , " {} Whisper data length: {}. Client count: {}. Channel count: {}. " , CLIENT_STR_LOG_PREFIX , dataLength , clientCount , channelCount ) ;
# endif
2020-01-23 20:57:58 -05:00
2020-09-06 15:00:27 -04:00
for ( const auto & client : this - > server - > getClients ( ) ) {
auto speaking_client = dynamic_pointer_cast < SpeakingClient > ( client ) ;
if ( ! speaking_client | | client = = this | | ! speaking_client - > currentChannel )
continue ;
2020-01-23 20:57:58 -05:00
2020-09-06 15:00:27 -04:00
auto clientChannelId = speaking_client - > getChannelId ( ) ;
auto clientId = speaking_client - > getClientId ( ) ;
2020-02-28 05:24:07 -05:00
2020-09-06 15:00:27 -04:00
for ( uint8_t index = 0 ; index < channelCount ; index + + ) {
if ( channelIds [ index ] = = clientChannelId ) {
goto add_client ;
}
}
2020-02-28 05:24:07 -05:00
2020-09-06 15:00:27 -04:00
for ( uint8_t index = 0 ; index < clientCount ; index + + ) {
if ( clientIds [ index ] = = clientId ) {
goto add_client ;
}
}
2020-02-28 05:24:07 -05:00
2020-09-06 15:00:27 -04:00
continue ;
2020-02-28 05:24:07 -05:00
2020-09-06 15:00:27 -04:00
add_client :
if ( ! speaking_client - > shouldReceiveVoiceWhisper ( this - > ref ( ) ) ) {
continue ;
}
2020-02-28 05:24:07 -05:00
2020-09-06 15:00:27 -04:00
target_clients . push_back ( speaking_client ) ;
}
}
2020-02-28 05:24:07 -05:00
2020-09-06 15:00:27 -04:00
if ( target_clients . empty ( ) ) {
if ( update_whisper_error ( this - > speak_last_no_whisper_target ) ) {
command_result result { error : : whisper_no_targets } ;
this - > notifyError ( result ) ;
2020-02-28 05:24:07 -05:00
}
2020-09-06 15:00:27 -04:00
return ;
}
if ( target_clients . size ( ) > this - > server - > properties ( ) [ property : : VIRTUALSERVER_MIN_CLIENTS_IN_CHANNEL_BEFORE_FORCED_SILENCE ] . as_save < size_t > ( ) ) {
if ( update_whisper_error ( this - > speak_last_too_many_whisper_targets ) ) {
command_result result { error : : whisper_too_many_targets } ;
this - > notifyError ( result ) ;
2020-02-28 05:24:07 -05:00
}
2020-09-06 15:00:27 -04:00
return ;
}
/* send the packet */
{
size_t voice_payload_length = payload . length ( ) - payload_offset ;
2020-02-28 05:24:07 -05:00
2020-01-23 20:57:58 -05:00
//Create the packet data
2020-09-06 15:00:27 -04:00
char whisper_packet_buffer [ kWhisperMaxHeaderLength + voice_payload_length ] ;
size_t whisper_packet_offset { 0 } ;
size_t whisper_packet_teamspeak_offset { 0 } ;
2020-01-23 20:57:58 -05:00
2020-09-06 15:00:27 -04:00
/* writing the teaspeak header */
if ( head ) {
auto uniqueId = this - > getUid ( ) ;
auto nickname = this - > getDisplayName ( ) ;
2020-01-23 20:57:58 -05:00
2020-09-06 15:00:27 -04:00
if ( uniqueId . length ( ) > kWhisperClientUniqueIdLength ) {
logCritical ( LOG_GENERAL , " Clients unique id is longer than the expected max length of {}. Unique length: {} " , kWhisperClientUniqueIdLength , uniqueId . length ( ) ) ;
return ;
}
if ( nickname . length ( ) > kMaxWhisperClientNameLength ) {
logCritical ( LOG_GENERAL , " Clients name is longer than the expected max length of {}. Name length: {} " , kMaxWhisperClientNameLength , nickname . length ( ) ) ;
return ;
}
memset ( whisper_packet_buffer + whisper_packet_offset , 0 , kWhisperClientUniqueIdLength ) ;
memcpy ( whisper_packet_buffer + whisper_packet_offset , uniqueId . data ( ) , uniqueId . length ( ) ) ;
whisper_packet_offset + = kWhisperClientUniqueIdLength ;
2020-01-23 20:57:58 -05:00
2020-09-06 15:00:27 -04:00
whisper_packet_buffer [ whisper_packet_offset + + ] = nickname . length ( ) ;
memcpy ( whisper_packet_buffer + whisper_packet_offset , nickname . data ( ) , nickname . length ( ) ) ;
whisper_packet_offset + = nickname . length ( ) ;
2020-01-23 20:57:58 -05:00
}
2020-09-06 15:00:27 -04:00
/* writing the "normal" header and payload */
{
whisper_packet_teamspeak_offset = whisper_packet_offset ;
* ( uint16_t * ) & whisper_packet_buffer [ whisper_packet_offset ] = htons ( voice_packet_id ) ;
whisper_packet_offset + = 2 ;
* ( uint16_t * ) & whisper_packet_buffer [ whisper_packet_offset ] = htons ( this - > getClientId ( ) ) ;
whisper_packet_offset + = 2 ;
whisper_packet_buffer [ whisper_packet_offset + + ] = voice_codec ;
if ( voice_payload_length > 0 ) {
memcpy ( & whisper_packet_buffer [ whisper_packet_offset ] , & payload [ payload_offset ] , voice_payload_length ) ;
whisper_packet_offset + = voice_payload_length ;
}
}
VoicePacketFlags flags { } ;
flags . head = head ;
pipes : : buffer_view teaspeak_packet { } , teamspeak_packet { } ;
teaspeak_packet = pipes : : buffer_view { whisper_packet_buffer , whisper_packet_offset } ;
teamspeak_packet = pipes : : buffer_view { whisper_packet_buffer + whisper_packet_teamspeak_offset , whisper_packet_offset - whisper_packet_teamspeak_offset } ;
2020-09-16 15:03:07 -04:00
auto self_ref = this - > ref ( ) ;
2020-09-06 15:00:27 -04:00
for ( const auto & cl : target_clients ) {
2020-09-16 15:03:07 -04:00
if ( cl = = self_ref | | cl - > shouldReceiveVoiceWhisper ( self_ref ) ) {
2020-09-06 15:00:27 -04:00
cl - > send_voice_whisper_packet ( teamspeak_packet , teaspeak_packet , flags ) ;
}
}
2020-01-23 20:57:58 -05:00
}
2020-09-06 15:00:27 -04:00
this - > resetIdleTime ( ) ;
this - > updateSpeak ( false , std : : chrono : : system_clock : : now ( ) ) ;
2019-07-17 13:37:18 -04:00
}
# define TEST_PARM(type) \
do { \
2020-01-23 20:57:58 -05:00
if ( ! cmd [ 0 ] [ key ] . castable < type > ( ) ) \
return { findError ( " parameter_invalid " ) , " Invalid type for " + key } ; \
2019-07-17 13:37:18 -04:00
} while ( false )
auto regex_wildcard = std : : regex ( " .* " ) ;
# define S(x) #x
# define HWID_REGEX(name, pattern) \
2020-05-10 10:23:02 -04:00
auto regex_hwid_ # # name = [ ] ( ) noexcept { \
2020-01-23 20:57:58 -05:00
try { \
return std : : regex ( pattern ) ; \
} catch ( std : : exception & ex ) { \
logError ( 0 , " Failed to parse regex for " S ( name ) ) ; \
} \
return regex_wildcard ; \
2019-07-17 13:37:18 -04:00
} ( ) ;
HWID_REGEX ( windows , " ^[a-z0-9]{32},[a-z0-9]{32}$ " ) ;
HWID_REGEX ( unix , " ^[a-z0-9]{32}$ " ) ;
HWID_REGEX ( android , " ^[a-z0-9]{16}$ " ) ;
HWID_REGEX ( ios , " ^[A-Z0-9]{8}-[A-Z0-9]{4}-[A-Z0-9]{4}-[A-Z0-9]{4}-[A-Z0-9]{12}$ " ) ;
2020-01-25 17:42:37 -05:00
command_result SpeakingClient : : handleCommandClientInit ( Command & cmd ) {
2020-01-23 20:57:58 -05:00
TIMING_START ( timings ) ;
2020-01-23 20:40:30 -05:00
{
lock_guard < threads : : Mutex > lock ( this - > server - > join_attempts_lock ) ;
auto inetAddr = this - > getPeerIp ( ) ;
if ( config : : voice : : clientConnectLimit > 0 & & this - > server - > join_attempts [ inetAddr ] + 1 > config : : voice : : clientConnectLimit )
2020-01-25 17:42:37 -05:00
return command_result { error : : client_join_rate_limit_reached } ;
2020-01-23 20:40:30 -05:00
if ( config : : voice : : connectLimit > 0 & & this - > server - > join_attempts [ " _ " ] + 1 > config : : voice : : connectLimit )
2020-01-25 17:42:37 -05:00
return command_result { error : : server_join_rate_limit_reached } ;
2020-01-23 20:40:30 -05:00
this - > server - > join_attempts [ inetAddr ] + + ;
this - > server - > join_attempts [ " _ " ] + + ;
}
TIMING_STEP ( timings , " join atmp c " ) ;
2020-01-25 17:42:37 -05:00
if ( ! DatabaseHelper : : assignDatabaseId ( this - > server - > getSql ( ) , this - > server - > getServerId ( ) , _this . lock ( ) ) )
return command_result { error : : vs_critical , " Could not assign database id! " } ;
2020-01-23 20:57:58 -05:00
TIMING_STEP ( timings , " db assign " ) ;
this - > server - > getGroupManager ( ) - > enableCache ( this - > getClientDatabaseId ( ) ) ;
TIMING_STEP ( timings , " gr cache " ) ;
2019-07-17 13:37:18 -04:00
2020-01-23 20:57:58 -05:00
const static vector < string > available_parameters = {
" client_nickname " ,
2019-07-17 13:37:18 -04:00
" client_version " ,
2020-01-23 20:57:58 -05:00
" client_platform " ,
" client_input_muted " ,
" client_input_hardware " ,
" client_output_hardware " ,
" client_output_muted " ,
" client_default_channel " ,
" client_default_channel_password " ,
" client_server_password " ,
" client_meta_data " ,
" client_version_sign " ,
" client_key_offset " ,
" client_nickname_phonetic " ,
" client_default_token " ,
" client_badges=badges " ,
" client_badges " ,
" client_myteamspeak_id " ,
" client_integrations " ,
" client_active_integrations_info " ,
" client_browser_engine " ,
" client_away " ,
" client_away_message " ,
" hwid " ,
" myTeamspeakId " ,
" acTime " ,
" userPubKey " ,
" authSign " ,
" pubSign " ,
" pubSignCert "
} ;
for ( const auto & key : cmd [ 0 ] . keys ( ) ) {
if ( key = = " return_code " ) continue ;
bool parm_allowed = false ;
for ( const auto & _allowed_key : available_parameters ) {
if ( _allowed_key = = key ) {
parm_allowed = true ;
break ;
}
}
if ( ! parm_allowed ) {
debugMessage ( this - > getServerId ( ) , " {} Tried to insert a not allowed parameter within clientinit (Key: {}, Value: {}) " , CLIENT_STR_LOG_PREFIX , key , cmd [ key ] . string ( ) ) ;
continue ;
}
if ( key = = " myTeamspeakId " ) {
this - > properties ( ) [ property : : CLIENT_MYTEAMSPEAK_ID ] = cmd [ key ] . string ( ) ;
continue ;
} else if ( key = = " acTime " ) continue ;
else if ( key = = " userPubKey " ) continue ;
else if ( key = = " authSign " ) continue ;
else if ( key = = " pubSign " ) continue ;
else if ( key = = " pubSignCert " ) continue ;
else if ( key = = " client_version " | | key = = " client_platform " ) {
for ( auto & character : cmd [ key ] . string ( ) )
2020-01-25 17:42:37 -05:00
if ( ! isascii ( character ) ) {
logWarning ( this - > getServerId ( ) , " {} Tried to join within an invalid supplied '{}' ({}) " , CLIENT_STR_LOG_PREFIX , key , cmd [ key ] . string ( ) ) ;
return command_result { error : : client_hacked } ;
}
2020-07-30 05:50:31 -04:00
} else if ( key = = " client_talk_request_msg " ) {
2020-08-18 16:03:07 -04:00
if ( cmd [ " client_talk_request_msg " ] . string ( ) . length ( ) > ts : : config : : server : : limits : : talk_power_request_message_length )
2020-07-30 05:50:31 -04:00
return command_result { error : : parameter_invalid_size , " client_talk_request_msg " } ;
} else if ( key = = " client_away_message " ) {
2020-08-18 16:03:07 -04:00
if ( cmd [ " client_away_message " ] . string ( ) . length ( ) > ts : : config : : server : : limits : : afk_message_length )
2020-07-30 05:50:31 -04:00
return command_result { error : : parameter_invalid_size , " client_away_message " } ;
2020-09-24 16:57:10 -04:00
} else if ( key = = " client_nickname_phonetic " ) {
2020-09-24 18:49:20 -04:00
auto name = cmd [ " client_nickname_phonetic " ] . string ( ) ;
2020-09-24 16:57:10 -04:00
if ( count_characters ( name ) > 30 ) return command_result { error : : parameter_invalid , " client_nickname_phonetic " } ;
} else if ( key = = " client_nickname " ) {
auto name = cmd [ " client_nickname " ] . string ( ) ;
if ( count_characters ( name ) < 3 ) return command_result { error : : parameter_invalid , " client_nickname " } ;
if ( count_characters ( name ) > 30 ) return command_result { error : : parameter_invalid , " client_nickname " } ;
2020-01-23 20:57:58 -05:00
}
2020-04-08 07:01:41 -04:00
const auto & info = property : : find < property : : ClientProperties > ( key ) ;
if ( info . is_undefined ( ) ) {
2019-07-17 13:37:18 -04:00
logError ( this - > getServerId ( ) , " {} Tried to pass a unknown value {}. Please report this, if you're sure that this key should be known! " , CLIENT_STR_LOG_PREFIX , key ) ;
continue ;
2020-01-23 20:57:58 -05:00
//return {findError("parameter_invalid"), "Unknown property " + key};
}
2020-04-08 07:01:41 -04:00
if ( ! info . validate_input ( cmd [ key ] . as < string > ( ) ) )
2020-01-25 17:42:37 -05:00
return command_result { error : : parameter_invalid } ;
2020-01-23 20:57:58 -05:00
this - > properties ( ) [ info ] = cmd [ key ] . as < std : : string > ( ) ;
}
debugMessage ( this - > getServerId ( ) , " {} Got client init. (HWID: {}) " , CLIENT_STR_LOG_PREFIX , this - > getHardwareId ( ) ) ;
TIMING_STEP ( timings , " props apply " ) ;
2020-01-26 08:21:34 -05:00
auto permissions_list = this - > calculate_permissions ( {
2020-01-23 20:57:58 -05:00
permission : : b_virtualserver_join_ignore_password ,
permission : : b_client_ignore_bans ,
permission : : b_client_ignore_vpn ,
permission : : i_client_max_clones_uid ,
permission : : i_client_max_clones_ip ,
permission : : i_client_max_clones_hwid ,
permission : : b_client_enforce_valid_hwid ,
permission : : b_client_use_reserved_slot
2020-01-26 08:21:34 -05:00
} , 0 ) ;
auto permissions = map < permission : : PermissionType , permission : : v2 : : PermissionFlaggedValue > ( permissions_list . begin ( ) , permissions_list . end ( ) ) ;
2020-01-23 20:57:58 -05:00
TIMING_STEP ( timings , " perm calc 1 " ) ;
2020-01-26 08:21:34 -05:00
if ( geoloc : : provider_vpn & & ! permission : : v2 : : permission_granted ( 1 , permissions [ permission : : b_client_ignore_vpn ] ) ) {
2020-01-23 20:57:58 -05:00
auto provider = this - > isAddressV4 ( ) ? geoloc : : provider_vpn - > resolveInfoV4 ( this - > getPeerIp ( ) , true ) : geoloc : : provider_vpn - > resolveInfoV6 ( this - > getPeerIp ( ) , true ) ;
if ( provider )
2020-01-25 17:42:37 -05:00
return command_result { error : : server_connect_banned , strvar : : transform ( ts : : config : : messages : : kick_vpn , strvar : : StringValue { " provider.name " , provider - > name } , strvar : : StringValue { " provider.website " , provider - > side } ) } ;
2020-01-23 20:57:58 -05:00
}
2020-01-26 08:21:34 -05:00
if ( this - > getType ( ) = = ClientType : : CLIENT_TEAMSPEAK & & permission : : v2 : : permission_granted ( 1 , permissions [ permission : : b_client_enforce_valid_hwid ] ) ) {
2020-01-23 20:57:58 -05:00
auto hwid = this - > properties ( ) [ property : : CLIENT_HARDWARE_ID ] . as < string > ( ) ;
if (
! std : : regex_match ( hwid , regex_hwid_windows ) & &
! std : : regex_match ( hwid , regex_hwid_unix ) & &
! std : : regex_match ( hwid , regex_hwid_android ) & &
! std : : regex_match ( hwid , regex_hwid_ios )
) {
2020-01-25 17:42:37 -05:00
return command_result { error : : parameter_invalid , config : : messages : : kick_invalid_hardware_id } ;
2020-01-23 20:57:58 -05:00
}
}
TIMING_STEP ( timings , " valid hw ip " ) ;
2020-01-26 08:21:34 -05:00
if ( ! permission : : v2 : : permission_granted ( 1 , permissions [ permission : : b_virtualserver_join_ignore_password ] ) )
2020-01-25 17:42:37 -05:00
if ( ! this - > server - > verifyServerPassword ( cmd [ " client_server_password " ] . string ( ) , true ) )
return command_result { error : : server_invalid_password } ;
2020-01-23 20:57:58 -05:00
2020-04-23 09:36:58 -04:00
if ( ! config : : server : : clients : : ignore_max_clone_permissions ) {
size_t clones_uid = 0 ;
size_t clones_ip = 0 ;
size_t clones_hwid = 0 ;
auto _own_hwid = this - > getHardwareId ( ) ;
this - > server - > forEachClient ( [ & ] ( const shared_ptr < ConnectedClient > & client ) {
if ( client - > getExternalType ( ) ! = CLIENT_TEAMSPEAK ) return ;
if ( client - > getUid ( ) = = this - > getUid ( ) )
clones_uid + + ;
if ( client - > getPeerIp ( ) = = this - > getPeerIp ( ) )
clones_ip + + ;
if ( ! _own_hwid . empty ( ) & & client - > getHardwareId ( ) = = _own_hwid )
clones_hwid + + ;
} ) ;
if ( clones_uid > 0 & & permissions [ permission : : i_client_max_clones_uid ] . has_value & & ! permission : : v2 : : permission_granted ( clones_uid , permissions [ permission : : i_client_max_clones_uid ] ) ) {
logMessage ( this - > getServerId ( ) , " {} Disconnecting because there are already {} uid clones connected. (Allowed: {}) " , CLIENT_STR_LOG_PREFIX , clones_uid , permissions [ permission : : i_client_max_clones_uid ] ) ;
return command_result { error : : client_too_many_clones_connected , " too many clones connected (uid) " } ;
}
2020-01-23 20:57:58 -05:00
2020-04-23 09:36:58 -04:00
if ( clones_ip > 0 & & permissions [ permission : : i_client_max_clones_ip ] . has_value & & ! permission : : v2 : : permission_granted ( clones_ip , permissions [ permission : : i_client_max_clones_ip ] ) ) {
logMessage ( this - > getServerId ( ) , " {} Disconnecting because there are already {} ip clones connected. (Allowed: {}) " , CLIENT_STR_LOG_PREFIX , clones_ip , permissions [ permission : : i_client_max_clones_ip ] ) ;
return command_result { error : : client_too_many_clones_connected , " too many clones connected (ip) " } ;
}
2020-01-23 20:57:58 -05:00
2020-04-23 09:36:58 -04:00
if ( clones_hwid > 0 & & permissions [ permission : : i_client_max_clones_hwid ] . has_value & & ! permission : : v2 : : permission_granted ( clones_hwid , permissions [ permission : : i_client_max_clones_hwid ] ) ) {
logMessage ( this - > getServerId ( ) , " {} Disconnecting because there are already {} hwid clones connected. (Allowed: {}) " , CLIENT_STR_LOG_PREFIX , clones_hwid , permissions [ permission : : i_client_max_clones_hwid ] ) ;
return command_result { error : : client_too_many_clones_connected , " too many clones connected (hwid) " } ;
}
TIMING_STEP ( timings , " max clones " ) ;
2020-01-23 20:57:58 -05:00
}
auto banEntry = this - > resolveActiveBan ( this - > getPeerIp ( ) ) ;
if ( banEntry ) {
logMessage ( this - > getServerId ( ) , " {} Disconnecting while init because of ban record. Record id {} at server {} " ,
CLIENT_STR_LOG_PREFIX ,
banEntry - > banId ,
banEntry - > serverId ) ;
serverInstance - > banManager ( ) - > trigger_ban ( banEntry , this - > getServerId ( ) , this - > getUid ( ) , this - > getHardwareId ( ) , this - > getDisplayName ( ) , this - > getPeerIp ( ) ) ;
string fullReason = string ( ) + " You are banned " + ( banEntry - > serverId = = 0 ? " globally " : " from this server " ) + " . Reason: \" " + banEntry - > reason + " \" . Ban expires " ;
string time ;
if ( banEntry - > until . time_since_epoch ( ) . count ( ) ! = 0 ) {
time + = " in " ;
auto seconds = chrono : : ceil < chrono : : seconds > ( banEntry - > until - chrono : : system_clock : : now ( ) ) . count ( ) ;
tm p { } ;
memset ( & p , 0 , sizeof ( p ) ) ;
while ( seconds > = 365 * 24 * 60 * 60 ) {
p . tm_year + + ;
seconds - = 365 * 24 * 60 * 60 ;
}
while ( seconds > = 24 * 60 * 60 ) {
p . tm_yday + + ;
seconds - = 24 * 60 * 60 ;
}
while ( seconds > = 60 * 60 ) {
p . tm_hour + + ;
seconds - = 60 * 60 ;
}
while ( seconds > = 60 ) {
p . tm_min + + ;
seconds - = 60 ;
}
p . tm_sec = ( int ) seconds ;
if ( p . tm_year > 0 )
time + = to_string ( p . tm_year ) + " years, " ;
if ( p . tm_yday > 0 )
time + = to_string ( p . tm_yday ) + " days, " ;
if ( p . tm_hour > 0 )
time + = to_string ( p . tm_hour ) + " hours, " ;
if ( p . tm_min > 0 )
time + = to_string ( p . tm_min ) + " minutes, " ;
if ( p . tm_sec > 0 )
time + = to_string ( p . tm_sec ) + " seconds, " ;
if ( time . empty ( ) ) time = " now, " ;
time = time . substr ( 0 , time . length ( ) - 2 ) ;
} else time = " never " ;
fullReason + = time + " ! " ;
2020-01-25 17:42:37 -05:00
return command_result { error : : server_connect_banned , fullReason } ;
2020-01-23 20:57:58 -05:00
}
TIMING_STEP ( timings , " ban resolve " ) ;
size_t count = 0 ;
{
for ( const auto & cl : this - > server - > getClients ( ) )
if ( ( cl - > getType ( ) = = CLIENT_TEAMSPEAK | | cl - > getType ( ) = = CLIENT_WEB | | cl - > getType ( ) = = CLIENT_TEASPEAK | | cl - > getType ( ) = = CLIENT_MUSIC ) )
if ( cl - > connectionState ( ) < = ConnectionState : : CONNECTED & & cl - > connectionState ( ) > = ConnectionState : : INIT_HIGH )
count + + ;
}
auto maxClients = this - > server - > properties ( ) [ property : : VIRTUALSERVER_MAXCLIENTS ] . as < size_t > ( ) ;
auto reserved = this - > server - > properties ( ) [ property : : VIRTUALSERVER_RESERVED_SLOTS ] . as < size_t > ( ) ;
2020-01-26 08:21:34 -05:00
bool allowReserved = permission : : v2 : : permission_granted ( 1 , permissions [ permission : : b_client_use_reserved_slot ] ) ;
2020-01-23 20:57:58 -05:00
if ( reserved > maxClients ) {
2020-01-25 17:42:37 -05:00
if ( ! allowReserved )
return command_result { error : : server_maxclients_reached } ;
2020-01-23 20:57:58 -05:00
} else if ( maxClients - ( allowReserved ? 0 : reserved ) < = count )
2020-01-25 17:42:37 -05:00
return command_result { error : : server_maxclients_reached } ;
2020-01-23 20:57:58 -05:00
TIMING_STEP ( timings , " max clients " ) ;
auto old_last_connected = this - > properties ( ) [ property : : CLIENT_LASTCONNECTED ] . as < int64_t > ( ) ;
this - > properties ( ) [ property : : CONNECTION_CLIENT_IP ] = this - > getLoggingPeerIp ( ) ;
this - > properties ( ) [ property : : CLIENT_LASTCONNECTED ] = std : : chrono : : duration_cast < std : : chrono : : seconds > ( std : : chrono : : system_clock : : now ( ) . time_since_epoch ( ) ) . count ( ) ;
this - > properties ( ) [ property : : CLIENT_TOTALCONNECTIONS ] + + ;
{
auto time_point = system_clock : : time_point ( ) + seconds ( old_last_connected ) ;
if ( time_point < build : : version ( ) - > timestamp ) {
logMessage ( this - > getServerId ( ) , " {} Client may cached a old permission list (Server is newer than the client's last join) " , CLIENT_STR_LOG_PREFIX ) ;
TIMING_STEP ( timings , " pre dummy p " ) ;
Command _dummy ( " dummy_permissionslist " ) ;
this - > handleCommandPermissionList ( _dummy ) ;
TIMING_STEP ( timings , " pst dummy p " ) ;
}
}
this - > postCommandHandler . emplace_back ( [ & ] ( ) {
auto self = dynamic_pointer_cast < SpeakingClient > ( _this . lock ( ) ) ;
std : : thread ( [ self ] ( ) {
if ( self - > state ! = ConnectionState : : INIT_HIGH ) return ;
try {
self - > processJoin ( ) ;
} catch ( std : : exception & ex ) {
logError ( self - > getServerId ( ) , " Failed to proceed client join for {}. Got exception with message {} " , CLIENT_STR_LOG_PREFIX_ ( self ) , ex . what ( ) ) ;
2020-02-01 08:32:16 -05:00
self - > close_connection ( chrono : : system_clock : : now ( ) + chrono : : seconds { 5 } ) ;
2020-01-23 20:57:58 -05:00
}
} ) . detach ( ) ;
} ) ;
debugMessage ( this - > getServerId ( ) , " {} Client init timings: {} " , CLIENT_STR_LOG_PREFIX , TIMING_FINISH ( timings ) ) ;
2020-01-25 17:42:37 -05:00
return command_result { error : : ok } ;
2019-07-17 13:37:18 -04:00
}
/* must be triggered while helding an execute lock */
//Note: Client permissions are may not really
void SpeakingClient : : processJoin ( ) {
2020-01-23 20:57:58 -05:00
TIMING_START ( timings ) ;
auto ref_server = this - > server ;
this - > resetIdleTime ( ) ;
threads : : MutexLock lock ( this - > command_lock ) ; //Don't process any commands!
if ( this - > state ! = ConnectionState : : INIT_HIGH ) {
logError ( this - > getServerId ( ) , " {} Invalid processJoin() connection state! " , CLIENT_STR_LOG_PREFIX ) ;
return ;
}
TIMING_STEP ( timings , " setup " ) ;
ref_server - > registerClient ( _this . lock ( ) ) ;
2020-11-07 07:17:51 -05:00
if ( this - > rtc_client_id ) {
/* in case of client reconnect */
this - > server - > rtc_server ( ) . destroy_client ( this - > rtc_client_id ) ;
}
if ( this - > getType ( ) = = ClientType : : CLIENT_TEAMSPEAK ) {
this - > rtc_client_id = this - > server - > rtc_server ( ) . create_native_client ( dynamic_pointer_cast < SpeakingClient > ( this - > ref ( ) ) ) ;
} else if ( this - > getType ( ) = = ClientType : : CLIENT_TEASPEAK ) {
/* TODO: Will be a RTP client later on, just without audio */
this - > rtc_client_id = this - > server - > rtc_server ( ) . create_native_client ( dynamic_pointer_cast < SpeakingClient > ( this - > ref ( ) ) ) ;
} else if ( this - > getType ( ) = = ClientType : : CLIENT_WEB ) {
this - > rtc_client_id = this - > server - > rtc_server ( ) . create_rtp_client ( dynamic_pointer_cast < SpeakingClient > ( this - > ref ( ) ) ) ;
}
2020-01-23 20:57:58 -05:00
TIMING_STEP ( timings , " server reg " ) ;
ref_server - > getGroupManager ( ) - > cleanupAssignments ( this - > getClientDatabaseId ( ) ) ;
TIMING_STEP ( timings , " grp cleanup " ) ;
ref_server - > getGroupManager ( ) - > update_server_group_property ( _this . lock ( ) , true , nullptr ) ;
TIMING_STEP ( timings , " grp apply " ) ;
this - > properties ( ) [ property : : CLIENT_COUNTRY ] = config : : geo : : countryFlag ;
if ( geoloc : : provider ) {
auto loc = this - > isAddressV4 ( ) ? geoloc : : provider - > resolveInfoV4 ( this - > getPeerIp ( ) , false ) : geoloc : : provider - > resolveInfoV6 ( this - > getPeerIp ( ) , false ) ;
if ( loc ) {
debugMessage ( this - > getServerId ( ) , " Client " + this - > getDisplayName ( ) + " | " + this - > getLoggingPeerIp ( ) + " comes from " + loc - > name + " | " + loc - > identifier ) ;
this - > properties ( ) [ property : : CLIENT_COUNTRY ] = loc - > identifier ;
} else {
logError ( ref_server ? ref_server - > getServerId ( ) : 0 , " Could not resolve country for ip " + this - > getLoggingPeerIp ( ) + " | " + this - > getDisplayName ( ) ) ;
}
}
//this->updateChannelClientProperties(false); /* will be already updated via assignChannel */
if ( ref_server - > properties ( ) [ property : : VIRTUALSERVER_HOSTMESSAGE_MODE ] . as < int > ( ) = = 3 & & ! ref_server - > properties ( ) [ property : : VIRTUALSERVER_HOSTMESSAGE ] . as < string > ( ) . empty ( ) ) {
auto weak = this - > _this ;
threads : : Thread ( [ weak ] ( ) {
threads : : self : : sleep_for ( milliseconds ( 2000 ) ) ;
auto client = weak . lock ( ) ;
if ( ! client | | ! client - > server ) return ;
client - > disconnect ( client - > server - > properties ( ) [ property : : VIRTUALSERVER_HOSTMESSAGE ] . as < string > ( ) ) ;
} ) . detach ( ) ;
}
TIMING_STEP ( timings , " ip 2 loc as " ) ; //IP to location assignment
this - > sendServerInit ( ) ;
debugMessage ( this - > getServerId ( ) , " Client id: " + to_string ( this - > getClientId ( ) ) ) ;
TIMING_STEP ( timings , " notify sini " ) ;
if ( ! ref_server - > assignDefaultChannel ( this - > ref ( ) , false ) ) {
2020-01-25 17:42:37 -05:00
auto result = command_result { error : : vs_critical , " Could not assign default channel! " } ;
this - > notifyError ( result ) ;
2020-04-28 12:27:49 -04:00
result . release_data ( ) ;
2020-02-01 08:32:16 -05:00
this - > close_connection ( system_clock : : now ( ) + seconds ( 1 ) ) ;
2020-01-23 20:57:58 -05:00
return ;
}
TIMING_STEP ( timings , " assign chan " ) ;
this - > sendChannelList ( true ) ;
this - > state = ConnectionState : : CONNECTED ;
TIMING_STEP ( timings , " send chan t " ) ;
/* trick the join method */
auto channel = this - > currentChannel ;
this - > currentChannel = nullptr ;
{
/* enforce an update of these properties */
this - > properties ( ) [ property : : CLIENT_CHANNEL_GROUP_INHERITED_CHANNEL_ID ] = " 0 " ;
this - > properties ( ) [ property : : CLIENT_CHANNEL_GROUP_ID ] = " 0 " ;
this - > properties ( ) [ property : : CLIENT_TALK_POWER ] = " 0 " ;
unique_lock server_channel_lock ( this - > server - > channel_tree_lock ) ;
this - > server - > client_move ( this - > ref ( ) , channel , nullptr , " " , ViewReasonId : : VREASON_USER_ACTION , false , server_channel_lock ) ;
2020-04-10 17:29:51 -04:00
if ( this - > getType ( ) ! = ClientType : : CLIENT_TEAMSPEAK ) this - > subscribeChannel ( { this - > currentChannel } , false , true ) ; /* su "improve" the TS3 clients join speed we send the channel clients a bit later, when the TS3 client gets his own client variables */
2020-01-23 20:57:58 -05:00
}
TIMING_STEP ( timings , " join move " ) ;
this - > properties ( ) - > triggerAllModified ( ) ;
this - > notifyServerGroupList ( ) ;
this - > notifyChannelGroupList ( ) ;
TIMING_STEP ( timings , " notify grou " ) ;
logMessage ( this - > getServerId ( ) , " Voice client {}/{} ({}) from {} joined. " ,
this - > getClientDatabaseId ( ) ,
this - > getUid ( ) ,
this - > getDisplayName ( ) ,
this - > getLoggingPeerIp ( ) + " : " + to_string ( this - > getPeerPort ( ) )
) ;
this - > connectTimestamp = chrono : : system_clock : : now ( ) ;
this - > idleTimestamp = chrono : : system_clock : : now ( ) ;
2020-03-20 12:58:58 -04:00
TIMING_STEP ( timings , " welcome msg " ) ;
{
std : : string message { } ;
config : : server : : clients : : WelcomeMessageType type { config : : server : : clients : : WELCOME_MESSAGE_TYPE_NONE } ;
if ( this - > getType ( ) = = ClientType : : CLIENT_TEASPEAK ) {
message = config : : server : : clients : : extra_welcome_message_teaspeak ;
type = config : : server : : clients : : extra_welcome_message_type_teaspeak ;
} else if ( this - > getType ( ) = = ClientType : : CLIENT_TEAMSPEAK ) {
message = config : : server : : clients : : extra_welcome_message_teamspeak ;
type = config : : server : : clients : : extra_welcome_message_type_teamspeak ;
} else if ( this - > getType ( ) = = ClientType : : CLIENT_WEB ) {
message = config : : server : : clients : : extra_welcome_message_teaweb ;
type = config : : server : : clients : : extra_welcome_message_type_teaweb ;
}
if ( type = = config : : server : : clients : : WELCOME_MESSAGE_TYPE_POKE ) {
this - > notifyClientPoke ( this - > server - > serverRoot , message ) ;
} else if ( type = = config : : server : : clients : : WELCOME_MESSAGE_TYPE_CHAT ) {
this - > notifyTextMessage ( ChatMessageMode : : TEXTMODE_SERVER , this - > server - > serverRoot , 0 , 0 , std : : chrono : : system_clock : : now ( ) , message ) ;
}
}
2020-01-23 20:57:58 -05:00
debugMessage ( this - > getServerId ( ) , " {} Client join timings: {} " , CLIENT_STR_LOG_PREFIX , TIMING_FINISH ( timings ) ) ;
2020-06-28 08:01:14 -04:00
serverInstance - > action_logger ( ) - > client_channel_logger . log_client_join ( this - > getServerId ( ) , this - > ref ( ) , this - > getChannelId ( ) , this - > currentChannel - > name ( ) ) ;
2019-07-17 13:37:18 -04:00
}
void SpeakingClient : : processLeave ( ) {
2020-01-23 20:57:58 -05:00
auto ownLock = _this . lock ( ) ;
auto server = this - > getServer ( ) ;
2020-06-28 08:01:14 -04:00
auto channel = this - > currentChannel ;
2020-01-23 20:57:58 -05:00
if ( server ) {
logMessage ( this - > getServerId ( ) , " Voice client {}/{} ({}) from {} left. " , this - > getClientDatabaseId ( ) , this - > getUid ( ) , this - > getDisplayName ( ) , this - > getLoggingPeerIp ( ) + " : " + to_string ( this - > getPeerPort ( ) ) ) ;
{
unique_lock server_channel_lock ( this - > server - > channel_tree_lock ) ;
server - > unregisterClient ( ownLock , " disconnected " , server_channel_lock ) ; /* already moves client to void if needed */
}
server - > groups - > disableCache ( ownLock - > getClientDatabaseId ( ) ) ;
2020-11-07 07:17:51 -05:00
server - > music_manager_ - > cleanup_client_bots ( this - > getClientDatabaseId ( ) ) ;
2020-01-23 20:57:58 -05:00
//ref_server = nullptr; Removed caused nullptr exceptions
}
{ //Delete own viewing clients
/*
* No need , are only weak references !
threads : : MutexLock l ( this - > viewLock ) ;
this - > visibleClients . clear ( ) ;
this - > mutedClients . clear ( ) ;
*/
}
2019-07-17 13:37:18 -04:00
}
void SpeakingClient : : triggerVoiceEnd ( ) {
2020-01-23 20:57:58 -05:00
this - > properties ( ) [ property : : CLIENT_FLAG_TALKING ] = false ;
2019-07-17 13:37:18 -04:00
}
2020-11-07 07:17:51 -05:00
void SpeakingClient : : updateSpeak ( bool only_update , const std : : chrono : : system_clock : : time_point & now ) {
std : : lock_guard speak_lock { this - > speak_mutex } ;
2020-01-23 20:57:58 -05:00
if ( this - > speak_last_packet + this - > speak_accuracy < now ) {
if ( this - > speak_last_packet > this - > speak_begin ) {
2020-11-07 07:17:51 -05:00
if ( ! this - > properties ( ) [ property : : CLIENT_FLAG_TALKING ] . as < bool > ( ) ) {
2020-01-23 20:57:58 -05:00
this - > properties ( ) [ property : : CLIENT_FLAG_TALKING ] = true ;
2020-11-07 07:17:51 -05:00
}
2020-01-23 20:57:58 -05:00
this - > speak_time + = duration_cast < milliseconds > ( this - > speak_last_packet - this - > speak_begin ) ;
} else {
2020-11-07 07:17:51 -05:00
if ( this - > properties ( ) [ property : : CLIENT_FLAG_TALKING ] . as < bool > ( ) ) {
2020-01-23 20:57:58 -05:00
this - > properties ( ) [ property : : CLIENT_FLAG_TALKING ] = false ;
2020-11-07 07:17:51 -05:00
}
2020-01-23 20:57:58 -05:00
}
this - > speak_begin = now ;
this - > speak_last_packet = now ;
}
2020-11-07 07:17:51 -05:00
if ( ! only_update ) {
2020-01-23 20:57:58 -05:00
this - > speak_last_packet = now ;
2020-11-07 07:17:51 -05:00
}
2019-07-17 13:37:18 -04:00
}
void SpeakingClient : : tick ( const std : : chrono : : system_clock : : time_point & time ) {
2020-01-23 20:57:58 -05:00
ConnectedClient : : tick ( time ) ;
ALARM_TIMER ( A1 , " SpeakingClient::tick " , milliseconds ( 2 ) ) ;
this - > updateSpeak ( true , time ) ;
if ( this - > state = = ConnectionState : : CONNECTED ) {
if ( this - > max_idle_time . has_value ) {
auto max_idle = this - > max_idle_time . value ;
if ( max_idle > 0 & & this - > idleTimestamp . time_since_epoch ( ) . count ( ) > 0 & & duration_cast < seconds > ( time - this - > idleTimestamp ) . count ( ) > max_idle ) {
this - > server - > notify_client_kick ( this - > ref ( ) , this - > server - > getServerRoot ( ) , ts : : config : : messages : : idle_time_exceeded , nullptr ) ;
2020-02-01 08:32:16 -05:00
this - > close_connection ( system_clock : : now ( ) + seconds ( 1 ) ) ;
2020-01-23 20:57:58 -05:00
}
}
}
2019-07-17 13:37:18 -04:00
}
void SpeakingClient : : updateChannelClientProperties ( bool channel_lock , bool notify ) {
2020-01-23 20:57:58 -05:00
ConnectedClient : : updateChannelClientProperties ( channel_lock , notify ) ;
2020-01-26 08:21:34 -05:00
this - > max_idle_time = this - > calculate_permission ( permission : : i_client_max_idletime , this - > currentChannel ? this - > currentChannel - > channelId ( ) : 0 ) ;
2019-07-17 13:37:18 -04:00
}
2020-01-25 17:42:37 -05:00
command_result SpeakingClient : : handleCommand ( Command & command ) {
2020-01-23 20:57:58 -05:00
if ( this - > connectionState ( ) = = ConnectionState : : INIT_HIGH ) {
if ( this - > handshake . state = = HandshakeState : : BEGIN | | this - > handshake . state = = HandshakeState : : IDENTITY_PROOF ) {
2020-01-25 17:42:37 -05:00
command_result result ;
2020-11-07 07:17:51 -05:00
if ( command . command ( ) = = " handshakebegin " ) {
2020-05-07 15:28:15 -04:00
result . reset ( this - > handleCommandHandshakeBegin ( command ) ) ;
2020-11-07 07:17:51 -05:00
} else if ( command . command ( ) = = " handshakeindentityproof " ) {
2020-05-07 15:28:15 -04:00
result . reset ( this - > handleCommandHandshakeIdentityProof ( command ) ) ;
2020-11-07 07:17:51 -05:00
} else {
2020-05-07 15:28:15 -04:00
result . reset ( command_result { error : : client_not_logged_in } ) ;
2020-11-07 07:17:51 -05:00
}
2020-01-23 20:57:58 -05:00
2020-11-07 07:17:51 -05:00
if ( result . has_error ( ) ) {
2020-01-25 17:42:37 -05:00
this - > postCommandHandler . push_back ( [ & ] {
2020-02-01 08:32:16 -05:00
this - > close_connection ( system_clock : : now ( ) + seconds ( 1 ) ) ;
2020-01-25 17:42:37 -05:00
} ) ;
2020-11-07 07:17:51 -05:00
}
2020-01-23 20:57:58 -05:00
return result ;
}
2020-11-07 07:17:51 -05:00
} else if ( this - > connectionState ( ) = = ConnectionState : : CONNECTED ) {
if ( command . command ( ) = = " rtcsessiondescribe " ) {
return this - > handleCommandRtcSessionDescribe ( command ) ;
} else if ( command . command ( ) = = " rtcicecandidate " ) {
return this - > handleCommandRtcIceCandidate ( command ) ;
} else if ( command . command ( ) = = " rtcbroadcast " ) {
return this - > handleCommandRtcBroadcast ( command ) ;
} else if ( command . command ( ) = = " rtcsessionreset " ) {
return this - > handleCommandRtcSessionReset ( command ) ;
}
2020-01-23 20:57:58 -05:00
}
return ConnectedClient : : handleCommand ( command ) ;
2019-07-17 13:37:18 -04:00
}
2020-11-07 07:17:51 -05:00
command_result SpeakingClient : : handleCommandRtcSessionDescribe ( Command & command ) {
CMD_REQ_SERVER ;
CMD_CHK_AND_INC_FLOOD_POINTS ( 15 ) ;
uint32_t mode ;
if ( command [ " mode " ] . string ( ) = = " offer " ) {
mode = 1 ;
} else if ( command [ " mode " ] . string ( ) = = " answer " ) {
mode = 2 ;
} else {
return command_result { error : : parameter_invalid , " mode " } ;
}
std : : string error { } ;
if ( ! this - > server - > rtc_server ( ) . apply_remote_description ( error , this - > rtc_client_id , mode , command [ " sdp " ] ) ) {
return command_result { error : : vs_critical , error } ;
}
if ( mode = = 1 ) {
std : : string result { } ;
if ( ! this - > server - > rtc_server ( ) . generate_local_description ( this - > rtc_client_id , result ) ) {
return command_result { error : : vs_critical , result } ;
} else {
ts : : command_builder notify { " notifyrtcsessiondescription " } ;
notify . put_unchecked ( 0 , " mode " , " answer " ) ;
notify . put_unchecked ( 0 , " sdp " , result ) ;
this - > sendCommand ( notify ) ;
}
}
return command_result { error : : ok } ;
}
command_result SpeakingClient : : handleCommandRtcSessionReset ( Command & command ) {
CMD_REQ_SERVER ;
CMD_CHK_AND_INC_FLOOD_POINTS ( 15 ) ;
this - > server - > rtc_server ( ) . reset_rtp_session ( this - > rtc_client_id ) ;
return command_result { error : : ok } ;
}
command_result SpeakingClient : : handleCommandRtcIceCandidate ( Command & command ) {
CMD_REQ_SERVER ;
std : : string error ;
if ( command [ 0 ] . has ( " candidate " ) ) {
auto candidate = command [ " candidate " ] . string ( ) ;
if ( ! this - > server - > rtc_server ( ) . add_ice_candidate ( error , this - > rtc_client_id , command [ " media_line " ] , candidate ) ) {
return command_result { error : : vs_critical , error } ;
}
} else {
this - > server - > rtc_server ( ) . ice_candidates_finished ( this - > rtc_client_id ) ;
}
return command_result { error : : ok } ;
}
command_result SpeakingClient : : handleCommandRtcBroadcast ( Command & command ) {
CMD_REQ_SERVER ;
CMD_CHK_AND_INC_FLOOD_POINTS ( 15 ) ;
/* TODO: Filter out duplicates */
std : : vector < std : : tuple < uint8_t , uint32_t > > broadcasts { } ;
broadcasts . reserve ( command . bulkCount ( ) ) ;
for ( size_t index { 0 } ; index < command . bulkCount ( ) ; index + + ) {
auto & bulk = command [ index ] ;
broadcasts . push_back ( std : : make_tuple ( bulk [ " type " ] , bulk . has ( " ssrc " ) ? bulk [ " ssrc " ] . as < uint32_t > ( ) : ( uint32_t ) 0 ) ) ;
}
ts : : command_result_bulk result { } ;
for ( size_t index { 0 } ; index < command . bulkCount ( ) ; index + + ) {
auto broadcast_result = this - > server - > rtc_server ( ) . start_broadcast ( this - > rtc_client_id , std : : get < 0 > ( broadcasts [ index ] ) , std : : get < 1 > ( broadcasts [ index ] ) ) ;
switch ( broadcast_result ) {
case rtc : : BroadcastStartResult : : Success :
result . emplace_result ( error : : ok ) ;
break ;
case rtc : : BroadcastStartResult : : InvalidBroadcastType :
result . emplace_result ( error : : parameter_invalid , " type " ) ;
break ;
case rtc : : BroadcastStartResult : : InvalidStreamId :
result . emplace_result ( error : : rtc_missing_target_channel ) ;
break ;
case rtc : : BroadcastStartResult : : ClientHasNoChannel :
result . emplace_result ( error : : vs_critical , " no channel " ) ;
break ;
case rtc : : BroadcastStartResult : : InvalidClient :
result . emplace_result ( error : : vs_critical , " invalid client " ) ;
break ;
case rtc : : BroadcastStartResult : : UnknownError :
default :
result . emplace_result ( error : : vs_critical , " unknown error " ) ;
}
}
return ts : : command_result { std : : move ( result ) } ;
}