Some more changes

This commit is contained in:
WolverinDEV 2020-03-05 14:10:22 +01:00
parent 3a5c152b3c
commit 6172628247
21 changed files with 1072 additions and 106 deletions

View File

@ -160,7 +160,7 @@ if (COMPILE_WEB_CLIENT)
src/client/web/WSWebClient.cpp src/client/web/WSWebClient.cpp
src/client/web/SampleHandler.cpp src/client/web/SampleHandler.cpp
src/client/web/VoiceBridge.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/groups/GroupManager.cpp src/groups/GroupManager.h src/groups/GroupAssignmentManager.cpp src/groups/GroupAssignmentManager.h src/groups/Group.cpp src/groups/Group.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 src/services/VirtualServerInformation.cpp src/services/VirtualServerInformation.h src/vserver/VirtualServerManager.cpp src/vserver/VirtualServerManager.h)
endif () endif ()
add_executable(PermHelper helpers/permgen.cpp) add_executable(PermHelper helpers/permgen.cpp)

View File

@ -1,58 +1,85 @@
CommandResult handleCommandClientUpdate(Command&); General lock order:
CommandResult handleCommandClientEdit(Command&); - Client execute lock
CommandResult handleCommandClientEdit(Command&, const std::shared_ptr<ConnectedClient>& /* target */); - Server state lock
CommandResult handleCommandClientMove(Command&); - Server channel tree
CommandResult handleCommandClientGetVariables(Command&); - Client channel tree
CommandResult handleCommandClientKick(Command&);
CommandResult handleCommandClientPoke(Command&);
CommandResult handleCommandChannelSubscribe(Command&); read lock: server channel tree => client lock write When executing a command:
CommandResult handleCommandChannelSubscribeAll(Command&); read lock: server channel tree => client lock write Lock order:
CommandResult handleCommandChannelUnsubscribe(Command&); read lock: server channel tree => client lock write - Client execute lock
CommandResult handleCommandChannelUnsubscribeAll(Command&); read lock: server channel tree => client lock write - Server state lock (Server should not try to change state while a client is executing something)
CommandResult handleCommandChannelCreate(Command&); write lock server channel tree => iterate clients lock (write) Notes:
CommandResult handleCommandChannelDelete(Command&); write lock server channel tree => iterate clients lock (write) The server might be null or the default server.
CommandResult handleCommandChannelEdit(Command&); write lock server channel tree This must be checked via the given macro!
CommandResult handleCommandChannelGetDescription(Command&); read lock: server channel tree
CommandResult handleCommandChannelMove(Command&); write lock server channel tree
CommandResult handleCommandChannelAddPerm(Command&); read lock: server channel tree => all clients write lock Disconnect/unregister a client:
CommandResult handleCommandChannelDelPerm(Command&); read lock: server channel tree => all clients write lock - Lock client execute lock
- Lock server state
- Unregister client from server and set server variable to nullptr
- Underlying server (UDP, Web, etc. disconnect)
CommandResult handleCommandChannelGroupDel(Command&); read lock: server channel tree => all clients in the group should not be allowed to switch a channel Client channel tree connect/mode/disconnect:
CommandResult handleCommandSetClientChannelGroup(Command&); read lock: server channel tree => client should not be allowed to switch channel - Lock client execute lock
- Lock server channel tree in write mode
- Lock client channel tree in write mode
- Move client & notify (notify required all clients to have their channel tree locked in shared mode)
CommandResult handleCommandPluginCmd(Command&); Command lock overview:
CommandResult handleCommandClientUpdate(Command&);
CommandResult handleCommandClientEdit(Command&);
CommandResult handleCommandClientEdit(Command&, const std::shared_ptr<ConnectedClient>& /* target */);
CommandResult handleCommandClientMove(Command&);
CommandResult handleCommandClientGetVariables(Command&);
CommandResult handleCommandClientKick(Command&);
CommandResult handleCommandClientPoke(Command&);
CommandResult handleCommandClientMute(Command&); CommandResult handleCommandChannelSubscribe(Command&); read lock: server channel tree => client lock write
CommandResult handleCommandClientUnmute(Command&); CommandResult handleCommandChannelSubscribeAll(Command&); read lock: server channel tree => client lock write
CommandResult handleCommandChannelUnsubscribe(Command&); read lock: server channel tree => client lock write
CommandResult handleCommandChannelUnsubscribeAll(Command&); read lock: server channel tree => client lock write
CommandResult handleCommandChannelCreate(Command&); write lock server channel tree => iterate clients lock (write)
CommandResult handleCommandChannelDelete(Command&); write lock server channel tree => iterate clients lock (write)
CommandResult handleCommandChannelEdit(Command&); write lock server channel tree
CommandResult handleCommandChannelGetDescription(Command&); read lock: server channel tree
CommandResult handleCommandChannelMove(Command&); write lock server channel tree
//Original from query but still reachable for all CommandResult handleCommandChannelAddPerm(Command&); read lock: server channel tree => all clients write lock
CommandResult handleCommandClientList(Command&); CommandResult handleCommandChannelDelPerm(Command&); read lock: server channel tree => all clients write lock
CommandResult handleCommandClientFind(Command&); CommandResult handleCommandChannelGroupDel(Command&); read lock: server channel tree => all clients in the group should not be allowed to switch a channel
CommandResult handleCommandClientInfo(Command&); CommandResult handleCommandSetClientChannelGroup(Command&); read lock: server channel tree => client should not be allowed to switch channel
CommandResult handleCommandVerifyChannelPassword(Command&); read lock: server channel tree CommandResult handleCommandPluginCmd(Command&);
handleCommandChannelFind read lock: server channel tree CommandResult handleCommandClientMute(Command&);
handleCommandChannelInfo read lock: server channel tree CommandResult handleCommandClientUnmute(Command&);
General command handling: client_command_lock //Original from query but still reachable for all
CommandResult handleCommandClientList(Command&);
CommandResult handleCommandClientFind(Command&);
CommandResult handleCommandClientInfo(Command&);
CommandResult handleCommandVerifyChannelPassword(Command&); read lock: server channel tree
handleCommandChannelFind read lock: server channel tree
handleCommandChannelInfo read lock: server channel tree
General command handling: client_command_lock
Ensure that only one command at time will be handeled Ensure that only one command at time will be handeled
Read access server channel tree: read lock channel_tree_lock Read access server channel tree: read lock channel_tree_lock
Write access server channel tree: lock channel_tree_lock Write access server channel tree: lock channel_tree_lock
Write access client channel tree: read lock channel_tree_lock => lock client channel tree Write access client channel tree: read lock channel_tree_lock => lock client channel tree
if we write to the server channel tree no client should have their channel tree updated if we write to the server channel tree no client should have their channel tree updated
Read access client channel tree: no lock required Read access client channel tree: no lock required
Note: the server channel tree should not be accessed! Note: the server channel tree should not be accessed!
Move client acts like access server channel tree: write lock channel_tree_lock => for each client write lock their tree Move client acts like access server channel tree: write lock channel_tree_lock => for each client write lock their tree
TODO: Some kind of perm channel lock TODO: Some kind of perm channel lock
TODO: Fix handleCommandChannelEdit TODO: Fix handleCommandChannelEdit
Test: Channel hide & show with clients! Multible clients as well! Test: Channel hide & show with clients! Multiple clients as well!

View File

