From 3a5c152b3cd79c5410b7917425a5840ab1e32051 Mon Sep 17 00:00:00 2001 From: WolverinDEV Date: Wed, 4 Mar 2020 16:26:43 +0100 Subject: [PATCH] Added new files --- server/src/groups/Group.cpp | 44 +- server/src/groups/Group.h | 148 +-- server/src/groups/GroupAssignmentManager.cpp | 1064 +++++++++--------- server/src/groups/GroupAssignmentManager.h | 218 ++-- server/src/groups/GroupManager.cpp | 10 +- server/src/groups/GroupManager.h | 162 +-- server/src/services/ClientChannelService.cpp | 756 ++++++------- server/src/services/ClientChannelService.h | 186 +-- server/src/services/PermissionsService.cpp | 636 +++++------ server/src/services/PermissionsService.h | 134 +-- server/src/vserver/DefaultServer.cpp | 66 +- server/src/vserver/DefaultServer.h | 28 +- server/src/vserver/VirtualServer.cpp | 50 +- server/src/vserver/VirtualServer.h | 104 +- server/src/vserver/VirtualServerBase.cpp | 154 +-- server/src/vserver/VirtualServerBase.h | 284 ++--- 16 files changed, 2022 insertions(+), 2022 deletions(-) diff --git a/server/src/groups/Group.cpp b/server/src/groups/Group.cpp index 7024a9d..09ee3e8 100644 --- a/server/src/groups/Group.cpp +++ b/server/src/groups/Group.cpp @@ -1,23 +1,23 @@ -// -// Created by WolverinDEV on 03/03/2020. -// - -#include "Group.h" -#include "./GroupManager.h" - -using namespace ts::server::groups; - -Group::Group(const std::shared_ptr &manager, ts::GroupId id, ts::server::groups::GroupType type, std::string name, - std::shared_ptr permissions) : manager_{manager}, group_id_{id}, type_{type}, name_{std::move(name)}, permissions_{std::move(permissions)} { } - -void Group::set_display_name(const std::string &name) { - this->name_ = name; - - auto handle = this->manager_.lock(); - if(handle) handle->update_group_name(this); -} - -void Group::set_permissions(const std::shared_ptr &permissions) { - assert(permissions); - this->permissions_ = permissions; +// +// Created by WolverinDEV on 03/03/2020. +// + +#include "Group.h" +#include "./GroupManager.h" + +using namespace ts::server::groups; + +Group::Group(const std::shared_ptr &manager, ts::GroupId id, ts::server::groups::GroupType type, std::string name, + std::shared_ptr permissions) : manager_{manager}, group_id_{id}, type_{type}, name_{std::move(name)}, permissions_{std::move(permissions)} { } + +void Group::set_display_name(const std::string &name) { + this->name_ = name; + + auto handle = this->manager_.lock(); + if(handle) handle->update_group_name(this); +} + +void Group::set_permissions(const std::shared_ptr &permissions) { + assert(permissions); + this->permissions_ = permissions; } \ No newline at end of file diff --git a/server/src/groups/Group.h b/server/src/groups/Group.h index 5fb7447..c113078 100644 --- a/server/src/groups/Group.h +++ b/server/src/groups/Group.h @@ -1,75 +1,75 @@ -#pragma once - -#include -#include - -namespace ts::server::groups { - enum GroupType { - GROUP_TYPE_QUERY, - GROUP_TYPE_TEMPLATE, - GROUP_TYPE_NORMAL - }; - - enum GroupNameMode { - GROUP_NAME_MODE_HIDDEN, - GROUP_NAME_MODE_BEFORE, - GROUP_NAME_MODE_BEHIND - }; - - typedef uint32_t GroupSortId; - typedef uint32_t GroupIconId; - - class GroupManager; - class Group { - friend class GroupManager; - public: - Group(const std::shared_ptr& /* manager */, GroupId /* id */, GroupType /* type */, std::string /* name */, std::shared_ptr /* permissions */); - ~Group() = default; - - /* information getters */ - [[nodiscard]] inline GroupId group_id() const { return this->group_id_; } - [[nodiscard]] inline GroupType group_type() const { return this->type_; } - [[nodiscard]] inline const std::string& display_name() const { return this->name_; } - [[nodiscard]] inline std::shared_ptr permissions() { return this->permissions_; } - - [[nodiscard]] inline bool save_assignments() const { - assert(this->permissions_); - auto value = this->permissions_->permission_value_flagged(permission::b_group_is_permanent); - return value.has_value ? permission::v2::permission_granted(1, value) : true; - } - - [[nodiscard]] inline GroupNameMode name_mode() const { - assert(this->permissions_); - auto value = this->permissions_->permission_value_flagged(permission::i_group_show_name_in_tree); - return value.has_value ? (GroupNameMode) value.value : GroupNameMode::GROUP_NAME_MODE_HIDDEN; - } - - [[nodiscard]] inline GroupSortId sort_id() const { - assert(this->permissions_); - auto value = this->permissions_->permission_value_flagged(permission::i_group_sort_id); - return value.has_value ? value.value : 0; - } - - [[nodiscard]] inline GroupIconId icon_id() const { - assert(this->permissions_); - auto value = this->permissions_->permission_value_flagged(permission::i_icon_id); - return value.has_value ? value.value : 0; - } - - /* information setters */ - void set_display_name(const std::string& name); - private: - std::weak_ptr manager_; - - const GroupId group_id_; - const GroupType type_; - std::string name_; - - std::shared_ptr permissions_; - - void set_permissions(const std::shared_ptr& /* permissions */); - }; - - class ServerGroup : public Group { }; - class ChannelGroup : public Group { }; +#pragma once + +#include +#include + +namespace ts::server::groups { + enum GroupType { + GROUP_TYPE_QUERY, + GROUP_TYPE_TEMPLATE, + GROUP_TYPE_NORMAL + }; + + enum GroupNameMode { + GROUP_NAME_MODE_HIDDEN, + GROUP_NAME_MODE_BEFORE, + GROUP_NAME_MODE_BEHIND + }; + + typedef uint32_t GroupSortId; + typedef uint32_t GroupIconId; + + class GroupManager; + class Group { + friend class GroupManager; + public: + Group(const std::shared_ptr& /* manager */, GroupId /* id */, GroupType /* type */, std::string /* name */, std::shared_ptr /* permissions */); + ~Group() = default; + + /* information getters */ + [[nodiscard]] inline GroupId group_id() const { return this->group_id_; } + [[nodiscard]] inline GroupType group_type() const { return this->type_; } + [[nodiscard]] inline const std::string& display_name() const { return this->name_; } + [[nodiscard]] inline std::shared_ptr permissions() { return this->permissions_; } + + [[nodiscard]] inline bool save_assignments() const { + assert(this->permissions_); + auto value = this->permissions_->permission_value_flagged(permission::b_group_is_permanent); + return value.has_value ? permission::v2::permission_granted(1, value) : true; + } + + [[nodiscard]] inline GroupNameMode name_mode() const { + assert(this->permissions_); + auto value = this->permissions_->permission_value_flagged(permission::i_group_show_name_in_tree); + return value.has_value ? (GroupNameMode) value.value : GroupNameMode::GROUP_NAME_MODE_HIDDEN; + } + + [[nodiscard]] inline GroupSortId sort_id() const { + assert(this->permissions_); + auto value = this->permissions_->permission_value_flagged(permission::i_group_sort_id); + return value.has_value ? value.value : 0; + } + + [[nodiscard]] inline GroupIconId icon_id() const { + assert(this->permissions_); + auto value = this->permissions_->permission_value_flagged(permission::i_icon_id); + return value.has_value ? value.value : 0; + } + + /* information setters */ + void set_display_name(const std::string& name); + private: + std::weak_ptr manager_; + + const GroupId group_id_; + const GroupType type_; + std::string name_; + + std::shared_ptr permissions_; + + void set_permissions(const std::shared_ptr& /* permissions */); + }; + + class ServerGroup : public Group { }; + class ChannelGroup : public Group { }; } \ No newline at end of file diff --git a/server/src/groups/GroupAssignmentManager.cpp b/server/src/groups/GroupAssignmentManager.cpp index 647f1b2..6bfea0e 100644 --- a/server/src/groups/GroupAssignmentManager.cpp +++ b/server/src/groups/GroupAssignmentManager.cpp @@ -1,533 +1,533 @@ -// -// Created by WolverinDEV on 03/03/2020. -// -#include -#include "./GroupAssignmentManager.h" -#include "./GroupManager.h" -#include "../vserver/VirtualServer.h" -#include "../client/ConnectedClient.h" - -using namespace ts::server::groups; - -GroupAssignmentManager::GroupAssignmentManager(GroupManager* handle) : manager_{handle} { } -GroupAssignmentManager::~GroupAssignmentManager() = default; - -bool GroupAssignmentManager::initialize(std::string &error) { - return true; -} - -sql::SqlManager* GroupAssignmentManager::sql_manager() { - return this->manager_->virtual_server()->sql_manager(); -} - -ts::ServerId GroupAssignmentManager::server_id() { - return this->manager_->virtual_server()->server_id(); -} - -bool GroupAssignmentManager::load_data(std::string &error) { - if constexpr(kCacheAllClients) { - std::lock_guard cache_lock{this->client_cache_lock}; - std::unique_ptr current_entry{nullptr}; - - auto res = sql::command(this->sql_manager(), "SELECT `groupId`, `cldbid`, `channelId`, `until` FROM `assignedGroups` WHERE `serverId` = :sid ORDER BY `cldbid`", variable{":sid", this->server_id()}) - .query([&](int length, std::string* value, std::string* column) { - ChannelId channel_id{0}; - GroupId group_id{0}; - ClientDbId client_dbid{0}; - - for(int index = 0; index < length; index++){ - try { - if(column[index] == "groupId"){ - group_id = stoll(value[index]); - } else if(column[index] == "until"){ - } else if(column[index] == "channelId"){ - channel_id = stoll(value[index]); - } else if(column[index] == "cldbid"){ - client_dbid = stoll(value[index]); - } - } catch(std::exception& ex) { - logError(this->server_id(), "Failed to load group assignment from database. Column {} contains an invalid value: {}", column[index], value[index]); - return 0; - } - } - - if(current_entry) - if(current_entry->client_database_id != client_dbid) - this->client_cache.push_back(std::move(current_entry)); - - if(!current_entry) { - current_entry = std::make_unique(); - current_entry->client_database_id = client_dbid; - } - - if(channel_id) - current_entry->channel_group_assignments.emplace_back(channel_id, group_id, false); - else - current_entry->server_group_assignments.emplace_back(group_id); - return 0; - }); - if(!res) { - error = "failed to query database (" + res.fmtStr() + ")"; - return false; - } - if(current_entry) - this->client_cache.push_back(std::move(current_entry)); - } - return true; -} - -void GroupAssignmentManager::unload_data() { - std::lock_guard cache_lock{this->client_cache_lock}; - this->client_cache.clear(); -} - -void GroupAssignmentManager::enable_cache_for_client(GroupAssignmentCalculateMode mode, ClientDbId cldbid) { - if constexpr(!kCacheAllClients) { - bool cache_exists{false}; - { - std::lock_guard cache_lock{this->client_cache_lock}; - for(auto& client : this->client_cache) - if(client->client_database_id == cldbid) { - client->use_count++; - cache_exists = true; - break; - } - } - - if(!cache_exists) { - auto cache = std::make_unique(); - cache->client_database_id = cldbid; - cache->use_count++; - - auto res = sql::command(this->sql_manager(), "SELECT `groupId`, `channelId`, `until` FROM `assignedGroups` WHERE `serverId` = :sid AND `cldbid` = :cldbid", variable{":sid", this->server_id()}, variable{":cldbid", cldbid}) - .query([&](int length, std::string* value, std::string* column) { - ChannelId channel_id{0}; - GroupId group_id{0}; - - for(int index = 0; index < length; index++){ - try { - if(column[index] == "groupId"){ - group_id = stoll(value[index]); - } else if(column[index] == "until"){ - } else if(column[index] == "channelId"){ - channel_id = stoll(value[index]); - } - } catch(std::exception& ex) { - logError(this->server_id(), "Failed to load group assignment from database for client {}. Column {} contains an invalid value: {}", cldbid, column[index], value[index]); - return 0; - } - } - if(!group_id) - return 0; - - if(channel_id) - cache->channel_group_assignments.emplace_back(channel_id, group_id, false); - else - cache->server_group_assignments.emplace_back(group_id); - return 0; - }); - - std::lock_guard cache_lock{this->client_cache_lock}; -#if 0 /* lets have some performance over double entries :D */ - for(auto& client : this->client_cache) - if(client->client_database_id == cldbid) { - /* somebody already inserted that client while we've loaded him */ - cache_exists = true; - break; - } - if(!cache_exists) -#endif - this->client_cache.push_back(std::move(cache)); - } - } - - if(mode == GroupAssignmentCalculateMode::GLOBAL) - if(auto parent = this->manager_->parent_manager(); parent) - parent->assignments().enable_cache_for_client(mode, cldbid); -} - -void GroupAssignmentManager::disable_cache_for_client(GroupAssignmentCalculateMode mode, ClientDbId cldbid) { - if constexpr(!kCacheAllClients) { - std::lock_guard cache_lock{this->client_cache_lock}; - this->client_cache.erase(std::remove_if(this->client_cache.begin(), this->client_cache.end(), [cldbid](const std::unique_ptr& client) { - return client->client_database_id == cldbid; - }), this->client_cache.end()); - } - - if(mode == GroupAssignmentCalculateMode::GLOBAL) - if(auto parent = this->manager_->parent_manager(); parent) - parent->assignments().disable_cache_for_client(mode, cldbid); -} - - -std::vector GroupAssignmentManager::server_groups_of_client(ts::server::groups::GroupAssignmentCalculateMode mode, - ts::ClientDbId cldbid) { - std::vector result{}; - bool cache_found{false}; - { - std::lock_guard cache_lock{this->client_cache_lock}; - for(auto& entry : this->client_cache) { - if(entry->client_database_id != cldbid) continue; - - result.reserve(entry->server_group_assignments.size()); - for(auto& assignment : entry->server_group_assignments) - result.push_back(assignment.group_id); - - cache_found = true; - break; - } - } - - if(!cache_found && !kCacheAllClients) { - debugMessage(this->server_id(), "Query client groups for client {} on server {}.", cldbid, this->server_id()); - - result.reserve(64); - auto res = sql::command(this->sql_manager(), "SELECT `groupId`, `until` FROM `assignedGroups` WHERE `serverId` = :sid AND `cldbid` = :cldbid AND `channelId` = 0", variable{":sid", this->server_id()}, variable{":cldbid", cldbid}) - .query([&](int length, std::string* value, std::string* column) { - GroupId group_id{0}; - try { - for(int index = 0; index < length; index++) { - if(column[index] == "groupId") { - group_id = std::stoull(value[index]); - break; - } - } - } catch(std::exception& ex) { - logWarning(this->server_id(), "Invalid data found in group assignment table. Failed to parse group id."); - return 0; - } - if(!group_id) return 0; - - result.push_back(group_id); - return 0; - }); - LOG_SQL_CMD(res); - } - - if(mode == GroupAssignmentCalculateMode::GLOBAL) - if(auto parent = this->manager_->parent_manager(); parent) { - auto parent_groups = parent->assignments().server_groups_of_client(mode, cldbid); - result.reserve(result.size() + parent_groups.size()); - result.insert(result.begin(), parent_groups.begin(), parent_groups.end()); - } - - return result; -} - -std::vector GroupAssignmentManager::channel_groups_of_client(GroupAssignmentCalculateMode mode, ClientDbId cldbid) { - std::vector result{}; - bool cache_found{false}; - { - std::lock_guard cache_lock{this->client_cache_lock}; - for(auto& entry : this->client_cache) { - if(entry->client_database_id != cldbid) continue; - - result.reserve(entry->channel_group_assignments.size()); - result.insert(result.begin(), entry->channel_group_assignments.begin(), entry->channel_group_assignments.end()); - cache_found = true; - break; - } - } - - if(!cache_found && !kCacheAllClients) { - debugMessage(this->server_id(), "Query client groups for client {} on server {}.", cldbid, this->server_id()); - - result.reserve(64); - auto res = sql::command(this->sql_manager(), "SELECT `groupId`, `channelId`, `until` FROM `assignedGroups` WHERE `serverId` = :sid AND `cldbid` = :cldbid AND `channelId` != 0", variable{":sid", this->server_id()}, variable{":cldbid", cldbid}) - .query([&](int length, std::string* value, std::string* column) { - GroupId group_id{0}; - ChannelId channel_id{0}; - try { - for(int index = 0; index < length; index++) { - if(column[index] == "groupId") { - group_id = std::stoull(value[index]); - } else if(column[index] == "channelId") { - channel_id = std::stoull(value[index]); - } - } - } catch(std::exception& ex) { - logWarning(this->server_id(), "Invalid data found in group assignment table. Failed to parse group or channel id."); - return 0; - } - if(!group_id || !channel_id) return 0; - - result.emplace_back(channel_id, group_id, false); - return 0; - }); - LOG_SQL_CMD(res); - } - - if(mode == GroupAssignmentCalculateMode::GLOBAL) - if(auto parent = this->manager_->parent_manager(); parent) { - auto parent_groups = parent->assignments().channel_groups_of_client(mode, cldbid); - result.reserve(result.size() + parent_groups.size()); - result.insert(result.begin(), parent_groups.begin(), parent_groups.end()); - } - - return result; -} - -std::deque GroupAssignmentManager::server_group_clients(GroupId group_id) { - std::deque result{}; - if constexpr(kCacheAllClients) { - std::lock_guard cache_lock{this->client_cache_lock}; - for(auto& client : this->client_cache) { - auto it = std::find_if(client->server_group_assignments.begin(), client->server_group_assignments.end(), [&](const ServerGroupAssignment& assignment) { - return assignment.group_id == group_id; - }); - if(it == client->server_group_assignments.end()) - continue; - result.push_back(client->client_database_id); - } - } else { - auto res = sql::command(this->sql_manager(), "SELECT `cldbid` FROM `assignedGroups` WHERE `serverId` = :sid AND `channelId` = 0 AND `groupId` = :gid", variable{":sid", this->server_id()}, variable{":gid", group_id}) - .query([&](int length, std::string* value, std::string* column) { - ClientDbId cldbid{0}; - try { - for(int index = 0; index < length; index++) { - if(column[index] == "cldbid") { - cldbid = std::stoull(value[index]); - break; - } - } - } catch(std::exception& ex) { - logWarning(this->server_id(), "Invalid data found in group assignment table. Failed to parse client database id."); - return 0; - } - if(!cldbid) return 0; - - result.push_back(cldbid); - return 0; - }); - LOG_SQL_CMD(res); - } - - if(auto parent = this->manager_->parent_manager(); parent) { - auto parent_clients = parent->assignments().server_group_clients(group_id); - result.insert(result.begin(), parent_clients.begin(), parent_clients.end()); - } - - return result; -} - -GroupAssignmentResult GroupAssignmentManager::add_server_group(ClientDbId client, GroupId group) { - bool cache_verified{false}; - { - std::lock_guard cache_lock{this->client_cache_lock}; - for(auto& entry : this->client_cache) { - if(entry->client_database_id != client) continue; - - auto it = std::find_if(entry->server_group_assignments.begin(), entry->server_group_assignments.end(), [&](const ServerGroupAssignment& assignment) { - return assignment.group_id == group; - }); - if(it != entry->server_group_assignments.end()) - return GroupAssignmentResult::ADD_ALREADY_MEMBER_OF_GROUP; - entry->server_group_assignments.emplace_back(group); - cache_verified = true; - break; - } - - if(!cache_verified && kCacheAllClients) { - /* add the client to the cache */ - auto cache = std::make_unique(); - cache->client_database_id = client; - cache->server_group_assignments.emplace_back(group); - this->client_cache.push_back(cache); - } - } - - { - - auto command = sql::command(this->sql_manager(), "INSERT INTO `assignedGroups` (`serverId`, `cldbid`, `groupId`, `channelId`, `until`) VALUES (:sid, :cldbid, :gid, :chid, :until)", - variable{":sid", this->server_id()}, - variable{":cldbid", client}, - variable{":gid", group}, - variable{":chid", 0}, - variable{":until", 0}); - if(cache_verified) - command.executeLater().waitAndGetLater(LOG_SQL_CMD, {-1, "failed to insert group assignment into the database"}); - else { - auto result = command.execute(); - if(!result) return GroupAssignmentResult::ADD_ALREADY_MEMBER_OF_GROUP; //TODO: Parse error from database? - } - } - return GroupAssignmentResult::SUCCESS; -} - -GroupAssignmentResult GroupAssignmentManager::remove_server_group(ClientDbId client, GroupId group) { - bool cache_verified{false}; - { - std::lock_guard cache_lock{this->client_cache_lock}; - for(auto& entry : this->client_cache) { - if(entry->client_database_id != client) continue; - - auto it = std::find_if(entry->server_group_assignments.begin(), entry->server_group_assignments.end(), [&](const ServerGroupAssignment& assignment) { - return assignment.group_id == group; - }); - if(it == entry->server_group_assignments.end()) - return GroupAssignmentResult::REMOVE_NOT_MEMBER_OF_GROUP; - entry->server_group_assignments.erase(it); - cache_verified = true; - break; - } - - if(!cache_verified && kCacheAllClients) - return GroupAssignmentResult::REMOVE_NOT_MEMBER_OF_GROUP; - } - - { - auto command = sql::command(this->sql_manager(), "DELETE FROM `assignedGroups` WHERE `serverId` = :sid AND `cldbid` = :cldbid AND `groupId` = :gid AND `channelId` = :chid", - variable{":sid", this->server_id()}, - variable{":cldbid", client}, - variable{":gid", group}, - variable{":chid", 0}); - if(cache_verified) - command.executeLater().waitAndGetLater(LOG_SQL_CMD, {-1, "failed to remove group assignment from database"}); - else { - auto result = command.execute(); - if(!result) return GroupAssignmentResult::REMOVE_NOT_MEMBER_OF_GROUP; //TODO: Parse error from database? - } - } - return GroupAssignmentResult::SUCCESS; -} - -GroupAssignmentResult GroupAssignmentManager::set_channel_group(ClientDbId client, GroupId group, ChannelId channel_id, bool temporary) { - bool cache_verified{false}; - { - std::lock_guard cache_lock{this->client_cache_lock}; - for(auto& entry : this->client_cache) { - if(entry->client_database_id != client) continue; - - auto it = std::find_if(entry->channel_group_assignments.begin(), entry->channel_group_assignments.end(), [&](const ChannelGroupAssignment& assignment) { - return assignment.channel_id == channel_id; - }); - if(it != entry->channel_group_assignments.end()) { - if(group) { - if(it->group_id == group) - return GroupAssignmentResult::SET_ALREADY_MEMBER_OF_GROUP; - it->group_id = group; - } else { - entry->channel_group_assignments.erase(it); - } - } else { - if(group) { - entry->channel_group_assignments.emplace_back(channel_id, group, temporary); - } - } - cache_verified = true; - break; - } - - if(!cache_verified && kCacheAllClients) { - if(group) { - /* add the client to the cache */ - auto cache = std::make_unique(); - cache->client_database_id = client; - cache->channel_group_assignments.emplace_back(channel_id, group, temporary); - this->client_cache.push_back(cache); - } else { - return GroupAssignmentResult::SUCCESS; - } - } - } - if(temporary) - return GroupAssignmentResult::SUCCESS; - - sql::command(this->sql_manager(), "DELETE FROM `assignedGroups` WHERE `serverId` = :sid AND `cldbid` = :cldbid AND `channelId` = :chid", - variable{":sid", this->server_id()}, - variable{":cldbid", client}, - variable{":chid", channel_id}) - .executeLater().waitAndGetLater(LOG_SQL_CMD, {1, "failed to delete old channel group assignment"}); - - if(group) { - sql::command(this->sql_manager(), "INSERT INTO `assignedGroups` (`serverId`, `cldbid`, `groupId`, `channelId`, `until`) VALUES (:sid, :cldbid, :gid, :chid, :until)", - variable{":sid", this->server_id()}, - variable{":cldbid", client}, - variable{":gid", group}, - variable{":chid", channel_id}, - variable{":until", 0}) - .executeLater().waitAndGetLater(LOG_SQL_CMD, {1, "failed to insert channel group assignment"}); - } - return GroupAssignmentResult::SUCCESS; -} - -void GroupAssignmentManager::cleanup_channel_assignments(ChannelId channel_id) { - { - std::lock_guard cache_lock{this->client_cache_lock}; - for(auto& client : this->client_cache) { - client->channel_group_assignments.erase(std::find_if(client->channel_group_assignments.begin(), client->channel_group_assignments.end(), [&](const ChannelGroupAssignment& assignment) { - return assignment.channel_id == channel_id; - })); - } - } - - sql::command(this->sql_manager(), "DELETE FROM `assignedGroups` WHERE `serverId` = :sid AND `channelId` = :cid", variable{":sid", this->server_id()}, variable{":cid", channel_id}) - .executeLater().waitAndGetLater(LOG_SQL_CMD, {1, "future failed"}); -} - -void GroupAssignmentManager::cleanup_assignments() { - { - - std::lock_guard cache_lock{this->client_cache_lock}; - this->client_cache.clear(); - } - - sql::command(this->sql_manager(), "DELETE FROM `assignedGroups` WHERE `serverId` = :sid", variable{":sid", this->server_id()}) - .executeLater().waitAndGetLater(LOG_SQL_CMD, {1, "future failed"}); -} - -void GroupAssignmentManager::cleanup_channel_temporary_assignment(ClientDbId client_dbid, ChannelId channel) { - std::lock_guard cache_lock{this->client_cache_lock}; - for(auto& client : this->client_cache) { - if(client->client_database_id == client_dbid) { - auto assignment = std::find_if(client->channel_group_assignments.begin(), client->channel_group_assignments.end(), [&](const ChannelGroupAssignment& assignment) { - return assignment.channel_id == channel; - }); - if(assignment->temporary_assignment) - client->channel_group_assignments.erase(assignment); - break; - } - } -} - -std::deque GroupAssignmentManager::update_client_group_properties(const std::shared_ptr &client, ChannelId channel_id) { - std::deque changed; - bool increase_join_state{false}; - - //Server groups - { - auto server_groups = this->server_groups_of_client(GroupAssignmentCalculateMode::GLOBAL, client->getClientDatabaseId()); - std::string group_string; - for(const auto& group : server_groups) - if(group_string.empty()) group_string += std::to_string(group); - else group_string += "," + std::to_string(group); - - if(client->properties()[property::CLIENT_SERVERGROUPS] != group_string) { - client->properties()[property::CLIENT_SERVERGROUPS] = group_string; - changed.push_back(property::CLIENT_SERVERGROUPS); - increase_join_state = true; - } - - } - - //Channel group - if(channel_id) { - auto assignment = this->manager_->virtual_server()->channel_service().calculate_channel_group(channel_id, client->getClientDatabaseId(), client->getType()); - - if(client->properties()[property::CLIENT_CHANNEL_GROUP_INHERITED_CHANNEL_ID] != assignment.inherited_channel_id) { - client->properties()[property::CLIENT_CHANNEL_GROUP_INHERITED_CHANNEL_ID] = assignment.inherited_channel_id; - changed.push_back(property::CLIENT_CHANNEL_GROUP_INHERITED_CHANNEL_ID); - } - - if(client->properties()[property::CLIENT_CHANNEL_GROUP_ID] != assignment.group_id) { - client->properties()[property::CLIENT_CHANNEL_GROUP_ID] = assignment.group_id; - changed.push_back(property::CLIENT_CHANNEL_GROUP_ID); - increase_join_state = true; - } - } - - if(increase_join_state) - client->increase_join_state(); /* groups have changed :) */ - - return changed; +// +// Created by WolverinDEV on 03/03/2020. +// +#include +#include "./GroupAssignmentManager.h" +#include "./GroupManager.h" +#include "../vserver/VirtualServer.h" +#include "../client/ConnectedClient.h" + +using namespace ts::server::groups; + +GroupAssignmentManager::GroupAssignmentManager(GroupManager* handle) : manager_{handle} { } +GroupAssignmentManager::~GroupAssignmentManager() = default; + +bool GroupAssignmentManager::initialize(std::string &error) { + return true; +} + +sql::SqlManager* GroupAssignmentManager::sql_manager() { + return this->manager_->virtual_server()->sql_manager(); +} + +ts::ServerId GroupAssignmentManager::server_id() { + return this->manager_->virtual_server()->server_id(); +} + +bool GroupAssignmentManager::load_data(std::string &error) { + if constexpr(kCacheAllClients) { + std::lock_guard cache_lock{this->client_cache_lock}; + std::unique_ptr current_entry{nullptr}; + + auto res = sql::command(this->sql_manager(), "SELECT `groupId`, `cldbid`, `channelId`, `until` FROM `assignedGroups` WHERE `serverId` = :sid ORDER BY `cldbid`", variable{":sid", this->server_id()}) + .query([&](int length, std::string* value, std::string* column) { + ChannelId channel_id{0}; + GroupId group_id{0}; + ClientDbId client_dbid{0}; + + for(int index = 0; index < length; index++){ + try { + if(column[index] == "groupId"){ + group_id = stoll(value[index]); + } else if(column[index] == "until"){ + } else if(column[index] == "channelId"){ + channel_id = stoll(value[index]); + } else if(column[index] == "cldbid"){ + client_dbid = stoll(value[index]); + } + } catch(std::exception& ex) { + logError(this->server_id(), "Failed to load group assignment from database. Column {} contains an invalid value: {}", column[index], value[index]); + return 0; + } + } + + if(current_entry) + if(current_entry->client_database_id != client_dbid) + this->client_cache.push_back(std::move(current_entry)); + + if(!current_entry) { + current_entry = std::make_unique(); + current_entry->client_database_id = client_dbid; + } + + if(channel_id) + current_entry->channel_group_assignments.emplace_back(channel_id, group_id, false); + else + current_entry->server_group_assignments.emplace_back(group_id); + return 0; + }); + if(!res) { + error = "failed to query database (" + res.fmtStr() + ")"; + return false; + } + if(current_entry) + this->client_cache.push_back(std::move(current_entry)); + } + return true; +} + +void GroupAssignmentManager::unload_data() { + std::lock_guard cache_lock{this->client_cache_lock}; + this->client_cache.clear(); +} + +void GroupAssignmentManager::enable_cache_for_client(GroupAssignmentCalculateMode mode, ClientDbId cldbid) { + if constexpr(!kCacheAllClients) { + bool cache_exists{false}; + { + std::lock_guard cache_lock{this->client_cache_lock}; + for(auto& client : this->client_cache) + if(client->client_database_id == cldbid) { + client->use_count++; + cache_exists = true; + break; + } + } + + if(!cache_exists) { + auto cache = std::make_unique(); + cache->client_database_id = cldbid; + cache->use_count++; + + auto res = sql::command(this->sql_manager(), "SELECT `groupId`, `channelId`, `until` FROM `assignedGroups` WHERE `serverId` = :sid AND `cldbid` = :cldbid", variable{":sid", this->server_id()}, variable{":cldbid", cldbid}) + .query([&](int length, std::string* value, std::string* column) { + ChannelId channel_id{0}; + GroupId group_id{0}; + + for(int index = 0; index < length; index++){ + try { + if(column[index] == "groupId"){ + group_id = stoll(value[index]); + } else if(column[index] == "until"){ + } else if(column[index] == "channelId"){ + channel_id = stoll(value[index]); + } + } catch(std::exception& ex) { + logError(this->server_id(), "Failed to load group assignment from database for client {}. Column {} contains an invalid value: {}", cldbid, column[index], value[index]); + return 0; + } + } + if(!group_id) + return 0; + + if(channel_id) + cache->channel_group_assignments.emplace_back(channel_id, group_id, false); + else + cache->server_group_assignments.emplace_back(group_id); + return 0; + }); + + std::lock_guard cache_lock{this->client_cache_lock}; +#if 0 /* lets have some performance over double entries :D */ + for(auto& client : this->client_cache) + if(client->client_database_id == cldbid) { + /* somebody already inserted that client while we've loaded him */ + cache_exists = true; + break; + } + if(!cache_exists) +#endif + this->client_cache.push_back(std::move(cache)); + } + } + + if(mode == GroupAssignmentCalculateMode::GLOBAL) + if(auto parent = this->manager_->parent_manager(); parent) + parent->assignments().enable_cache_for_client(mode, cldbid); +} + +void GroupAssignmentManager::disable_cache_for_client(GroupAssignmentCalculateMode mode, ClientDbId cldbid) { + if constexpr(!kCacheAllClients) { + std::lock_guard cache_lock{this->client_cache_lock}; + this->client_cache.erase(std::remove_if(this->client_cache.begin(), this->client_cache.end(), [cldbid](const std::unique_ptr& client) { + return client->client_database_id == cldbid; + }), this->client_cache.end()); + } + + if(mode == GroupAssignmentCalculateMode::GLOBAL) + if(auto parent = this->manager_->parent_manager(); parent) + parent->assignments().disable_cache_for_client(mode, cldbid); +} + + +std::vector GroupAssignmentManager::server_groups_of_client(ts::server::groups::GroupAssignmentCalculateMode mode, + ts::ClientDbId cldbid) { + std::vector result{}; + bool cache_found{false}; + { + std::lock_guard cache_lock{this->client_cache_lock}; + for(auto& entry : this->client_cache) { + if(entry->client_database_id != cldbid) continue; + + result.reserve(entry->server_group_assignments.size()); + for(auto& assignment : entry->server_group_assignments) + result.push_back(assignment.group_id); + + cache_found = true; + break; + } + } + + if(!cache_found && !kCacheAllClients) { + debugMessage(this->server_id(), "Query client groups for client {} on server {}.", cldbid, this->server_id()); + + result.reserve(64); + auto res = sql::command(this->sql_manager(), "SELECT `groupId`, `until` FROM `assignedGroups` WHERE `serverId` = :sid AND `cldbid` = :cldbid AND `channelId` = 0", variable{":sid", this->server_id()}, variable{":cldbid", cldbid}) + .query([&](int length, std::string* value, std::string* column) { + GroupId group_id{0}; + try { + for(int index = 0; index < length; index++) { + if(column[index] == "groupId") { + group_id = std::stoull(value[index]); + break; + } + } + } catch(std::exception& ex) { + logWarning(this->server_id(), "Invalid data found in group assignment table. Failed to parse group id."); + return 0; + } + if(!group_id) return 0; + + result.push_back(group_id); + return 0; + }); + LOG_SQL_CMD(res); + } + + if(mode == GroupAssignmentCalculateMode::GLOBAL) + if(auto parent = this->manager_->parent_manager(); parent) { + auto parent_groups = parent->assignments().server_groups_of_client(mode, cldbid); + result.reserve(result.size() + parent_groups.size()); + result.insert(result.begin(), parent_groups.begin(), parent_groups.end()); + } + + return result; +} + +std::vector GroupAssignmentManager::channel_groups_of_client(GroupAssignmentCalculateMode mode, ClientDbId cldbid) { + std::vector result{}; + bool cache_found{false}; + { + std::lock_guard cache_lock{this->client_cache_lock}; + for(auto& entry : this->client_cache) { + if(entry->client_database_id != cldbid) continue; + + result.reserve(entry->channel_group_assignments.size()); + result.insert(result.begin(), entry->channel_group_assignments.begin(), entry->channel_group_assignments.end()); + cache_found = true; + break; + } + } + + if(!cache_found && !kCacheAllClients) { + debugMessage(this->server_id(), "Query client groups for client {} on server {}.", cldbid, this->server_id()); + + result.reserve(64); + auto res = sql::command(this->sql_manager(), "SELECT `groupId`, `channelId`, `until` FROM `assignedGroups` WHERE `serverId` = :sid AND `cldbid` = :cldbid AND `channelId` != 0", variable{":sid", this->server_id()}, variable{":cldbid", cldbid}) + .query([&](int length, std::string* value, std::string* column) { + GroupId group_id{0}; + ChannelId channel_id{0}; + try { + for(int index = 0; index < length; index++) { + if(column[index] == "groupId") { + group_id = std::stoull(value[index]); + } else if(column[index] == "channelId") { + channel_id = std::stoull(value[index]); + } + } + } catch(std::exception& ex) { + logWarning(this->server_id(), "Invalid data found in group assignment table. Failed to parse group or channel id."); + return 0; + } + if(!group_id || !channel_id) return 0; + + result.emplace_back(channel_id, group_id, false); + return 0; + }); + LOG_SQL_CMD(res); + } + + if(mode == GroupAssignmentCalculateMode::GLOBAL) + if(auto parent = this->manager_->parent_manager(); parent) { + auto parent_groups = parent->assignments().channel_groups_of_client(mode, cldbid); + result.reserve(result.size() + parent_groups.size()); + result.insert(result.begin(), parent_groups.begin(), parent_groups.end()); + } + + return result; +} + +std::deque GroupAssignmentManager::server_group_clients(GroupId group_id) { + std::deque result{}; + if constexpr(kCacheAllClients) { + std::lock_guard cache_lock{this->client_cache_lock}; + for(auto& client : this->client_cache) { + auto it = std::find_if(client->server_group_assignments.begin(), client->server_group_assignments.end(), [&](const ServerGroupAssignment& assignment) { + return assignment.group_id == group_id; + }); + if(it == client->server_group_assignments.end()) + continue; + result.push_back(client->client_database_id); + } + } else { + auto res = sql::command(this->sql_manager(), "SELECT `cldbid` FROM `assignedGroups` WHERE `serverId` = :sid AND `channelId` = 0 AND `groupId` = :gid", variable{":sid", this->server_id()}, variable{":gid", group_id}) + .query([&](int length, std::string* value, std::string* column) { + ClientDbId cldbid{0}; + try { + for(int index = 0; index < length; index++) { + if(column[index] == "cldbid") { + cldbid = std::stoull(value[index]); + break; + } + } + } catch(std::exception& ex) { + logWarning(this->server_id(), "Invalid data found in group assignment table. Failed to parse client database id."); + return 0; + } + if(!cldbid) return 0; + + result.push_back(cldbid); + return 0; + }); + LOG_SQL_CMD(res); + } + + if(auto parent = this->manager_->parent_manager(); parent) { + auto parent_clients = parent->assignments().server_group_clients(group_id); + result.insert(result.begin(), parent_clients.begin(), parent_clients.end()); + } + + return result; +} + +GroupAssignmentResult GroupAssignmentManager::add_server_group(ClientDbId client, GroupId group) { + bool cache_verified{false}; + { + std::lock_guard cache_lock{this->client_cache_lock}; + for(auto& entry : this->client_cache) { + if(entry->client_database_id != client) continue; + + auto it = std::find_if(entry->server_group_assignments.begin(), entry->server_group_assignments.end(), [&](const ServerGroupAssignment& assignment) { + return assignment.group_id == group; + }); + if(it != entry->server_group_assignments.end()) + return GroupAssignmentResult::ADD_ALREADY_MEMBER_OF_GROUP; + entry->server_group_assignments.emplace_back(group); + cache_verified = true; + break; + } + + if(!cache_verified && kCacheAllClients) { + /* add the client to the cache */ + auto cache = std::make_unique(); + cache->client_database_id = client; + cache->server_group_assignments.emplace_back(group); + this->client_cache.push_back(cache); + } + } + + { + + auto command = sql::command(this->sql_manager(), "INSERT INTO `assignedGroups` (`serverId`, `cldbid`, `groupId`, `channelId`, `until`) VALUES (:sid, :cldbid, :gid, :chid, :until)", + variable{":sid", this->server_id()}, + variable{":cldbid", client}, + variable{":gid", group}, + variable{":chid", 0}, + variable{":until", 0}); + if(cache_verified) + command.executeLater().waitAndGetLater(LOG_SQL_CMD, {-1, "failed to insert group assignment into the database"}); + else { + auto result = command.execute(); + if(!result) return GroupAssignmentResult::ADD_ALREADY_MEMBER_OF_GROUP; //TODO: Parse error from database? + } + } + return GroupAssignmentResult::SUCCESS; +} + +GroupAssignmentResult GroupAssignmentManager::remove_server_group(ClientDbId client, GroupId group) { + bool cache_verified{false}; + { + std::lock_guard cache_lock{this->client_cache_lock}; + for(auto& entry : this->client_cache) { + if(entry->client_database_id != client) continue; + + auto it = std::find_if(entry->server_group_assignments.begin(), entry->server_group_assignments.end(), [&](const ServerGroupAssignment& assignment) { + return assignment.group_id == group; + }); + if(it == entry->server_group_assignments.end()) + return GroupAssignmentResult::REMOVE_NOT_MEMBER_OF_GROUP; + entry->server_group_assignments.erase(it); + cache_verified = true; + break; + } + + if(!cache_verified && kCacheAllClients) + return GroupAssignmentResult::REMOVE_NOT_MEMBER_OF_GROUP; + } + + { + auto command = sql::command(this->sql_manager(), "DELETE FROM `assignedGroups` WHERE `serverId` = :sid AND `cldbid` = :cldbid AND `groupId` = :gid AND `channelId` = :chid", + variable{":sid", this->server_id()}, + variable{":cldbid", client}, + variable{":gid", group}, + variable{":chid", 0}); + if(cache_verified) + command.executeLater().waitAndGetLater(LOG_SQL_CMD, {-1, "failed to remove group assignment from database"}); + else { + auto result = command.execute(); + if(!result) return GroupAssignmentResult::REMOVE_NOT_MEMBER_OF_GROUP; //TODO: Parse error from database? + } + } + return GroupAssignmentResult::SUCCESS; +} + +GroupAssignmentResult GroupAssignmentManager::set_channel_group(ClientDbId client, GroupId group, ChannelId channel_id, bool temporary) { + bool cache_verified{false}; + { + std::lock_guard cache_lock{this->client_cache_lock}; + for(auto& entry : this->client_cache) { + if(entry->client_database_id != client) continue; + + auto it = std::find_if(entry->channel_group_assignments.begin(), entry->channel_group_assignments.end(), [&](const ChannelGroupAssignment& assignment) { + return assignment.channel_id == channel_id; + }); + if(it != entry->channel_group_assignments.end()) { + if(group) { + if(it->group_id == group) + return GroupAssignmentResult::SET_ALREADY_MEMBER_OF_GROUP; + it->group_id = group; + } else { + entry->channel_group_assignments.erase(it); + } + } else { + if(group) { + entry->channel_group_assignments.emplace_back(channel_id, group, temporary); + } + } + cache_verified = true; + break; + } + + if(!cache_verified && kCacheAllClients) { + if(group) { + /* add the client to the cache */ + auto cache = std::make_unique(); + cache->client_database_id = client; + cache->channel_group_assignments.emplace_back(channel_id, group, temporary); + this->client_cache.push_back(cache); + } else { + return GroupAssignmentResult::SUCCESS; + } + } + } + if(temporary) + return GroupAssignmentResult::SUCCESS; + + sql::command(this->sql_manager(), "DELETE FROM `assignedGroups` WHERE `serverId` = :sid AND `cldbid` = :cldbid AND `channelId` = :chid", + variable{":sid", this->server_id()}, + variable{":cldbid", client}, + variable{":chid", channel_id}) + .executeLater().waitAndGetLater(LOG_SQL_CMD, {1, "failed to delete old channel group assignment"}); + + if(group) { + sql::command(this->sql_manager(), "INSERT INTO `assignedGroups` (`serverId`, `cldbid`, `groupId`, `channelId`, `until`) VALUES (:sid, :cldbid, :gid, :chid, :until)", + variable{":sid", this->server_id()}, + variable{":cldbid", client}, + variable{":gid", group}, + variable{":chid", channel_id}, + variable{":until", 0}) + .executeLater().waitAndGetLater(LOG_SQL_CMD, {1, "failed to insert channel group assignment"}); + } + return GroupAssignmentResult::SUCCESS; +} + +void GroupAssignmentManager::cleanup_channel_assignments(ChannelId channel_id) { + { + std::lock_guard cache_lock{this->client_cache_lock}; + for(auto& client : this->client_cache) { + client->channel_group_assignments.erase(std::find_if(client->channel_group_assignments.begin(), client->channel_group_assignments.end(), [&](const ChannelGroupAssignment& assignment) { + return assignment.channel_id == channel_id; + })); + } + } + + sql::command(this->sql_manager(), "DELETE FROM `assignedGroups` WHERE `serverId` = :sid AND `channelId` = :cid", variable{":sid", this->server_id()}, variable{":cid", channel_id}) + .executeLater().waitAndGetLater(LOG_SQL_CMD, {1, "future failed"}); +} + +void GroupAssignmentManager::cleanup_assignments() { + { + + std::lock_guard cache_lock{this->client_cache_lock}; + this->client_cache.clear(); + } + + sql::command(this->sql_manager(), "DELETE FROM `assignedGroups` WHERE `serverId` = :sid", variable{":sid", this->server_id()}) + .executeLater().waitAndGetLater(LOG_SQL_CMD, {1, "future failed"}); +} + +void GroupAssignmentManager::cleanup_channel_temporary_assignment(ClientDbId client_dbid, ChannelId channel) { + std::lock_guard cache_lock{this->client_cache_lock}; + for(auto& client : this->client_cache) { + if(client->client_database_id == client_dbid) { + auto assignment = std::find_if(client->channel_group_assignments.begin(), client->channel_group_assignments.end(), [&](const ChannelGroupAssignment& assignment) { + return assignment.channel_id == channel; + }); + if(assignment->temporary_assignment) + client->channel_group_assignments.erase(assignment); + break; + } + } +} + +std::deque GroupAssignmentManager::update_client_group_properties(const std::shared_ptr &client, ChannelId channel_id) { + std::deque changed; + bool increase_join_state{false}; + + //Server groups + { + auto server_groups = this->server_groups_of_client(GroupAssignmentCalculateMode::GLOBAL, client->getClientDatabaseId()); + std::string group_string; + for(const auto& group : server_groups) + if(group_string.empty()) group_string += std::to_string(group); + else group_string += "," + std::to_string(group); + + if(client->properties()[property::CLIENT_SERVERGROUPS] != group_string) { + client->properties()[property::CLIENT_SERVERGROUPS] = group_string; + changed.push_back(property::CLIENT_SERVERGROUPS); + increase_join_state = true; + } + + } + + //Channel group + if(channel_id) { + auto assignment = this->manager_->virtual_server()->channel_service().calculate_channel_group(channel_id, client->getClientDatabaseId(), client->getType()); + + if(client->properties()[property::CLIENT_CHANNEL_GROUP_INHERITED_CHANNEL_ID] != assignment.inherited_channel_id) { + client->properties()[property::CLIENT_CHANNEL_GROUP_INHERITED_CHANNEL_ID] = assignment.inherited_channel_id; + changed.push_back(property::CLIENT_CHANNEL_GROUP_INHERITED_CHANNEL_ID); + } + + if(client->properties()[property::CLIENT_CHANNEL_GROUP_ID] != assignment.group_id) { + client->properties()[property::CLIENT_CHANNEL_GROUP_ID] = assignment.group_id; + changed.push_back(property::CLIENT_CHANNEL_GROUP_ID); + increase_join_state = true; + } + } + + if(increase_join_state) + client->increase_join_state(); /* groups have changed :) */ + + return changed; } \ No newline at end of file diff --git a/server/src/groups/GroupAssignmentManager.h b/server/src/groups/GroupAssignmentManager.h index 2079261..5517262 100644 --- a/server/src/groups/GroupAssignmentManager.h +++ b/server/src/groups/GroupAssignmentManager.h @@ -1,110 +1,110 @@ -#pragma once - -#include -#include -#include -#include -#include -#include -#include - -namespace sql { - class SqlManager; -} - -namespace ts::server { - class ConnectedClient; - namespace vserver { - class VirtualServerBase; - } - - namespace groups { - enum struct GroupAssignmentCalculateMode { - LOCAL, /* only calculate clients groups for the local server */ - GLOBAL /* use the parent group manager as well, if existing */ - }; - - class ServerGroup; - class ChannelGroup; - class GroupManager; - - struct ChannelGroupAssignment { - ChannelGroupAssignment(ChannelId channel_id, GroupId group_id, bool t) : channel_id{channel_id}, group_id{group_id}, temporary_assignment{t} { } - ChannelGroupAssignment(const ChannelGroupAssignment& other) = default; - ChannelGroupAssignment(ChannelGroupAssignment&&) = default; - - ChannelId channel_id; - GroupId group_id; - bool temporary_assignment; - }; - - struct ServerGroupAssignment { - explicit ServerGroupAssignment(GroupId group_id) : group_id{group_id} { } - ServerGroupAssignment(const ServerGroupAssignment& other) = default; - ServerGroupAssignment(ServerGroupAssignment&&) = default; - - GroupId group_id; - }; - - enum struct GroupAssignmentResult { - SUCCESS, - ADD_ALREADY_MEMBER_OF_GROUP, - REMOVE_NOT_MEMBER_OF_GROUP, - SET_ALREADY_MEMBER_OF_GROUP - }; - - class GroupAssignmentManager { - constexpr static bool kCacheAllClients{true}; - public: - explicit GroupAssignmentManager(GroupManager* /* manager */); - ~GroupAssignmentManager(); - - /* general load/initialize methods */ - bool initialize(std::string& /* error */); - bool load_data(std::string& /* error */); - void unload_data(); - - /* client specific cache methods */ - void enable_cache_for_client(GroupAssignmentCalculateMode /* mode */, ClientDbId /* client database id */); - void disable_cache_for_client(GroupAssignmentCalculateMode /* mode */, ClientDbId /* client database id */); - - /* info/query methods */ - [[nodiscard]] std::vector server_groups_of_client(GroupAssignmentCalculateMode /* mode */, ClientDbId /* client database id */); - [[nodiscard]] std::vector channel_groups_of_client(GroupAssignmentCalculateMode /* mode */, ClientDbId /* client database id */); - - [[nodiscard]] std::deque server_group_clients(GroupId /* group id */); - //[[nodiscard]] std::deque channel_group_clients(GroupId /* group id */, ChannelId /* channel id */); - - /* change methods */ - GroupAssignmentResult add_server_group(ClientDbId /* client database id */, GroupId /* group id */); - GroupAssignmentResult remove_server_group(ClientDbId /* client database id */, GroupId /* group id */); - - /* Use channel group id 0 to delete any assignment */ - GroupAssignmentResult set_channel_group(ClientDbId /* client database id */, GroupId /* group id */, ChannelId /* channel id */, bool /* temporary assignment */); - - std::deque update_client_group_properties(const std::shared_ptr &client, ChannelId /* target channel */); - - void cleanup_assignments(); - void cleanup_channel_assignments(ChannelId /* channel */); - void cleanup_channel_temporary_assignment(ClientDbId /* client database id */, ChannelId /* channel */); - private: - struct ClientCache { - ClientDbId client_database_id{0}; - size_t use_count{0}; - - std::deque channel_group_assignments{}; - std::deque server_group_assignments{}; - }; - - GroupManager* manager_; - - std::mutex client_cache_lock; - std::deque> client_cache{}; - - - [[nodiscard]] sql::SqlManager* sql_manager(); - [[nodiscard]] ServerId server_id(); - }; - - } +#pragma once + +#include +#include +#include +#include +#include +#include +#include + +namespace sql { + class SqlManager; +} + +namespace ts::server { + class ConnectedClient; + namespace vserver { + class VirtualServerBase; + } + + namespace groups { + enum struct GroupAssignmentCalculateMode { + LOCAL, /* only calculate clients groups for the local server */ + GLOBAL /* use the parent group manager as well, if existing */ + }; + + class ServerGroup; + class ChannelGroup; + class GroupManager; + + struct ChannelGroupAssignment { + ChannelGroupAssignment(ChannelId channel_id, GroupId group_id, bool t) : channel_id{channel_id}, group_id{group_id}, temporary_assignment{t} { } + ChannelGroupAssignment(const ChannelGroupAssignment& other) = default; + ChannelGroupAssignment(ChannelGroupAssignment&&) = default; + + ChannelId channel_id; + GroupId group_id; + bool temporary_assignment; + }; + + struct ServerGroupAssignment { + explicit ServerGroupAssignment(GroupId group_id) : group_id{group_id} { } + ServerGroupAssignment(const ServerGroupAssignment& other) = default; + ServerGroupAssignment(ServerGroupAssignment&&) = default; + + GroupId group_id; + }; + + enum struct GroupAssignmentResult { + SUCCESS, + ADD_ALREADY_MEMBER_OF_GROUP, + REMOVE_NOT_MEMBER_OF_GROUP, + SET_ALREADY_MEMBER_OF_GROUP + }; + + class GroupAssignmentManager { + constexpr static bool kCacheAllClients{true}; + public: + explicit GroupAssignmentManager(GroupManager* /* manager */); + ~GroupAssignmentManager(); + + /* general load/initialize methods */ + bool initialize(std::string& /* error */); + bool load_data(std::string& /* error */); + void unload_data(); + + /* client specific cache methods */ + void enable_cache_for_client(GroupAssignmentCalculateMode /* mode */, ClientDbId /* client database id */); + void disable_cache_for_client(GroupAssignmentCalculateMode /* mode */, ClientDbId /* client database id */); + + /* info/query methods */ + [[nodiscard]] std::vector server_groups_of_client(GroupAssignmentCalculateMode /* mode */, ClientDbId /* client database id */); + [[nodiscard]] std::vector channel_groups_of_client(GroupAssignmentCalculateMode /* mode */, ClientDbId /* client database id */); + + [[nodiscard]] std::deque server_group_clients(GroupId /* group id */); + //[[nodiscard]] std::deque channel_group_clients(GroupId /* group id */, ChannelId /* channel id */); + + /* change methods */ + GroupAssignmentResult add_server_group(ClientDbId /* client database id */, GroupId /* group id */); + GroupAssignmentResult remove_server_group(ClientDbId /* client database id */, GroupId /* group id */); + + /* Use channel group id 0 to delete any assignment */ + GroupAssignmentResult set_channel_group(ClientDbId /* client database id */, GroupId /* group id */, ChannelId /* channel id */, bool /* temporary assignment */); + + std::deque update_client_group_properties(const std::shared_ptr &client, ChannelId /* target channel */); + + void cleanup_assignments(); + void cleanup_channel_assignments(ChannelId /* channel */); + void cleanup_channel_temporary_assignment(ClientDbId /* client database id */, ChannelId /* channel */); + private: + struct ClientCache { + ClientDbId client_database_id{0}; + size_t use_count{0}; + + std::deque channel_group_assignments{}; + std::deque server_group_assignments{}; + }; + + GroupManager* manager_; + + std::mutex client_cache_lock; + std::deque> client_cache{}; + + + [[nodiscard]] sql::SqlManager* sql_manager(); + [[nodiscard]] ServerId server_id(); + }; + + } } \ No newline at end of file diff --git a/server/src/groups/GroupManager.cpp b/server/src/groups/GroupManager.cpp index 499c4ec..a851a25 100644 --- a/server/src/groups/GroupManager.cpp +++ b/server/src/groups/GroupManager.cpp @@ -1,5 +1,5 @@ -// -// Created by WolverinDEV on 03/03/2020. -// - -#include "GroupManager.h" +// +// Created by WolverinDEV on 03/03/2020. +// + +#include "GroupManager.h" diff --git a/server/src/groups/GroupManager.h b/server/src/groups/GroupManager.h index 7aa0355..5471f43 100644 --- a/server/src/groups/GroupManager.h +++ b/server/src/groups/GroupManager.h @@ -1,82 +1,82 @@ -#pragma once - -#include -#include -#include -#include -#include -#include -#include "./GroupAssignmentManager.h" -#include "./Group.h" - -namespace ts::server { - namespace vserver { - class VirtualServerBase; - } - - namespace groups { - enum struct GroupCalculateMode { - LOCAL, /* only calculate clients groups for the local server */ - GLOBAL /* use the parent group manager as well, if existing */ - }; - - enum struct ActionReturnCode { - RENAME_NAME_ALREADY_USED, - RENAME_GROUP_INVALID_ID, - - DELETE_GROUP_INVALID_ID, - - CUSTOM_ERROR - }; - - class GroupManager { - friend class Group; - public: - GroupManager(vserver::VirtualServerBase* /* virtual server */, std::shared_ptr /* parent */); - ~GroupManager(); - - bool initialize(std::string& /* error */); - bool load_data(std::string& /* error */); - void unload_data(); - - [[nodiscard]] inline vserver::VirtualServerBase* virtual_server() { return this->virtual_server_; } - - [[nodiscard]] inline const std::shared_ptr& parent_manager() { return this->parent_manager_; } - [[nodiscard]] inline GroupAssignmentManager& assignments() { return this->assignment_manager_; } - - [[nodiscard]] std::deque> server_groups(GroupCalculateMode /* mode */); - [[nodiscard]] std::deque> channel_groups(GroupCalculateMode /* mode */); - - [[nodiscard]] std::shared_ptr default_server_group(ClientType /* client type */); - [[nodiscard]] std::shared_ptr default_channel_group(ClientType /* client type */); - - [[nodiscard]] std::shared_ptr find_server_group(GroupId /* group id */); - [[nodiscard]] std::shared_ptr find_channel_group(GroupId /* group id */); - - [[nodiscard]] std::shared_ptr create_server_group(GroupType type, const std::string& /* group name */); - [[nodiscard]] std::shared_ptr create_channel_group(GroupType type, const std::string& /* group name */); - - [[nodiscard]] ActionReturnCode copy_server_group(GroupId /* group id */, GroupType /* target group type */, const std::string& /* target group name */); - [[nodiscard]] ActionReturnCode copy_channel_group(GroupId /* group id */, GroupType /* target group type */, const std::string& /* target group name */); - - [[nodiscard]] ActionReturnCode copy_server_group_permissions(GroupId /* group id */, GroupId /* target group */); - [[nodiscard]] ActionReturnCode copy_channel_group_permissions(GroupId /* group id */, GroupId /* target group */); - - [[nodiscard]] ActionReturnCode rename_server_group(GroupId /* group id */, const std::string& /* target group name */); - [[nodiscard]] ActionReturnCode rename_channel_group(GroupId /* group id */, const std::string& /* target group name */); - - [[nodiscard]] ActionReturnCode delete_server_group(GroupId /* group id */); - [[nodiscard]] ActionReturnCode delete_channel_group(GroupId /* group id */); - private: - vserver::VirtualServerBase* virtual_server_; - - std::shared_ptr parent_manager_; - GroupAssignmentManager assignment_manager_; - - std::mutex group_lock_{}; - - [[nodiscard]] sql::SqlManager* sql_manager(); - void update_group_name(Group* /* group */); - }; - } +#pragma once + +#include +#include +#include +#include +#include +#include +#include "./GroupAssignmentManager.h" +#include "./Group.h" + +namespace ts::server { + namespace vserver { + class VirtualServerBase; + } + + namespace groups { + enum struct GroupCalculateMode { + LOCAL, /* only calculate clients groups for the local server */ + GLOBAL /* use the parent group manager as well, if existing */ + }; + + enum struct ActionReturnCode { + RENAME_NAME_ALREADY_USED, + RENAME_GROUP_INVALID_ID, + + DELETE_GROUP_INVALID_ID, + + CUSTOM_ERROR + }; + + class GroupManager { + friend class Group; + public: + GroupManager(vserver::VirtualServerBase* /* virtual server */, std::shared_ptr /* parent */); + ~GroupManager(); + + bool initialize(std::string& /* error */); + bool load_data(std::string& /* error */); + void unload_data(); + + [[nodiscard]] inline vserver::VirtualServerBase* virtual_server() { return this->virtual_server_; } + + [[nodiscard]] inline const std::shared_ptr& parent_manager() { return this->parent_manager_; } + [[nodiscard]] inline GroupAssignmentManager& assignments() { return this->assignment_manager_; } + + [[nodiscard]] std::deque> server_groups(GroupCalculateMode /* mode */); + [[nodiscard]] std::deque> channel_groups(GroupCalculateMode /* mode */); + + [[nodiscard]] std::shared_ptr default_server_group(ClientType /* client type */); + [[nodiscard]] std::shared_ptr default_channel_group(ClientType /* client type */); + + [[nodiscard]] std::shared_ptr find_server_group(GroupId /* group id */); + [[nodiscard]] std::shared_ptr find_channel_group(GroupId /* group id */); + + [[nodiscard]] std::shared_ptr create_server_group(GroupType type, const std::string& /* group name */); + [[nodiscard]] std::shared_ptr create_channel_group(GroupType type, const std::string& /* group name */); + + [[nodiscard]] ActionReturnCode copy_server_group(GroupId /* group id */, GroupType /* target group type */, const std::string& /* target group name */); + [[nodiscard]] ActionReturnCode copy_channel_group(GroupId /* group id */, GroupType /* target group type */, const std::string& /* target group name */); + + [[nodiscard]] ActionReturnCode copy_server_group_permissions(GroupId /* group id */, GroupId /* target group */); + [[nodiscard]] ActionReturnCode copy_channel_group_permissions(GroupId /* group id */, GroupId /* target group */); + + [[nodiscard]] ActionReturnCode rename_server_group(GroupId /* group id */, const std::string& /* target group name */); + [[nodiscard]] ActionReturnCode rename_channel_group(GroupId /* group id */, const std::string& /* target group name */); + + [[nodiscard]] ActionReturnCode delete_server_group(GroupId /* group id */); + [[nodiscard]] ActionReturnCode delete_channel_group(GroupId /* group id */); + private: + vserver::VirtualServerBase* virtual_server_; + + std::shared_ptr parent_manager_; + GroupAssignmentManager assignment_manager_; + + std::mutex group_lock_{}; + + [[nodiscard]] sql::SqlManager* sql_manager(); + void update_group_name(Group* /* group */); + }; + } } \ No newline at end of file diff --git a/server/src/services/ClientChannelService.cpp b/server/src/services/ClientChannelService.cpp index 67d1c63..31eb8a7 100644 --- a/server/src/services/ClientChannelService.cpp +++ b/server/src/services/ClientChannelService.cpp @@ -1,379 +1,379 @@ -// -// Created by WolverinDEV on 03/03/2020. -// - -#include -#include "./ClientChannelService.h" -#include "../client/ConnectedClient.h" -#include "../vserver/VirtualServer.h" -#include "../groups/GroupManager.h" -#include "../groups/GroupAssignmentManager.h" -#include -#include -#include - -using namespace ts::server::channels; - -ts::ServerId ClientChannelService::get_server_id() const { - return this->virtual_server_->server_id(); -} - -ChannelGroupInheritance ClientChannelService::calculate_channel_group(ChannelId channel_id, ClientDbId client_dbid, ClientType client_type) { - auto tree_lock = this->virtual_server_->lock_channel_clients(); - tree_lock.auto_lock_shared(); - - auto assignments = this->virtual_server_->group_manager()->assignments().channel_groups_of_client(groups::GroupAssignmentCalculateMode::GLOBAL, client_dbid); - - auto inheritance_channel = this->virtual_server_->server_channel_tree()->findChannel(channel_id); - while(inheritance_channel) { - auto inheritance_channel_id = inheritance_channel->channelId(); - auto assignment = std::find_if(assignments.begin(), assignments.end(), [&](const groups::ChannelGroupAssignment& assignment) { - return assignment.channel_id == inheritance_channel_id; - }); - - if(assignment == assignments.end() || !assignment->group_id) { - auto permission = inheritance_channel->permissions()->permission_value_flagged(permission::b_channel_group_inheritance_end); - if(permission::v2::permission_granted(1, permission)) - break; - - inheritance_channel = inheritance_channel->parent(); - } else { - return { - assignment->group_id, - inheritance_channel_id - }; - } - } - - auto default_group = this->virtual_server_->group_manager()->default_channel_group(client_type); - assert(default_group); - return { - default_group->group_id(), - channel_id - }; -} - -/* - * General notice: - * We're locking the clients command_lock, so when changing the currentChannel variable nothing gets messed up. - * As a little note: This must be done before locking the channel tree at all. - */ - -ClientMoveResult ClientChannelService::client_ban(const std::shared_ptr &target, - const std::shared_ptr &invoker, const std::string &reason, size_t time, - ts::rwshared_lock &tree_lock) { - tree_lock.auto_unlock(); - auto command_lock = target->lock_command_handling(); - if(auto err = tree_lock.auto_lock_exclusive(); err) - return ClientMoveResult::TREE_LOCK_FAILED; - - std::unique_lock client_chan_tree_lock{target->get_channel_lock()}; - auto old_channel = target->getChannel(); - target->setChannel(nullptr); - client_chan_tree_lock.unlock(); - - ChannelId old_channel_id{0}; - if(old_channel) { - old_channel_id = old_channel->channelId(); - for(const auto& client : this->virtual_server_->connected_clients(false)) { - if(!client || client == target) - continue; - - std::unique_lock client_channel_lock(client->get_channel_lock()); - if(client->isClientVisible(target, false)) - client->notifyClientLeftViewBanned(target, old_channel_id, invoker, time, false, reason); - } - - auto s_channel = dynamic_pointer_cast(old_channel); - assert(s_channel); - s_channel->unregister_client(target); - } - - target->notifyClientLeftViewBanned(target, old_channel_id, invoker, time, false, reason); - return ClientMoveResult::SUCCESS; -} - -ClientMoveResult ClientChannelService::client_kick(const std::shared_ptr &target, - const std::shared_ptr &invoker, const std::string &reason, - const std::shared_ptr &target_channel, - ts::rwshared_lock &tree_lock) { - if(target_channel) { - return this->client_move(target, target_channel, invoker, reason, ViewReasonId::VREASON_CHANNEL_KICK, true, tree_lock); - } else { - tree_lock.auto_unlock(); - auto command_lock = target->lock_command_handling(); - if(auto err = tree_lock.auto_lock_exclusive(); err) - return ClientMoveResult::TREE_LOCK_FAILED; - - std::unique_lock client_chan_tree_lock{target->get_channel_lock()}; - auto old_channel = target->getChannel(); - target->setChannel(nullptr); - client_chan_tree_lock.unlock(); - - ChannelId old_channel_id{0}; - if(old_channel) { - old_channel_id = old_channel->channelId(); - for(const auto& client : this->virtual_server_->connected_clients(false)) { - if(!client || client == target) - continue; - - std::unique_lock client_channel_lock(client->get_channel_lock()); - if(client->isClientVisible(target, false)) - client->notifyClientLeftViewKicked(target, old_channel_id, reason, invoker, false, nullptr); - } - - auto s_channel = dynamic_pointer_cast(old_channel); - assert(s_channel); - s_channel->unregister_client(target); - } - - target->notifyClientLeftViewKicked(target, old_channel_id, reason, invoker, false, nullptr); - } - - return ClientMoveResult::SUCCESS; -} - -ClientMoveResult ClientChannelService::client_move(const std::shared_ptr &target, - std::shared_ptr s_target_channel, - const std::shared_ptr &invoker, - const std::string &reason_message, - ViewReasonId reason_id, - bool notify_client, - ts::rwshared_lock &tree_lock) { - TIMING_START(timings); - tree_lock.auto_unlock(); - - auto command_lock = target->lock_command_handling(); - if(auto err = tree_lock.auto_lock_exclusive(); err) - return ClientMoveResult::TREE_LOCK_FAILED; - - TIMING_STEP(timings, "tree lock setup"); - if(target->getChannel() == s_target_channel) - return ClientMoveResult::SUCCESS; - - /* first step: resolve the target channel / or fix missing */ - auto s_source_channel = dynamic_pointer_cast(target->getChannel()); - assert(!target->getChannel() || s_source_channel != nullptr); - - auto server_channel_tree = this->virtual_server_->server_channel_tree(); - std::deque client_updates; - std::deque changed_groups{}; - if(s_target_channel) { - /* don't let the client join a deleted channel */ - if(s_target_channel->deleted) { - s_target_channel = dynamic_pointer_cast(server_channel_tree->getDefaultChannel()); - assert(s_target_channel); - } - - /* update the group properties here already, so for all enter views we could just send the new props directly */ - changed_groups = this->virtual_server_->group_manager()->assignments().update_client_group_properties(target, s_target_channel->channelId()); - client_updates.insert(client_updates.end(), changed_groups.begin(), changed_groups.end()); //TODO: Only update for clients which have no enter? - } - - auto l_target_channel = s_target_channel ? server_channel_tree->findLinkedChannel(s_target_channel->channelId()) : nullptr; - auto l_source_channel = s_source_channel ? server_channel_tree->findLinkedChannel(s_source_channel->channelId()) : nullptr; - TIMING_STEP(timings, "channel lookup "); - - - /* second step: show the target channel to the client if its not shown and let him subscibe to the channel */ - if(s_target_channel && notify_client) { - std::unique_lock client_channel_lock(target->get_channel_lock()); - - bool success = false; - /* TODO: Use a bunk here and not a notify for every single */ - for(const auto& channel : target->channel_view()->show_channel(l_target_channel, success)) - target->notifyChannelShow(channel->channel(), channel->previous_channel); - sassert(success); - if(!success) - return ClientMoveResult::VIEW_INSERT_FAILED; - - target->subscribeChannel({s_target_channel}, false, true); - } - TIMING_STEP(timings, "target show chan"); - - if(s_target_channel) { - for(const auto& client : this->virtual_server_->connected_clients(false)) { - if (!notify_client && client == target) continue; - - std::unique_lock client_channel_lock{client->get_channel_lock()}; - auto chan_target = client->channel_view()->find_channel(s_target_channel); - - if(chan_target) { - auto chan_source = client->channel_view()->find_channel(s_source_channel); - if(chan_source) { - if (chan_target->subscribed || client == target) { - if (client == target || client->isClientVisible(target, false)) { - client->notifyClientMoved(target, s_target_channel, reason_id, reason_message, invoker, false); - } else { - client->notifyClientEnterView(target, invoker, reason_message, s_target_channel, reason_id, s_source_channel, false); - } - } else if(client->isClientVisible(target, false)){ - //Client got out of view - client->notifyClientLeftView(target, s_target_channel, reason_id, reason_message.empty() ? std::string{"view left"} : reason_message, invoker, false); - } - } else { - if(client == target && client->getType() != ClientType::CLIENT_INTERNAL && client->getType() != ClientType::CLIENT_MUSIC) - logCritical(this->get_server_id(), "{} Client enters visibility twice!", CLIENT_STR_LOG_PREFIX_(client)); - - //Client entered view - if(chan_target->subscribed) - client->notifyClientEnterView(target, invoker, reason_message, s_target_channel, ViewReasonId::VREASON_USER_ACTION, nullptr, false); - } - } else { - /* target channel isn't visible => so client gone out of view */ - if(client == target && client->getType() != ClientType::CLIENT_INTERNAL && client->getType() != ClientType::CLIENT_MUSIC) - logCritical(this->get_server_id(), "{} Moving own client into a not visible channel! This shall not happen!", CLIENT_STR_LOG_PREFIX_(client)); - //Test for in view? (Notify already does but nvm) - - if(client->isClientVisible(target, false)){ - //Client got out of view - if(reason_id == ViewReasonId::VREASON_USER_ACTION) - client->notifyClientLeftView(target, nullptr, ViewReasonId::VREASON_SERVER_LEFT, reason_message.empty() ? "joined a hidden channel" : reason_message, invoker, false); - else - client->notifyClientLeftView(target, nullptr, ViewReasonId::VREASON_SERVER_LEFT, reason_message.empty() ? "moved to a hidden channel" : reason_message, invoker, false); - } - } - - } - - if(s_source_channel) - s_source_channel->unregister_client(target); - s_target_channel->register_client(target); - } else { - /* client left the server */ - if(target->getChannel()) { - for(const auto& client : this->virtual_server_->connected_clients(false)) { - if(!client || client == target) - continue; - - std::unique_lock client_channel_lock(client->get_channel_lock()); - if(client->isClientVisible(target, false)) - client->notifyClientLeftView(target, nullptr, reason_id, reason_message, invoker, false); - } - - s_source_channel->unregister_client(target); - } - } - TIMING_STEP(timings, "notify viewers "); - - target->setChannel(s_target_channel); - tree_lock.downgrade_lock(); - - std::unique_lock client_channel_lock(target->get_channel_lock()); - TIMING_STEP(timings, "lock own ch tree"); - - if (s_source_channel) { - s_source_channel->properties()[property::CHANNEL_LAST_LEFT] = std::chrono::duration_cast(std::chrono::system_clock::now().time_since_epoch()).count(); - - this->virtual_server_->group_manager()->assignments().cleanup_channel_temporary_assignment(target->getClientDatabaseId(), s_source_channel->channelId()); - auto update = target->properties()[property::CLIENT_IS_TALKER].as() || target->properties()[property::CLIENT_TALK_REQUEST].as() > 0; - - if(update) { - target->properties()[property::CLIENT_IS_TALKER] = 0; - target->properties()[property::CLIENT_TALK_REQUEST] = 0; - target->properties()[property::CLIENT_TALK_REQUEST_MSG] = ""; - client_updates.push_back(property::CLIENT_IS_TALKER); - client_updates.push_back(property::CLIENT_TALK_REQUEST); - client_updates.push_back(property::CLIENT_TALK_REQUEST_MSG); - } - TIMING_STEP(timings, "src chan up"); - } - - if (s_target_channel) { - if(target->update_cached_permissions()) /* update cached calculated permissions */ - target->sendNeededPermissions(false); - TIMING_STEP(timings, "perm gr upd"); - - if(s_source_channel) { - std::deque deleted; - for(const auto& channel : target->channel_view()->test_channel(l_source_channel, l_target_channel)) - deleted.push_back(channel->channelId()); - - if(!deleted.empty()) - target->notifyChannelHide(deleted, false); - - /* force unsubscribe from old channel */ - auto i_source_channel = s_source_channel->channelId(); - if(std::find(deleted.begin(), deleted.end(), i_source_channel) == deleted.end()) { - auto source_channel_sub_power = target->calculate_permission(permission::i_channel_subscribe_power, i_source_channel); - if(!s_source_channel->permission_granted(permission::i_channel_needed_subscribe_power, source_channel_sub_power, false)) { - auto source_channel_sub_power_ignore = target->calculate_permission(permission::b_channel_ignore_subscribe_power, i_source_channel); - if(!permission::v2::permission_granted(1, source_channel_sub_power_ignore, true)) { - logTrace(this->get_server_id(), "Force unsubscribing of client {} for channel {}/{}. (Channel switch and no permissions)", - CLIENT_STR_LOG_PREFIX_(target), s_source_channel->name(), - i_source_channel - ); - target->unsubscribeChannel({s_source_channel}, false); //Unsubscribe last channel (hasn't permissions) - } - } - } - TIMING_STEP(timings, "src hide ts"); - } - } - client_channel_lock.unlock(); - /* both methods lock if they require stuff */ - this->notifyClientPropertyUpdates(target, client_updates, s_source_channel ? true : false); - TIMING_STEP(timings, "notify cpro"); - if(s_target_channel) { - target->updateChannelClientProperties(false, s_source_channel ? true : false); - TIMING_STEP(timings, "notify_t_pr"); - } - debugMessage(this->get_server_id(), "{} Client move timings: {}", CLIENT_STR_LOG_PREFIX_(target), TIMING_FINISH(timings)); -} - -/* - * 1. flag channel as deleted (lock channel tree so no moves) - * 2. Gather all clients within the channel (lock their execute lock) - * 3. Unlock channel tree and lock client locks - * 4. lock channel tree again and move the clients (No new clients should be joined because channel is flagged as deleted!) - * - * Note: channel cant be a ref because the channel itself gets deleted! - */ -ChannelDeleteResult ClientChannelService::delete_channel(std::shared_ptr channel, const std::shared_ptr &invoker, const std::string &kick_message, ts::rwshared_lock &tree_lock) { - if(auto err = tree_lock.auto_lock_exclusive(); err) - return ChannelDeleteResult::TREE_LOCK_FAILED; - - if(channel->deleted) - return ChannelDeleteResult::SUCCESS; - - std::deque> clients; - { - for(const auto& sub_channel : this->virtual_server_->server_channel_tree()->channels(channel)) { - auto s_channel = dynamic_pointer_cast(sub_channel); - assert(s_channel); - - auto chan_clients = this->virtual_server_->find_clients_by_channel(s_channel); - clients.insert(clients.end(), chan_clients.begin(), chan_clients.end()); - s_channel->deleted = true; - } - auto chan_clients = this->virtual_server_->find_clients_by_channel(channel); - clients.insert(clients.end(), chan_clients.begin(), chan_clients.end()); - channel->deleted = true; - } - auto default_channel = dynamic_pointer_cast(this->virtual_server_->server_channel_tree()->getDefaultChannel()); - assert(default_channel); - tree_lock.auto_unlock(); - - std::deque> command_locks; - for(const auto& client : clients) - command_locks.push_back(std::move(std::unique_lock(client->get_channel_lock()))); - - for(const auto& client : clients) { - auto result = this->client_move(client, default_channel, invoker, kick_message, ViewReasonId::VREASON_CHANNEL_KICK, true, tree_lock); - if(result != ClientMoveResult::SUCCESS) - return ChannelDeleteResult::CLIENT_MOVE_FAILED; - } - - if(auto err = tree_lock.auto_lock_exclusive(); err) - return ChannelDeleteResult::TREE_LOCK_FAILED; - command_locks.clear(); - - auto channel_ids = this->virtual_server_->server_channel_tree()->delete_channel_root(channel); - tree_lock.auto_lock_shared(); - - for(auto& client : this->virtual_server_->connected_clients(false)) { - std::unique_lock client_channel_lock(client->get_channel_lock()); - client->notifyChannelDeleted(client->channel_view()->delete_channel_root(channel), invoker); - } - return ChannelDeleteResult::SUCCESS; +// +// Created by WolverinDEV on 03/03/2020. +// + +#include +#include "./ClientChannelService.h" +#include "../client/ConnectedClient.h" +#include "../vserver/VirtualServer.h" +#include "../groups/GroupManager.h" +#include "../groups/GroupAssignmentManager.h" +#include +#include +#include + +using namespace ts::server::channels; + +ts::ServerId ClientChannelService::get_server_id() const { + return this->virtual_server_->server_id(); +} + +ChannelGroupInheritance ClientChannelService::calculate_channel_group(ChannelId channel_id, ClientDbId client_dbid, ClientType client_type) { + auto tree_lock = this->virtual_server_->lock_channel_clients(); + tree_lock.auto_lock_shared(); + + auto assignments = this->virtual_server_->group_manager()->assignments().channel_groups_of_client(groups::GroupAssignmentCalculateMode::GLOBAL, client_dbid); + + auto inheritance_channel = this->virtual_server_->server_channel_tree()->findChannel(channel_id); + while(inheritance_channel) { + auto inheritance_channel_id = inheritance_channel->channelId(); + auto assignment = std::find_if(assignments.begin(), assignments.end(), [&](const groups::ChannelGroupAssignment& assignment) { + return assignment.channel_id == inheritance_channel_id; + }); + + if(assignment == assignments.end() || !assignment->group_id) { + auto permission = inheritance_channel->permissions()->permission_value_flagged(permission::b_channel_group_inheritance_end); + if(permission::v2::permission_granted(1, permission)) + break; + + inheritance_channel = inheritance_channel->parent(); + } else { + return { + assignment->group_id, + inheritance_channel_id + }; + } + } + + auto default_group = this->virtual_server_->group_manager()->default_channel_group(client_type); + assert(default_group); + return { + default_group->group_id(), + channel_id + }; +} + +/* + * General notice: + * We're locking the clients command_lock, so when changing the currentChannel variable nothing gets messed up. + * As a little note: This must be done before locking the channel tree at all. + */ + +ClientMoveResult ClientChannelService::client_ban(const std::shared_ptr &target, + const std::shared_ptr &invoker, const std::string &reason, size_t time, + ts::rwshared_lock &tree_lock) { + tree_lock.auto_unlock(); + auto command_lock = target->lock_command_handling(); + if(auto err = tree_lock.auto_lock_exclusive(); err) + return ClientMoveResult::TREE_LOCK_FAILED; + + std::unique_lock client_chan_tree_lock{target->get_channel_lock()}; + auto old_channel = target->getChannel(); + target->setChannel(nullptr); + client_chan_tree_lock.unlock(); + + ChannelId old_channel_id{0}; + if(old_channel) { + old_channel_id = old_channel->channelId(); + for(const auto& client : this->virtual_server_->connected_clients(false)) { + if(!client || client == target) + continue; + + std::unique_lock client_channel_lock(client->get_channel_lock()); + if(client->isClientVisible(target, false)) + client->notifyClientLeftViewBanned(target, old_channel_id, invoker, time, false, reason); + } + + auto s_channel = dynamic_pointer_cast(old_channel); + assert(s_channel); + s_channel->unregister_client(target); + } + + target->notifyClientLeftViewBanned(target, old_channel_id, invoker, time, false, reason); + return ClientMoveResult::SUCCESS; +} + +ClientMoveResult ClientChannelService::client_kick(const std::shared_ptr &target, + const std::shared_ptr &invoker, const std::string &reason, + const std::shared_ptr &target_channel, + ts::rwshared_lock &tree_lock) { + if(target_channel) { + return this->client_move(target, target_channel, invoker, reason, ViewReasonId::VREASON_CHANNEL_KICK, true, tree_lock); + } else { + tree_lock.auto_unlock(); + auto command_lock = target->lock_command_handling(); + if(auto err = tree_lock.auto_lock_exclusive(); err) + return ClientMoveResult::TREE_LOCK_FAILED; + + std::unique_lock client_chan_tree_lock{target->get_channel_lock()}; + auto old_channel = target->getChannel(); + target->setChannel(nullptr); + client_chan_tree_lock.unlock(); + + ChannelId old_channel_id{0}; + if(old_channel) { + old_channel_id = old_channel->channelId(); + for(const auto& client : this->virtual_server_->connected_clients(false)) { + if(!client || client == target) + continue; + + std::unique_lock client_channel_lock(client->get_channel_lock()); + if(client->isClientVisible(target, false)) + client->notifyClientLeftViewKicked(target, old_channel_id, reason, invoker, false, nullptr); + } + + auto s_channel = dynamic_pointer_cast(old_channel); + assert(s_channel); + s_channel->unregister_client(target); + } + + target->notifyClientLeftViewKicked(target, old_channel_id, reason, invoker, false, nullptr); + } + + return ClientMoveResult::SUCCESS; +} + +ClientMoveResult ClientChannelService::client_move(const std::shared_ptr &target, + std::shared_ptr s_target_channel, + const std::shared_ptr &invoker, + const std::string &reason_message, + ViewReasonId reason_id, + bool notify_client, + ts::rwshared_lock &tree_lock) { + TIMING_START(timings); + tree_lock.auto_unlock(); + + auto command_lock = target->lock_command_handling(); + if(auto err = tree_lock.auto_lock_exclusive(); err) + return ClientMoveResult::TREE_LOCK_FAILED; + + TIMING_STEP(timings, "tree lock setup"); + if(target->getChannel() == s_target_channel) + return ClientMoveResult::SUCCESS; + + /* first step: resolve the target channel / or fix missing */ + auto s_source_channel = dynamic_pointer_cast(target->getChannel()); + assert(!target->getChannel() || s_source_channel != nullptr); + + auto server_channel_tree = this->virtual_server_->server_channel_tree(); + std::deque client_updates; + std::deque changed_groups{}; + if(s_target_channel) { + /* don't let the client join a deleted channel */ + if(s_target_channel->deleted) { + s_target_channel = dynamic_pointer_cast(server_channel_tree->getDefaultChannel()); + assert(s_target_channel); + } + + /* update the group properties here already, so for all enter views we could just send the new props directly */ + changed_groups = this->virtual_server_->group_manager()->assignments().update_client_group_properties(target, s_target_channel->channelId()); + client_updates.insert(client_updates.end(), changed_groups.begin(), changed_groups.end()); //TODO: Only update for clients which have no enter? + } + + auto l_target_channel = s_target_channel ? server_channel_tree->findLinkedChannel(s_target_channel->channelId()) : nullptr; + auto l_source_channel = s_source_channel ? server_channel_tree->findLinkedChannel(s_source_channel->channelId()) : nullptr; + TIMING_STEP(timings, "channel lookup "); + + + /* second step: show the target channel to the client if its not shown and let him subscibe to the channel */ + if(s_target_channel && notify_client) { + std::unique_lock client_channel_lock(target->get_channel_lock()); + + bool success = false; + /* TODO: Use a bunk here and not a notify for every single */ + for(const auto& channel : target->channel_view()->show_channel(l_target_channel, success)) + target->notifyChannelShow(channel->channel(), channel->previous_channel); + sassert(success); + if(!success) + return ClientMoveResult::VIEW_INSERT_FAILED; + + target->subscribeChannel({s_target_channel}, false, true); + } + TIMING_STEP(timings, "target show chan"); + + if(s_target_channel) { + for(const auto& client : this->virtual_server_->connected_clients(false)) { + if (!notify_client && client == target) continue; + + std::unique_lock client_channel_lock{client->get_channel_lock()}; + auto chan_target = client->channel_view()->find_channel(s_target_channel); + + if(chan_target) { + auto chan_source = client->channel_view()->find_channel(s_source_channel); + if(chan_source) { + if (chan_target->subscribed || client == target) { + if (client == target || client->isClientVisible(target, false)) { + client->notifyClientMoved(target, s_target_channel, reason_id, reason_message, invoker, false); + } else { + client->notifyClientEnterView(target, invoker, reason_message, s_target_channel, reason_id, s_source_channel, false); + } + } else if(client->isClientVisible(target, false)){ + //Client got out of view + client->notifyClientLeftView(target, s_target_channel, reason_id, reason_message.empty() ? std::string{"view left"} : reason_message, invoker, false); + } + } else { + if(client == target && client->getType() != ClientType::CLIENT_INTERNAL && client->getType() != ClientType::CLIENT_MUSIC) + logCritical(this->get_server_id(), "{} Client enters visibility twice!", CLIENT_STR_LOG_PREFIX_(client)); + + //Client entered view + if(chan_target->subscribed) + client->notifyClientEnterView(target, invoker, reason_message, s_target_channel, ViewReasonId::VREASON_USER_ACTION, nullptr, false); + } + } else { + /* target channel isn't visible => so client gone out of view */ + if(client == target && client->getType() != ClientType::CLIENT_INTERNAL && client->getType() != ClientType::CLIENT_MUSIC) + logCritical(this->get_server_id(), "{} Moving own client into a not visible channel! This shall not happen!", CLIENT_STR_LOG_PREFIX_(client)); + //Test for in view? (Notify already does but nvm) + + if(client->isClientVisible(target, false)){ + //Client got out of view + if(reason_id == ViewReasonId::VREASON_USER_ACTION) + client->notifyClientLeftView(target, nullptr, ViewReasonId::VREASON_SERVER_LEFT, reason_message.empty() ? "joined a hidden channel" : reason_message, invoker, false); + else + client->notifyClientLeftView(target, nullptr, ViewReasonId::VREASON_SERVER_LEFT, reason_message.empty() ? "moved to a hidden channel" : reason_message, invoker, false); + } + } + + } + + if(s_source_channel) + s_source_channel->unregister_client(target); + s_target_channel->register_client(target); + } else { + /* client left the server */ + if(target->getChannel()) { + for(const auto& client : this->virtual_server_->connected_clients(false)) { + if(!client || client == target) + continue; + + std::unique_lock client_channel_lock(client->get_channel_lock()); + if(client->isClientVisible(target, false)) + client->notifyClientLeftView(target, nullptr, reason_id, reason_message, invoker, false); + } + + s_source_channel->unregister_client(target); + } + } + TIMING_STEP(timings, "notify viewers "); + + target->setChannel(s_target_channel); + tree_lock.downgrade_lock(); + + std::unique_lock client_channel_lock(target->get_channel_lock()); + TIMING_STEP(timings, "lock own ch tree"); + + if (s_source_channel) { + s_source_channel->properties()[property::CHANNEL_LAST_LEFT] = std::chrono::duration_cast(std::chrono::system_clock::now().time_since_epoch()).count(); + + this->virtual_server_->group_manager()->assignments().cleanup_channel_temporary_assignment(target->getClientDatabaseId(), s_source_channel->channelId()); + auto update = target->properties()[property::CLIENT_IS_TALKER].as() || target->properties()[property::CLIENT_TALK_REQUEST].as() > 0; + + if(update) { + target->properties()[property::CLIENT_IS_TALKER] = 0; + target->properties()[property::CLIENT_TALK_REQUEST] = 0; + target->properties()[property::CLIENT_TALK_REQUEST_MSG] = ""; + client_updates.push_back(property::CLIENT_IS_TALKER); + client_updates.push_back(property::CLIENT_TALK_REQUEST); + client_updates.push_back(property::CLIENT_TALK_REQUEST_MSG); + } + TIMING_STEP(timings, "src chan up"); + } + + if (s_target_channel) { + if(target->update_cached_permissions()) /* update cached calculated permissions */ + target->sendNeededPermissions(false); + TIMING_STEP(timings, "perm gr upd"); + + if(s_source_channel) { + std::deque deleted; + for(const auto& channel : target->channel_view()->test_channel(l_source_channel, l_target_channel)) + deleted.push_back(channel->channelId()); + + if(!deleted.empty()) + target->notifyChannelHide(deleted, false); + + /* force unsubscribe from old channel */ + auto i_source_channel = s_source_channel->channelId(); + if(std::find(deleted.begin(), deleted.end(), i_source_channel) == deleted.end()) { + auto source_channel_sub_power = target->calculate_permission(permission::i_channel_subscribe_power, i_source_channel); + if(!s_source_channel->permission_granted(permission::i_channel_needed_subscribe_power, source_channel_sub_power, false)) { + auto source_channel_sub_power_ignore = target->calculate_permission(permission::b_channel_ignore_subscribe_power, i_source_channel); + if(!permission::v2::permission_granted(1, source_channel_sub_power_ignore, true)) { + logTrace(this->get_server_id(), "Force unsubscribing of client {} for channel {}/{}. (Channel switch and no permissions)", + CLIENT_STR_LOG_PREFIX_(target), s_source_channel->name(), + i_source_channel + ); + target->unsubscribeChannel({s_source_channel}, false); //Unsubscribe last channel (hasn't permissions) + } + } + } + TIMING_STEP(timings, "src hide ts"); + } + } + client_channel_lock.unlock(); + /* both methods lock if they require stuff */ + this->notifyClientPropertyUpdates(target, client_updates, s_source_channel ? true : false); + TIMING_STEP(timings, "notify cpro"); + if(s_target_channel) { + target->updateChannelClientProperties(false, s_source_channel ? true : false); + TIMING_STEP(timings, "notify_t_pr"); + } + debugMessage(this->get_server_id(), "{} Client move timings: {}", CLIENT_STR_LOG_PREFIX_(target), TIMING_FINISH(timings)); +} + +/* + * 1. flag channel as deleted (lock channel tree so no moves) + * 2. Gather all clients within the channel (lock their execute lock) + * 3. Unlock channel tree and lock client locks + * 4. lock channel tree again and move the clients (No new clients should be joined because channel is flagged as deleted!) + * + * Note: channel cant be a ref because the channel itself gets deleted! + */ +ChannelDeleteResult ClientChannelService::delete_channel(std::shared_ptr channel, const std::shared_ptr &invoker, const std::string &kick_message, ts::rwshared_lock &tree_lock) { + if(auto err = tree_lock.auto_lock_exclusive(); err) + return ChannelDeleteResult::TREE_LOCK_FAILED; + + if(channel->deleted) + return ChannelDeleteResult::SUCCESS; + + std::deque> clients; + { + for(const auto& sub_channel : this->virtual_server_->server_channel_tree()->channels(channel)) { + auto s_channel = dynamic_pointer_cast(sub_channel); + assert(s_channel); + + auto chan_clients = this->virtual_server_->find_clients_by_channel(s_channel); + clients.insert(clients.end(), chan_clients.begin(), chan_clients.end()); + s_channel->deleted = true; + } + auto chan_clients = this->virtual_server_->find_clients_by_channel(channel); + clients.insert(clients.end(), chan_clients.begin(), chan_clients.end()); + channel->deleted = true; + } + auto default_channel = dynamic_pointer_cast(this->virtual_server_->server_channel_tree()->getDefaultChannel()); + assert(default_channel); + tree_lock.auto_unlock(); + + std::deque> command_locks; + for(const auto& client : clients) + command_locks.push_back(std::move(std::unique_lock(client->get_channel_lock()))); + + for(const auto& client : clients) { + auto result = this->client_move(client, default_channel, invoker, kick_message, ViewReasonId::VREASON_CHANNEL_KICK, true, tree_lock); + if(result != ClientMoveResult::SUCCESS) + return ChannelDeleteResult::CLIENT_MOVE_FAILED; + } + + if(auto err = tree_lock.auto_lock_exclusive(); err) + return ChannelDeleteResult::TREE_LOCK_FAILED; + command_locks.clear(); + + auto channel_ids = this->virtual_server_->server_channel_tree()->delete_channel_root(channel); + tree_lock.auto_lock_shared(); + + for(auto& client : this->virtual_server_->connected_clients(false)) { + std::unique_lock client_channel_lock(client->get_channel_lock()); + client->notifyChannelDeleted(client->channel_view()->delete_channel_root(channel), invoker); + } + return ChannelDeleteResult::SUCCESS; } \ No newline at end of file diff --git a/server/src/services/ClientChannelService.h b/server/src/services/ClientChannelService.h index 5f1f161..00a0282 100644 --- a/server/src/services/ClientChannelService.h +++ b/server/src/services/ClientChannelService.h @@ -1,94 +1,94 @@ -#pragma once - -#include -#include -#include -#include -#include -#include - -namespace ts { - class ServerChannel; -} - -namespace ts::server { - class ConnectedClient; - - namespace vserver { - class VirtualServerBase; - } -} - -namespace ts::server::channels { - enum struct ClientMoveResult { - SUCCESS, - TREE_LOCK_FAILED, - VIEW_INSERT_FAILED - }; - - enum struct ChannelDeleteResult { - SUCCESS, - TREE_LOCK_FAILED, - CLIENT_MOVE_FAILED - }; - - struct ChannelGroupInheritance { - GroupId group_id{}; - ChannelId inherited_channel_id{}; - }; - - class ClientChannelService { - public: - explicit ClientChannelService(vserver::VirtualServerBase*); - ~ClientChannelService(); - - bool initialize(std::string& /* error */); - - /* channel group related stuff */ - /* this will lock the channel tree in read mode */ - [[nodiscard]] ChannelGroupInheritance calculate_channel_group(ChannelId /* channel id */, ClientDbId /* client db id */, ClientType /* fallback type */); - - /* - * ATTENTION: The methods client_ban, client_kick, client_move unlock the channel tree completely. - * All channel tree related variable must be checked again before using. - */ - /* Note: Use only this method to disconnect the client and notify everybody else that he has been banned! */ - /* remove a client from the channel tree because he's banned */ - [[nodiscard]] ClientMoveResult client_ban( - const std::shared_ptr& /* client */, - const std::shared_ptr& /* invoker */, - const std::string& /* reason */, - size_t /* length */, - ts::rwshared_lock& /* tree lock */); - - /* remove/move a client from/within the channel tree because he has been kicked */ - [[nodiscard]] ClientMoveResult client_kick( - const std::shared_ptr& /* client */, - const std::shared_ptr& /* invoker */, - const std::string& /* reason */, - const std::shared_ptr& /* target channel */, - ts::rwshared_lock& /* tree lock */ - ); - - /* move a client within the channel tree */ - [[nodiscard]] ClientMoveResult client_move( - const std::shared_ptr& /* client */, - std::shared_ptr /* target channel */, - const std::shared_ptr& /* invoker */, - const std::string& /* reason */, - ViewReasonId /* reason id */, - bool /* notify the client */, - ts::rwshared_lock& /* tree lock */ - ); - - [[nodiscard]] ChannelDeleteResult delete_channel( - std::shared_ptr /* target channel */, - const std::shared_ptr& /* invoker */, - const std::string& /* kick message */, - ts::rwshared_lock& /* tree lock */ - ); - private: - vserver::VirtualServerBase* virtual_server_{nullptr}; - [[nodiscard]] ServerId get_server_id() const; - }; +#pragma once + +#include +#include +#include +#include +#include +#include + +namespace ts { + class ServerChannel; +} + +namespace ts::server { + class ConnectedClient; + + namespace vserver { + class VirtualServerBase; + } +} + +namespace ts::server::channels { + enum struct ClientMoveResult { + SUCCESS, + TREE_LOCK_FAILED, + VIEW_INSERT_FAILED + }; + + enum struct ChannelDeleteResult { + SUCCESS, + TREE_LOCK_FAILED, + CLIENT_MOVE_FAILED + }; + + struct ChannelGroupInheritance { + GroupId group_id{}; + ChannelId inherited_channel_id{}; + }; + + class ClientChannelService { + public: + explicit ClientChannelService(vserver::VirtualServerBase*); + ~ClientChannelService(); + + bool initialize(std::string& /* error */); + + /* channel group related stuff */ + /* this will lock the channel tree in read mode */ + [[nodiscard]] ChannelGroupInheritance calculate_channel_group(ChannelId /* channel id */, ClientDbId /* client db id */, ClientType /* fallback type */); + + /* + * ATTENTION: The methods client_ban, client_kick, client_move unlock the channel tree completely. + * All channel tree related variable must be checked again before using. + */ + /* Note: Use only this method to disconnect the client and notify everybody else that he has been banned! */ + /* remove a client from the channel tree because he's banned */ + [[nodiscard]] ClientMoveResult client_ban( + const std::shared_ptr& /* client */, + const std::shared_ptr& /* invoker */, + const std::string& /* reason */, + size_t /* length */, + ts::rwshared_lock& /* tree lock */); + + /* remove/move a client from/within the channel tree because he has been kicked */ + [[nodiscard]] ClientMoveResult client_kick( + const std::shared_ptr& /* client */, + const std::shared_ptr& /* invoker */, + const std::string& /* reason */, + const std::shared_ptr& /* target channel */, + ts::rwshared_lock& /* tree lock */ + ); + + /* move a client within the channel tree */ + [[nodiscard]] ClientMoveResult client_move( + const std::shared_ptr& /* client */, + std::shared_ptr /* target channel */, + const std::shared_ptr& /* invoker */, + const std::string& /* reason */, + ViewReasonId /* reason id */, + bool /* notify the client */, + ts::rwshared_lock& /* tree lock */ + ); + + [[nodiscard]] ChannelDeleteResult delete_channel( + std::shared_ptr /* target channel */, + const std::shared_ptr& /* invoker */, + const std::string& /* kick message */, + ts::rwshared_lock& /* tree lock */ + ); + private: + vserver::VirtualServerBase* virtual_server_{nullptr}; + [[nodiscard]] ServerId get_server_id() const; + }; } \ No newline at end of file diff --git a/server/src/services/PermissionsService.cpp b/server/src/services/PermissionsService.cpp index 9cdcd48..1095efe 100644 --- a/server/src/services/PermissionsService.cpp +++ b/server/src/services/PermissionsService.cpp @@ -1,319 +1,319 @@ -// -// Created by WolverinDEV on 03/03/2020. -// - -#include -#include "./PermissionsService.h" -#include "../InstanceHandler.h" -#include "../vserver/VirtualServerBase.h" -#include "../groups/Group.h" -#include "../groups/GroupManager.h" -#include "../groups/GroupAssignmentManager.h" - -using namespace ts::permission; -using namespace ts::permission::v2; -using namespace ts::server::permissions; - -PermissionService::PermissionService(ts::server::vserver::VirtualServerBase *handle) : virtual_server_{handle} {} - -bool PermissionService::initialize(std::string &) { return true; } - -bool PermissionService::load_data(std::string &) { return true; } -void PermissionService::unload_data() {} - -ts::ServerId PermissionService::get_server_id() const { - return this->virtual_server_->server_id(); -} - -PermissionFlaggedValue PermissionService::calculate_client_permission( - PermissionType permission, - ClientDbId cldbid, - ClientType type, - ChannelId channel, - bool granted, - std::shared_ptr cache) { - auto result = this->calculate_client_permissions({permission}, cldbid, type, channel, granted, std::move(cache)); - if(result.empty()) return {0, false}; - - return result.front().second; -} - - -std::vector> PermissionService::calculate_client_permissions( - const std::deque& permissions, - ClientDbId client_dbid, - ClientType client_type, - ChannelId channel_id, - bool calculate_granted, - std::shared_ptr cache) { - if(permissions.empty()) return {}; - - std::vector> result{}; - result.reserve(permissions.size()); - - if(cache->client_database_id && cache->client_database_id != client_dbid) - cache = nullptr; - - if(!cache) - cache = std::make_shared(); - cache->client_type = client_type; - cache->client_database_id = client_dbid; - - if(!cache->client_permissions) - cache->client_permissions = serverInstance->databaseHelper()->loadClientPermissionManager(this->virtual_server_->server_ref(), client_dbid); - - bool have_skip_permission = false; - int skip_permission_type = -1; /* -1 := unset | 0 := skip, not explicit | 1 := skip, explicit */ - - bool have_skip; - - /* - * server_group_data[0] := Server group id - * server_group_data[1] := Skip flag - * server_group_data[2] := Negate flag - * server_group_data[3] := Permission value - */ - typedef std::tuple GroupData; - bool server_group_data_initialized = false; - std::vector server_group_data; - GroupData* active_server_group; - - /* function to calculate skip permission */ - auto calculate_skip = [&]{ - skip_permission_type = 0; - /* test for skip permission within the client permission manager */ - { - auto skip_value = cache->client_permissions->permission_value_flagged(permission::b_client_skip_channelgroup_permissions); - if(skip_value.has_value) { - have_skip_permission = skip_value.value == 1; - skip_permission_type = 1; - logTrace(this->get_server_id(), "[Permission] Found skip permission in client permissions. Value: {}", have_skip_permission); - } - } - /* test for skip permission within all server groups */ - if(skip_permission_type != 1) { - this->load_group_assignments(cache); - assert(cache->assignment_server_groups_set); - for(const auto& assignment : cache->assignment_server_groups) { - auto group_permissions = assignment->permissions(); - auto flagged_value = group_permissions->permission_value_flagged(permission::b_client_skip_channelgroup_permissions); - if(flagged_value.has_value) { - have_skip_permission |= flagged_value.value == 1; - if(have_skip_permission) { - logTrace(this->get_server_id(), "[Permission] Found skip permission in client server group. Group: {} ({}), Value: {}", assignment->group_id(), assignment->display_name(), have_skip_permission); - break; - } - } - } - } - }; - - auto initialize_group_data = [&](const permission::PermissionType& permission_type) { - server_group_data_initialized = true; - active_server_group = nullptr; - - this->load_group_assignments(cache); - assert(cache->assignment_server_groups_set); - - server_group_data.resize(cache->assignment_server_groups.size()); - auto it = server_group_data.begin(); - for(auto& group : cache->assignment_server_groups) { - auto group_permissions = group->permissions(); - auto permission_flags = group_permissions->permission_flags(permission_type); - - auto flag_set = calculate_granted ? permission_flags.grant_set : permission_flags.value_set; - if(!flag_set) - continue; - - //TODO: Test if there is may a group channel permissions - auto value = group_permissions->permission_values(permission_type); - *it = std::make_tuple(group->group_id(), (bool) permission_flags.skip, (bool) permission_flags.negate, calculate_granted ? value.grant : value.value); - it++; - } - if(it == server_group_data.begin()) - return; /* no server group has that permission */ - - server_group_data.erase(it, server_group_data.end()); /* remove unneeded */ - - auto found_negate = false; - for(auto& group : server_group_data) { - if(std::get<2>(group)) { - found_negate = true; - break; - } - } - - if(found_negate) { - server_group_data.erase(remove_if(server_group_data.begin(), server_group_data.end(), [](auto data) { return !std::get<2>(data); }), server_group_data.end()); - logTrace(this->get_server_id(), "[Permission] Found negate flag within server groups. Groups left: {}", server_group_data.size()); - if(server_group_data.empty()) - logTrace(this->get_server_id(), "[Permission] After non negated groups have been kicked out the negated groups are empty! This should not happen! Permission: {}, Client ID: {}", permission_type, client_dbid); - permission::PermissionValue current_lowest = 0; - for(auto& group : server_group_data) { - if(!active_server_group || (std::get<3>(group) < current_lowest && std::get<3>(group) != -1)) { - current_lowest = std::get<3>(group); - active_server_group = &group; - } - } - } else { - permission::PermissionValue current_highest = 0; - for(auto& group : server_group_data) { - if(!active_server_group || (std::get<3>(group) > current_highest || std::get<3>(group) == -1)) { - current_highest = std::get<3>(group); - active_server_group = &group; - } - } - } - }; - - for(const auto& permission : permissions) { - if(permission == permission::b_client_skip_channelgroup_permissions) { - if(skip_permission_type == -1) /* initialize skip flag */ - calculate_skip(); - result.push_back({permission, {have_skip_permission, skip_permission_type == 1}}); - continue; - } - - server_group_data_initialized = false; /* reset all group data */ - auto client_permission_flags = cache->client_permissions->permission_flags(permission); - /* lets try to resolve the channel specific permission */ - if(channel_id > 0 && client_permission_flags.channel_specific) { - auto data = cache->client_permissions->channel_permission(permission, channel_id); - if(calculate_granted ? data.flags.grant_set : data.flags.value_set) { - result.push_back({permission, {calculate_granted ? data.values.grant : data.values.value, true}}); - logTrace(this->get_server_id(), "[Permission] Calculation for client {} of permission {} returned {} (Client channel permission)", client_dbid, permission::resolvePermissionData(permission)->name, data.values.value); - continue; - } - } - - - have_skip = channel_id == 0; - if(!have_skip) { - /* look if somewhere is the skip permission flag set */ - if(skip_permission_type == -1) /* initialize skip flag */ - calculate_skip(); - have_skip = have_skip_permission; - } - if(!have_skip) { - /* okey we've no global skip. Then now lookup the groups and the client permissions */ - if(calculate_granted ? client_permission_flags.grant_set : client_permission_flags.value_set) { - /* okey the client has the permission, this counts */ - have_skip = client_permission_flags.skip; - } else { - if(!server_group_data_initialized) - initialize_group_data(permission); - if(active_server_group) - have_skip = std::get<1>(*active_server_group); - } - } - - if(!have_skip) { - /* lookup the channel group */ - { - this->load_channel_assignment(cache, channel_id); - assert(cache->assignment_channel_group_channel == channel_id); - - auto channel_assignment = cache->assignment_channel_group; - if(channel_assignment) { - auto group_permissions = channel_assignment->permissions(); - auto permission_flags = group_permissions->permission_flags(permission); - - auto flag_set = calculate_granted ? permission_flags.grant_set : permission_flags.value_set; - if(flag_set) { - auto value = group_permissions->permission_values(permission); - result.push_back({permission, {calculate_granted ? value.grant : value.value, true}}); - logTrace(this->get_server_id(), "[Permission] Calculation for client {} of permission {} returned {} (Channel group permission)", client_dbid, permission::resolvePermissionData(permission)->name, calculate_granted ? value.grant : value.value); - continue; - } - } - } - - /* lookup the channel permissions. Whyever? */ - { - this->load_server_channel(cache, channel_id); - if(cache->server_channel) { - auto channel_permissions = cache->server_channel->permissions(); - auto data = calculate_granted ? channel_permissions->permission_granted_flagged(permission) : channel_permissions->permission_value_flagged(permission); - if(data.has_value) { - result.push_back({permission, {data.value, true}}); - logTrace(this->get_server_id(), "[Permission] Calculation for client {} of permission {} returned {} (Channel permission)", client_dbid, permission::resolvePermissionData(permission)->name, data.value); - continue; - } - } - } - } - - if(calculate_granted ? client_permission_flags.grant_set : client_permission_flags.value_set) { - auto client_value = cache->client_permissions->permission_values(permission); - result.push_back({permission, {calculate_granted ? client_value.grant : client_value.value, true}}); - logTrace(this->get_server_id(), "[Permission] Calculation for client {} of permission {} returned {} (Client permission)", client_dbid, permission::resolvePermissionData(permission)->name, client_value.value); - continue; - } - - if(!server_group_data_initialized) - initialize_group_data(permission); - if(active_server_group) { - result.push_back({permission, {get<3>(*active_server_group), true}}); - logTrace(this->get_server_id(), "[Permission] Calculation for client {} of permission {} returned {} (Server group permission of group {})", client_dbid, permission::resolvePermissionData(permission)->name, get<3>(*active_server_group), get<0>(*active_server_group)); - continue; - } - - logTrace(this->get_server_id(), "[Permission] Calculation for client {} of permission {} returned in no permission.", client_dbid, permission::resolvePermissionData(permission)->name); - result.push_back({permission, {permNotGranted, false}}); - } - - return result; -} - -void PermissionService::load_group_assignments(const std::shared_ptr &cache) { - if(cache->assignment_server_groups_set) return; - cache->assignment_server_groups_set = true; - - auto group_manager = this->virtual_server_->group_manager(); - auto assignments = group_manager->assignments().server_groups_of_client(groups::GroupAssignmentCalculateMode::GLOBAL, cache->client_database_id); - cache->assignment_server_groups.reserve(assignments.size()); - - for(auto& entry : assignments) { - auto group = group_manager->find_server_group(entry); - if(!group) continue; - - cache->assignment_server_groups.push_back(group); - } - - if(cache->assignment_server_groups.empty()) - cache->assignment_server_groups.push_back(group_manager->default_server_group(cache->client_type)); -} - -void PermissionService::load_channel_assignment(const std::shared_ptr &cache, ts::ChannelId channel_id) { - if(cache->assignment_channel_group_channel == channel_id) return; - cache->assignment_channel_group_channel = channel_id; - - auto group_manager = this->virtual_server_->group_manager(); - - auto channel_groups = group_manager->assignments().channel_groups_of_client(groups::GroupAssignmentCalculateMode::GLOBAL, cache->client_database_id); - auto it = std::find_if(channel_groups.begin(), channel_groups.end(), [&](const groups::ChannelGroupAssignment& assignment) { - return assignment.channel_id == channel_id; - }); - - GroupId group_id{0}; - if(it != channel_groups.end()) - group_id = it->group_id; - - if(!group_id) { - auto assignment = this->virtual_server_->channel_service().calculate_channel_group(channel_id, cache->client_database_id, cache->client_type); - group_id = assignment.group_id; - } - - cache->assignment_channel_group = group_manager->find_channel_group(group_id); - if(!cache->assignment_channel_group) - cache->assignment_channel_group = group_manager->default_channel_group(cache->client_type); -} - -void PermissionService::load_server_channel(const std::shared_ptr &cache, ts::ChannelId channel_id) { - if(cache->last_server_channel == channel_id) return; - cache->last_server_channel = channel_id; - - auto channel_tree = this->virtual_server_->server_channel_tree(); - auto tree_lock = this->virtual_server_->lock_channel_clients(); - cache->server_channel = channel_tree->findChannel(channel_id); +// +// Created by WolverinDEV on 03/03/2020. +// + +#include +#include "./PermissionsService.h" +#include "../InstanceHandler.h" +#include "../vserver/VirtualServerBase.h" +#include "../groups/Group.h" +#include "../groups/GroupManager.h" +#include "../groups/GroupAssignmentManager.h" + +using namespace ts::permission; +using namespace ts::permission::v2; +using namespace ts::server::permissions; + +PermissionService::PermissionService(ts::server::vserver::VirtualServerBase *handle) : virtual_server_{handle} {} + +bool PermissionService::initialize(std::string &) { return true; } + +bool PermissionService::load_data(std::string &) { return true; } +void PermissionService::unload_data() {} + +ts::ServerId PermissionService::get_server_id() const { + return this->virtual_server_->server_id(); +} + +PermissionFlaggedValue PermissionService::calculate_client_permission( + PermissionType permission, + ClientDbId cldbid, + ClientType type, + ChannelId channel, + bool granted, + std::shared_ptr cache) { + auto result = this->calculate_client_permissions({permission}, cldbid, type, channel, granted, std::move(cache)); + if(result.empty()) return {0, false}; + + return result.front().second; +} + + +std::vector> PermissionService::calculate_client_permissions( + const std::deque& permissions, + ClientDbId client_dbid, + ClientType client_type, + ChannelId channel_id, + bool calculate_granted, + std::shared_ptr cache) { + if(permissions.empty()) return {}; + + std::vector> result{}; + result.reserve(permissions.size()); + + if(cache->client_database_id && cache->client_database_id != client_dbid) + cache = nullptr; + + if(!cache) + cache = std::make_shared(); + cache->client_type = client_type; + cache->client_database_id = client_dbid; + + if(!cache->client_permissions) + cache->client_permissions = serverInstance->databaseHelper()->loadClientPermissionManager(this->virtual_server_->server_ref(), client_dbid); + + bool have_skip_permission = false; + int skip_permission_type = -1; /* -1 := unset | 0 := skip, not explicit | 1 := skip, explicit */ + + bool have_skip; + + /* + * server_group_data[0] := Server group id + * server_group_data[1] := Skip flag + * server_group_data[2] := Negate flag + * server_group_data[3] := Permission value + */ + typedef std::tuple GroupData; + bool server_group_data_initialized = false; + std::vector server_group_data; + GroupData* active_server_group; + + /* function to calculate skip permission */ + auto calculate_skip = [&]{ + skip_permission_type = 0; + /* test for skip permission within the client permission manager */ + { + auto skip_value = cache->client_permissions->permission_value_flagged(permission::b_client_skip_channelgroup_permissions); + if(skip_value.has_value) { + have_skip_permission = skip_value.value == 1; + skip_permission_type = 1; + logTrace(this->get_server_id(), "[Permission] Found skip permission in client permissions. Value: {}", have_skip_permission); + } + } + /* test for skip permission within all server groups */ + if(skip_permission_type != 1) { + this->load_group_assignments(cache); + assert(cache->assignment_server_groups_set); + for(const auto& assignment : cache->assignment_server_groups) { + auto group_permissions = assignment->permissions(); + auto flagged_value = group_permissions->permission_value_flagged(permission::b_client_skip_channelgroup_permissions); + if(flagged_value.has_value) { + have_skip_permission |= flagged_value.value == 1; + if(have_skip_permission) { + logTrace(this->get_server_id(), "[Permission] Found skip permission in client server group. Group: {} ({}), Value: {}", assignment->group_id(), assignment->display_name(), have_skip_permission); + break; + } + } + } + } + }; + + auto initialize_group_data = [&](const permission::PermissionType& permission_type) { + server_group_data_initialized = true; + active_server_group = nullptr; + + this->load_group_assignments(cache); + assert(cache->assignment_server_groups_set); + + server_group_data.resize(cache->assignment_server_groups.size()); + auto it = server_group_data.begin(); + for(auto& group : cache->assignment_server_groups) { + auto group_permissions = group->permissions(); + auto permission_flags = group_permissions->permission_flags(permission_type); + + auto flag_set = calculate_granted ? permission_flags.grant_set : permission_flags.value_set; + if(!flag_set) + continue; + + //TODO: Test if there is may a group channel permissions + auto value = group_permissions->permission_values(permission_type); + *it = std::make_tuple(group->group_id(), (bool) permission_flags.skip, (bool) permission_flags.negate, calculate_granted ? value.grant : value.value); + it++; + } + if(it == server_group_data.begin()) + return; /* no server group has that permission */ + + server_group_data.erase(it, server_group_data.end()); /* remove unneeded */ + + auto found_negate = false; + for(auto& group : server_group_data) { + if(std::get<2>(group)) { + found_negate = true; + break; + } + } + + if(found_negate) { + server_group_data.erase(remove_if(server_group_data.begin(), server_group_data.end(), [](auto data) { return !std::get<2>(data); }), server_group_data.end()); + logTrace(this->get_server_id(), "[Permission] Found negate flag within server groups. Groups left: {}", server_group_data.size()); + if(server_group_data.empty()) + logTrace(this->get_server_id(), "[Permission] After non negated groups have been kicked out the negated groups are empty! This should not happen! Permission: {}, Client ID: {}", permission_type, client_dbid); + permission::PermissionValue current_lowest = 0; + for(auto& group : server_group_data) { + if(!active_server_group || (std::get<3>(group) < current_lowest && std::get<3>(group) != -1)) { + current_lowest = std::get<3>(group); + active_server_group = &group; + } + } + } else { + permission::PermissionValue current_highest = 0; + for(auto& group : server_group_data) { + if(!active_server_group || (std::get<3>(group) > current_highest || std::get<3>(group) == -1)) { + current_highest = std::get<3>(group); + active_server_group = &group; + } + } + } + }; + + for(const auto& permission : permissions) { + if(permission == permission::b_client_skip_channelgroup_permissions) { + if(skip_permission_type == -1) /* initialize skip flag */ + calculate_skip(); + result.push_back({permission, {have_skip_permission, skip_permission_type == 1}}); + continue; + } + + server_group_data_initialized = false; /* reset all group data */ + auto client_permission_flags = cache->client_permissions->permission_flags(permission); + /* lets try to resolve the channel specific permission */ + if(channel_id > 0 && client_permission_flags.channel_specific) { + auto data = cache->client_permissions->channel_permission(permission, channel_id); + if(calculate_granted ? data.flags.grant_set : data.flags.value_set) { + result.push_back({permission, {calculate_granted ? data.values.grant : data.values.value, true}}); + logTrace(this->get_server_id(), "[Permission] Calculation for client {} of permission {} returned {} (Client channel permission)", client_dbid, permission::resolvePermissionData(permission)->name, data.values.value); + continue; + } + } + + + have_skip = channel_id == 0; + if(!have_skip) { + /* look if somewhere is the skip permission flag set */ + if(skip_permission_type == -1) /* initialize skip flag */ + calculate_skip(); + have_skip = have_skip_permission; + } + if(!have_skip) { + /* okey we've no global skip. Then now lookup the groups and the client permissions */ + if(calculate_granted ? client_permission_flags.grant_set : client_permission_flags.value_set) { + /* okey the client has the permission, this counts */ + have_skip = client_permission_flags.skip; + } else { + if(!server_group_data_initialized) + initialize_group_data(permission); + if(active_server_group) + have_skip = std::get<1>(*active_server_group); + } + } + + if(!have_skip) { + /* lookup the channel group */ + { + this->load_channel_assignment(cache, channel_id); + assert(cache->assignment_channel_group_channel == channel_id); + + auto channel_assignment = cache->assignment_channel_group; + if(channel_assignment) { + auto group_permissions = channel_assignment->permissions(); + auto permission_flags = group_permissions->permission_flags(permission); + + auto flag_set = calculate_granted ? permission_flags.grant_set : permission_flags.value_set; + if(flag_set) { + auto value = group_permissions->permission_values(permission); + result.push_back({permission, {calculate_granted ? value.grant : value.value, true}}); + logTrace(this->get_server_id(), "[Permission] Calculation for client {} of permission {} returned {} (Channel group permission)", client_dbid, permission::resolvePermissionData(permission)->name, calculate_granted ? value.grant : value.value); + continue; + } + } + } + + /* lookup the channel permissions. Whyever? */ + { + this->load_server_channel(cache, channel_id); + if(cache->server_channel) { + auto channel_permissions = cache->server_channel->permissions(); + auto data = calculate_granted ? channel_permissions->permission_granted_flagged(permission) : channel_permissions->permission_value_flagged(permission); + if(data.has_value) { + result.push_back({permission, {data.value, true}}); + logTrace(this->get_server_id(), "[Permission] Calculation for client {} of permission {} returned {} (Channel permission)", client_dbid, permission::resolvePermissionData(permission)->name, data.value); + continue; + } + } + } + } + + if(calculate_granted ? client_permission_flags.grant_set : client_permission_flags.value_set) { + auto client_value = cache->client_permissions->permission_values(permission); + result.push_back({permission, {calculate_granted ? client_value.grant : client_value.value, true}}); + logTrace(this->get_server_id(), "[Permission] Calculation for client {} of permission {} returned {} (Client permission)", client_dbid, permission::resolvePermissionData(permission)->name, client_value.value); + continue; + } + + if(!server_group_data_initialized) + initialize_group_data(permission); + if(active_server_group) { + result.push_back({permission, {get<3>(*active_server_group), true}}); + logTrace(this->get_server_id(), "[Permission] Calculation for client {} of permission {} returned {} (Server group permission of group {})", client_dbid, permission::resolvePermissionData(permission)->name, get<3>(*active_server_group), get<0>(*active_server_group)); + continue; + } + + logTrace(this->get_server_id(), "[Permission] Calculation for client {} of permission {} returned in no permission.", client_dbid, permission::resolvePermissionData(permission)->name); + result.push_back({permission, {permNotGranted, false}}); + } + + return result; +} + +void PermissionService::load_group_assignments(const std::shared_ptr &cache) { + if(cache->assignment_server_groups_set) return; + cache->assignment_server_groups_set = true; + + auto group_manager = this->virtual_server_->group_manager(); + auto assignments = group_manager->assignments().server_groups_of_client(groups::GroupAssignmentCalculateMode::GLOBAL, cache->client_database_id); + cache->assignment_server_groups.reserve(assignments.size()); + + for(auto& entry : assignments) { + auto group = group_manager->find_server_group(entry); + if(!group) continue; + + cache->assignment_server_groups.push_back(group); + } + + if(cache->assignment_server_groups.empty()) + cache->assignment_server_groups.push_back(group_manager->default_server_group(cache->client_type)); +} + +void PermissionService::load_channel_assignment(const std::shared_ptr &cache, ts::ChannelId channel_id) { + if(cache->assignment_channel_group_channel == channel_id) return; + cache->assignment_channel_group_channel = channel_id; + + auto group_manager = this->virtual_server_->group_manager(); + + auto channel_groups = group_manager->assignments().channel_groups_of_client(groups::GroupAssignmentCalculateMode::GLOBAL, cache->client_database_id); + auto it = std::find_if(channel_groups.begin(), channel_groups.end(), [&](const groups::ChannelGroupAssignment& assignment) { + return assignment.channel_id == channel_id; + }); + + GroupId group_id{0}; + if(it != channel_groups.end()) + group_id = it->group_id; + + if(!group_id) { + auto assignment = this->virtual_server_->channel_service().calculate_channel_group(channel_id, cache->client_database_id, cache->client_type); + group_id = assignment.group_id; + } + + cache->assignment_channel_group = group_manager->find_channel_group(group_id); + if(!cache->assignment_channel_group) + cache->assignment_channel_group = group_manager->default_channel_group(cache->client_type); +} + +void PermissionService::load_server_channel(const std::shared_ptr &cache, ts::ChannelId channel_id) { + if(cache->last_server_channel == channel_id) return; + cache->last_server_channel = channel_id; + + auto channel_tree = this->virtual_server_->server_channel_tree(); + auto tree_lock = this->virtual_server_->lock_channel_clients(); + cache->server_channel = channel_tree->findChannel(channel_id); } \ No newline at end of file diff --git a/server/src/services/PermissionsService.h b/server/src/services/PermissionsService.h index eab5829..3270849 100644 --- a/server/src/services/PermissionsService.h +++ b/server/src/services/PermissionsService.h @@ -1,68 +1,68 @@ -#pragma once - -#include -#include - -namespace ts::server { - namespace groups { - class ServerGroup; - class ChannelGroup; - } - namespace vserver { - class VirtualServerBase; - } -} - -namespace ts::server::permissions { - struct CalculateCache { - ClientDbId client_database_id{0}; - ClientType client_type{ClientType::UNKNOWN}; - - std::shared_ptr client_permissions; - std::vector> assignment_server_groups; - bool assignment_server_groups_set{false}; - - ChannelId assignment_channel_group_channel{0}; - std::shared_ptr assignment_channel_group{}; - - std::shared_ptr server_channel; - ChannelId last_server_channel{0}; - }; - - class PermissionService { - public: - explicit PermissionService(vserver::VirtualServerBase*); - ~PermissionService() = default; - - bool initialize(std::string& /* error */); - - bool load_data(std::string& /* error */); - void unload_data(); - - permission::v2::PermissionFlaggedValue calculate_client_permission( - permission::PermissionType, - ClientDbId, - ClientType type, - ChannelId channel, - bool granted, - std::shared_ptr cache{nullptr} - ); - - std::vector> calculate_client_permissions( - const std::deque&, - ClientDbId, - ClientType type, - ChannelId channel, - bool granted, - std::shared_ptr cache{nullptr} - ); - private: - vserver::VirtualServerBase* virtual_server_{nullptr}; - - [[nodiscard]] ServerId get_server_id() const; - - void load_group_assignments(const std::shared_ptr&); - void load_channel_assignment(const std::shared_ptr&, ChannelId /* channel id */); - void load_server_channel(const std::shared_ptr&, ChannelId /* channel id */); - }; +#pragma once + +#include +#include + +namespace ts::server { + namespace groups { + class ServerGroup; + class ChannelGroup; + } + namespace vserver { + class VirtualServerBase; + } +} + +namespace ts::server::permissions { + struct CalculateCache { + ClientDbId client_database_id{0}; + ClientType client_type{ClientType::UNKNOWN}; + + std::shared_ptr client_permissions; + std::vector> assignment_server_groups; + bool assignment_server_groups_set{false}; + + ChannelId assignment_channel_group_channel{0}; + std::shared_ptr assignment_channel_group{}; + + std::shared_ptr server_channel; + ChannelId last_server_channel{0}; + }; + + class PermissionService { + public: + explicit PermissionService(vserver::VirtualServerBase*); + ~PermissionService() = default; + + bool initialize(std::string& /* error */); + + bool load_data(std::string& /* error */); + void unload_data(); + + permission::v2::PermissionFlaggedValue calculate_client_permission( + permission::PermissionType, + ClientDbId, + ClientType type, + ChannelId channel, + bool granted, + std::shared_ptr cache{nullptr} + ); + + std::vector> calculate_client_permissions( + const std::deque&, + ClientDbId, + ClientType type, + ChannelId channel, + bool granted, + std::shared_ptr cache{nullptr} + ); + private: + vserver::VirtualServerBase* virtual_server_{nullptr}; + + [[nodiscard]] ServerId get_server_id() const; + + void load_group_assignments(const std::shared_ptr&); + void load_channel_assignment(const std::shared_ptr&, ChannelId /* channel id */); + void load_server_channel(const std::shared_ptr&, ChannelId /* channel id */); + }; } \ No newline at end of file diff --git a/server/src/vserver/DefaultServer.cpp b/server/src/vserver/DefaultServer.cpp index 1570927..7b755f2 100644 --- a/server/src/vserver/DefaultServer.cpp +++ b/server/src/vserver/DefaultServer.cpp @@ -1,33 +1,33 @@ -// -// Created by WolverinDEV on 03/03/2020. -// - -#include -#include "DefaultServer.h" - -using namespace ts::server::vserver; - -VirtualServerStartResult DefaultServer::start_server_virtual_(ts::rwshared_lock &lock, std::string &error) { - if(!lock.auto_lock_exclusive()) { - error = "failed to lock server state"; - return VirtualServerStartResult::CUSTOM; - } - - if(auto result = DefaultServer::start_server_virtual_(lock, error); result != VirtualServerStartResult::SUCCESS) - return result; - - //TODO: Load anything - - this->status_ = VirtualServerStatus::VIRTUAL; - return VirtualServerStartResult::SUCCESS; -} - -VirtualServerStartResult DefaultServer::start_server_(ts::rwshared_lock &lock, std::string &string) { - return VirtualServerStartResult::SERVER_IS_DEFAULT; -} - -void DefaultServer::stop_server_(ts::rwshared_lock &lock) { - VirtualServerBase::stop_server_(lock); - - /* TODO: Save server states? Because we can't really shut down. */ -} +// +// Created by WolverinDEV on 03/03/2020. +// + +#include +#include "DefaultServer.h" + +using namespace ts::server::vserver; + +VirtualServerStartResult DefaultServer::start_server_virtual_(ts::rwshared_lock &lock, std::string &error) { + if(!lock.auto_lock_exclusive()) { + error = "failed to lock server state"; + return VirtualServerStartResult::CUSTOM; + } + + if(auto result = DefaultServer::start_server_virtual_(lock, error); result != VirtualServerStartResult::SUCCESS) + return result; + + //TODO: Load anything + + this->status_ = VirtualServerStatus::VIRTUAL; + return VirtualServerStartResult::SUCCESS; +} + +VirtualServerStartResult DefaultServer::start_server_(ts::rwshared_lock &lock, std::string &string) { + return VirtualServerStartResult::SERVER_IS_DEFAULT; +} + +void DefaultServer::stop_server_(ts::rwshared_lock &lock) { + VirtualServerBase::stop_server_(lock); + + /* TODO: Save server states? Because we can't really shut down. */ +} diff --git a/server/src/vserver/DefaultServer.h b/server/src/vserver/DefaultServer.h index dfc368c..890ce52 100644 --- a/server/src/vserver/DefaultServer.h +++ b/server/src/vserver/DefaultServer.h @@ -1,15 +1,15 @@ -#pragma once - -#include "./VirtualServerBase.h" - -namespace ts::server::vserver { - class DefaultServer : public VirtualServerBase { - public: - - private: - protected: - VirtualServerStartResult start_server_virtual_(ts::rwshared_lock &lock, std::string &string) override; - VirtualServerStartResult start_server_(ts::rwshared_lock &lock, std::string &string) override; - void stop_server_(ts::rwshared_lock &lock) override; - }; +#pragma once + +#include "./VirtualServerBase.h" + +namespace ts::server::vserver { + class DefaultServer : public VirtualServerBase { + public: + + private: + protected: + VirtualServerStartResult start_server_virtual_(ts::rwshared_lock &lock, std::string &string) override; + VirtualServerStartResult start_server_(ts::rwshared_lock &lock, std::string &string) override; + void stop_server_(ts::rwshared_lock &lock) override; + }; } \ No newline at end of file diff --git a/server/src/vserver/VirtualServer.cpp b/server/src/vserver/VirtualServer.cpp index dc49ffb..1b747f2 100644 --- a/server/src/vserver/VirtualServer.cpp +++ b/server/src/vserver/VirtualServer.cpp @@ -1,26 +1,26 @@ -// -// Created by WolverinDEV on 03/03/2020. -// - -#include "VirtualServer.h" - -using namespace ts::server::vserver; - -//This method gets called when server is OFFLINE or DEPLOYING -VirtualServerStartResult VirtualServer::start_server_virtual_(ts::rwshared_lock &lock, - std::string &error) { - //TOOD: load bans, public key, etc - - return VirtualServerStartResult::SUCCESS; -} - -VirtualServerStartResult VirtualServer::start_server_(ts::rwshared_lock &lock, std::string &error) { - //TODO: Start voice binding etc - - return VirtualServerStartResult::SUCCESS; -} - -void VirtualServer::stop_server_(ts::rwshared_lock &lock) { - //TODO: Stop bindings (if set) and unload everything - +// +// Created by WolverinDEV on 03/03/2020. +// + +#include "VirtualServer.h" + +using namespace ts::server::vserver; + +//This method gets called when server is OFFLINE or DEPLOYING +VirtualServerStartResult VirtualServer::start_server_virtual_(ts::rwshared_lock &lock, + std::string &error) { + //TOOD: load bans, public key, etc + + return VirtualServerStartResult::SUCCESS; +} + +VirtualServerStartResult VirtualServer::start_server_(ts::rwshared_lock &lock, std::string &error) { + //TODO: Start voice binding etc + + return VirtualServerStartResult::SUCCESS; +} + +void VirtualServer::stop_server_(ts::rwshared_lock &lock) { + //TODO: Stop bindings (if set) and unload everything + } \ No newline at end of file diff --git a/server/src/vserver/VirtualServer.h b/server/src/vserver/VirtualServer.h index 164785b..f699994 100644 --- a/server/src/vserver/VirtualServer.h +++ b/server/src/vserver/VirtualServer.h @@ -1,53 +1,53 @@ -#pragma once - -#include "./VirtualServerBase.h" - -namespace ts { - namespace music { - class MusicBotManager; - } - - namespace stats { - class ConnectionStatistics; - } - - namespace server { - namespace tokens { - class TokenManager; - } - - namespace bans { - class BanManager; - } - - namespace conversation { - class ConversationManager; - } - } -} - -namespace ts::server::vserver { - class VirtualServer : public VirtualServerBase { - public: - bool initialize(std::string &string) override; - - /* managers */ - [[nodiscard]] inline tokens::TokenManager& token_manager() { return *this->token_manager_; } - [[nodiscard]] inline bans::BanManager& ban_manager() { return *this->ban_manager_; } - [[nodiscard]] inline const std::shared_ptr& music_bot_manager() { return this->music_bots_; } - [[nodiscard]] inline const std::shared_ptr& conversation_manager() { return this->conversation_manager_; } - - private: - std::unique_ptr token_manager_; - std::unique_ptr ban_manager_; - - std::shared_ptr music_bots_; - std::shared_ptr conversation_manager_; - - protected: - VirtualServerStartResult start_server_virtual_(ts::rwshared_lock &lock, std::string &string) override; - VirtualServerStartResult start_server_(ts::rwshared_lock &lock, std::string &string) override; - void stop_server_(ts::rwshared_lock &lock) override; - void execute_tick(const std::chrono::system_clock::time_point &point) override; - }; +#pragma once + +#include "./VirtualServerBase.h" + +namespace ts { + namespace music { + class MusicBotManager; + } + + namespace stats { + class ConnectionStatistics; + } + + namespace server { + namespace tokens { + class TokenManager; + } + + namespace bans { + class BanManager; + } + + namespace conversation { + class ConversationManager; + } + } +} + +namespace ts::server::vserver { + class VirtualServer : public VirtualServerBase { + public: + bool initialize(std::string &string) override; + + /* managers */ + [[nodiscard]] inline tokens::TokenManager& token_manager() { return *this->token_manager_; } + [[nodiscard]] inline bans::BanManager& ban_manager() { return *this->ban_manager_; } + [[nodiscard]] inline const std::shared_ptr& music_bot_manager() { return this->music_bots_; } + [[nodiscard]] inline const std::shared_ptr& conversation_manager() { return this->conversation_manager_; } + + private: + std::unique_ptr token_manager_; + std::unique_ptr ban_manager_; + + std::shared_ptr music_bots_; + std::shared_ptr conversation_manager_; + + protected: + VirtualServerStartResult start_server_virtual_(ts::rwshared_lock &lock, std::string &string) override; + VirtualServerStartResult start_server_(ts::rwshared_lock &lock, std::string &string) override; + void stop_server_(ts::rwshared_lock &lock) override; + void execute_tick(const std::chrono::system_clock::time_point &point) override; + }; } \ No newline at end of file diff --git a/server/src/vserver/VirtualServerBase.cpp b/server/src/vserver/VirtualServerBase.cpp index 79f4ceb..33bffe5 100644 --- a/server/src/vserver/VirtualServerBase.cpp +++ b/server/src/vserver/VirtualServerBase.cpp @@ -1,78 +1,78 @@ -// -// Created by WolverinDEV on 03/03/2020. -// - -#include "./VirtualServerBase.h" -#include - -using namespace ts::server::vserver; - -bool VirtualServerBase::initialize(std::string &error) { - //TODO? - return true; -} - -VirtualServerStartResult VirtualServerBase::start_server_virtual(std::string &error) { - ts::rwshared_lock slock{this->status_mutex_, ts::rw_lock_exclusive}; - if(this->status_ != VirtualServerStatus::STOPPED) - return VirtualServerStartResult::SERVER_IS_NOT_OFFLINE; - slock.downgrade_lock(); - - if(auto err = this->start_server_virtual_(slock, error); err != VirtualServerStartResult::SUCCESS) - return err; - - if(auto err = slock.upgrade_lock(); err) - logCritical(this->server_id(), "Failed to lock status lock after server virtual start. Ignoring it and still setting state to virtual!"); - - this->status_ = VirtualServerStatus::VIRTUAL; - return VirtualServerStartResult::SUCCESS; -} - -VirtualServerStartResult VirtualServerBase::start_server(std::string &error) { - ts::rwshared_lock slock{this->status_mutex_, ts::rw_lock_exclusive}; - - if(this->status_ != VirtualServerStatus::VIRTUAL && this->status_ == VirtualServerStatus::STOPPED) - return VirtualServerStartResult::SERVER_IS_NOT_OFFLINE; - - auto full_start = this->status_ == VirtualServerStatus::STOPPED; - this->status_ = VirtualServerStatus::BOOTING; - - /* even if queries could be connected, a start from virtual should not lead to a crash! */ - slock.downgrade_lock(); - - if(full_start) - if(auto err = this->start_server_virtual_(slock, error); err != VirtualServerStartResult::SUCCESS) - return err; - - if(auto err = this->start_server_(slock, error); err != VirtualServerStartResult::SUCCESS) - return err; - - if(auto err = slock.upgrade_lock(); err) - logCritical(this->server_id(), "Failed to lock status lock after server start. Ignoring it and still setting state to running!"); - - this->status_ = VirtualServerStatus::RUNNING; - return VirtualServerStartResult::SUCCESS; -} - -VirtualServerStopResult VirtualServerBase::stop_server() { - ts::rwshared_lock slock{this->status_mutex_, ts::rw_lock_shared}; - if(this->status_ != VirtualServerStatus::RUNNING && this->status_ != VirtualServerStatus::VIRTUAL) - return VirtualServerStopResult::SERVER_IS_NOT_RUNNING; - - if(auto err = slock.upgrade_lock(); err) - return VirtualServerStopResult::STATE_LOCK_FAILED; - - this->status_ = VirtualServerStatus::STOPPING; - slock.downgrade_lock(); - this->stop_server_(slock); - - if(auto err = slock.upgrade_lock(); err) - logCritical(this->server_id(), "Failed to lock status lock after server stop. Ignoring it and still setting state to stopped!"); - - this->status_ = VirtualServerStatus::STOPPED; - return VirtualServerStopResult::SUCCESS; -} - -VirtualServerStartResult VirtualServerBase::start_server_(ts::rwshared_lock &slock, std::string &error) { - //TODO: Load server permissions, etc +// +// Created by WolverinDEV on 03/03/2020. +// + +#include "./VirtualServerBase.h" +#include + +using namespace ts::server::vserver; + +bool VirtualServerBase::initialize(std::string &error) { + //TODO? + return true; +} + +VirtualServerStartResult VirtualServerBase::start_server_virtual(std::string &error) { + ts::rwshared_lock slock{this->status_mutex_, ts::rw_lock_exclusive}; + if(this->status_ != VirtualServerStatus::STOPPED) + return VirtualServerStartResult::SERVER_IS_NOT_OFFLINE; + slock.downgrade_lock(); + + if(auto err = this->start_server_virtual_(slock, error); err != VirtualServerStartResult::SUCCESS) + return err; + + if(auto err = slock.upgrade_lock(); err) + logCritical(this->server_id(), "Failed to lock status lock after server virtual start. Ignoring it and still setting state to virtual!"); + + this->status_ = VirtualServerStatus::VIRTUAL; + return VirtualServerStartResult::SUCCESS; +} + +VirtualServerStartResult VirtualServerBase::start_server(std::string &error) { + ts::rwshared_lock slock{this->status_mutex_, ts::rw_lock_exclusive}; + + if(this->status_ != VirtualServerStatus::VIRTUAL && this->status_ == VirtualServerStatus::STOPPED) + return VirtualServerStartResult::SERVER_IS_NOT_OFFLINE; + + auto full_start = this->status_ == VirtualServerStatus::STOPPED; + this->status_ = VirtualServerStatus::BOOTING; + + /* even if queries could be connected, a start from virtual should not lead to a crash! */ + slock.downgrade_lock(); + + if(full_start) + if(auto err = this->start_server_virtual_(slock, error); err != VirtualServerStartResult::SUCCESS) + return err; + + if(auto err = this->start_server_(slock, error); err != VirtualServerStartResult::SUCCESS) + return err; + + if(auto err = slock.upgrade_lock(); err) + logCritical(this->server_id(), "Failed to lock status lock after server start. Ignoring it and still setting state to running!"); + + this->status_ = VirtualServerStatus::RUNNING; + return VirtualServerStartResult::SUCCESS; +} + +VirtualServerStopResult VirtualServerBase::stop_server() { + ts::rwshared_lock slock{this->status_mutex_, ts::rw_lock_shared}; + if(this->status_ != VirtualServerStatus::RUNNING && this->status_ != VirtualServerStatus::VIRTUAL) + return VirtualServerStopResult::SERVER_IS_NOT_RUNNING; + + if(auto err = slock.upgrade_lock(); err) + return VirtualServerStopResult::STATE_LOCK_FAILED; + + this->status_ = VirtualServerStatus::STOPPING; + slock.downgrade_lock(); + this->stop_server_(slock); + + if(auto err = slock.upgrade_lock(); err) + logCritical(this->server_id(), "Failed to lock status lock after server stop. Ignoring it and still setting state to stopped!"); + + this->status_ = VirtualServerStatus::STOPPED; + return VirtualServerStopResult::SUCCESS; +} + +VirtualServerStartResult VirtualServerBase::start_server_(ts::rwshared_lock &slock, std::string &error) { + //TODO: Load server permissions, etc } \ No newline at end of file diff --git a/server/src/vserver/VirtualServerBase.h b/server/src/vserver/VirtualServerBase.h index feb7575..5efa564 100644 --- a/server/src/vserver/VirtualServerBase.h +++ b/server/src/vserver/VirtualServerBase.h @@ -1,143 +1,143 @@ -#pragma once - -#include -#include -#include -#include -#include "../services/PermissionsService.h" -#include "../services/ClientChannelService.h" - -namespace ts::server { - class ConnectedClient; - - namespace groups { - class GroupManager; - } -} - -namespace ts { - class Properties; - class ServerChannelTree; - - namespace stats { - class ConnectionStatistics; - } -} - - -namespace ts::server::vserver { - struct VirtualServerStatus { - enum value { - STOPPED, /* no data has been loaded */ - - BOOTING, /* loading & initializing data */ - VIRTUAL, /* only available for query clients */ - - STARTING, /* start bindings */ - RUNNING, /* server is running */ - - STOPPING, /* server is stopping and unloading any data */ - - DEPLOYING /* server is in deploy modus */ - }; - }; - - enum struct VirtualServerStartResult { - SUCCESS, - BIND_FAILED, - SERVER_IS_DEFAULT, - CUSTOM, - - SERVER_IS_NOT_OFFLINE - }; - - enum struct VirtualServerStopResult { - SUCCESS, - SERVER_IS_NOT_RUNNING, - STATE_LOCK_FAILED - }; - - class VirtualServerBase { - public: - //TODO: Constructor - //VirtualServerBase(); - ~VirtualServerBase(); - - [[nodiscard]] inline std::shared_ptr server_ref() { return this->server_ref_.lock(); } - - /* basic initialize to the STOPPED state */ - virtual bool initialize(std::string& /* error */); - VirtualServerStartResult start_server_virtual(std::string& /* error */); - VirtualServerStartResult start_server(std::string& /* error */); - VirtualServerStopResult stop_server(); - - /* basic information */ - [[nodiscard]] inline ServerId server_id() const { return this->server_id_; } - [[nodiscard]] inline VirtualServerStatus::value server_status() const { return this->status_; } - [[nodiscard]] inline ts::rwshared_lock lock_server_status() { return ts::rwshared_lock{this->status_mutex_, ts::rw_lock_shared}; } - - [[nodiscard]] inline sql::SqlManager* sql_manager() { return this->sql_manager_; } - [[nodiscard]] inline const std::shared_ptr& server_connection_statistics() { return this->server_connection_statistics_; } - - [[nodiscard]] inline permissions::PermissionService& permission_service() { return this->permission_service_; } - - [[nodiscard]] inline channels::ClientChannelService& channel_service() { return this->channel_service_; } - [[nodiscard]] inline ts::rwshared_lock lock_channel_clients() { return ts::rwshared_lock{this->channel_clients_lock_, ts::rw_lock_shared}; } - [[nodiscard]] inline std::mutex& client_nickname_lock() { return this->client_nickname_lock_; } - - [[nodiscard]] inline const std::shared_ptr& server_properties() { return this->server_properties_; } - [[nodiscard]] inline const std::shared_ptr& server_channel_tree() { return this->server_channel_tree_; } - - [[nodiscard]] inline const std::shared_ptr& group_manager() { return this->group_manager_; } - - /* client management */ - /* this may block until the current command of the client has been finished executed */ - void unregister_client(const std::shared_ptr& /* client */); - void register_client(const std::shared_ptr& /* client */); - - /* ATTENTION: lock_channel_clients should be acquired for the following methods. Else the client might be disconnected while working with him. */ - [[nodiscard]] std::vector> connected_clients(bool /* must be registered within the channel tree */); - [[nodiscard]] std::shared_ptr find_client_by_id(ClientId /* id */); - [[nodiscard]] std::vector> find_clients_by_uid(ClientId /* id */); - [[nodiscard]] std::vector> find_clients_by_channel(const std::shared_ptr& /* channel */); - protected: - std::weak_ptr server_ref_{}; - - sql::SqlManager* sql_manager_{nullptr}; //TODO! - - ServerId server_id_{0}; - ts::rw_mutex status_mutex_{}; - VirtualServerStatus::value status_{VirtualServerStatus::STOPPED}; - - std::shared_ptr server_properties_{nullptr}; - std::shared_ptr server_channel_tree_{nullptr}; - - /* - * Lock whenever we want to read/write from the channel tree or the clients within - * Attention: We're not allowed to do permission calculations while having the channel tree in write lock! - */ - ts::rw_mutex channel_clients_lock_{}; - std::mutex client_nickname_lock_{}; - - /* some services */ - permissions::PermissionService permission_service_; - channels::ClientChannelService channel_service_; - - struct { - size_t count{0}; - std::mutex mutex{}; - std::vector> clients{}; - } registered_clients; - - std::shared_ptr group_manager_; - - std::shared_ptr server_connection_statistics_; - - /* private methods */ - virtual VirtualServerStartResult start_server_virtual_(ts::rwshared_lock& /* state lock */, std::string& /* error */); - virtual VirtualServerStartResult start_server_(ts::rwshared_lock& /* state lock */, std::string& /* error */); - virtual void stop_server_(ts::rwshared_lock& /* state lock */); - - virtual void execute_tick(const std::chrono::system_clock::time_point& /* now */) = 0; - }; +#pragma once + +#include +#include +#include +#include +#include "../services/PermissionsService.h" +#include "../services/ClientChannelService.h" + +namespace ts::server { + class ConnectedClient; + + namespace groups { + class GroupManager; + } +} + +namespace ts { + class Properties; + class ServerChannelTree; + + namespace stats { + class ConnectionStatistics; + } +} + + +namespace ts::server::vserver { + struct VirtualServerStatus { + enum value { + STOPPED, /* no data has been loaded */ + + BOOTING, /* loading & initializing data */ + VIRTUAL, /* only available for query clients */ + + STARTING, /* start bindings */ + RUNNING, /* server is running */ + + STOPPING, /* server is stopping and unloading any data */ + + DEPLOYING /* server is in deploy modus */ + }; + }; + + enum struct VirtualServerStartResult { + SUCCESS, + BIND_FAILED, + SERVER_IS_DEFAULT, + CUSTOM, + + SERVER_IS_NOT_OFFLINE + }; + + enum struct VirtualServerStopResult { + SUCCESS, + SERVER_IS_NOT_RUNNING, + STATE_LOCK_FAILED + }; + + class VirtualServerBase { + public: + //TODO: Constructor + //VirtualServerBase(); + ~VirtualServerBase(); + + [[nodiscard]] inline std::shared_ptr server_ref() { return this->server_ref_.lock(); } + + /* basic initialize to the STOPPED state */ + virtual bool initialize(std::string& /* error */); + VirtualServerStartResult start_server_virtual(std::string& /* error */); + VirtualServerStartResult start_server(std::string& /* error */); + VirtualServerStopResult stop_server(); + + /* basic information */ + [[nodiscard]] inline ServerId server_id() const { return this->server_id_; } + [[nodiscard]] inline VirtualServerStatus::value server_status() const { return this->status_; } + [[nodiscard]] inline ts::rwshared_lock lock_server_status() { return ts::rwshared_lock{this->status_mutex_, ts::rw_lock_shared}; } + + [[nodiscard]] inline sql::SqlManager* sql_manager() { return this->sql_manager_; } + [[nodiscard]] inline const std::shared_ptr& server_connection_statistics() { return this->server_connection_statistics_; } + + [[nodiscard]] inline permissions::PermissionService& permission_service() { return this->permission_service_; } + + [[nodiscard]] inline channels::ClientChannelService& channel_service() { return this->channel_service_; } + [[nodiscard]] inline ts::rwshared_lock lock_channel_clients() { return ts::rwshared_lock{this->channel_clients_lock_, ts::rw_lock_shared}; } + [[nodiscard]] inline std::mutex& client_nickname_lock() { return this->client_nickname_lock_; } + + [[nodiscard]] inline const std::shared_ptr& server_properties() { return this->server_properties_; } + [[nodiscard]] inline const std::shared_ptr& server_channel_tree() { return this->server_channel_tree_; } + + [[nodiscard]] inline const std::shared_ptr& group_manager() { return this->group_manager_; } + + /* client management */ + /* this may block until the current command of the client has been finished executed */ + void unregister_client(const std::shared_ptr& /* client */); + void register_client(const std::shared_ptr& /* client */); + + /* ATTENTION: lock_channel_clients should be acquired for the following methods. Else the client might be disconnected while working with him. */ + [[nodiscard]] std::vector> connected_clients(bool /* must be registered within the channel tree */); + [[nodiscard]] std::shared_ptr find_client_by_id(ClientId /* id */); + [[nodiscard]] std::vector> find_clients_by_uid(ClientId /* id */); + [[nodiscard]] std::vector> find_clients_by_channel(const std::shared_ptr& /* channel */); + protected: + std::weak_ptr server_ref_{}; + + sql::SqlManager* sql_manager_{nullptr}; //TODO! + + ServerId server_id_{0}; + ts::rw_mutex status_mutex_{}; + VirtualServerStatus::value status_{VirtualServerStatus::STOPPED}; + + std::shared_ptr server_properties_{nullptr}; + std::shared_ptr server_channel_tree_{nullptr}; + + /* + * Lock whenever we want to read/write from the channel tree or the clients within + * Attention: We're not allowed to do permission calculations while having the channel tree in write lock! + */ + ts::rw_mutex channel_clients_lock_{}; + std::mutex client_nickname_lock_{}; + + /* some services */ + permissions::PermissionService permission_service_; + channels::ClientChannelService channel_service_; + + struct { + size_t count{0}; + std::mutex mutex{}; + std::vector> clients{}; + } registered_clients; + + std::shared_ptr group_manager_; + + std::shared_ptr server_connection_statistics_; + + /* private methods */ + virtual VirtualServerStartResult start_server_virtual_(ts::rwshared_lock& /* state lock */, std::string& /* error */); + virtual VirtualServerStartResult start_server_(ts::rwshared_lock& /* state lock */, std::string& /* error */); + virtual void stop_server_(ts::rwshared_lock& /* state lock */); + + virtual void execute_tick(const std::chrono::system_clock::time_point& /* now */) = 0; + }; } \ No newline at end of file