2020-02-28 05:24:07 -05:00
# include "DatabaseHandler.h"
# include <misc/base64.h>
2019-07-17 13:37:18 -04:00
# include <log/LogUtils.h>
using namespace std ;
using namespace std : : chrono ;
using namespace license ;
2020-02-28 05:24:07 -05:00
using namespace license : : server : : database ;
2019-07-17 13:37:18 -04:00
using namespace sql ;
2020-02-28 05:24:07 -05:00
DatabaseHandler : : DatabaseHandler ( sql : : SqlManager * database ) : database ( database ) {
2019-07-17 13:37:18 -04:00
this - > id_cache = make_shared < KeyIdCache > ( this ) ;
}
2020-02-28 05:24:07 -05:00
DatabaseHandler : : ~ DatabaseHandler ( ) { }
2019-07-17 13:37:18 -04:00
/*
LicenseType type ;
std : : string username ;
std : : string first_name ;
std : : string last_name ;
std : : string email ;
std : : chrono : : system_clock : : time_point start ;
std : : chrono : : system_clock : : time_point end ;
std : : chrono : : system_clock : : time_point creation ;
*/
# define CTBL(cmd) \
res = command ( this - > database , cmd ) . execute ( ) ; \
if ( ! res ) { \
error = " Could not setup tables! ( " + res . fmtStr ( ) + " ) " ; \
return false ; \
}
# define CIDX(cmd) \
res = command ( this - > database , cmd ) . execute ( ) ; \
if ( ! res & & res . msg ( ) . find ( " Duplicate " ) = = string : : npos & & res . msg ( ) . find ( " exist " ) = = string : : npos ) { \
error = " Could not setup indexes! ( " + res . fmtStr ( ) + " ) " ; \
return false ; \
}
# define SET_VERSION(ver) \
version = ver ; \
sql : : command ( this - > database , " UPDATE `general` SET `value` = :version WHERE `key` = 'version' " , variable { " :version " , version } ) . execute ( ) ;
2020-02-28 05:24:07 -05:00
bool DatabaseHandler : : setup ( std : : string & error ) {
2019-07-17 13:37:18 -04:00
result res ;
int version = - 1 ;
sql : : command ( this - > database , " SELECT `value` FROM `general` WHERE `key` = 'version' " ) . query ( [ ] ( int * version , int lnegth , string * values , string * names ) {
* version = stoll ( values [ 0 ] ) ;
return 0 ;
} , & version ) ;
switch ( version ) {
case - 1 :
CTBL ( " CREATE TABLE IF NOT EXISTS `license` (`key` BINARY(64) NOT NULL PRIMARY KEY, `type` INT, `deleted` BOOL, `issuer` TEXT) " ) ;
CTBL ( " CREATE TABLE IF NOT EXISTS `license_info` (`key` BINARY(64) NOT NULL PRIMARY KEY, `username` VARCHAR(128), `first_name` TEXT, `last_name` TEXT, `email` TEXT, `begin` BIGINT, `end` BIGINT, `generated` BIGINT) " ) ;
CTBL ( " CREATE TABLE IF NOT EXISTS `license_request` (`key` BINARY(64) NOT NULL, `ip` TEXT, `timestamp` BIGINT, `result` INT) " ) ;
CTBL ( " CREATE TABLE IF NOT EXISTS `general` (`key` VARCHAR(64), `value` TEXT) " ) ;
CTBL ( " INSERT INTO `general` (`key`, `value`) VALUES ('version', '0'); " ) ;
SET_VERSION ( 0 ) ;
case 0 :
2020-02-06 07:49:39 -05:00
logMessage ( LOG_GENERAL , " Updating database! To version 1 " ) ;
2019-07-17 13:37:18 -04:00
CTBL ( " CREATE TABLE IF NOT EXISTS `history_speach` (`keyId` INT, `timestamp` BIGINT, `total` BIGINT, `dead` BIGINT, `online` BIGINT, `varianz` BIGINT) " ) ;
CTBL ( " CREATE TABLE IF NOT EXISTS `history_online` (`keyId` INT, `timestamp` BIGINT, `server` INT, `clients` INT, `web` INT, `music` INT, `queries` INT) " ) ;
CIDX ( " CREATE INDEX `license_key` ON `license` (`key`) " ) ;
CIDX ( " CREATE INDEX `license_info_key` ON `license_info` (`key`) " ) ;
CTBL ( " ALTER TABLE `license` DROP PRIMARY KEY, ADD `keyId` INTEGER NOT NULL AUTO_INCREMENT PRIMARY KEY FIRST; " ) ;
CTBL ( " ALTER TABLE `license_info` DROP PRIMARY KEY, ADD `keyId` INT NOT NULL PRIMARY KEY AUTO_INCREMENT FIRST; " ) ;
//Fixing key id's
CTBL ( " UPDATE `license_info` LEFT JOIN `license` ON `license`.`key` = `license_info`.`key` SET `license_info`.`keyId` = `license`.`keyId` " ) ;
//Deleting the key blob and removing auto
CTBL ( " ALTER TABLE `license_info` CHANGE `keyId` `keyId` INT NOT NULL UNIQUE, DROP COLUMN `key` " ) ;
//Fixing request table
CTBL ( " ALTER TABLE license_request ADD COLUMN `keyId` INT(0) NOT NULL; " ) ;
CTBL ( " UPDATE license_request LEFT JOIN license ON license.key = license_request.key SET license_request.keyId = license.keyId; " ) ;
CTBL ( " ALTER TABLE license_request DROP COLUMN `key`; " ) ;
//IDK why but i failed stuff :D
CTBL ( " ALTER TABLE license DROP PRIMARY KEY, CHANGE `keyId` `keyId` INT NOT NULL AUTO_INCREMENT PRIMARY KEY " ) ;
SET_VERSION ( 1 ) ;
case 1 :
CTBL ( " ALTER TABLE history_online ADD COLUMN `ip` TEXT AFTER `keyId`; " ) ;
CTBL ( " ALTER TABLE history_speach ADD COLUMN `ip` TEXT AFTER `keyId`; " ) ;
SET_VERSION ( 2 ) ;
case 2 :
CTBL ( " CREATE TABLE `history_version` (`keyId` INT, `ip` VARCHAR(64), `timestamp` BIGINT, `version` VARCHAR(126)); " ) ;
SET_VERSION ( 3 ) ;
case 3 :
/*
CTBL ( " CREATE TABLE `save_history_online` AS SELECT * FROM `history_online`; " ) ;
CTBL ( " CREATE TABLE `save_history_speach` AS SELECT * FROM `history_speach`; " ) ;
CTBL ( " CREATE TABLE `save_history_version` AS SELECT * FROM `history_version`; " ) ;
*/
CTBL ( " ALTER TABLE `history_online` CHANGE `ip` `unique_id` VARCHAR(64) NOT NULL; " ) ;
CTBL ( " ALTER TABLE `history_speach` CHANGE `ip` `unique_id` VARCHAR(64) NOT NULL; " ) ;
CTBL ( " ALTER TABLE `history_version` CHANGE `ip` `unique_id` VARCHAR(64) NOT NULL; " ) ;
CTBL ( " ALTER TABLE `license_request` ADD `unique_id` VARCHAR(64) NOT NULL AFTER `ip`; " ) ;
SET_VERSION ( 4 ) ;
case 4 :
CTBL ( " CREATE TABLE IF NOT EXISTS `users` (`username` VARCHAR(64) NOT NULL PRIMARY KEY, `password_hash` VARCHAR(128), `status` INT, `owner` VARCHAR(64)) " ) ;
2020-02-28 05:24:07 -05:00
SET_VERSION ( 5 ) ;
case 5 :
CTBL ( " CREATE TABLE `license_upgrades` (`upgrade_id` INT PRIMARY KEY NOT NULL, `old_key_id` INT, `new_key_id` INT, `timestamp_begin` BIGINT, `timestamp_end` BIGINT, `valid` INT, `use_count` INT, `license` BLOB); " ) ;
CTBL ( " ALTER TABLE `license_upgrades` ADD INDEX(`old_key_id`) " ) ;
CTBL ( " ALTER TABLE `license` ADD INDEX(`keyId`); " ) ;
CTBL ( " ALTER TABLE `license` ADD COLUMN `upgrade_id` INT DEFAULT 0; " ) ;
SET_VERSION ( 6 ) ;
case 6 :
CTBL ( " CREATE TABLE license_upgrade_log (`upgrade_id` INT, `timestamp` INT, `unique_id` VARCHAR(64), `server_ip` INT, `succeeded` TINYINT); " ) ;
CIDX ( " CREATE INDEX `upgrade_id_timestamp` ON `license_upgrade_log` (`upgrade_id`, `timestamp`) " ) ;
SET_VERSION ( 7 ) ;
2019-07-17 13:37:18 -04:00
default : ;
}
return true ;
}
2020-02-28 05:24:07 -05:00
bool DatabaseHandler : : register_license ( const std : : string & key , const shared_ptr < LicenseInfo > & info , const std : : string & issuer ) {
2019-07-17 13:37:18 -04:00
result res ;
res = command ( this - > database , " INSERT INTO `license` (`key`, `type`, `deleted`, `issuer`) VALUES (:key, :type, :deleted, :issuer) " , variable { " :key " , key } , variable { " :type " , ( uint32_t ) info - > type } , variable { " :deleted " , false } , variable { " :issuer " , issuer } ) . execute ( ) ;
if ( ! res ) {
2020-02-06 07:49:39 -05:00
logError ( LOG_GENERAL , " Could not register new license ( " + res . fmtStr ( ) + " ) " ) ;
2019-07-17 13:37:18 -04:00
return false ;
}
2020-02-28 05:24:07 -05:00
auto keyId = this - > id_cache - > get_key_id_from_key ( key ) ;
2019-07-17 13:37:18 -04:00
if ( keyId = = 0 ) return false ;
res = command ( this - > database , " INSERT INTO `license_info` (`keyId`, `username`, `first_name`, `last_name`, `email`, `begin`, `end`, `generated`) VALUES (:key, :username, :first_name, :last_name, :email, :begin, :end, :generated) " ,
variable { " :key " , keyId } ,
variable { " :username " , info - > username } ,
variable { " :first_name " , info - > first_name } ,
variable { " :last_name " , info - > last_name } ,
variable { " :email " , info - > email } ,
variable { " :generated " , duration_cast < milliseconds > ( info - > creation . time_since_epoch ( ) ) . count ( ) } ,
variable { " :begin " , duration_cast < milliseconds > ( info - > start . time_since_epoch ( ) ) . count ( ) } ,
variable { " :end " , duration_cast < milliseconds > ( info - > end . time_since_epoch ( ) ) . count ( ) }
) . execute ( ) ;
if ( ! res ) {
2020-02-06 07:49:39 -05:00
logError ( LOG_GENERAL , " Could not register new license info ( " + res . fmtStr ( ) + " ) " ) ;
2019-07-17 13:37:18 -04:00
return false ;
}
return true ;
}
2020-02-28 05:24:07 -05:00
bool DatabaseHandler : : delete_license ( const std : : string & key , bool full ) {
2019-07-17 13:37:18 -04:00
if ( full ) {
2020-02-28 05:24:07 -05:00
auto keyId = this - > id_cache - > get_key_id_from_key ( key ) ;
2019-07-17 13:37:18 -04:00
if ( keyId = = 0 ) return false ; //Never exists
auto res = command ( this - > database , " DELETE FROM `license` WHERE `key` = :key " , variable { " :key " , key } ) . execute ( ) ;
2020-02-06 07:49:39 -05:00
if ( ! res ) logError ( LOG_GENERAL , " Could not delete license ( " + res . fmtStr ( ) + " ) " ) ;
2019-07-17 13:37:18 -04:00
res = command ( this - > database , " DELETE FROM `license_info` WHERE `keyId` = :key " , variable { " :keyId " , keyId } ) . execute ( ) ;
2020-02-06 07:49:39 -05:00
if ( ! res ) logError ( LOG_GENERAL , " Could not delete license ( " + res . fmtStr ( ) + " ) " ) ;
2019-07-17 13:37:18 -04:00
return ! ! res ;
} else {
auto res = command ( this - > database , " UPDATE `license` SET `deleted` = :true WHERE `key` = :key " , variable { " :true " , true } , variable { " :key " , key } ) . execute ( ) ;
2020-02-06 07:49:39 -05:00
if ( ! res ) logError ( LOG_GENERAL , " Could not delete license ( " + res . fmtStr ( ) + " ) " ) ;
2019-07-17 13:37:18 -04:00
return ! ! res ;
}
}
2020-02-28 05:24:07 -05:00
bool DatabaseHandler : : validLicenseKey ( const std : : string & key ) {
2019-07-17 13:37:18 -04:00
bool valid = false ;
auto res = command ( this - > database , " SELECT * FROM `license` WHERE `key` = :key AND `deleted` = :false LIMIT 1 " , variable { " :false " , false } , variable { " :key " , key } ) . query ( [ ] ( bool * flag , int , char * * , char * * ) {
* flag = true ;
return 0 ;
} , & valid ) ;
2020-02-06 07:49:39 -05:00
if ( ! res ) logError ( LOG_GENERAL , " Could not validate license ( " + res . fmtStr ( ) + " ) " ) ;
2019-07-17 13:37:18 -04:00
return ! ! res & & valid ;
}
2020-02-28 05:24:07 -05:00
inline std : : map < std : : string , std : : shared_ptr < LicenseInfo > > query_license ( SqlManager * mgr , const std : : string & key , int offset , int length ) {
2019-07-17 13:37:18 -04:00
std : : map < std : : string , std : : shared_ptr < LicenseInfo > > result ;
2020-02-28 05:24:07 -05:00
auto query = string ( ) + " SELECT `key`, `username`, `first_name`, `last_name`, `email`, `begin`, `end`, `generated`, `deleted`, `upgrade_id` FROM `license_info` INNER JOIN `license` ON `license_info`.`keyId` = `license`.`keyId` " ;
2019-07-17 13:37:18 -04:00
if ( ! key . empty ( ) )
query + = " WHERE `key` = :key " ;
else
query + = " WHERE `deleted` = :false " ;
if ( offset > 0 | | length > 0 )
query + = " LIMIT " + to_string ( offset ) + " , " + to_string ( length ) ;
logTrace ( LOG_GENERAL , " Executing query {} " , query ) ;
auto res = command ( mgr , query , variable { " :key " , key } , variable { " :false " , false } ) . query ( [ ] ( std : : map < std : : string , std : : shared_ptr < LicenseInfo > > * list , int length , std : : string * values , std : : string * names ) {
auto info = make_shared < LicenseInfo > ( ) ;
info - > deleted = false ;
string k ;
for ( int index = 0 ; index < length ; index + + ) {
try {
if ( names [ index ] = = " username " )
info - > username = values [ index ] ;
else if ( names [ index ] = = " first_name " )
info - > first_name = values [ index ] ;
else if ( names [ index ] = = " key " )
k = values [ index ] ;
else if ( names [ index ] = = " last_name " )
info - > last_name = values [ index ] ;
else if ( names [ index ] = = " email " )
info - > email = values [ index ] ;
else if ( names [ index ] = = " begin " )
info - > start = system_clock : : time_point ( ) + milliseconds ( stoll ( values [ index ] ) ) ;
else if ( names [ index ] = = " end " )
info - > end = system_clock : : time_point ( ) + milliseconds ( stoll ( values [ index ] ) ) ;
else if ( names [ index ] = = " generated " )
info - > creation = system_clock : : time_point ( ) + milliseconds ( stoll ( values [ index ] ) ) ;
else if ( names [ index ] = = " deleted " )
info - > deleted = values [ index ] = = " 1 " | | values [ index ] = = " true " ;
2020-02-28 05:24:07 -05:00
else if ( names [ index ] = = " upgrade_id " )
info - > upgrade_id = std : : stol ( values [ index ] ) ;
2019-07-17 13:37:18 -04:00
else
logError ( LOG_GENERAL , " Unknown field {} " , names [ index ] ) ;
} catch ( std : : exception & ex ) {
logError ( LOG_GENERAL , " Failed to parse field {} ({}). Message: {} " , names [ index ] , values [ index ] , ex . what ( ) ) ;
return 1 ;
}
}
( * list ) [ k ] = info ;
return 0 ;
} , & result ) ;
logTrace ( LOG_GENERAL , " Query returned {} results " , result . size ( ) ) ;
2020-02-06 07:49:39 -05:00
if ( ! res ) logError ( LOG_GENERAL , " Could not query license ( " + res . fmtStr ( ) + " ) " ) ;
2019-07-17 13:37:18 -04:00
return result ;
}
2020-02-28 05:24:07 -05:00
std : : shared_ptr < LicenseInfo > DatabaseHandler : : query_license_info ( const std : : string & key ) {
2019-07-17 13:37:18 -04:00
auto result = query_license ( this - > database , key , 0 , 0 ) ;
if ( result . empty ( ) ) return nullptr ;
return result . begin ( ) - > second ;
}
2020-02-28 05:24:07 -05:00
std : : map < std : : string , std : : shared_ptr < LicenseInfo > > DatabaseHandler : : list_licenses ( int offset , int limit ) {
2019-07-17 13:37:18 -04:00
return query_license ( this - > database , " " , offset , limit ) ;
}
2020-02-28 05:24:07 -05:00
bool DatabaseHandler : : logRequest ( const std : : string & key , const std : : string & unique_id , const std : : string & ip , const std : : string & version , int state ) {
2019-07-17 13:37:18 -04:00
result res ;
2020-02-28 05:24:07 -05:00
auto keyId = this - > id_cache - > get_key_id_from_key ( key ) ;
2019-07-17 13:37:18 -04:00
if ( keyId = = 0 ) {
logError ( LOG_GENERAL , " Failed to log license request (could not resolve key id) " ) ;
return false ;
}
auto timestamp = duration_cast < milliseconds > ( system_clock : : now ( ) . time_since_epoch ( ) ) . count ( ) ;
//SELECT * FROM `license_info` WHERE `keyId` IN (SELECT `keyId` FROM `license` WHERE `key` = '000')
res = command ( this - > database , " INSERT INTO `license_request` (`keyId`, `ip`, `unique_id`, `timestamp`, `result`) VALUES (:key, :ip, :unique_id, :timestamp, :result) " ,
variable { " :key " , keyId } ,
variable { " :ip " , ip } ,
variable { " :timestamp " , timestamp } ,
variable { " :unique_id " , unique_id } ,
variable { " :result " , state } ) . execute ( ) ;
if ( ! res ) {
2020-02-06 07:49:39 -05:00
logError ( LOG_GENERAL , " Could not log license validation ( " + res . fmtStr ( ) + " ) " ) ;
2019-07-17 13:37:18 -04:00
return false ;
}
{ //Log version
res = command ( this - > database , " INSERT INTO `history_version`(`keyId`, `unique_id`, `timestamp`, `version`) VALUES (:key, :unique_id, :time, :version) " ,
variable { " :key " , keyId } ,
variable { " :time " , timestamp } ,
variable { " :unique_id " , unique_id } ,
variable { " :version " , version }
) . execute ( ) ;
if ( ! res )
2020-02-06 07:49:39 -05:00
logError ( LOG_GENERAL , " Could not log license version statistic ( " + res . fmtStr ( ) + " ) " ) ;
2019-07-17 13:37:18 -04:00
res = { } ;
}
return true ;
}
2020-02-28 05:24:07 -05:00
bool DatabaseHandler : : logStatistic ( const std : : string & key , const std : : string & unique_id , const std : : string & ip ,
const ts : : proto : : license : : PropertyUpdateRequest & data ) {
2019-07-17 13:37:18 -04:00
result res ;
2020-02-28 05:24:07 -05:00
auto keyId = this - > id_cache - > get_key_id_from_key ( key ) ;
2019-07-17 13:37:18 -04:00
if ( keyId = = 0 ) return false ;
auto time = duration_cast < milliseconds > ( system_clock : : now ( ) . time_since_epoch ( ) ) . count ( ) ;
{ //Log online
res = command ( this - > database , " INSERT INTO `history_online`(`keyId`, `unique_id`, `timestamp`, `server`, `clients`, `web`, `music`, `queries`) VALUES (:key, :unique_id, :time, :server, :client, :web, :music, :query) " ,
variable { " :key " , keyId } ,
variable { " :unique_id " , unique_id } ,
variable { " :time " , time } ,
variable { " :server " , data . servers_online ( ) } ,
variable { " :client " , data . clients_online ( ) } ,
variable { " :web " , data . web_clients_online ( ) } ,
variable { " :music " , data . bots_online ( ) } ,
variable { " :query " , data . queries_online ( ) }
) . execute ( ) ;
if ( ! res )
2020-02-06 07:49:39 -05:00
logError ( LOG_GENERAL , " Could not log license statistics ( " + res . fmtStr ( ) + " ) " ) ;
2019-07-17 13:37:18 -04:00
res = { } ;
}
//SELECT * FROM `license_info` WHERE `keyId` IN (SELECT `keyId` FROM `license` WHERE `key` = '000')
{
res = command ( this - > database , " INSERT INTO `history_speach`(`keyId`, `unique_id`, `timestamp`, `total`, `dead`, `online`, `varianz`) VALUES (:key, :unique_id, :time, :total, :dead, :online, :varianz) " ,
variable { " :key " , keyId } ,
variable { " :unique_id " , unique_id } ,
variable { " :time " , time } ,
variable { " :total " , data . speach_total ( ) } ,
variable { " :dead " , data . speach_dead ( ) } ,
variable { " :online " , data . speach_online ( ) } ,
variable { " :varianz " , data . speach_varianz ( ) }
) . execute ( ) ;
if ( ! res )
2020-02-06 07:49:39 -05:00
logError ( LOG_GENERAL , " Could not log license statistics ( " + res . fmtStr ( ) + " ) " ) ;
2019-07-17 13:37:18 -04:00
res = { } ;
}
return true ;
}
2020-02-28 05:24:07 -05:00
std : : shared_ptr < DatabaseHandler : : UserHistory > DatabaseHandler : : list_statistics_user ( const system_clock : : time_point & begin , const system_clock : : time_point & end , const milliseconds & interval ) {
2019-07-17 13:37:18 -04:00
auto timeout = hours ( 2 ) + minutes ( 10 ) ; //TODO timeout configurable?
2019-08-25 16:16:42 -04:00
/* initialize the result */
auto allocated_records = ( size_t ) floor ( ( end - begin ) / interval ) + 1 ;
auto _result = ( UserHistory * ) malloc ( sizeof ( UserHistory ) + sizeof ( GlobalUserStatistics ) * allocated_records ) ;
new ( _result ) UserHistory ( ) ;
auto result = shared_ptr < UserHistory > ( _result , [ ] ( UserHistory * ptr ) {
ptr - > ~ UserHistory ( ) ;
free ( ptr ) ;
} ) ;
result - > record_count = 0 ;
result - > begin = begin ;
result - > end = end ;
result - > interval = interval ;
memset ( & result - > history [ 0 ] , 0 , allocated_records * sizeof ( GlobalUserStatistics ) ) ;
auto info = & _result - > history [ 0 ] ;
/* temp db variables */
2020-02-28 05:24:07 -05:00
map < std : : string , DatabaseHandler : : DatabaseUserStatistics > server_statistics ;
2019-08-25 16:16:42 -04:00
bool have_key , have_uid ;
2020-02-28 05:24:07 -05:00
DatabaseHandler : : DatabaseUserStatistics temp_stats ; /* temp struct for stats parsing */
DatabaseHandler : : DatabaseUserStatistics * stats_ptr ; /* pointer to the target stats */
2019-08-25 16:16:42 -04:00
std : : chrono : : system_clock : : time_point current_timestamp = begin + interval ; /* upper limit of the current interval */
auto state = command ( this - > database , " SELECT * FROM `history_online` WHERE `timestamp` >= :timestamp_start AND `timestamp` <= :timestamp_end ORDER BY `timestamp` ASC " ,
2019-07-17 13:37:18 -04:00
variable { " :timestamp_start " , duration_cast < milliseconds > ( begin . time_since_epoch ( ) - timeout ) . count ( ) } ,
variable { " :timestamp_end " , duration_cast < milliseconds > ( end . time_since_epoch ( ) ) . count ( ) }
2019-08-25 16:16:42 -04:00
) . query ( [ & ] ( int columns , std : : string * values , std : : string * names ) {
/* find the user statistic ptr */
{
size_t key_id = 0 ;
std : : string unique_id ;
have_key = false ;
have_uid = false ;
for ( int index = 0 ; index < columns ; index + + ) {
2020-02-06 07:49:39 -05:00
if ( names [ index ] = = " keyId " ) {
key_id = strtol ( values [ index ] . c_str ( ) , nullptr , 10 ) ;
if ( key_id = = 0 ) return 0 ; /* invalid key id */
have_key = true ;
if ( have_key & & have_uid ) goto process_tag ;
} else if ( names [ index ] = = " unique_id " ) {
unique_id = values [ index ] ;
have_uid = true ;
if ( have_key & & have_uid ) goto process_tag ;
}
2019-08-25 16:16:42 -04:00
}
2020-02-06 07:49:39 -05:00
return 0 ; /* key or uid haven't been found */
2019-08-25 16:16:42 -04:00
2020-02-06 07:49:39 -05:00
process_tag :
2019-08-25 16:16:42 -04:00
stats_ptr = & server_statistics [ to_string ( key_id ) + " _ " + unique_id ] ;
}
2019-07-17 13:37:18 -04:00
for ( int index = 0 ; index < columns ; index + + ) {
2020-02-06 07:49:39 -05:00
if ( names [ index ] = = " timestamp " )
temp_stats . timestamp = system_clock : : time_point ( ) + milliseconds ( stoll ( values [ index ] ) ) ;
else if ( names [ index ] = = " server " )
temp_stats . servers_online = strtol ( values [ index ] . c_str ( ) , nullptr , 10 ) ;
else if ( names [ index ] = = " clients " )
temp_stats . clients_online = strtol ( values [ index ] . c_str ( ) , nullptr , 10 ) ;
else if ( names [ index ] = = " web " )
temp_stats . web_clients_online = strtol ( values [ index ] . c_str ( ) , nullptr , 10 ) ;
else if ( names [ index ] = = " music " )
temp_stats . bots_online = strtol ( values [ index ] . c_str ( ) , nullptr , 10 ) ;
else if ( names [ index ] = = " queries " )
temp_stats . queries_online = strtol ( values [ index ] . c_str ( ) , nullptr , 10 ) ;
2019-07-17 13:37:18 -04:00
}
2019-08-25 16:16:42 -04:00
/* because the query could only be oldest to newest */
while ( temp_stats . timestamp > current_timestamp ) {
assert ( _result - > record_count < allocated_records ) ; /* ensure we write to valid memory */
auto min_timestamp = current_timestamp - timeout ;
for ( auto & server : server_statistics ) {
auto & second = server . second ;
if ( second . timestamp < min_timestamp | | second . timestamp . time_since_epoch ( ) . count ( ) = = 0 )
continue ; /* last server request is too old to be counted */
info - > instance_online + + ;
2020-03-02 14:40:48 -05:00
info - > instance_empty + = second . web_clients_online = = 0 & & second . clients_online = = 0 ;
2019-08-25 16:16:42 -04:00
info - > queries_online + = second . queries_online ;
info - > bots_online + = second . bots_online ;
info - > web_clients_online + = second . web_clients_online ;
info - > clients_online + = second . clients_online ;
info - > servers_online + = second . servers_online ;
}
info + + ;
_result - > record_count + + ;
current_timestamp + = interval ; /* lets gather for the next interval */
}
/* write the "new" statistic */
memcpy ( stats_ptr , & temp_stats , sizeof ( temp_stats ) ) ;
2019-07-17 13:37:18 -04:00
return 0 ;
} ) ;
if ( ! state ) {
2020-02-06 07:49:39 -05:00
logError ( LOG_GENERAL , " Could not read license statistics ( " + state . fmtStr ( ) + " ) " ) ;
2019-07-17 13:37:18 -04:00
return { } ;
}
2019-08-25 16:16:42 -04:00
/* flush the last record */
do {
assert ( _result - > record_count < allocated_records ) ; /* ensure we write to valid memory */
2019-07-17 13:37:18 -04:00
2019-08-25 16:16:42 -04:00
auto min_timestamp = current_timestamp - timeout ;
2019-07-17 13:37:18 -04:00
for ( auto & server : server_statistics ) {
2019-08-25 16:16:42 -04:00
auto & second = server . second ;
if ( second . timestamp < min_timestamp )
continue ; /* last server request is too old to be counted */
info - > instance_online + + ;
2020-03-02 14:40:48 -05:00
info - > instance_empty + = second . web_clients_online = = 0 & & second . clients_online = = 0 ;
2019-08-25 16:16:42 -04:00
info - > queries_online + = second . queries_online ;
info - > bots_online + = second . bots_online ;
info - > web_clients_online + = second . web_clients_online ;
info - > clients_online + = second . clients_online ;
info - > servers_online + = second . servers_online ;
2019-07-17 13:37:18 -04:00
}
2019-08-25 16:16:42 -04:00
info + + ;
_result - > record_count + + ;
2019-07-17 13:37:18 -04:00
current_timestamp + = interval ;
2019-08-25 16:16:42 -04:00
} while ( current_timestamp < end ) ;
2019-07-17 13:37:18 -04:00
return result ;
}
2020-02-28 05:24:07 -05:00
std : : deque < std : : unique_ptr < DatabaseHandler : : GlobalVersionsStatistic > > DatabaseHandler : : list_statistics_version ( const std : : chrono : : system_clock : : time_point & begin , const std : : chrono : : system_clock : : time_point & end , const std : : chrono : : milliseconds & interval ) {
map < std : : string , deque < unique_ptr < DatabaseHandler : : GlobalVersionsStatistic > > > server_statistics ;
2019-07-17 13:37:18 -04:00
auto timeout = hours ( 2 ) + minutes ( 10 ) ; //TODO timeout configurable?
2019-08-25 16:16:42 -04:00
auto state = command ( this - > database , " SELECT * FROM `history_version` WHERE `timestamp` >= :timestamp_start AND `timestamp` <= :timestamp_end ORDER BY `timestamp` ASC " ,
2019-07-17 13:37:18 -04:00
variable { " :timestamp_start " , duration_cast < milliseconds > ( begin . time_since_epoch ( ) - timeout ) . count ( ) } ,
variable { " :timestamp_end " , duration_cast < milliseconds > ( end . time_since_epoch ( ) ) . count ( ) }
) . query ( [ & server_statistics ] ( int columns , std : : string * values , std : : string * names ) {
size_t key_id = 0 ;
std : : string unique_id ;
2020-02-28 05:24:07 -05:00
auto info = make_unique < DatabaseHandler : : GlobalVersionsStatistic > ( ) ;
2019-07-17 13:37:18 -04:00
for ( int index = 0 ; index < columns ; index + + ) {
try {
if ( names [ index ] = = " keyId " )
key_id = stoull ( values [ index ] ) ;
else if ( names [ index ] = = " unique_id " )
unique_id = values [ index ] ;
else if ( names [ index ] = = " timestamp " )
info - > timestamp = system_clock : : time_point ( ) + milliseconds ( stoll ( values [ index ] ) ) ;
else if ( names [ index ] = = " version " )
info - > versions [ values [ index ] ] = 1 ;
} catch ( std : : exception & ex ) {
2020-02-06 07:49:39 -05:00
logError ( LOG_GENERAL , " Failed to parse column " + names [ index ] + " => " + ex . what ( ) + " (Value: " + values [ index ] + " ) " ) ;
2019-07-17 13:37:18 -04:00
return 0 ;
}
}
if ( key_id = = 0 ) return 0 ;
server_statistics [ to_string ( key_id ) + " _ " + unique_id ] . push_back ( std : : move ( info ) ) ;
return 0 ;
} ) ;
if ( ! state ) {
2020-02-06 07:49:39 -05:00
logError ( LOG_GENERAL , " Could not read license statistics ( " + state . fmtStr ( ) + " ) " ) ;
2019-07-17 13:37:18 -04:00
return { } ;
}
2020-02-28 05:24:07 -05:00
std : : deque < std : : unique_ptr < DatabaseHandler : : GlobalVersionsStatistic > > result ;
2019-07-17 13:37:18 -04:00
system_clock : : time_point current_timestamp = begin ;
while ( current_timestamp < = end ) {
2020-02-28 05:24:07 -05:00
auto info = make_unique < DatabaseHandler : : GlobalVersionsStatistic > ( ) ;
2019-07-17 13:37:18 -04:00
info - > timestamp = current_timestamp ;
for ( auto & server : server_statistics ) {
while ( ! server . second . empty ( ) ) {
auto & first = * server . second . begin ( ) ;
if ( first - > timestamp > current_timestamp ) break ; //Entry within the future
if ( first - > timestamp + timeout < current_timestamp ) { //Entry within the past
server . second . pop_front ( ) ;
continue ;
}
if ( server . second . size ( ) > 1 ) {
auto & second = * ( server . second . begin ( ) + 1 ) ;
if ( second - > timestamp < = current_timestamp ) {
server . second . pop_front ( ) ; //The next entry is more up 2 date
continue ;
}
}
for ( const auto & entry : first - > versions )
info - > versions [ entry . first ] + = entry . second ;
break ;
}
}
result . push_back ( std : : move ( info ) ) ;
current_timestamp + = interval ;
}
return result ;
2020-02-28 05:24:07 -05:00
}
bool DatabaseHandler : : register_license_upgrade ( license_key_id_t old_key_id , license_key_id_t new_key_id ,
const std : : chrono : : system_clock : : time_point & begin_timestamp , const std : : chrono : : system_clock : : time_point & end_timestamp , const std : : string & license_key ) {
2020-03-19 19:56:36 -04:00
auto upgrade_id = std : : chrono : : ceil < std : : chrono : : milliseconds > ( std : : chrono : : system_clock : : now ( ) . time_since_epoch ( ) ) . count ( ) ;
2020-02-28 05:24:07 -05:00
auto sql_result = sql : : command ( this - > sql ( ) , " INSERT INTO `license_upgrades` (`upgrade_id`, `old_key_id`, `new_key_id`, `timestamp_begin`, `timestamp_end`, `valid`, `use_count`, `license`) VALUES "
" (:upgrade_id, :old_key_id, :new_key_id, :timestamp_begin, :timestamp_end, 1, 0, :license) " ,
variable { " :upgrade_id " , upgrade_id } ,
variable { " :old_key_id " , old_key_id } ,
variable { " :new_key_id " , new_key_id } ,
variable { " :timestamp_begin " , std : : chrono : : duration_cast < std : : chrono : : milliseconds > ( begin_timestamp . time_since_epoch ( ) ) . count ( ) } ,
variable { " :timestamp_end " , std : : chrono : : duration_cast < std : : chrono : : milliseconds > ( end_timestamp . time_since_epoch ( ) ) . count ( ) } ,
variable { " :license " , base64 : : decode ( license_key ) } ) . execute ( ) ;
if ( ! sql_result ) {
logError ( LOG_GENERAL , " Failed to insert license upgrade: {} " , sql_result . fmtStr ( ) ) ;
return false ;
}
sql_result = sql : : command ( this - > sql ( ) , " UPDATE `license` SET `upgrade_id` = :upgrade_id WHERE `keyId` = :key_id " ,
variable { " :upgrade_id " , upgrade_id } ,
variable { " :key_id " , old_key_id } ) . execute ( ) ;
if ( ! sql_result ) {
logError ( LOG_GENERAL , " Failed to set license upgrade: {} " , sql_result . fmtStr ( ) ) ;
return false ;
}
return true ;
}
std : : unique_ptr < LicenseUpgrade > DatabaseHandler : : query_license_upgrade ( upgrade_id_t id ) {
std : : unique_ptr < LicenseUpgrade > result { } ;
auto sql_result = sql : : command ( this - > sql ( ) , " SELECT `upgrade_id`, `old_key_id`, `new_key_id`, `timestamp_begin`, `timestamp_end`, `valid`, `use_count`, `license` FROM `license_upgrades` WHERE `upgrade_id` = :upgrade_id LIMIT 1 " ,
variable { " :upgrade_id " , id } ) . query ( [ & ] ( int length , std : : string * values , std : : string * names ) {
result = std : : make_unique < LicenseUpgrade > ( ) ;
for ( size_t index = 0 ; index < length ; index + + ) {
try {
if ( names [ index ] = = " upgrade_id " )
result - > upgrade_id = std : : stoull ( values [ index ] ) ;
else if ( names [ index ] = = " old_key_id " )
result - > old_license_key_id = std : : stoull ( values [ index ] ) ;
else if ( names [ index ] = = " new_key_id " )
result - > new_license_key_id = std : : stoull ( values [ index ] ) ;
else if ( names [ index ] = = " timestamp_begin " )
result - > begin_timestamp = std : : chrono : : system_clock : : time_point { } + std : : chrono : : milliseconds { std : : stoull ( values [ index ] ) } ;
else if ( names [ index ] = = " timestamp_end " )
result - > end_timestamp = std : : chrono : : system_clock : : time_point { } + std : : chrono : : milliseconds { std : : stoull ( values [ index ] ) } ;
else if ( names [ index ] = = " use_count " )
result - > update_count = std : : stoull ( values [ index ] ) ;
else if ( names [ index ] = = " valid " )
result - > valid = std : : stoull ( values [ index ] ) > 0 ;
else if ( names [ index ] = = " license " )
result - > license_key = base64 : : encode ( values [ index ] ) ;
} catch ( std : : exception & ex ) {
result = nullptr ;
logWarning ( LOG_GENERAL , " Failed to parse column {} for upgrade id {}. (Value: {}) " , names [ index ] , id , values [ index ] ) ;
return ;
}
}
} ) ;
if ( ! sql_result ) {
logWarning ( LOG_GENERAL , " Failed to query license upgrade info for upgrade {}: {} " , id , sql_result . fmtStr ( ) ) ;
return nullptr ;
}
return result ;
}
void DatabaseHandler : : log_license_upgrade_attempt ( upgrade_id_t upgrade_id , bool succeeded , const std : : string & unique_id , const std : : string & ip ) {
auto result = sql : : command ( this - > sql ( ) , " INSERT INTO `license_upgrade_log` (`upgrade_id`, `timestamp`, `unique_id`, `server_ip`, `succeeded`) VALUES (:upgrade_id, :timestamp, :unique_id, :server_ip, :succeeded); " ,
variable { " :upgrade_id " , upgrade_id } ,
variable { " :timestamp " , std : : chrono : : duration_cast < std : : chrono : : milliseconds > ( std : : chrono : : system_clock : : now ( ) . time_since_epoch ( ) ) . count ( ) } ,
variable { " :unique_id " , unique_id } ,
variable { " :server_ip " , ip } ,
variable { " :succeeded " , succeeded } ) . execute ( ) ;
if ( ! result )
logWarning ( LOG_GENERAL , " Failed to insert upgrade log into database ({}) " , result . fmtStr ( ) ) ;
if ( succeeded ) {
result = sql : : command ( this - > sql ( ) , " UPDATE `license_upgrades` SET `use_count` = `use_count` + 1 WHERE `upgrade_id` = :upgrade_id " ,
variable { " :upgrade_id " , upgrade_id } ) . execute ( ) ;
if ( ! result )
logWarning ( LOG_GENERAL , " Failed to increase upgrade use count MySQL ({}) " , result . fmtStr ( ) ) ;
}
2019-07-17 13:37:18 -04:00
}