diff --git a/git-teaspeak b/git-teaspeak index e7befd4..c4f7fdb 160000 --- a/git-teaspeak +++ b/git-teaspeak @@ -1 +1 @@ -Subproject commit e7befd4fc9c96b966b459ea5ad8530dc8fe9345b +Subproject commit c4f7fdb5d692ea376ac2b7bef4fbb0b81c6ea6c5 diff --git a/server/src/DatabaseHelper.cpp b/server/src/DatabaseHelper.cpp index 1d71aae..da8c580 100644 --- a/server/src/DatabaseHelper.cpp +++ b/server/src/DatabaseHelper.cpp @@ -560,14 +560,16 @@ std::shared_ptr DatabaseHelper::default_properties_client(std::share return properties; } +std::mutex DatabaseHelper::database_id_mutex{}; + bool DatabaseHelper::assignDatabaseId(sql::SqlManager *sql, ServerId id, std::shared_ptr cl) { cl->loadDataForCurrentServer(); 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){ *ptr = static_cast(stoll(values[0])); return 0; - }, &cldbid); + }, &new_client_database_id); auto pf = LOG_SQL_CMD; pf(res); 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{":fconnect", duration_cast(system_clock::now().time_since_epoch()).count()}, variable{":lconnect", 0}, variable{":connections", 0}); - if(cldbid == 0){ //Completly 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){ - *ptr = static_cast(stoll(values[0])); - return 0; - }, &cldbid); - pf(res); + if(new_client_database_id == 0) { /* we've a completely new user */ + std::lock_guard db_id_lock{DatabaseHelper::database_id_mutex}; + res = sql::command(sql, "SELECT `cldbid` FROM `clients` WHERE `serverId` = 0 ORDER BY `cldbid` DESC LIMIT 1").query([&](int length, std::string* values, std::string* names) { + assert(length == 1); + new_client_database_id = (ClientDbId) stoll(values[0]); + }); + LOG_SQL_CMD(res); if(!res) return false; - cldbid += 1; - res = insertTemplate.command().values(variable{":serverId", 0}, variable{":cldbid", cldbid}).execute(); //Insert global - pf(res); + new_client_database_id += 1; + res = insertTemplate.command().values(variable{":serverId", 0}, variable{":cldbid", new_client_database_id}).execute(); //Insert global + LOG_SQL_CMD(res); 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 { - 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 - 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); if(!res) return false; } @@ -602,7 +605,7 @@ bool DatabaseHelper::assignDatabaseId(sql::SqlManager *sql, ServerId id, std::sh 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; } diff --git a/server/src/DatabaseHelper.h b/server/src/DatabaseHelper.h index cce5a6c..4eee7de 100644 --- a/server/src/DatabaseHelper.h +++ b/server/src/DatabaseHelper.h @@ -78,6 +78,7 @@ namespace ts { public: static std::shared_ptr default_properties_client(std::shared_ptr /* properties */, ClientType /* type */); static bool assignDatabaseId(sql::SqlManager *, ServerId id, std::shared_ptr); + static std::mutex database_id_mutex; explicit DatabaseHelper(sql::SqlManager*); ~DatabaseHelper(); diff --git a/server/src/Group.cpp b/server/src/Group.cpp index c157cd8..c1668c6 100644 --- a/server/src/Group.cpp +++ b/server/src/Group.cpp @@ -167,8 +167,7 @@ int GroupManager::insertGroupFromDb(int count, char **values, char **column) { groupId = (GroupType) stoll(values[index]); else if(strcmp(column[index], "displayName") == 0) 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()) { diff --git a/server/src/VirtualServerManager.cpp b/server/src/VirtualServerManager.cpp index e8b5e95..4514969 100644 --- a/server/src/VirtualServerManager.cpp +++ b/server/src/VirtualServerManager.cpp @@ -201,9 +201,11 @@ shared_ptr VirtualServerManager::findServerByPort(uint16_t port) return nullptr; } -uint16_t VirtualServerManager::next_available_port() { +uint16_t VirtualServerManager::next_available_port(const std::string& host_string) { auto instances = this->serverInstances(); - deque unallowed_ports; + std::vector unallowed_ports{}; + unallowed_ports.reserve(instances.size()); + for(const auto& instance : instances) { unallowed_ports.push_back(instance->properties()[property::VIRTUALSERVER_PORT].as()); @@ -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; while(true) { - if(port < 1024) goto c; + if(port < 1024) goto next_port; for(auto& p : unallowed_ports) { 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; - c: + next_port: port++; } return port; @@ -317,6 +340,7 @@ shared_ptr VirtualServerManager::create_server(std::string hosts, if(!sid_success) 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"}); 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;", diff --git a/server/src/VirtualServerManager.h b/server/src/VirtualServerManager.h index c4c2193..77f6006 100644 --- a/server/src/VirtualServerManager.h +++ b/server/src/VirtualServerManager.h @@ -38,7 +38,7 @@ namespace ts::server { std::shared_ptr findServerById(ServerId); std::shared_ptr 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 */); std::deque> serverInstances(){ diff --git a/server/src/channel/ServerChannel.cpp b/server/src/channel/ServerChannel.cpp index 56748e6..22b65fd 100644 --- a/server/src/channel/ServerChannel.cpp +++ b/server/src/channel/ServerChannel.cpp @@ -102,7 +102,7 @@ std::shared_ptr ServerChannelTree::createChannel(ChannelId parentI channel->properties()[property::CHANNEL_LAST_LEFT] = chrono::duration_cast(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; pf(result); @@ -493,7 +493,7 @@ bool ServerChannelTree::validateChannelIcons() { } 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); if(!res){ 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) { ChannelId channelId = 0; ChannelId parentId = 0; - auto type = static_cast(0xFF); int index = 0; @@ -520,8 +519,6 @@ int ServerChannelTree::loadChannelFromData(int argc, char **data, char **column) for(index = 0; index < argc; index++){ if(strcmp(column[index], "channelId") == 0) channelId = static_cast(stoll(data[index])); else if(strcmp(column[index], "parentId") == 0) parentId = static_cast(stoll(data[index])); - else if(strcmp(column[index], "type") == 0) type = static_cast(stoll(data[index])); - else if(strcmp(column[index], "serverId") == 0) {} else logError(this->getServerId(), "ServerChannelTree::loadChannelFromData called with invalid column from sql \"{}\"", column[index]); } } catch (std::exception& ex) { diff --git a/server/src/client/query/QueryClientCommands.cpp b/server/src/client/query/QueryClientCommands.cpp index c2995d2..a4f58fd 100644 --- a/server/src/client/query/QueryClientCommands.cpp +++ b/server/src/client/query/QueryClientCommands.cpp @@ -101,12 +101,15 @@ command_result QueryClient::handleCommand(Command& cmd) { case string_hash("bindinglist"): return this->handleCommandBindingList(cmd); case string_hash("serversnapshotdeploy"): { - //return this->handleCommandServerSnapshotDeploy(cmd); +#if 1 + return this->handleCommandServerSnapshotDeploy(cmd); +#else auto cmd_str = cmd.build(); ts::command_parser parser{cmd_str}; if(!parser.parse(true)) return command_result{error::vs_critical}; return this->handleCommandServerSnapshotDeployNew(parser); +#endif } case string_hash("serversnapshotcreate"): return this->handleCommandServerSnapshotCreate(cmd); @@ -587,9 +590,9 @@ command_result QueryClient::handleCommandServerCreate(Command& cmd) { time_wait = duration_cast(end - start); } - uint16_t freePort = serverInstance->getVoiceServerManager()->next_available_port(); - std::string host = cmd[0].has("virtualserver_host") ? cmd["virtualserver_host"].as() : config::binding::DefaultVoiceHost; + uint16_t freePort = serverInstance->getVoiceServerManager()->next_available_port(host); + uint16_t port = cmd[0].has("virtualserver_port") ? cmd["virtualserver_port"].as() : freePort; { 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); 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); server_create_lock.unlock(); auto end = system_clock::now(); diff --git a/server/src/manager/SqlDataManager.cpp b/server/src/manager/SqlDataManager.cpp index 758b6eb..e74c50b 100644 --- a/server/src/manager/SqlDataManager.cpp +++ b/server/src/manager/SqlDataManager.cpp @@ -445,6 +445,7 @@ ROLLBACK; return false; } } + /* 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(); @@ -468,6 +469,10 @@ ROLLBACK; error = "failed to rename new clients table to the old clients table (" + result.fmtStr() + ")"; return false; } + + CREATE_INDEX("clients", "serverId"); + CREATE_INDEX2R("clients", "serverId", "clientUid"); + CREATE_INDEX2R("clients", "serverId", "cldbid"); } db_version(12); default: diff --git a/server/src/snapshots/deploy.cpp b/server/src/snapshots/deploy.cpp index 4729094..ee8c426 100644 --- a/server/src/snapshots/deploy.cpp +++ b/server/src/snapshots/deploy.cpp @@ -17,6 +17,8 @@ using namespace ts::server; using SnapshotType = ts::server::snapshots::type; 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) { if(data.bulk(0).has_key("version")) { 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 client_id_mapping{}; std::map channel_id_mapping{}; std::map channel_group_id_mapping{}; std::map server_group_id_mapping{}; @@ -302,24 +305,58 @@ bool VirtualServerManager::deploy_raw_snapshot(std::string &error, ts::ServerId /* cleanup all old data */ this->delete_server_in_db(server_id); + /* register server & properties */ + { + + sql::InsertQuery insert_property_query{"properties", + sql::Column("serverId"), + sql::Column("type"), + sql::Column("id"), + sql::Column("key"), + sql::Column("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 */ { - //`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::Column("serverId"), - sql::Column("original_client_id"), - sql::Column("clientUid"), - sql::Column("firstConnect"), - sql::Column("lastConnect"), - - sql::Column("connections"), - sql::Column("lastName") + sql::Column("cldbid"), + sql::Column("clientUid") }; sql::InsertQuery insert_server_query{"clients", sql::Column("serverId"), + sql::Column("cldbid"), sql::Column("original_client_id"), sql::Column("clientUid"), sql::Column("firstConnect"), @@ -328,10 +365,36 @@ bool VirtualServerManager::deploy_raw_snapshot(std::string &error, ts::ServerId sql::Column("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) { - /* 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, + client.parsed_data.database_id, /* to keep stuff unique */ client.parsed_data.database_id, client.parsed_data.unique_id, std::chrono::floor(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.nickname ); - */ - insert_general_query.add_entry( - server_id, - client.parsed_data.database_id, - client.parsed_data.unique_id, - std::chrono::floor(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) - logWarning(server_id, "Failed to insert client {} into the database: {}", parsed_clients[std::get<0>(fail)], std::get<1>(fail).fmtStr()); + { + auto result = insert_general_query.execute(this->handle->getSql(), true); + 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) .query([&](int length, std::string* values, std::string* names) { 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]); 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 */ @@ -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"), + sql::Column("channelId"), + sql::Column("parentId") + }; + + sql::InsertQuery insert_property_query{"properties", + sql::Column("serverId"), + sql::Column("type"), + sql::Column("id"), + sql::Column("key"), + sql::Column("value") + }; + + for(auto& channel : parsed_channels) { + auto channel_id = channel.properties[property::CHANNEL_ID].as(); + insert_query.add_entry( + server_id, + channel_id, + channel.properties[property::CHANNEL_PID].as() + ); + + 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 */ { + sql::InsertQuery insert_query{"permissions", + sql::Column("serverId"), + sql::Column("type"), + sql::Column("id"), + sql::Column("channelId"), + sql::Column("permId"), + + sql::Column("value"), + sql::Column("grant"), + sql::Column("flag_skip"), + sql::Column("flag_negate") + }; for(auto& entry : channel_permissions) { - auto new_id = channel_id_mapping.find(entry.id1); - if(new_id == channel_id_mapping.end()) { - error = "missing channel id mapping for channel permission entry"; - return false; + { + auto new_id = channel_id_mapping.find(entry.id1); + if(new_id == channel_id_mapping.end()) { + 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 */ @@ -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"), + sql::Column("cldbid"), + sql::Column("groupId"), + sql::Column("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 */ { 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)" - /* client permissions */ + /* channel group relations */ { - sql::InsertQuery insert_query{"permissions", - sql::Column("serverId"), - sql::Column("type"), - sql::Column("id"), - sql::Column("channelId"), - sql::Column("permId"), - - sql::Column("value"), - sql::Column("grant"), - sql::Column("flag_skip"), - sql::Column("flag_negate"), + sql::InsertQuery insert_query{"assignedGroups", + sql::Column("serverId"), + sql::Column("cldbid"), + sql::Column("groupId"), + sql::Column("channelId") }; - 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); + 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"), + sql::Column("type"), + sql::Column("id"), + sql::Column("channelId"), + sql::Column("permId"), + sql::Column("value"), + sql::Column("grant"), + sql::Column("flag_skip"), + sql::Column("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 */ { + sql::InsertQuery insert_query{"permissions", + sql::Column("serverId"), + sql::Column("type"), + sql::Column("id"), + sql::Column("channelId"), + sql::Column("permId"), + + sql::Column("value"), + sql::Column("grant"), + sql::Column("flag_skip"), + sql::Column("flag_negate"), + }; + for(auto& entry : client_channel_permissions) { - auto new_id = channel_id_mapping.find(entry.id1); - if(new_id == channel_id_mapping.end()) { - error = "missing channel id mapping for client channel permission entry"; - return false; + { + auto new_id = channel_id_mapping.find(entry.id1); + if(new_id == channel_id_mapping.end()) { + 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"; diff --git a/shared b/shared index 16c2272..c31cc9d 160000 --- a/shared +++ b/shared @@ -1 +1 @@ -Subproject commit 16c2272fe4b479c55e6db6642d039a42b0774325 +Subproject commit c31cc9d0ee8fbcd04ff3a76e8fc4ab8723127a84