Updated to 1.4.13 ;)

This commit is contained in:
WolverinDEV 2020-04-16 14:05:58 +02:00
parent 9705f84bc0
commit b79b496ad1
11 changed files with 411 additions and 83 deletions

@ -1 +1 @@
Subproject commit e7befd4fc9c96b966b459ea5ad8530dc8fe9345b Subproject commit c4f7fdb5d692ea376ac2b7bef4fbb0b81c6ea6c5

View File

@ -560,14 +560,16 @@ std::shared_ptr<Properties> DatabaseHelper::default_properties_client(std::share
return properties; return properties;
} }
std::mutex DatabaseHelper::database_id_mutex{};
bool DatabaseHelper::assignDatabaseId(sql::SqlManager *sql, ServerId id, std::shared_ptr<DataClient> cl) { bool DatabaseHelper::assignDatabaseId(sql::SqlManager *sql, ServerId id, std::shared_ptr<DataClient> cl) {
cl->loadDataForCurrentServer(); cl->loadDataForCurrentServer();
if(cl->getClientDatabaseId() == 0){ //Client does not exist if(cl->getClientDatabaseId() == 0){ //Client does not exist
ClientDbId cldbid = 0; ClientDbId new_client_database_id{0};
auto res = sql::command(sql, "SELECT `cldbid` FROM `clients` WHERE `serverId` = 0 AND `clientUid` = :cluid", variable{":cluid", cl->getUid()}).query([](ClientDbId* ptr, int length, char** values, char** names){ auto res = sql::command(sql, "SELECT `cldbid` FROM `clients` WHERE `serverId` = 0 AND `clientUid` = :cluid", variable{":cluid", cl->getUid()}).query([](ClientDbId* ptr, int length, char** values, char** names){
*ptr = static_cast<ClientDbId>(stoll(values[0])); *ptr = static_cast<ClientDbId>(stoll(values[0]));
return 0; return 0;
}, &cldbid); }, &new_client_database_id);
auto pf = LOG_SQL_CMD; auto pf = LOG_SQL_CMD;
pf(res); pf(res);
if(!res) return false; if(!res) return false;
@ -576,25 +578,26 @@ bool DatabaseHelper::assignDatabaseId(sql::SqlManager *sql, ServerId id, std::sh
variable{":cluid", cl->getUid()}, variable{":name", cl->getDisplayName()}, variable{":cluid", cl->getUid()}, variable{":name", cl->getDisplayName()},
variable{":fconnect", duration_cast<seconds>(system_clock::now().time_since_epoch()).count()}, variable{":lconnect", 0}, variable{":fconnect", duration_cast<seconds>(system_clock::now().time_since_epoch()).count()}, variable{":lconnect", 0},
variable{":connections", 0}); variable{":connections", 0});
if(cldbid == 0){ //Completly new user if(new_client_database_id == 0) { /* we've a completely new user */
res = sql::command(sql, "SELECT `cldbid` FROM `clients` WHERE `serverId` = 0 ORDER BY `cldbid` DESC LIMIT 1").query([](ClientDbId* ptr, int length, char** values, char** names){ std::lock_guard db_id_lock{DatabaseHelper::database_id_mutex};
*ptr = static_cast<ClientDbId>(stoll(values[0])); res = sql::command(sql, "SELECT `cldbid` FROM `clients` WHERE `serverId` = 0 ORDER BY `cldbid` DESC LIMIT 1").query([&](int length, std::string* values, std::string* names) {
return 0; assert(length == 1);
}, &cldbid); new_client_database_id = (ClientDbId) stoll(values[0]);
pf(res); });
LOG_SQL_CMD(res);
if(!res) return false; if(!res) return false;
cldbid += 1; new_client_database_id += 1;
res = insertTemplate.command().values(variable{":serverId", 0}, variable{":cldbid", cldbid}).execute(); //Insert global res = insertTemplate.command().values(variable{":serverId", 0}, variable{":cldbid", new_client_database_id}).execute(); //Insert global
pf(res); LOG_SQL_CMD(res);
if(!res) return false; if(!res) return false;
debugMessage(id, "Having new instance user on server {}. (Database ID: {} Name: {})", id, cldbid, cl->getDisplayName()); debugMessage(LOG_INSTANCE, "Registered a new client. Unique id: {}, First server: {}, Database ID: {}", cl->getUid(), id, new_client_database_id);
} else { } else {
debugMessage(id, "Having new server user on server {}. (Database ID: {} Name: {})", id, cldbid, cl->getDisplayName()); debugMessage(id, "Having new client, which is already known on this instance. Unique id: {}, First server: {}, Database ID: {}", cl->getUid(), id, new_client_database_id);
} }
if(id != 0){ //Else already inserted if(id != 0){ //Else already inserted
res = insertTemplate.command().values(variable{":serverId", id}, variable{":cldbid", cldbid}).execute(); res = insertTemplate.command().values(variable{":serverId", id}, variable{":cldbid", new_client_database_id}).execute();
pf(res); pf(res);
if(!res) return false; if(!res) return false;
} }
@ -602,7 +605,7 @@ bool DatabaseHelper::assignDatabaseId(sql::SqlManager *sql, ServerId id, std::sh
return assignDatabaseId(sql, id, cl); return assignDatabaseId(sql, id, cl);
} }
logTrace(id, "Loaded client from database. Database id: {} Unique id: {}", cl->getClientDatabaseId(), cl->getUid()); logTrace(id, "Loaded client successfully from database. Database id: {} Unique id: {}", cl->getClientDatabaseId(), cl->getUid());
return true; return true;
} }

