2019-07-17 19:37:18 +02:00
# include <algorithm>
# include <utility>
# include <log/LogUtils.h>
# include <misc/std_unique_ptr.h>
# include <Properties.h>
2020-01-27 02:21:39 +01:00
# include "VirtualServerManager.h"
2019-07-17 19:37:18 +02:00
# include "src/server/VoiceServer.h"
# include "InstanceHandler.h"
# include "InstanceHandler.h"
2020-01-26 14:21:34 +01:00
//TODO: When using the new command builder make sure you're using a std::deque as the underlying bulk type!
2019-07-17 19:37:18 +02:00
using namespace std ;
using namespace ts ;
using namespace ts : : server ;
# define PREFIX string("[SNAPSHOT] ")
inline std : : string avArguments ( const ts : : Command & cmd , int index ) {
2020-01-24 02:57:58 +01:00
stringstream res ;
for ( const auto & key : cmd [ index ] . keys ( ) )
res < < key < < " = " < < cmd [ index ] [ key ] . as < std : : string > ( ) < < " " ;
return res . str ( ) ;
2019-07-17 19:37:18 +02:00
}
//TeamSpeak: permid=i_channel_needed_permission_modify_power permvalue=75 permskip=0 permnegated=0
//TeaSpeak: perm=i_channel_needed_permission_modify_power value=75 granted=75 flag_skip=0 flag_negated=0
struct SnapshotPermissionEntry {
2020-01-24 02:57:58 +01:00
std : : shared_ptr < permission : : PermissionTypeEntry > type = permission : : PermissionTypeEntry : : unknown ;
permission : : PermissionValue value = permNotGranted ;
permission : : PermissionValue grant = permNotGranted ;
bool negated = false ;
bool skipped = false ;
SnapshotPermissionEntry ( const std : : shared_ptr < permission : : PermissionTypeEntry > & type , permission : : PermissionValue value , permission : : PermissionValue grant ) : type ( std : : move ( type ) ) , value ( value ) , grant ( grant ) { }
SnapshotPermissionEntry ( const std : : shared_ptr < permission : : PermissionTypeEntry > & type , permission : : PermissionValue value , permission : : PermissionValue grant , bool negated , bool skipped ) : type ( type ) , value ( value ) , grant ( grant ) , negated ( negated ) , skipped ( skipped ) { }
SnapshotPermissionEntry ( ) = default ;
static deque < std : : unique_ptr < SnapshotPermissionEntry > > parse ( const Command & cmd , int & index , permission : : teamspeak : : GroupType type , int version , const std : : vector < std : : string > & ends , bool ignore_first = false ) {
deque < std : : unique_ptr < SnapshotPermissionEntry > > result { } ;
while ( true ) {
bool end_reached = false ;
for ( const auto & e : ends )
if ( cmd [ index ] . has ( e ) ) { end_reached = true ; break ; }
if ( end_reached | | cmd . bulkCount ( ) < index ) {
if ( ignore_first ) ignore_first = false ;
else break ;
}
if ( version = = 0 ) {
auto permission_name = cmd [ index ] [ " permid " ] . string ( ) ;
for ( const auto & mapped : permission : : teamspeak : : map_key ( permission_name , type ) ) {
auto permission = permission : : resolvePermissionData ( mapped ) ;
if ( permission - > type = = permission : : unknown ) {
logError ( 0 , " Failed to parse permission {}. Original: {} " , mapped , permission_name ) ;
index + + ;
continue ;
}
bool found = false ;
for ( auto & e : result )
if ( e - > type - > type = = permission - > type ) {
found = true ;
if ( permission - > grantName ( ) = = mapped )
e - > grant = cmd [ index ] [ " permvalue " ] ;
else {
e - > value = cmd [ index ] [ " permvalue " ] ;
e - > negated = cmd [ index ] [ " permnegated " ] ;
e - > skipped = cmd [ index ] [ " permskip " ] ;
}
}
if ( ! found ) {
auto entry = make_unique < SnapshotPermissionEntry > ( ) ;
entry - > type = permission ;
if ( permission - > grantName ( ) = = mapped )
entry - > grant = cmd [ index ] [ " permvalue " ] ;
else {
entry - > value = cmd [ index ] [ " permvalue " ] ;
entry - > negated = cmd [ index ] [ " permnegated " ] ;
entry - > skipped = cmd [ index ] [ " permskip " ] ;
}
result . push_back ( std : : move ( entry ) ) ;
}
}
} else if ( version > = 1 ) {
auto permission_name = cmd [ index ] [ " perm " ] . string ( ) ;
auto permission = permission : : resolvePermissionData ( permission_name ) ;
if ( permission - > type = = permission : : unknown ) {
logError ( 0 , " Failed to parse permission {} " , permission_name ) ;
index + + ;
continue ;
}
auto entry = make_unique < SnapshotPermissionEntry > ( ) ;
entry - > type = permission ;
entry - > value = cmd [ index ] [ " value " ] ;
entry - > grant = cmd [ index ] [ " grant " ] ;
entry - > skipped = cmd [ index ] [ " flag_skip " ] ;
entry - > negated = cmd [ index ] [ " flag_negated " ] ;
result . push_back ( std : : move ( entry ) ) ;
} else {
logError ( 0 , " Failed to parse snapshot permission entry! Invalid version! " ) ;
index + + ;
continue ;
}
index + + ;
}
deque < std : : unique_ptr < SnapshotPermissionEntry > > addings { } ;
for ( const auto & perm : result ) {
if ( perm - > type - > type = = permission : : i_group_auto_update_type ) { //Migrate this type
for ( const auto & e : permission : : update : : migrate )
if ( e . type = = perm - > value ) {
auto _type = permission : : resolvePermissionData ( e . permission . name ) ;
if ( _type - > type = = permission : : unknown ) {
logError ( 0 , " Invalid update permission for update group {}. Permission name: {} " , e . type , e . permission . name ) ;
} else {
addings . push_back ( make_unique < SnapshotPermissionEntry > ( _type , e . permission . value , e . permission . granted , e . permission . negated , e . permission . skipped ) ) ;
}
}
}
}
for ( auto & a : addings )
result . push_back ( move ( a ) ) ;
return result ;
}
void write ( Command & cmd , int & index , permission : : teamspeak : : GroupType type , int version ) {
if ( version = = 0 ) {
if ( this - > value ! = permNotGranted ) {
for ( const auto & name : permission : : teamspeak : : unmap_key ( this - > type - > name , type ) ) {
cmd [ index ] [ " permid " ] = name ;
cmd [ index ] [ " permvalue " ] = this - > value ;
cmd [ index ] [ " permskip " ] = this - > skipped ;
cmd [ index ] [ " permnegated " ] = this - > negated ;
index + + ;
}
}
if ( this - > grant ! = permNotGranted ) {
for ( const auto & name : permission : : teamspeak : : unmap_key ( this - > type - > grantName ( ) , type ) ) {
cmd [ index ] [ " permid " ] = name ;
cmd [ index ] [ " permvalue " ] = this - > grant ;
cmd [ index ] [ " permskip " ] = false ;
cmd [ index ] [ " permnegated " ] = false ;
index + + ;
}
}
} else if ( version > = 1 ) {
cmd [ index ] [ " perm " ] = this - > type - > name ;
cmd [ index ] [ " value " ] = this - > value ;
cmd [ index ] [ " grant " ] = this - > grant ;
cmd [ index ] [ " flag_skip " ] = this - > skipped ;
cmd [ index ] [ " flag_negated " ] = this - > negated ;
index + + ;
} else {
logError ( 0 , " Could not write snapshot permission! Invalid version. ({}) " , version ) ;
}
}
2019-07-17 19:37:18 +02:00
} ;
2020-07-31 17:35:14 +02:00
#if 0
2020-01-27 02:21:39 +01:00
std : : shared_ptr < VirtualServer > VirtualServerManager : : createServerFromSnapshot ( shared_ptr < VirtualServer > old , std : : string host ,
uint16_t port , const ts : : Command & arguments ,
std : : string & error ) {
2020-01-24 02:57:58 +01:00
ServerId serverId = 0 ;
map < ClientDbId , map < ChannelId , ClientDbId > > channelGroupRelations ; //cid is the new cgid
map < GroupId , GroupId > channelGroupMapping ;
map < ClientDbId , vector < GroupId > > serverGroupRelations ; //gid is the new gid
map < GroupId , GroupId > serverGroupMapping ;
map < ClientDbId , ClientDbId > db_mapping_client_id ;
deque < pair < std : : string , threads : : Future < sql : : result > > > futures ;
bool sid_success = false ;
serverId = this - > next_available_server_id ( sid_success ) ;
if ( ! sid_success ) {
error = " failed to generate new sid " ;
return nullptr ;
}
ServerId log_server_id = old ? old - > getServerId ( ) : serverId ;
2020-02-16 12:04:23 +01:00
futures . emplace_back (
2020-01-24 02:57:58 +01:00
" server id register " ,
sql : : command ( this - > handle - > getSql ( ) , " INSERT INTO `servers` (`serverId`, `host`, `port`) VALUES (:sid, :host, :port) " , variable { " :sid " , serverId } , variable { " :host " , host } , variable { " :port " , port } ) . executeLater ( )
2020-02-16 12:04:23 +01:00
) ;
2020-01-24 02:57:58 +01:00
sql : : model sql_insert_channel ( this - > handle - > getSql ( ) , " INSERT INTO `channels` (`serverId`, `channelId`, `type`, `parentId`) VALUES (:sid, :id, :type, :pid) " , variable { " :sid " , serverId } ) ;
sql : : model sql_insert_group_assignment ( this - > handle - > getSql ( ) , " INSERT INTO `assignedGroups` (`serverId`, `cldbid`, `groupId`, `channelId`, `until`) VALUES (:sid, :cldbid, :gid, :chid, :until) " , variable { " :sid " , serverId } , variable { " :until " , 0 } ) ;
sql : : model sql_insert_permission ( this - > handle - > getSql ( ) , " INSERT INTO `permissions` (`serverId`, `type`, `id`, `channelId`, `permId`, `value`, `grant`, `flag_skip`, `flag_negate`) VALUES (:serverId, :type, :id, :chId, :permId, :value, :grant, :flag_skip, :flag_negate) " ,
variable { " :serverId " , serverId } ) ;
sql : : model sql_update_permission_grant ( this - > handle - > getSql ( ) , " UPDATE `permissions` SET `grant` = :grant WHERE `serverId` = :sid AND `type` = :type AND `id` = :id AND `channelId` = :chid AND `permId` = :key " , variable { " :sid " , serverId } ) ;
sql : : model sql_insert_property ( this - > handle - > getSql ( ) , " INSERT INTO `properties` (`serverId`, `type`, `id`, `key`, `value`) VALUES (:sid, :type, :id, :key, :value) " , variable { " :sid " , serverId } ) ;
sql : : model sql_insert_bot ( this - > handle - > getSql ( ) , " INSERT INTO `musicbots` (`serverId`, `botId`, `uniqueId`, `owner`) VALUES (:server_id, :bot_id, :unique_id, :owner) " , variable { " :server_id " , serverId } ) ;
sql : : model sql_insert_playlist ( this - > handle - > getSql ( ) , " INSERT INTO `playlists` (`serverId`, `playlist_id`) VALUES (:server_id, :playlist_id) " , variable { " :server_id " , serverId } ) ;
sql : : model sql_insert_playlist_song ( this - > handle - > getSql ( ) , " INSERT INTO `playlist_songs` (`serverId`, `playlist_id`, `song_id`, `order_id`, `invoker_dbid`, `url`, `url_loader`, `loaded`, `metadata`) VALUES (:server_id, :playlist_id, :song_id, :order_id, :invoker_dbid, :url, :url_loader, :loaded, :metadata) " , variable { " :server_id " , serverId } ) ;
int index = 0 ;
auto snapshot_version = arguments [ index ] . has ( " snapshot_version " ) ? arguments [ index + + ] [ " snapshot_version " ] : 0 ;
debugMessage ( 0 , " Got server snapshot with version {} " , snapshot_version ) ;
while ( true ) {
2020-04-08 13:01:41 +02:00
for ( const auto & key : arguments [ index ] . keys ( ) ) {
2020-01-24 02:57:58 +01:00
if ( key = = " end_virtualserver " ) continue ;
if ( key = = " begin_virtualserver " ) continue ;
if ( snapshot_version = = 0 ) {
if ( key = = " virtualserver_download_quota " | | key = = " virtualserver_upload_quota " | | key = = " virtualserver_max_download_total_bandwidth " | | key = = " virtualserver_max_upload_total_bandwidth " ) {
auto value = arguments [ index ] [ key ] . string ( ) ;
try {
arguments [ index ] [ key ] = to_string ( ( int64_t ) std : : stoull ( value ) ) ;
} catch ( const std : : exception & ex ) {
continue ;
}
}
}
debugMessage ( LOG_GENERAL , PREFIX + " Having server key {} = {} " , key , arguments [ index ] [ key ] . as < std : : string > ( ) ) ;
2020-02-16 12:04:23 +01:00
futures . emplace_back (
2020-01-24 02:57:58 +01:00
" server property import proprty= " + key ,
sql_insert_property . command ( ) . values (
variable { " :type " , property : : PropertyType : : PROP_TYPE_SERVER } ,
variable { " :id " , 0 } ,
variable { " :key " , key } ,
variable { " :value " , arguments [ index ] [ key ] . string ( ) }
) . executeLater ( )
2020-02-16 12:04:23 +01:00
) ;
2020-01-24 02:57:58 +01:00
}
if ( arguments [ index ] . has ( " end_virtualserver " ) ) break ;
index + + ;
}
//`serverId` INT NOT NULL, `playlist_id` INT, `song_id` INT, `order_id` INT, `invoker_dbid` INT, `url` TEXT, `url_loader` TEXT
/*
* sql : : command ( this - > handle - > getSql ( ) , " INSERT INTO `playlists` (`serverId`, `playlist_id`) VALUES (:server_id, :playlist_id) " ,
variable { " :server_id " , this - > ref_server ( ) - > getServerId ( ) } ,
variable { " :playlist_id " , playlist_id }
)
*/
index + + ;
for ( ; index < arguments . bulkCount ( ) ; index + + ) {
if ( arguments [ index ] . has ( " begin_channels " ) ) {
while ( true ) {
if ( arguments [ index ] . has ( " end_channels " ) ) break ;
auto channel_id = arguments [ index ] [ " channel_id " ] . string ( ) ;
2020-02-16 12:04:23 +01:00
futures . emplace_back (
2020-01-24 02:57:58 +01:00
" channel register cid= " + channel_id ,
sql_insert_channel . command ( ) . values ( variable { " :id " , channel_id } , variable { " :type " , - 1 } , variable { " :pid " , arguments [ index ] [ " channel_pid " ] . string ( ) } ) . executeLater ( )
2020-02-16 12:04:23 +01:00
) ; //TODO findout type!
2020-01-24 02:57:58 +01:00
for ( const auto & key : arguments [ index ] . keys ( ) ) {
if ( key = = " channel_id " ) continue ;
if ( key = = " channel_pid " ) continue ;
if ( key = = " channel_security_salt " ) continue ;
if ( key = = " channel_filepath " ) continue ; //TODO implement channel_filepath?
if ( key = = " begin_channels " ) continue ;
debugMessage ( log_server_id , PREFIX + " Having channel key " + key + " = " + arguments [ index ] [ key ] . as < std : : string > ( ) + " for channel " + arguments [ index ] [ " channel_id " ] . as < std : : string > ( ) ) ;
futures . push_back ( {
" channel prop register cid= " + channel_id + " property= " + key ,
sql_insert_property . command ( ) . values ( variable { " :sid " , serverId } ,
variable { " :type " , property : : PropertyType : : PROP_TYPE_CHANNEL } ,
variable { " :id " , channel_id } ,
variable { " :key " , key } ,
variable { " :value " , arguments [ index ] [ key ] . as < std : : string > ( ) }
) . executeLater ( )
2019-07-17 19:37:18 +02:00
} ) ;
2020-01-24 02:57:58 +01:00
}
index + + ;
}
} else if ( arguments [ index ] . has ( " begin_clients " ) ) {
while ( true ) {
if ( arguments [ index ] . has ( " end_clients " ) ) break ;
debugMessage ( log_server_id , PREFIX + " Create client {}/{} -> end: {} " , arguments [ index ] [ " client_id " ] . as < std : : string > ( ) , arguments [ index ] [ " client_nickname " ] . as < std : : string > ( ) , to_string ( arguments [ index ] . has ( " end_clients " ) ) ) ;
ClientDbId oldId = 0 ;
auto res = sql : : command ( this - > handle - > getSql ( ) , " SELECT `cldbid` FROM `clients` WHERE `clientUid` = :uid AND `serverId` = 0 " , variable { " :uid " , arguments [ index ] [ " client_unique_id " ] . as < std : : string > ( ) } ) . query ( [ ] ( ClientDbId * ptr , int , char * * v , char * * ) { * ptr = stoll ( v [ 0 ] ) ; return 0 ; } , & oldId ) ;
LOG_SQL_CMD ( res ) ;
if ( oldId = = 0 ) {
res = sql : : command ( this - > handle - > getSql ( ) , " SELECT `cldbid` FROM `clients` WHERE `serverId` = 0 ORDER BY `cldbid` DESC LIMIT 1 " ) . query ( [ ] ( ClientDbId * ptr , int length , char * * values , char * * names ) {
* ptr = static_cast < ClientDbId > ( stoll ( values [ 0 ] ) ) ;
return 0 ;
} , & oldId ) ;
LOG_SQL_CMD ( res ) ;
oldId + = 1 ;
//Create a new client
sql : : command ( this - > handle - > getSql ( ) , " INSERT INTO `clients` (`serverId`, `cldbid`, `clientUid`, `firstConnect`, `lastConnect`, `connections`, `lastName`) VALUES (:sid, :cldbid, :cluid, :firstConnect, :lastConnect, :connections, :name) " ,
variable { " :sid " , 0 } , variable { " :cldbid " , oldId } , variable { " :cluid " , arguments [ index ] [ " client_unique_id " ] . as < std : : string > ( ) } ,
variable { " :firstConnect " , arguments [ index ] [ " client_created " ] . as < std : : string > ( ) } ,
variable { " :lastConnect " , " 0 " } , variable { " :connections " , " 0 " } , variable { " :name " , arguments [ index ] [ " client_nickname " ] . as < std : : string > ( ) } ) . execute ( ) ; //TODO log error
}
db_mapping_client_id [ arguments [ index ] [ " client_id " ] . as < ClientDbId > ( ) ] = oldId ;
sql : : command ( this - > handle - > getSql ( ) , " INSERT INTO `clients` (`serverId`, `cldbid`, `clientUid`, `firstConnect`, `lastConnect`, `connections`, `lastName`) VALUES (:sid, :cldbid, :cluid, :firstConnect, :lastConnect, :connections, :name) " ,
variable { " :sid " , serverId } , variable { " :cldbid " , oldId } , variable { " :cluid " , arguments [ index ] [ " client_unique_id " ] . as < std : : string > ( ) } ,
variable { " :firstConnect " , arguments [ index ] [ " client_created " ] . as < std : : string > ( ) } ,
variable { " :lastConnect " , " 0 " } , variable { " :connections " , " 0 " } , variable { " :name " , arguments [ index ] [ " client_nickname " ] . as < std : : string > ( ) } ) . execute ( ) ; //TODO log error
//TODO here
index + + ;
}
} else if ( arguments [ index ] . has ( " begin_permissions " ) ) {
while ( true ) {
index + + ;
if ( arguments [ index ] . has ( " end_permissions " ) ) break ;
else if ( arguments [ index ] . has ( " server_groups " ) ) {
GroupId currentGroupId = 1 ;
while ( true ) {
if ( arguments [ index ] . has ( " end_groups " ) ) break ;
if ( ! arguments [ index ] . has ( " id " ) ) { //new group
logError ( 0 , " Invalid server group permission entry! Missing group id! " ) ;
index + + ;
continue ;
}
currentGroupId = static_cast < GroupId > ( GroupManager : : generateGroupId ( this - > handle - > getSql ( ) ) ) ;
debugMessage ( log_server_id , PREFIX + " Insert group: " + to_string ( currentGroupId ) + " - " + arguments [ index ] [ " name " ] . as < string > ( ) ) ;
serverGroupMapping [ arguments [ index ] [ " id " ] . as < GroupId > ( ) ] = currentGroupId ;
LOG_SQL_CMD ( sql : : command ( this - > handle - > getSql ( ) , " INSERT INTO `groups` (`serverId`, `groupId`, `target`, `type`, `displayName`) VALUES (:sid, :gid, :target, :type, :name) " ,
variable { " :sid " , serverId } ,
variable { " :gid " , currentGroupId } ,
variable { " :target " , GROUPTARGET_SERVER } ,
variable { " :type " , GroupType : : GROUP_TYPE_NORMAL } ,
variable { " :name " , arguments [ index ] [ " name " ] . as < string > ( ) } )
. execute ( ) ) ;
auto permissions = SnapshotPermissionEntry : : parse ( arguments , index , permission : : teamspeak : : SERVER , snapshot_version , { " end_group " } ) ;
for ( const auto & entry : permissions ) {
futures . push_back ( {
" server group permission sgid= " + to_string ( currentGroupId ) + " permId= " + entry - > type - > name ,
sql_insert_permission . command ( ) . values (
variable { " :id " , currentGroupId } ,
variable { " :type " , permission : : SQL_PERM_GROUP } ,
variable { " :chId " , 0 } ,
variable { " :permId " , entry - > type - > name } ,
variable { " :value " , entry - > value } ,
variable { " :grant " , entry - > grant } ,
variable { " :flag_skip " , entry - > skipped } ,
variable { " :flag_negate " , entry - > negated }
) . executeLater ( )
} ) ;
}
index + + ;
}
//Relations for sgroups after list
index + + ;
while ( true ) {
if ( arguments [ index ] . has ( " end_relations " ) ) break ;
serverGroupRelations [ db_mapping_client_id [ arguments [ index ] [ " cldbid " ] . as < ClientDbId > ( ) ] ] . push_back ( serverGroupMapping [ arguments [ index ] [ " gid " ] . as < GroupId > ( ) ] ) ;
futures . push_back ( {
" server group relation " ,
sql_insert_group_assignment . command ( ) . values ( variable { " :cldbid " , db_mapping_client_id [ arguments [ index ] [ " cldbid " ] . as < ClientDbId > ( ) ] } , variable { " :gid " , serverGroupMapping [ arguments [ index ] [ " gid " ] . as < GroupId > ( ) ] } , variable { " :chid " , " 0 " } ) . executeLater ( )
} ) ;
index + + ;
}
} else if ( arguments [ index ] . has ( " channel_groups " ) ) {
GroupId currentGroupId = 1 ;
while ( true ) {
if ( arguments [ index ] . has ( " end_groups " ) ) break ;
if ( ! arguments [ index ] . has ( " id " ) ) { //new group
logError ( 0 , " Invalid server group permission entry! Missing group id! " ) ;
index + + ;
continue ;
}
currentGroupId = static_cast < GroupId > ( GroupManager : : generateGroupId ( this - > handle - > getSql ( ) ) ) ;
debugMessage ( log_server_id , PREFIX + " Insert channel group: " + to_string ( currentGroupId ) + " - " + arguments [ index ] [ " name " ] . as < string > ( ) ) ;
channelGroupMapping [ arguments [ index ] [ " id " ] . as < GroupId > ( ) ] = currentGroupId ;
LOG_SQL_CMD ( sql : : command ( this - > handle - > getSql ( ) , " INSERT INTO `groups` (`serverId`, `groupId`, `target`, `type`, `displayName`) VALUES (:sid, :gid, :target, :type, :name) " ,
variable { " :sid " , serverId } ,
variable { " :gid " , currentGroupId } ,
variable { " :target " , GROUPTARGET_CHANNEL } ,
variable { " :type " , GroupType : : GROUP_TYPE_NORMAL } ,
variable { " :name " , arguments [ index ] [ " name " ] . as < string > ( ) } )
. execute ( ) ) ;
auto permissions = SnapshotPermissionEntry : : parse ( arguments , index , permission : : teamspeak : : SERVER , snapshot_version , { " end_group " } ) ;
for ( const auto & entry : permissions ) {
futures . push_back ( {
" channel group property insert cgid= " + to_string ( currentGroupId ) + " property= " + entry - > type - > name ,
sql_insert_permission . command ( ) . values (
variable { " :id " , currentGroupId } ,
variable { " :type " , permission : : SQL_PERM_GROUP } ,
variable { " :chId " , 0 } ,
variable { " :permId " , entry - > type - > name } ,
variable { " :value " , entry - > value } ,
variable { " :grant " , entry - > grant } ,
variable { " :flag_skip " , entry - > skipped } ,
variable { " :flag_negate " , entry - > negated }
) . executeLater ( )
} ) ;
}
index + + ;
}
//Relations for sgroups after list
ChannelId chId = 0 ;
index + + ;
while ( true ) {
if ( arguments [ index ] . has ( " end_relations " ) ) break ;
if ( arguments [ index ] . has ( " iid " ) ) chId = arguments [ index ] [ " iid " ] ;
channelGroupRelations [ db_mapping_client_id [ arguments [ index ] [ " cldbid " ] . as < ClientDbId > ( ) ] ] [ chId ] = channelGroupMapping [ arguments [ index ] [ " gid " ] . as < GroupId > ( ) ] ;
futures . push_back ( {
" channel group relation " ,
sql_insert_group_assignment . command ( ) . values ( variable { " :cldbid " , db_mapping_client_id [ arguments [ index ] [ " cldbid " ] . as < ClientDbId > ( ) ] } , variable { " :gid " , channelGroupMapping [ arguments [ index ] [ " gid " ] . as < GroupId > ( ) ] } , variable { " :chid " , chId } ) . executeLater ( )
} ) ;
index + + ;
}
} else if ( arguments [ index ] . has ( " client_flat " ) ) {
ClientDbId currentId = 0 ;
while ( true ) {
if ( arguments [ index ] . has ( " end_flat " ) ) break ;
if ( arguments [ index ] . has ( " id1 " ) )
currentId = arguments [ index ] [ " id1 " ] ;
auto permissions = SnapshotPermissionEntry : : parse ( arguments , index , permission : : teamspeak : : CLIENT , snapshot_version , { " id1 " , " end_flat " } , true ) ;
for ( const auto & entry : permissions ) {
futures . push_back ( {
" client permission insert clientId= " + to_string ( currentId ) + " permId= " + entry - > type - > name ,
sql_insert_permission . command ( ) . values (
variable { " :id " , currentId } ,
variable { " :type " , permission : : SQL_PERM_USER } ,
variable { " :chId " , 0 } ,
variable { " :permId " , entry - > type - > name } ,
variable { " :value " , entry - > value } ,
variable { " :grant " , entry - > grant } ,
variable { " :flag_skip " , entry - > skipped } ,
variable { " :flag_negate " , entry - > negated }
) . executeLater ( )
} ) ;
}
}
} else if ( arguments [ index ] . has ( " channel_flat " ) ) {
ChannelId currentChannelId = 0 ;
while ( true ) {
if ( arguments [ index ] . has ( " end_flat " ) ) break ;
if ( arguments [ index ] . has ( " id1 " ) )
currentChannelId = arguments [ index ] [ " id1 " ] ; //We also have id2 unknown for what this is. Maybe parent?
auto permissions = SnapshotPermissionEntry : : parse ( arguments , index , permission : : teamspeak : : CHANNEL , snapshot_version , { " id1 " , " end_flat " } , true ) ;
for ( const auto & entry : permissions ) {
futures . push_back ( {
" channel permission insert channelId= " + to_string ( currentChannelId ) + " permId= " + entry - > type - > name ,
sql_insert_permission . command ( ) . values (
variable { " :id " , 0 } ,
variable { " :type " , permission : : SQL_PERM_CHANNEL } ,
variable { " :chId " , currentChannelId } ,
variable { " :permId " , entry - > type - > name } ,
variable { " :value " , entry - > value } ,
variable { " :grant " , entry - > grant } ,
variable { " :flag_skip " , entry - > skipped } ,
variable { " :flag_negate " , entry - > negated }
) . executeLater ( )
} ) ;
}
}
} else if ( arguments [ index ] . has ( " channel_client_flat " ) ) {
ClientDbId currentClientId = 0 ;
ChannelId currentChannelId = 0 ;
while ( true ) {
if ( arguments [ index ] . has ( " end_flat " ) ) break ;
//id1 = channel id
//id2 = client id
if ( arguments [ index ] . has ( " id1 " ) )
currentChannelId = arguments [ index ] [ " id1 " ] ;
if ( arguments [ index ] . has ( " id2 " ) )
currentClientId = db_mapping_client_id [ arguments [ index ] [ " id2 " ] ] ;
if ( currentChannelId > 0 & & currentClientId > 0 ) {
auto permissions = SnapshotPermissionEntry : : parse ( arguments , index , permission : : teamspeak : : CLIENT , snapshot_version , { " id1 " , " id2 " , " end_flat " } , true ) ;
for ( const auto & entry : permissions ) {
futures . push_back ( {
" client channel permission insert " ,
sql_insert_permission . command ( ) . values (
variable { " :id " , currentClientId } ,
variable { " :type " , permission : : SQL_PERM_CHANNEL } ,
variable { " :chId " , currentChannelId } ,
variable { " :permId " , entry - > type - > name } ,
variable { " :value " , entry - > value } ,
variable { " :grant " , entry - > grant } ,
variable { " :flag_skip " , entry - > skipped } ,
variable { " :flag_negate " , entry - > negated }
) . executeLater ( )
} ) ;
}
} else index + + ;
}
} else {
error = " Invalid tag in permissions. Available: " + avArguments ( arguments , index ) ;
return nullptr ;
}
}
} else if ( arguments [ index ] . has ( " begin_music " ) ) {
PlaylistId playlist_index = 0 ;
PlaylistId playlist_error = 0xFFFFFFFF ;
map < PlaylistId , PlaylistId > db_mapping_playlist ;
/*
//gather static info (not needed because its a new server)
{
auto sql_result = sql : : command ( this - > handle - > getSql ( ) , " SELECT `playlist_id` FROM `playlists` WHERE `serverId` = :server_id ORDER BY `playlist_id` DESC " , variable { " :server_id " , serverId } ) . query ( [ & ] ( int length , string * values , string * names ) {
if ( length ! = 1 ) return ;
try {
playlist_index = ( PlaylistId ) stoll ( values [ 0 ] ) ;
} catch ( const std : : exception & ex ) {
error = " Failed to parse playlist id from database. ID: " + values [ 0 ] ;
return ;
}
} ) ;
LOG_SQL_CMD ( sql_result ) ;
if ( playlist_index = = 0 ) {
error = " failed to gather info " ;
return nullptr ;
}
}
*/
while ( ! arguments [ index ] . has ( " end_music " ) ) {
if ( arguments [ index ] . has ( " begin_bots " ) ) {
while ( ! arguments [ index ] . has ( " end_bots " ) ) {
ClientDbId bot_id = arguments [ index ] [ " bot_id " ] ;
if ( db_mapping_client_id . find ( bot_id ) = = db_mapping_client_id . end ( ) ) {
logError ( log_server_id , PREFIX + " Failed to insert music bot within id {}. (Mapping not found) " , bot_id ) ;
index + + ;
continue ;
}
ClientDbId new_bot_id = db_mapping_client_id [ bot_id ] ;
//begin_bots
auto sql_result = sql_insert_bot . command ( ) . values (
variable { " :bot_id " , new_bot_id } ,
variable { " :unique_id " , arguments [ index ] [ " bot_unique_id " ] . string ( ) } ,
variable { " :owner " , arguments [ index ] [ " bot_owner_id " ] . string ( ) }
) . execute ( ) ;
if ( ! sql_result ) {
logError ( log_server_id , PREFIX + " Failed to insert music bot with id {} (old: {}). ({}) " , new_bot_id , bot_id , sql_result . fmtStr ( ) ) ;
index + + ;
continue ;
}
for ( const auto & key : arguments [ index ] . keys ( ) ) {
if ( key = = " begin_music " ) continue ;
if ( key = = " begin_bots " ) continue ;
if ( key = = " bot_unique_id " ) continue ;
if ( key = = " bot_owner_id " ) continue ;
if ( key = = " bot_id " ) continue ;
2020-04-08 13:01:41 +02:00
const auto & property = property : : find < property : : ClientProperties > ( key ) ;
if ( property . is_undefined ( ) ) {
2020-01-24 02:57:58 +01:00
debugMessage ( log_server_id , PREFIX + " Failed to parse give music bot property {} for bot {} (old: {}). Value: {} " , key , new_bot_id , bot_id , arguments [ index ] [ key ] . string ( ) ) ;
continue ;
}
futures . push_back ( {
" music bot insert " ,
sql_insert_property . command ( ) . values (
variable { " :type " , property : : PropertyType : : PROP_TYPE_CLIENT } ,
variable { " :id " , new_bot_id } ,
variable { " :key " , key } ,
variable { " :value " , arguments [ index ] [ key ] . string ( ) }
) . executeLater ( )
} ) ;
}
index + + ;
}
} else if ( arguments [ index ] . has ( " begin_playlist " ) ) {
while ( ! arguments [ index ] . has ( " end_playlist " ) ) {
PlaylistId playlist_id = arguments [ index ] [ " playlist_id " ] ;
db_mapping_playlist [ + + playlist_index ] = playlist_id ;
auto sql_result = sql_insert_playlist . command ( ) . values ( variable { " :playlist_id " , playlist_index } ) . execute ( ) ;
if ( ! sql_result ) {
db_mapping_playlist [ playlist_index ] = playlist_error ;
logError ( log_server_id , PREFIX + " Failed to insert playlist with id {} (old: {}). ({}) " , playlist_index , playlist_id , sql_result . fmtStr ( ) ) ;
index + + ;
continue ;
}
for ( const auto & key : arguments [ index ] . keys ( ) ) {
if ( key = = " begin_music " ) continue ;
if ( key = = " begin_playlist " ) continue ;
if ( key = = " playlist_id " ) continue ;
2020-04-08 13:01:41 +02:00
const auto & property = property : : find < property : : ClientProperties > ( key ) ;
if ( property . is_undefined ( ) ) {
2020-01-24 02:57:58 +01:00
debugMessage ( log_server_id , PREFIX + " Failed to parse given playlist property {} for playlist {} (old: {}). Value: {} " , key , playlist_index , playlist_id , arguments [ index ] [ key ] . string ( ) ) ;
continue ;
}
futures . push_back ( {
" playlist insert " ,
sql_insert_property . command ( ) . values (
variable { " :type " , property : : PropertyType : : PROP_TYPE_PLAYLIST } ,
variable { " :id " , playlist_index } ,
variable { " :key " , key } ,
variable { " :value " , arguments [ index ] [ key ] . string ( ) }
) . executeLater ( )
} ) ;
}
index + + ;
}
} else if ( arguments [ index ] . has ( " begin_playlist_songs " ) ) {
PlaylistId current_playlist = 0 ;
PlaylistId current_playlist_mapped = 0 ;
while ( ! arguments [ index ] . has ( " end_playlist_songs " ) ) {
if ( arguments [ index ] . has ( " song_playlist_id " ) ) {
current_playlist = arguments [ index ] [ " song_playlist_id " ] ;
current_playlist_mapped = ( PlaylistId ) db_mapping_client_id [ current_playlist ] ;
if ( current_playlist_mapped = = 0 ) {
logError ( log_server_id , PREFIX + " Failed to insert playlist song entry {}. Playlist id {} hasn't been mapped " , arguments [ index ] [ " song_id " ] . value ( ) , current_playlist ) ;
current_playlist_mapped = playlist_error ;
}
}
if ( current_playlist_mapped = = playlist_error ) { /* current list is an error */
index + + ;
continue ;
}
auto sql_future = sql_insert_playlist_song . command ( ) . values (
variable { " :playlist_id " , current_playlist_mapped } ,
variable { " :song_id " , arguments [ index ] [ " song_id " ] . value ( ) } ,
variable { " :order_id " , arguments [ index ] [ " song_order " ] . value ( ) } ,
variable { " :invoker_dbid " , arguments [ index ] [ " song_invoker " ] . value ( ) } ,
variable { " :url " , arguments [ index ] [ " song_url " ] . value ( ) } ,
variable { " :url_loader " , arguments [ index ] [ " song_url_loader " ] . value ( ) } ,
variable { " :loaded " , arguments [ index ] [ " song_loaded " ] . value ( ) } ,
variable { " :metadata " , arguments [ index ] [ " song_metadata " ] . value ( ) }
) . executeLater ( ) ;
futures . push_back ( {
" song insert " ,
sql_future
} ) ;
index + + ;
}
}
index + + ;
}
} else {
error = " Invalid root tag. Available: " + avArguments ( arguments , index ) ;
return nullptr ;
}
}
debugMessage ( serverId , " Wait for success! " ) ;
for ( const auto & future : futures ) {
auto result = future . second . waitAndGet ( { - 1 , " timeout " } , chrono : : system_clock : : now ( ) + chrono : : seconds ( 5 ) ) ;
if ( ! result ) {
logError ( serverId , " Failed to execute snapshotdeploy command {}: {} " , future . first , result . fmtStr ( ) ) ;
}
}
if ( old )
this - > deleteServer ( old ) ;
//Now we load the server
2020-01-26 18:04:38 +01:00
auto server = make_shared < VirtualServer > ( serverId , this - > handle - > getSql ( ) ) ;
2020-01-24 02:57:58 +01:00
server - > self = server ;
if ( ! server - > initialize ( false ) ) {
//FIXME Error handling!
}
server - > properties ( ) [ property : : VIRTUALSERVER_HOST ] = host ;
server - > properties ( ) [ property : : VIRTUALSERVER_PORT ] = port ;
server - > properties ( ) [ property : : VIRTUALSERVER_ASK_FOR_PRIVILEGEKEY ] = false ;
{
threads : : MutexLock l ( this - > instanceLock ) ;
this - > instances . push_back ( server ) ;
}
this - > adjust_executor_threads ( ) ;
server - > properties ( ) [ property : : VIRTUALSERVER_DEFAULT_SERVER_GROUP ] = serverGroupMapping [ server - > properties ( ) [ property : : VIRTUALSERVER_DEFAULT_SERVER_GROUP ] . as < GroupId > ( ) ] ;
server - > properties ( ) [ property : : VIRTUALSERVER_DEFAULT_CHANNEL_GROUP ] = channelGroupMapping [ server - > properties ( ) [ property : : VIRTUALSERVER_DEFAULT_CHANNEL_GROUP ] . as < GroupId > ( ) ] ;
server - > properties ( ) [ property : : VIRTUALSERVER_DEFAULT_CHANNEL_ADMIN_GROUP ] = channelGroupMapping [ server - > properties ( ) [ property : : VIRTUALSERVER_DEFAULT_CHANNEL_ADMIN_GROUP ] . as < GroupId > ( ) ] ;
server - > ensureValidDefaultGroups ( ) ;
return server ;
2019-07-17 19:37:18 +02:00
}
2020-07-31 17:35:14 +02:00
# endif
2019-07-17 19:37:18 +02:00
struct CommandTuple {
2020-01-24 02:57:58 +01:00
Command & cmd ;
int & index ;
int version ;
2019-07-17 19:37:18 +02:00
} ;
struct PermissionCommandTuple {
2020-01-24 02:57:58 +01:00
Command & cmd ;
int & index ;
int version ;
ClientDbId client ;
ChannelId channel ;
2019-07-17 19:37:18 +02:00
} ;
inline bool writePermissions ( const shared_ptr < permission : : v2 : : PermissionManager > & manager , Command & cmd , int & index , int version , permission : : teamspeak : : GroupType type , std : : string & error ) {
2020-01-24 02:57:58 +01:00
for ( const auto & permission_container : manager - > permissions ( ) ) {
auto permission = get < 1 > ( permission_container ) ;
SnapshotPermissionEntry {
permission : : resolvePermissionData ( get < 0 > ( permission_container ) ) ,
permission . flags . value_set ? permission . values . value : permNotGranted ,
permission . flags . grant_set ? permission . values . grant : permNotGranted ,
permission . flags . negate ,
permission . flags . skip
} . write ( cmd , index , type , version ) ;
}
return true ;
2019-07-17 19:37:18 +02:00
}
2020-01-26 18:04:38 +01:00
inline void writeRelations ( const shared_ptr < VirtualServer > & server , GroupTarget type , Command & cmd , int & index , int version ) {
2020-01-24 02:57:58 +01:00
PermissionCommandTuple parm { cmd , index , version , 0 , 0 } ;
auto res = sql : : command ( server - > getSql ( ) , " SELECT `cldbid`, `groups`.`groupId`, `channelId`, `until` FROM `assignedGroups` INNER JOIN `groups` ON `groups`.`serverId` = `assignedGroups`.`serverId` AND `groups`.`groupId` = `assignedGroups`.`groupId` WHERE `groups`.`serverId` = :sid AND `groups`.target = :type " ,
variable { " :sid " , server - > getServerId ( ) } ,
variable { " :type " , type }
) . query ( [ ] ( PermissionCommandTuple * commandIndex , int length , char * * value , char * * name ) {
ClientDbId cldbid = 0 ;
ChannelId channelId = 0 ;
GroupId gid = 0 ;
int64_t until = 0 ;
for ( int idx = 0 ; idx < length ; idx + + ) {
try {
if ( strcmp ( name [ idx ] , " cldbid " ) = = 0 )
cldbid = stoul ( value [ idx ] ) ;
else if ( strcmp ( name [ idx ] , " groupId " ) = = 0 )
gid = stoul ( value [ idx ] ) ;
else if ( strcmp ( name [ idx ] , " channelId " ) = = 0 )
channelId = stoul ( value [ idx ] ) ;
else if ( strcmp ( name [ idx ] , " until " ) = = 0 )
until = stoll ( value [ idx ] ) ;
} catch ( std : : exception & ex ) {
logError ( 0 , " Failed to write snapshot group relation (Skipping it)! Message: {} @ {} => {} " , ex . what ( ) , name [ idx ] , value [ idx ] ) ;
return 0 ;
}
}
if ( commandIndex - > channel ! = channelId ) {
commandIndex - > channel = channelId ;
commandIndex - > cmd [ commandIndex - > index ] [ " iid " ] = channelId ;
}
commandIndex - > cmd [ commandIndex - > index ] [ " gid " ] = gid ;
commandIndex - > cmd [ commandIndex - > index ] [ " until " ] = until ;
commandIndex - > cmd [ commandIndex - > index + + ] [ " cldbid " ] = cldbid ;
return 0 ;
} , & parm ) ;
LOG_SQL_CMD ( res ) ;
2019-07-17 19:37:18 +02:00
}
struct DatabaseMusicbot {
2020-01-24 02:57:58 +01:00
ClientDbId bot_id ;
ClientDbId bot_owner_id ;
std : : string bot_unique_id ;
2019-07-17 19:37:18 +02:00
} ;
2020-01-27 02:21:39 +01:00
bool VirtualServerManager : : createServerSnapshot ( Command & cmd , shared_ptr < VirtualServer > server , int version , std : : string & error ) {
2019-07-17 19:37:18 +02:00
2020-01-24 02:57:58 +01:00
int index = 0 ;
2020-07-31 17:35:14 +02:00
if ( version = = - 1 ) version = 3 ; //Auto versioned
if ( version < 0 | | version > 3 ) {
2020-01-24 02:57:58 +01:00
error = " Invalid snapshot version! " ;
return false ;
}
if ( version > 0 ) cmd [ index + + ] [ " snapshot_version " ] = version ;
//Server
{
cmd [ index ] [ " begin_virtualserver " ] = " " ;
for ( const auto & serverProperty : server - > properties ( ) . list_properties ( property : : FLAG_SNAPSHOT ) ) {
if ( version = = 0 ) {
switch ( serverProperty . type ( ) . property_index ) {
case property : : VIRTUALSERVER_DOWNLOAD_QUOTA :
case property : : VIRTUALSERVER_UPLOAD_QUOTA :
case property : : VIRTUALSERVER_MAX_DOWNLOAD_TOTAL_BANDWIDTH :
case property : : VIRTUALSERVER_MAX_UPLOAD_TOTAL_BANDWIDTH :
2021-03-01 14:16:44 +01:00
cmd [ index ] [ std : : string { serverProperty . type ( ) . name } ] = ( uint64_t ) serverProperty . as_or < int64_t > ( 0 ) ;
2020-01-24 02:57:58 +01:00
default :
break ;
}
}
2020-04-08 13:01:41 +02:00
cmd [ index ] [ std : : string { serverProperty . type ( ) . name } ] = serverProperty . value ( ) ;
2020-01-24 02:57:58 +01:00
}
cmd [ index + + ] [ " end_virtualserver " ] = " " ;
}
//Channels
{
cmd [ index ] [ " begin_channels " ] = " " ;
for ( const auto & channel : server - > getChannelTree ( ) - > channels ( ) ) {
for ( const auto & channelProperty : channel - > properties ( ) . list_properties ( property : : FLAG_SNAPSHOT ) ) {
2021-03-01 14:16:44 +01:00
if ( channelProperty . type ( ) = = property : : CHANNEL_ID ) {
cmd [ index ] [ " channel_id " ] = channelProperty . value ( ) ;
} else if ( channelProperty . type ( ) = = property : : CHANNEL_PID ) {
cmd [ index ] [ " channel_pid " ] = channelProperty . value ( ) ;
} else {
cmd [ index ] [ std : : string { channelProperty . type ( ) . name } ] = channelProperty . value ( ) ;
}
2020-01-24 02:57:58 +01:00
}
index + + ;
}
cmd [ index + + ] [ " end_channels " ] = " " ;
}
//Clients
{
cmd [ index ] [ " begin_clients " ] = " " ;
2020-07-31 17:35:14 +02:00
struct CallbackArgument {
Command & command ;
int & index ;
int version ;
} ;
CallbackArgument callback_argument { cmd , index , version } ;
this - > handle - > databaseHelper ( ) - > listDatabaseClients ( server - > getServerId ( ) , std : : nullopt , std : : nullopt , [ ] ( void * ptr_argument , const DatabaseClient & client ) {
auto argument = ( CallbackArgument * ) ptr_argument ;
argument - > command [ argument - > index ] [ " client_id " ] = client . client_database_id ;
argument - > command [ argument - > index ] [ " client_unique_id " ] = client . client_unique_id ;
argument - > command [ argument - > index ] [ " client_nickname " ] = client . client_nickname ;
argument - > command [ argument - > index ] [ " client_created " ] = client . client_created ;
argument - > command [ argument - > index ] [ " client_description " ] = client . client_description ;
if ( argument - > version = = 0 )
argument - > command [ argument - > index ] [ " client_unread_messages " ] = 0 ;
argument - > index + + ;
} , & callback_argument ) ;
2020-01-24 02:57:58 +01:00
cmd [ index + + ] [ " end_clients " ] = " " ;
}
//music and music bots
if ( version > = 2 ) {
cmd [ index ] [ " begin_music " ] = " " ;
/* music bots */
{
cmd [ index ] [ " begin_bots " ] = " " ;
deque < DatabaseMusicbot > music_bots ;
auto sql_result = sql : : command ( server - > getSql ( ) , " SELECT `botId`, `uniqueId`, `owner` FROM `musicbots` WHERE `serverId` = :sid " , variable { " :sid " , server - > getServerId ( ) } ) . query ( [ & ] ( int length , string * values , string * names ) {
DatabaseMusicbot data ;
for ( int column = 0 ; column < length ; column + + ) {
try {
if ( names [ column ] = = " botId " )
data . bot_id = stoll ( values [ column ] ) ;
else if ( names [ column ] = = " uniqueId " )
data . bot_unique_id = values [ column ] ;
else if ( names [ column ] = = " owner " )
data . bot_owner_id = stoll ( values [ column ] ) ;
} catch ( std : : exception & ex ) {
return ;
}
}
music_bots . emplace_back ( data ) ;
} ) ;
if ( ! sql_result )
logError ( server - > getServerId ( ) , PREFIX + " Failed to write music bots to snapshot. {} " , sql_result . fmtStr ( ) ) ;
for ( const auto & music_bot : music_bots ) {
auto properties = serverInstance - > databaseHelper ( ) - > query_properties ( server - > getServerId ( ) , property : : PROP_TYPE_CLIENT , music_bot . bot_id ) ;
cmd [ index ] [ " bot_unique_id " ] = music_bot . bot_unique_id ;
cmd [ index ] [ " bot_owner_id " ] = music_bot . bot_owner_id ;
cmd [ index ] [ " bot_id " ] = music_bot . bot_id ;
for ( const auto & property : properties ) {
if ( ( property - > type - > flags & ( property : : FLAG_SAVE_MUSIC | property : : FLAG_SAVE ) ) = = 0 ) continue ;
if ( property - > value = = property - > type - > default_value ) continue ;
2020-04-08 13:01:41 +02:00
cmd [ index ] [ std : : string { property - > type - > name } ] = property - > value ;
2020-01-24 02:57:58 +01:00
}
index + + ;
}
cmd [ index + + ] [ " end_bots " ] = " " ;
}
/* playlists */
{
cmd [ index ] [ " begin_playlist " ] = " " ;
deque < PlaylistId > playlist_ids ;
auto sql_result = sql : : command ( server - > getSql ( ) , " SELECT `playlist_id` FROM `playlists` WHERE `serverId` = :sid " , variable { " :sid " , server - > getServerId ( ) } ) . query ( [ & ] ( int length , string * values , string * names ) {
try {
playlist_ids . push_back ( stoll ( values [ 0 ] ) ) ;
} catch ( std : : exception & ex ) {
return ;
}
} ) ;
if ( ! sql_result )
logError ( server - > getServerId ( ) , PREFIX + " Failed to write playlists to snapshot. {} " , sql_result . fmtStr ( ) ) ;
for ( const auto & playlist : playlist_ids ) {
auto properties = serverInstance - > databaseHelper ( ) - > query_properties ( server - > getServerId ( ) , property : : PROP_TYPE_PLAYLIST , playlist ) ;
cmd [ index ] [ " playlist_id " ] = playlist ;
for ( const auto & property : properties ) {
if ( ( property - > type - > flags & ( property : : FLAG_SAVE_MUSIC | property : : FLAG_SAVE ) ) = = 0 ) continue ;
if ( property - > value = = property - > type - > default_value ) continue ;
2020-04-08 13:01:41 +02:00
cmd [ index ] [ std : : string { property - > type - > name } ] = property - > value ;
2020-01-24 02:57:58 +01:00
}
index + + ;
}
cmd [ index + + ] [ " end_playlist " ] = " " ;
}
/* playlist info */
{
cmd [ index ] [ " begin_playlist_songs " ] = " " ;
//playlist_songs => `serverId` INT NOT NULL, `playlist_id` INT, `song_id` INT, `order_id` INT, `invoker_dbid` INT, `url` TEXT, `url_loader` TEXT, `loaded` BOOL, `metadata` TEXT
PlaylistId current_playlist = 0 ;
auto sql_result = sql : : command ( server - > getSql ( ) ,
" SELECT `playlist_id`, `song_id`, `order_id`, `invoker_dbid`, `url`, `url_loader`, `loaded`, `metadata` FROM `playlist_songs` WHERE `serverId` = :sid ORDER BY `playlist_id` " , variable { " :sid " , server - > getServerId ( ) }
) . query ( [ & ] ( int length , string * values , string * names ) {
for ( int column = 0 ; column < length ; column + + ) {
if ( names [ column ] = = " song_id " )
cmd [ index ] [ " song_id " ] = values [ column ] ;
else if ( names [ column ] = = " order_id " )
cmd [ index ] [ " song_order " ] = values [ column ] ;
else if ( names [ column ] = = " invoker_dbid " )
cmd [ index ] [ " song_invoker " ] = values [ column ] ;
else if ( names [ column ] = = " url " )
cmd [ index ] [ " song_url " ] = values [ column ] ;
else if ( names [ column ] = = " url_loader " )
cmd [ index ] [ " song_url_loader " ] = values [ column ] ;
else if ( names [ column ] = = " loaded " )
cmd [ index ] [ " song_loaded " ] = values [ column ] ;
else if ( names [ column ] = = " metadata " )
cmd [ index ] [ " song_metadata " ] = values [ column ] ;
else if ( names [ column ] = = " playlist_id " ) {
try {
auto playlist_id = stoll ( values [ column ] ) ;
if ( current_playlist ! = playlist_id ) {
cmd [ index ] [ " song_playlist_id " ] = values [ column ] ; /* song_playlist_id will be only set if the playlist id had changed */
current_playlist = playlist_id ;
}
} catch ( std : : exception & ex ) {
logError ( server - > getServerId ( ) , PREFIX + " Failed to parse playlist id. value: {}, message: {} " , values [ column ] , ex . what ( ) ) ;
}
}
}
index + + ;
} ) ;
if ( ! sql_result )
logError ( server - > getServerId ( ) , PREFIX + " Failed to write playlist songs to snapshot. {} " , sql_result . fmtStr ( ) ) ;
cmd [ index + + ] [ " end_playlist_songs " ] = " " ;
}
cmd [ index + + ] [ " end_music " ] = " " ;
}
//Permissions
{
cmd [ index + + ] [ " begin_permissions " ] = " " ;
//Server groups
{
//List groups
{
cmd [ index ] [ " server_groups " ] = " " ;
for ( const auto & group : server - > getGroupManager ( ) - > availableServerGroups ( false ) ) {
cmd [ index ] [ " id " ] = group - > groupId ( ) ;
cmd [ index ] [ " name " ] = group - > name ( ) ;
if ( ! writePermissions ( group - > permissions ( ) , cmd , index , version , permission : : teamspeak : : SERVER , error ) ) break ;
cmd [ index + + ] [ " end_group " ] = " " ;
}
cmd [ index + + ] [ " end_groups " ] = " " ;
}
//List relations
{
cmd [ index ] [ " begin_relations " ] = " " ;
writeRelations ( server , GROUPTARGET_SERVER , cmd , index , version ) ;
cmd [ index + + ] [ " end_relations " ] = " " ;
}
}
//Channel groups
{
//List groups
{
cmd [ index ] [ " channel_groups " ] = " " ;
for ( const auto & group : server - > getGroupManager ( ) - > availableChannelGroups ( false ) ) {
cmd [ index ] [ " id " ] = group - > groupId ( ) ;
cmd [ index ] [ " name " ] = group - > name ( ) ;
if ( ! writePermissions ( group - > permissions ( ) , cmd , index , version , permission : : teamspeak : : SERVER , error ) ) break ;
cmd [ index + + ] [ " end_group " ] = " " ;
}
cmd [ index + + ] [ " end_groups " ] = " " ;
}
//List relations
{
cmd [ index ] [ " begin_relations " ] = " " ;
writeRelations ( server , GROUPTARGET_CHANNEL , cmd , index , version ) ;
cmd [ index + + ] [ " end_relations " ] = " " ;
}
}
//Client rights
{
cmd [ index ] [ " client_flat " ] = " " ;
PermissionCommandTuple parm { cmd , index , version , 0 , 0 } ;
auto res = sql : : command ( server - > getSql ( ) , " SELECT `id`, `permId`, `value`, `grant`, `flag_skip`, `flag_negate` FROM `permissions` WHERE `serverId` = :sid AND `type` = :type AND `channelId` = 0 ORDER BY `id` " ,
variable { " :sid " , server - > getServerId ( ) } ,
variable { " :type " , permission : : SQL_PERM_USER }
) . query ( [ & ] ( PermissionCommandTuple * commandIndex , int length , char * * values , char * * names ) {
auto type = permission : : resolvePermissionData ( permission : : unknown ) ;
permission : : PermissionValue value = 0 , grant = 0 ;
bool skipped = false , negated = false ;
ClientDbId cldbid = 0 ;
for ( int idx = 0 ; idx < length ; idx + + ) {
try {
if ( strcmp ( names [ idx ] , " id " ) = = 0 )
cldbid = stoul ( values [ idx ] & & strlen ( values [ idx ] ) > 0 ? values [ idx ] : " 0 " ) ;
else if ( strcmp ( names [ idx ] , " value " ) = = 0 )
value = values [ idx ] & & strlen ( values [ idx ] ) > 0 ? stoi ( values [ idx ] ) : permNotGranted ;
else if ( strcmp ( names [ idx ] , " grant " ) = = 0 )
grant = values [ idx ] & & strlen ( values [ idx ] ) > 0 ? stoi ( values [ idx ] ) : permNotGranted ;
else if ( strcmp ( names [ idx ] , " permId " ) = = 0 ) {
type = permission : : resolvePermissionData ( values [ idx ] ) ;
if ( type - > type = = permission : : unknown ) {
logError ( 0 , " Could not parse client permission for snapshot (Invalid type {})! Skipping it! " , values [ idx ] ) ;
return 0 ;
}
}
else if ( strcmp ( names [ idx ] , " flag_skip " ) = = 0 )
skipped = values [ idx ] ? strcmp ( values [ idx ] , " 1 " ) = = 0 : false ;
else if ( strcmp ( names [ idx ] , " flag_negate " ) = = 0 )
negated = values [ idx ] ? strcmp ( values [ idx ] , " 1 " ) = = 0 : false ;
} catch ( std : : exception & ex ) {
logError ( 0 , " Failed to write snapshot client permission (Skipping it)! Message: {} @ {} => {} " , ex . what ( ) , names [ idx ] , values [ idx ] ) ;
return 0 ;
}
}
if ( type - > type = = permission : : unknown ) {
logError ( 0 , " Could not parse client permission for snapshot (Missing type)! Skipping it! " ) ;
return 0 ;
}
if ( cldbid = = 0 ) {
logError ( 0 , " Could not parse client permission for snapshot (Missing cldbid)! Skipping it! " ) ;
return 0 ;
}
if ( value = = permNotGranted & & grant = = permNotGranted & & ! skipped & & ! negated ) return 0 ;
if ( commandIndex - > client ! = cldbid ) {
commandIndex - > client = cldbid ;
commandIndex - > cmd [ commandIndex - > index ] [ " id1 " ] = cldbid ;
commandIndex - > cmd [ commandIndex - > index ] [ " id2 " ] = 0 ;
}
SnapshotPermissionEntry { type , value , grant , negated , skipped } . write ( commandIndex - > cmd , commandIndex - > index , permission : : teamspeak : : CLIENT , commandIndex - > version ) ;
return 0 ;
} , & parm ) ;
LOG_SQL_CMD ( res ) ;
cmd [ index + + ] [ " end_flat " ] = " " ;
}
//Channel rights
{
cmd [ index ] [ " channel_flat " ] = " " ;
PermissionCommandTuple parm { cmd , index , version , 0 , 0 } ;
auto res = sql : : command ( server - > getSql ( ) , " SELECT `channelId`, `permId`, `value`, `grant`, `flag_skip`, `flag_negate` FROM `permissions` WHERE `serverId` = :sid AND `type` = :type " ,
variable { " :sid " , server - > getServerId ( ) } ,
variable { " :type " , permission : : SQL_PERM_CHANNEL } ) . query (
[ & ] ( PermissionCommandTuple * commandIndex , int length , char * * values , char * * names ) {
auto type = permission : : resolvePermissionData ( permission : : unknown ) ;
permission : : PermissionValue value = 0 , grant = 0 ;
bool skipped = false , negated = false ;
ChannelId chid = 0 ;
for ( int idx = 0 ; idx < length ; idx + + ) {
try {
if ( strcmp ( names [ idx ] , " channelId " ) = = 0 )
chid = stoul ( values [ idx ] & & strlen ( values [ idx ] ) > 0 ? values [ idx ] : " 0 " ) ;
else if ( strcmp ( names [ idx ] , " value " ) = = 0 )
value = values [ idx ] & & strlen ( values [ idx ] ) > 0 ? stoi ( values [ idx ] ) : permNotGranted ;
else if ( strcmp ( names [ idx ] , " grant " ) = = 0 )
grant = values [ idx ] & & strlen ( values [ idx ] ) > 0 ? stoi ( values [ idx ] ) : permNotGranted ;
else if ( strcmp ( names [ idx ] , " permId " ) = = 0 ) {
type = permission : : resolvePermissionData ( values [ idx ] ) ;
if ( type - > type = = permission : : unknown ) {
logError ( 0 , " Could not parse channel permission for snapshot (Invalid type {})! Skipping it! " , values [ idx ] ) ;
return 0 ;
}
}
else if ( strcmp ( names [ idx ] , " flag_skip " ) = = 0 )
skipped = values [ idx ] ? strcmp ( values [ idx ] , " 1 " ) = = 0 : false ;
else if ( strcmp ( names [ idx ] , " flag_negate " ) = = 0 )
negated = values [ idx ] ? strcmp ( values [ idx ] , " 1 " ) = = 0 : false ;
} catch ( std : : exception & ex ) {
logError ( 0 , " Failed to write snapshot channel permission (Skipping it)! Message: {} @ {} => {} " , ex . what ( ) , names [ idx ] , values [ idx ] ) ;
return 0 ;
}
}
if ( type - > type = = permission : : unknown ) {
logError ( 0 , " Could not parse channel permission for snapshot (Missing type)! Skipping it! " ) ;
return 0 ;
}
if ( chid = = 0 ) {
logError ( 0 , " Could not parse channel permission for snapshot (Missing channel id)! Skipping it! " ) ;
return 0 ;
}
if ( value = = permNotGranted & & grant = = permNotGranted & & ! skipped & & ! negated ) return 0 ;
if ( commandIndex - > channel ! = chid ) {
commandIndex - > channel = chid ;
commandIndex - > cmd [ commandIndex - > index ] [ " id1 " ] = chid ;
commandIndex - > cmd [ commandIndex - > index ] [ " id2 " ] = 0 ;
}
SnapshotPermissionEntry { type , value , grant , negated , skipped } . write ( commandIndex - > cmd , commandIndex - > index , permission : : teamspeak : : CHANNEL , commandIndex - > version ) ;
return 0 ;
} , & parm ) ;
LOG_SQL_CMD ( res ) ;
cmd [ index + + ] [ " end_flat " ] = " " ;
}
//Client channel rights
{
cmd [ index ] [ " channel_client_flat " ] = " " ;
PermissionCommandTuple parm { cmd , index , version , 0 , 0 } ;
auto res = sql : : command ( server - > getSql ( ) , " SELECT `id`, `channelId`, `permId`, `value`, `grant`, `flag_skip`, `flag_negate` FROM `permissions` WHERE `serverId` = :sid AND `type` = :type AND `channelId` != 0 " ,
variable { " :sid " , server - > getServerId ( ) } ,
variable { " :type " , permission : : SQL_PERM_USER } ) . query (
[ & ] ( PermissionCommandTuple * commandIndex , int length , char * * values , char * * names ) {
auto type = permission : : resolvePermissionData ( permission : : unknown ) ;
permission : : PermissionValue value = 0 , grant = 0 ;
bool skipped = false , negated = false ;
ChannelId chid = 0 ;
ClientDbId cldbid = 0 ;
for ( int idx = 0 ; idx < length ; idx + + ) {
try {
if ( strcmp ( names [ idx ] , " channelId " ) = = 0 )
chid = stoul ( values [ idx ] & & strlen ( values [ idx ] ) > 0 ? values [ idx ] : " 0 " ) ;
if ( strcmp ( names [ idx ] , " id " ) = = 0 )
cldbid = stoul ( values [ idx ] & & strlen ( values [ idx ] ) > 0 ? values [ idx ] : " 0 " ) ;
else if ( strcmp ( names [ idx ] , " value " ) = = 0 )
value = values [ idx ] & & strlen ( values [ idx ] ) > 0 ? stoi ( values [ idx ] ) : permNotGranted ;
else if ( strcmp ( names [ idx ] , " grant " ) = = 0 )
grant = values [ idx ] & & strlen ( values [ idx ] ) > 0 ? stoi ( values [ idx ] ) : permNotGranted ;
else if ( strcmp ( names [ idx ] , " permId " ) = = 0 ) {
type = permission : : resolvePermissionData ( values [ idx ] ) ;
if ( type - > type = = permission : : unknown ) {
logError ( 0 , " Could not parse channel client permission for snapshot (Invalid type {})! Skipping it! " , values [ idx ] ) ;
return 0 ;
}
}
else if ( strcmp ( names [ idx ] , " flag_skip " ) = = 0 )
skipped = values [ idx ] ? strcmp ( values [ idx ] , " 1 " ) = = 0 : false ;
else if ( strcmp ( names [ idx ] , " flag_negate " ) = = 0 )
negated = values [ idx ] ? strcmp ( values [ idx ] , " 1 " ) = = 0 : false ;
} catch ( std : : exception & ex ) {
logError ( 0 , " Failed to write snapshot channel client permission (Skipping it)! Message: {} @ {} => {} " , ex . what ( ) , names [ idx ] , values [ idx ] ) ;
return 0 ;
}
}
if ( type - > type = = permission : : unknown ) {
logError ( 0 , " Could not parse channel client permission for snapshot (Missing type)! Skipping it! " ) ;
return 0 ;
}
if ( chid = = 0 ) {
logError ( 0 , " Could not parse channel client permission for snapshot (Missing channel id)! Skipping it! " ) ;
return 0 ;
}
if ( cldbid = = 0 ) {
logError ( 0 , " Could not parse channel client permission for snapshot (Missing cldbid)! Skipping it! " ) ;
return 0 ;
}
if ( value = = permNotGranted & & grant = = permNotGranted & & ! skipped & & ! negated ) return 0 ;
if ( commandIndex - > channel ! = chid | | commandIndex - > client ! = cldbid ) {
commandIndex - > channel = chid ;
commandIndex - > client = cldbid ;
commandIndex - > cmd [ commandIndex - > index ] [ " id1 " ] = chid ;
commandIndex - > cmd [ commandIndex - > index ] [ " id2 " ] = cldbid ;
}
SnapshotPermissionEntry { type , value , grant , negated , skipped } . write ( commandIndex - > cmd , commandIndex - > index , permission : : teamspeak : : CLIENT , commandIndex - > version ) ;
return 0 ;
} , & parm ) ;
LOG_SQL_CMD ( res ) ;
cmd [ index + + ] [ " end_flat " ] = " " ;
}
cmd [ index + + ] [ " end_permissions " ] = " " ;
}
return true ;
2019-07-17 19:37:18 +02:00
}