2019-07-17 13:37:18 -04:00
# include <algorithm>
# include <cstring>
# include <log/LogUtils.h>
# include <sql/SqlQuery.h>
# include <src/client/DataClient.h>
# include <misc/std_unique_ptr.h>
using namespace std ;
using namespace std : : chrono ;
using namespace ts ;
using namespace ts : : server ;
using namespace ts : : permission ;
//#define DISABLE_CACHING
DatabaseHelper : : DatabaseHelper ( sql : : SqlManager * srv ) : sql ( srv ) { }
DatabaseHelper : : ~ DatabaseHelper ( ) {
for ( const auto & elm : cachedPermissionManagers )
delete elm ;
cachedPermissionManagers . clear ( ) ;
}
void DatabaseHelper : : tick ( ) {
{
threads : : MutexLock l ( this - > permManagerLock ) ;
auto cpy = this - > cachedPermissionManagers ;
for ( const auto & mgr : cpy ) {
//if(mgr->ownLock && system_clock::now() - mgr->lastAccess > minutes(5)) //TODO remove instand delete!
mgr - > ownLock . reset ( ) ;
if ( mgr - > manager . expired ( ) ) {
this - > cachedPermissionManagers . erase ( std : : find ( this - > cachedPermissionManagers . begin ( ) , this - > cachedPermissionManagers . end ( ) , mgr ) ) ;
delete mgr ;
}
}
}
2020-01-23 20:57:58 -05:00
{
threads : : MutexLock l ( this - > propsLock ) ;
auto pcpy = this - > cachedProperties ;
2020-04-14 05:49:07 -04:00
for ( const auto & mgr : pcpy ) {
2020-01-23 20:57:58 -05:00
if ( mgr - > ownLock & & system_clock : : now ( ) - mgr - > lastAccess > minutes ( 5 ) )
mgr - > ownLock . reset ( ) ;
if ( mgr - > properties . expired ( ) ) {
this - > cachedProperties . erase ( std : : find ( this - > cachedProperties . begin ( ) , this - > cachedProperties . end ( ) , mgr ) ) ;
delete mgr ;
}
}
}
2019-07-17 13:37:18 -04:00
}
int collectData ( deque < shared_ptr < ClientDatabaseInfo > > * list , int length , char * * values , char * * columns ) {
shared_ptr < ClientDatabaseInfo > entry = std : : make_shared < ClientDatabaseInfo > ( ) ;
for ( int index = 0 ; index < length ; index + + )
if ( strcmp ( columns [ index ] , " cldbid " ) = = 0 )
entry - > cldbid = static_cast < ClientDbId > ( stol ( values [ index ] ) ) ;
else if ( strcmp ( columns [ index ] , " clientUid " ) = = 0 )
entry - > uniqueId = values [ index ] ;
else if ( strcmp ( columns [ index ] , " firstConnect " ) = = 0 )
entry - > created = time_point < system_clock > ( ) + seconds ( stoll ( values [ index ] ) ) ;
else if ( strcmp ( columns [ index ] , " lastConnect " ) = = 0 )
entry - > lastjoin = time_point < system_clock > ( ) + seconds ( stoll ( values [ index ] ) ) ;
else if ( strcmp ( columns [ index ] , " connections " ) = = 0 )
entry - > connections = static_cast < uint32_t > ( stoi ( values [ index ] ) ) ;
else if ( strcmp ( columns [ index ] , " lastName " ) = = 0 )
entry - > lastName = values [ index ] ;
else if ( strcmp ( columns [ index ] , " serverId " ) = = 0 ) ;
2019-11-23 15:16:55 -05:00
else logError ( LOG_GENERAL , " Invalid db key for manager data. Key: {} " , columns [ index ] ) ;
2019-07-17 13:37:18 -04:00
list - > push_back ( entry ) ;
return 0 ;
}
# define MAX_QUERY 32
2020-01-26 12:04:38 -05:00
std : : deque < std : : shared_ptr < ClientDatabaseInfo > > DatabaseHelper : : queryDatabaseInfo ( const std : : shared_ptr < VirtualServer > & server , const std : : deque < ClientDbId > & list ) {
2019-07-17 13:37:18 -04:00
if ( list . empty ( ) ) return { } ;
deque < shared_ptr < ClientDatabaseInfo > > result ;
if ( list . size ( ) < = MAX_QUERY ) {
std : : string query = " SELECT * FROM `clients` WHERE `serverId` = :serverId AND ( " ;
for ( auto elm : list )
query + = " `cldbid` = " + to_string ( elm ) + " OR " ;
query = query . substr ( 0 , query . length ( ) - 3 ) + " ) " ;
2019-11-23 15:16:55 -05:00
logTrace ( server ? server - > getServerId ( ) : 0 , " [SQL] queryDatabseInfo(...) -> {} " , query ) ;
2019-07-17 13:37:18 -04:00
auto state = sql : : command ( this - > sql , query , variable { " :serverId " , server ? server - > getServerId ( ) : 0 } ) . query ( std : : function < decltype ( collectData ) > ( collectData ) , & result ) ;
auto pf = LOG_SQL_CMD ;
pf ( state ) ;
if ( ! state ) return { } ;
} else {
std : : deque < ClientDbId > sub ;
do {
sub . insert ( sub . begin ( ) , list . begin ( ) , list . begin ( ) + min ( list . size ( ) , ( size_t ) MAX_QUERY ) ) ;
list . erase ( list . begin ( ) , list . begin ( ) + min ( list . size ( ) , ( size_t ) MAX_QUERY ) ) ;
auto res = this - > queryDatabaseInfo ( server , sub ) ;
result . insert ( result . end ( ) , res . begin ( ) , res . end ( ) ) ;
sub . clear ( ) ;
} while ( ! list . empty ( ) ) ;
}
return result ;
}
2020-01-26 12:04:38 -05:00
std : : deque < std : : shared_ptr < ClientDatabaseInfo > > DatabaseHelper : : queryDatabaseInfoByUid ( const std : : shared_ptr < VirtualServer > & server , std : : deque < std : : string > list ) {
2019-07-17 13:37:18 -04:00
if ( list . empty ( ) ) return { } ;
2020-01-23 20:57:58 -05:00
deque < shared_ptr < ClientDatabaseInfo > > result ;
2019-07-17 13:37:18 -04:00
if ( list . size ( ) < = MAX_QUERY ) {
std : : string query = " SELECT * FROM `clients` WHERE `serverId` = :serverId AND ( " ;
for ( const auto & elm : list )
query + = " `clientUid` = ' " + elm + " ' OR " ;
query = query . substr ( 0 , query . length ( ) - 3 ) + " ) " ;
2020-01-23 20:57:58 -05:00
logTrace ( server ? server - > getServerId ( ) : 0 , " [SQL] queryDatabseInfoByUid(...) -> {} " , query ) ;
2019-07-17 13:37:18 -04:00
auto state = sql : : command ( this - > sql , query , variable { " :serverId " , server ? server - > getServerId ( ) : 0 } ) . query ( function < decltype ( collectData ) > ( collectData ) , & result ) ;
auto pf = LOG_SQL_CMD ;
pf ( state ) ;
if ( ! state ) return { } ;
} else {
std : : deque < std : : string > sub ;
do {
sub . insert ( sub . begin ( ) , list . begin ( ) , list . begin ( ) + min ( list . size ( ) , ( size_t ) MAX_QUERY ) ) ;
list . erase ( list . begin ( ) , list . begin ( ) + min ( list . size ( ) , ( size_t ) MAX_QUERY ) ) ;
auto res = this - > queryDatabaseInfoByUid ( server , sub ) ;
result . insert ( result . end ( ) , res . begin ( ) , res . end ( ) ) ;
sub . clear ( ) ;
} while ( ! list . empty ( ) ) ;
}
return result ;
}
2020-01-26 12:04:38 -05:00
bool DatabaseHelper : : validClientDatabaseId ( const std : : shared_ptr < VirtualServer > & server , ClientDbId cldbid ) { return cldbid > 0 ; } //TODO here check
2019-07-17 13:37:18 -04:00
2020-01-26 12:04:38 -05:00
void DatabaseHelper : : deleteClient ( const std : : shared_ptr < VirtualServer > & server , ClientDbId cldbid ) {
2019-07-17 13:37:18 -04:00
ServerId sid = static_cast < ServerId > ( server ? server - > getServerId ( ) : 0 ) ;
{
lock_guard < threads : : Mutex > lock ( permManagerLock ) ;
for ( auto permMgr : this - > cachedPermissionManagers )
if ( permMgr - > cldbid = = cldbid & & permMgr - > sid = = sid ) {
this - > cachedPermissionManagers . erase ( std : : find ( this - > cachedPermissionManagers . begin ( ) , this - > cachedPermissionManagers . end ( ) , permMgr ) ) ;
delete permMgr ;
break ;
}
}
//TODO remove from props cache?
auto state = sql : : command ( this - > sql , " DELETE FROM `properties` WHERE `serverId` = :sid AND (`type` = :type1 OR `type` = :type2) AND `id` = :id " , variable { " :sid " , sid } , variable { " :type1 " , property : : PROP_TYPE_CONNECTION } , variable { " :type2 " , property : : PROP_TYPE_CLIENT } , variable { " :id " , cldbid } ) . execute ( ) ;
state = sql : : command ( this - > sql , " DELETE FROM `permissions` WHERE `serverId` = :sid AND `type` = :type AND `id` = :id " , variable { " :sid " , sid } , variable { " :type " , permission : : SQL_PERM_USER } , variable { " :id " , cldbid } ) . execute ( ) ;
state = sql : : command ( this - > sql , " DELETE FROM `clients` WHERE `serverId` = :sid AND `cldbid` = :id " , variable { " :sid " , sid } , variable { " :id " , cldbid } ) . execute ( ) ;
state = sql : : command ( this - > sql , " DELETE FROM `bannedClients` WHERE `serverId` = :sid AND `invokerDbid` = :id " , variable { " :sid " , sid } , variable { " :id " , cldbid } ) . execute ( ) ;
state = sql : : command ( this - > sql , " DELETE FROM `assignedGroups` WHERE `serverId` = :sid AND `cldbid` = :id " , variable { " :sid " , sid } , variable { " :id " , cldbid } ) . execute ( ) ;
//TODO delete letters
//TODO delete query
//TODO delete complains
}
2020-03-23 05:58:07 -04:00
inline sql : : result load_permissions_v2 (
const std : : shared_ptr < VirtualServer > & server ,
v2 : : PermissionManager * manager ,
sql : : command & command ,
bool test_channel , /* only used for client permissions (client channel permissions) */
bool is_channel ) {
2020-01-23 20:57:58 -05:00
auto start = system_clock : : now ( ) ;
auto server_id = server ? server - > getServerId ( ) : 0 ;
return command . query ( [ & ] ( int length , char * * values , char * * names ) {
permission : : PermissionType key = permission : : PermissionType : : undefined ;
permission : : PermissionValue value = permNotGranted , granted = permNotGranted ;
bool negated = false , skipped = false ;
2020-02-01 08:32:16 -05:00
ChannelId channel_id { 0 } ;
2020-01-23 20:57:58 -05:00
int index ;
2020-02-01 08:32:16 -05:00
2020-01-23 20:57:58 -05:00
try {
for ( index = 0 ; index < length ; index + + ) {
if ( strcmp ( names [ index ] , " permId " ) = = 0 ) {
key = permission : : resolvePermissionData ( values [ index ] ) - > type ;
if ( key = = permission : : unknown ) {
debugMessage ( server_id , " [SQL] Permission entry contains invalid permission type! Type: {} Command: {} " , values [ index ] , command . sqlCommand ( ) ) ;
return 0 ;
}
if ( key = = permission : : undefined ) {
debugMessage ( server_id , " [SQL] Permission entry contains undefined permission type! Type: {} Command: {} " , values [ index ] , command . sqlCommand ( ) ) ;
return 0 ;
}
} else if ( strcmp ( names [ index ] , " channelId " ) = = 0 ) {
2020-02-01 08:32:16 -05:00
channel_id = stoull ( values [ index ] ) ;
2020-01-23 20:57:58 -05:00
} else if ( strcmp ( names [ index ] , " value " ) = = 0 ) {
value = stoi ( values [ index ] ) ;
} else if ( strcmp ( names [ index ] , " grant " ) = = 0 ) {
granted = stoi ( values [ index ] ) ;
} else if ( strcmp ( names [ index ] , " flag_skip " ) = = 0 )
skipped = strcmp ( values [ index ] , " 1 " ) = = 0 ;
else if ( strcmp ( names [ index ] , " flag_negate " ) = = 0 )
negated = strcmp ( values [ index ] , " 1 " ) = = 0 ;
}
} catch ( std : : exception & ex ) {
logError ( server_id , " [SQL] Cant load permissions! Failed to parse value! Command: {} Message: {} Key: {} Value: {} " , command . sqlCommand ( ) , ex . what ( ) , names [ index ] , values [ index ] ) ;
return 0 ;
}
2020-02-01 08:32:16 -05:00
if ( channel_id > 0 & & test_channel ) {
if ( ! server )
logError ( server_id , " [SQL] Cant find channel for channel bound permission (No server given)! Command: {} ChannelID: {} " , command . sqlCommand ( ) , values [ index ] ) ;
else {
auto channel = server - > getChannelTree ( ) - > findChannel ( channel_id ) ;
if ( ! channel )
logError ( server_id , " [SQL] Cant find channel for channel bound permission! Command: {} ChannelID: {} " , command . sqlCommand ( ) , values [ index ] ) ;
}
}
2020-01-23 20:57:58 -05:00
if ( key = = permission : : undefined ) {
debugMessage ( server_id , " [SQL] Permission entry misses permission type! Command: {} " , command . sqlCommand ( ) ) ;
return 0 ;
}
2020-03-23 05:58:07 -04:00
if ( channel_id = = 0 | | is_channel )
2020-01-23 20:57:58 -05:00
manager - > load_permission ( key , { value , granted } , skipped , negated , value ! = permNotGranted , granted ! = permNotGranted ) ;
else
2020-02-01 08:32:16 -05:00
manager - > load_permission ( key , { value , granted } , channel_id , skipped , negated , value ! = permNotGranted , granted ! = permNotGranted ) ;
2020-01-23 20:57:58 -05:00
return 0 ;
} ) ;
auto end = system_clock : : now ( ) ;
auto time = end - start ;
logTrace ( server_id , " [SQL] load_permissions( \" {} \" ) took {}ms " , command . sqlCommand ( ) , duration_cast < milliseconds > ( time ) . count ( ) ) ;
2019-07-17 13:37:18 -04:00
}
# define UPDATE_COMMAND "UPDATE `permissions` SET `value` = :value, `grant` = :grant, `flag_skip` = :flag_skip, `flag_negate` = :flag_negate WHERE `serverId` = :serverId AND `type` = :type AND `id` = :id AND `permId` = :permId AND `channelId` = :chId"
# define INSERT_COMMAND "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)"
# define DELETE_COMMAND "DELETE FROM `permissions` WHERE `serverId` = :serverId AND `type` = :type AND `id` = :id AND `permId` = :permId AND `channelId` = :chId"
2020-01-26 12:04:38 -05:00
std : : shared_ptr < v2 : : PermissionManager > DatabaseHelper : : loadClientPermissionManager ( const std : : shared_ptr < VirtualServer > & server , ClientDbId cldbid ) {
2020-01-23 20:57:58 -05:00
auto server_id = server ? server - > getServerId ( ) : 0 ;
2019-07-17 13:37:18 -04:00
# ifndef DISABLE_CACHING
2020-01-23 20:57:58 -05:00
{
lock_guard < threads : : Mutex > lock ( permManagerLock ) ;
for ( auto permMgr : this - > cachedPermissionManagers )
if ( permMgr - > cldbid = = cldbid & & permMgr - > sid = = ( server ? server - > getServerId ( ) : 0 ) ) {
auto ptr = permMgr - > manager . lock ( ) ;
if ( ! ptr ) {
this - > cachedPermissionManagers . erase ( std : : find ( this - > cachedPermissionManagers . begin ( ) , this - > cachedPermissionManagers . end ( ) , permMgr ) ) ;
delete permMgr ;
break ;
}
permMgr - > lastAccess = system_clock : : now ( ) ;
return ptr ;
}
}
2019-07-17 13:37:18 -04:00
# endif
2020-01-23 20:57:58 -05:00
logTrace ( server_id , " [Permission] Loading client permission manager for client {} " , cldbid ) ;
auto permission_manager = std : : make_shared < v2 : : PermissionManager > ( ) ;
bool loaded = false ;
if ( this - > use_startup_cache & & server ) {
shared_ptr < StartupCacheEntry > entry ;
{
threads : : MutexLock lock ( this - > startup_lock ) ;
for ( const auto & entries : this - > startup_entries ) {
if ( entries - > sid = = server - > getServerId ( ) ) {
entry = entries ;
break ;
}
}
}
if ( entry ) {
for ( const auto & perm : entry - > permissions ) {
if ( perm - > type = = permission : : SQL_PERM_USER & & perm - > id = = cldbid ) {
auto channel = perm - > channelId > 0 ? server - > getChannelTree ( ) - > findChannel ( perm - > channelId ) : nullptr ;
if ( channel )
permission_manager - > load_permission ( perm - > permission - > type , { perm - > value , perm - > grant } , channel - > channelId ( ) , perm - > flag_skip , perm - > flag_negate , perm - > value ! = permNotGranted , perm - > grant ! = permNotGranted ) ;
else
permission_manager - > load_permission ( perm - > permission - > type , { perm - > value , perm - > grant } , perm - > flag_skip , perm - > flag_negate , perm - > value ! = permNotGranted , perm - > grant ! = permNotGranted ) ;
}
}
loaded = true ;
}
}
if ( ! loaded ) {
auto command = sql : : command ( this - > sql , " SELECT `permId`, `value`, `channelId`, `grant`, `flag_skip`, `flag_negate` FROM `permissions` WHERE `serverId` = :serverId AND `type` = :type AND `id` = :id " ,
variable { " :serverId " , server ? server - > getServerId ( ) : 0 } ,
variable { " :type " , permission : : SQL_PERM_USER } ,
variable { " :id " , cldbid } ) ;
2020-03-23 05:58:07 -04:00
LOG_SQL_CMD ( load_permissions_v2 ( server , permission_manager . get ( ) , command , true , false ) ) ;
2020-01-23 20:57:58 -05:00
}
2019-07-17 13:37:18 -04:00
# ifndef DISABLE_CACHING
2020-01-23 20:57:58 -05:00
this - > permManagerLock . lock ( ) ;
auto entry = new CachedPermissionManager ( ) ;
entry - > sid = server_id ;
entry - > manager = permission_manager ;
entry - > ownLock = permission_manager ;
entry - > cldbid = cldbid ;
entry - > lastAccess = system_clock : : now ( ) ;
this - > cachedPermissionManagers . push_back ( entry ) ;
this - > permManagerLock . unlock ( ) ;
2019-07-17 13:37:18 -04:00
# endif
2020-01-23 20:57:58 -05:00
return permission_manager ;
2019-07-17 13:37:18 -04:00
}
2020-01-26 12:04:38 -05:00
void DatabaseHelper : : saveClientPermissions ( const std : : shared_ptr < ts : : server : : VirtualServer > & server , ts : : ClientDbId client_dbid , const std : : shared_ptr < ts : : permission : : v2 : : PermissionManager > & permissions ) {
2020-01-23 20:57:58 -05:00
const auto updates = permissions - > flush_db_updates ( ) ;
if ( updates . empty ( ) )
return ;
auto server_id = server ? server - > getServerId ( ) : 0 ;
for ( auto & update : updates ) {
std : : string query = update . flag_delete ? DELETE_COMMAND : ( update . flag_db ? UPDATE_COMMAND : INSERT_COMMAND ) ;
auto permission_data = permission : : resolvePermissionData ( update . permission ) ;
auto value = update . update_value = = v2 : : delete_value ? permNotGranted : update . values . value ;
auto grant = update . update_grant = = v2 : : delete_value ? permNotGranted : update . values . grant ;
logTrace ( server_id , " [CHANNEL] Updating client permission for client {}: {}. New value: {}. New grant: {}. Query: {} " ,
client_dbid ,
permission_data - > name ,
update . values . value ,
update . values . grant ,
query
) ;
sql : : command ( this - > sql , query ,
variable { " :serverId " , server ? server - > getServerId ( ) : 0 } ,
variable { " :id " , client_dbid } ,
variable { " :chId " , update . channel_id } ,
variable { " :type " , permission : : SQL_PERM_USER } ,
variable { " :permId " , permission_data - > name } ,
variable { " :value " , value } ,
variable { " :grant " , grant } ,
variable { " :flag_skip " , update . flag_skip } ,
variable { " :flag_negate " , update . flag_negate } )
. executeLater ( ) . waitAndGetLater ( LOG_SQL_CMD , { - 1 , " future error " } ) ;
}
2019-07-17 13:37:18 -04:00
}
2020-01-26 12:04:38 -05:00
std : : shared_ptr < permission : : v2 : : PermissionManager > DatabaseHelper : : loadGroupPermissions ( const std : : shared_ptr < VirtualServer > & server , ts : : GroupId group_id ) {
2020-01-23 20:57:58 -05:00
auto result = std : : make_shared < v2 : : PermissionManager > ( ) ;
if ( this - > use_startup_cache & & server ) {
shared_ptr < StartupCacheEntry > entry ;
{
threads : : MutexLock lock ( this - > startup_lock ) ;
for ( const auto & entries : this - > startup_entries ) {
if ( entries - > sid = = server - > getServerId ( ) ) {
entry = entries ;
break ;
}
}
}
if ( entry ) {
for ( const auto & perm : entry - > permissions ) {
if ( perm - > type = = permission : : SQL_PERM_GROUP & & perm - > id = = group_id ) {
result - > load_permission ( perm - > permission - > type , { perm - > value , perm - > grant } , perm - > flag_skip , perm - > flag_negate , perm - > value ! = permNotGranted , perm - > grant ! = permNotGranted ) ;
}
}
return result ;
}
}
//7931
auto command = sql : : command ( this - > sql , " SELECT `channelId`, `permId`, `value`, `grant`, `flag_skip`, `flag_negate` FROM `permissions` WHERE `serverId` = :serverId AND `type` = :type AND `id` = :id " ,
variable { " :serverId " , server ? server - > getServerId ( ) : 0 } ,
variable { " :type " , permission : : SQL_PERM_GROUP } ,
variable { " :id " , group_id } ) ;
2020-03-23 05:58:07 -04:00
LOG_SQL_CMD ( load_permissions_v2 ( server , result . get ( ) , command , false , false ) ) ;
2019-07-17 13:37:18 -04:00
return result ;
}
2020-01-26 12:04:38 -05:00
void DatabaseHelper : : saveGroupPermissions ( const std : : shared_ptr < ts : : server : : VirtualServer > & server , ts : : GroupId group_id , const std : : shared_ptr < ts : : permission : : v2 : : PermissionManager > & permissions ) {
2020-01-23 20:57:58 -05:00
const auto updates = permissions - > flush_db_updates ( ) ;
if ( updates . empty ( ) )
return ;
auto server_id = server ? server - > getServerId ( ) : 0 ;
for ( auto & update : updates ) {
std : : string query = update . flag_delete ? DELETE_COMMAND : ( update . flag_db ? UPDATE_COMMAND : INSERT_COMMAND ) ;
auto permission_data = permission : : resolvePermissionData ( update . permission ) ;
auto value = update . update_value = = v2 : : delete_value ? permNotGranted : update . values . value ;
auto grant = update . update_grant = = v2 : : delete_value ? permNotGranted : update . values . grant ;
logTrace ( server_id , " [CHANNEL] Updating group permission for group {}: {}. New value: {}. New grant: {}. Query: {} " ,
group_id ,
permission_data - > name ,
value ,
grant ,
query
) ;
sql : : command ( this - > sql , query ,
variable { " :serverId " , server ? server - > getServerId ( ) : 0 } ,
variable { " :id " , group_id } ,
variable { " :chId " , 0 } ,
variable { " :type " , permission : : SQL_PERM_GROUP } ,
variable { " :permId " , permission_data - > name } ,
variable { " :value " , value } ,
variable { " :grant " , grant } ,
variable { " :flag_skip " , update . flag_skip } ,
variable { " :flag_negate " , update . flag_negate } )
. executeLater ( ) . waitAndGetLater ( LOG_SQL_CMD , { - 1 , " future error " } ) ;
}
2019-07-17 13:37:18 -04:00
}
2020-02-01 08:32:16 -05:00
std : : shared_ptr < permission : : v2 : : PermissionManager > DatabaseHelper : : loadPlaylistPermissions ( const std : : shared_ptr < ts : : server : : VirtualServer > & server , ts : : PlaylistId playlist_id ) {
shared_ptr < permission : : v2 : : PermissionManager > result ;
2020-01-23 20:57:58 -05:00
if ( this - > use_startup_cache & & server ) {
shared_ptr < StartupCacheEntry > entry ;
{
threads : : MutexLock lock ( this - > startup_lock ) ;
for ( const auto & entries : this - > startup_entries ) {
if ( entries - > sid = = server - > getServerId ( ) ) {
entry = entries ;
break ;
}
}
}
if ( entry ) {
2020-02-01 08:32:16 -05:00
result = std : : make_shared < permission : : v2 : : PermissionManager > ( ) ;
2020-01-23 20:57:58 -05:00
for ( const auto & perm : entry - > permissions ) {
if ( perm - > type = = permission : : SQL_PERM_PLAYLIST & & perm - > id = = playlist_id ) {
2020-02-21 14:32:25 -05:00
if ( perm - > channelId )
result - > load_permission ( perm - > permission - > type , { perm - > value , perm - > grant } , perm - > channelId , perm - > flag_skip , perm - > flag_negate , perm - > value ! = permNotGranted , perm - > grant ! = permNotGranted ) ;
else
result - > load_permission ( perm - > permission - > type , { perm - > value , perm - > grant } , perm - > flag_skip , perm - > flag_negate , perm - > value ! = permNotGranted , perm - > grant ! = permNotGranted ) ;
2020-01-23 20:57:58 -05:00
}
}
}
2020-02-01 08:32:16 -05:00
return result ;
2020-01-23 20:57:58 -05:00
}
2020-02-01 08:32:16 -05:00
result = std : : make_shared < permission : : v2 : : PermissionManager > ( ) ;
auto command = sql : : command ( this - > sql , " SELECT `channelId`, `permId`, `value`, `grant`, `flag_skip`, `flag_negate` FROM `permissions` WHERE `serverId` = :serverId AND `type` = :type AND `id` = :id " ,
variable { " :serverId " , server ? server - > getServerId ( ) : 0 } ,
variable { " :type " , permission : : SQL_PERM_PLAYLIST } ,
variable { " :id " , playlist_id } ) ;
2020-03-23 05:58:07 -04:00
LOG_SQL_CMD ( load_permissions_v2 ( server , result . get ( ) , command , false , false ) ) ;
2020-02-01 08:32:16 -05:00
return result ;
}
2020-01-23 20:57:58 -05:00
2020-02-01 08:32:16 -05:00
void DatabaseHelper : : savePlaylistPermissions ( const std : : shared_ptr < VirtualServer > & server , PlaylistId pid , const std : : shared_ptr < permission : : v2 : : PermissionManager > & permissions ) {
const auto updates = permissions - > flush_db_updates ( ) ;
if ( updates . empty ( ) )
return ;
2020-01-23 20:57:58 -05:00
2020-02-01 08:32:16 -05:00
auto server_id = server ? server - > getServerId ( ) : 0 ;
for ( auto & update : updates ) {
std : : string query = update . flag_delete ? DELETE_COMMAND : ( update . flag_db ? UPDATE_COMMAND : INSERT_COMMAND ) ;
2020-01-23 20:57:58 -05:00
2020-02-01 08:32:16 -05:00
auto permission_data = permission : : resolvePermissionData ( update . permission ) ;
auto value = update . update_value = = v2 : : delete_value ? permNotGranted : update . values . value ;
auto grant = update . update_grant = = v2 : : delete_value ? permNotGranted : update . values . grant ;
logTrace ( server_id , " [PLAYLIST] Updating playlist permission for playlist {}: {}. New value: {}. New grant: {}. Query: {} " ,
pid ,
permission_data - > name ,
value ,
grant ,
query
) ;
2020-01-23 20:57:58 -05:00
sql : : command ( this - > sql , query ,
variable { " :serverId " , server ? server - > getServerId ( ) : 0 } ,
2020-02-01 08:32:16 -05:00
variable { " :id " , pid } ,
2020-02-02 08:58:46 -05:00
variable { " :chId " , update . channel_id } ,
2020-01-23 20:57:58 -05:00
variable { " :type " , permission : : SQL_PERM_PLAYLIST } ,
2020-02-01 08:32:16 -05:00
variable { " :permId " , permission_data - > name } ,
variable { " :value " , value } ,
variable { " :grant " , grant } ,
variable { " :flag_skip " , update . flag_skip } ,
variable { " :flag_negate " , update . flag_negate } )
. executeLater ( ) . waitAndGetLater ( LOG_SQL_CMD , { - 1 , " future error " } ) ;
}
2019-07-17 13:37:18 -04:00
}
2020-01-26 12:04:38 -05:00
std : : shared_ptr < permission : : v2 : : PermissionManager > DatabaseHelper : : loadChannelPermissions ( const std : : shared_ptr < VirtualServer > & server , ts : : ChannelId channel ) {
2020-01-23 20:57:58 -05:00
auto result = std : : make_shared < v2 : : PermissionManager > ( ) ;
if ( this - > use_startup_cache & & server ) {
shared_ptr < StartupCacheEntry > entry ;
{
threads : : MutexLock lock ( this - > startup_lock ) ;
for ( const auto & entries : this - > startup_entries ) {
if ( entries - > sid = = server - > getServerId ( ) ) {
entry = entries ;
break ;
}
}
}
if ( entry ) {
for ( const auto & perm : entry - > permissions ) {
if ( perm - > type = = permission : : SQL_PERM_CHANNEL & & perm - > channelId = = channel ) {
result - > load_permission ( perm - > permission - > type , { perm - > value , perm - > grant } , perm - > flag_skip , perm - > flag_negate , perm - > value ! = permNotGranted , perm - > grant ! = permNotGranted ) ;
}
}
return result ;
}
}
auto command = sql : : command ( sql , " SELECT `permId`, `value`, `grant`, `flag_skip`, `flag_negate` FROM `permissions` WHERE `serverId` = :serverId AND `channelId` = :chid AND `type` = :type AND `id` = :id " ,
variable { " :serverId " , server ? server - > getServerId ( ) : 0 } ,
variable { " :chid " , channel } ,
variable { " :id " , 0 } ,
variable { " :type " , permission : : SQL_PERM_CHANNEL } ) ;
2020-03-23 05:58:07 -04:00
LOG_SQL_CMD ( load_permissions_v2 ( server , result . get ( ) , command , false , true ) ) ;
2020-01-23 20:57:58 -05:00
return result ;
2019-07-17 13:37:18 -04:00
}
2020-01-26 12:04:38 -05:00
void DatabaseHelper : : saveChannelPermissions ( const std : : shared_ptr < ts : : server : : VirtualServer > & server , ts : : ChannelId channel_id , const std : : shared_ptr < ts : : permission : : v2 : : PermissionManager > & permissions ) {
2020-01-23 20:57:58 -05:00
const auto updates = permissions - > flush_db_updates ( ) ;
if ( updates . empty ( ) )
return ;
auto server_id = server ? server - > getServerId ( ) : 0 ;
for ( auto & update : updates ) {
std : : string query = update . flag_delete ? DELETE_COMMAND : ( update . flag_db ? UPDATE_COMMAND : INSERT_COMMAND ) ;
auto value = update . update_value = = v2 : : delete_value ? permNotGranted : update . values . value ;
auto grant = update . update_grant = = v2 : : delete_value ? permNotGranted : update . values . grant ;
auto permission_data = permission : : resolvePermissionData ( update . permission ) ;
logTrace ( server_id , " [CHANNEL] Updating channel permission for channel {}: {}. New value: {}. New grant: {}. Query: {} " ,
channel_id ,
permission_data - > name ,
value ,
grant ,
query
) ;
sql : : command ( this - > sql , query ,
variable { " :serverId " , server ? server - > getServerId ( ) : 0 } ,
variable { " :id " , 0 } ,
variable { " :chId " , channel_id } ,
variable { " :type " , permission : : SQL_PERM_CHANNEL } ,
variable { " :permId " , permission_data - > name } ,
variable { " :value " , value } ,
variable { " :grant " , grant } ,
variable { " :flag_skip " , update . flag_skip } ,
variable { " :flag_negate " , update . flag_negate } )
. executeLater ( ) . waitAndGetLater ( LOG_SQL_CMD , { - 1 , " future error " } ) ;
}
2019-07-17 13:37:18 -04:00
}
2019-10-16 15:11:54 -04:00
std : : shared_ptr < Properties > DatabaseHelper : : default_properties_client ( std : : shared_ptr < Properties > properties , ClientType type ) {
2020-01-23 20:57:58 -05:00
if ( ! properties )
properties = make_shared < Properties > ( ) ;
2019-10-16 15:11:54 -04:00
2020-01-23 20:57:58 -05:00
properties - > register_property_type < property : : ClientProperties > ( ) ;
properties - > register_property_type < property : : ConnectionProperties > ( ) ;
2019-10-16 15:11:54 -04:00
2020-01-23 20:57:58 -05:00
if ( type = = ClientType : : CLIENT_MUSIC | | type = = ClientType : : CLIENT_QUERY ) {
( * properties ) [ property : : CLIENT_INPUT_HARDWARE ] = true ;
( * properties ) [ property : : CLIENT_OUTPUT_HARDWARE ] = true ;
}
2019-10-16 15:11:54 -04:00
2020-01-23 20:57:58 -05:00
return properties ;
2019-07-17 13:37:18 -04:00
}
2020-04-16 08:05:58 -04:00
std : : mutex DatabaseHelper : : database_id_mutex { } ;
2019-07-17 13:37:18 -04:00
bool DatabaseHelper : : assignDatabaseId ( sql : : SqlManager * sql , ServerId id , std : : shared_ptr < DataClient > cl ) {
cl - > loadDataForCurrentServer ( ) ;
if ( cl - > getClientDatabaseId ( ) = = 0 ) { //Client does not exist
2020-04-16 08:05:58 -04:00
ClientDbId new_client_database_id { 0 } ;
2019-07-17 13:37:18 -04:00
auto res = sql : : command ( sql , " SELECT `cldbid` FROM `clients` WHERE `serverId` = 0 AND `clientUid` = :cluid " , variable { " :cluid " , cl - > getUid ( ) } ) . query ( [ ] ( ClientDbId * ptr , int length , char * * values , char * * names ) {
* ptr = static_cast < ClientDbId > ( stoll ( values [ 0 ] ) ) ;
return 0 ;
2020-04-16 08:05:58 -04:00
} , & new_client_database_id ) ;
2019-07-17 13:37:18 -04:00
auto pf = LOG_SQL_CMD ;
pf ( res ) ;
if ( ! res ) return false ;
auto insertTemplate = sql : : model ( sql , " INSERT INTO `clients` (`serverId`, `cldbId`, `clientUid`, `lastName`,`firstConnect`,`lastConnect`, `connections`) VALUES (:serverId, :cldbid, :cluid, :name, :fconnect, :lconnect, :connections) " ,
2020-04-14 05:49:07 -04:00
variable { " :cluid " , cl - > getUid ( ) } , variable { " :name " , cl - > getDisplayName ( ) } ,
variable { " :fconnect " , duration_cast < seconds > ( system_clock : : now ( ) . time_since_epoch ( ) ) . count ( ) } , variable { " :lconnect " , 0 } ,
variable { " :connections " , 0 } ) ;
2020-04-16 08:05:58 -04:00
if ( new_client_database_id = = 0 ) { /* we've a completely new user */
std : : lock_guard db_id_lock { DatabaseHelper : : database_id_mutex } ;
res = sql : : command ( sql , " SELECT `cldbid` FROM `clients` WHERE `serverId` = 0 ORDER BY `cldbid` DESC LIMIT 1 " ) . query ( [ & ] ( int length , std : : string * values , std : : string * names ) {
assert ( length = = 1 ) ;
new_client_database_id = ( ClientDbId ) stoll ( values [ 0 ] ) ;
} ) ;
LOG_SQL_CMD ( res ) ;
2019-07-17 13:37:18 -04:00
if ( ! res ) return false ;
2020-04-16 08:05:58 -04:00
new_client_database_id + = 1 ;
res = insertTemplate . command ( ) . values ( variable { " :serverId " , 0 } , variable { " :cldbid " , new_client_database_id } ) . execute ( ) ; //Insert global
LOG_SQL_CMD ( res ) ;
2019-07-17 13:37:18 -04:00
if ( ! res ) return false ;
2020-04-16 08:05:58 -04:00
debugMessage ( LOG_INSTANCE , " Registered a new client. Unique id: {}, First server: {}, Database ID: {} " , cl - > getUid ( ) , id , new_client_database_id ) ;
2019-07-17 13:37:18 -04:00
} else {
2020-04-16 08:05:58 -04:00
debugMessage ( id , " Having new client, which is already known on this instance. Unique id: {}, First server: {}, Database ID: {} " , cl - > getUid ( ) , id , new_client_database_id ) ;
2019-07-17 13:37:18 -04:00
}
if ( id ! = 0 ) { //Else already inserted
2020-04-16 08:05:58 -04:00
res = insertTemplate . command ( ) . values ( variable { " :serverId " , id } , variable { " :cldbid " , new_client_database_id } ) . execute ( ) ;
2019-07-17 13:37:18 -04:00
pf ( res ) ;
if ( ! res ) return false ;
}
return assignDatabaseId ( sql , id , cl ) ;
}
2020-04-16 08:05:58 -04:00
logTrace ( id , " Loaded client successfully from database. Database id: {} Unique id: {} " , cl - > getClientDatabaseId ( ) , cl - > getUid ( ) ) ;
2019-07-17 13:37:18 -04:00
return true ;
}
inline sql : : result load_properties ( ServerId sid , deque < unique_ptr < FastPropertyEntry > > & properties , sql : : command & command ) {
2020-01-23 20:57:58 -05:00
auto start = system_clock : : now ( ) ;
2019-07-17 13:37:18 -04:00
auto result = command . query ( [ & ] ( int length , string * values , string * names ) {
string key , value ;
property : : PropertyType type = property : : PROP_TYPE_UNKNOWN ;
for ( int index = 0 ; index < length ; index + + ) {
2020-01-23 20:57:58 -05:00
try {
if ( names [ index ] = = " key " ) key = values [ index ] ;
else if ( names [ index ] = = " value " ) value = values [ index ] ;
else if ( names [ index ] = = " type " ) type = ( property : : PropertyType ) stoll ( values [ index ] ) ;
} catch ( const std : : exception & ex ) {
logError ( sid , " Failed to load parse property \" {} \" . key: {}, value: {}, message: {} " , key , names [ index ] , values [ index ] , ex . what ( ) ) ;
return 0 ;
}
2019-07-17 13:37:18 -04:00
}
2020-04-08 07:01:41 -04:00
const auto & info = property : : find ( type , key ) ;
if ( info . name = = " undefined " ) {
2019-07-17 13:37:18 -04:00
logError ( sid , " Found unknown property in database! ({}) " , key ) ;
return 0 ;
}
/*
auto prop = properties - > operator [ ] ( info ) ;
prop = value ;
prop . setModified ( true ) ;
prop . setDbReference ( true ) ;
*/
2020-04-08 07:01:41 -04:00
auto data = std : : make_unique < FastPropertyEntry > ( ) ;
data - > type = & info ;
2019-07-17 13:37:18 -04:00
data - > value = value ;
2020-01-23 20:57:58 -05:00
properties . push_back ( move ( data ) ) ;
2019-07-17 13:37:18 -04:00
return 0 ;
} ) ;
auto end = system_clock : : now ( ) ;
auto time = end - start ;
2020-01-23 20:57:58 -05:00
logTrace ( sid , " [SQL] load_properties( \" {} \" ) needs {}ms " , command . sqlCommand ( ) , duration_cast < milliseconds > ( time ) . count ( ) ) ;
2019-07-17 13:37:18 -04:00
return result ;
}
2020-01-26 12:04:38 -05:00
std : : shared_ptr < Properties > DatabaseHelper : : loadServerProperties ( const std : : shared_ptr < ts : : server : : VirtualServer > & server ) {
2020-01-23 20:57:58 -05:00
auto props = std : : make_shared < Properties > ( ) ;
props - > register_property_type < property : : VirtualServerProperties > ( ) ;
( * props ) [ property : : VIRTUALSERVER_HOST ] = config : : binding : : DefaultVoiceHost ;
( * props ) [ property : : VIRTUALSERVER_WEB_HOST ] = config : : binding : : DefaultWebHost ;
bool loaded = false ;
if ( use_startup_cache & & server ) {
shared_ptr < StartupCacheEntry > entry ;
{
threads : : MutexLock lock ( this - > startup_lock ) ;
for ( const auto & entries : this - > startup_entries ) {
if ( entries - > sid = = server - > getServerId ( ) ) {
entry = entries ;
break ;
}
}
}
if ( entry ) {
for ( const auto & prop : entry - > properties ) {
if ( prop - > type = = property : : PROP_TYPE_SERVER & & prop - > id = = 0 ) {
auto p = ( * props ) [ prop - > info ] ;
p = prop - > value ;
p . setModified ( true ) ;
p . setDbReference ( true ) ;
}
}
loaded = true ;
}
}
if ( ! loaded ) {
auto command = sql : : command ( this - > sql , " SELECT `key`, `value`, `type` FROM properties WHERE `serverId` = :serverId AND `type` = :type " , variable { " :serverId " , server ? server - > getServerId ( ) : 0 } , variable { " :type " , property : : PropertyType : : PROP_TYPE_SERVER } ) ;
deque < unique_ptr < FastPropertyEntry > > property_list ;
LOG_SQL_CMD ( load_properties ( server ? server - > getServerId ( ) : 0 , property_list , command ) ) ;
for ( const auto & entry : property_list ) {
auto prop = props - > operator [ ] ( entry - > type ) ;
prop = entry - > value ;
prop . setModified ( true ) ;
prop . setDbReference ( true ) ;
}
}
2020-01-26 12:04:38 -05:00
weak_ptr < VirtualServer > weak = server ;
2020-01-23 20:57:58 -05:00
ServerId serverId = server ? server - > getServerId ( ) : 0 ;
props - > registerNotifyHandler ( [ & , serverId , weak ] ( Property & prop ) {
if ( ( prop . type ( ) . flags & property : : FLAG_SAVE ) = = 0 ) {
prop . setModified ( false ) ;
return ;
}
auto weak_server = weak . lock ( ) ;
if ( ! weak_server & & serverId ! = 0 ) return ;
string sql ;
if ( prop . hasDbReference ( ) )
sql = " UPDATE `properties` SET `value` = :value WHERE `serverId` = :sid AND `type` = :type AND `id` = :id AND `key` = :key " ;
else {
prop . setDbReference ( true ) ;
sql = " INSERT INTO `properties` (`serverId`, `type`, `id`, `key`, `value`) VALUES (:sid, :type, :id, :key, :value) " ;
}
2020-04-08 07:01:41 -04:00
logTrace ( serverId , " Updating server property: " + std : : string { prop . type ( ) . name } + " . New value: " + prop . value ( ) + " . Query: " + sql ) ;
2020-01-23 20:57:58 -05:00
sql : : command ( this - > sql , sql ,
variable { " :sid " , serverId } ,
variable { " :type " , property : : PropertyType : : PROP_TYPE_SERVER } ,
variable { " :id " , 0 } ,
variable { " :key " , prop . type ( ) . name } ,
variable { " :value " , prop . value ( ) }
) . executeLater ( ) . waitAndGetLater ( LOG_SQL_CMD , sql : : result { 1 , " future failed " } ) ;
} ) ;
return props ;
2019-07-17 13:37:18 -04:00
}
2020-01-26 12:04:38 -05:00
std : : shared_ptr < Properties > DatabaseHelper : : loadPlaylistProperties ( const std : : shared_ptr < ts : : server : : VirtualServer > & server , PlaylistId id ) {
2019-07-17 13:37:18 -04:00
auto props = std : : make_shared < Properties > ( ) ;
2020-01-23 20:57:58 -05:00
props - > register_property_type < property : : PlaylistProperties > ( ) ;
( * props ) [ property : : PLAYLIST_ID ] = id ;
2019-07-17 13:37:18 -04:00
bool loaded = false ;
if ( use_startup_cache & & server ) {
shared_ptr < StartupCacheEntry > entry ;
{
threads : : MutexLock lock ( this - > startup_lock ) ;
for ( const auto & entries : this - > startup_entries ) {
if ( entries - > sid = = server - > getServerId ( ) ) {
entry = entries ;
break ;
}
}
}
if ( entry ) {
for ( const auto & prop : entry - > properties ) {
if ( prop - > type = = property : : PROP_TYPE_PLAYLIST & & prop - > id = = id ) {
auto p = ( * props ) [ prop - > info ] ;
p = prop - > value ;
p . setModified ( true ) ;
p . setDbReference ( true ) ;
}
}
loaded = true ;
}
}
if ( ! loaded ) {
2020-01-23 20:57:58 -05:00
auto command = sql : : command ( this - > sql , " SELECT `key`, `value`, `type` FROM properties WHERE `serverId` = :serverId AND `type` = :type AND `id` = :id " , variable { " :serverId " , server ? server - > getServerId ( ) : 0 } , variable { " :type " , property : : PropertyType : : PROP_TYPE_PLAYLIST } , variable { " :id " , id } ) ;
deque < unique_ptr < FastPropertyEntry > > property_list ;
LOG_SQL_CMD ( load_properties ( server ? server - > getServerId ( ) : 0 , property_list , command ) ) ;
for ( const auto & entry : property_list ) {
auto prop = props - > operator [ ] ( entry - > type ) ;
prop = entry - > value ;
prop . setModified ( true ) ;
prop . setDbReference ( true ) ;
}
2019-07-17 13:37:18 -04:00
}
2020-01-26 12:04:38 -05:00
weak_ptr < VirtualServer > weak = server ;
2019-07-17 13:37:18 -04:00
ServerId serverId = server ? server - > getServerId ( ) : 0 ;
props - > registerNotifyHandler ( [ & , serverId , weak , id ] ( Property & prop ) {
if ( ( prop . type ( ) . flags & property : : FLAG_SAVE ) = = 0 ) {
prop . setModified ( false ) ;
return ;
}
auto weak_server = weak . lock ( ) ;
if ( ! weak_server & & serverId ! = 0 ) return ;
string sql ;
if ( prop . hasDbReference ( ) )
sql = " UPDATE `properties` SET `value` = :value WHERE `serverId` = :sid AND `type` = :type AND `id` = :id AND `key` = :key " ;
else {
prop . setDbReference ( true ) ;
sql = " INSERT INTO `properties` (`serverId`, `type`, `id`, `key`, `value`) VALUES (:sid, :type, :id, :key, :value) " ;
}
2020-01-23 20:57:58 -05:00
logTrace ( serverId , " Updating playlist property for {}. Key: {} Value: {} " , id , prop . type ( ) . name , prop . value ( ) ) ;
2019-07-17 13:37:18 -04:00
sql : : command ( this - > sql , sql ,
variable { " :sid " , serverId } ,
variable { " :type " , property : : PropertyType : : PROP_TYPE_PLAYLIST } ,
variable { " :id " , id } ,
variable { " :key " , prop . type ( ) . name } ,
variable { " :value " , prop . value ( ) }
) . executeLater ( ) . waitAndGetLater ( LOG_SQL_CMD , sql : : result { 1 , " future failed " } ) ;
} ) ;
return props ;
}
2020-01-26 12:04:38 -05:00
std : : shared_ptr < Properties > DatabaseHelper : : loadChannelProperties ( const shared_ptr < VirtualServer > & server , ChannelId channel ) {
2019-07-17 13:37:18 -04:00
ServerId serverId = server ? server - > getServerId ( ) : 0U ;
auto props = std : : make_shared < Properties > ( ) ;
2020-01-23 20:57:58 -05:00
props - > register_property_type < property : : ChannelProperties > ( ) ;
2019-07-17 13:37:18 -04:00
if ( server ) {
props - > operator [ ] ( property : : CHANNEL_TOPIC ) = server - > properties ( ) [ property : : VIRTUALSERVER_DEFAULT_CHANNEL_TOPIC ] . value ( ) ;
props - > operator [ ] ( property : : CHANNEL_DESCRIPTION ) = server - > properties ( ) [ property : : VIRTUALSERVER_DEFAULT_CHANNEL_DESCRIPTION ] . value ( ) ;
}
bool loaded = false ;
if ( use_startup_cache & & server ) {
shared_ptr < StartupCacheEntry > entry ;
{
threads : : MutexLock lock ( this - > startup_lock ) ;
for ( const auto & entries : this - > startup_entries ) {
if ( entries - > sid = = server - > getServerId ( ) ) {
entry = entries ;
break ;
}
}
}
if ( entry ) {
for ( const auto & prop : entry - > properties ) {
if ( prop - > type = = property : : PROP_TYPE_CHANNEL & & prop - > id = = channel ) {
auto p = ( * props ) [ prop - > info ] ;
p = prop - > value ;
p . setModified ( true ) ;
p . setDbReference ( true ) ;
}
}
loaded = true ;
}
}
if ( ! loaded ) {
auto command = sql : : command ( this - > sql , " SELECT `key`, `value`, `type` FROM properties WHERE `serverId` = :serverId AND `type` = :type AND `id` = :id " , variable { " :serverId " , serverId } , variable { " :type " , property : : PropertyType : : PROP_TYPE_CHANNEL } , variable { " :id " , channel } ) ;
2020-01-23 20:57:58 -05:00
deque < unique_ptr < FastPropertyEntry > > property_list ;
2019-07-17 13:37:18 -04:00
LOG_SQL_CMD ( load_properties ( serverId , property_list , command ) ) ;
for ( const auto & entry : property_list ) {
2020-01-23 20:57:58 -05:00
auto prop = props - > operator [ ] ( entry - > type ) ;
prop = entry - > value ;
prop . setModified ( true ) ;
prop . setDbReference ( true ) ;
2019-07-17 13:37:18 -04:00
}
}
2020-01-26 12:04:38 -05:00
weak_ptr < VirtualServer > weak = server ;
2019-07-17 13:37:18 -04:00
props - > registerNotifyHandler ( [ & , weak , serverId , channel ] ( Property & prop ) {
auto weak_server = weak . lock ( ) ;
if ( ! weak_server & & serverId ! = 0 )
2020-01-23 20:57:58 -05:00
return ;
2019-07-17 13:37:18 -04:00
if ( ( prop . type ( ) . flags & property : : FLAG_SAVE ) = = 0 )
2020-01-23 20:57:58 -05:00
return ;
2019-07-17 13:37:18 -04:00
if ( ! prop . isModified ( ) )
2020-01-23 20:57:58 -05:00
return ;
2019-07-17 13:37:18 -04:00
std : : string query ;
if ( prop . type ( ) = = property : : CHANNEL_PID ) {
query = " UPDATE `channels` SET `parentId` = :value WHERE `serverId` = :serverId AND `channelId` = :id " ;
} else if ( ! prop . hasDbReference ( ) ) {
query = " INSERT INTO `properties` (`serverId`, `type`, `id`, `key`, `value`) VALUES (:serverId, :type, :id, :key, :value) " ;
} else {
query = " UPDATE `properties` SET `value` = :value WHERE `serverId` = :serverId AND `id` = :id AND `key` = :key AND `type` = :type " ;
}
2020-01-23 20:57:58 -05:00
logTrace ( serverId , " [CHANNEL] Updating channel property for channel {}: {}. New value: '{}'. Query: {} " , channel , prop . type ( ) . name , prop . value ( ) , query ) ;
2019-07-17 13:37:18 -04:00
sql : : command ( this - > sql , query ,
variable { " :serverId " , serverId } ,
variable { " :id " , channel } ,
variable { " :type " , property : : PropertyType : : PROP_TYPE_CHANNEL } ,
variable { " :key " , prop . type ( ) . name } ,
variable { " :value " , prop . value ( ) }
) . executeLater ( ) . waitAndGetLater ( LOG_SQL_CMD , { - 1 , " future error " } ) ;
prop . setModified ( false ) ;
prop . setDbReference ( true ) ;
} ) ;
return props ;
}
2020-01-26 12:04:38 -05:00
std : : shared_ptr < Properties > DatabaseHelper : : loadClientProperties ( const std : : shared_ptr < VirtualServer > & server , ClientDbId cldbid , ClientType type ) {
2019-10-16 15:11:54 -04:00
auto props = DatabaseHelper : : default_properties_client ( nullptr , type ) ;
2019-07-17 13:37:18 -04:00
if ( server ) {
props - > operator [ ] ( property : : CLIENT_DESCRIPTION ) = server - > properties ( ) [ property : : VIRTUALSERVER_DEFAULT_CLIENT_DESCRIPTION ] . value ( ) ;
}
bool loaded = false ;
if ( use_startup_cache & & server ) {
shared_ptr < StartupCacheEntry > entry ;
{
threads : : MutexLock lock ( this - > startup_lock ) ;
for ( const auto & entries : this - > startup_entries ) {
if ( entries - > sid = = server - > getServerId ( ) ) {
entry = entries ;
break ;
}
}
}
if ( entry ) {
for ( const auto & prop : entry - > properties ) {
if ( prop - > id = = cldbid & & ( prop - > type = = property : : PROP_TYPE_CLIENT | | prop - > type = = property : : PROP_TYPE_CONNECTION ) ) {
auto p = ( * props ) [ prop - > info ] ;
p = prop - > value ;
p . setModified ( true ) ;
p . setDbReference ( true ) ;
}
}
loaded = true ;
}
}
if ( ! loaded ) {
auto command = sql : : command ( this - > sql , " SELECT `key`, `value`, `type` FROM properties WHERE `serverId` = :serverId AND (`type` = :type1 OR `type` = :type2) AND `id` = :id " , variable { " :serverId " , server ? server - > getServerId ( ) : 0 } , variable { " :type1 " , property : : PropertyType : : PROP_TYPE_CONNECTION } , variable { " :type2 " , property : : PropertyType : : PROP_TYPE_CLIENT } , variable { " :id " , cldbid } ) ;
2020-01-23 20:57:58 -05:00
deque < unique_ptr < FastPropertyEntry > > property_list ;
LOG_SQL_CMD ( load_properties ( server ? server - > getServerId ( ) : 0 , property_list , command ) ) ;
for ( const auto & entry : property_list ) {
auto prop = props - > operator [ ] ( entry - > type ) ;
prop = entry - > value ;
prop . setModified ( true ) ;
prop . setDbReference ( true ) ;
}
2019-07-17 13:37:18 -04:00
}
2020-01-26 12:04:38 -05:00
weak_ptr < VirtualServer > weak_server = server ;
2020-01-23 20:57:58 -05:00
auto server_id = server ? server - > getServerId ( ) : 0 ;
2019-08-16 10:13:52 -04:00
props - > registerNotifyHandler ( [ & , weak_server , server_id , cldbid , type ] ( Property & prop ) { //General save
2020-01-23 20:57:58 -05:00
auto server = weak_server . lock ( ) ;
if ( ! server & & server_id ! = 0 ) {
logError ( server_id , " Tried to update client permissions of a expired server! " ) ;
return ;
}
2019-07-17 13:37:18 -04:00
if ( ! prop . isModified ( ) ) return ;
2019-07-23 04:37:56 -04:00
if ( ( prop . type ( ) . flags & property : : FLAG_SAVE ) = = 0 & & ( type ! = ClientType : : CLIENT_MUSIC | | ( prop . type ( ) . flags & property : : FLAG_SAVE_MUSIC ) = = 0 ) ) {
2020-04-08 07:01:41 -04:00
logTrace ( server ? server - > getServerId ( ) : 0 , " [Property] Not saving property ' " + std : : string { prop . type ( ) . name } + " ', changed for " + to_string ( cldbid ) + " (New value: " + prop . value ( ) + " ) " ) ;
2019-07-17 13:37:18 -04:00
return ;
}
if ( ! prop . get_handle ( ) ) return ;
if ( ! prop . get_handle ( ) - > isSaveEnabled ( ) ) return ;
if ( ! prop . hasDbReference ( ) & & ( prop . default_value ( ) = = prop . value ( ) ) ) return ; //No changes to default value
prop . setModified ( false ) ;
std : : string sql ;
if ( prop . hasDbReference ( ) )
sql = " UPDATE `properties` SET `value` = :value WHERE `serverId` = :serverId AND `type` = :type AND `id` = :id AND `key` = :key " ;
else {
prop . setDbReference ( true ) ;
sql = " INSERT INTO `properties` (`serverId`, `type`, `id`, `key`, `value`) VALUES (:serverId, :type, :id, :key, :value) " ;
}
2020-04-08 07:01:41 -04:00
logTrace ( server ? server - > getServerId ( ) : 0 , " [Property] Changed property in db key: " + std : : string { prop . type ( ) . name } + " value: " + prop . value ( ) ) ;
2019-07-17 13:37:18 -04:00
sql : : command ( this - > sql , sql ,
2020-01-23 20:57:58 -05:00
variable { " :serverId " , server ? server - > getServerId ( ) : 0 } ,
variable { " :type " , prop . type ( ) . type_property } ,
variable { " :id " , cldbid } ,
variable { " :key " , prop . type ( ) . name } ,
variable { " :value " , prop . value ( ) }
2019-07-17 13:37:18 -04:00
) . executeLater ( ) . waitAndGetLater ( LOG_SQL_CMD , sql : : result { 1 , " future failed " } ) ;
} ) ;
props - > registerNotifyHandler ( [ & , weak_server , server_id , cldbid ] ( Property & prop ) {
2020-01-23 20:57:58 -05:00
auto server = weak_server . lock ( ) ;
if ( ! server & & server_id ! = 0 ) {
logError ( server_id , " Tried to update client permissions of a expired server! " ) ;
return ;
}
2019-07-17 13:37:18 -04:00
2020-01-23 20:57:58 -05:00
std : : string query ;
2019-07-17 13:37:18 -04:00
if ( prop . type ( ) = = property : : CLIENT_TOTALCONNECTIONS )
query = " UPDATE `clients` SET `connections` = :value WHERE `serverId` = :serverId AND `cldbid` = :cldbid " ;
else if ( prop . type ( ) = = property : : CLIENT_NICKNAME )
query = " UPDATE `clients` SET `lastName` = :value WHERE `serverId` = :serverId AND `cldbid` = :cldbid " ;
else if ( prop . type ( ) = = property : : CLIENT_LASTCONNECTED )
query = " UPDATE `clients` SET `lastConnect` = :value WHERE `serverId` = :serverId AND `cldbid` = :cldbid " ;
if ( query . empty ( ) ) return ;
2020-04-08 07:01:41 -04:00
debugMessage ( server ? server - > getServerId ( ) : 0 , " [Property] Changing client property ' " + std : : string { prop . type ( ) . name } + " ' for " + to_string ( cldbid ) + " (New value: " + prop . value ( ) + " , SQL: " + query + " ) " ) ;
2019-07-17 13:37:18 -04:00
sql : : command ( this - > sql , query , variable { " :serverId " , server ? server - > getServerId ( ) : 0 } , variable { " :cldbid " , cldbid } , variable { " :value " , prop . value ( ) } ) . executeLater ( ) . waitAndGetLater ( LOG_SQL_CMD , { 1 , " future failed " } ) ;
} ) ;
return props ;
}
void DatabaseHelper : : loadStartupCache ( ) {
2020-01-23 20:57:58 -05:00
this - > loadStartupPermissionCache ( ) ;
2019-07-17 13:37:18 -04:00
this - > loadStartupPropertyCache ( ) ;
2020-01-23 20:57:58 -05:00
this - > use_startup_cache = true ;
2019-07-17 13:37:18 -04:00
}
size_t DatabaseHelper : : cacheBinarySize ( ) {
size_t result = 0 ;
result + = sizeof ( this - > startup_entries ) ;
for ( const auto & entry : this - > startup_entries ) {
result + = sizeof ( entry ) ;
result + = sizeof ( * entry . get ( ) ) ;
for ( const auto & e : entry - > permissions ) {
result + = sizeof ( e ) ;
result + = sizeof ( e . get ( ) ) ;
}
for ( const auto & e : entry - > properties ) {
result + = sizeof ( e ) ;
result + = sizeof ( e . get ( ) ) ;
2020-01-23 20:57:58 -05:00
result + = e - > value . length ( ) ;
2019-07-17 13:37:18 -04:00
}
}
return result ;
}
void DatabaseHelper : : clearStartupCache ( ts : : ServerId sid ) {
2020-01-23 20:57:58 -05:00
if ( sid = = 0 ) {
threads : : MutexLock lock ( this - > startup_lock ) ;
this - > startup_entries . clear ( ) ;
this - > use_startup_cache = false ;
} else {
threads : : MutexLock lock ( this - > startup_lock ) ;
/*
this - > startup_entries . erase ( std : : remove_if ( this - > startup_entries . begin ( ) , this - > startup_entries . end ( ) , [ & ] ( const shared_ptr < StartupCacheEntry > & entry ) {
return entry - > sid = = sid ;
} ) , this - > startup_entries . end ( ) ) ;
*/
}
2019-07-17 13:37:18 -04:00
}
//SELECT `serverId`, `type`, `id`, `key`, `value` FROM properties ORDER BY `serverId`
//SELECT `serverId`, `type`, `id`, `channelId`, `permId`, `value`, `grant`, `flag_skip`, `flag_negate` FROM permissions ORDER BY `serverId`
struct StartupPermissionArgument {
std : : shared_ptr < StartupCacheEntry > current_server ;
} ;
void DatabaseHelper : : loadStartupPermissionCache ( ) {
2020-01-23 20:57:58 -05:00
StartupPermissionArgument arg ;
sql : : command ( this - > sql , " SELECT `serverId`, `type`, `id`, `channelId`, `permId`, `value`, `grant`, `flag_skip`, `flag_negate` FROM permissions ORDER BY `serverId` " ) . query ( [ & ] ( StartupPermissionArgument * arg , int length , char * * values , char * * names ) {
auto key = permission : : PermissionTypeEntry : : unknown ;
permission : : PermissionValue value = permNotGranted , granted = permNotGranted ;
permission : : PermissionSqlType type = SQL_PERM_GROUP ;
bool negated = false , skipped = false ;
ChannelId channel = 0 ;
uint64_t id = 0 ;
ServerId serverId = 0 ;
int index ;
try {
for ( index = 0 ; index < length ; index + + ) {
if ( strcmp ( names [ index ] , " permId " ) = = 0 ) {
key = permission : : resolvePermissionData ( values [ index ] ) ;
if ( key - > type = = permission : : unknown | | key - > type = = permission : : undefined ) {
debugMessage ( 0 , " [SQL] Permission entry contains invalid permission type! Type: {} " , values [ index ] ) ;
return 0 ;
}
} else if ( strcmp ( names [ index ] , " channelId " ) = = 0 ) {
channel = stoull ( values [ index ] ) ;
} else if ( strcmp ( names [ index ] , " id " ) = = 0 ) {
id = stoull ( values [ index ] ) ;
} else if ( strcmp ( names [ index ] , " value " ) = = 0 ) {
value = stoi ( values [ index ] ) ;
} else if ( strcmp ( names [ index ] , " grant " ) = = 0 ) {
granted = stoi ( values [ index ] ) ;
} else if ( strcmp ( names [ index ] , " flag_skip " ) = = 0 )
skipped = strcmp ( values [ index ] , " 1 " ) = = 0 ;
else if ( strcmp ( names [ index ] , " flag_negate " ) = = 0 )
negated = strcmp ( values [ index ] , " 1 " ) = = 0 ;
else if ( strcmp ( names [ index ] , " serverId " ) = = 0 )
serverId = stoll ( values [ index ] ) ;
else if ( strcmp ( names [ index ] , " type " ) = = 0 )
type = static_cast < PermissionSqlType > ( stoll ( values [ index ] ) ) ;
}
} catch ( std : : exception & ex ) {
logError ( 0 , " [SQL] Cant load permissions! Failed to parse value! Message: {}. Key: {}, Value: \" {} \" " , ex . what ( ) , names [ index ] , values [ index ] ) ;
return 0 ;
}
if ( key = = permission : : PermissionTypeEntry : : unknown ) {
debugMessage ( 0 , " [SQL] Permission entry misses permission type! " ) ;
return 0 ;
}
if ( serverId = = 0 ) return 0 ;
if ( ! arg - > current_server | | arg - > current_server - > sid ! = serverId ) {
2019-07-17 13:37:18 -04:00
arg - > current_server = nullptr ;
2020-01-23 20:57:58 -05:00
{
threads : : MutexLock lock ( this - > startup_lock ) ;
for ( const auto & entry : this - > startup_entries ) {
if ( entry - > sid = = serverId ) {
arg - > current_server = entry ;
break ;
}
}
if ( ! arg - > current_server ) {
arg - > current_server = make_shared < StartupCacheEntry > ( ) ;
arg - > current_server - > sid = serverId ;
this - > startup_entries . push_back ( arg - > current_server ) ;
}
}
}
auto entry = make_unique < StartupPermissionEntry > ( ) ;
entry - > permission = key ;
entry - > type = type ;
entry - > value = value ;
entry - > grant = granted ;
entry - > flag_negate = negated ;
entry - > flag_skip = skipped ;
entry - > id = id ;
entry - > channelId = channel ;
arg - > current_server - > permissions . push_back ( std : : move ( entry ) ) ;
return 0 ;
2019-07-17 13:37:18 -04:00
} , & arg ) ;
}
void DatabaseHelper : : loadStartupPropertyCache ( ) {
StartupPermissionArgument arg ;
sql : : command ( this - > sql , " SELECT `serverId`, `type`, `id`, `key`, `value` FROM properties ORDER BY `serverId` " ) . query ( [ & ] ( StartupPermissionArgument * arg , int length , char * * values , char * * names ) {
std : : string key , value ;
property : : PropertyType type = property : : PROP_TYPE_UNKNOWN ;
ServerId serverId = 0 ;
uint64_t id = 0 ;
for ( int index = 0 ; index < length ; index + + ) {
try {
2020-01-23 20:57:58 -05:00
if ( strcmp ( names [ index ] , " key " ) = = 0 ) key = values [ index ] ;
else if ( strcmp ( names [ index ] , " value " ) = = 0 ) value = values [ index ] = = nullptr ? " " : values [ index ] ;
else if ( strcmp ( names [ index ] , " type " ) = = 0 ) type = ( property : : PropertyType ) stoll ( values [ index ] ) ;
else if ( strcmp ( names [ index ] , " serverId " ) = = 0 ) serverId = stoll ( values [ index ] ) ;
else if ( strcmp ( names [ index ] , " id " ) = = 0 ) id = stoll ( values [ index ] ) ;
2019-07-17 13:37:18 -04:00
} catch ( const std : : exception & ex ) {
2020-01-23 20:57:58 -05:00
logError ( 0 , " [SQL] Cant load property! Failed to parse value! Message: {}. Key: {}, Value: \" {} \" " , ex . what ( ) , names [ index ] , values [ index ] ) ;
return 0 ;
2019-07-17 13:37:18 -04:00
}
}
2020-04-08 07:01:41 -04:00
const auto & info = property : : find ( type , key ) ;
if ( info . is_undefined ( ) ) {
2020-01-23 20:57:58 -05:00
logError ( serverId , " Invalid property ({} | {}) " , key , type ) ;
return 0 ;
2019-07-17 13:37:18 -04:00
}
2020-01-23 20:57:58 -05:00
if ( serverId = = 0 ) return 0 ;
2019-07-17 13:37:18 -04:00
2020-01-23 20:57:58 -05:00
if ( ! arg - > current_server | | arg - > current_server - > sid ! = serverId ) {
2019-07-17 13:37:18 -04:00
arg - > current_server = nullptr ;
2020-01-23 20:57:58 -05:00
{
threads : : MutexLock lock ( this - > startup_lock ) ;
for ( const auto & entry : this - > startup_entries ) {
if ( entry - > sid = = serverId ) {
arg - > current_server = entry ;
break ;
}
}
if ( ! arg - > current_server ) {
arg - > current_server = make_shared < StartupCacheEntry > ( ) ;
arg - > current_server - > sid = serverId ;
this - > startup_entries . push_back ( arg - > current_server ) ;
}
}
}
2019-07-17 13:37:18 -04:00
auto entry = make_unique < StartupPropertyEntry > ( ) ;
2020-04-08 07:01:41 -04:00
entry - > info = & info ;
2019-07-17 13:37:18 -04:00
entry - > value = value ;
entry - > id = id ;
entry - > type = type ;
arg - > current_server - > properties . push_back ( std : : move ( entry ) ) ;
return 0 ;
} , & arg ) ;
}
2020-01-26 12:04:38 -05:00
bool DatabaseHelper : : deleteGroupPermissions ( const std : : shared_ptr < ts : : server : : VirtualServer > & server , ts : : GroupId group_id ) {
2020-01-23 20:57:58 -05:00
auto command = sql : : command ( this - > sql , " DELETE FROM `permissions` WHERE `serverId` = :serverId AND `type` = :type AND `id` = :id " ,
variable { " :serverId " , server ? server - > getServerId ( ) : 0 } ,
variable { " :type " , permission : : SQL_PERM_GROUP } ,
variable { " :id " , group_id } ) . execute ( ) ;
LOG_SQL_CMD ( command ) ;
return ! ! command ;
2019-07-17 13:37:18 -04:00
}
2020-01-26 12:04:38 -05:00
bool DatabaseHelper : : deleteChannelPermissions ( const std : : shared_ptr < ts : : server : : VirtualServer > & server , ts : : ChannelId channel_id ) {
2020-01-23 20:57:58 -05:00
auto command = sql : : command ( sql , " DELETE FROM `permissions` WHERE `serverId` = :serverId AND `channelId` = :chid " ,
variable { " :serverId " , server ? server - > getServerId ( ) : 0 } ,
variable { " :chid " , channel_id } ) . execute ( ) ;
LOG_SQL_CMD ( command ) ;
return ! ! command ;
2019-07-17 13:37:18 -04:00
}
std : : deque < std : : unique_ptr < FastPropertyEntry > > DatabaseHelper : : query_properties ( ts : : ServerId server_id , ts : : property : : PropertyType type , uint64_t id ) {
2020-01-23 20:57:58 -05:00
deque < unique_ptr < FastPropertyEntry > > result ;
2019-07-17 13:37:18 -04:00
2020-01-23 20:57:58 -05:00
auto command = sql : : command ( this - > sql , " SELECT `key`, `value`, `type` FROM properties WHERE `serverId` = :serverId AND `type` = :type AND `id` = :id " , variable { " :serverId " , server_id } , variable { " :type " , type } , variable { " :id " , id } ) ;
LOG_SQL_CMD ( load_properties ( server_id , result , command ) ) ;
2019-07-17 13:37:18 -04:00
2020-01-23 20:57:58 -05:00
return result ;
2019-07-17 13:37:18 -04:00
}
2020-01-26 12:04:38 -05:00
bool DatabaseHelper : : deletePlaylist ( const std : : shared_ptr < ts : : server : : VirtualServer > & server , ts : : PlaylistId playlist_id ) {
2020-01-23 20:57:58 -05:00
auto server_id = server ? server - > getServerId ( ) : ( ServerId ) 0 ;
sql : : command ( this - > sql , " DELETE FROM `playlists` WHERE `serverId` = :server_id AND `playlist_id` = :playlist_id " ,
variable { " :server_id " , server_id } ,
variable { " :playlist_id " , playlist_id }
) . executeLater ( ) . waitAndGetLater ( LOG_SQL_CMD , { - 1 , " failed to delete playlist " + to_string ( playlist_id ) + " from table `playlists` " } ) ;
sql : : command ( this - > sql , " DELETE FROM `permissions` WHERE `serverId` = :serverId AND `type` = :type AND `id` = :id " ,
variable { " :serverId " , server ? server - > getServerId ( ) : 0 } ,
variable { " :type " , permission : : SQL_PERM_PLAYLIST } ,
variable { " :id " , playlist_id }
) . executeLater ( ) . waitAndGetLater ( LOG_SQL_CMD , { - 1 , " failed to delete playlist permissions for playlist " + to_string ( playlist_id ) } ) ;
sql : : command ( this - > sql , " DELETE FROM `properties` WHERE `serverId` = :serverId AND `type` = :type AND `id` = :id " ,
variable { " :serverId " , server ? server - > getServerId ( ) : 0 } ,
variable { " :type " , property : : PROP_TYPE_PLAYLIST } ,
variable { " :id " , playlist_id }
) . executeLater ( ) . waitAndGetLater ( LOG_SQL_CMD , { - 1 , " failed to delete playlist properties for playlist " + to_string ( playlist_id ) } ) ;
return true ;
2019-07-17 13:37:18 -04:00
}