From be4a99dcd7e625b571910e391b415c801101dd0b Mon Sep 17 00:00:00 2001 From: WolverinDEV Date: Wed, 4 Mar 2020 16:24:36 +0100 Subject: [PATCH] Begining with the new VS structure --- server/CMakeLists.txt | 13 +- server/helpers/PermMapGen.cpp | 2 +- server/helpers/permgen.cpp | 2 +- server/src/DatabaseHelper.cpp | 30 +- server/src/DatabaseHelper.h | 22 +- server/src/Group.cpp | 4 +- server/src/Group.h | 8 +- server/src/ServerManagerSnapshotDeploy.cpp | 2 +- server/src/TS3ServerClientManager.cpp | 12 +- server/src/VirtualServer.cpp | 4 +- server/src/VirtualServer.h | 4 +- server/src/channel/ServerChannel.h | 6 +- server/src/client/ConnectedClient.cpp | 25 +- server/src/client/ConnectedClient.h | 31 +- .../client/ConnectedClientCommandHandler.cpp | 2 +- .../client/ConnectedClientNotifyHandler.cpp | 4 +- server/src/client/DataClient.h | 2 +- server/src/client/command_handler/channel.cpp | 4 +- server/src/client/command_handler/client.cpp | 4 +- server/src/client/command_handler/file.cpp | 4 +- server/src/client/command_handler/misc.cpp | 5 +- server/src/client/command_handler/music.cpp | 4 +- server/src/client/command_handler/server.cpp | 4 +- server/src/client/query/QueryClient.h | 10 +- server/src/client/query/QueryClientNotify.cpp | 14 +- server/src/client/web/WebClient.cpp | 2 +- server/src/groups/Group.cpp | 23 + server/src/groups/Group.h | 75 +++ server/src/groups/GroupAssignmentManager.cpp | 533 ++++++++++++++++++ server/src/groups/GroupAssignmentManager.h | 110 ++++ server/src/groups/GroupManager.cpp | 5 + server/src/groups/GroupManager.h | 82 +++ server/src/manager/BanManager.cpp | 1 + server/src/manager/BanManager.h | 136 ++--- server/src/manager/PermissionNameMapper.h | 2 +- server/src/manager/SqlDataManager.cpp | 3 +- server/src/manager/TokeManager.cpp | 4 +- server/src/manager/TokeManager.h | 4 +- server/src/music/MusicPlaylist.cpp | 2 +- server/src/music/MusicPlaylist.h | 2 +- server/src/music/PlayablePlaylist.cpp | 2 +- server/src/music/PlayablePlaylist.h | 2 +- server/src/music/PlaylistPermissions.cpp | 2 +- server/src/music/PlaylistPermissions.h | 8 +- server/src/services/ClientChannelService.cpp | 379 +++++++++++++ server/src/services/ClientChannelService.h | 94 +++ server/src/services/PermissionsService.cpp | 319 +++++++++++ server/src/services/PermissionsService.h | 68 +++ server/src/vserver/DefaultServer.cpp | 33 ++ server/src/vserver/DefaultServer.h | 15 + server/src/vserver/VirtualServer.cpp | 26 + server/src/vserver/VirtualServer.h | 53 ++ server/src/vserver/VirtualServerBase.cpp | 78 +++ server/src/vserver/VirtualServerBase.h | 143 +++++ server/thread_concept | 11 +- shared | 2 +- 56 files changed, 2253 insertions(+), 188 deletions(-) create mode 100644 server/src/groups/Group.cpp create mode 100644 server/src/groups/Group.h create mode 100644 server/src/groups/GroupAssignmentManager.cpp create mode 100644 server/src/groups/GroupAssignmentManager.h create mode 100644 server/src/groups/GroupManager.cpp create mode 100644 server/src/groups/GroupManager.h create mode 100644 server/src/services/ClientChannelService.cpp create mode 100644 server/src/services/ClientChannelService.h create mode 100644 server/src/services/PermissionsService.cpp create mode 100644 server/src/services/PermissionsService.h create mode 100644 server/src/vserver/DefaultServer.cpp create mode 100644 server/src/vserver/DefaultServer.h create mode 100644 server/src/vserver/VirtualServer.cpp create mode 100644 server/src/vserver/VirtualServer.h create mode 100644 server/src/vserver/VirtualServerBase.cpp create mode 100644 server/src/vserver/VirtualServerBase.h diff --git a/server/CMakeLists.txt b/server/CMakeLists.txt index f3931c9..b3c5a83 100644 --- a/server/CMakeLists.txt +++ b/server/CMakeLists.txt @@ -138,7 +138,16 @@ set(SERVER_SOURCE_FILES src/manager/ConversationManager.cpp src/client/SpeakingClientHandshake.cpp - src/client/command_handler/music.cpp src/client/command_handler/file.cpp) + src/client/command_handler/music.cpp + src/client/command_handler/file.cpp + + src/vserver/VirtualServerBase.cpp + src/vserver/VirtualServer.cpp + src/vserver/DefaultServer.cpp + + src/services/PermissionsService.cpp + src/services/ClientChannelService.cpp +) if (COMPILE_WEB_CLIENT) add_definitions(-DCOMPILE_WEB_CLIENT) @@ -151,7 +160,7 @@ if (COMPILE_WEB_CLIENT) src/client/web/WSWebClient.cpp src/client/web/SampleHandler.cpp src/client/web/VoiceBridge.cpp - src/client/command_handler/helpers.h src/music/PlaylistPermissions.cpp src/music/PlaylistPermissions.h src/lincense/LicenseService.cpp src/lincense/LicenseService.h) + src/client/command_handler/helpers.h src/music/PlaylistPermissions.cpp src/music/PlaylistPermissions.h src/lincense/LicenseService.cpp src/lincense/LicenseService.h src/groups/GroupManager.cpp src/groups/GroupManager.h src/groups/GroupAssignmentManager.cpp src/groups/GroupAssignmentManager.h src/groups/Group.cpp src/groups/Group.h) endif () add_executable(PermHelper helpers/permgen.cpp) diff --git a/server/helpers/PermMapGen.cpp b/server/helpers/PermMapGen.cpp index a0cebd5..a9a9fc6 100644 --- a/server/helpers/PermMapGen.cpp +++ b/server/helpers/PermMapGen.cpp @@ -6,7 +6,7 @@ #include /* required from permission manager */ #include "log/LogUtils.h" #include "Definitions.h" -#include "PermissionManager.h" +#include "PermissionRegister.h" using namespace std; using namespace ts; diff --git a/server/helpers/permgen.cpp b/server/helpers/permgen.cpp index f8380ac..0570daa 100644 --- a/server/helpers/permgen.cpp +++ b/server/helpers/permgen.cpp @@ -6,7 +6,7 @@ #include /* required from permission manager */ #include "log/LogUtils.h" #include "Definitions.h" -#include "PermissionManager.h" +#include "PermissionRegister.h" using namespace std; using namespace ts; diff --git a/server/src/DatabaseHelper.cpp b/server/src/DatabaseHelper.cpp index ab35a59..794ef24 100644 --- a/server/src/DatabaseHelper.cpp +++ b/server/src/DatabaseHelper.cpp @@ -159,7 +159,7 @@ void DatabaseHelper::deleteClient(const std::shared_ptr& server, //TODO delete complains } -inline sql::result load_permissions_v2(const std::shared_ptr& server, v2::PermissionManager* manager, sql::command& command, bool test_channel /* only used for client permissions (client channel permissions) */) { +inline sql::result load_permissions_v2(const std::shared_ptr& server, v2::PermissionRegister* manager, sql::command& command, bool test_channel /* only used for client permissions (client channel permissions) */) { auto start = system_clock::now(); auto server_id = server ? server->getServerId() : 0; @@ -229,7 +229,7 @@ inline sql::result load_permissions_v2(const std::shared_ptr& ser #define INSERT_COMMAND "INSERT INTO `permissions` (`serverId`, `type`, `id`, `channelId`, `permId`, `value`, `grant`, `flag_skip`, `flag_negate`) VALUES (:serverId, :type, :id, :chId, :permId, :value, :grant, :flag_skip, :flag_negate)" #define DELETE_COMMAND "DELETE FROM `permissions` WHERE `serverId` = :serverId AND `type` = :type AND `id` = :id AND `permId` = :permId AND `channelId` = :chId" -std::shared_ptr DatabaseHelper::loadClientPermissionManager(const std::shared_ptr& server, ClientDbId cldbid) { +std::shared_ptr DatabaseHelper::loadClientPermissionManager(const std::shared_ptr& server, ClientDbId cldbid) { auto server_id = server ? server->getServerId() : 0; #ifndef DISABLE_CACHING { @@ -249,7 +249,7 @@ std::shared_ptr DatabaseHelper::loadClientPermissionManag #endif logTrace(server_id, "[Permission] Loading client permission manager for client {}", cldbid); - auto permission_manager = std::make_shared(); + auto permission_manager = std::make_shared(); bool loaded = false; if(this->use_startup_cache && server) { shared_ptr entry; @@ -301,7 +301,7 @@ std::shared_ptr DatabaseHelper::loadClientPermissionManag } -void DatabaseHelper::saveClientPermissions(const std::shared_ptr &server, ts::ClientDbId client_dbid, const std::shared_ptr &permissions) { +void DatabaseHelper::saveClientPermissions(const std::shared_ptr &server, ts::ClientDbId client_dbid, const std::shared_ptr &permissions) { const auto updates = permissions->flush_db_updates(); if(updates.empty()) return; @@ -336,8 +336,8 @@ void DatabaseHelper::saveClientPermissions(const std::shared_ptr DatabaseHelper::loadGroupPermissions(const std::shared_ptr& server, ts::GroupId group_id) { - auto result = std::make_shared(); +std::shared_ptr DatabaseHelper::loadGroupPermissions(const std::shared_ptr& server, ts::GroupId group_id) { + auto result = std::make_shared(); if(this->use_startup_cache && server) { shared_ptr entry; { @@ -368,7 +368,7 @@ std::shared_ptr DatabaseHelper::loadGroupPerm return result; } -void DatabaseHelper::saveGroupPermissions(const std::shared_ptr &server, ts::GroupId group_id, const std::shared_ptr &permissions) { +void DatabaseHelper::saveGroupPermissions(const std::shared_ptr &server, ts::GroupId group_id, const std::shared_ptr &permissions) { const auto updates = permissions->flush_db_updates(); if(updates.empty()) return; @@ -402,8 +402,8 @@ void DatabaseHelper::saveGroupPermissions(const std::shared_ptr DatabaseHelper::loadPlaylistPermissions(const std::shared_ptr &server, ts::PlaylistId playlist_id) { - shared_ptr result; +std::shared_ptr DatabaseHelper::loadPlaylistPermissions(const std::shared_ptr &server, ts::PlaylistId playlist_id) { + shared_ptr result; if(this->use_startup_cache && server) { shared_ptr entry; { @@ -416,7 +416,7 @@ std::shared_ptr DatabaseHelper::loadPlaylistP } } if(entry) { - result = std::make_shared(); + result = std::make_shared(); for(const auto& perm : entry->permissions) { if(perm->type == permission::SQL_PERM_PLAYLIST && perm->id == playlist_id) { @@ -431,7 +431,7 @@ std::shared_ptr DatabaseHelper::loadPlaylistP return result; } - result = std::make_shared(); + result = std::make_shared(); auto command = sql::command(this->sql, "SELECT `channelId`, `permId`, `value`, `grant`, `flag_skip`, `flag_negate` FROM `permissions` WHERE `serverId` = :serverId AND `type` = :type AND `id` = :id", variable{":serverId", server ? server->getServerId() : 0}, variable{":type", permission::SQL_PERM_PLAYLIST}, @@ -440,7 +440,7 @@ std::shared_ptr DatabaseHelper::loadPlaylistP return result; } -void DatabaseHelper::savePlaylistPermissions(const std::shared_ptr &server, PlaylistId pid, const std::shared_ptr &permissions) { +void DatabaseHelper::savePlaylistPermissions(const std::shared_ptr &server, PlaylistId pid, const std::shared_ptr &permissions) { const auto updates = permissions->flush_db_updates(); if(updates.empty()) return; @@ -474,8 +474,8 @@ void DatabaseHelper::savePlaylistPermissions(const std::shared_ptr DatabaseHelper::loadChannelPermissions(const std::shared_ptr& server, ts::ChannelId channel) { - auto result = std::make_shared(); +std::shared_ptr DatabaseHelper::loadChannelPermissions(const std::shared_ptr& server, ts::ChannelId channel) { + auto result = std::make_shared(); if(this->use_startup_cache && server) { shared_ptr entry; { @@ -506,7 +506,7 @@ std::shared_ptr DatabaseHelper::loadChannelPe return result; } -void DatabaseHelper::saveChannelPermissions(const std::shared_ptr &server, ts::ChannelId channel_id, const std::shared_ptr &permissions) { +void DatabaseHelper::saveChannelPermissions(const std::shared_ptr &server, ts::ChannelId channel_id, const std::shared_ptr &permissions) { const auto updates = permissions->flush_db_updates(); if(updates.empty()) return; diff --git a/server/src/DatabaseHelper.h b/server/src/DatabaseHelper.h index 7e3c4aa..60af788 100644 --- a/server/src/DatabaseHelper.h +++ b/server/src/DatabaseHelper.h @@ -4,7 +4,7 @@ #include #include #include -#include +#include #include #include @@ -26,8 +26,8 @@ namespace ts { struct CachedPermissionManager { ServerId sid; ClientDbId cldbid; - std::weak_ptr manager; - std::shared_ptr ownLock; + std::weak_ptr manager; + std::shared_ptr ownLock; std::chrono::time_point lastAccess; }; @@ -91,17 +91,17 @@ namespace ts { std::deque> queryDatabaseInfo(const std::shared_ptr&, const std::deque&); std::deque> queryDatabaseInfoByUid(const std::shared_ptr &, std::deque); - std::shared_ptr loadClientPermissionManager(const std::shared_ptr&, ClientDbId); - void saveClientPermissions(const std::shared_ptr&, ClientDbId , const std::shared_ptr& /* permission manager */); + std::shared_ptr loadClientPermissionManager(const std::shared_ptr&, ClientDbId); + void saveClientPermissions(const std::shared_ptr&, ClientDbId , const std::shared_ptr& /* permission manager */); - std::shared_ptr loadChannelPermissions(const std::shared_ptr&, ChannelId); - void saveChannelPermissions(const std::shared_ptr&, ChannelId, const std::shared_ptr& /* permission manager */); + std::shared_ptr loadChannelPermissions(const std::shared_ptr&, ChannelId); + void saveChannelPermissions(const std::shared_ptr&, ChannelId, const std::shared_ptr& /* permission manager */); - std::shared_ptr loadGroupPermissions(const std::shared_ptr&, GroupId); - void saveGroupPermissions(const std::shared_ptr&, GroupId, const std::shared_ptr& /* permission manager */); + std::shared_ptr loadGroupPermissions(const std::shared_ptr&, GroupId); + void saveGroupPermissions(const std::shared_ptr&, GroupId, const std::shared_ptr& /* permission manager */); - std::shared_ptr loadPlaylistPermissions(const std::shared_ptr&, PlaylistId /* playlist id */); - void savePlaylistPermissions(const std::shared_ptr&, PlaylistId, const std::shared_ptr& /* permission manager */); + std::shared_ptr loadPlaylistPermissions(const std::shared_ptr&, PlaylistId /* playlist id */); + void savePlaylistPermissions(const std::shared_ptr&, PlaylistId, const std::shared_ptr& /* permission manager */); std::shared_ptr loadServerProperties(const std::shared_ptr&); //Read and write std::shared_ptr loadPlaylistProperties(const std::shared_ptr&, PlaylistId); //Read and write diff --git a/server/src/Group.cpp b/server/src/Group.cpp index c157cd8..3ff0b45 100644 --- a/server/src/Group.cpp +++ b/server/src/Group.cpp @@ -21,7 +21,7 @@ Group::Group(GroupManager* handle, GroupTarget target, GroupType type, GroupId g this->_properties = new Properties(); this->_properties->register_property_type(); - this->setPermissionManager(make_shared()); + this->setPermissionManager(make_shared()); this->properties()[property::GROUP_ID] = groupId; this->properties()[property::GROUP_TYPE] = type; @@ -59,7 +59,7 @@ Group::Group(GroupManager* handle, GroupTarget target, GroupType type, GroupId g }); } -void Group::setPermissionManager(const std::shared_ptr &manager) { +void Group::setPermissionManager(const std::shared_ptr &manager) { this->_permissions = manager; this->apply_properties_from_permissions(); } diff --git a/server/src/Group.h b/server/src/Group.h index c1f8f99..ef1cc47 100644 --- a/server/src/Group.h +++ b/server/src/Group.h @@ -4,7 +4,7 @@ #include #include #include -#include "PermissionManager.h" +#include "PermissionRegister.h" #include "Properties.h" #include "channel/ServerChannel.h" #include "Definitions.h" @@ -82,7 +82,7 @@ namespace ts { Group(GroupManager* handle, GroupTarget target, GroupType type, GroupId groupId); ~Group(); - std::shared_ptr permissions(){ return this->_permissions; } + std::shared_ptr permissions(){ return this->_permissions; } Properties& properties(){ return *this->_properties; } GroupNameMode nameMode(){ return (GroupNameMode) (uint8_t) properties()[property::GROUP_NAMEMODE]; } @@ -133,10 +133,10 @@ namespace ts { return data.has_value ? data.value : 0; } private: - void setPermissionManager(const std::shared_ptr& manager); + void setPermissionManager(const std::shared_ptr& manager); GroupManager* handle; - std::shared_ptr _permissions; + std::shared_ptr _permissions; Properties* _properties; GroupTarget _target; GroupType _type; diff --git a/server/src/ServerManagerSnapshotDeploy.cpp b/server/src/ServerManagerSnapshotDeploy.cpp index f1d7b74..a86cad7 100644 --- a/server/src/ServerManagerSnapshotDeploy.cpp +++ b/server/src/ServerManagerSnapshotDeploy.cpp @@ -707,7 +707,7 @@ struct PermissionCommandTuple { ChannelId channel; }; -inline bool writePermissions(const shared_ptr& manager, Command& cmd, int& index, int version, permission::teamspeak::GroupType type, std::string& error) { +inline bool writePermissions(const shared_ptr& manager, Command& cmd, int& index, int version, permission::teamspeak::GroupType type, std::string& error) { for(const auto& permission_container : manager->permissions()) { auto permission = get<1>(permission_container); SnapshotPermissionEntry{ diff --git a/server/src/TS3ServerClientManager.cpp b/server/src/TS3ServerClientManager.cpp index 9a60e19..732f173 100644 --- a/server/src/TS3ServerClientManager.cpp +++ b/server/src/TS3ServerClientManager.cpp @@ -259,14 +259,16 @@ void VirtualServer::notify_client_ban(const shared_ptr &target, lock_guard command_lock(target->command_lock); unique_lock server_channel_lock(this->channel_tree_lock); /* we're "moving" a client! */ + ChannelId old_channel_id{0}; if(target->currentChannel) { + old_channel_id = target->currentChannel->channelId(); for(const auto& client : this->getClients()) { if(!client || client == target) continue; unique_lock client_channel_lock(client->channel_lock); if(client->isClientVisible(target, false)) - client->notifyClientLeftViewBanned(target, reason, invoker, time, false); + client->notifyClientLeftViewBanned(target, old_channel_id, invoker, time, false, reason); } auto s_channel = dynamic_pointer_cast(target->currentChannel); @@ -275,7 +277,7 @@ void VirtualServer::notify_client_ban(const shared_ptr &target, /* now disconnect the target itself */ unique_lock client_channel_lock(target->channel_lock); - target->notifyClientLeftViewBanned(target, reason, invoker, time, false); + target->notifyClientLeftViewBanned(target, old_channel_id, invoker, time, false, reason); target->currentChannel = nullptr; } @@ -294,14 +296,16 @@ void VirtualServer::notify_client_kick( lock_guard command_lock(target->command_lock); unique_lock server_channel_lock(this->channel_tree_lock); /* we're "moving" a client! */ + ChannelId old_channel_id{0}; if(target->currentChannel) { + old_channel_id = target->currentChannel->channelId(); for(const auto& client : this->getClients()) { if(!client || client == target) continue; unique_lock client_channel_lock(client->channel_lock); if(client->isClientVisible(target, false)) - client->notifyClientLeftViewKicked(target, nullptr, reason, invoker, false); + client->notifyClientLeftViewKicked(target, old_channel_id, reason, invoker, false, nullptr); } auto s_channel = dynamic_pointer_cast(target->currentChannel); @@ -310,7 +314,7 @@ void VirtualServer::notify_client_kick( /* now disconnect the target itself */ unique_lock client_channel_lock(target->channel_lock); - target->notifyClientLeftViewKicked(target, nullptr, reason, invoker, false); + target->notifyClientLeftViewKicked(target, old_channel_id, reason, invoker, false, nullptr); target->currentChannel = nullptr; } } diff --git a/server/src/VirtualServer.cpp b/server/src/VirtualServer.cpp index 5b4c832..4687ff0 100644 --- a/server/src/VirtualServer.cpp +++ b/server/src/VirtualServer.cpp @@ -154,7 +154,7 @@ bool VirtualServer::initialize(bool test_properties) { if(default_channel->properties()[property::CHANNEL_FLAG_PASSWORD].as()) default_channel->properties()[property::CHANNEL_FLAG_PASSWORD] = false; - this->tokenManager = new token::TokenManager(this); + this->tokenManager = new server::tokens::TokenManager(this); this->tokenManager->loadTokens(); this->complains = new ComplainManager(this); @@ -1108,7 +1108,7 @@ bool VirtualServer::resetPermissions(std::string& token) { this->properties()[property::VIRTUALSERVER_DEFAULT_CHANNEL_GROUP] = this->getGroupManager()->findGroup(GroupTarget::GROUPTARGET_CHANNEL, default_channel_guest->name()).front()->groupId(); auto token_admin = this->getGroupManager()->findGroup(GroupTarget::GROUPTARGET_SERVER, default_server_admin->name()).front()->groupId(); - auto created = this->tokenManager->createToken(token::TOKEN_SERVER, token_admin, "Default server token for the server admin."); + auto created = this->tokenManager->createToken(server::tokens::TOKEN_SERVER, token_admin, "Default server token for the server admin."); if(!created) { logCritical(this->serverId, "Failed to generate default serveradmin token!"); } else { diff --git a/server/src/VirtualServer.h b/server/src/VirtualServer.h index 6ff1131..81eecaf 100644 --- a/server/src/VirtualServer.h +++ b/server/src/VirtualServer.h @@ -106,7 +106,7 @@ namespace ts { bool global_skip = false; bool global_skip_set = false; - std::shared_ptr client_permissions; + std::shared_ptr client_permissions; std::vector> assignment_server_groups; bool assignment_server_groups_set = false; @@ -290,7 +290,7 @@ namespace ts { std::shared_ptr udpVoiceServer = nullptr; WebControlServer* webControlServer = nullptr; - token::TokenManager* tokenManager = nullptr; + server::tokens::TokenManager* tokenManager = nullptr; ComplainManager* complains = nullptr; letter::LetterManager* letters = nullptr; std::shared_ptr musicManager; diff --git a/server/src/channel/ServerChannel.h b/server/src/channel/ServerChannel.h index 0d4d42d..789ebb0 100644 --- a/server/src/channel/ServerChannel.h +++ b/server/src/channel/ServerChannel.h @@ -1,11 +1,11 @@ #pragma once +#include #include #include #include "Properties.h" -#include "PermissionManager.h" +#include "PermissionRegister.h" #include "BasicChannel.h" -#include "../Group.h" #include #include @@ -45,7 +45,7 @@ namespace ts { void deleteSemiPermanentChannels(); - std::shared_ptr tree_head() { return this->head; } + [[nodiscard]] std::shared_ptr tree_head() { return this->head; } protected: virtual ChannelId generateChannelId() override; diff --git a/server/src/client/ConnectedClient.cpp b/server/src/client/ConnectedClient.cpp index 0ac2e5d..54856ae 100644 --- a/server/src/client/ConnectedClient.cpp +++ b/server/src/client/ConnectedClient.cpp @@ -21,7 +21,7 @@ using namespace std; using namespace std::chrono; using namespace ts; using namespace ts::server; -using namespace ts::token; +using namespace ts::server::tokens; extern ts::server::InstanceHandler* serverInstance; @@ -451,11 +451,10 @@ bool ConnectedClient::notifyClientLeftView( return true; } -bool ConnectedClient::notifyClientLeftViewKicked(const std::shared_ptr &client, - const std::shared_ptr &target_channel, - const std::string& message, - std::shared_ptr invoker, - bool lock_channel_tree) { +bool ConnectedClient::notifyClientLeftViewKicked(const std::shared_ptr &client, ChannelId channel_from, + const std::string &message, std::shared_ptr invoker, + bool lock_channel_tree, + const std::shared_ptr &target_channel) { assert(!lock_channel_tree); /* not supported yet! */ assert(client && client->getClientId() != 0); assert(client->currentChannel || &*client == this); @@ -479,7 +478,7 @@ bool ConnectedClient::notifyClientLeftViewKicked(const std::shared_ptrgetClientId(); - cmd["cfid"] = client->currentChannel ? client->currentChannel->channelId() : 0; + cmd["cfid"] = channel_from; cmd["ctid"] = target_channel ? target_channel->channelId() : 0; cmd["reasonid"] = (uint8_t) (target_channel ? ViewReasonId::VREASON_CHANNEL_KICK : ViewReasonId::VREASON_SERVER_KICK); cmd["reasonmsg"] = message; @@ -494,12 +493,10 @@ bool ConnectedClient::notifyClientLeftViewKicked(const std::shared_ptr &client, - const std::string& message, - std::shared_ptr invoker, - size_t length, - bool lock_channel_tree) { +bool ConnectedClient::notifyClientLeftViewBanned(const std::shared_ptr &client, ChannelId channel_from, + std::shared_ptr invoker, size_t length, + bool lock_channel_tree, + const std::string &message) { assert(!lock_channel_tree); /* not supported yet! */ assert(client && client->getClientId() != 0); @@ -508,7 +505,7 @@ bool ConnectedClient::notifyClientLeftViewBanned( Command cmd("notifyclientleftview"); cmd["clid"] = client->getClientId(); - cmd["cfid"] = client->currentChannel ? client->currentChannel->channelId() : 0; + cmd["cfid"] = channel_from; cmd["ctid"] = 0; cmd["reasonid"] = ViewReasonId::VREASON_BAN; cmd["reasonmsg"] = message; diff --git a/server/src/client/ConnectedClient.h b/server/src/client/ConnectedClient.h index e91f310..fdcfbf7 100644 --- a/server/src/client/ConnectedClient.h +++ b/server/src/client/ConnectedClient.h @@ -96,6 +96,8 @@ namespace ts { virtual void setClientId(uint16_t clId) { properties()[property::CLIENT_ID] = clId; } inline std::shared_ptr getChannel(){ return this->currentChannel; } + /* ATTENTION: Do this only with command_lock locked, and client_channel_tree lock lock in exclusive mode */ + inline void setChannel(const std::shared_ptr& channel){ this->currentChannel = channel; } inline ChannelId getChannelId(){ auto channel = this->currentChannel; return channel ? channel->channelId() : 0; } inline std::shared_ptr getServer(){ return this->server; } inline ServerId getServerId(){ return this->server ? this->server->getServerId() : (ServerId) 0; } @@ -122,7 +124,7 @@ namespace ts { virtual bool notifyClientNeededPermissions(); virtual bool notifyServerGroupList(); virtual bool notifyGroupPermList(const std::shared_ptr&, bool); - virtual bool notifyClientPermList(ClientDbId, const std::shared_ptr&, bool); + virtual bool notifyClientPermList(ClientDbId, const std::shared_ptr&, bool); virtual bool notifyChannelGroupList(); virtual bool notifyConnectionInfo(const std::shared_ptr &target, const std::shared_ptr &info); virtual bool notifyChannelSubscribed(const std::deque> &); @@ -210,20 +212,13 @@ namespace ts { const ViewReasonServerLeftT& /* mode */ ); - virtual bool notifyClientLeftViewKicked( - const std::shared_ptr &client, - const std::shared_ptr &target_channel, - const std::string& message, - std::shared_ptr invoker, - bool lock_channel_tree - ); - virtual bool notifyClientLeftViewBanned( - const std::shared_ptr &client, - const std::string& message, - std::shared_ptr invoker, - size_t length, - bool lock_channel_tree - ); + virtual bool notifyClientLeftViewKicked(const std::shared_ptr &client, ChannelId channel_from, + const std::string &message, std::shared_ptr invoker, + bool lock_channel_tree, const std::shared_ptr &target_channel); + virtual bool notifyClientLeftViewBanned(const std::shared_ptr &client, ChannelId channel_from, + std::shared_ptr invoker, size_t length, + bool lock_channel_tree, + const std::string &message); virtual bool notifyMusicPlayerSongChange(const std::shared_ptr& bot, const std::shared_ptr& newEntry); virtual bool notifyMusicQueueAdd(const std::shared_ptr& bot, const std::shared_ptr& entry, int index, const std::shared_ptr& invoker); @@ -299,6 +294,9 @@ namespace ts { inline bool playlist_subscribed(const std::shared_ptr& playlist) const { return this->_subscribed_playlist.lock() == playlist; } + + [[nodiscard]] inline auto lock_command_handling() { return std::lock_guard{this->command_lock}; } + void increase_join_state() { this->join_state_id++; } protected: std::weak_ptr _this; sockaddr_storage remote_address; @@ -311,9 +309,6 @@ namespace ts { std::shared_mutex finalDisconnectLock; /* locked before state lock! */ - std::vector cached_server_groups{}; /* variable locked with channel_lock */ - GroupId cached_channel_group = 0; /* variable locked with channel_lock */ - std::deque> visibleClients{}; /* variable locked with channel_lock */ std::deque> mutedClients{}; /* variable locked with channel_lock */ std::deque> openChats{}; /* variable locked with channel_lock */ diff --git a/server/src/client/ConnectedClientCommandHandler.cpp b/server/src/client/ConnectedClientCommandHandler.cpp index 4ab4011..6f601f8 100644 --- a/server/src/client/ConnectedClientCommandHandler.cpp +++ b/server/src/client/ConnectedClientCommandHandler.cpp @@ -12,7 +12,7 @@ #include "../server/file/FileServer.h" #include "../server/VoiceServer.h" #include "voice/VoiceClient.h" -#include "PermissionManager.h" +#include "PermissionRegister.h" #include "../InstanceHandler.h" #include "../server/QueryServer.h" #include "file/FileClient.h" diff --git a/server/src/client/ConnectedClientNotifyHandler.cpp b/server/src/client/ConnectedClientNotifyHandler.cpp index 3c930c4..6bf94a2 100644 --- a/server/src/client/ConnectedClientNotifyHandler.cpp +++ b/server/src/client/ConnectedClientNotifyHandler.cpp @@ -19,7 +19,7 @@ using namespace std::chrono; using namespace std; using namespace ts; using namespace ts::server; -using namespace ts::token; +using namespace ts::server::tokens; extern ts::server::InstanceHandler* serverInstance; @@ -133,7 +133,7 @@ bool ConnectedClient::notifyGroupPermList(const std::shared_ptr& group, b return true; } -bool ConnectedClient::notifyClientPermList(ClientDbId cldbid, const std::shared_ptr& mgr, bool perm_sids) { +bool ConnectedClient::notifyClientPermList(ClientDbId cldbid, const std::shared_ptr& mgr, bool perm_sids) { Command res(this->getExternalType() == CLIENT_TEAMSPEAK ? "notifyclientpermlist" : ""); auto permissions = mgr->permissions(); diff --git a/server/src/client/DataClient.h b/server/src/client/DataClient.h index 48fc12e..9af04e8 100644 --- a/server/src/client/DataClient.h +++ b/server/src/client/DataClient.h @@ -102,7 +102,7 @@ namespace ts { sql::SqlManager* sql; std::shared_ptr server; - std::shared_ptr clientPermissions = nullptr; + std::shared_ptr clientPermissions = nullptr; std::shared_ptr _properties; std::shared_ptr currentChannel = nullptr; diff --git a/server/src/client/command_handler/channel.cpp b/server/src/client/command_handler/channel.cpp index 3c430ee..0842fa7 100644 --- a/server/src/client/command_handler/channel.cpp +++ b/server/src/client/command_handler/channel.cpp @@ -8,7 +8,7 @@ #include "../../server/file/FileServer.h" #include "../../server/VoiceServer.h" #include "../voice/VoiceClient.h" -#include "PermissionManager.h" +#include "PermissionRegister.h" #include "../../InstanceHandler.h" #include "../../server/QueryServer.h" #include "../file/FileClient.h" @@ -32,7 +32,7 @@ using namespace std::chrono; using namespace std; using namespace ts; using namespace ts::server; -using namespace ts::token; +using namespace ts::server::tokens; //{findError("parameter_invalid"), "could not resolve permission " + (cmd[index].has("permid") ? cmd[index]["permid"].as() : cmd[index]["permsid"].as())}; \ //TODO: Log missing permissions? diff --git a/server/src/client/command_handler/client.cpp b/server/src/client/command_handler/client.cpp index a8779ea..3800eda 100644 --- a/server/src/client/command_handler/client.cpp +++ b/server/src/client/command_handler/client.cpp @@ -7,7 +7,7 @@ #include "../InternalClient.h" #include "../../server/file/FileServer.h" #include "../voice/VoiceClient.h" -#include "PermissionManager.h" +#include "PermissionRegister.h" #include "../../InstanceHandler.h" #include "../../server/QueryServer.h" #include "../file/FileClient.h" @@ -32,7 +32,7 @@ using namespace std::chrono; using namespace std; using namespace ts; using namespace ts::server; -using namespace ts::token; +using namespace ts::server::tokens; #define QUERY_PASSWORD_LENGTH 12 diff --git a/server/src/client/command_handler/file.cpp b/server/src/client/command_handler/file.cpp index cb08cbf..a759030 100644 --- a/server/src/client/command_handler/file.cpp +++ b/server/src/client/command_handler/file.cpp @@ -16,7 +16,7 @@ #include "../../server/file/FileServer.h" #include "../../server/VoiceServer.h" #include "../voice/VoiceClient.h" -#include "PermissionManager.h" +#include "PermissionRegister.h" #include "../../InstanceHandler.h" #include "../../server/QueryServer.h" #include "../file/FileClient.h" @@ -48,7 +48,7 @@ using namespace std::chrono; using namespace std; using namespace ts; using namespace ts::server; -using namespace ts::token; +using namespace ts::server::tokens; #define QUERY_PASSWORD_LENGTH 12 diff --git a/server/src/client/command_handler/misc.cpp b/server/src/client/command_handler/misc.cpp index ecca6e2..c424398 100644 --- a/server/src/client/command_handler/misc.cpp +++ b/server/src/client/command_handler/misc.cpp @@ -16,7 +16,7 @@ #include "../../server/file/FileServer.h" #include "../../server/VoiceServer.h" #include "../voice/VoiceClient.h" -#include "PermissionManager.h" +#include "PermissionRegister.h" #include "../../InstanceHandler.h" #include "../../server/QueryServer.h" #include "../file/FileClient.h" @@ -48,7 +48,7 @@ using namespace std::chrono; using namespace std; using namespace ts; using namespace ts::server; -using namespace ts::token; +using namespace ts::server::tokens; #define QUERY_PASSWORD_LENGTH 12 @@ -460,6 +460,7 @@ command_result ConnectedClient::handleCommandSetClientChannelGroup(Command &cmd) } } + //TODO: When using new VS model, check for a temporary assignment! this->server->groups->setChannelGroup(target_cldbid, serverGroup, channel); for (const auto &targetClient : this->server->findClientsByCldbId(target_cldbid)) { unique_lock client_channel_lock_w(targetClient->channel_lock); diff --git a/server/src/client/command_handler/music.cpp b/server/src/client/command_handler/music.cpp index 1b8e161..cab6fff 100644 --- a/server/src/client/command_handler/music.cpp +++ b/server/src/client/command_handler/music.cpp @@ -16,7 +16,7 @@ #include "../../server/file/FileServer.h" #include "../../server/VoiceServer.h" #include "../voice/VoiceClient.h" -#include "PermissionManager.h" +#include "PermissionRegister.h" #include "../../InstanceHandler.h" #include "../../server/QueryServer.h" #include "../file/FileClient.h" @@ -49,7 +49,7 @@ using namespace std::chrono; using namespace std; using namespace ts; using namespace ts::server; -using namespace ts::token; +using namespace ts::server::tokens; command_result ConnectedClient::handleCommandMusicBotCreate(Command& cmd) { if(!config::music::enabled) return command_result{error::music_disabled}; diff --git a/server/src/client/command_handler/server.cpp b/server/src/client/command_handler/server.cpp index 62ae0b5..4520c4e 100644 --- a/server/src/client/command_handler/server.cpp +++ b/server/src/client/command_handler/server.cpp @@ -16,7 +16,7 @@ #include "../../server/file/FileServer.h" #include "../../server/VoiceServer.h" #include "../voice/VoiceClient.h" -#include "PermissionManager.h" +#include "PermissionRegister.h" #include "../../InstanceHandler.h" #include "../../server/QueryServer.h" #include "../file/FileClient.h" @@ -48,7 +48,7 @@ using namespace std::chrono; using namespace std; using namespace ts; using namespace ts::server; -using namespace ts::token; +using namespace ts::server::tokens; #define QUERY_PASSWORD_LENGTH 12 diff --git a/server/src/client/query/QueryClient.h b/server/src/client/query/QueryClient.h index 6d84a55..127b61b 100644 --- a/server/src/client/query/QueryClient.h +++ b/server/src/client/query/QueryClient.h @@ -139,9 +139,15 @@ namespace ts::server { bool notifyClientLeftView(const std::deque> &deque, const std::string &string, bool b, const ViewReasonServerLeftT &t) override; - bool notifyClientLeftViewKicked(const std::shared_ptr &client, const std::shared_ptr &target_channel, const std::string &message, std::shared_ptr invoker, bool lock_channel_tree) override; + bool notifyClientLeftViewKicked(const std::shared_ptr &client, ChannelId channel_from, + const std::string &message, std::shared_ptr invoker, + bool lock_channel_tree, + const std::shared_ptr &target_channel) override; - bool notifyClientLeftViewBanned(const std::shared_ptr &client, const std::string &message, std::shared_ptr invoker, size_t length, bool lock_channel_tree) override; + bool notifyClientLeftViewBanned(const std::shared_ptr &client, ChannelId channel_from, + std::shared_ptr invoker, size_t length, + bool lock_channel_tree, + const std::string &message) override; private: command_result handleCommandExit(Command&); diff --git a/server/src/client/query/QueryClientNotify.cpp b/server/src/client/query/QueryClientNotify.cpp index 6f45233..f30ba8b 100644 --- a/server/src/client/query/QueryClientNotify.cpp +++ b/server/src/client/query/QueryClientNotify.cpp @@ -175,7 +175,10 @@ bool QueryClient::notifyClientLeftView(const std::deque &client, const std::shared_ptr &target_channel, const std::string &message, std::shared_ptr invoker, bool lock_channel_tree) { +bool QueryClient::notifyClientLeftViewKicked(const std::shared_ptr &client, ChannelId channel_from, + const std::string &message, std::shared_ptr invoker, + bool lock_channel_tree, + const std::shared_ptr &target_channel) { if(!this->eventActive(QueryEventGroup::QEVENTGROUP_CLIENT_VIEW, QueryEventSpecifier::QEVENTSPECIFIER_CLIENT_VIEW_LEAVE)) { this->visibleClients.erase(std::remove_if(this->visibleClients.begin(), this->visibleClients.end(), [&, client](const weak_ptr& weak) { auto c = weak.lock(); @@ -187,10 +190,13 @@ bool QueryClient::notifyClientLeftViewKicked(const std::shared_ptrvisibleClients.end()); return true; } - return ConnectedClient::notifyClientLeftViewKicked(client, target_channel, message, invoker, lock_channel_tree); + return ConnectedClient::notifyClientLeftViewKicked(client, channel_from, message, invoker, lock_channel_tree, target_channel); } -bool QueryClient::notifyClientLeftViewBanned(const std::shared_ptr &client, const std::string &message, std::shared_ptr invoker, size_t length, bool lock_channel_tree) { +bool QueryClient::notifyClientLeftViewBanned(const std::shared_ptr &client, ChannelId channel_from, + std::shared_ptr invoker, size_t length, + bool lock_channel_tree, + const std::string &message) { if(!this->eventActive(QueryEventGroup::QEVENTGROUP_CLIENT_VIEW, QueryEventSpecifier::QEVENTSPECIFIER_CLIENT_VIEW_LEAVE)) { this->visibleClients.erase(std::remove_if(this->visibleClients.begin(), this->visibleClients.end(), [&, client](const weak_ptr& weak) { auto c = weak.lock(); @@ -202,7 +208,7 @@ bool QueryClient::notifyClientLeftViewBanned(const std::shared_ptrvisibleClients.end()); return true; } - return ConnectedClient::notifyClientLeftViewBanned(client, message, invoker, length, lock_channel_tree); + return ConnectedClient::notifyClientLeftViewBanned(client, channel_from, invoker, length, lock_channel_tree, message); } bool QueryClient::notifyMusicQueueAdd(const std::shared_ptr &bot, const std::shared_ptr &entry, int index, const std::shared_ptr &invoker) { diff --git a/server/src/client/web/WebClient.cpp b/server/src/client/web/WebClient.cpp index 6d04ae9..300f09e 100644 --- a/server/src/client/web/WebClient.cpp +++ b/server/src/client/web/WebClient.cpp @@ -629,7 +629,7 @@ bool WebClient::disconnect(const std::string &reason) { unique_lock server_channel_lock(this->server->channel_tree_lock); { unique_lock own_lock(this->channel_lock); - this->notifyClientLeftViewKicked(this->ref(), nullptr, reason, this->server->serverRoot, false); + this->notifyClientLeftViewKicked(this->ref(), this->currentChannel ? this->currentChannel->channelId() : 0, reason, this->server->serverRoot, false, nullptr); } this->server->client_move(this->ref(), nullptr, this->server->serverRoot, reason, ViewReasonId::VREASON_SERVER_KICK, false, server_channel_lock); this->server->unregisterClient(_this.lock(), "disconnected", server_channel_lock); diff --git a/server/src/groups/Group.cpp b/server/src/groups/Group.cpp new file mode 100644 index 0000000..7024a9d --- /dev/null +++ b/server/src/groups/Group.cpp @@ -0,0 +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; +} \ No newline at end of file diff --git a/server/src/groups/Group.h b/server/src/groups/Group.h new file mode 100644 index 0000000..5fb7447 --- /dev/null +++ b/server/src/groups/Group.h @@ -0,0 +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 { }; +} \ No newline at end of file diff --git a/server/src/groups/GroupAssignmentManager.cpp b/server/src/groups/GroupAssignmentManager.cpp new file mode 100644 index 0000000..647f1b2 --- /dev/null +++ b/server/src/groups/GroupAssignmentManager.cpp @@ -0,0 +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; +} \ No newline at end of file diff --git a/server/src/groups/GroupAssignmentManager.h b/server/src/groups/GroupAssignmentManager.h new file mode 100644 index 0000000..2079261 --- /dev/null +++ b/server/src/groups/GroupAssignmentManager.h @@ -0,0 +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(); + }; + + } +} \ No newline at end of file diff --git a/server/src/groups/GroupManager.cpp b/server/src/groups/GroupManager.cpp new file mode 100644 index 0000000..499c4ec --- /dev/null +++ b/server/src/groups/GroupManager.cpp @@ -0,0 +1,5 @@ +// +// Created by WolverinDEV on 03/03/2020. +// + +#include "GroupManager.h" diff --git a/server/src/groups/GroupManager.h b/server/src/groups/GroupManager.h new file mode 100644 index 0000000..7aa0355 --- /dev/null +++ b/server/src/groups/GroupManager.h @@ -0,0 +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 */); + }; + } +} \ No newline at end of file diff --git a/server/src/manager/BanManager.cpp b/server/src/manager/BanManager.cpp index 25fb22a..16129f1 100644 --- a/server/src/manager/BanManager.cpp +++ b/server/src/manager/BanManager.cpp @@ -9,6 +9,7 @@ using namespace std; using namespace std::chrono; using namespace ts; using namespace ts::server; +using namespace ts::server::bans; BanManager::BanManager(sql::SqlManager* handle) : sql(handle) {} diff --git a/server/src/manager/BanManager.h b/server/src/manager/BanManager.h index cd3a366..c6cfecf 100644 --- a/server/src/manager/BanManager.h +++ b/server/src/manager/BanManager.h @@ -10,8 +10,7 @@ #include #include -namespace ts { - namespace server { +namespace ts::server { enum BanStringType { BST_WILD_V4, BST_WILD_V6, @@ -19,91 +18,92 @@ namespace ts { BST_REGEX }; } -} DEFINE_VARIABLE_TRANSFORM(ts::server::BanStringType, VARTYPE_INT, std::to_string((uint8_t) in), static_cast(in.as())); -namespace ts { - namespace server { +namespace ts::server { class VirtualServer; class ConnectedClient; - struct BanRecord { - ServerId serverId; - BanId banId; + namespace bans { + struct BanRecord { + ServerId serverId; + BanId banId; - ClientDbId invokerDbId; - std::string invokerUid; - std::string invokerName; + ClientDbId invokerDbId; + std::string invokerUid; + std::string invokerName; - std::string hwid; - std::string uid; + std::string hwid; + std::string uid; - std::string name; - std::string ip; - BanStringType strType; + std::string name; + std::string ip; + BanStringType strType; - std::chrono::time_point created; - std::chrono::time_point until; + std::chrono::time_point created; + std::chrono::time_point until; - std::string reason; + std::string reason; - size_t triggered; - }; + size_t triggered; + }; - struct BanTrigger { - ServerId server_id; - BanId ban_id; - std::chrono::system_clock::time_point timestamp; + struct BanTrigger { + ServerId server_id; + BanId ban_id; + std::chrono::system_clock::time_point timestamp; - std::string unique_id; - std::string hardware_id; - std::string name; - std::string ip; - }; + std::string unique_id; + std::string hardware_id; + std::string name; + std::string ip; + }; - class BanManager { - public: - BanManager(sql::SqlManager*); - ~BanManager(); + class BanManager { + public: + BanManager(sql::SqlManager*); + BanManager(const BanManager&) = delete; + BanManager(BanManager&&) = delete; + ~BanManager(); - bool loadBans(); + bool loadBans(); - std::deque> listBans(ServerId); + std::deque> listBans(ServerId); - std::shared_ptr findBanById(ServerId, uint64_t banId); - std::shared_ptr findBanByHwid(ServerId, std::string hwid); - std::shared_ptr findBanByUid(ServerId, std::string uid); - std::shared_ptr findBanByIp(ServerId, std::string ip); - std::shared_ptr findBanByName(ServerId, std::string nickName); - std::shared_ptr findBanExact(ServerId, - const std::string& /* reason */, - const std::string& /* uid */, - const std::string& /* ip */, - const std::string& /* display name */, - const std::string& /* hardware id */); + std::shared_ptr findBanById(ServerId, uint64_t banId); + std::shared_ptr findBanByHwid(ServerId, std::string hwid); + std::shared_ptr findBanByUid(ServerId, std::string uid); + std::shared_ptr findBanByIp(ServerId, std::string ip); + std::shared_ptr findBanByName(ServerId, std::string nickName); + std::shared_ptr findBanExact(ServerId, + const std::string& /* reason */, + const std::string& /* uid */, + const std::string& /* ip */, + const std::string& /* display name */, + const std::string& /* hardware id */); - void deleteAllBans(ServerId sid); + void deleteAllBans(ServerId sid); - BanId registerBan(ServerId, ClientDbId invoker, std::string reason, std::string uid, std::string ip, std::string nickName, std::string hwid, std::chrono::time_point until); - void unban(ServerId, BanId); - void unban(std::shared_ptr); - void updateBan(std::shared_ptr); + BanId registerBan(ServerId, ClientDbId invoker, std::string reason, std::string uid, std::string ip, std::string nickName, std::string hwid, std::chrono::time_point until); + void unban(ServerId, BanId); + void unban(std::shared_ptr); + void updateBan(std::shared_ptr); - void updateBanReason(std::shared_ptr, std::string); - void updateBanTimeout(std::shared_ptr, std::chrono::time_point); + void updateBanReason(std::shared_ptr, std::string); + void updateBanTimeout(std::shared_ptr, std::chrono::time_point); - void trigger_ban(const std::shared_ptr& /* record */, - ServerId /* server */, - const std::string& /* unique id */, - const std::string& /* hardware id */, - const std::string& /* nickname */, - const std::string& /* ip */ - ); - std::deque> trigger_list(const std::shared_ptr& /* record */, ServerId /* server id */, ssize_t /* offset */, ssize_t /* limit */); + void trigger_ban(const std::shared_ptr& /* record */, + ServerId /* server */, + const std::string& /* unique id */, + const std::string& /* hardware id */, + const std::string& /* nickname */, + const std::string& /* ip */ + ); + std::deque> trigger_list(const std::shared_ptr& /* record */, ServerId /* server id */, ssize_t /* offset */, ssize_t /* limit */); - private: - sql::SqlManager* sql = nullptr; - std::atomic current_ban_index; - }; - } -} \ No newline at end of file + private: + sql::SqlManager* sql = nullptr; + std::atomic current_ban_index; + }; + } + } \ No newline at end of file diff --git a/server/src/manager/PermissionNameMapper.h b/server/src/manager/PermissionNameMapper.h index 6f778df..2972bc2 100644 --- a/server/src/manager/PermissionNameMapper.h +++ b/server/src/manager/PermissionNameMapper.h @@ -1,7 +1,7 @@ #pragma once #include -#include "PermissionManager.h" +#include "PermissionRegister.h" #include "Definitions.h" namespace ts { diff --git a/server/src/manager/SqlDataManager.cpp b/server/src/manager/SqlDataManager.cpp index 51f2ab1..809626e 100644 --- a/server/src/manager/SqlDataManager.cpp +++ b/server/src/manager/SqlDataManager.cpp @@ -7,7 +7,7 @@ #include #include #include -#include +#include #include "SqlDataManager.h" using namespace std; @@ -412,6 +412,7 @@ ROLLBACK; CREATE_INDEX2R("conversation_blocks", "server_id", "conversation_id"); db_version(11); default: + //CREATE TABLE `assigned_channel_groups` (`server_id` INT NOT NULL, `cldbid` INT NOT NULL, `channel_id` INT NOT NULL, `group_id` INT, `until` BIGINT DEFAULT -1, PRIMARY KEY `server_id`, `cldbid`, `channel_id`) break; } diff --git a/server/src/manager/TokeManager.cpp b/server/src/manager/TokeManager.cpp index 3fa1a1b..ebde545 100644 --- a/server/src/manager/TokeManager.cpp +++ b/server/src/manager/TokeManager.cpp @@ -15,7 +15,7 @@ using namespace std; using namespace std::chrono; using namespace ts; using namespace ts::server; -using namespace ts::token; +using namespace ts::server::tokens; TokenManager::TokenManager(server::VirtualServer* handle) : handle(handle) { @@ -30,7 +30,7 @@ bool TokenManager::loadTokens() { } int TokenManager::loadTokenFromDb(int length, char **values, char **columns) { - std::string token, description = ""; + std::string token{}, description{}; auto type = static_cast(0xFF); GroupId group = 0; ChannelId chId = 0; diff --git a/server/src/manager/TokeManager.h b/server/src/manager/TokeManager.h index 891ed46..1a5458f 100644 --- a/server/src/manager/TokeManager.h +++ b/server/src/manager/TokeManager.h @@ -11,7 +11,7 @@ namespace ts { class VirtualServer; } - namespace token { + namespace server::tokens { enum TokenType : uint8_t { TOKEN_SERVER, TOKEN_CHANNEL @@ -44,4 +44,4 @@ namespace ts { }; } } -DEFINE_VARIABLE_TRANSFORM(ts::token::TokenType, VARTYPE_INT, std::to_string((uint8_t) in), static_cast(in.as())); \ No newline at end of file +DEFINE_VARIABLE_TRANSFORM(ts::server::tokens::TokenType, VARTYPE_INT, std::to_string((uint8_t) in), static_cast(in.as())); \ No newline at end of file diff --git a/server/src/music/MusicPlaylist.cpp b/server/src/music/MusicPlaylist.cpp index 2787502..0ea0f93 100644 --- a/server/src/music/MusicPlaylist.cpp +++ b/server/src/music/MusicPlaylist.cpp @@ -13,7 +13,7 @@ using namespace ts::music; using namespace std; using namespace std::chrono; -Playlist::Playlist(const std::shared_ptr &manager, std::shared_ptr properties, std::shared_ptr permissions) : +Playlist::Playlist(const std::shared_ptr &manager, std::shared_ptr properties, std::shared_ptr permissions) : PlaylistPermissions{std::move(permissions)}, _properties{std::move(properties)}, manager{manager} { } Playlist::~Playlist() { diff --git a/server/src/music/MusicPlaylist.h b/server/src/music/MusicPlaylist.h index 7b9f006..7751693 100644 --- a/server/src/music/MusicPlaylist.h +++ b/server/src/music/MusicPlaylist.h @@ -90,7 +90,7 @@ namespace ts { }; }; - Playlist(const std::shared_ptr& /* manager */, std::shared_ptr /* properties */, std::shared_ptr /* permissions */); + Playlist(const std::shared_ptr& /* manager */, std::shared_ptr /* properties */, std::shared_ptr /* permissions */); virtual ~Playlist(); virtual void load_songs(); diff --git a/server/src/music/PlayablePlaylist.cpp b/server/src/music/PlayablePlaylist.cpp index aede933..5e3211a 100644 --- a/server/src/music/PlayablePlaylist.cpp +++ b/server/src/music/PlayablePlaylist.cpp @@ -11,7 +11,7 @@ using namespace ts::music; using namespace std; using namespace std::chrono; -PlayablePlaylist::PlayablePlaylist(const std::shared_ptr &handle, const std::shared_ptr &props, const std::shared_ptr& perms) : Playlist(handle, props, perms) { } +PlayablePlaylist::PlayablePlaylist(const std::shared_ptr &handle, const std::shared_ptr &props, const std::shared_ptr& perms) : Playlist(handle, props, perms) { } PlayablePlaylist::~PlayablePlaylist() {} diff --git a/server/src/music/PlayablePlaylist.h b/server/src/music/PlayablePlaylist.h index a282004..93388bf 100644 --- a/server/src/music/PlayablePlaylist.h +++ b/server/src/music/PlayablePlaylist.h @@ -24,7 +24,7 @@ namespace ts { SHUFFLE }; }; - PlayablePlaylist(const std::shared_ptr& /* manager */, const std::shared_ptr& /* properties */, const std::shared_ptr& /* permissions */); + PlayablePlaylist(const std::shared_ptr& /* manager */, const std::shared_ptr& /* properties */, const std::shared_ptr& /* permissions */); virtual ~PlayablePlaylist(); void load_songs() override; diff --git a/server/src/music/PlaylistPermissions.cpp b/server/src/music/PlaylistPermissions.cpp index a87dc97..79089af 100644 --- a/server/src/music/PlaylistPermissions.cpp +++ b/server/src/music/PlaylistPermissions.cpp @@ -9,7 +9,7 @@ using namespace ts; using namespace ts::music; -PlaylistPermissions::PlaylistPermissions(std::shared_ptr permissions) : _permissions{std::move(permissions)} {} +PlaylistPermissions::PlaylistPermissions(std::shared_ptr permissions) : _permissions{std::move(permissions)} {} permission::v2::PermissionFlaggedValue PlaylistPermissions::calculate_client_specific_permissions(ts::permission::PermissionType permission, const std::shared_ptr& client) { auto val = this->_permissions->channel_permission(permission, client->getClientDatabaseId()); diff --git a/server/src/music/PlaylistPermissions.h b/server/src/music/PlaylistPermissions.h index b73a7fc..0f6e35d 100644 --- a/server/src/music/PlaylistPermissions.h +++ b/server/src/music/PlaylistPermissions.h @@ -1,6 +1,6 @@ #pragma once -#include +#include namespace ts::server { class VirtualServer; @@ -14,15 +14,15 @@ namespace ts::music { ignore_playlist_owner, do_no_require_granted }; - PlaylistPermissions(std::shared_ptr permissions); + PlaylistPermissions(std::shared_ptr permissions); - inline const std::shared_ptr& permission_manager() const { return this->_permissions; } + inline const std::shared_ptr& permission_manager() const { return this->_permissions; } /* returns permission::ok if client has permissions */ permission::PermissionType client_has_permissions(const std::shared_ptr& client, permission::PermissionType needed_permission, permission::PermissionType granted_permission, uint8_t /* ignore playlist owner */ = 0); permission::v2::PermissionFlaggedValue calculate_client_specific_permissions(permission::PermissionType /* permission */, const std::shared_ptr& /* client */); protected: - const std::shared_ptr _permissions; + const std::shared_ptr _permissions; virtual bool is_playlist_owner(ClientDbId /* database id */) const = 0; private: diff --git a/server/src/services/ClientChannelService.cpp b/server/src/services/ClientChannelService.cpp new file mode 100644 index 0000000..67d1c63 --- /dev/null +++ b/server/src/services/ClientChannelService.cpp @@ -0,0 +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; +} \ No newline at end of file diff --git a/server/src/services/ClientChannelService.h b/server/src/services/ClientChannelService.h new file mode 100644 index 0000000..5f1f161 --- /dev/null +++ b/server/src/services/ClientChannelService.h @@ -0,0 +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; + }; +} \ No newline at end of file diff --git a/server/src/services/PermissionsService.cpp b/server/src/services/PermissionsService.cpp new file mode 100644 index 0000000..9cdcd48 --- /dev/null +++ b/server/src/services/PermissionsService.cpp @@ -0,0 +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); +} \ No newline at end of file diff --git a/server/src/services/PermissionsService.h b/server/src/services/PermissionsService.h new file mode 100644 index 0000000..eab5829 --- /dev/null +++ b/server/src/services/PermissionsService.h @@ -0,0 +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 */); + }; +} \ No newline at end of file diff --git a/server/src/vserver/DefaultServer.cpp b/server/src/vserver/DefaultServer.cpp new file mode 100644 index 0000000..1570927 --- /dev/null +++ b/server/src/vserver/DefaultServer.cpp @@ -0,0 +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. */ +} diff --git a/server/src/vserver/DefaultServer.h b/server/src/vserver/DefaultServer.h new file mode 100644 index 0000000..dfc368c --- /dev/null +++ b/server/src/vserver/DefaultServer.h @@ -0,0 +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; + }; +} \ No newline at end of file diff --git a/server/src/vserver/VirtualServer.cpp b/server/src/vserver/VirtualServer.cpp new file mode 100644 index 0000000..dc49ffb --- /dev/null +++ b/server/src/vserver/VirtualServer.cpp @@ -0,0 +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 + +} \ No newline at end of file diff --git a/server/src/vserver/VirtualServer.h b/server/src/vserver/VirtualServer.h new file mode 100644 index 0000000..164785b --- /dev/null +++ b/server/src/vserver/VirtualServer.h @@ -0,0 +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; + }; +} \ No newline at end of file diff --git a/server/src/vserver/VirtualServerBase.cpp b/server/src/vserver/VirtualServerBase.cpp new file mode 100644 index 0000000..79f4ceb --- /dev/null +++ b/server/src/vserver/VirtualServerBase.cpp @@ -0,0 +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 +} \ No newline at end of file diff --git a/server/src/vserver/VirtualServerBase.h b/server/src/vserver/VirtualServerBase.h new file mode 100644 index 0000000..feb7575 --- /dev/null +++ b/server/src/vserver/VirtualServerBase.h @@ -0,0 +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; + }; +} \ No newline at end of file diff --git a/server/thread_concept b/server/thread_concept index 9875e9d..9d95dc1 100644 --- a/server/thread_concept +++ b/server/thread_concept @@ -14,4 +14,13 @@ Web client: IO-Thread: ?? Executor thread: - - \ No newline at end of file + - + + + +Definition Services: +Services only provide information or are helper for certain action. +They do not save any states or variables. + +Definition Manager: +Unlike services managers are controlling data in memory and/or within the database. \ No newline at end of file diff --git a/shared b/shared index ece70e4..ae4751e 160000 --- a/shared +++ b/shared @@ -1 +1 @@ -Subproject commit ece70e4df474a5e027e3c54e8a8e1be84a2a6a72 +Subproject commit ae4751ee6fcb857771ff5f4e57459a75638e2b1f