2019-07-17 19:37:18 +02: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
2020-07-31 17:35:14 +02:00
struct ts : : server : : CachedPermissionManager {
ServerId server_id { 0 } ;
ClientDbId client_database_id { 0 } ;
std : : weak_ptr < permission : : v2 : : PermissionManager > instance { } ;
std : : shared_ptr < permission : : v2 : : PermissionManager > instance_ref { } ; /* reference to the current instance, will be refreshed every time the instance gets accessed */
std : : chrono : : time_point < std : : chrono : : system_clock > last_access { } ;
} ;
struct ts : : server : : StartupCacheEntry {
ServerId sid { 0 } ;
std : : deque < std : : unique_ptr < StartupPermissionEntry > > permissions { } ;
std : : deque < std : : unique_ptr < StartupPropertyEntry > > properties { } ;
} ;
2019-07-17 19:37:18 +02:00
DatabaseHelper : : DatabaseHelper ( sql : : SqlManager * srv ) : sql ( srv ) { }
DatabaseHelper : : ~ DatabaseHelper ( ) {
2020-07-31 17:35:14 +02:00
this - > cached_permission_managers . clear ( ) ;
2019-07-17 19:37:18 +02:00
}
void DatabaseHelper : : tick ( ) {
2020-07-31 17:35:14 +02:00
auto cache_timeout = std : : chrono : : system_clock : : now ( ) - std : : chrono : : minutes { 10 } ;
2019-07-17 19:37:18 +02:00
{
2020-07-31 17:35:14 +02:00
std : : lock_guard cp_lock { this - > cached_permission_manager_lock } ;
2019-07-17 19:37:18 +02:00
2020-07-31 17:35:14 +02:00
this - > cached_permission_managers . erase ( std : : remove_if ( this - > cached_permission_managers . begin ( ) , this - > cached_permission_managers . end ( ) , [ & ] ( const std : : unique_ptr < CachedPermissionManager > & manager ) {
if ( manager - > last_access < cache_timeout )
manager - > instance_ref = nullptr ;
if ( manager - > instance . expired ( ) )
return true ;
return false ;
} ) , this - > cached_permission_managers . end ( ) ) ;
2020-01-24 02:57:58 +01:00
}
2019-07-17 19:37:18 +02:00
}
2020-07-30 20:25:45 +02:00
constexpr static std : : string_view kSqlBase { " SELECT `client_unique_id`, `client_database_id`, `client_nickname`, `client_created`, `client_last_connected`, `client_total_connections` FROM `clients_server` " } ;
inline std : : deque < std : : shared_ptr < ClientDatabaseInfo > > query_database_client_info ( sql : : SqlManager * sql_manager , ServerId server_id , const std : : string & query , const std : : vector < variable > & variables ) {
std : : deque < std : : shared_ptr < ClientDatabaseInfo > > result { } ;
2019-07-17 19:37:18 +02:00
2020-07-30 20:25:45 +02:00
sql : : command command { sql_manager , query } ;
for ( const auto & variable : variables )
command . value ( variable ) ;
auto sql_result = command . query ( [ & ] ( int length , std : : string * values , std : : string * names ) {
auto entry = std : : make_shared < ClientDatabaseInfo > ( ) ;
auto index { 0 } ;
try {
assert ( names [ index ] = = " client_unique_id " ) ;
entry - > client_unique_id = values [ index + + ] ;
assert ( names [ index ] = = " client_database_id " ) ;
entry - > client_database_id = std : : stoull ( values [ index + + ] ) ;
assert ( names [ index ] = = " client_nickname " ) ;
entry - > client_nickname = values [ index + + ] ;
assert ( names [ index ] = = " client_created " ) ;
entry - > client_created = std : : chrono : : system_clock : : time_point { } + std : : chrono : : seconds { std : : stoull ( values [ index + + ] ) } ;
assert ( names [ index ] = = " client_last_connected " ) ;
entry - > client_last_connected = std : : chrono : : system_clock : : time_point { } + std : : chrono : : seconds { std : : stoull ( values [ index + + ] ) } ;
assert ( names [ index ] = = " client_total_connections " ) ;
entry - > client_total_connections = std : : stoull ( values [ index + + ] ) ;
assert ( index = = length ) ;
} catch ( std : : exception & ex ) {
logError ( server_id , " Failed to parse client base properties at index {}: {}. Query: {} " ,
index - 1 ,
ex . what ( ) ,
query
) ;
}
result . push_back ( std : : move ( entry ) ) ;
} ) ;
if ( ! sql_result ) {
logError ( server_id , " Failed to query client database infos: {}; Query: {} " , sql_result . fmtStr ( ) , query ) ;
return result ;
2019-07-17 19:37:18 +02:00
}
return result ;
}
2020-07-30 20:25:45 +02:00
std : : deque < std : : shared_ptr < ClientDatabaseInfo > > DatabaseHelper : : queryDatabaseInfo ( const std : : shared_ptr < VirtualServer > & server , const std : : deque < ClientDbId > & list ) {
if ( list . empty ( ) )
return { } ;
std : : string valueList { } ;
for ( const auto & element : list )
valueList + = " , " + std : : to_string ( element ) ;
valueList = valueList . substr ( 2 ) ;
auto serverId = server ? server - > getServerId ( ) : 0 ;
return query_database_client_info ( this - > sql , serverId , std : : string { kSqlBase } + " WHERE `server_id` = :sid AND `client_database_id` IN ( " + valueList + " ) " , { variable { " :sid " , serverId } } ) ;
}
2020-01-26 18:04:38 +01:00
std : : deque < std : : shared_ptr < ClientDatabaseInfo > > DatabaseHelper : : queryDatabaseInfoByUid ( const std : : shared_ptr < VirtualServer > & server , std : : deque < std : : string > list ) {
2020-07-30 20:25:45 +02:00
if ( list . empty ( ) )
return { } ;
2019-07-17 19:37:18 +02:00
2020-07-30 20:25:45 +02:00
std : : string valueList { } ;
for ( size_t value_index { 0 } ; value_index < list . size ( ) ; value_index + + )
valueList + = " , :v " + std : : to_string ( value_index ) ;
valueList = valueList . substr ( 2 ) ;
auto serverId = server ? server - > getServerId ( ) : 0 ;
std : : vector < variable > values { } ;
values . reserve ( list . size ( ) + 1 ) ;
values . emplace_back ( " :sid " , serverId ) ;
for ( size_t value_index { 0 } ; value_index < list . size ( ) ; value_index + + )
values . emplace_back ( " :v " + std : : to_string ( value_index ) , list [ value_index ] ) ;
return query_database_client_info ( this - > sql , serverId , std : : string { kSqlBase } + " WHERE `server_id` = :sid AND `client_unique_id` IN ( " + valueList + " ) " , values) ;
2019-07-17 19:37:18 +02:00
}
2020-01-26 18:04:38 +01:00
bool DatabaseHelper : : validClientDatabaseId ( const std : : shared_ptr < VirtualServer > & server , ClientDbId cldbid ) { return cldbid > 0 ; } //TODO here check
2019-07-17 19:37:18 +02:00
2020-01-26 18:04:38 +01:00
void DatabaseHelper : : deleteClient ( const std : : shared_ptr < VirtualServer > & server , ClientDbId cldbid ) {
2020-07-30 20:25:45 +02:00
auto serverId = ( ServerId ) ( server ? server - > getServerId ( ) : 0 ) ;
2019-07-17 19:37:18 +02:00
{
2020-07-31 17:35:14 +02:00
lock_guard lock { cached_permission_manager_lock } ;
this - > cached_permission_managers . erase ( std : : remove_if ( this - > cached_permission_managers . begin ( ) , this - > cached_permission_managers . end ( ) , [ & ] ( const auto & entry ) {
return entry - > server_id = = serverId & & entry - > client_database_id = = cldbid ;
} ) , this - > cached_permission_managers . end ( ) ) ;
2019-07-17 19:37:18 +02:00
}
2020-07-30 20:25:45 +02:00
sql : : result state { } ;
2019-07-17 19:37:18 +02:00
2020-07-30 20:25:45 +02:00
state = sql : : command ( this - > sql , " DELETE FROM `properties` WHERE `serverId` = :sid AND (`type` = :type1 OR `type` = :type2) AND `id` = :id " , variable { " :sid " , serverId } , 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 " , serverId } , variable { " :type " , permission : : SQL_PERM_USER } , variable { " :id " , cldbid } ) . execute ( ) ;
state = sql : : command ( this - > sql , " DELETE FROM `bannedClients` WHERE `serverId` = :sid AND `invokerDbid` = :id " , variable { " :sid " , serverId } , variable { " :id " , cldbid } ) . execute ( ) ;
state = sql : : command ( this - > sql , " DELETE FROM `assignedGroups` WHERE `serverId` = :sid AND `cldbid` = :id " , variable { " :sid " , serverId } , variable { " :id " , cldbid } ) . execute ( ) ;
if ( serverId = = 0 ) {
state = sql : : command ( this - > sql , " DELETE FROM `clients_server` WHERE `client_database_id` = :id " , variable { " :id " , cldbid } ) . execute ( ) ;
state = sql : : command ( this - > sql , " DELETE FROM `clients` WHERE `client_database_id` = :id " , variable { " :id " , cldbid } ) . execute ( ) ;
2020-08-23 22:23:12 +02:00
} else {
state = sql : : command ( this - > sql , " DELETE FROM `clients_server` WHERE `server_id` = :sid AND `client_database_id` = :id " , variable { " :sid " , serverId } , variable { " :id " , cldbid } ) . execute ( ) ;
2020-07-30 20:25:45 +02:00
}
2019-07-17 19:37:18 +02:00
//TODO delete letters
//TODO delete query
//TODO delete complains
}
2020-03-23 10:58:07 +01: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-24 02:57:58 +01: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 14:32:16 +01:00
ChannelId channel_id { 0 } ;
2020-01-24 02:57:58 +01:00
int index ;
2020-02-01 14:32:16 +01:00
2020-01-24 02:57:58 +01: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 14:32:16 +01:00
channel_id = stoull ( values [ index ] ) ;
2020-01-24 02:57:58 +01: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 14:32:16 +01: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-24 02:57:58 +01:00
if ( key = = permission : : undefined ) {
debugMessage ( server_id , " [SQL] Permission entry misses permission type! Command: {} " , command . sqlCommand ( ) ) ;
return 0 ;
}
2020-03-23 10:58:07 +01:00
if ( channel_id = = 0 | | is_channel )
2020-01-24 02:57:58 +01:00
manager - > load_permission ( key , { value , granted } , skipped , negated , value ! = permNotGranted , granted ! = permNotGranted ) ;
else
2020-02-01 14:32:16 +01:00
manager - > load_permission ( key , { value , granted } , channel_id , skipped , negated , value ! = permNotGranted , granted ! = permNotGranted ) ;
2020-01-24 02:57:58 +01: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 19:37:18 +02:00
}
2020-07-31 17:35:14 +02:00
constexpr static std : : string_view kPermissionUpdateCommand { " 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 " } ;
constexpr static std : : string_view kPermissionInsertCommand { " 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) " } ;
constexpr static std : : string_view kPermissionDeleteCommand { " DELETE FROM `permissions` WHERE `serverId` = :serverId AND `type` = :type AND `id` = :id AND `permId` = :permId AND `channelId` = :chId " } ;
std : : shared_ptr < permission : : v2 : : PermissionManager > DatabaseHelper : : find_cached_permission_manager ( ServerId server_id ,
ClientDbId client_database_id ) {
for ( auto it = this - > cached_permission_managers . begin ( ) ; it ! = this - > cached_permission_managers . end ( ) ; it + + ) {
auto & cached_manager = * it ;
if ( cached_manager - > client_database_id = = client_database_id & & cached_manager - > server_id = = server_id ) {
auto manager = cached_manager - > instance . lock ( ) ;
if ( ! manager ) {
this - > cached_permission_managers . erase ( it ) ;
break ;
}
cached_manager - > last_access = system_clock : : now ( ) ;
cached_manager - > instance_ref = manager ;
return manager ;
}
}
return nullptr ;
}
2019-07-17 19:37:18 +02:00
2020-01-26 18:04:38 +01:00
std : : shared_ptr < v2 : : PermissionManager > DatabaseHelper : : loadClientPermissionManager ( const std : : shared_ptr < VirtualServer > & server , ClientDbId cldbid ) {
2020-01-24 02:57:58 +01:00
auto server_id = server ? server - > getServerId ( ) : 0 ;
2019-07-17 19:37:18 +02:00
# ifndef DISABLE_CACHING
2020-01-24 02:57:58 +01:00
{
2020-07-31 17:35:14 +02:00
std : : lock_guard lock { cached_permission_manager_lock } ;
auto manager = this - > find_cached_permission_manager ( server_id , cldbid ) ;
if ( manager ) return manager ;
2020-01-24 02:57:58 +01:00
}
2019-07-17 19:37:18 +02:00
# endif
2020-01-24 02:57:58 +01: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 10:58:07 +01:00
LOG_SQL_CMD ( load_permissions_v2 ( server , permission_manager . get ( ) , command , true , false ) ) ;
2020-01-24 02:57:58 +01:00
}
2019-07-17 19:37:18 +02:00
# ifndef DISABLE_CACHING
2020-07-31 17:35:14 +02:00
auto cache_entry = std : : make_unique < CachedPermissionManager > ( ) ;
cache_entry - > server_id = server_id ;
cache_entry - > instance = permission_manager ;
cache_entry - > instance_ref = permission_manager ;
cache_entry - > client_database_id = cldbid ;
cache_entry - > last_access = system_clock : : now ( ) ;
{
std : : lock_guard cache_lock { this - > cached_permission_manager_lock } ;
/* test if we might not got a second instance */
auto manager = this - > find_cached_permission_manager ( server_id , cldbid ) ;
if ( manager ) return manager ;
this - > cached_permission_managers . push_back ( std : : move ( cache_entry ) ) ;
}
2019-07-17 19:37:18 +02:00
# endif
2020-01-24 02:57:58 +01:00
return permission_manager ;
2019-07-17 19:37:18 +02:00
}
2020-01-26 18:04:38 +01: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-24 02:57:58 +01:00
const auto updates = permissions - > flush_db_updates ( ) ;
if ( updates . empty ( ) )
return ;
auto server_id = server ? server - > getServerId ( ) : 0 ;
for ( auto & update : updates ) {
2020-07-31 17:35:14 +02:00
std : : string query { update . flag_delete ? kPermissionDeleteCommand : ( update . flag_db ? kPermissionUpdateCommand : kPermissionInsertCommand ) } ;
2020-01-24 02:57:58 +01: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 , " [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 19:37:18 +02:00
}
2020-01-26 18:04:38 +01:00
std : : shared_ptr < permission : : v2 : : PermissionManager > DatabaseHelper : : loadGroupPermissions ( const std : : shared_ptr < VirtualServer > & server , ts : : GroupId group_id ) {
2020-01-24 02:57:58 +01: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 10:58:07 +01:00
LOG_SQL_CMD ( load_permissions_v2 ( server , result . get ( ) , command , false , false ) ) ;
2019-07-17 19:37:18 +02:00
return result ;
}
2020-01-26 18:04:38 +01: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-24 02:57:58 +01:00
const auto updates = permissions - > flush_db_updates ( ) ;
if ( updates . empty ( ) )
return ;
auto server_id = server ? server - > getServerId ( ) : 0 ;
for ( auto & update : updates ) {
2020-07-31 17:35:14 +02:00
std : : string query { update . flag_delete ? kPermissionDeleteCommand : ( update . flag_db ? kPermissionUpdateCommand : kPermissionInsertCommand ) } ;
2020-01-24 02:57:58 +01: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 , " [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 19:37:18 +02:00
}
2020-02-01 14:32:16 +01: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-24 02:57:58 +01: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 14:32:16 +01:00
result = std : : make_shared < permission : : v2 : : PermissionManager > ( ) ;
2020-01-24 02:57:58 +01:00
for ( const auto & perm : entry - > permissions ) {
if ( perm - > type = = permission : : SQL_PERM_PLAYLIST & & perm - > id = = playlist_id ) {
2020-02-21 20:32:25 +01: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-24 02:57:58 +01:00
}
}
}
2020-02-01 14:32:16 +01:00
return result ;
2020-01-24 02:57:58 +01:00
}
2020-02-01 14:32:16 +01: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 10:58:07 +01:00
LOG_SQL_CMD ( load_permissions_v2 ( server , result . get ( ) , command , false , false ) ) ;
2020-02-01 14:32:16 +01:00
return result ;
}
2020-01-24 02:57:58 +01:00
2020-02-01 14:32:16 +01: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-24 02:57:58 +01:00
2020-02-01 14:32:16 +01:00
auto server_id = server ? server - > getServerId ( ) : 0 ;
for ( auto & update : updates ) {
2020-07-31 17:35:14 +02:00
std : : string query { update . flag_delete ? kPermissionDeleteCommand : ( update . flag_db ? kPermissionUpdateCommand : kPermissionInsertCommand ) } ;
2020-01-24 02:57:58 +01:00
2020-02-01 14:32:16 +01: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-24 02:57:58 +01:00
sql : : command ( this - > sql , query ,
variable { " :serverId " , server ? server - > getServerId ( ) : 0 } ,
2020-02-01 14:32:16 +01:00
variable { " :id " , pid } ,
2020-02-02 14:58:46 +01:00
variable { " :chId " , update . channel_id } ,
2020-01-24 02:57:58 +01:00
variable { " :type " , permission : : SQL_PERM_PLAYLIST } ,
2020-02-01 14:32:16 +01: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 19:37:18 +02:00
}
2020-01-26 18:04:38 +01:00
std : : shared_ptr < permission : : v2 : : PermissionManager > DatabaseHelper : : loadChannelPermissions ( const std : : shared_ptr < VirtualServer > & server , ts : : ChannelId channel ) {
2020-01-24 02:57:58 +01: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 10:58:07 +01:00
LOG_SQL_CMD ( load_permissions_v2 ( server , result . get ( ) , command , false , true ) ) ;
2020-01-24 02:57:58 +01:00
return result ;
2019-07-17 19:37:18 +02:00
}
2020-01-26 18:04:38 +01: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-24 02:57:58 +01:00
const auto updates = permissions - > flush_db_updates ( ) ;
if ( updates . empty ( ) )
return ;
auto server_id = server ? server - > getServerId ( ) : 0 ;
for ( auto & update : updates ) {
2020-07-31 17:35:14 +02:00
std : : string query { update . flag_delete ? kPermissionDeleteCommand : ( update . flag_db ? kPermissionUpdateCommand : kPermissionInsertCommand ) } ;
2020-01-24 02:57:58 +01:00
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 19:37:18 +02:00
}
2019-10-16 21:11:54 +02:00
std : : shared_ptr < Properties > DatabaseHelper : : default_properties_client ( std : : shared_ptr < Properties > properties , ClientType type ) {
2020-01-24 02:57:58 +01:00
if ( ! properties )
properties = make_shared < Properties > ( ) ;
2019-10-16 21:11:54 +02:00
2020-01-24 02:57:58 +01:00
properties - > register_property_type < property : : ClientProperties > ( ) ;
properties - > register_property_type < property : : ConnectionProperties > ( ) ;
2019-10-16 21:11:54 +02:00
2020-01-24 02:57:58 +01: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 21:11:54 +02:00
2020-01-24 02:57:58 +01:00
return properties ;
2019-07-17 19:37:18 +02:00
}
2020-07-30 20:25:45 +02:00
bool DatabaseHelper : : assignDatabaseId ( sql : : SqlManager * sql , ServerId serverId , std : : shared_ptr < DataClient > cl ) {
2019-07-17 19:37:18 +02:00
cl - > loadDataForCurrentServer ( ) ;
2020-07-30 20:25:45 +02:00
if ( cl - > getClientDatabaseId ( ) = = 0 ) {
/* client does not exists, create a new one */
sql : : result sql_result { } ;
std : : string insert_or_ignore { sql - > getType ( ) = = sql : : TYPE_SQLITE ? " INSERT OR IGNORE " : " INSERT IGNORE " } ;
auto currentTimeSeconds = duration_cast < seconds > ( system_clock : : now ( ) . time_since_epoch ( ) ) . count ( ) ;
sql_result = sql : : command { sql , insert_or_ignore + " INTO `clients` (`client_unique_id`, `client_created`) VALUES (:uniqueId, :now) " ,
variable { " :uniqueId " , cl - > getUid ( ) } ,
variable { " :now " , currentTimeSeconds }
} . execute ( ) ;
if ( ! sql_result ) {
logCritical ( LOG_INSTANCE , " Failed to execute client insert command for {}: {} " , cl - > getUid ( ) , sql_result . fmtStr ( ) ) ;
return false ;
2019-07-17 19:37:18 +02:00
}
2020-07-30 20:25:45 +02:00
sql_result = sql : : command { sql , " INSERT INTO `clients_server` (`server_id`, `client_unique_id`, `client_database_id`, `client_created`) SELECT :serverId, :uniqueId, `client_database_id`, :now FROM `clients` WHERE `client_unique_id` = :uniqueId; " ,
variable { " :serverId " , serverId } ,
variable { " :uniqueId " , cl - > getUid ( ) } ,
variable { " :now " , currentTimeSeconds }
} . execute ( ) ;
if ( ! sql_result ) {
logCritical ( LOG_INSTANCE , " Failed to execute client server insert command for {}: {} " , cl - > getUid ( ) , sql_result . fmtStr ( ) ) ;
return false ;
2019-07-17 19:37:18 +02:00
}
2020-07-30 20:25:45 +02:00
if ( ! cl - > loadDataForCurrentServer ( ) )
return false ;
debugMessage ( serverId , " Successfully registered client {} for server {} with database id {}. " , cl - > getUid ( ) , serverId , cl - > getClientDatabaseId ( ) ) ;
return true ;
2019-07-17 19:37:18 +02:00
}
return true ;
}
inline sql : : result load_properties ( ServerId sid , deque < unique_ptr < FastPropertyEntry > > & properties , sql : : command & command ) {
2020-01-24 02:57:58 +01:00
auto start = system_clock : : now ( ) ;
2019-07-17 19:37:18 +02: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-24 02:57:58 +01: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 19:37:18 +02:00
}
2020-04-08 13:01:41 +02:00
const auto & info = property : : find ( type , key ) ;
if ( info . name = = " undefined " ) {
2019-07-17 19:37:18 +02: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 13:01:41 +02:00
auto data = std : : make_unique < FastPropertyEntry > ( ) ;
data - > type = & info ;
2019-07-17 19:37:18 +02:00
data - > value = value ;
2020-01-24 02:57:58 +01:00
properties . push_back ( move ( data ) ) ;
2019-07-17 19:37:18 +02:00
return 0 ;
} ) ;
auto end = system_clock : : now ( ) ;
auto time = end - start ;
2020-01-24 02:57:58 +01:00
logTrace ( sid , " [SQL] load_properties( \" {} \" ) needs {}ms " , command . sqlCommand ( ) , duration_cast < milliseconds > ( time ) . count ( ) ) ;
2019-07-17 19:37:18 +02:00
return result ;
}
2020-01-26 18:04:38 +01:00
std : : shared_ptr < Properties > DatabaseHelper : : loadServerProperties ( const std : : shared_ptr < ts : : server : : VirtualServer > & server ) {
2020-01-24 02:57:58 +01: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 18:04:38 +01:00
weak_ptr < VirtualServer > weak = server ;
2020-01-24 02:57:58 +01: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 13:01:41 +02:00
logTrace ( serverId , " Updating server property: " + std : : string { prop . type ( ) . name } + " . New value: " + prop . value ( ) + " . Query: " + sql ) ;
2020-01-24 02:57:58 +01: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 19:37:18 +02:00
}
2020-01-26 18:04:38 +01:00
std : : shared_ptr < Properties > DatabaseHelper : : loadPlaylistProperties ( const std : : shared_ptr < ts : : server : : VirtualServer > & server , PlaylistId id ) {
2019-07-17 19:37:18 +02:00
auto props = std : : make_shared < Properties > ( ) ;
2020-01-24 02:57:58 +01:00
props - > register_property_type < property : : PlaylistProperties > ( ) ;
( * props ) [ property : : PLAYLIST_ID ] = id ;
2019-07-17 19:37:18 +02: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-24 02:57:58 +01: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 19:37:18 +02:00
}
2020-01-26 18:04:38 +01:00
weak_ptr < VirtualServer > weak = server ;
2019-07-17 19:37:18 +02: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-24 02:57:58 +01:00
logTrace ( serverId , " Updating playlist property for {}. Key: {} Value: {} " , id , prop . type ( ) . name , prop . value ( ) ) ;
2019-07-17 19:37:18 +02: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 18:04:38 +01:00
std : : shared_ptr < Properties > DatabaseHelper : : loadChannelProperties ( const shared_ptr < VirtualServer > & server , ChannelId channel ) {
2019-07-17 19:37:18 +02:00
ServerId serverId = server ? server - > getServerId ( ) : 0U ;
auto props = std : : make_shared < Properties > ( ) ;
2020-01-24 02:57:58 +01:00
props - > register_property_type < property : : ChannelProperties > ( ) ;
2019-07-17 19:37:18 +02: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-24 02:57:58 +01:00
deque < unique_ptr < FastPropertyEntry > > property_list ;
2019-07-17 19:37:18 +02:00
LOG_SQL_CMD ( load_properties ( serverId , property_list , command ) ) ;
for ( const auto & entry : property_list ) {
2020-01-24 02:57:58 +01:00
auto prop = props - > operator [ ] ( entry - > type ) ;
prop = entry - > value ;
prop . setModified ( true ) ;
prop . setDbReference ( true ) ;
2019-07-17 19:37:18 +02:00
}
}
2020-01-26 18:04:38 +01:00
weak_ptr < VirtualServer > weak = server ;
2019-07-17 19:37:18 +02:00
props - > registerNotifyHandler ( [ & , weak , serverId , channel ] ( Property & prop ) {
auto weak_server = weak . lock ( ) ;
if ( ! weak_server & & serverId ! = 0 )
2020-01-24 02:57:58 +01:00
return ;
2019-07-17 19:37:18 +02:00
if ( ( prop . type ( ) . flags & property : : FLAG_SAVE ) = = 0 )
2020-01-24 02:57:58 +01:00
return ;
2019-07-17 19:37:18 +02:00
if ( ! prop . isModified ( ) )
2020-01-24 02:57:58 +01:00
return ;
2019-07-17 19:37:18 +02: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-24 02:57:58 +01:00
logTrace ( serverId , " [CHANNEL] Updating channel property for channel {}: {}. New value: '{}'. Query: {} " , channel , prop . type ( ) . name , prop . value ( ) , query ) ;
2019-07-17 19:37:18 +02: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 18:04:38 +01:00
std : : shared_ptr < Properties > DatabaseHelper : : loadClientProperties ( const std : : shared_ptr < VirtualServer > & server , ClientDbId cldbid , ClientType type ) {
2019-10-16 21:11:54 +02:00
auto props = DatabaseHelper : : default_properties_client ( nullptr , type ) ;
2019-07-17 19:37:18 +02:00
if ( server ) {
props - > operator [ ] ( property : : CLIENT_DESCRIPTION ) = server - > properties ( ) [ property : : VIRTUALSERVER_DEFAULT_CLIENT_DESCRIPTION ] . value ( ) ;
}
2020-07-30 20:25:45 +02:00
2019-07-17 19:37:18 +02: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 - > 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 ;
}
}
2020-07-30 20:25:45 +02:00
2019-07-17 19:37:18 +02:00
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-24 02:57:58 +01: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 19:37:18 +02:00
}
2020-01-26 18:04:38 +01:00
weak_ptr < VirtualServer > weak_server = server ;
2020-01-24 02:57:58 +01:00
auto server_id = server ? server - > getServerId ( ) : 0 ;
2019-08-16 16:13:52 +02:00
props - > registerNotifyHandler ( [ & , weak_server , server_id , cldbid , type ] ( Property & prop ) { //General save
2020-01-24 02:57:58 +01: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 19:37:18 +02:00
if ( ! prop . isModified ( ) ) return ;
2019-07-23 10:37:56 +02:00
if ( ( prop . type ( ) . flags & property : : FLAG_SAVE ) = = 0 & & ( type ! = ClientType : : CLIENT_MUSIC | | ( prop . type ( ) . flags & property : : FLAG_SAVE_MUSIC ) = = 0 ) ) {
2020-04-08 13:01:41 +02: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 19:37:18 +02: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 ) ;
2020-07-30 20:25:45 +02:00
std : : string sqlCommand ;
2019-07-17 19:37:18 +02:00
if ( prop . hasDbReference ( ) )
2020-07-30 20:25:45 +02:00
sqlCommand = " UPDATE `properties` SET `value` = :value WHERE `serverId` = :serverId AND `type` = :type AND `id` = :id AND `key` = :key " ;
2019-07-17 19:37:18 +02:00
else {
prop . setDbReference ( true ) ;
2020-07-30 20:25:45 +02:00
sqlCommand = " INSERT INTO `properties` (`serverId`, `type`, `id`, `key`, `value`) VALUES (:serverId, :type, :id, :key, :value) " ;
2019-07-17 19:37:18 +02:00
}
2020-04-08 13:01:41 +02:00
logTrace ( server ? server - > getServerId ( ) : 0 , " [Property] Changed property in db key: " + std : : string { prop . type ( ) . name } + " value: " + prop . value ( ) ) ;
2020-07-30 20:25:45 +02:00
sql : : command ( this - > sql , sqlCommand ,
2020-01-24 02:57:58 +01: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 19:37:18 +02:00
) . executeLater ( ) . waitAndGetLater ( LOG_SQL_CMD , sql : : result { 1 , " future failed " } ) ;
} ) ;
props - > registerNotifyHandler ( [ & , weak_server , server_id , cldbid ] ( Property & prop ) {
2020-01-24 02:57:58 +01: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 19:37:18 +02:00
2020-07-30 20:25:45 +02:00
std : : string column ;
2020-08-18 22:03:07 +02:00
if ( prop . type ( ) . type_property = = property : : PROP_TYPE_CLIENT ) {
switch ( prop . type ( ) . property_index ) {
case property : : CLIENT_NICKNAME :
column = " client_nickname " ;
break ;
2020-07-30 20:25:45 +02:00
2020-08-18 22:03:07 +02:00
case property : : CLIENT_LASTCONNECTED :
column = " client_last_connected " ;
break ;
2020-08-13 12:58:19 +02:00
2020-08-18 22:03:07 +02:00
case property : : CLIENT_TOTALCONNECTIONS :
column = " client_total_connections " ;
break ;
2020-07-30 20:25:45 +02:00
2020-08-18 22:03:07 +02:00
case property : : CLIENT_MONTH_BYTES_UPLOADED :
column = " client_month_upload " ;
break ;
2020-07-30 20:25:45 +02:00
2020-08-18 22:03:07 +02:00
case property : : CLIENT_TOTAL_BYTES_UPLOADED :
column = " client_total_upload " ;
break ;
2020-07-30 20:25:45 +02:00
2020-08-18 22:03:07 +02:00
case property : : CLIENT_MONTH_BYTES_DOWNLOADED :
column = " client_month_download " ;
break ;
2020-07-30 20:25:45 +02:00
2020-08-18 22:03:07 +02:00
case property : : CLIENT_TOTAL_BYTES_DOWNLOADED :
column = " client_total_download " ;
break ;
2020-07-30 20:25:45 +02:00
2020-08-18 22:03:07 +02:00
default :
return ;
}
} else if ( prop . type ( ) . type_property = = property : : PROP_TYPE_CONNECTION ) {
switch ( prop . type ( ) . property_index ) {
case property : : CONNECTION_CLIENT_IP :
column = " client_ip " ;
break ;
2020-07-30 20:25:45 +02:00
2020-08-18 22:03:07 +02:00
default :
return ;
}
2020-07-30 20:25:45 +02:00
}
2020-08-01 11:34:24 +02:00
debugMessage ( server ? server - > getServerId ( ) : 0 , " [Property] Changing client property '{}' for {} (New value: {}, Column: {}) " ,
2020-07-30 20:25:45 +02:00
prop . type ( ) . name ,
cldbid ,
prop . value ( ) ,
column
) ;
2020-08-01 00:09:01 +02:00
sql : : command ( this - > sql , " UPDATE `clients_server` SET ` " + column + " ` = :value WHERE `server_id` = :serverId AND `client_database_id` = :cldbid " ,
2020-07-30 20:25:45 +02:00
variable { " :serverId " , server ? server - > getServerId ( ) : 0 } ,
variable { " :cldbid " , cldbid } ,
variable { " :value " , prop . value ( ) }
) . executeLater ( ) . waitAndGetLater ( LOG_SQL_CMD , { 1 , " future failed " } ) ;
2019-07-17 19:37:18 +02:00
} ) ;
return props ;
}
void DatabaseHelper : : loadStartupCache ( ) {
2020-01-24 02:57:58 +01:00
this - > loadStartupPermissionCache ( ) ;
2019-07-17 19:37:18 +02:00
this - > loadStartupPropertyCache ( ) ;
2020-01-24 02:57:58 +01:00
this - > use_startup_cache = true ;
2019-07-17 19:37:18 +02: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-24 02:57:58 +01:00
result + = e - > value . length ( ) ;
2019-07-17 19:37:18 +02:00
}
}
return result ;
}
void DatabaseHelper : : clearStartupCache ( ts : : ServerId sid ) {
2020-01-24 02:57:58 +01: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 19:37:18 +02:00
}
2020-07-31 17:35:14 +02:00
void DatabaseHelper : : handleServerDelete ( ServerId server_id ) {
{
std : : lock_guard pm_lock { this - > cached_permission_manager_lock } ;
this - > cached_permission_managers . erase ( std : : remove_if ( this - > cached_permission_managers . begin ( ) , this - > cached_permission_managers . end ( ) , [ & ] ( const auto & entry ) {
return entry - > server_id = = server_id ;
} ) , this - > cached_permission_managers . end ( ) ) ;
}
}
2019-07-17 19:37:18 +02: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-24 02:57:58 +01: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 19:37:18 +02:00
arg - > current_server = nullptr ;
2020-01-24 02:57:58 +01: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 19:37:18 +02: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-24 02:57:58 +01: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 19:37:18 +02:00
} catch ( const std : : exception & ex ) {
2020-01-24 02:57:58 +01: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 19:37:18 +02:00
}
}
2020-04-08 13:01:41 +02:00
const auto & info = property : : find ( type , key ) ;
if ( info . is_undefined ( ) ) {
2020-01-24 02:57:58 +01:00
logError ( serverId , " Invalid property ({} | {}) " , key , type ) ;
return 0 ;
2019-07-17 19:37:18 +02:00
}
2020-01-24 02:57:58 +01:00
if ( serverId = = 0 ) return 0 ;
2019-07-17 19:37:18 +02:00
2020-01-24 02:57:58 +01:00
if ( ! arg - > current_server | | arg - > current_server - > sid ! = serverId ) {
2019-07-17 19:37:18 +02:00
arg - > current_server = nullptr ;
2020-01-24 02:57:58 +01: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 19:37:18 +02:00
auto entry = make_unique < StartupPropertyEntry > ( ) ;
2020-04-08 13:01:41 +02:00
entry - > info = & info ;
2019-07-17 19:37:18 +02:00
entry - > value = value ;
entry - > id = id ;
entry - > type = type ;
arg - > current_server - > properties . push_back ( std : : move ( entry ) ) ;
return 0 ;
} , & arg ) ;
}
2020-08-18 22:03:07 +02:00
void DatabaseHelper : : deleteGroupArtifacts ( ServerId server_id , GroupId group_id ) {
sql : : result result { } ;
result = sql : : command ( this - > sql , " DELETE FROM `permissions` WHERE `serverId` = :serverId AND `type` = :type AND `id` = :id " ,
variable { " :serverId " , server_id } ,
variable { " :type " , permission : : SQL_PERM_GROUP } ,
variable { " :id " , group_id } ) . execute ( ) ;
LOG_SQL_CMD ( result ) ;
result = sql : : command ( this - > sql , " DELETE FROM `tokens` WHERE `serverId` = :serverId AND `targetGroup` = :id " ,
variable { " :serverId " , server_id } ,
variable { " :id " , group_id } ) . execute ( ) ;
LOG_SQL_CMD ( result ) ;
2019-07-17 19:37:18 +02:00
}
2020-01-26 18:04:38 +01:00
bool DatabaseHelper : : deleteChannelPermissions ( const std : : shared_ptr < ts : : server : : VirtualServer > & server , ts : : ChannelId channel_id ) {
2020-01-24 02:57:58 +01: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 19:37:18 +02:00
}
std : : deque < std : : unique_ptr < FastPropertyEntry > > DatabaseHelper : : query_properties ( ts : : ServerId server_id , ts : : property : : PropertyType type , uint64_t id ) {
2020-01-24 02:57:58 +01:00
deque < unique_ptr < FastPropertyEntry > > result ;
2019-07-17 19:37:18 +02:00
2020-01-24 02:57:58 +01: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 19:37:18 +02:00
2020-01-24 02:57:58 +01:00
return result ;
2019-07-17 19:37:18 +02:00
}
2020-01-26 18:04:38 +01:00
bool DatabaseHelper : : deletePlaylist ( const std : : shared_ptr < ts : : server : : VirtualServer > & server , ts : : PlaylistId playlist_id ) {
2020-01-24 02:57:58 +01: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 ;
2020-07-31 17:35:14 +02:00
}
constexpr static auto kDBListQuery { R " (
SELECT ` clients ` . * , ` properties ` . ` value ` as ` client_description ` FROM (
SELECT
` clients_server ` . ` client_database_id ` ,
` clients_server ` . ` client_unique_id ` ,
` clients_server ` . ` client_nickname ` ,
` clients_server ` . ` client_ip ` ,
` clients_server ` . ` client_created ` ,
` clients_server ` . ` client_last_connected ` ,
` clients_server ` . ` client_total_connections ` ,
` clients ` . ` client_login_name ` FROM ` clients_server `
INNER JOIN ` clients ` ON ` clients ` . ` client_database_id ` = ` clients_server ` . ` client_database_id `
WHERE ` server_id ` = : serverId LIMIT : offset , : limit
) AS ` clients `
LEFT JOIN ` properties ` ON ` properties ` . serverId = : serverId AND ` properties ` . key = ' client_description ' AND ` properties ` . ` id ` = ` clients ` . ` client_database_id `
) " };
void DatabaseHelper : : listDatabaseClients (
ServerId server_id ,
const std : : optional < int64_t > & offset ,
const std : : optional < int64_t > & limit ,
void ( * callback ) ( void * , const DatabaseClient & ) ,
void * user_argument ) {
DatabaseClient client ;
size_t set_index { 0 } ;
auto sqlResult = sql : : command { this - > sql , kDBListQuery ,
variable { " :serverId " , server_id } ,
variable { " :offset " , offset . has_value ( ) ? * offset : 0 } ,
variable { " :limit " , limit . has_value ( ) ? * limit : - 1 }
} . query ( [ & ] ( int length , std : : string * values , std : : string * names ) {
set_index + + ;
auto index { 0 } ;
try {
assert ( names [ index ] = = " client_database_id " ) ;
client . client_database_id = std : : stoull ( values [ index + + ] ) ;
assert ( names [ index ] = = " client_unique_id " ) ;
client . client_unique_id = values [ index + + ] ;
assert ( names [ index ] = = " client_nickname " ) ;
client . client_nickname = values [ index + + ] ;
assert ( names [ index ] = = " client_ip " ) ;
client . client_ip = values [ index + + ] ;
assert ( names [ index ] = = " client_created " ) ;
2020-08-13 12:58:19 +02:00
client . client_created = values [ index + + ] ;
2020-07-31 17:35:14 +02:00
assert ( names [ index ] = = " client_last_connected " ) ;
2020-08-13 12:58:19 +02:00
client . client_last_connected = values [ index + + ] ;
2020-07-31 17:35:14 +02:00
assert ( names [ index ] = = " client_total_connections " ) ;
client . client_total_connections = values [ index + + ] ;
assert ( names [ index ] = = " client_login_name " ) ;
client . client_login_name = values [ index + + ] ;
assert ( names [ index ] = = " client_description " ) ;
client . client_description = values [ index + + ] ;
assert ( index = = length ) ;
} catch ( std : : exception & ex ) {
logError ( server_id , " Failed to parse client base properties at index {}: {}. Offset: {} Limit: {} Set: {} " ,
index - 1 ,
ex . what ( ) ,
offset . has_value ( ) ? std : : to_string ( * offset ) : " not given " ,
limit . has_value ( ) ? std : : to_string ( * limit ) : " not given " ,
set_index - 1
) ;
return ;
}
callback ( user_argument , client ) ;
} ) ;
2019-07-17 19:37:18 +02:00
}