From f205075d97a9aa139e4747e8f0e56e0745b7f5e9 Mon Sep 17 00:00:00 2001 From: WolverinDEV Date: Tue, 14 Apr 2020 11:49:07 +0200 Subject: [PATCH] Changed for new deploy algorithm --- git-teaspeak | 2 +- music | 2 +- server/CMakeLists.txt | 30 +++- server/src/DatabaseHelper.cpp | 6 +- server/src/Group.h | 1 - server/src/VirtualServerManager.cpp | 43 +++-- server/src/VirtualServerManager.h | 153 +++++++++--------- server/src/client/query/QueryClient.h | 1 + .../src/client/query/QueryClientCommands.cpp | 35 +++- server/src/manager/SqlDataManager.cpp | 63 +++++++- server/src/snapshots/deploy.cpp | 133 ++++++++++++++- server/tests/snapshots/permission.cpp | 43 +++++ shared | 2 +- 13 files changed, 415 insertions(+), 99 deletions(-) create mode 100644 server/tests/snapshots/permission.cpp diff --git a/git-teaspeak b/git-teaspeak index cc874a4..df91da7 160000 --- a/git-teaspeak +++ b/git-teaspeak @@ -1 +1 @@ -Subproject commit cc874a443ac40fabf0342b410dae5ae46045fa4f +Subproject commit df91da75149777e774da60e948439ef426a690dc diff --git a/music b/music index 7ef7ea7..8e1ce32 160000 --- a/music +++ b/music @@ -1 +1 @@ -Subproject commit 7ef7ea785aebc26d3f9c6e396270e7b03eccf587 +Subproject commit 8e1ce32ae0b03f54efc54e76cd118cf01057159c diff --git a/server/CMakeLists.txt b/server/CMakeLists.txt index 66948d2..5ea78e1 100644 --- a/server/CMakeLists.txt +++ b/server/CMakeLists.txt @@ -137,6 +137,13 @@ set(SERVER_SOURCE_FILES src/weblist/WebListManager.cpp src/weblist/TeamSpeakWebClient.cpp + src/snapshots/permission.cpp + src/snapshots/client.cpp + src/snapshots/channel.cpp + src/snapshots/server.cpp + src/snapshots/groups.cpp + src/snapshots/deploy.cpp + src/manager/ConversationManager.cpp src/client/SpeakingClientHandshake.cpp src/client/command_handler/music.cpp src/client/command_handler/file.cpp) @@ -308,4 +315,25 @@ if (NOT DISABLE_JEMALLOC) jemalloc ) add_definitions(-DHAVE_JEMALLOC) -endif () \ No newline at end of file +endif () + + +add_executable(Snapshots-Permissions-Test src/snapshots/permission.cpp tests/snapshots/permission.cpp) +target_link_libraries(Snapshots-Permissions-Test PUBLIC + TeaSpeak + CXXTerminal::static #Static + ${StringVariable_LIBRARIES_STATIC} + ${YAML_CPP_LIBRARIES} + pthread + stdc++fs + libevent::core libevent::pthreads + + #Require a so + sqlite3 + DataPipes::rtc::shared + + tomcrypt::static + tommath::static + ${glib20_DIR}/lib/x86_64-linux-gnu/libffi.so.7 ${nice_DIR}/lib/libnice.so.10 +) +target_include_directories(Snapshots-Permissions-Test PUBLIC ${CMAKE_SOURCE_DIR}/server/src/) \ No newline at end of file diff --git a/server/src/DatabaseHelper.cpp b/server/src/DatabaseHelper.cpp index 931aac1..1d71aae 100644 --- a/server/src/DatabaseHelper.cpp +++ b/server/src/DatabaseHelper.cpp @@ -37,7 +37,7 @@ void DatabaseHelper::tick() { { threads::MutexLock l(this->propsLock); auto pcpy = this->cachedProperties; - for(const auto& mgr : pcpy){ + for(const auto& mgr : pcpy) { if(mgr->ownLock && system_clock::now() - mgr->lastAccess > minutes(5)) mgr->ownLock.reset(); if(mgr->properties.expired()) { @@ -573,7 +573,9 @@ bool DatabaseHelper::assignDatabaseId(sql::SqlManager *sql, ServerId id, std::sh if(!res) return false; auto insertTemplate = sql::model(sql, "INSERT INTO `clients` (`serverId`, `cldbId`, `clientUid`, `lastName`,`firstConnect`,`lastConnect`, `connections`) VALUES (:serverId, :cldbid, :cluid, :name, :fconnect, :lconnect, :connections)", - 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}); + 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])); diff --git a/server/src/Group.h b/server/src/Group.h index c1f8f99..4a9323d 100644 --- a/server/src/Group.h +++ b/server/src/Group.h @@ -215,7 +215,6 @@ namespace ts { bool isClientCached(const ClientDbId& /* client database id */); void clearCache(); - bool isLocalGroup(std::shared_ptr); protected: void handleChannelDeleted(const ChannelId& /* channel id */); diff --git a/server/src/VirtualServerManager.cpp b/server/src/VirtualServerManager.cpp index 705c950..e8b5e95 100644 --- a/server/src/VirtualServerManager.cpp +++ b/server/src/VirtualServerManager.cpp @@ -394,21 +394,8 @@ bool VirtualServerManager::deleteServer(shared_ptr server) { } this->handle->properties()[property::SERVERINSTANCE_SPOKEN_TIME_DELETED] += server->properties()[property::VIRTUALSERVER_SPOKEN_TIME].as(); - sql::command(this->handle->getSql(), "DELETE FROM `tokens` WHERE `serverId` = :sid", variable{":sid", server->getServerId()}).executeLater().waitAndGetLater(LOG_SQL_CMD, {1, "future failed"}); - sql::command(this->handle->getSql(), "DELETE FROM `properties` WHERE `serverId` = :sid", variable{":sid", server->getServerId()}).executeLater().waitAndGetLater(LOG_SQL_CMD, {1, "future failed"}); - sql::command(this->handle->getSql(), "DELETE FROM `permissions` WHERE `serverId` = :sid", variable{":sid", server->getServerId()}).executeLater().waitAndGetLater(LOG_SQL_CMD, {1, "future failed"}); - sql::command(this->handle->getSql(), "DELETE FROM `groups` WHERE `serverId` = :sid", variable{":sid", server->getServerId()}).executeLater().waitAndGetLater(LOG_SQL_CMD, {1, "future failed"}); - sql::command(this->handle->getSql(), "DELETE FROM `clients` WHERE `serverId` = :sid", variable{":sid", server->getServerId()}).executeLater().waitAndGetLater(LOG_SQL_CMD, {1, "future failed"}); - sql::command(this->handle->getSql(), "DELETE FROM `channels` WHERE `serverId` = :sid", variable{":sid", server->getServerId()}).executeLater().waitAndGetLater(LOG_SQL_CMD, {1, "future failed"}); - sql::command(this->handle->getSql(), "DELETE FROM `bannedClients` WHERE `serverId` = :sid", variable{":sid", server->getServerId()}).executeLater().waitAndGetLater(LOG_SQL_CMD, {1, "future failed"}); - sql::command(this->handle->getSql(), "DELETE FROM `assignedGroups` WHERE `serverId` = :sid", variable{":sid", server->getServerId()}).executeLater().waitAndGetLater(LOG_SQL_CMD, {1, "future failed"}); - sql::command(this->handle->getSql(), "DELETE FROM `servers` WHERE `serverId` = :sid", variable{":sid", server->getServerId()}).executeLater().waitAndGetLater(LOG_SQL_CMD, {1, "future failed"}); - sql::command(this->handle->getSql(), "DELETE FROM `musicbots` WHERE `serverId` = :sid", variable{":sid", server->getServerId()}).executeLater().waitAndGetLater(LOG_SQL_CMD, {1, "future failed"}); - - sql::command(this->handle->getSql(), "DELETE FROM `bannedClients` WHERE `serverId` = :sid", variable{":sid", server->getServerId()}).executeLater().waitAndGetLater(LOG_SQL_CMD, {1, "future failed"}); - sql::command(this->handle->getSql(), "DELETE FROM `ban_trigger` WHERE `server_id` = :sid", variable{":sid", server->getServerId()}).executeLater().waitAndGetLater(LOG_SQL_CMD, {1, "future failed"}); + this->delete_server_in_db(server->serverId); this->handle->getFileServer()->deleteServer(server); - return true; } @@ -447,4 +434,32 @@ void VirtualServerManager::tickHandshakeClients() { if(vserver) vserver->tickHandshakingClients(); } +} + +void VirtualServerManager::delete_server_in_db(ts::ServerId server_id) { +#define execute_delete(statement) \ +result = sql::command(this->handle->getSql(), statement, variable{":sid", server_id}).execute(); \ +if(!result) { \ + logWarning(LOG_INSTANCE, "Failed to execute SQL command {}: {}", statement, result.fmtStr()); \ + result = sql::result{}; \ +} + + sql::result result{}; + + execute_delete("DELETE FROM `tokens` WHERE `serverId` = :sid"); + execute_delete("DELETE FROM `properties` WHERE `serverId` = :sid"); + execute_delete("DELETE FROM `permissions` WHERE `serverId` = :sid"); + execute_delete("DELETE FROM `clients` WHERE `serverId` = :sid"); + execute_delete("DELETE FROM `channels` WHERE `serverId` = :sid"); + execute_delete("DELETE FROM `bannedClients` WHERE `serverId` = :sid"); + execute_delete("DELETE FROM `ban_trigger` WHERE `server_id` = :sid"); + execute_delete("DELETE FROM `assignedGroups` WHERE `serverId` = :sid"); + execute_delete("DELETE FROM `servers` WHERE `serverId` = :sid"); + + execute_delete("DELETE FROM `musicbots` WHERE `serverId` = :sid"); + execute_delete("DELETE FROM `conversations` WHERE `server_id` = :sid"); + execute_delete("DELETE FROM `conversation_blocks` WHERE `server_id` = :sid"); + + execute_delete("DELETE FROM `playlists` WHERE `serverId` = :sid"); + execute_delete("DELETE FROM `playlist_songs` WHERE `serverId` = :sid"); } \ No newline at end of file diff --git a/server/src/VirtualServerManager.h b/server/src/VirtualServerManager.h index ced73e9..c4c2193 100644 --- a/server/src/VirtualServerManager.h +++ b/server/src/VirtualServerManager.h @@ -5,95 +5,104 @@ #include "client/voice/PrecomputedPuzzles.h" #include "server/VoiceIOManager.h" #include "VirtualServer.h" +#include +#include "snapshots/snapshot.h" -namespace ts { - namespace server { - class InstanceHandler; +namespace ts::server { + class InstanceHandler; - struct ServerReport { - size_t avariable; - size_t online; + struct ServerReport { + size_t avariable; + size_t online; - size_t slots; - size_t onlineClients; - size_t onlineChannels; - }; - class VirtualServerManager { - public: - enum State { - STOPPED, - STARTING, - STARTED, - STOPPING - }; + size_t slots; + size_t onlineClients; + size_t onlineChannels; + }; + class VirtualServerManager { + public: + enum State { + STOPPED, + STARTING, + STARTED, + STOPPING + }; - explicit VirtualServerManager(InstanceHandler*); - ~VirtualServerManager(); + explicit VirtualServerManager(InstanceHandler*); + ~VirtualServerManager(); - bool initialize(bool execute_autostart = true); + bool initialize(bool execute_autostart = true); - std::shared_ptr create_server(std::string hosts, uint16_t port); - bool deleteServer(std::shared_ptr); + std::shared_ptr create_server(std::string hosts, uint16_t port); + bool deleteServer(std::shared_ptr); - std::shared_ptr findServerById(ServerId); - std::shared_ptr findServerByPort(uint16_t); - uint16_t next_available_port(); - ServerId next_available_server_id(bool& /* success */); - - std::deque> serverInstances(){ - threads::MutexLock l(this->instanceLock); - return instances; - } + std::shared_ptr findServerById(ServerId); + std::shared_ptr findServerByPort(uint16_t); + uint16_t next_available_port(); + ServerId next_available_server_id(bool& /* success */); - ServerReport report(); - OnlineClientReport clientReport(); - size_t runningServers(); - size_t usedSlots(); + std::deque> serverInstances(){ + threads::MutexLock l(this->instanceLock); + return instances; + } - void executeAutostart(); - void shutdownAll(const std::string&); + ServerReport report(); + OnlineClientReport clientReport(); + size_t runningServers(); + size_t usedSlots(); - //Dotn use shared_ptr references to keep sure that they be hold in memory - bool createServerSnapshot(Command &cmd, std::shared_ptr server, int version, std::string &error); - std::shared_ptr createServerFromSnapshot(std::shared_ptr old, std::string, uint16_t, const ts::Command &, std::string &); + void executeAutostart(); + void shutdownAll(const std::string&); - udp::PuzzleManager* rsaPuzzles() { return this->puzzles; } + //Dotn use shared_ptr references to keep sure that they be hold in memory + bool createServerSnapshot(Command &cmd, std::shared_ptr server, int version, std::string &error); + std::shared_ptr createServerFromSnapshot(std::shared_ptr old, std::string, uint16_t, const ts::Command &, std::string &); + bool deploy_snapshot(std::string& /* error */, ServerId /* target server id */, const command_parser& /* source */); - event::EventExecutor* get_join_loop() { return this->join_loop; } - event::EventExecutor* get_executor_loop() { return this->execute_loop; } + udp::PuzzleManager* rsaPuzzles() { return this->puzzles; } - inline void adjust_executor_threads() { - std::unique_lock instance_lock(this->instanceLock); - auto instance_count = this->instances.size(); - instance_lock.unlock(); + event::EventExecutor* get_join_loop() { return this->join_loop; } + event::EventExecutor* get_executor_loop() { return this->execute_loop; } - auto threads = std::min(config::threads::voice::execute_per_server * instance_count, config::threads::voice::execute_limit); - this->execute_loop->threads(threads); - } - io::VoiceIOManager* ioManager(){ return this->_ioManager; } + inline void adjust_executor_threads() { + std::unique_lock instance_lock(this->instanceLock); + auto instance_count = this->instances.size(); + instance_lock.unlock(); - threads::Mutex server_create_lock; + auto threads = std::min(config::threads::voice::execute_per_server * instance_count, config::threads::voice::execute_limit); + this->execute_loop->threads(threads); + } + io::VoiceIOManager* ioManager(){ return this->_ioManager; } - State getState() { return this->state; } - private: - State state = State::STOPPED; - InstanceHandler* handle; - threads::Mutex instanceLock; - std::deque> instances; - udp::PuzzleManager* puzzles{nullptr}; + threads::Mutex server_create_lock; - event::EventExecutor* execute_loop = nullptr; - event::EventExecutor* join_loop = nullptr; - threads::Scheduler* handshakeTickers = nullptr; - io::VoiceIOManager* _ioManager = nullptr; + State getState() { return this->state; } + private: + State state = State::STOPPED; + InstanceHandler* handle; + threads::Mutex instanceLock; + std::deque> instances; + udp::PuzzleManager* puzzles{nullptr}; - struct { - std::thread executor{}; - std::condition_variable condition; - std::mutex lock; - } acknowledge; + event::EventExecutor* execute_loop = nullptr; + event::EventExecutor* join_loop = nullptr; + threads::Scheduler* handshakeTickers = nullptr; + io::VoiceIOManager* _ioManager = nullptr; - void tickHandshakeClients(); - }; - } + struct { + std::thread executor{}; + std::condition_variable condition; + std::mutex lock; + } acknowledge; + + void tickHandshakeClients(); + + void delete_server_in_db(ServerId /* server id */); + + /* methods used to preprocess a snapshot */ + bool deploy_ts3_snapshot(std::string& /* error */, ServerId /* target server id */, const command_parser& /* source */); + bool deploy_teaspeak_snapshot(std::string& /* error */, ServerId /* target server id */, const command_parser& /* source */); + /* actual deploy method */ + bool deploy_raw_snapshot(std::string& /* error */, ServerId /* target server id */, const command_parser& /* source */, const std::string& /* hash */, size_t /* offset */, snapshots::type /* type */, snapshots::version_t /* version */); + }; } \ No newline at end of file diff --git a/server/src/client/query/QueryClient.h b/server/src/client/query/QueryClient.h index 5d5baf4..d264b6b 100644 --- a/server/src/client/query/QueryClient.h +++ b/server/src/client/query/QueryClient.h @@ -171,6 +171,7 @@ namespace ts::server { command_result handleCommandServerIdGetByPort(Command&); command_result handleCommandServerSnapshotDeploy(Command&); + command_result handleCommandServerSnapshotDeployNew(const command_parser&); command_result handleCommandServerSnapshotCreate(Command&); command_result handleCommandServerProcessStop(Command&); diff --git a/server/src/client/query/QueryClientCommands.cpp b/server/src/client/query/QueryClientCommands.cpp index 482a165..c2995d2 100644 --- a/server/src/client/query/QueryClientCommands.cpp +++ b/server/src/client/query/QueryClientCommands.cpp @@ -100,9 +100,14 @@ command_result QueryClient::handleCommand(Command& cmd) { return this->handleCommandHostInfo(cmd); case string_hash("bindinglist"): return this->handleCommandBindingList(cmd); - case string_hash("serversnapshotdeploy"): - return this->handleCommandServerSnapshotDeploy(cmd); + case string_hash("serversnapshotdeploy"): { + //return this->handleCommandServerSnapshotDeploy(cmd); + 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); + } case string_hash("serversnapshotcreate"): return this->handleCommandServerSnapshotCreate(cmd); case string_hash("serverprocessstop"): @@ -869,6 +874,32 @@ command_result QueryClient::handleCommandServerSnapshotDeploy(Command& cmd) { return command_result{error::ok}; } +command_result QueryClient::handleCommandServerSnapshotDeployNew(const ts::command_parser &command) { + CMD_RESET_IDLE; + + if(this->server) { + return command_result{error::not_implemented}; + ACTION_REQUIRES_GLOBAL_PERMISSION(permission::b_virtualserver_snapshot_deploy, 1); + //host = this->server->properties()[property::VIRTUALSERVER_HOST].as(); + //port = this->server->properties()[property::VIRTUALSERVER_PORT].as(); + } else { + ACTION_REQUIRES_INSTANCE_PERMISSION(permission::b_virtualserver_snapshot_deploy, 1); + } + + std::string error{}; + unique_lock server_create_lock(serverInstance->getVoiceServerManager()->server_create_lock); + //TODO: Create a server if no exists + server_create_lock.unlock(); + + //TODO: Stop the server completely + if(!serverInstance->getVoiceServerManager()->deploy_snapshot(error, 111, command)) { + //TODO: Delete server is it was new + return command_result{error::vs_critical, error}; + } + + return command_result{error::ok}; +} + command_result QueryClient::handleCommandServerSnapshotCreate(Command& cmd) { ACTION_REQUIRES_GLOBAL_PERMISSION(permission::b_virtualserver_snapshot_create, 1); CMD_RESET_IDLE; diff --git a/server/src/manager/SqlDataManager.cpp b/server/src/manager/SqlDataManager.cpp index ab5ba47..758b6eb 100644 --- a/server/src/manager/SqlDataManager.cpp +++ b/server/src/manager/SqlDataManager.cpp @@ -44,7 +44,7 @@ if(!result && result.msg().find(ignore) == string::npos){ #define RESIZE_COLUMN(tblName, rowName, size) up vote EXECUTE("Could not change column size", "ALTER TABLE " tblName " ALTER COLUMN " rowName " varchar(" size ")"); -#define CURRENT_DATABASE_VERSION 11 +#define CURRENT_DATABASE_VERSION 12 #define CURRENT_PERMISSION_VERSION 3 #define CLIENT_UID_LENGTH "64" @@ -144,7 +144,7 @@ bool SqlDataManager::initialize(std::string& error) { //Advanced locked test { bool property_exists = false; - sql::command(this->sql(), "SELECT * FORM `general` WHERE `key` = :key", variable{":key", "lock_test"}).query([](bool& flag, int, string*, string*) { flag = true; }, property_exists); + sql::command(this->sql(), "SELECT * FORM `general` WHERE `key` = :key", variable{":key", "lock_test"}).query([&](int, string*, string*) { property_exists = true; }); sql::result res; if(!property_exists) { res = sql::command(this->sql(), "INSERT INTO `general` (`key`, `value`) VALUES (:key, :value);", variable{":key", "lock_test"}, variable{":value", "UPDATE ME!"}).execute(); @@ -411,6 +411,65 @@ ROLLBACK; CREATE_INDEX("conversations", "server_id"); CREATE_INDEX2R("conversation_blocks", "server_id", "conversation_id"); db_version(11); + + case 11: + /* update the group table */ + { + result = sql::command(this->sql(), "CREATE TABLE `groups_v2` (`serverId` INT NOT NULL, `groupId` INTEGER AUTO_INCREMENT NOT NULL PRIMARY KEY, `target` INT, `type` INT, `displayName` VARCHAR(128));").execute(); + if(!result) { + error = "failed to create new groups table (" + result.fmtStr() + ")"; + return false; + } + result = sql::command(this->sql(), "INSERT INTO `groups_v2`(`serverId`, `groupId`, `target`, `type`, `displayName`) SELECT `serverId`, `groupId`, `target`, `type`, `displayName` FROM `groups`;").execute(); + if(!result) { + sql::command(this->sql(), "DROP TABLE `groups_v2`;").execute(); + error = "failed to insert data into the new groups table (" + result.fmtStr() + ")"; + return false; + } + result = sql::command(this->sql(), "DROP TABLE `groups`;").execute(); + if(!result) { + error = "failed to delete old groups table (" + result.fmtStr() + ")"; + return false; + } + result = sql::command(this->sql(), "ALTER TABLE `groups_v2` RENAME TO groups;").execute(); + if(!result) { + error = "failed to rename new groups table to the old groups table (" + result.fmtStr() + ")"; + return false; + } + + CREATE_INDEX2R("groups", "serverId", "groupId"); + CREATE_INDEX("groups", "serverId"); + result = sql::command(this->sql(), "ALTER TABLE `groups` ADD COLUMN `original_id` INTEGER DEFAULT 0;").execute(); + if(!result) { + error = "Failed to alter groups table"; + 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(); + if(!result) { + error = "failed to create new clients table (" + result.fmtStr() + ")"; + return false; + } + result = sql::command(this->sql(), "INSERT INTO `clients_v2` (`serverId`, `cldbid`, `clientUid`, `firstConnect`, `lastConnect`, `connections`, `lastName`) SELECT `serverId`, `cldbid`, `clientUid`, `firstConnect`, `lastConnect`, `connections`, `lastName` FROM `clients`;").execute(); + if(!result) { + sql::command(this->sql(), "DROP TABLE `groups_v2`;").execute(); + error = "failed to insert data into the new clients table (" + result.fmtStr() + ")"; + return false; + } + result = sql::command(this->sql(), "DROP TABLE `clients`;").execute(); + if(!result) { + error = "failed to delete old clients table (" + result.fmtStr() + ")"; + return false; + } + result = sql::command(this->sql(), "ALTER TABLE `clients_v2` RENAME TO clients;").execute(); + if(!result) { + error = "failed to rename new clients table to the old clients table (" + result.fmtStr() + ")"; + return false; + } + } + db_version(12); default: break; } diff --git a/server/src/snapshots/deploy.cpp b/server/src/snapshots/deploy.cpp index 55807d2..4729094 100644 --- a/server/src/snapshots/deploy.cpp +++ b/server/src/snapshots/deploy.cpp @@ -8,6 +8,8 @@ #include "./client.h" #include "./groups.h" #include "../VirtualServerManager.h" +#include "../InstanceHandler.h" +#include #include using namespace ts; @@ -297,6 +299,74 @@ bool VirtualServerManager::deploy_raw_snapshot(std::string &error, ts::ServerId /* lets start inserting data to the database */ { + /* cleanup all old data */ + this->delete_server_in_db(server_id); + + /* 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::InsertQuery insert_server_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") + }; + + for(const auto& client : parsed_clients) { + /* + 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(), + std::chrono::floor(client.parsed_data.timestamp_last_connected.time_since_epoch()).count(), + 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()); + + 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}; + try { + original_id = std::stoull(values[0]); + new_id = std::stoull(values[1]); + } catch (std::exception& ex) { + 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; + }); + } + /* channels */ { /* Assign each channel a new id */ @@ -338,18 +408,77 @@ bool VirtualServerManager::deploy_raw_snapshot(std::string &error, ts::ServerId /* server groups */ { - /* TODO: Insert to the database & load result */ + sql::model insert_model{this->handle->getSql(), "INSERT INTO `groups` (`serverId`, `target`, `type`, `displayName`, `original_id`) VALUES (:serverId, :target, :type, :name, :id)"}; + insert_model.value(":serverId", server_id).value(":target", GroupTarget::GROUPTARGET_SERVER).value(":type", GroupType::GROUP_TYPE_NORMAL); + for(auto& group : parsed_server_groups) { + auto result = insert_model.command().value(":name", group.parsed_data.name).value(":id", group.parsed_data.group_id).execute(); + if(!result) + logWarning(server_id, "Failed to insert server group \"{}\" into the database", group.parsed_data.name); + } + + sql::command{this->handle->getSql(), "SELECT `original_id`,`groupId` FROM `groups` WHERE `serverId` = :sid AND `target` = :target AND `type` = :type"} + .value(":serverId", server_id).value(":target", GroupTarget::GROUPTARGET_SERVER).value(":type", GroupType::GROUP_TYPE_NORMAL) + .query([&](int length, std::string* values, std::string* names) { + GroupId original_id{0}, new_id{0}; + try { + original_id = std::stoull(values[0]); + new_id = std::stoull(values[1]); + } catch (std::exception& ex) { + logWarning(server_id, "Failed to parse server group mapping for group id {} (New ID: {})", values[1], values[0]); + return; + } + server_group_id_mapping[original_id] = new_id; + }); } /* channel groups */ { - /* TODO: Insert to the database & load the result into the mapping */ + sql::model insert_model{this->handle->getSql(), "INSERT INTO `groups` (`serverId`, `target`, `type`, `displayName`, `original_id`) VALUES (:serverId, :target, :type, :name, :id)"}; + insert_model.value(":serverId", server_id).value(":target", GroupTarget::GROUPTARGET_CHANNEL).value(":type", GroupType::GROUP_TYPE_NORMAL); + + for(auto& group : parsed_server_groups) { + auto result = insert_model.command().value(":name", group.parsed_data.name).value(":id", group.parsed_data.group_id).execute(); + if(!result) + logWarning(server_id, "Failed to insert channel group \"{}\" into the database", group.parsed_data.name); + } + + sql::command{this->handle->getSql(), "SELECT `original_id`,`groupId` FROM `groups` WHERE `serverId` = :sid AND `target` = :target AND `type` = :type"} + .value(":serverId", server_id).value(":target", GroupTarget::GROUPTARGET_CHANNEL).value(":type", GroupType::GROUP_TYPE_NORMAL) + .query([&](int length, std::string* values, std::string* names) { + GroupId original_id{0}, new_id{0}; + try { + original_id = std::stoull(values[0]); + new_id = std::stoull(values[1]); + } catch (std::exception& ex) { + logWarning(server_id, "Failed to parse channel group mapping for group id {} (New ID: {})", values[1], values[0]); + return; + } + channel_group_id_mapping[original_id] = new_id; + }); } +#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 */ { + 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& permission : client_permissions) { + + } + + auto result = insert_query.execute(this->handle->getSql(), true); } /* register clients in the database */ diff --git a/server/tests/snapshots/permission.cpp b/server/tests/snapshots/permission.cpp new file mode 100644 index 0000000..6d68ed0 --- /dev/null +++ b/server/tests/snapshots/permission.cpp @@ -0,0 +1,43 @@ +// +// Created by WolverinDEV on 11/04/2020. +// + +#include + +using namespace ts::server::snapshots; + +void test_write() { + std::string error{}; + size_t offset{0}; + ts::command_builder result{""}; + + permission_writer writer{type::TEAMSPEAK, 0, result, ts::permission::teamspeak::GroupType::GENERAL}; + std::deque entries{}; + { + { + auto& entry = entries.emplace_back(); + entry.type = ts::permission::resolvePermissionData("b_virtualserver_modify_host"); + entry.granted = {2, true}; + entry.value = {4, true}; + } + { + auto& entry = entries.emplace_back(); + entry.type = ts::permission::resolvePermissionData("i_icon_id"); + entry.granted = {0, false}; + entry.value = {4, true}; + entry.flag_skip = true; + } + } + + if(!writer.write(error, offset, entries)) { + std::cerr << error << "\n"; + assert(false); + return; + } + + std::cout << "Offset: " << offset << ". Command: " << result.build() << "\n"; +} + +int main() { + test_write(); +} \ No newline at end of file diff --git a/shared b/shared index ee7f26b..707736d 160000 --- a/shared +++ b/shared @@ -1 +1 @@ -Subproject commit ee7f26b7ed883027f0a81ece1c2aacd41053ce1e +Subproject commit 707736d896f46489133e6da6b7543238274c37e6