@ -159,10 +159,9 @@ void DatabaseHelper::deleteClient(const std::shared_ptr<VirtualServer>& server,
//TODO delete complains //TODO delete complains
} }
inline sql::result load_permissions_v2(const std::shared_ptr<VirtualServer>& server, v2::PermissionRegister* manager, sql::command& command, bool test_channel /* only used for client permissions (client channel permissions) */) { inline sql::result load_permissions_v2(VirtualServerId server_id, v2::PermissionRegister* manager, sql::command& command, bool test_channel /* only used for client permissions (client channel permissions) */) {
auto start = system_clock::now(); auto start = system_clock::now();
auto server_id = server ? server->getServerId() : 0;
return command.query([&](int length, char** values, char** names){ return command.query([&](int length, char** values, char** names){
permission::PermissionType key = permission::PermissionType::undefined; permission::PermissionType key = permission::PermissionType::undefined;
permission::PermissionValue value = permNotGranted, granted = permNotGranted; permission::PermissionValue value = permNotGranted, granted = permNotGranted;
@ -198,6 +197,8 @@ inline sql::result load_permissions_v2(const std::shared_ptr<VirtualServer>& ser
return 0; return 0;
} }
if(channel_id > 0 && test_channel) { if(channel_id > 0 && test_channel) {
//TODO: Test for invalid channels
/*
if(!server) if(!server)
logError(server_id, "[SQL] Cant find channel for channel bound permission (No server given)! Command: {} ChannelID: {}", command.sqlCommand(), values[index]); logError(server_id, "[SQL] Cant find channel for channel bound permission (No server given)! Command: {} ChannelID: {}", command.sqlCommand(), values[index]);
else { else {
@ -205,6 +206,7 @@ inline sql::result load_permissions_v2(const std::shared_ptr<VirtualServer>& ser
if(!channel) if(!channel)
logError(server_id, "[SQL] Cant find channel for channel bound permission! Command: {} ChannelID: {}", command.sqlCommand(), values[index]); logError(server_id, "[SQL] Cant find channel for channel bound permission! Command: {} ChannelID: {}", command.sqlCommand(), values[index]);
} }
*/
} }
@ -282,7 +284,7 @@ std::shared_ptr<v2::PermissionRegister> DatabaseHelper::loadClientPermissionMana
variable{":serverId", server ? server->getServerId() : 0}, variable{":serverId", server ? server->getServerId() : 0},
variable{":type", permission::SQL_PERM_USER}, variable{":type", permission::SQL_PERM_USER},
variable{":id", cldbid}); variable{":id", cldbid});
LOG_SQL_CMD(load_permissions_v2(server, permission_manager.get(), command, true)); LOG_SQL_CMD(load_permissions_v2(server ? server->getServerId() : 0, permission_manager.get(), command, true));
} }
@ -336,14 +338,14 @@ void DatabaseHelper::saveClientPermissions(const std::shared_ptr<ts::server::Vir
} }
std::shared_ptr<permission::v2::PermissionRegister> DatabaseHelper::loadGroupPermissions(const std::shared_ptr<VirtualServer>& server, ts::GroupId group_id) { std::shared_ptr<permission::v2::PermissionRegister> DatabaseHelper::loadGroupPermissions(VirtualServerId server, ts::GroupId group_id, uint8_t type) {
auto result = std::make_shared<v2::PermissionRegister>(); auto result = std::make_shared<v2::PermissionRegister>();
if(this->use_startup_cache && server) { if(this->use_startup_cache && server) {
shared_ptr<StartupCacheEntry> entry; shared_ptr<StartupCacheEntry> entry;
{ {
threads::MutexLock lock(this->startup_lock); threads::MutexLock lock(this->startup_lock);
for(const auto& entries : this->startup_entries) { for(const auto& entries : this->startup_entries) {
if(entries->sid == server->getServerId()) { if(entries->sid == server) {
entry = entries; entry = entries;
break; break;
} }
@ -361,19 +363,18 @@ std::shared_ptr<permission::v2::PermissionRegister> DatabaseHelper::loadGroupPer
//7931 //7931
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", 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{":serverId", server},
variable{":type", permission::SQL_PERM_GROUP}, variable{":type", permission::SQL_PERM_GROUP},
variable{":id", group_id}); variable{":id", group_id});
LOG_SQL_CMD(load_permissions_v2(server, result.get(), command, false)); LOG_SQL_CMD(load_permissions_v2(server, result.get(), command, false));
return result; return result;
} }
void DatabaseHelper::saveGroupPermissions(const std::shared_ptr<ts::server::VirtualServer> &server, ts::GroupId group_id, const std::shared_ptr<ts::permission::v2::PermissionRegister> &permissions) { void DatabaseHelper::saveGroupPermissions(VirtualServerId server_id, ts::GroupId group_id, const std::shared_ptr<ts::permission::v2::PermissionRegister> &permissions) {
const auto updates = permissions->flush_db_updates(); const auto updates = permissions->flush_db_updates();
if(updates.empty()) if(updates.empty())
return; return;
auto server_id = server ? server->getServerId() : 0;
for(auto& update : updates) { for(auto& update : updates) {
std::string query = update.flag_delete ? DELETE_COMMAND : (update.flag_db ? UPDATE_COMMAND : INSERT_COMMAND); std::string query = update.flag_delete ? DELETE_COMMAND : (update.flag_db ? UPDATE_COMMAND : INSERT_COMMAND);
@ -388,7 +389,7 @@ void DatabaseHelper::saveGroupPermissions(const std::shared_ptr<ts::server::Virt
query query
); );
sql::command(this->sql, query, sql::command(this->sql, query,
variable{":serverId", server ? server->getServerId() : 0}, variable{":serverId", server_id},
variable{":id", group_id}, variable{":id", group_id},
variable{":chId", 0}, variable{":chId", 0},
variable{":type", permission::SQL_PERM_GROUP}, variable{":type", permission::SQL_PERM_GROUP},
@ -436,7 +437,7 @@ std::shared_ptr<permission::v2::PermissionRegister> DatabaseHelper::loadPlaylist
variable{":serverId", server ? server->getServerId() : 0}, variable{":serverId", server ? server->getServerId() : 0},
variable{":type", permission::SQL_PERM_PLAYLIST}, variable{":type", permission::SQL_PERM_PLAYLIST},
variable{":id", playlist_id}); variable{":id", playlist_id});
LOG_SQL_CMD(load_permissions_v2(server, result.get(), command, false)); LOG_SQL_CMD(load_permissions_v2(server ? server->getServerId() : 0, result.get(), command, false));
return result; return result;
} }
@ -502,7 +503,7 @@ std::shared_ptr<permission::v2::PermissionRegister> DatabaseHelper::loadChannelP
variable{":chid", channel}, variable{":chid", channel},
variable{":id", 0}, variable{":id", 0},
variable{":type", permission::SQL_PERM_CHANNEL}); variable{":type", permission::SQL_PERM_CHANNEL});
LOG_SQL_CMD(load_permissions_v2(server, result.get(), command, false)); LOG_SQL_CMD(load_permissions_v2(server ? server->getServerId() : 0, result.get(), command, false));
return result; return result;
} }

View File

@ -97,8 +97,8 @@ namespace ts {
std::shared_ptr<permission::v2::PermissionRegister> loadChannelPermissions(const std::shared_ptr<VirtualServer>&, ChannelId); std::shared_ptr<permission::v2::PermissionRegister> loadChannelPermissions(const std::shared_ptr<VirtualServer>&, ChannelId);
void saveChannelPermissions(const std::shared_ptr<VirtualServer>&, ChannelId, const std::shared_ptr<permission::v2::PermissionRegister>& /* permission manager */); void saveChannelPermissions(const std::shared_ptr<VirtualServer>&, ChannelId, const std::shared_ptr<permission::v2::PermissionRegister>& /* permission manager */);
std::shared_ptr<permission::v2::PermissionRegister> loadGroupPermissions(const std::shared_ptr<VirtualServer>&, GroupId); std::shared_ptr<permission::v2::PermissionRegister> loadGroupPermissions(VirtualServerId, GroupId, uint8_t /* group type */);
void saveGroupPermissions(const std::shared_ptr<VirtualServer>&, GroupId, const std::shared_ptr<permission::v2::PermissionRegister>& /* permission manager */); void saveGroupPermissions(VirtualServerId, GroupId, const std::shared_ptr<permission::v2::PermissionRegister>& /* permission manager */);
std::shared_ptr<permission::v2::PermissionRegister> loadPlaylistPermissions(const std::shared_ptr<VirtualServer>&, PlaylistId /* playlist id */); std::shared_ptr<permission::v2::PermissionRegister> loadPlaylistPermissions(const std::shared_ptr<VirtualServer>&, PlaylistId /* playlist id */);
void savePlaylistPermissions(const std::shared_ptr<VirtualServer>&, PlaylistId, const std::shared_ptr<permission::v2::PermissionRegister>& /* permission manager */); void savePlaylistPermissions(const std::shared_ptr<VirtualServer>&, PlaylistId, const std::shared_ptr<permission::v2::PermissionRegister>& /* permission manager */);

View File

@ -29,7 +29,9 @@ namespace ts {
enum GroupTarget { enum GroupTarget {
GROUPTARGET_SERVER, GROUPTARGET_SERVER,
GROUPTARGET_CHANNEL GROUPTARGET_CHANNEL,
GROUPTARGET_UNKNOWN
}; };
enum GroupNameMode { enum GroupNameMode {

View File

@ -560,6 +560,10 @@ bool ConnectedClient::notifyClientNeededPermissions() {
cmd[index]["permid"] = value.first; cmd[index]["permid"] = value.first;
cmd[index++]["permvalue"] = value.second.has_value ? value.second.value : 0; cmd[index++]["permvalue"] = value.second.has_value ? value.second.value : 0;
} }
if(index == 0) {
cmd[index]["permid"] = permission::i_client_talk_power;
cmd[index++]["permvalue"] = 0;
}
this->sendCommand(cmd); this->sendCommand(cmd);
return true; return true;

View File

@ -105,7 +105,8 @@ namespace ts {
std::shared_ptr<permission::v2::PermissionRegister> clientPermissions = nullptr; std::shared_ptr<permission::v2::PermissionRegister> clientPermissions = nullptr;
std::shared_ptr<Properties> _properties; std::shared_ptr<Properties> _properties;
std::shared_ptr<BasicChannel> currentChannel = nullptr; /* the current channel is save to access when the server channel tree has been locked (shared or exclusively) */
std::shared_ptr<BasicChannel> currentChannel{nullptr};
}; };
} }
} }

View File