View File

@ -78,6 +78,7 @@ namespace ts {
public: public:
static std::shared_ptr<Properties> default_properties_client(std::shared_ptr<Properties> /* properties */, ClientType /* type */); static std::shared_ptr<Properties> default_properties_client(std::shared_ptr<Properties> /* properties */, ClientType /* type */);
static bool assignDatabaseId(sql::SqlManager *, ServerId id, std::shared_ptr<DataClient>); static bool assignDatabaseId(sql::SqlManager *, ServerId id, std::shared_ptr<DataClient>);
static std::mutex database_id_mutex;
explicit DatabaseHelper(sql::SqlManager*); explicit DatabaseHelper(sql::SqlManager*);
~DatabaseHelper(); ~DatabaseHelper();

View File

@ -167,8 +167,7 @@ int GroupManager::insertGroupFromDb(int count, char **values, char **column) {
groupId = (GroupType) stoll(values[index]); groupId = (GroupType) stoll(values[index]);
else if(strcmp(column[index], "displayName") == 0) else if(strcmp(column[index], "displayName") == 0)
targetName = values[index]; targetName = values[index];
else if(strcmp(column[index], "serverId") == 0); //else cerr << "Invalid group table row " << column[index] << endl;
else cerr << "Invalid group table row " << column[index] << endl;
} }
if((size_t) groupId == 0 || (size_t) target == 0xff || (size_t) type == 0xff || targetName.empty()) { if((size_t) groupId == 0 || (size_t) target == 0xff || (size_t) type == 0xff || targetName.empty()) {

View File

@ -201,9 +201,11 @@ shared_ptr<VirtualServer> VirtualServerManager::findServerByPort(uint16_t port)
return nullptr; return nullptr;
} }
uint16_t VirtualServerManager::next_available_port() { uint16_t VirtualServerManager::next_available_port(const std::string& host_string) {
auto instances = this->serverInstances(); auto instances = this->serverInstances();
deque<uint16_t> unallowed_ports; std::vector<uint16_t> unallowed_ports{};
unallowed_ports.reserve(instances.size());
for(const auto& instance : instances) { for(const auto& instance : instances) {
unallowed_ports.push_back(instance->properties()[property::VIRTUALSERVER_PORT].as<uint16_t>()); unallowed_ports.push_back(instance->properties()[property::VIRTUALSERVER_PORT].as<uint16_t>());
@ -214,18 +216,39 @@ uint16_t VirtualServerManager::next_available_port() {
} }
} }
} }
auto bindings = net::resolve_bindings(host_string, 0);
uint16_t port = config::voice::default_voice_port; uint16_t port = config::voice::default_voice_port;
while(true) { while(true) {
if(port < 1024) goto c; if(port < 1024) goto next_port;
for(auto& p : unallowed_ports) { for(auto& p : unallowed_ports) {
if(p == port) if(p == port)
goto c; goto next_port;
}
for(auto& binding : bindings) {
if(!std::get<2>(binding).empty()) continue; /* error on that */
auto& baddress = std::get<1>(binding);
auto& raw_port = baddress.ss_family == AF_INET ? ((sockaddr_in*) &baddress)->sin_port : ((sockaddr_in6*) &baddress)->sin6_port;
raw_port = htons(port);
switch (net::address_available(baddress, net::binding_type::TCP)) {
case net::binding_result::ADDRESS_USED:
goto next_port;
default:
break; /* if we've an internal error we ignore it */
}
switch (net::address_available(baddress, net::binding_type::UDP)) {
case net::binding_result::ADDRESS_USED:
goto next_port;
default:
break; /* if we've an internal error we ignore it */
}
} }
break; break;
c: next_port:
port++; port++;
} }
return port; return port;
@ -317,6 +340,7 @@ shared_ptr<VirtualServer> VirtualServerManager::create_server(std::string hosts,
if(!sid_success) if(!sid_success)
return nullptr; return nullptr;
this->delete_server_in_db(serverId); /* just to ensure */
sql::command(this->handle->getSql(), "INSERT INTO `servers` (`serverId`, `host`, `port`) VALUES (:sid, :host, :port)", variable{":sid", serverId}, variable{":host", hosts}, variable{":port", port}).executeLater().waitAndGetLater(LOG_SQL_CMD, {1, "future failed"}); sql::command(this->handle->getSql(), "INSERT INTO `servers` (`serverId`, `host`, `port`) VALUES (:sid, :host, :port)", variable{":sid", serverId}, variable{":host", hosts}, variable{":port", port}).executeLater().waitAndGetLater(LOG_SQL_CMD, {1, "future failed"});
auto prop_copy = sql::command(this->handle->getSql(), "INSERT INTO `properties` (`serverId`, `type`, `id`, `key`, `value`) SELECT :target_sid AS `serverId`, `type`, `id`, `key`, `value` FROM `properties` WHERE `type` = :type AND `id` = 0 AND `serverId` = 0;", auto prop_copy = sql::command(this->handle->getSql(), "INSERT INTO `properties` (`serverId`, `type`, `id`, `key`, `value`) SELECT :target_sid AS `serverId`, `type`, `id`, `key`, `value` FROM `properties` WHERE `type` = :type AND `id` = 0 AND `serverId` = 0;",

View File

@ -38,7 +38,7 @@ namespace ts::server {
std::shared_ptr<VirtualServer> findServerById(ServerId); std::shared_ptr<VirtualServer> findServerById(ServerId);
std::shared_ptr<VirtualServer> findServerByPort(uint16_t); std::shared_ptr<VirtualServer> findServerByPort(uint16_t);
uint16_t next_available_port(); uint16_t next_available_port(const std::string& /* host string */);
ServerId next_available_server_id(bool& /* success */); ServerId next_available_server_id(bool& /* success */);
std::deque<std::shared_ptr<VirtualServer>> serverInstances(){ std::deque<std::shared_ptr<VirtualServer>> serverInstances(){

View File

@ -102,7 +102,7 @@ std::shared_ptr<BasicChannel> ServerChannelTree::createChannel(ChannelId parentI
channel->properties()[property::CHANNEL_LAST_LEFT] = chrono::duration_cast<chrono::milliseconds>(chrono::system_clock::now().time_since_epoch()).count(); channel->properties()[property::CHANNEL_LAST_LEFT] = chrono::duration_cast<chrono::milliseconds>(chrono::system_clock::now().time_since_epoch()).count();
auto result = sql::command(this->sql, "INSERT INTO `channels` (`serverId`, `channelId`, `type`, `parentId`) VALUES(:sid, :chid, :type, :parent);", variable{":sid", this->getServerId()}, variable{":chid", channel->channelId()}, variable{":type", channel->channelType()}, variable{":parent", channel->parent() ? channel->parent()->channelId() : 0}).execute(); auto result = sql::command(this->sql, "INSERT INTO `channels` (`serverId`, `channelId`, `parentId`) VALUES(:sid, :chid, :parent);", variable{":sid", this->getServerId()}, variable{":chid", channel->channelId()}, variable{":parent", channel->parent() ? channel->parent()->channelId() : 0}).execute();
auto pf = LOG_SQL_CMD; auto pf = LOG_SQL_CMD;
pf(result); pf(result);
@ -493,7 +493,7 @@ bool ServerChannelTree::validateChannelIcons() {
} }
void ServerChannelTree::loadChannelsFromDatabase() { void ServerChannelTree::loadChannelsFromDatabase() {
auto res = sql::command(this->sql, "SELECT * FROM `channels` WHERE `serverId` = :sid", variable{":sid", this->getServerId()}).query(&ServerChannelTree::loadChannelFromData, this); auto res = sql::command(this->sql, "SELECT `channelId`, `parentId` FROM `channels` WHERE `serverId` = :sid", variable{":sid", this->getServerId()}).query(&ServerChannelTree::loadChannelFromData, this);
(LOG_SQL_CMD)(res); (LOG_SQL_CMD)(res);
if(!res){ if(!res){
logError(this->getServerId(), "Could not load channel tree from database"); logError(this->getServerId(), "Could not load channel tree from database");
@ -512,7 +512,6 @@ void ServerChannelTree::loadChannelsFromDatabase() {
int ServerChannelTree::loadChannelFromData(int argc, char **data, char **column) { int ServerChannelTree::loadChannelFromData(int argc, char **data, char **column) {
ChannelId channelId = 0; ChannelId channelId = 0;
ChannelId parentId = 0; ChannelId parentId = 0;
auto type = static_cast<ChannelType::ChannelType>(0xFF);
int index = 0; int index = 0;
@ -520,8 +519,6 @@ int ServerChannelTree::loadChannelFromData(int argc, char **data, char **column)
for(index = 0; index < argc; index++){ for(index = 0; index < argc; index++){
if(strcmp(column[index], "channelId") == 0) channelId = static_cast<ChannelId>(stoll(data[index])); if(strcmp(column[index], "channelId") == 0) channelId = static_cast<ChannelId>(stoll(data[index]));
else if(strcmp(column[index], "parentId") == 0) parentId = static_cast<ChannelId>(stoll(data[index])); else if(strcmp(column[index], "parentId") == 0) parentId = static_cast<ChannelId>(stoll(data[index]));
else if(strcmp(column[index], "type") == 0) type = static_cast<ChannelType::ChannelType>(stoll(data[index]));
else if(strcmp(column[index], "serverId") == 0) {}
else logError(this->getServerId(), "ServerChannelTree::loadChannelFromData called with invalid column from sql \"{}\"", column[index]); else logError(this->getServerId(), "ServerChannelTree::loadChannelFromData called with invalid column from sql \"{}\"", column[index]);
} }
} catch (std::exception& ex) { } catch (std::exception& ex) {

View File

@ -101,12 +101,15 @@ command_result QueryClient::handleCommand(Command& cmd) {
case string_hash("bindinglist"): case string_hash("bindinglist"):
return this->handleCommandBindingList(cmd); return this->handleCommandBindingList(cmd);
case string_hash("serversnapshotdeploy"): { case string_hash("serversnapshotdeploy"): {
//return this->handleCommandServerSnapshotDeploy(cmd); #if 1
return this->handleCommandServerSnapshotDeploy(cmd);
#else
auto cmd_str = cmd.build(); auto cmd_str = cmd.build();
ts::command_parser parser{cmd_str}; ts::command_parser parser{cmd_str};
if(!parser.parse(true)) return command_result{error::vs_critical}; if(!parser.parse(true)) return command_result{error::vs_critical};
return this->handleCommandServerSnapshotDeployNew(parser); return this->handleCommandServerSnapshotDeployNew(parser);
#endif
} }
case string_hash("serversnapshotcreate"): case string_hash("serversnapshotcreate"):
return this->handleCommandServerSnapshotCreate(cmd); return this->handleCommandServerSnapshotCreate(cmd);
@ -587,9 +590,9 @@ command_result QueryClient::handleCommandServerCreate(Command& cmd) {
time_wait = duration_cast<milliseconds>(end - start); time_wait = duration_cast<milliseconds>(end - start);
} }
uint16_t freePort = serverInstance->getVoiceServerManager()->next_available_port();
std::string host = cmd[0].has("virtualserver_host") ? cmd["virtualserver_host"].as<string>() : config::binding::DefaultVoiceHost; std::string host = cmd[0].has("virtualserver_host") ? cmd["virtualserver_host"].as<string>() : config::binding::DefaultVoiceHost;
uint16_t freePort = serverInstance->getVoiceServerManager()->next_available_port(host);
uint16_t port = cmd[0].has("virtualserver_port") ? cmd["virtualserver_port"].as<uint16_t>() : freePort; uint16_t port = cmd[0].has("virtualserver_port") ? cmd["virtualserver_port"].as<uint16_t>() : freePort;
{ {
auto _start = system_clock::now(); auto _start = system_clock::now();
@ -844,7 +847,7 @@ command_result QueryClient::handleCommandServerSnapshotDeploy(Command& cmd) {
unique_lock server_create_lock(serverInstance->getVoiceServerManager()->server_create_lock); unique_lock server_create_lock(serverInstance->getVoiceServerManager()->server_create_lock);
if(port == 0) if(port == 0)
port = serverInstance->getVoiceServerManager()->next_available_port(); port = serverInstance->getVoiceServerManager()->next_available_port(host);
auto result = serverInstance->getVoiceServerManager()->createServerFromSnapshot(this->server, host, port, cmd, error); auto result = serverInstance->getVoiceServerManager()->createServerFromSnapshot(this->server, host, port, cmd, error);
server_create_lock.unlock(); server_create_lock.unlock();
auto end = system_clock::now(); auto end = system_clock::now();

View File

@ -445,6 +445,7 @@ ROLLBACK;
return false; return false;
} }
} }
/* update the client table */ /* update the client table */
{ {
result = sql::command(this->sql(), "CREATE TABLE `clients_v2` (`serverId` INT NOT NULL, `cldbid` INTEGER, `original_client_id` INTEGER DEFAULT 0, `clientUid` VARCHAR(64) NOT NULL, `firstConnect` BIGINT DEFAULT 0, `lastConnect` BIGINT DEFAULT 0, `connections` INT DEFAULT 0, `lastName` VARCHAR(128) DEFAULT '', UNIQUE(`serverId`, `clientUid`));").execute(); result = sql::command(this->sql(), "CREATE TABLE `clients_v2` (`serverId` INT NOT NULL, `cldbid` INTEGER, `original_client_id` INTEGER DEFAULT 0, `clientUid` VARCHAR(64) NOT NULL, `firstConnect` BIGINT DEFAULT 0, `lastConnect` BIGINT DEFAULT 0, `connections` INT DEFAULT 0, `lastName` VARCHAR(128) DEFAULT '', UNIQUE(`serverId`, `clientUid`));").execute();
@ -468,6 +469,10 @@ ROLLBACK;
error = "failed to rename new clients table to the old clients table (" + result.fmtStr() + ")"; error = "failed to rename new clients table to the old clients table (" + result.fmtStr() + ")";
return false; return false;
} }
CREATE_INDEX("clients", "serverId");
CREATE_INDEX2R("clients", "serverId", "clientUid");
CREATE_INDEX2R("clients", "serverId", "cldbid");
} }
db_version(12); db_version(12);
default: default:

View File

@ -17,6 +17,8 @@ using namespace ts::server;
using SnapshotType = ts::server::snapshots::type; using SnapshotType = ts::server::snapshots::type;
using SnapshotVersion = ts::server::snapshots::version_t; using SnapshotVersion = ts::server::snapshots::version_t;
//FIXME: Music bots & Playlists
bool VirtualServerManager::deploy_snapshot(std::string &error, ServerId server_id, const command_parser &data) { bool VirtualServerManager::deploy_snapshot(std::string &error, ServerId server_id, const command_parser &data) {
if(data.bulk(0).has_key("version")) { if(data.bulk(0).has_key("version")) {
return this->deploy_ts3_snapshot(error, server_id, data); return this->deploy_ts3_snapshot(error, server_id, data);
@ -293,6 +295,7 @@ bool VirtualServerManager::deploy_raw_snapshot(std::string &error, ts::ServerId
} }
} }
std::map<ClientDbId, ChannelId> client_id_mapping{};
std::map<ChannelId, ChannelId> channel_id_mapping{}; std::map<ChannelId, ChannelId> channel_id_mapping{};
std::map<ChannelId, ChannelId> channel_group_id_mapping{}; std::map<ChannelId, ChannelId> channel_group_id_mapping{};
std::map<ChannelId, ChannelId> server_group_id_mapping{}; std::map<ChannelId, ChannelId> server_group_id_mapping{};
@ -302,24 +305,58 @@ bool VirtualServerManager::deploy_raw_snapshot(std::string &error, ts::ServerId
/* cleanup all old data */ /* cleanup all old data */
this->delete_server_in_db(server_id); this->delete_server_in_db(server_id);
/* register server & properties */
{
sql::InsertQuery insert_property_query{"properties",
sql::Column<ServerId>("serverId"),
sql::Column<property::PropertyType>("type"),
sql::Column<uint64_t>("id"),
sql::Column<std::string>("key"),
sql::Column<std::string>("value")
};
for(const auto& property : parsed_server.properties.list_properties(property::FLAG_SAVE)) {
if(!property.isModified()) continue;
insert_property_query.add_entry(
server_id,
property::PROP_TYPE_SERVER,
0,
std::string{property.type().name},
property.value()
);
}
{
auto result = insert_property_query.execute(this->handle->getSql(), true);
if(result.failed_entries.size() > 0)
logWarning(server_id, "Failed to insert all server properties into the database. Failed property count: {}", result.failed_entries.size());
}
//FIXME: Override host & port
auto result = sql::command{this->handle->getSql(), "INSERT INTO `servers` (`serverId`, `host`, `port`) VALUES (:sid, :host, :port)"}
.value(":sid", server_id)
.value(":host", parsed_server.properties[property::VIRTUALSERVER_HOST].value())
.value(":port", parsed_server.properties[property::VIRTUALSERVER_PORT].value())
.execute();
if(!result) {
error = "failed to register the server (" + result.fmtStr() + ")";
return false;
}
}
/* register clients */ /* register clients */
{ {
//`original_client_id` INTEGER DEFAULT 0
//CREATE TABLE `clients_v2` (`serverId` INT NOT NULL, `cldbid` INTEGER, `clientUid` VARCHAR(64) NOT NULL, `firstConnect` BIGINT DEFAULT 0, `lastConnect` BIGINT DEFAULT 0, `connections` INT DEFAULT 0, `lastName` VARCHAR(128) DEFAULT '', UNIQUE(`serverId`, `clientUid`));
sql::InsertQuery insert_general_query{"clients", sql::InsertQuery insert_general_query{"clients",
sql::Column<ServerId>("serverId"), sql::Column<ServerId>("serverId"),
sql::Column<ClientDbId>("original_client_id"), sql::Column<ClientDbId>("cldbid"),
sql::Column<std::string>("clientUid"), sql::Column<std::string>("clientUid")
sql::Column<int64_t>("firstConnect"),
sql::Column<int64_t>("lastConnect"),
sql::Column<uint64_t>("connections"),
sql::Column<std::string>("lastName")
}; };
sql::InsertQuery insert_server_query{"clients", sql::InsertQuery insert_server_query{"clients",
sql::Column<ServerId>("serverId"), sql::Column<ServerId>("serverId"),
sql::Column<ClientDbId>("cldbid"),
sql::Column<ClientDbId>("original_client_id"), sql::Column<ClientDbId>("original_client_id"),
sql::Column<std::string>("clientUid"), sql::Column<std::string>("clientUid"),
sql::Column<int64_t>("firstConnect"), sql::Column<int64_t>("firstConnect"),
@ -328,10 +365,36 @@ bool VirtualServerManager::deploy_raw_snapshot(std::string &error, ts::ServerId
sql::Column<std::string>("lastName") sql::Column<std::string>("lastName")
}; };
{
auto update_result = sql::command(this->handle->getSql(), "UPDATE `clients` SET `original_client_id` = 0 WHERE `original_client_id` = :sid AND `serverId` = 0", variable{":sid", server_id}).execute();
if(!update_result)
logWarning(server_id, "Failed to reset client id mapping for this server: {}", update_result.fmtStr());
}
std::unique_lock db_id_lock{DatabaseHelper::database_id_mutex};
ClientDbId new_client_database_id{0};
{
auto query_result = sql::command(this->handle->getSql(), "SELECT `cldbid` FROM `clients` WHERE `serverId` = 0 ORDER BY `cldbid` DESC LIMIT 1").query([&](int length, std::string* values, std::string* names) {
assert(length == 1);
new_client_database_id = (ClientDbId) stoll(values[0]);
});
if(!query_result) {
error = "failed to query the current client database index (" + query_result.fmtStr() + ")";
return false;
}
}
for(const auto& client : parsed_clients) { for(const auto& client : parsed_clients) {
/*
insert_general_query.add_entry( insert_general_query.add_entry(
0,
++new_client_database_id, /* this might, or might not be used as new id */
client.parsed_data.unique_id
);
/* we're updating the database id afterwards */
insert_server_query.add_entry(
server_id, server_id,
client.parsed_data.database_id, /* to keep stuff unique */
client.parsed_data.database_id, client.parsed_data.database_id,
client.parsed_data.unique_id, client.parsed_data.unique_id,
std::chrono::floor<std::chrono::seconds>(client.parsed_data.timestamp_created.time_since_epoch()).count(), std::chrono::floor<std::chrono::seconds>(client.parsed_data.timestamp_created.time_since_epoch()).count(),
@ -339,20 +402,32 @@ bool VirtualServerManager::deploy_raw_snapshot(std::string &error, ts::ServerId
client.parsed_data.client_total_connections, client.parsed_data.client_total_connections,
client.parsed_data.nickname client.parsed_data.nickname
); );
*/
insert_general_query.add_entry(
server_id,
client.parsed_data.database_id,
client.parsed_data.unique_id,
std::chrono::floor<std::chrono::seconds>(client.parsed_data.timestamp_created.time_since_epoch()).count()
);
} }
auto result = insert_general_query.execute(this->handle->getSql(), true); {
for(const auto& fail : result.failed_entries) auto result = insert_general_query.execute(this->handle->getSql(), true);
logWarning(server_id, "Failed to insert client {} into the database: {}", parsed_clients[std::get<0>(fail)], std::get<1>(fail).fmtStr()); for(const auto& fail : result.failed_entries)
logWarning(server_id, "Failed to insert client {} into the general database: {}", parsed_clients[std::get<0>(fail)].parsed_data.database_id, std::get<1>(fail).fmtStr());
}
db_id_lock.unlock();
sql::command{this->handle->getSql(), "SELECT `original_client_id`,`cldbid` FROM `clients` WHERE `serverId` = :sid;"} {
auto result = insert_server_query.execute(this->handle->getSql(), true);
for(const auto& fail : result.failed_entries)
logWarning(server_id, "Failed to insert client {} into the server database: {}", parsed_clients[std::get<0>(fail)].parsed_data.database_id, std::get<1>(fail).fmtStr());
}
{
auto update_result = sql::command{this->handle->getSql(), "UPDATE `clients` SET `cldbid` = (SELECT `cldbid` from `clients` AS `t` WHERE `t`.`clientUid` = `clients`.`clientUid` LIMIT 1) WHERE `serverId` = :sid;"}
.value(":sid", server_id)
.execute();
if(!update_result) {
error = "failed to update client database ids (" + update_result.fmtStr() + ")";
return false;
}
}
auto map_query_result = sql::command{this->handle->getSql(), "SELECT `original_client_id`,`cldbid` FROM `clients` WHERE `serverId` = :sid;"}
.value(":serverId", server_id) .value(":serverId", server_id)
.query([&](int length, std::string* values, std::string* names) { .query([&](int length, std::string* values, std::string* names) {
ClientDbId original_id{0}, new_id{0}; ClientDbId original_id{0}, new_id{0};
@ -363,8 +438,12 @@ bool VirtualServerManager::deploy_raw_snapshot(std::string &error, ts::ServerId
logWarning(server_id, "Failed to parse client database entry mapping for group id {} (New ID: {})", values[1], values[0]); logWarning(server_id, "Failed to parse client database entry mapping for group id {} (New ID: {})", values[1], values[0]);
return; return;
} }
server_group_id_mapping[original_id] = new_id; client_id_mapping[original_id] = new_id;
}); });
if(!map_query_result) {
error = "failed to query client dabase id mappings (" + map_query_result.fmtStr() + ")";
return false;
}
} }
/* channels */ /* channels */
@ -390,20 +469,98 @@ bool VirtualServerManager::deploy_raw_snapshot(std::string &error, ts::ServerId
} }
} }
//TODO: Insert them into the database
sql::InsertQuery insert_query{"channels",
sql::Column<ServerId>("serverId"),
sql::Column<ChannelId>("channelId"),
sql::Column<ChannelId>("parentId")
};
sql::InsertQuery insert_property_query{"properties",
sql::Column<ServerId>("serverId"),
sql::Column<property::PropertyType>("type"),
sql::Column<uint64_t>("id"),
sql::Column<std::string>("key"),
sql::Column<std::string>("value")
};
for(auto& channel : parsed_channels) {
auto channel_id = channel.properties[property::CHANNEL_ID].as<ChannelId>();
insert_query.add_entry(
server_id,
channel_id,
channel.properties[property::CHANNEL_PID].as<ChannelId>()
);
for(const auto& property : channel.properties.list_properties(property::FLAG_SAVE)) {
if(!property.isModified()) continue;
insert_property_query.add_entry(
server_id,
property::PROP_TYPE_CHANNEL,
channel_id,
std::string{property.type().name},
property.value()
);
}
}
{
auto result = insert_query.execute(this->handle->getSql(), true);
for(const auto& fail : result.failed_entries)
logWarning(server_id, "Failed to insert channel {} into the server database: {}", parsed_channels[std::get<0>(fail)].properties[property::CHANNEL_NAME].value(), std::get<1>(fail).fmtStr());
}
{
auto result = insert_property_query.execute(this->handle->getSql(), true);
if(result.failed_entries.size() > 0)
logWarning(server_id, "Failed to insert all channel properties into the database. Failed property cound: {}", result.failed_entries.size());
}
} }
/* channel permissions */ /* channel permissions */
{ {
sql::InsertQuery insert_query{"permissions",
sql::Column<ServerId>("serverId"),
sql::Column<permission::PermissionSqlType>("type"),
sql::Column<uint64_t>("id"),
sql::Column<ChannelId>("channelId"),
sql::Column<std::string>("permId"),
sql::Column<permission::PermissionValue>("value"),
sql::Column<permission::PermissionValue>("grant"),
sql::Column<bool>("flag_skip"),
sql::Column<bool>("flag_negate")
};
for(auto& entry : channel_permissions) { for(auto& entry : channel_permissions) {
auto new_id = channel_id_mapping.find(entry.id1); {
if(new_id == channel_id_mapping.end()) { auto new_id = channel_id_mapping.find(entry.id1);
error = "missing channel id mapping for channel permission entry"; if(new_id == channel_id_mapping.end()) {
return false; logWarning(server_id, "Missing channel id mapping for channel permission entry (channel id: {}). Skipping permission insert.", entry.id1);
continue;
}
entry.id1 = new_id->second;
}
for(const auto& permission : entry.permissions) {
insert_query.add_entry(
server_id,
permission::SQL_PERM_CHANNEL,
entry.id1,
0,
permission.type->name,
permission.value.has_value ? permission.value.value : permNotGranted,
permission.granted.has_value ? permission.granted.value : permNotGranted,
permission.flag_skip,
permission.flag_negate
);
} }
entry.id1 = new_id->second;
} }
auto result = insert_query.execute(this->handle->getSql(), true);
if(!result.failed_entries.empty())
logWarning(server_id, "Failed to insert all channel permissions into the database. Failed permission count: {}", result.failed_entries.size());
} }
/* server groups */ /* server groups */
@ -432,6 +589,41 @@ bool VirtualServerManager::deploy_raw_snapshot(std::string &error, ts::ServerId
}); });
} }
/* server group relations */
{
sql::InsertQuery insert_query{"assignedGroups",
sql::Column<ServerId>("serverId"),
sql::Column<ClientDbId>("cldbid"),
sql::Column<GroupId>("groupId"),
sql::Column<ChannelId>("channelId")
};
for(auto& relation : parsed_server_group_relations) {
for(auto& entry : relation.second) {
ClientId client_id{};
{
auto new_id = client_id_mapping.find(entry.client_id);
if(new_id == client_id_mapping.end()) {
logWarning(server_id, "Missing client id mapping for channel group relation permission entry (client id: {}). Skipping relation insert.", entry.client_id);
continue;
}
client_id = new_id->second;
}
insert_query.add_entry(
server_id,
client_id,
entry.group_id,
0
);
}
}
auto result = insert_query.execute(this->handle->getSql(), true);
if(!result.failed_entries.empty())
logWarning(server_id, "Failed to insert all server group relations into the database. Failed insert count: {}", result.failed_entries.size());
}
/* channel groups */ /* channel groups */
{ {
sql::model insert_model{this->handle->getSql(), "INSERT INTO `groups` (`serverId`, `target`, `type`, `displayName`, `original_id`) VALUES (:serverId, :target, :type, :name, :id)"}; sql::model insert_model{this->handle->getSql(), "INSERT INTO `groups` (`serverId`, `target`, `type`, `displayName`, `original_id`) VALUES (:serverId, :target, :type, :name, :id)"};
@ -458,44 +650,148 @@ bool VirtualServerManager::deploy_raw_snapshot(std::string &error, ts::ServerId
}); });
} }
#define INSERT_PERMISSION_COMMAND "INSERT INTO `permissions` (`serverId`, `type`, `id`, `channelId`, `permId`, `value`, `grant`, `flag_skip`, `flag_negate`) VALUES (:serverId, :type, :id, :chId, :permId, :value, :grant, :flag_skip, :flag_negate)" /* channel group relations */
/* client permissions */
{ {
sql::InsertQuery insert_query{"permissions", sql::InsertQuery insert_query{"assignedGroups",
sql::Column<ServerId>("serverId"), sql::Column<ServerId>("serverId"),
sql::Column<permission::PermissionSqlType>("type"), sql::Column<ClientDbId>("cldbid"),
sql::Column<uint64_t>("id"), sql::Column<GroupId>("groupId"),
sql::Column<ChannelId>("channelId"), sql::Column<ChannelId>("channelId")
sql::Column<std::string>("permId"),
sql::Column<permission::PermissionValue>("value"),
sql::Column<permission::PermissionValue>("grant"),
sql::Column<bool>("flag_skip"),
sql::Column<bool>("flag_negate"),
}; };
for(auto& permission : client_permissions) { for(auto& relation : parsed_channel_group_relations) {
ChannelId channel_id{};
{
auto new_id = channel_id_mapping.find(relation.first);
if(new_id == channel_id_mapping.end()) {
logWarning(server_id, "Missing channel id mapping for channel group relation entry (channel id: {}). Skipping relation insert.", relation.first);
continue;
}
channel_id = new_id->second;
}
for(auto& entry : relation.second) {
ClientId client_id{};
{
auto new_id = client_id_mapping.find(entry.client_id);
if(new_id == client_id_mapping.end()) {
logWarning(server_id, "Missing client id mapping for channel group relation permission entry (client id: {}, channel id: {}). Skipping relation insert.", entry.client_id, relation.first);
continue;
}
client_id = new_id->second;
}
insert_query.add_entry(
server_id,
client_id,
entry.group_id,
channel_id
);
}
} }
auto result = insert_query.execute(this->handle->getSql(), true); auto result = insert_query.execute(this->handle->getSql(), true);
if(!result.failed_entries.empty())
logWarning(server_id, "Failed to insert all channel group relations into the database. Failed insert count: {}", result.failed_entries.size());
} }
/* register clients in the database */ /* client permissions */
{ {
sql::InsertQuery insert_query{"permissions",
sql::Column<ServerId>("serverId"),
sql::Column<permission::PermissionSqlType>("type"),
sql::Column<uint64_t>("id"),
sql::Column<ChannelId>("channelId"),
sql::Column<std::string>("permId"),
sql::Column<permission::PermissionValue>("value"),
sql::Column<permission::PermissionValue>("grant"),
sql::Column<bool>("flag_skip"),
sql::Column<bool>("flag_negate"),
};
for(auto& entry : client_permissions) {
{
auto new_id = client_id_mapping.find(entry.id1);
if(new_id == client_id_mapping.end()) {
logWarning(server_id, "Missing client id mapping for client permission entry (client id: {}). Skipping permission insert.", entry.id1);
continue;
}
entry.id1 = new_id->second;
}
for(const auto& permission : entry.permissions) {
insert_query.add_entry(
server_id,
permission::SQL_PERM_USER,
entry.id1,
0,
permission.type->name,
permission.value.has_value ? permission.value.value : permNotGranted,
permission.granted.has_value ? permission.granted.value : permNotGranted,
permission.flag_skip,
permission.flag_negate
);
}
}
auto result = insert_query.execute(this->handle->getSql(), true);
if(!result.failed_entries.empty())
logWarning(server_id, "Failed to insert all client permissions into the database. Failed permission count: {}", result.failed_entries.size());
} }
/* client channel permissions */ /* client channel permissions */
{ {
sql::InsertQuery insert_query{"permissions",
sql::Column<ServerId>("serverId"),
sql::Column<permission::PermissionSqlType>("type"),
sql::Column<uint64_t>("id"),
sql::Column<ChannelId>("channelId"),
sql::Column<std::string>("permId"),
sql::Column<permission::PermissionValue>("value"),
sql::Column<permission::PermissionValue>("grant"),
sql::Column<bool>("flag_skip"),
sql::Column<bool>("flag_negate"),
};
for(auto& entry : client_channel_permissions) { for(auto& entry : client_channel_permissions) {
auto new_id = channel_id_mapping.find(entry.id1); {
if(new_id == channel_id_mapping.end()) { auto new_id = channel_id_mapping.find(entry.id1);
error = "missing channel id mapping for client channel permission entry"; if(new_id == channel_id_mapping.end()) {
return false; logWarning(server_id, "Missing channel id mapping for client channel permission entry (client id: {}, channel id: {}). Skipping permission insert.", entry.id2, entry.id1);
continue;
}
entry.id1 = new_id->second;
}
{
auto new_id = client_id_mapping.find(entry.id2);
if(new_id == client_id_mapping.end()) {
logWarning(server_id, "Missing client id mapping for client channel permission entry (client id: {}, channel id: {}). Skipping permission insert.", entry.id2, entry.id1);
continue;
}
entry.id2 = new_id->second;
}
for(const auto& permission : entry.permissions) {
insert_query.add_entry(
server_id,
permission::SQL_PERM_USER,
entry.id2,
(ChannelId) entry.id1,
permission.type->name,
permission.value.has_value ? permission.value.value : permNotGranted,
permission.granted.has_value ? permission.granted.value : permNotGranted,
permission.flag_skip,
permission.flag_negate
);
} }
entry.id1 = new_id->second;
} }
auto result = insert_query.execute(this->handle->getSql(), true);
if(!result.failed_entries.empty())
logWarning(server_id, "Failed to insert all client channel permissions into the database. Failed permission count: {}", result.failed_entries.size());
} }
} }
error = "not implemented"; error = "not implemented";

2
shared

@ -1 +1 @@
Subproject commit 16c2272fe4b479c55e6db6642d039a42b0774325 Subproject commit c31cc9d0ee8fbcd04ff3a76e8fc4ab8723127a84