@ -7,15 +7,8 @@
using namespace ts::server::groups; using namespace ts::server::groups;
Group::Group(const std::shared_ptr<GroupManager> &manager, ts::GroupId id, ts::server::groups::GroupType type, std::string name, Group::Group(VirtualServerId sid, ts::GroupId id, ts::server::groups::GroupType type, std::string name,
std::shared_ptr<permission::v2::PermissionRegister> permissions) : manager_{manager}, group_id_{id}, type_{type}, name_{std::move(name)}, permissions_{std::move(permissions)} { } std::shared_ptr<permission::v2::PermissionRegister> permissions) : virtual_server_id_{sid}, 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<permission::v2::PermissionRegister> &permissions) { void Group::set_permissions(const std::shared_ptr<permission::v2::PermissionRegister> &permissions) {
assert(permissions); assert(permissions);

View File

@ -7,7 +7,9 @@ namespace ts::server::groups {
enum GroupType { enum GroupType {
GROUP_TYPE_QUERY, GROUP_TYPE_QUERY,
GROUP_TYPE_TEMPLATE, GROUP_TYPE_TEMPLATE,
GROUP_TYPE_NORMAL GROUP_TYPE_NORMAL,
GROUP_TYPE_UNKNOWN = 0xFF
}; };
enum GroupNameMode { enum GroupNameMode {
@ -23,13 +25,15 @@ namespace ts::server::groups {
class Group { class Group {
friend class GroupManager; friend class GroupManager;
public: public:
Group(const std::shared_ptr<GroupManager>& /* manager */, GroupId /* id */, GroupType /* type */, std::string /* name */, std::shared_ptr<permission::v2::PermissionRegister> /* permissions */); Group(VirtualServerId /* server id */, GroupId /* id */, GroupType /* type */, std::string /* name */, std::shared_ptr<permission::v2::PermissionRegister> /* permissions */);
~Group() = default; ~Group() = default;
/* information getters */ /* information getters */
[[nodiscard]] inline VirtualServerId virtual_server_id() const { return this->virtual_server_id_; }
[[nodiscard]] inline GroupId group_id() const { return this->group_id_; } [[nodiscard]] inline GroupId group_id() const { return this->group_id_; }
[[nodiscard]] inline GroupType group_type() const { return this->type_; } [[nodiscard]] inline GroupType group_type() const { return this->type_; }
[[nodiscard]] inline const std::string& display_name() const { return this->name_; } [[nodiscard]] inline const std::string& display_name() const { return this->name_; }
/* we're not returning a cr here because the permissions might get reloaded */
[[nodiscard]] inline std::shared_ptr<permission::v2::PermissionRegister> permissions() { return this->permissions_; } [[nodiscard]] inline std::shared_ptr<permission::v2::PermissionRegister> permissions() { return this->permissions_; }
[[nodiscard]] inline bool save_assignments() const { [[nodiscard]] inline bool save_assignments() const {
@ -56,11 +60,8 @@ namespace ts::server::groups {
return value.has_value ? value.value : 0; return value.has_value ? value.value : 0;
} }
/* information setters */
void set_display_name(const std::string& name);
private: private:
std::weak_ptr<GroupManager> manager_; const VirtualServerId virtual_server_id_;
const GroupId group_id_; const GroupId group_id_;
const GroupType type_; const GroupType type_;
std::string name_; std::string name_;
@ -70,6 +71,13 @@ namespace ts::server::groups {
void set_permissions(const std::shared_ptr<permission::v2::PermissionRegister>& /* permissions */); void set_permissions(const std::shared_ptr<permission::v2::PermissionRegister>& /* permissions */);
}; };
class ServerGroup : public Group { }; class ServerGroup : public Group {
class ChannelGroup : public Group { }; public:
ServerGroup(VirtualServerId /* server id */, GroupId /* id */, GroupType /* type */, std::string /* name */, std::shared_ptr<permission::v2::PermissionRegister> /* permissions */);
};
class ChannelGroup : public Group {
public:
ChannelGroup(VirtualServerId /* server id */, GroupId /* id */, GroupType /* type */, std::string /* name */, std::shared_ptr<permission::v2::PermissionRegister> /* permissions */);
};
} }

View File

@ -1,5 +1,612 @@
// //
// Created by WolverinDEV on 03/03/2020. // Created by WolverinDEV on 03/03/2020.
// //
#include <log/LogUtils.h>
#include "GroupManager.h" #include "GroupManager.h"
#include "../vserver/VirtualServerBase.h"
#include "../InstanceHandler.h"
using namespace ts::server::groups;
GroupManager::GroupManager(ts::server::vserver::VirtualServerBase *server, std::shared_ptr<GroupManager> parent)
: virtual_server_{server}, parent_manager_{std::move(parent)}, assignment_manager_{this} {
assert(server);
}
GroupManager::~GroupManager() = default;
ts::ServerId GroupManager::server_id() {
return this->virtual_server_->server_id();
}
sql::SqlManager* GroupManager::sql_manager() {
return this->virtual_server_->sql_manager();
}
bool GroupManager::initialize(const std::shared_ptr<GroupManager>& self, std::string &error) {
assert(&*self == this);
this->weak_ref_ = self;
return this->assignment_manager_.initialize(error);
}
GroupLoadResult GroupManager::load_data(bool initialize) {
std::lock_guard manage_lock{this->group_manage_lock_};
{
std::lock_guard list_lock{this->group_lock_};
this->server_groups_.clear();
this->channel_groups_.clear();
}
auto res = sql::command(this->sql_manager(), "SELECT * FROM `groups` WHERE `serverId` = :sid", variable{":sid", this->server_id()}).query(&GroupManager::insert_group_from_sql, this);
if(!res) {
LOG_SQL_CMD(res);
return GroupLoadResult::DATABASE_ERROR;
}
if(this->server_groups_.empty() || this->channel_groups_.empty())
return GroupLoadResult::NO_GROUPS;
this->ensure_valid_default_groups(initialize);
return GroupLoadResult::SUCCESS;
}
void GroupManager::unload_data() {
std::lock_guard manage_lock{this->group_manage_lock_};
{
std::lock_guard list_lock{this->group_lock_};
this->server_groups_.clear();
this->channel_groups_.clear();
}
}
void GroupManager::reset_groups(bool db_cleanup) {
std::lock_guard manage_lock{this->group_manage_lock_};
this->unload_data();
if(db_cleanup) {
LOG_SQL_CMD(sql::command(this->virtual_server_->sql_manager(), "DELETE FROM `permissions` WHERE `serverId` = :serverId AND `type` = :type",
variable{":serverId", this->server_id()},
variable{":type", ts::permission::SQL_PERM_GROUP}).execute());
LOG_SQL_CMD(sql::command(this->virtual_server_->sql_manager(), "DELETE FROM `assignedGroups` WHERE `serverId` = :serverId", variable{":serverId", this->server_id()}).execute());
LOG_SQL_CMD(sql::command(this->virtual_server_->sql_manager(), "DELETE FROM `groups` WHERE `serverId` = :serverId", variable{":serverId", this->server_id()}).execute());
}
if(auto error = this->load_data(true); error != GroupLoadResult::SUCCESS)
logCritical(this->server_id(), "Failed to load groups after group unload ({}). There might be no groups loaded now!", (int) error);
}
int GroupManager::insert_group_from_sql(int length, std::string *values, std::string *names) {
GroupId group_id{0};
GroupType group_type{GroupType::GROUP_TYPE_UNKNOWN};
std::string group_name{};
GroupTarget group_target{GroupTarget::GROUPTARGET_UNKNOWN};
for(size_t index = 0; index < length; index++) {
try {
if(names[index] == "groupId")
group_id = std::stoull(values[index]);
else if(names[index] == "target")
group_target = (GroupTarget) std::stoull(values[index]);
else if(names[index] == "type")
group_type = (GroupType) std::stoull(values[index]);
else if(names[index] == "displayName")
group_name = names[index];
} catch(std::exception& ex) {
logWarning(this->server_id(), "Failed to parse group from database. Failed to parse column {} (value: {})", names[index], values[index]);
return 0;
}
}
if(!group_id || group_type == GroupType::GROUP_TYPE_UNKNOWN || group_target == GroupTarget::GROUPTARGET_UNKNOWN) {
logWarning(this->server_id(), "Failed to query group from database. Invalid values found.");
return 0;
}
auto database_target = (uint8_t) (group_target == GroupTarget::GROUPTARGET_SERVER ? DatabaseGroupTarget::SERVER : DatabaseGroupTarget::CHANNEL);
auto permissions = serverInstance->databaseHelper()->loadGroupPermissions(this->server_id(), group_id, database_target);
if(group_target == GroupTarget::GROUPTARGET_SERVER) {
auto group = std::make_shared<ServerGroup>(this->server_id(), group_id, group_type, group_name, permissions);
std::lock_guard lock{this->group_lock_};
this->server_groups_.push_back(group);
} else {
auto group = std::make_shared<ChannelGroup>(this->server_id(), group_id, group_type, group_name, permissions);
std::lock_guard lock{this->group_lock_};
this->channel_groups_.push_back(group);
}
return 0;
}
void GroupManager::ensure_valid_default_groups(bool initialize) {
assert(!this->channel_groups_.empty() && !this->server_groups_.empty());
auto fallback_server_group = this->server_groups_.front();
auto fallback_channel_group = this->channel_groups_.front();
auto properties = this->virtual_server_->server_properties();
/* default server music group */
{
auto group_id = properties->find(property::VIRTUALSERVER_DEFAULT_MUSIC_GROUP).as_save<GroupId>();
auto group = this->find_server_group(groups::GroupCalculateMode::GLOBAL, group_id);
if(!group) {
if(!initialize)
logWarning(this->server_id(), "Missing default music bot group ({}). Try to reinitialize from serverinstance_template_musicdefault_group.", group_id);
group_id = serverInstance->properties()[property::SERVERINSTANCE_TEMPLATE_MUSICDEFAULT_GROUP].as<GroupId>();
group = this->find_server_group(groups::GroupCalculateMode::GLOBAL, group_id);
if(!group) {
group = fallback_server_group;
logCritical(LOG_INSTANCE, "Failed to find default music bot group ({}). Using first available group ({}).", group_id, fallback_server_group->group_id());
}
auto group_name = group->display_name();
group = this->find_server_group_by_name(GroupCalculateMode::LOCAL, group_name);
if(!group) {
group = fallback_server_group;
logError(this->server_id(), "Missing default music bot group and could not find group from template name ({}). Using {} as fallback.", group_name, group);
}
properties->find(property::VIRTUALSERVER_DEFAULT_MUSIC_GROUP) = group->group_id();
}
}
/* default server guest group */
{
auto group_id = properties->find(property::VIRTUALSERVER_DEFAULT_SERVER_GROUP).as_save<GroupId>();
auto group = this->find_server_group(groups::GroupCalculateMode::GLOBAL, group_id);
if(!group) {
if(!initialize)
logWarning(this->server_id(), "Missing default server guest group ({}). Try to reinitialize from serverinstance_template_serverdefault_group.", group_id);
group_id = serverInstance->properties()[property::SERVERINSTANCE_TEMPLATE_SERVERDEFAULT_GROUP].as<GroupId>();
group = this->find_server_group(groups::GroupCalculateMode::GLOBAL, group_id);
if(!group) {
group = fallback_server_group;
logCritical(LOG_INSTANCE, "Failed to find default server guest group ({}). Using first available group ({}).", group_id, fallback_server_group->group_id());
}
auto group_name = group->display_name();
group = this->find_server_group_by_name(GroupCalculateMode::LOCAL, group_name);
if(!group) {
group = fallback_server_group;
logError(this->server_id(), "Missing default server guest group and could not find group from template name ({}). Using {} as fallback.", group_name, group);
}
properties->find(property::VIRTUALSERVER_DEFAULT_SERVER_GROUP) = group->group_id();
}
}
/* default channel admin group */
{
auto group_id = properties->find(property::VIRTUALSERVER_DEFAULT_CHANNEL_ADMIN_GROUP).as_save<GroupId>();
auto group = this->find_channel_group(groups::GroupCalculateMode::GLOBAL, group_id);
if(!group) {
if(!initialize)
logWarning(this->server_id(), "Missing default channel admin group ({}). Try to reinitialize from serverinstance_template_channeladmin_group.", group_id);
group_id = serverInstance->properties()[property::SERVERINSTANCE_TEMPLATE_CHANNELADMIN_GROUP].as<GroupId>();
group = this->find_channel_group(groups::GroupCalculateMode::GLOBAL, group_id);
if(!group) {
group = fallback_channel_group;
logCritical(LOG_INSTANCE, "Failed to find default channel admin group ({}). Using first available group ({}).", group_id, fallback_channel_group->group_id());
}
auto group_name = group->display_name();
group = this->find_channel_group_by_name(GroupCalculateMode::LOCAL, group_name);
if(!group) {
group = fallback_channel_group;
logError(this->server_id(), "Missing default channel admin group and could not find group from template name ({}). Using {} as fallback.", group_name, group);
}
properties->find(property::VIRTUALSERVER_DEFAULT_CHANNEL_ADMIN_GROUP) = group->group_id();
}
}
/* default channel group */
{
auto group_id = properties->find(property::VIRTUALSERVER_DEFAULT_CHANNEL_GROUP).as_save<GroupId>();
auto group = this->find_channel_group(groups::GroupCalculateMode::GLOBAL, group_id);
if(!group) {
if(!initialize)
logWarning(this->server_id(), "Missing default channel guest group ({}). Try to reinitialize from serverinstance_template_channeldefault_group.", group_id);
group_id = serverInstance->properties()[property::SERVERINSTANCE_TEMPLATE_CHANNELDEFAULT_GROUP].as<GroupId>();
group = this->find_channel_group(groups::GroupCalculateMode::GLOBAL, group_id);
if(!group) {
group = fallback_channel_group;
logCritical(LOG_INSTANCE, "Failed to find default channel guest group ({}). Using first available group ({}).", group_id, fallback_channel_group->group_id());
}
auto group_name = group->display_name();
group = this->find_channel_group_by_name(GroupCalculateMode::LOCAL, group_name);
if(!group) {
group = fallback_channel_group;
logError(this->server_id(), "Missing default channel guest group and could not find group from template name ({}). Using {} as fallback.", group_name, group);
}
properties->find(property::VIRTUALSERVER_DEFAULT_CHANNEL_GROUP) = group->group_id();
}
}
}
std::shared_ptr<ServerGroup> GroupManager::default_server_group(ClientType client_type, bool enforce_id) {
auto id =
client_type != ClientType::CLIENT_QUERY ?
this->virtual_server_->server_properties()->find(property::VIRTUALSERVER_DEFAULT_SERVER_GROUP).as_save<GroupId>() :
serverInstance->properties()[property::SERVERINSTANCE_GUEST_SERVERQUERY_GROUP].as_save<GroupId>();
auto group = this->find_server_group(groups::GroupCalculateMode::GLOBAL, id);
if(group || enforce_id) return group;
std::lock_guard glock{this->group_lock_};
return this->server_groups_.empty() ? nullptr : this->server_groups_.front();
}
std::shared_ptr<ChannelGroup> GroupManager::default_channel_group(ClientType client_type, bool enforce_id) {
auto id = this->virtual_server_->server_properties()->find(property::VIRTUALSERVER_DEFAULT_CHANNEL_GROUP).as_save<GroupId>();
auto group = this->find_channel_group(groups::GroupCalculateMode::GLOBAL, id);
if(group || enforce_id) return group;
std::lock_guard glock{this->group_lock_};
return this->channel_groups_.empty() ? nullptr : this->channel_groups_.front();
}
std::shared_ptr<ServerGroup> GroupManager::find_server_group(GroupCalculateMode mode, GroupId group_id) {
{
std::lock_guard glock{this->group_lock_};
auto it = std::find_if(this->server_groups_.begin(), this->server_groups_.end(), [&](const std::shared_ptr<ServerGroup>& group) {
return group->group_id() == group_id;
});
if(it != this->server_groups_.end())
return *it;
}
if(mode == GroupCalculateMode::GLOBAL && this->parent_manager_)
return this->parent_manager_->find_server_group(mode, group_id);
return nullptr;
}
std::shared_ptr<ServerGroup> GroupManager::find_server_group_by_name(GroupCalculateMode mode, const std::string &name) {
{
std::string lname{name};
std::transform(lname.begin(), lname.end(), lname.begin(), ::tolower);
std::lock_guard glock{this->group_lock_};
auto it = std::find_if(this->server_groups_.begin(), this->server_groups_.end(),
[&](const std::shared_ptr<ServerGroup> &group) {
std::string lgroup_name{group->name_};
std::transform(lgroup_name.begin(), lgroup_name.end(), lgroup_name.begin(),
::tolower);
return lname == lgroup_name;
});
if(it != this->server_groups_.end())
return *it;
}
if(mode == GroupCalculateMode::GLOBAL && this->parent_manager_)
return this->parent_manager_->find_server_group_by_name(mode, name);
return nullptr;
}
std::shared_ptr<ChannelGroup> GroupManager::find_channel_group(GroupCalculateMode mode, GroupId group_id) {
{
std::lock_guard glock{this->group_lock_};
auto it = std::find_if(this->channel_groups_.begin(), this->channel_groups_.end(), [&](const std::shared_ptr<ChannelGroup>& group) {
return group->group_id() == group_id;
});
if(it != this->channel_groups_.end())
return *it;
}
if(mode == GroupCalculateMode::GLOBAL && this->parent_manager_)
return this->parent_manager_->find_channel_group(mode, group_id);
return nullptr;
}
std::shared_ptr<ChannelGroup> GroupManager::find_channel_group_by_name(GroupCalculateMode mode, const std::string &name) {
{
std::string lname{name};
std::transform(lname.begin(), lname.end(), lname.begin(), ::tolower);
std::lock_guard glock{this->group_lock_};
auto it = std::find_if(this->channel_groups_.begin(), this->channel_groups_.end(),
[&](const std::shared_ptr<ChannelGroup> &group) {
std::string lgroup_name{group->name_};
std::transform(lgroup_name.begin(), lgroup_name.end(), lgroup_name.begin(),
::tolower);
return lname == lgroup_name;
});
if(it != this->channel_groups_.end())
return *it;
}
if(mode == GroupCalculateMode::GLOBAL && this->parent_manager_)
return this->parent_manager_->find_channel_group_by_name(mode, name);
return nullptr;
}
GroupCreateResult GroupManager::create_server_group(GroupType type, const std::string &name, std::shared_ptr<ServerGroup>& result) {
std::lock_guard manage_lock{this->group_manage_lock_};
auto self = this->weak_ref_.lock();
if(!self) return GroupCreateResult::DATABASE_ERROR;
if(this->find_server_group_by_name(GroupCalculateMode::LOCAL, name))
return GroupCreateResult::NAME_ALREADY_IN_USED;
auto group_id = this->generate_server_group_id();
if(!group_id)
return GroupCreateResult::FAILED_TO_GENERATE_ID;
auto res = sql::command(this->sql_manager(), "INSERT INTO `groups` (`serverId`, `groupId`, `target`, `type`, `displayName`) VALUES (:sid, :gid, :target, :type, :name)",
variable{":sid", this->server_id()}, variable{":gid", group_id}, variable{":target", DatabaseGroupTarget::SERVER}, variable{":type", type}, variable{":name", name}).execute();
if(!res) {
LOG_SQL_CMD(res);
return GroupCreateResult::DATABASE_ERROR;
}
auto permissions = serverInstance->databaseHelper()->loadGroupPermissions(this->server_id(), group_id, (uint8_t) DatabaseGroupTarget::SERVER);
auto group = std::make_shared<ServerGroup>(self->server_id(), group_id, type, name, permissions);
{
std::lock_guard glock{this->group_lock_};
this->server_groups_.push_back(group);
}
result = group;
return GroupCreateResult::SUCCESS;
}
GroupCreateResult GroupManager::create_channel_group(GroupType type, const std::string &name, std::shared_ptr<ChannelGroup> &result) {
std::lock_guard manage_lock{this->group_manage_lock_};
auto self = this->weak_ref_.lock();
if(!self) return GroupCreateResult::DATABASE_ERROR;
if(this->find_channel_group_by_name(GroupCalculateMode::LOCAL, name))
return GroupCreateResult::NAME_ALREADY_IN_USED;
auto group_id = this->generate_channel_group_id();
if(!group_id)
return GroupCreateResult::FAILED_TO_GENERATE_ID;
auto res = sql::command(this->sql_manager(), "INSERT INTO `groups` (`serverId`, `groupId`, `target`, `type`, `displayName`) VALUES (:sid, :gid, :target, :type, :name)",
variable{":sid", this->server_id()}, variable{":gid", group_id}, variable{":target", DatabaseGroupTarget::CHANNEL}, variable{":type", type}, variable{":name", name}).execute();
if(!res) {
LOG_SQL_CMD(res);
return GroupCreateResult::DATABASE_ERROR;
}
auto permissions = serverInstance->databaseHelper()->loadGroupPermissions(this->server_id(), group_id, (uint8_t) DatabaseGroupTarget::CHANNEL);
auto group = std::make_shared<ChannelGroup>(this->server_id(), group_id, type, name, permissions);
{
std::lock_guard glock{this->group_lock_};
this->channel_groups_.push_back(group);
}
result = group;
return GroupCreateResult::SUCCESS;
}
GroupCopyResult GroupManager::copy_server_group(GroupId source, GroupType target_type, const std::string &display_name, std::shared_ptr<ServerGroup>& result) {
std::lock_guard manage_lock{this->group_manage_lock_};
auto self = this->weak_ref_.lock();
if(!self) return GroupCopyResult::DATABASE_ERROR;
std::shared_ptr<ServerGroup> group{};
auto create_result = this->create_server_group(target_type, display_name, group);
switch(create_result) {
case GroupCreateResult::SUCCESS:
break;
case GroupCreateResult::DATABASE_ERROR:
return GroupCopyResult::DATABASE_ERROR;
case GroupCreateResult::NAME_ALREADY_IN_USED:
return GroupCopyResult::NAME_ALREADY_IN_USE;
case GroupCreateResult::FAILED_TO_GENERATE_ID:
return GroupCopyResult::FAILED_TO_GENERATE_ID;
}
assert(group);
return this->copy_server_group_permissions(source, group->group_id());
}
GroupCopyResult GroupManager::copy_channel_group(GroupId source, GroupType target_type, const std::string &display_name, std::shared_ptr<ChannelGroup> &result) {
std::lock_guard manage_lock{this->group_manage_lock_};
auto self = this->weak_ref_.lock();
if(!self) return GroupCopyResult::DATABASE_ERROR;
std::shared_ptr<ChannelGroup> group{};
auto create_result = this->create_channel_group(target_type, display_name, group);
switch(create_result) {
case GroupCreateResult::SUCCESS:
break;
case GroupCreateResult::DATABASE_ERROR:
return GroupCopyResult::DATABASE_ERROR;
case GroupCreateResult::NAME_ALREADY_IN_USED:
return GroupCopyResult::NAME_ALREADY_IN_USE;
case GroupCreateResult::FAILED_TO_GENERATE_ID:
return GroupCopyResult::FAILED_TO_GENERATE_ID;
}
assert(group);
return this->copy_channel_group_permissions(source, group->group_id());
}
GroupCopyResult GroupManager::copy_server_group_permissions(GroupId source, GroupId target) {
std::lock_guard manage_lock{this->group_manage_lock_};
auto self = this->weak_ref_.lock();
if(!self) return GroupCopyResult::DATABASE_ERROR;
auto source_group = this->find_server_group(groups::GroupCalculateMode::GLOBAL, source);
if(!source_group)
return GroupCopyResult::UNKNOWN_SOURCE_GROUP;
auto target_group = this->find_server_group(groups::GroupCalculateMode::GLOBAL, target);
if(!target_group)
return GroupCopyResult::UNKNOWN_TARGET_GROUP;
auto res = sql::command(this->sql_manager(), "DELETE FROM `permissions` WHERE `serverId` = :sid AND `type` = :type AND `id` = :id",
variable{":sid", this->server_id()}, variable{":type", ts::permission::SQL_PERM_GROUP}, variable{":id", target}).execute();
if(!res) {
LOG_SQL_CMD(res);
return GroupCopyResult::DATABASE_ERROR;
}
res = sql::command(this->sql_manager(), "INSERT INTO `permissions` (`serverId`, `type`, `id`, `channelId`, `permId`, `value`, `grant`, `flag_skip`, `flag_negate`) SELECT :tsid AS `serverId`, `type`, :target AS `id`, 0 AS `channelId`, `permId`, `value`,`grant`,`flag_skip`, `flag_negate` FROM `permissions` WHERE `serverId` = :ssid AND `type` = :type AND `id` = :source",
variable{":ssid", source_group->virtual_server_id()},
variable{":tsid", target_group->virtual_server_id()},
variable{":type", ts::permission::SQL_PERM_GROUP},
variable{":source", source},
variable{":target", target}).execute();
target_group->set_permissions(serverInstance->databaseHelper()->loadGroupPermissions(this->server_id(), target, (uint8_t) DatabaseGroupTarget::SERVER));
LOG_SQL_CMD(res);
return GroupCopyResult::SUCCESS;
}
GroupCopyResult GroupManager::copy_channel_group_permissions(GroupId source, GroupId target) {
std::lock_guard manage_lock{this->group_manage_lock_};
auto self = this->weak_ref_.lock();
if(!self) return GroupCopyResult::DATABASE_ERROR;
auto source_group = this->find_channel_group(groups::GroupCalculateMode::GLOBAL, source);
if(!source_group)
return GroupCopyResult::UNKNOWN_SOURCE_GROUP;
auto target_group = this->find_channel_group(groups::GroupCalculateMode::GLOBAL, target);
if(!target_group)
return GroupCopyResult::UNKNOWN_TARGET_GROUP;
auto res = sql::command(this->sql_manager(), "DELETE FROM `permissions` WHERE `serverId` = :sid AND `type` = :type AND `id` = :id",
variable{":sid", this->server_id()}, variable{":type", ts::permission::SQL_PERM_GROUP}, variable{":id", target}).execute();
if(!res) {
LOG_SQL_CMD(res);
return GroupCopyResult::DATABASE_ERROR;
}
res = sql::command(this->sql_manager(), "INSERT INTO `permissions` (`serverId`, `type`, `id`, `channelId`, `permId`, `value`, `grant`, `flag_skip`, `flag_negate`) SELECT :tsid AS `serverId`, `type`, :target AS `id`, 0 AS `channelId`, `permId`, `value`,`grant`,`flag_skip`, `flag_negate` FROM `permissions` WHERE `serverId` = :ssid AND `type` = :type AND `id` = :source",
variable{":ssid", source_group->virtual_server_id()},
variable{":tsid", target_group->virtual_server_id()},
variable{":type", ts::permission::SQL_PERM_GROUP},
variable{":source", source},
variable{":target", target}).execute();
target_group->set_permissions(serverInstance->databaseHelper()->loadGroupPermissions(this->server_id(), target, (uint8_t) DatabaseGroupTarget::CHANNEL));
LOG_SQL_CMD(res);
return GroupCopyResult::SUCCESS;
}
GroupRenameResult GroupManager::rename_server_group(GroupId group_id, const std::string &name) {
std::lock_guard manage_lock{this->group_manage_lock_};
auto self = this->weak_ref_.lock();
if(!self) return GroupRenameResult::DATABASE_ERROR;
if(name.empty() || name.length() > 40)
return GroupRenameResult::NAME_INVALID;
auto group = this->find_server_group(groups::GroupCalculateMode::GLOBAL, group_id);
if(!group) return GroupRenameResult::INVALID_GROUP_ID;
if(this->find_server_group_by_name(GroupCalculateMode::LOCAL, name))
return GroupRenameResult::NAME_ALREADY_USED;
sql::command(this->sql_manager(), "UPDATE `groups` SET `displayName` = :name WHERE `serverId` = :server AND `groupId` = :group_id AND `target` = :target",
variable{":server", this->server_id()},
variable{":target", DatabaseGroupTarget::SERVER},
variable{":group_id", group_id}).executeLater().waitAndGetLater(LOG_SQL_CMD, {-1, "future failed"});
group->name_ = name;
return GroupRenameResult::SUCCESS;
}
GroupRenameResult GroupManager::rename_channel_group(GroupId group_id, const std::string &name) {
std::lock_guard manage_lock{this->group_manage_lock_};
auto self = this->weak_ref_.lock();
if(!self) return GroupRenameResult::DATABASE_ERROR;
if(name.empty() || name.length() > 40)
return GroupRenameResult::NAME_INVALID;
auto group = this->find_channel_group(groups::GroupCalculateMode::GLOBAL, group_id);
if(!group) return GroupRenameResult::INVALID_GROUP_ID;
if(this->find_channel_group_by_name(GroupCalculateMode::LOCAL, name))
return GroupRenameResult::NAME_ALREADY_USED;
sql::command(this->sql_manager(), "UPDATE `groups` SET `displayName` = :name WHERE `serverId` = :server AND `groupId` = :group_id AND `target` = :target",
variable{":server", this->server_id()},
variable{":target", DatabaseGroupTarget::CHANNEL},
variable{":group_id", group_id}).executeLater().waitAndGetLater(LOG_SQL_CMD, {-1, "future failed"});
group->name_ = name;
return GroupRenameResult::SUCCESS;
}
GroupDeleteResult GroupManager::delete_server_group(GroupId group_id) {
std::lock_guard manage_lock{this->group_manage_lock_};
auto self = this->weak_ref_.lock();
if(!self) return GroupDeleteResult::DATABASE_ERROR;
{
std::lock_guard glock{this->group_lock_};
auto it = std::find_if(this->server_groups_.begin(), this->server_groups_.begin(), [&](const std::shared_ptr<ServerGroup>& group) {
return group->group_id() == group_id;
});
if(it == this->server_groups_.end())
return GroupDeleteResult::INVALID_GROUP_ID;
this->server_groups_.erase(it);
}
sql::command(this->sql_manager(), "DELETE FROM WHERE `serverId` = :server AND `groupId` = :group_id AND `target` = :target",
variable{":server", this->server_id()},
variable{":target", DatabaseGroupTarget::SERVER},
variable{":group_id", group_id}).executeLater().waitAndGetLater(LOG_SQL_CMD, {-1, "future failed"});
return GroupDeleteResult::SUCCESS;
}
GroupDeleteResult GroupManager::delete_channel_group(GroupId group_id) {
std::lock_guard manage_lock{this->group_manage_lock_};
auto self = this->weak_ref_.lock();
if(!self) return GroupDeleteResult::DATABASE_ERROR;
{
std::lock_guard glock{this->group_lock_};
auto it = std::find_if(this->channel_groups_.begin(), this->channel_groups_.begin(), [&](const std::shared_ptr<ChannelGroup>& group) {
return group->group_id() == group_id;
});
if(it == this->channel_groups_.end())
return GroupDeleteResult::INVALID_GROUP_ID;
this->channel_groups_.erase(it);
}
sql::command(this->sql_manager(), "DELETE FROM WHERE `serverId` = :server AND `groupId` = :group_id AND `target` = :target",
variable{":server", this->server_id()},
variable{":target", DatabaseGroupTarget::CHANNEL},
variable{":group_id", group_id}).executeLater().waitAndGetLater(LOG_SQL_CMD, {-1, "future failed"});
return GroupDeleteResult::SUCCESS;
}
ts::GroupId GroupManager::generate_server_group_id() {
std::lock_guard glock{this->group_lock_};
ts::GroupId highest_group_id{0};
for(auto& group : this->channel_groups_)
highest_group_id = std::max(group->group_id(), highest_group_id);
for(auto& group : this->server_groups_)
highest_group_id = std::max(group->group_id(), highest_group_id);
if(this->parent_manager_)
highest_group_id = std::max(this->parent_manager_->generate_server_group_id(), highest_group_id);
return highest_group_id + 1;
}
ts::GroupId GroupManager::generate_channel_group_id() {
return this->generate_server_group_id();
}

View File

@ -20,63 +20,114 @@ namespace ts::server {
GLOBAL /* use the parent group manager as well, if existing */ GLOBAL /* use the parent group manager as well, if existing */
}; };
enum struct ActionReturnCode { enum struct GroupLoadResult {
RENAME_NAME_ALREADY_USED, SUCCESS,
RENAME_GROUP_INVALID_ID, NO_GROUPS,
DATABASE_ERROR
};
DELETE_GROUP_INVALID_ID, enum struct GroupCreateResult {
SUCCESS,
NAME_ALREADY_IN_USED,
FAILED_TO_GENERATE_ID,
DATABASE_ERROR
};
CUSTOM_ERROR enum struct GroupCopyResult {
SUCCESS,
UNKNOWN_SOURCE_GROUP,
UNKNOWN_TARGET_GROUP,
NAME_ALREADY_IN_USE,
FAILED_TO_GENERATE_ID,
DATABASE_ERROR
};
enum struct GroupRenameResult {
SUCCESS,
INVALID_GROUP_ID,
NAME_ALREADY_USED,
NAME_INVALID,
DATABASE_ERROR
};
enum struct GroupDeleteResult {
SUCCESS,
INVALID_GROUP_ID,
DATABASE_ERROR
}; };
class GroupManager { class GroupManager {
friend class Group; friend class Group;
public: public:
enum struct DatabaseGroupTarget {
SERVER,
CHANNEL
};
GroupManager(vserver::VirtualServerBase* /* virtual server */, std::shared_ptr<GroupManager> /* parent */); GroupManager(vserver::VirtualServerBase* /* virtual server */, std::shared_ptr<GroupManager> /* parent */);
~GroupManager(); ~GroupManager();
bool initialize(std::string& /* error */); bool initialize(const std::shared_ptr<GroupManager>& /* self ref, */, std::string& /* error */);
bool load_data(std::string& /* error */); GroupLoadResult load_data(bool /* initialize */ = false);
void unload_data(); void unload_data();
void reset_groups(bool /* cleanup database */);
[[nodiscard]] inline vserver::VirtualServerBase* virtual_server() { return this->virtual_server_; } [[nodiscard]] inline vserver::VirtualServerBase* virtual_server() { return this->virtual_server_; }
[[nodiscard]] inline const std::shared_ptr<GroupManager>& parent_manager() { return this->parent_manager_; } [[nodiscard]] inline const std::shared_ptr<GroupManager>& parent_manager() { return this->parent_manager_; }
[[nodiscard]] inline GroupAssignmentManager& assignments() { return this->assignment_manager_; } [[nodiscard]] inline GroupAssignmentManager& assignments() { return this->assignment_manager_; }
[[nodiscard]] std::deque<std::shared_ptr<ServerGroup>> server_groups(GroupCalculateMode /* mode */); [[nodiscard]] std::vector<std::shared_ptr<ServerGroup>> server_groups(GroupCalculateMode /* mode */);
[[nodiscard]] std::deque<std::shared_ptr<ChannelGroup>> channel_groups(GroupCalculateMode /* mode */); [[nodiscard]] std::vector<std::shared_ptr<ChannelGroup>> channel_groups(GroupCalculateMode /* mode */);
[[nodiscard]] std::shared_ptr<ServerGroup> default_server_group(ClientType /* client type */); [[nodiscard]] std::shared_ptr<ServerGroup> default_server_group(ClientType /* client type */, bool /* enforce id */ = false);
[[nodiscard]] std::shared_ptr<ChannelGroup> default_channel_group(ClientType /* client type */); [[nodiscard]] std::shared_ptr<ChannelGroup> default_channel_group(ClientType /* client type */, bool /* enforce id */ = false);
[[nodiscard]] std::shared_ptr<ServerGroup> find_server_group(GroupId /* group id */); [[nodiscard]] std::shared_ptr<ServerGroup> find_server_group(GroupCalculateMode /* mode */, GroupId /* group id */);
[[nodiscard]] std::shared_ptr<ChannelGroup> find_channel_group(GroupId /* group id */); [[nodiscard]] std::shared_ptr<ChannelGroup> find_channel_group(GroupCalculateMode /* mode */, GroupId /* group id */);
[[nodiscard]] std::shared_ptr<ServerGroup> create_server_group(GroupType type, const std::string& /* group name */); [[nodiscard]] std::shared_ptr<ServerGroup> find_server_group_by_name(GroupCalculateMode /* mode */, const std::string& /* group name */);
[[nodiscard]] std::shared_ptr<ServerGroup> create_channel_group(GroupType type, const std::string& /* group name */); [[nodiscard]] std::shared_ptr<ChannelGroup> find_channel_group_by_name(GroupCalculateMode /* mode */, const std::string& /* group name */);
[[nodiscard]] ActionReturnCode copy_server_group(GroupId /* group id */, GroupType /* target group type */, const std::string& /* target group name */); [[nodiscard]] GroupCreateResult create_server_group(GroupType type, const std::string& /* group name */, std::shared_ptr<ServerGroup>& /* result */);
[[nodiscard]] ActionReturnCode copy_channel_group(GroupId /* group id */, GroupType /* target group type */, const std::string& /* target group name */); [[nodiscard]] GroupCreateResult create_channel_group(GroupType type, const std::string& /* group name */, std::shared_ptr<ChannelGroup>& /* result */);
[[nodiscard]] ActionReturnCode copy_server_group_permissions(GroupId /* group id */, GroupId /* target group */); [[nodiscard]] GroupCopyResult copy_server_group(GroupId /* group id */, GroupType /* target group type */, const std::string& /* target group name */, std::shared_ptr<ServerGroup>& /* result */);
[[nodiscard]] ActionReturnCode copy_channel_group_permissions(GroupId /* group id */, GroupId /* target group */); [[nodiscard]] GroupCopyResult copy_channel_group(GroupId /* group id */, GroupType /* target group type */, const std::string& /* target group name */, std::shared_ptr<ChannelGroup>& /* result */);
[[nodiscard]] ActionReturnCode rename_server_group(GroupId /* group id */, const std::string& /* target group name */); [[nodiscard]] GroupCopyResult copy_server_group_permissions(GroupId /* group id */, GroupId /* target group */);
[[nodiscard]] ActionReturnCode rename_channel_group(GroupId /* group id */, const std::string& /* target group name */); [[nodiscard]] GroupCopyResult copy_channel_group_permissions(GroupId /* group id */, GroupId /* target group */);
[[nodiscard]] ActionReturnCode delete_server_group(GroupId /* group id */); [[nodiscard]] GroupRenameResult rename_server_group(GroupId /* group id */, const std::string& /* target group name */);
[[nodiscard]] ActionReturnCode delete_channel_group(GroupId /* group id */); [[nodiscard]] GroupRenameResult rename_channel_group(GroupId /* group id */, const std::string& /* target group name */);
[[nodiscard]] GroupDeleteResult delete_server_group(GroupId /* group id */);
[[nodiscard]] GroupDeleteResult delete_channel_group(GroupId /* group id */);
private: private:
vserver::VirtualServerBase* virtual_server_; vserver::VirtualServerBase* virtual_server_;
std::weak_ptr<GroupManager> weak_ref_;
std::shared_ptr<GroupManager> parent_manager_; std::shared_ptr<GroupManager> parent_manager_;
GroupAssignmentManager assignment_manager_; GroupAssignmentManager assignment_manager_;
/* recursive_mutex due to the copy server/channel group methods */
std::recursive_mutex group_manage_lock_{};
std::mutex group_lock_{}; std::mutex group_lock_{};
/* I think std::vector is better here because we will iterate more often than add groups */
std::vector<std::shared_ptr<ServerGroup>> server_groups_{};
std::vector<std::shared_ptr<ChannelGroup>> channel_groups_{};
[[nodiscard]] sql::SqlManager* sql_manager(); [[nodiscard]] sql::SqlManager* sql_manager();
void update_group_name(Group* /* group */); [[nodiscard]] ServerId server_id();
void ensure_valid_default_groups(bool /* initialize */);
int insert_group_from_sql(int /* length */, std::string* /* values */, std::string* /* columns */);
[[nodiscard]] GroupId generate_server_group_id();
[[nodiscard]] GroupId generate_channel_group_id();
}; };
} }
} }
DEFINE_TRANSFORMS(ts::server::groups::GroupManager::DatabaseGroupTarget, uint8_t);

View File

@ -19,6 +19,7 @@ ts::ServerId ClientChannelService::get_server_id() const {
} }
ChannelGroupInheritance ClientChannelService::calculate_channel_group(ChannelId channel_id, ClientDbId client_dbid, ClientType client_type) { ChannelGroupInheritance ClientChannelService::calculate_channel_group(ChannelId channel_id, ClientDbId client_dbid, ClientType client_type) {
auto server_state_lock = this->virtual_server_->lock_server_status();
auto tree_lock = this->virtual_server_->lock_channel_clients(); auto tree_lock = this->virtual_server_->lock_channel_clients();
tree_lock.auto_lock_shared(); tree_lock.auto_lock_shared();
@ -67,6 +68,7 @@ ClientMoveResult ClientChannelService::client_ban(const std::shared_ptr<Connecte
if(auto err = tree_lock.auto_lock_exclusive(); err) if(auto err = tree_lock.auto_lock_exclusive(); err)
return ClientMoveResult::TREE_LOCK_FAILED; return ClientMoveResult::TREE_LOCK_FAILED;
auto server_state_lock = this->virtual_server_->lock_server_status();
std::unique_lock client_chan_tree_lock{target->get_channel_lock()}; std::unique_lock client_chan_tree_lock{target->get_channel_lock()};
auto old_channel = target->getChannel(); auto old_channel = target->getChannel();
target->setChannel(nullptr); target->setChannel(nullptr);
@ -105,6 +107,7 @@ ClientMoveResult ClientChannelService::client_kick(const std::shared_ptr<Connect
if(auto err = tree_lock.auto_lock_exclusive(); err) if(auto err = tree_lock.auto_lock_exclusive(); err)
return ClientMoveResult::TREE_LOCK_FAILED; return ClientMoveResult::TREE_LOCK_FAILED;
auto server_state_lock = this->virtual_server_->lock_server_status();
std::unique_lock client_chan_tree_lock{target->get_channel_lock()}; std::unique_lock client_chan_tree_lock{target->get_channel_lock()};
auto old_channel = target->getChannel(); auto old_channel = target->getChannel();
target->setChannel(nullptr); target->setChannel(nullptr);
@ -147,6 +150,8 @@ ClientMoveResult ClientChannelService::client_move(const std::shared_ptr<Connect
if(auto err = tree_lock.auto_lock_exclusive(); err) if(auto err = tree_lock.auto_lock_exclusive(); err)
return ClientMoveResult::TREE_LOCK_FAILED; return ClientMoveResult::TREE_LOCK_FAILED;
auto server_state_lock = this->virtual_server_->lock_server_status();
TIMING_STEP(timings, "tree lock setup"); TIMING_STEP(timings, "tree lock setup");
if(target->getChannel() == s_target_channel) if(target->getChannel() == s_target_channel)
return ClientMoveResult::SUCCESS; return ClientMoveResult::SUCCESS;

View File

@ -6,9 +6,11 @@
#include "./PermissionsService.h" #include "./PermissionsService.h"
#include "../InstanceHandler.h" #include "../InstanceHandler.h"
#include "../vserver/VirtualServerBase.h" #include "../vserver/VirtualServerBase.h"
#include "../vserver/VirtualServer.h"
#include "../groups/Group.h" #include "../groups/Group.h"
#include "../groups/GroupManager.h" #include "../groups/GroupManager.h"
#include "../groups/GroupAssignmentManager.h" #include "../groups/GroupAssignmentManager.h"
#include "../client/ConnectedClient.h"
using namespace ts::permission; using namespace ts::permission;
using namespace ts::permission::v2; using namespace ts::permission::v2;
@ -274,7 +276,7 @@ void PermissionService::load_group_assignments(const std::shared_ptr<CalculateCa
cache->assignment_server_groups.reserve(assignments.size()); cache->assignment_server_groups.reserve(assignments.size());
for(auto& entry : assignments) { for(auto& entry : assignments) {
auto group = group_manager->find_server_group(entry); auto group = group_manager->find_server_group(groups::GroupCalculateMode::GLOBAL, entry);
if(!group) continue; if(!group) continue;
cache->assignment_server_groups.push_back(group); cache->assignment_server_groups.push_back(group);
@ -304,7 +306,7 @@ void PermissionService::load_channel_assignment(const std::shared_ptr<CalculateC
group_id = assignment.group_id; group_id = assignment.group_id;
} }
cache->assignment_channel_group = group_manager->find_channel_group(group_id); cache->assignment_channel_group = group_manager->find_channel_group(groups::GroupCalculateMode::GLOBAL, group_id);
if(!cache->assignment_channel_group) if(!cache->assignment_channel_group)
cache->assignment_channel_group = group_manager->default_channel_group(cache->client_type); cache->assignment_channel_group = group_manager->default_channel_group(cache->client_type);
} }
@ -317,3 +319,54 @@ void PermissionService::load_server_channel(const std::shared_ptr<CalculateCache
auto tree_lock = this->virtual_server_->lock_channel_clients(); auto tree_lock = this->virtual_server_->lock_channel_clients();
cache->server_channel = channel_tree->findChannel(channel_id); cache->server_channel = channel_tree->findChannel(channel_id);
} }
PermissionResetResult PermissionService::reset_server_permissions() {
//TODO: Delete all tokens?
LOG_SQL_CMD(sql::command(this->virtual_server_->sql_manager(), "DELETE FROM `permissions` WHERE `serverId` = :serverId", variable{":serverId", this->get_server_id()}).execute());
LOG_SQL_CMD(sql::command(this->virtual_server_->sql_manager(), "DELETE FROM `assignedGroups` WHERE `serverId` = :serverId", variable{":serverId", this->get_server_id()}).execute());
LOG_SQL_CMD(sql::command(this->virtual_server_->sql_manager(), "DELETE FROM `groups` WHERE `serverId` = :serverId", variable{":serverId", this->get_server_id()}).execute());
auto group_manager = this->virtual_server_->group_manager();
group_manager->reset_groups(false);
auto properties = this->virtual_server_->server_properties();
properties->find(property::VIRTUALSERVER_AUTOGENERATED_PRIVILEGEKEY) = "";
properties->find(property::VIRTUALSERVER_ASK_FOR_PRIVILEGEKEY) = true;
if(auto vs = dynamic_cast<vserver::VirtualServer*>(this->virtual_server_); vs){
auto default_admin_group_id = serverInstance->properties().find(property::SERVERINSTANCE_TEMPLATE_SERVERADMIN_GROUP).as_save<GroupId>();
auto group = this->virtual_server_->group_manager()->find_server_group(groups::GroupCalculateMode::GLOBAL, default_admin_group_id);
if(!group) {
logCritical(LOG_INSTANCE, "Missing default template server admin group ({}). We're not generating an admin token.", default_admin_group_id);
} else {
auto group_name = group->display_name();
group = this->virtual_server_->group_manager()->find_server_group_by_name(groups::GroupCalculateMode::LOCAL, group_name);
if(!group) {
logError(this->get_server_id(), "Could not find server admin group from template name ({}). We're not generating an admin token.", group_name, group);
} else {
auto token = vs->token_manager().createToken(server::tokens::TOKEN_SERVER, group->group_id(), "Default server token for the server admin.");
if(!token) {
logError(this->get_server_id(), "Failed to generate default server admin token.");
} else {
properties->find(property::VIRTUALSERVER_AUTOGENERATED_PRIVILEGEKEY) = token->token;
properties->find(property::VIRTUALSERVER_ASK_FOR_PRIVILEGEKEY) = true;
}
}
}
}
for(const auto& client : this->virtual_server_->connected_clients(false)) {
if(client->getType() != ClientType::CLIENT_QUERY) {
client->notifyServerGroupList();
client->notifyChannelGroupList();
}
if(this->notifyClientPropertyUpdates(client, group_manager->assignments().update_client_group_properties(client, client->getChannelId()))) {
if(client->update_cached_permissions()) /* update cached calculated permissions */
client->sendNeededPermissions(false); /* cached permissions had changed, notify the client */
}
client->updateChannelClientProperties(true, true);
}
return PermissionResetResult::SUCCESS;
}

View File

@ -29,6 +29,10 @@ namespace ts::server::permissions {
ChannelId last_server_channel{0}; ChannelId last_server_channel{0};
}; };
enum struct PermissionResetResult {
SUCCESS
};
class PermissionService { class PermissionService {
public: public:
explicit PermissionService(vserver::VirtualServerBase*); explicit PermissionService(vserver::VirtualServerBase*);
@ -39,6 +43,8 @@ namespace ts::server::permissions {
bool load_data(std::string& /* error */); bool load_data(std::string& /* error */);
void unload_data(); void unload_data();
PermissionResetResult reset_server_permissions();
permission::v2::PermissionFlaggedValue calculate_client_permission( permission::v2::PermissionFlaggedValue calculate_client_permission(
permission::PermissionType, permission::PermissionType,
ClientDbId, ClientDbId,

View File

@ -0,0 +1,15 @@
//
// Created by WolverinDEV on 04/03/2020.
//
#include "./VirtualServerInformation.h"
#include "../vserver/VirtualServerBase.h"
using namespace ts::server::vserver;
InformationService::InformationService(ts::server::vserver::VirtualServerBase *handle) : virtual_server_{handle} {}
InformationService::~InformationService() {}
float InformationService::averagePing() {
}

View File

@ -0,0 +1,18 @@
#pragma once
namespace ts::server::vserver {
class VirtualServerBase;
class InformationService {
public:
explicit InformationService(VirtualServerBase*);
~InformationService();
[[nodiscard]] float averagePing();
[[nodiscard]] float averagePacketLoss();
[[nodiscard]] bool could_default_create_channel();
private:
VirtualServerBase* virtual_server_;
};
}

View File

@ -3,6 +3,9 @@
// //
#include "./VirtualServerBase.h" #include "./VirtualServerBase.h"
#include "./VirtualServerBase.h"
#include "../client/ConnectedClient.h"
#include "VirtualServerBase.h"
#include <log/LogUtils.h> #include <log/LogUtils.h>
using namespace ts::server::vserver; using namespace ts::server::vserver;
@ -76,3 +79,136 @@ VirtualServerStopResult VirtualServerBase::stop_server() {
VirtualServerStartResult VirtualServerBase::start_server_(ts::rwshared_lock<ts::rw_mutex> &slock, std::string &error) { VirtualServerStartResult VirtualServerBase::start_server_(ts::rwshared_lock<ts::rw_mutex> &slock, std::string &error) {
//TODO: Load server permissions, etc //TODO: Load server permissions, etc
} }
VirtualServerClientRegisterResult VirtualServerBase::register_client(const std::shared_ptr<ConnectedClient> &client) {
assert(client);
ts::rwshared_lock status_lock{this->status_mutex_};
if(this->server_status() != VirtualServerStatus::VIRTUAL && this->server_status() != VirtualServerStatus::RUNNING)
return VirtualServerClientRegisterResult::SERVER_NOT_ONLINE;
{
std::lock_guard client_lock{this->registered_clients.mutex};
if(client->getClientId() > 0) {
logCritical(this->server_id(), "Client {} ({}|{}) has been already registered somewhere!", client->getDisplayName(), client->getClientId(), client->getUid());
return VirtualServerClientRegisterResult::CLIENT_ALREADY_KNOWN;
}
ClientId client_id = 0;
ClientId max_client_id = this->registered_clients.clients.size();
while(client_id < max_client_id && this->registered_clients.clients[client_id])
client_id++;
if(client_id > 1024 * 8)
return VirtualServerClientRegisterResult::PROTOCOL_LIMIT_REACHED;
if(client_id == max_client_id)
this->registered_clients.clients.push_back(client);
else
this->registered_clients.clients[client_id] = client;
this->registered_clients.count++;
client->setClientId(client_id);
}
{
std::lock_guard nlock{this->client_nickname_lock_};
auto login_name = client->getDisplayName();
while(login_name.length() < 3)
login_name += ".";
if(client->getExternalType() == ClientType::CLIENT_TEAMSPEAK)
client->properties()[property::CLIENT_LOGIN_NAME] = login_name;
std::shared_ptr<ConnectedClient> found_client = nullptr;
auto client_name = login_name;
size_t counter = 0;
{
std::lock_guard clients_lock(this->registered_clients.mutex);
while(true) {
for(auto& _client : this->registered_clients.clients) {
if(!_client) continue;
if(_client->getDisplayName() == client_name && _client != client)
goto increase_name;
}
goto nickname_valid;
increase_name:
client_name = login_name + std::to_string(++counter);
}
}
nickname_valid:
client->setDisplayName(client_name);
}
{
auto properties = this->server_properties();
if(client->getType() == ClientType::CLIENT_TEAMSPEAK || client->getType() == ClientType::CLIENT_WEB) {
properties->find(property::VIRTUALSERVER_CLIENT_CONNECTIONS) ++; //increase client connections
properties->find(property::VIRTUALSERVER_LAST_CLIENT_CONNECT) = std::chrono::duration_cast<std::chrono::seconds>(std::chrono::system_clock::now().time_since_epoch()).count();
} else if(client->getType() == ClientType::CLIENT_QUERY) {
properties->find(property::VIRTUALSERVER_LAST_QUERY_CONNECT) ++; //increase query connections
properties->find(property::VIRTUALSERVER_QUERY_CLIENT_CONNECTIONS) = std::chrono::duration_cast<std::chrono::seconds>(std::chrono::system_clock::now().time_since_epoch()).count();
}
}
return VirtualServerClientRegisterResult::SUCCESS;
}
void VirtualServerBase::unregister_client(const std::shared_ptr<ConnectedClient> &client) {
ts::rwshared_lock status_lock{this->status_mutex_, ts::rw_lock_defered};
{
auto execute_lock = client->lock_command_handling();
if(client->getChannel()) {
auto tree_lock = this->lock_channel_clients();
auto result = this->channel_service().client_move(client, nullptr, nullptr, "", ViewReasonId::VREASON_SERVER_LEFT, false, tree_lock);
if(result != channels::ClientMoveResult::SUCCESS)
logCritical(this->server_id(), "Failed to unregister client from channel tree on client unregister ({}).", (int) result);
}
status_lock.auto_lock_shared();
auto client_id = client->getClientId();
if(client_id == 0) return; /* client hasn't been registered */
{
std::lock_guard lock(this->registered_clients.mutex);
if(client_id >= this->registered_clients.clients.size()) {
logCritical(this->server_id(), "Client {} ({}|{}) has been registered, but client id exceed client id! Failed to unregister client.", client->getDisplayName(), client_id, client->getUid());
} else {
auto& client_container = this->registered_clients.clients[client_id];
if(client_container != client) {
logCritical(this->server_id(), "Client {} ({}|{}) has been registered, but container hasn't client set! Failed to unregister client.", client->getDisplayName(), client_id, client->getUid());
} else {
client_container.reset();
this->registered_clients.count--;
}
}
}
}
{
auto properties = this->server_properties();
if(client->getType() == ClientType::CLIENT_TEAMSPEAK || client->getType() == ClientType::CLIENT_WEB) {
properties->find(property::VIRTUALSERVER_LAST_CLIENT_DISCONNECT) = std::chrono::duration_cast<std::chrono::seconds>(std::chrono::system_clock::now().time_since_epoch()).count();
} else if(client->getType() == ClientType::CLIENT_QUERY) {
properties->find(property::VIRTUALSERVER_LAST_QUERY_DISCONNECT) = std::chrono::duration_cast<std::chrono::seconds>(std::chrono::system_clock::now().time_since_epoch()).count();
}
}
{
if(!chan_tree_lock.owns_lock())
chan_tree_lock.lock();
if(cl->currentChannel) //We dont have to make him invisible if he hasnt even a channel
this->client_move(cl, nullptr, nullptr, reason, ViewReasonId::VREASON_SERVER_LEFT, false, chan_tree_lock);
}
serverInstance->databaseHelper()->saveClientPermissions(this->ref(), cl->getClientDatabaseId(), cl->clientPermissions);
cl->setClientId(0);
return true;
}

View File

@ -57,6 +57,19 @@ namespace ts::server::vserver {
STATE_LOCK_FAILED STATE_LOCK_FAILED
}; };
enum struct VirtualServerClientRegisterResult {
SUCCESS,
PROTOCOL_LIMIT_REACHED,
CLIENT_ALREADY_KNOWN,
SERVER_NOT_ONLINE
};
#define var_access_requires_server_loaded(variable) \
do { \
assert(this->status_ != VirtualServerStatus::STOPPED); \
return variable; \
} while(0);
class VirtualServerBase { class VirtualServerBase {
public: public:
//TODO: Constructor //TODO: Constructor
@ -85,15 +98,15 @@ namespace ts::server::vserver {
[[nodiscard]] inline ts::rwshared_lock<ts::rw_mutex> lock_channel_clients() { return ts::rwshared_lock{this->channel_clients_lock_, ts::rw_lock_shared}; } [[nodiscard]] inline ts::rwshared_lock<ts::rw_mutex> 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 std::mutex& client_nickname_lock() { return this->client_nickname_lock_; }
[[nodiscard]] inline const std::shared_ptr<Properties>& server_properties() { return this->server_properties_; } [[nodiscard]] inline const std::shared_ptr<Properties>& server_properties() { var_access_requires_server_loaded(this->server_properties_); }
[[nodiscard]] inline const std::shared_ptr<ServerChannelTree>& server_channel_tree() { return this->server_channel_tree_; } [[nodiscard]] inline const std::shared_ptr<ServerChannelTree>& server_channel_tree() { var_access_requires_server_loaded(this->server_channel_tree_); }
[[nodiscard]] inline const std::shared_ptr<groups::GroupManager>& group_manager() { return this->group_manager_; } [[nodiscard]] inline const std::shared_ptr<groups::GroupManager>& group_manager() { var_access_requires_server_loaded(this->group_manager_); }
/* client management */ /* client management */
/* this may block until the current command of the client has been finished executed */ /* this may block until the current command of the client has been finished executed */
void unregister_client(const std::shared_ptr<ConnectedClient>& /* client */); void unregister_client(const std::shared_ptr<ConnectedClient>& /* client */);
void register_client(const std::shared_ptr<ConnectedClient>& /* client */); VirtualServerClientRegisterResult register_client(const std::shared_ptr<ConnectedClient>& /* client */);
/* ATTENTION: lock_channel_clients should be acquired for the following methods. Else the client might be disconnected while working with him. */ /* ATTENTION: lock_channel_clients should be acquired for the following methods. Else the client might be disconnected while working with him. */
[[nodiscard]] std::vector<std::shared_ptr<ConnectedClient>> connected_clients(bool /* must be registered within the channel tree */); [[nodiscard]] std::vector<std::shared_ptr<ConnectedClient>> connected_clients(bool /* must be registered within the channel tree */);

View File

@ -0,0 +1,5 @@
//
// Created by WolverinDEV on 04/03/2020.
//
#include "VirtualServerManager.h"

View File

@ -0,0 +1,21 @@
#pragma once
#include <string>
namespace ts::server::vserver {
enum struct VirtualServerStartResult {
};
class VirtualServerManager {
public:
explicit VirtualServerManager();
~VirtualServerManager();
bool initialize(std::string& /* error */);
private:
};
}

2
shared

@ -1 +1 @@
Subproject commit ae4751ee6fcb857771ff5f4e57459a75638e2b1f Subproject commit 0d0d7dd1924ee93bcbe4875c3cf007117d05e4d7