Finalizing the new group manager
This commit is contained in:
parent
061ce4c284
commit
5991e5f4ad
|
@ -8,7 +8,7 @@ set(TEASPEAK_SERVER ON)
|
|||
|
||||
#end now
|
||||
#set(MEMORY_DEBUG_FLAGS " -fsanitize=leak -fsanitize=address -fstack-protector-all ")
|
||||
#set(MEMORY_DEBUG_FLAGS "-fsanitize=address -fstack-protector-all")
|
||||
set(MEMORY_DEBUG_FLAGS "-fsanitize=address -fstack-protector-all")
|
||||
#set(MEMORY_DEBUG_FLAGS "-fstack-protector-all")
|
||||
#set(MEMORY_DEBUG_FLAGS " -fsanitize=address -static-libasan")
|
||||
|
||||
|
|
|
@ -29,8 +29,9 @@ void LocalFileTransfer::shutdown_disk_io() {
|
|||
{
|
||||
std::unique_lock qlock{this->disk_io.queue_lock};
|
||||
this->disk_io.notify_work_awaiting.notify_all();
|
||||
while(this->disk_io.queue_head)
|
||||
while(this->disk_io.queue_head) {
|
||||
this->disk_io.notify_client_processed.wait_for(qlock, std::chrono::seconds{10});
|
||||
}
|
||||
|
||||
if(this->disk_io.queue_head) {
|
||||
logWarning(0, "Failed to flush disk IO. Force aborting.");
|
||||
|
|
|
@ -1 +1 @@
|
|||
Subproject commit d0babbc62fb657783a04737a1265ab1c36636bf6
|
||||
Subproject commit 8c2608f90739a42cd727c8c8eab3d60bbb6dca53
|
|
@ -117,12 +117,12 @@ bool DatabaseHandler::setup(std::string& error) {
|
|||
SET_VERSION(6);
|
||||
|
||||
case 6:
|
||||
CTBL("CREATE TABLE license_upgrade_log (`upgrade_id` INT, `timestamp` INT, `unique_id` VARCHAR(64), `server_ip` INT, `succeeded` TINYINT);");
|
||||
CTBL("CREATE TABLE license_upgrade_log (`upgrade_id` INT, `timestamp` INT, `unique_id` VARCHAR(64), `server_ip` VARCHAR(32), `succeeded` TINYINT);");
|
||||
CIDX("CREATE INDEX `upgrade_id_timestamp` ON `license_upgrade_log` (`upgrade_id`, `timestamp`)");
|
||||
SET_VERSION(7);
|
||||
|
||||
default:;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
2
rtclib
2
rtclib
|
@ -1 +1 @@
|
|||
Subproject commit cdf42fccd34c769fabfef7208f829c3f4b62a595
|
||||
Subproject commit ea2dd197d26330850dfef795cf7db140d26194d7
|
|
@ -56,6 +56,7 @@ set(SERVER_SOURCE_FILES
|
|||
src/server/VoiceServer.cpp
|
||||
src/server/POWHandler.cpp
|
||||
src/client/voice/VoiceClientConnection.cpp
|
||||
src/client/command_handler/groups.cpp
|
||||
src/client/command_handler/channel.cpp
|
||||
src/client/command_handler/client.cpp
|
||||
src/client/command_handler/server.cpp
|
||||
|
@ -223,7 +224,7 @@ target_link_libraries(PermMapHelper
|
|||
|
||||
SET(CPACK_PACKAGE_VERSION_MAJOR "1")
|
||||
SET(CPACK_PACKAGE_VERSION_MINOR "5")
|
||||
SET(CPACK_PACKAGE_VERSION_PATCH "2")
|
||||
SET(CPACK_PACKAGE_VERSION_PATCH "3")
|
||||
if (BUILD_TYPE_NAME EQUAL OFF)
|
||||
SET(CPACK_PACKAGE_VERSION_DATA "beta")
|
||||
elseif (BUILD_TYPE_NAME STREQUAL "")
|
||||
|
|
|
@ -172,8 +172,6 @@ inline sql::result load_permissions_v2(
|
|||
sql::command& command,
|
||||
bool test_channel, /* only used for client permissions (client channel permissions) */
|
||||
bool is_channel) {
|
||||
auto start = system_clock::now();
|
||||
|
||||
return command.query([&](int length, char** values, char** names){
|
||||
permission::PermissionType key = permission::PermissionType::undefined;
|
||||
permission::PermissionValue value = permNotGranted, granted = permNotGranted;
|
||||
|
@ -214,10 +212,11 @@ inline sql::result load_permissions_v2(
|
|||
return 0;
|
||||
}
|
||||
|
||||
if(channel_id == 0 || is_channel)
|
||||
if(channel_id == 0 || is_channel) {
|
||||
manager->load_permission(key, {value, granted}, skipped, negated, value != permNotGranted, granted != permNotGranted);
|
||||
else
|
||||
} else {
|
||||
manager->load_permission(key, {value, granted}, channel_id, skipped, negated, value != permNotGranted, granted != permNotGranted);
|
||||
}
|
||||
return 0;
|
||||
});
|
||||
|
||||
|
@ -387,7 +386,7 @@ std::shared_ptr<permission::v2::PermissionManager> DatabaseHelper::loadGroupPerm
|
|||
return result;
|
||||
}
|
||||
|
||||
void DatabaseHelper::saveGroupPermissions(const ServerId &server_id, ts::GroupId group_id, uint8_t /* target */, const std::shared_ptr<ts::permission::v2::PermissionManager> &permissions) {
|
||||
void DatabaseHelper::saveGroupPermissions(const ServerId &server_id, ts::GroupId group_id, uint8_t target, const std::shared_ptr<ts::permission::v2::PermissionManager> &permissions) {
|
||||
const auto updates = permissions->flush_db_updates();
|
||||
if(updates.empty())
|
||||
return;
|
||||
|
@ -398,8 +397,8 @@ void DatabaseHelper::saveGroupPermissions(const ServerId &server_id, ts::GroupId
|
|||
auto permission_data = permission::resolvePermissionData(update.permission);
|
||||
auto value = update.update_value == v2::delete_value ? permNotGranted : update.values.value;
|
||||
auto grant = update.update_grant == v2::delete_value ? permNotGranted : update.values.grant;
|
||||
logTrace(server_id, "[CHANNEL] Updating group permission for group {}: {}. New value: {}. New grant: {}. Query: {}",
|
||||
group_id,
|
||||
logTrace(server_id, "Updating group permission for group {}/{}: {}. New value: {}. New grant: {}. Query: {}",
|
||||
target, group_id,
|
||||
permission_data->name,
|
||||
value,
|
||||
grant,
|
||||
|
@ -608,9 +607,7 @@ bool DatabaseHelper::assignDatabaseId(sql::SqlManager *sql, ServerId serverId, s
|
|||
return false;
|
||||
|
||||
debugMessage(serverId, "Successfully registered client {} for server {} with database id {}.", cl->getUid(), serverId, cl->getClientDatabaseId());
|
||||
return true;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
|
@ -12,961 +12,3 @@ using namespace std::chrono;
|
|||
using namespace ts;
|
||||
using namespace ts::server;
|
||||
using namespace ts::permission;
|
||||
|
||||
extern InstanceHandler* serverInstance;
|
||||
Group::Group(GroupManager* handle, GroupTarget target, GroupType type, GroupId groupId) : _target(target), _type(type) {
|
||||
memtrack::allocated<Group>(this);
|
||||
this->handle = handle;
|
||||
|
||||
this->_properties = std::make_shared<PropertyManager>();
|
||||
this->_properties->register_property_type<property::GroupProperties>();
|
||||
this->setPermissionManager(make_shared<permission::v2::PermissionManager>());
|
||||
|
||||
this->properties()[property::GROUP_ID] = groupId;
|
||||
this->properties()[property::GROUP_TYPE] = type;
|
||||
/*
|
||||
this->_properties->registerProperty("sgid", groupId, PROP_GROUP_INIT_SERVER);
|
||||
this->_properties->registerProperty("cgid", groupId, PROP_GROUP_INIT_CHANNEL);
|
||||
this->_properties->registerProperty("gid", groupId, PROP_PRIVATE_TEMP);
|
||||
|
||||
|
||||
this->_properties->registerProperty("type", (uint8_t) type, PROP_GROUP_INIT);
|
||||
this->_properties->registerProperty("name", "Undefined group #" + to_string(groupId), PROP_GROUP_INIT | PROP_SNAPSHOT);
|
||||
this->_properties->registerProperty("sortid", 0, PROP_GROUP_INIT);
|
||||
this->_properties->registerProperty("savedb", 0, PROP_GROUP_INIT);
|
||||
this->_properties->registerProperty("namemode", 0, PROP_GROUP_INIT);
|
||||
this->_properties->registerProperty("iconid", 0, PROP_GROUP_INIT);
|
||||
|
||||
this->_properties->registerProperty("default_group", false, PROP_PRIVATE_TEMP);
|
||||
*/
|
||||
|
||||
this->_properties->registerNotifyHandler([&](Property& prop){
|
||||
if((prop.type().flags & property::FLAG_SAVE) == 0) return;
|
||||
if(!this->handle) return;
|
||||
|
||||
std::string sql;
|
||||
if(prop.hasDbReference()){
|
||||
sql = "UPDATE `properties` SET `value` = :value WHERE `serverId` = :sid AND `type` = :type AND `id` = :gid AND `key` = :key";
|
||||
} else {
|
||||
prop.setDbReference(true);
|
||||
sql = "INSERT INTO `properties` (`serverId`, `type`, `id`, `key`, `value`) VALUES (:sid, :type, :gid, :key, :value)";
|
||||
}
|
||||
|
||||
sql::command(this->handle->sql, sql,
|
||||
variable{":sid", this->handle->getServerId()}, variable{":type", property::PropertyType::PROP_TYPE_GROUP}, variable{":gid", this->groupId()}, variable{":key", prop.type().name}, variable{":value", prop.value()})
|
||||
.executeLater().waitAndGetLater(LOG_SQL_CMD, {1, "future failed"});
|
||||
});
|
||||
}
|
||||
|
||||
void Group::setPermissionManager(const std::shared_ptr<permission::v2::PermissionManager> &manager) {
|
||||
this->_permissions = manager;
|
||||
this->apply_properties_from_permissions();
|
||||
}
|
||||
|
||||
void Group::apply_properties_from_permissions() {
|
||||
{
|
||||
auto permission = this->_permissions->permission_value_flagged(permission::i_icon_id);
|
||||
this->properties()[property::GROUP_ICONID] = permission.has_value ? permission.value : 0;
|
||||
}
|
||||
{
|
||||
auto permission = this->_permissions->permission_value_flagged(permission::i_group_show_name_in_tree);
|
||||
this->properties()[property::GROUP_NAMEMODE] = permission.has_value ? permission.value : 0;
|
||||
}
|
||||
{
|
||||
auto permission = this->_permissions->permission_value_flagged(permission::i_group_sort_id);
|
||||
this->properties()[property::GROUP_SORTID] = permission.has_value ? permission.value : 0;
|
||||
}
|
||||
{
|
||||
auto permission = this->_permissions->permission_value_flagged(permission::b_group_is_permanent);
|
||||
this->properties()[property::GROUP_SAVEDB] = permission.has_value ? permission.value : 0;
|
||||
}
|
||||
}
|
||||
|
||||
Group::~Group() {
|
||||
memtrack::freed<Group>(this);
|
||||
}
|
||||
|
||||
GroupManager::GroupManager(const shared_ptr<VirtualServer> &server, sql::SqlManager *sql, std::shared_ptr<GroupManager> root) : server(server), sql(sql), root(std::move(root)) { }
|
||||
|
||||
GroupManager::~GroupManager() {}
|
||||
|
||||
bool GroupManager::loadGroupFormDatabase(GroupId id) {
|
||||
std::lock_guard glock{this->group_lock};
|
||||
if(id == 0){
|
||||
this->groups.clear();
|
||||
|
||||
auto res = sql::command(this->sql, "SELECT * FROM `groups` WHERE `serverId` = :sid", variable{":sid", this->getServerId()}).query(&GroupManager::insertGroupFromDb, this);
|
||||
LOG_SQL_CMD(res);
|
||||
|
||||
return true;
|
||||
} else {
|
||||
for(const auto &e : this->groups)
|
||||
if(e->groupId() == id){
|
||||
this->groups.erase(find(this->groups.begin(), this->groups.end(), e));
|
||||
break;
|
||||
}
|
||||
auto res = sql::command(this->sql, "SELECT * FROM `groups` WHERE `serverId` = :sid AND `groupId` = :gid",
|
||||
variable{":sid", this->getServerId()}, variable{":gid", id}).query(&GroupManager::insertGroupFromDb, this);
|
||||
auto fn = LOG_SQL_CMD;
|
||||
fn(res);
|
||||
return res;
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<std::shared_ptr<Group>> GroupManager::availableGroups(bool root) {
|
||||
std::vector<std::shared_ptr<Group>> response;
|
||||
|
||||
{
|
||||
std::lock_guard glock{this->group_lock};
|
||||
for(const auto& group : this->groups)
|
||||
response.push_back(group);
|
||||
}
|
||||
|
||||
if(root && this->root){
|
||||
auto elm = this->root->availableGroups();
|
||||
for(const auto& e : elm)
|
||||
response.push_back(e);
|
||||
}
|
||||
return response;
|
||||
}
|
||||
|
||||
std::vector<std::shared_ptr<Group>> GroupManager::availableServerGroups(bool root){
|
||||
std::vector<std::shared_ptr<Group>> response;
|
||||
|
||||
{
|
||||
std::lock_guard glock{this->group_lock};
|
||||
for(const auto& group : this->groups)
|
||||
if(group->target() == GroupTarget::GROUPTARGET_SERVER)
|
||||
response.push_back(group);
|
||||
}
|
||||
|
||||
if(root && this->root){
|
||||
auto elm = this->root->availableServerGroups();
|
||||
for(const auto& e : elm)
|
||||
response.push_back(e);
|
||||
}
|
||||
return response;
|
||||
}
|
||||
|
||||
std::vector<std::shared_ptr<Group>> GroupManager::availableChannelGroups(bool root) {
|
||||
std::vector<std::shared_ptr<Group>> response;
|
||||
{
|
||||
std::lock_guard glock{this->group_lock};
|
||||
for(const auto& group : this->groups)
|
||||
if(group->target() == GroupTarget::GROUPTARGET_CHANNEL)
|
||||
response.push_back(group);
|
||||
}
|
||||
if(root && this->root){
|
||||
auto elm = this->root->availableChannelGroups(true);
|
||||
for(const auto& e : elm)
|
||||
response.push_back(e);
|
||||
}
|
||||
return response;
|
||||
}
|
||||
|
||||
int GroupManager::insertGroupFromDb(int count, char **values, char **column) {
|
||||
GroupId groupId = 0;
|
||||
auto target = (GroupTarget) 0xff;
|
||||
auto type = (GroupType) 0xff;
|
||||
std::string targetName;
|
||||
|
||||
for(int index = 0; index < count; index++){
|
||||
if(strcmp(column[index], "target") == 0)
|
||||
target = (GroupTarget) stoll(values[index]);
|
||||
else if(strcmp(column[index], "type") == 0)
|
||||
type = (GroupType) stoll(values[index]);
|
||||
else if(strcmp(column[index], "groupId") == 0)
|
||||
groupId = (GroupType) stoll(values[index]);
|
||||
else if(strcmp(column[index], "displayName") == 0)
|
||||
targetName = values[index];
|
||||
//else cerr << "Invalid group table row " << column[index] << endl;
|
||||
}
|
||||
|
||||
if((size_t) groupId == 0 || (size_t) target == 0xff || (size_t) type == 0xff || targetName.empty()) {
|
||||
logCritical(this->getServerId(), "Found invalid group ad database! (GroupId " + to_string(groupId) + ", Target " + to_string(target) + ", Type " + to_string(type) + ", Name '" + targetName + "')");
|
||||
return 0;
|
||||
}
|
||||
/*
|
||||
assert(groupId != 0);
|
||||
assert(target != 0xff);
|
||||
assert(type != 0xff);
|
||||
assert(!targetName.empty());
|
||||
*/
|
||||
|
||||
shared_ptr<Group> group = std::make_shared<Group>(this, target, type, groupId);
|
||||
|
||||
/*
|
||||
auto res = sql::command(this->sql, "SELECT `key`, `value` FROM `properties` WHERE `serverId` = :sid AND `type` = :type AND `id` = :gid",
|
||||
variable{":sid", this->getServerId()}, variable{":type", property::PROP_TYPE_GROUP}, variable{":gid", group->groupId()}).query([this](Group* g, int length, char** values, char** columns){
|
||||
string key, value;
|
||||
for(int index = 0; index < length; index++)
|
||||
if(strcmp(columns[index], "key") == 0)
|
||||
key = values[index];
|
||||
else if(strcmp(columns[index], "value") == 0)
|
||||
value = values[index];
|
||||
|
||||
auto info = property::info<property::GroupProperties>(key);
|
||||
if(info == property::GROUP_UNDEFINED) {
|
||||
logError(this->getServerId(), "Invalid property for group: " + key);
|
||||
return 0;
|
||||
}
|
||||
|
||||
auto prop = g->properties()[info.property];
|
||||
prop.setDbReference(true);
|
||||
prop.value(value, false);
|
||||
return 0;
|
||||
}, group.get());
|
||||
auto print = LOG_SQL_CMD;
|
||||
print(res);
|
||||
*/
|
||||
//FIXME load group properties view database helper (or drop it full because no saved properties)
|
||||
|
||||
group->properties()[property::GROUP_NAME] = targetName;
|
||||
|
||||
group->setPermissionManager(serverInstance->databaseHelper()->loadGroupPermissions(this->getServerId(), group->groupId(), 0));
|
||||
|
||||
debugMessage(this->getServerId(), "Push back group -> " + to_string(group->groupId()) + " - " + group->name());
|
||||
this->groups.push_back(group);
|
||||
|
||||
#if 0
|
||||
auto iconId = (IconId) group->icon_id();
|
||||
if(iconId != 0 && serverInstance && serverInstance->getFileServer() && !serverInstance->getFileServer()->iconExists(this->server.lock(), iconId)) {
|
||||
logMessage(this->getServerId(), "[FILE] Missing group icon (" + to_string(iconId) + ").");
|
||||
if(config::server::delete_missing_icon_permissions) group->permissions()->set_permission(permission::i_icon_id, {0, 0}, permission::v2::delete_value, permission::v2::do_nothing);
|
||||
}
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
|
||||
void GroupManager::handleChannelDeleted(const ChannelId& channel_id) {
|
||||
unique_lock cache_lock(this->cacheLock);
|
||||
auto cached_clients = std::vector<shared_ptr<CachedClient>>{this->cachedClients.begin(), this->cachedClients.end()};
|
||||
cache_lock.unlock();
|
||||
|
||||
for(auto& entry : cached_clients) {
|
||||
lock_guard entry_lock(entry->lock);
|
||||
entry->channel_groups.erase(channel_id);
|
||||
}
|
||||
}
|
||||
|
||||
bool GroupManager::isLocalGroup(std::shared_ptr<Group> gr) {
|
||||
std::lock_guard glock{this->group_lock};
|
||||
return std::find(this->groups.begin(), this->groups.end(), gr) != this->groups.end();
|
||||
}
|
||||
|
||||
std::shared_ptr<Group> GroupManager::defaultGroup(GroupTarget type, bool enforce_property) {
|
||||
threads::MutexLock lock(this->cacheLock);
|
||||
if(this->groups.empty()) return nullptr;
|
||||
|
||||
auto server = this->server.lock();
|
||||
auto id =
|
||||
server ?
|
||||
server->properties()[type == GroupTarget::GROUPTARGET_SERVER ? property::VIRTUALSERVER_DEFAULT_SERVER_GROUP
|
||||
: property::VIRTUALSERVER_DEFAULT_CHANNEL_GROUP].as_or<GroupId>(0) :
|
||||
serverInstance->properties()[property::SERVERINSTANCE_GUEST_SERVERQUERY_GROUP].as_or<GroupId>(0);
|
||||
auto group = this->findGroupLocal(id);
|
||||
if(group || enforce_property) return group;
|
||||
|
||||
{
|
||||
std::lock_guard glock{this->group_lock};
|
||||
for(auto elm : this->groups)
|
||||
if(elm->target() == type)
|
||||
return elm;
|
||||
}
|
||||
|
||||
return nullptr; //Worst case!
|
||||
}
|
||||
|
||||
std::shared_ptr<Group> GroupManager::findGroup(GroupId groupId) {
|
||||
auto result = this->findGroupLocal(groupId);
|
||||
if(!result && this->root) result = this->root->findGroup(groupId);
|
||||
return result;
|
||||
}
|
||||
|
||||
std::shared_ptr<Group> GroupManager::findGroupLocal(GroupId groupId) {
|
||||
std::lock_guard glock{this->group_lock};
|
||||
for(const auto& elm : this->groups)
|
||||
if(elm->groupId() == groupId) return elm;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
std::vector<std::shared_ptr<Group>> GroupManager::findGroup(GroupTarget target, std::string name) {
|
||||
vector<shared_ptr<Group>> res;
|
||||
{
|
||||
std::lock_guard glock{this->group_lock};
|
||||
for(const auto &elm : this->groups)
|
||||
if(elm->name() == name && elm->target() == target) res.push_back(elm);
|
||||
}
|
||||
if(this->root) {
|
||||
auto r = root->findGroup(target, name);
|
||||
for(const auto &e : r) res.push_back(e);
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
ServerId GroupManager::getServerId() {
|
||||
auto l = this->server.lock();
|
||||
return l ? l->getServerId() : 0;
|
||||
}
|
||||
|
||||
std::shared_ptr<Group> GroupManager::createGroup(GroupTarget target, GroupType type, std::string name) {
|
||||
if(type != GROUP_TYPE_NORMAL && this->root) return root->createGroup(target, type, name);
|
||||
|
||||
auto rawId = generateGroupId(this->sql);
|
||||
if(rawId <= 0) {
|
||||
logError(this->getServerId(), "Could not create a new group! ({})", "Could not generate group id");
|
||||
return nullptr;
|
||||
}
|
||||
auto groupId = (GroupId) rawId;
|
||||
auto res = sql::command(this->sql, "INSERT INTO `groups` (`serverId`, `groupId`, `target`, `type`, `displayName`) VALUES (:sid, :gid, :target, :type, :name)",
|
||||
variable{":sid", this->getServerId()}, variable{":gid", groupId}, variable{":target", target}, variable{":type", type}, variable{":name", name}).execute();
|
||||
auto print = LOG_SQL_CMD;
|
||||
print(res);
|
||||
if(!res) return nullptr;
|
||||
|
||||
std::shared_ptr<Group> group = std::make_shared<Group>(this, target, type, groupId);
|
||||
group->properties()[property::GROUP_NAME] = name;
|
||||
group->setPermissionManager(serverInstance->databaseHelper()->loadGroupPermissions(this->getServerId(), group->groupId(), 0));
|
||||
|
||||
std::lock_guard glock{this->group_lock};
|
||||
this->groups.push_back(group);
|
||||
return group;
|
||||
}
|
||||
|
||||
GroupId GroupManager::copyGroup(std::shared_ptr<Group> group, GroupType type, std::string name, ServerId targetServerId) {
|
||||
auto group_server = group->handle->getServerId();
|
||||
auto groupId = generateGroupId(this->sql);
|
||||
|
||||
auto print = LOG_SQL_CMD;
|
||||
auto res = sql::command(this->sql, "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{":target", groupId}, variable{":ssid", group_server}, variable{":tsid", targetServerId}, variable{":source", group->groupId()}, variable{":type", SQL_PERM_GROUP}).execute();
|
||||
print(res);
|
||||
|
||||
//Properties not used currently
|
||||
res = sql::command(this->sql, "INSERT INTO `properties` (`serverId`, `type`, `id`, `key`, `value`) SELECT :tsid AS `serverId`, `type`, :target AS `id`, `key`, `value` FROM `properties` WHERE `serverId` = :ssid AND `type` = :type AND `id` = :source",
|
||||
variable{":target", groupId}, variable{":ssid", group_server}, variable{":tsid", targetServerId}, variable{":source", group->groupId()}, variable{":type", property::PropertyType::PROP_TYPE_GROUP}).execute();
|
||||
print(res);
|
||||
|
||||
res = sql::command(this->sql, "INSERT INTO `groups` (`serverId`, `groupId`, `target`, `type`, `displayName`) VALUES (:sid, :gid, :target, :type, :name)",
|
||||
variable{":sid", targetServerId}, variable{":gid", groupId}, variable{":target", group->target()}, variable{":type", type}, variable{":name", name}).execute();
|
||||
print(res);
|
||||
|
||||
if(targetServerId == (this->getServerId()))
|
||||
this->loadGroupFormDatabase(groupId);
|
||||
else if(this->root)
|
||||
this->root->loadGroupFormDatabase(groupId);
|
||||
return groupId;
|
||||
}
|
||||
|
||||
bool GroupManager::copyGroupPermissions(const shared_ptr<Group> &source, const shared_ptr<Group> &target) {
|
||||
auto targetServer = target->handle->getServerId();
|
||||
auto sourceServer = source->handle->getServerId();
|
||||
|
||||
auto res = sql::command(this->sql, "DELETE FROM `permissions` WHERE `serverId` = :sid AND `type` = :type AND `id` = :id", variable{":sid", targetServer}, variable{":type", SQL_PERM_GROUP}, variable{":id", target->groupId()}).execute();
|
||||
LOG_SQL_CMD(res);
|
||||
|
||||
res = sql::command(this->sql, "INSERT INTO `permissions` (`serverId`, `type`, `id`, `channelId`, `permId`, `value`, `grant`) SELECT :tsid AS `serverId`, `type`, :target AS `id`, 0 AS `channelId`, `permId`, `value`,`grant` FROM `permissions` WHERE `serverId` = :ssid AND `type` = :type AND `id` = :source",
|
||||
variable{":ssid", sourceServer}, variable{":tsid", targetServer}, variable{":type", SQL_PERM_GROUP}, variable{":source", source->groupId()}, variable{":target", target->groupId()}).execute();
|
||||
|
||||
target->setPermissionManager(serverInstance->databaseHelper()->loadGroupPermissions(target->handle->getServerId(), target->groupId(), 0));
|
||||
LOG_SQL_CMD(res);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool GroupManager::reloadGroupPermissions(std::shared_ptr<Group> group) {
|
||||
if(!isLocalGroup(group)){
|
||||
if(this->root) return this->root->reloadGroupPermissions(group);
|
||||
return false;
|
||||
}
|
||||
|
||||
group->setPermissionManager(serverInstance->databaseHelper()->loadGroupPermissions(this->getServerId(), group->groupId(), 0));
|
||||
return true;
|
||||
}
|
||||
|
||||
bool GroupManager::renameGroup(std::shared_ptr<Group> group, std::string name) {
|
||||
if(!isLocalGroup(group)){
|
||||
if(this->root) return this->root->renameGroup(group, name);
|
||||
return false;
|
||||
}
|
||||
|
||||
// CREATE_TABLE("groups", "`serverId` INT NOT NULL, `groupId` INTEGER, `target` INT, `type` INT, `displayName` TEXT");
|
||||
group->properties()[property::GROUP_NAME] = name;
|
||||
sql::command(this->sql, "UPDATE `groups` SET `displayName` = :name WHERE `serverId` = :sid AND `groupId` = :gid AND `target` = :target",
|
||||
variable{":name", name}, variable{":gid", group->groupId()}, variable{":target", group->target()}, variable{":sid", this->getServerId()}).executeLater().waitAndGetLater(LOG_SQL_CMD, {-1, "failed"});
|
||||
return true;
|
||||
}
|
||||
|
||||
bool GroupManager::deleteAllGroups() {
|
||||
LOG_SQL_CMD(sql::command(this->sql, "DELETE FROM `groups` WHERE `serverId` = :sid", variable{":sid", this->getServerId()}).execute());
|
||||
LOG_SQL_CMD(sql::command(this->sql, "DELETE FROM `assignedGroups` WHERE `serverId` = :sid", variable{":sid", this->getServerId()}).execute());
|
||||
LOG_SQL_CMD(sql::command(this->sql, "DELETE FROM `permissions` WHERE `serverId` = :sid AND `type` = :type", variable{":sid", this->getServerId()}, variable{":type", SQL_PERM_GROUP}).execute());
|
||||
{
|
||||
lock_guard cache_lock(this->cacheLock);
|
||||
for(const auto& entry : this->cachedClients) {
|
||||
lock_guard entry_lock(entry->lock);
|
||||
entry->server_groups.clear();
|
||||
entry->channel_groups.clear();
|
||||
}
|
||||
}
|
||||
this->groups.clear();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool GroupManager::deleteGroup(std::shared_ptr<Group> group) {
|
||||
if(!isLocalGroup(group)){
|
||||
if(this->root) return this->root->deleteGroup(group);
|
||||
return false;
|
||||
}
|
||||
|
||||
{
|
||||
std::lock_guard glock{this->group_lock};
|
||||
this->groups.erase(std::find(this->groups.begin(), this->groups.end(), group));
|
||||
}
|
||||
|
||||
/* erase the group out of our cache */
|
||||
{
|
||||
lock_guard cache_lock(this->cacheLock);
|
||||
for(auto& entry : this->cachedClients) {
|
||||
lock_guard entry_lock(entry->lock);
|
||||
|
||||
entry->server_groups.erase(std::remove_if(entry->server_groups.begin(), entry->server_groups.end(), [&](const std::shared_ptr<GroupAssignment>& group_assignment) {
|
||||
return group_assignment->group == group;
|
||||
}), entry->server_groups.end());
|
||||
|
||||
for(auto it = entry->channel_groups.begin(); it != entry->channel_groups.end();) {
|
||||
if(it->second->group == group)
|
||||
it = entry->channel_groups.erase(it);
|
||||
else
|
||||
it++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool flag_sql = false;
|
||||
auto res = sql::command(this->sql, "DELETE FROM `groups` WHERE `serverId` = :sid AND `groupId` = :gid", variable{":sid", this->getServerId()}, variable{":gid", group->groupId()}).execute();
|
||||
LOG_SQL_CMD(res);
|
||||
flag_sql |= !res;
|
||||
|
||||
res = sql::command(this->sql, "DELETE FROM `assignedGroups` WHERE `serverId` = :sid AND `groupId` = :gid", variable{":sid", this->getServerId()}, variable{":gid", group->groupId()}).execute();
|
||||
LOG_SQL_CMD(res);
|
||||
flag_sql |= !res;
|
||||
|
||||
serverInstance->databaseHelper()->deleteGroupArtifacts(this->getServerId(), group->groupId());
|
||||
|
||||
if(flag_sql)
|
||||
logError(this->getServerId(), "Could not delete group {} ({}) from database. May leader to invalid data", group->name(), group->groupId());
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
int64_t GroupManager::generateGroupId(sql::SqlManager* sql) {
|
||||
int64_t hightestGroupId = 0;
|
||||
sql::command(sql, "SELECT `groupId` FROM `groups` ORDER BY `groupId` DESC LIMIT 1").query([](int64_t* ptr, int, char** values, char**){
|
||||
*ptr = stoul(values[0]);
|
||||
return 0;
|
||||
}, &hightestGroupId);
|
||||
return hightestGroupId + 1;
|
||||
}
|
||||
|
||||
std::deque<property::ClientProperties> GroupManager::update_server_group_property(const shared_ptr<server::ConnectedClient> &client, bool channel_lock, const std::shared_ptr<BasicChannel>& channel) {
|
||||
std::deque<property::ClientProperties> changed;
|
||||
|
||||
//Server groups
|
||||
{
|
||||
auto groups = this->getServerGroups(client->getClientDatabaseId(), client->getType());
|
||||
string group_string;
|
||||
for(const auto& group : groups)
|
||||
if(group_string.empty()) group_string += to_string(group->group->groupId());
|
||||
else group_string += "," + to_string(group->group->groupId());
|
||||
if(client->properties()[property::CLIENT_SERVERGROUPS] != group_string) {
|
||||
client->properties()[property::CLIENT_SERVERGROUPS] = group_string;
|
||||
changed.push_back(property::CLIENT_SERVERGROUPS);
|
||||
|
||||
unique_lock chan_lock(client->channel_lock, defer_lock);
|
||||
if(channel_lock)
|
||||
chan_lock.lock();
|
||||
|
||||
client->cached_server_groups.clear();
|
||||
client->cached_server_groups.reserve(groups.size());
|
||||
for(const auto& group : groups)
|
||||
client->cached_server_groups.push_back(group->group->groupId());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
//Channel groups
|
||||
if(channel){
|
||||
shared_ptr<GroupAssignment> group = this->getChannelGroup(client->getClientDatabaseId(), channel, true);
|
||||
if(client->properties()[property::CLIENT_CHANNEL_GROUP_INHERITED_CHANNEL_ID] != group->channelId) {
|
||||
client->properties()[property::CLIENT_CHANNEL_GROUP_INHERITED_CHANNEL_ID] = group->channelId;
|
||||
changed.push_back(property::CLIENT_CHANNEL_GROUP_INHERITED_CHANNEL_ID);
|
||||
}
|
||||
if(client->properties()[property::CLIENT_CHANNEL_GROUP_ID] != group->group->groupId()) {
|
||||
client->properties()[property::CLIENT_CHANNEL_GROUP_ID] = group->group->groupId();
|
||||
changed.push_back(property::CLIENT_CHANNEL_GROUP_ID);
|
||||
|
||||
unique_lock chan_lock(client->channel_lock, defer_lock);
|
||||
if(channel_lock)
|
||||
chan_lock.lock();
|
||||
|
||||
client->cached_channel_group = group->group->groupId();
|
||||
}
|
||||
}
|
||||
|
||||
if(!changed.empty())
|
||||
client->join_state_id++; /* groups have changed :) */
|
||||
|
||||
return changed;
|
||||
}
|
||||
|
||||
void GroupManager::cleanupAssignments(ClientDbId client) {
|
||||
if(this->root)
|
||||
this->root->cleanupAssignments(client);
|
||||
|
||||
for(const auto& assignment : this->getAssignedServerGroups(client)) {
|
||||
if(!assignment->group->is_permanent())
|
||||
this->removeServerGroup(client, assignment->group);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void GroupManager::enableCache(const ClientDbId& client_database_id) {
|
||||
if(this->root)
|
||||
this->root->enableCache(client_database_id);
|
||||
|
||||
unique_lock cache_lock(this->cacheLock);
|
||||
/* test if we're already having the client */
|
||||
for(auto& entry : this->cachedClients)
|
||||
if(entry->client_database_id == client_database_id) {
|
||||
entry->use_count++;
|
||||
return; /* client already cached, no need to cache client */
|
||||
}
|
||||
|
||||
auto entry = std::make_shared<CachedClient>();
|
||||
entry->client_database_id = client_database_id;
|
||||
entry->use_count++;
|
||||
this->cachedClients.push_back(entry);
|
||||
|
||||
lock_guard client_cache_lock(entry->lock); /* lock the client because we're currently loading the cache. */
|
||||
cache_lock.unlock();
|
||||
|
||||
auto res = sql::command(this->sql, "SELECT `groupId`, `channelId`, `until` FROM `assignedGroups` WHERE `serverId` = :sid AND `cldbid` = :cldbid", variable{":sid", this->getServerId()}, variable{":cldbid", client_database_id})
|
||||
.query([&](int length, std::string* value, std::string* column) {
|
||||
shared_ptr<Group> group = nullptr;
|
||||
time_point<system_clock> until;
|
||||
ChannelId channelId = 0;
|
||||
|
||||
for(int index = 0; index < length; index++){
|
||||
try {
|
||||
if(column[index] == "groupId"){
|
||||
group = this->findGroup(stoll(value[index]));
|
||||
} else if(column[index] == "until"){
|
||||
until = time_point<system_clock>() + milliseconds(stoll(value[index]));
|
||||
} else if(column[index] == "channelId"){
|
||||
channelId = stoll(value[index]);
|
||||
} else {
|
||||
logError(this->getServerId(), "Unknown column in group assignment query: {}", column[index]);
|
||||
continue;
|
||||
}
|
||||
} catch(std::exception& ex) {
|
||||
logError(this->getServerId(), "Failed to load group assignment from database for client {}. Column {} contains an invalid value: {}", client_database_id, column[index], value[index]);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
if(!group) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
auto assignment = std::make_shared<GroupAssignment>();
|
||||
assignment->group = group;
|
||||
assignment->until = until;
|
||||
assignment->parent = &*entry;
|
||||
assignment->channelId = channelId;
|
||||
assignment->server = this->getServerId();
|
||||
|
||||
if(channelId == 0)
|
||||
entry->server_groups.push_back(assignment);
|
||||
else
|
||||
entry->channel_groups[channelId] = assignment;
|
||||
return 0;
|
||||
});
|
||||
}
|
||||
|
||||
//FIXME: This method till get far more often then it should be. We should add a flag if the group cache is loaded for each std::shared_ptr<ConnectedClient> instance
|
||||
void GroupManager::disableCache(const ClientDbId& client_database_id) {
|
||||
if(this->root) {
|
||||
this->root->disableCache(client_database_id);
|
||||
}
|
||||
|
||||
lock_guard cache_lock(this->cacheLock);
|
||||
this->cachedClients.erase(std::remove_if(this->cachedClients.begin(), this->cachedClients.end(), [&](const std::shared_ptr<CachedClient>& client) {
|
||||
if(client->client_database_id != client_database_id)
|
||||
return false;
|
||||
|
||||
lock_guard client_lock{client->lock};
|
||||
return (--client->use_count) == 0;
|
||||
}), this->cachedClients.end());
|
||||
}
|
||||
|
||||
void GroupManager::clearCache() {
|
||||
if(this->root) this->root->clearCache();
|
||||
|
||||
lock_guard lock(this->cacheLock);
|
||||
this->cachedClients.clear();
|
||||
}
|
||||
|
||||
bool GroupManager::isClientCached(const ClientDbId& client_database_id) {
|
||||
return this->resolve_cached_client(client_database_id) == nullptr;
|
||||
}
|
||||
|
||||
constexpr static auto kGroupMemberListQuery{R"(
|
||||
SELECT
|
||||
assignedGroups.cldbid,
|
||||
clients_server.client_unique_id,
|
||||
clients_server.client_nickname,
|
||||
assignedGroups.channelId,
|
||||
assignedGroups.until
|
||||
FROM assignedGroups
|
||||
INNER JOIN clients_server ON
|
||||
clients_server.client_database_id = assignedGroups.cldbid AND clients_server.server_id = :sid
|
||||
WHERE assignedGroups.`serverId` = :sid AND `groupId` = :gid;
|
||||
)"};
|
||||
|
||||
typedef std::vector<std::shared_ptr<GroupMember>> ResList;
|
||||
std::deque<GroupMember> GroupManager::listGroupMembers(std::shared_ptr<Group> group, bool names) {
|
||||
if(!isLocalGroup(group)){
|
||||
if(this->root)
|
||||
return this->root->listGroupMembers(group, names);
|
||||
return {};
|
||||
}
|
||||
|
||||
std::deque<GroupMember> result{};
|
||||
|
||||
size_t set_index{0};
|
||||
sql::command{this->sql, std::string{kGroupMemberListQuery}, variable{":sid", this->getServerId()}, variable{":gid", group->groupId()}}
|
||||
.query([&](int length, std::string* values, std::string* names) {
|
||||
set_index++;
|
||||
|
||||
auto index{0};
|
||||
try {
|
||||
auto& member = result.emplace_back();
|
||||
assert(names[index] == "cldbid");
|
||||
member.cldbId = std::stoull(values[index++]);
|
||||
|
||||
assert(names[index] == "client_unique_id");
|
||||
member.uid = values[index++];
|
||||
|
||||
assert(names[index] == "client_nickname");
|
||||
member.displayName = values[index++];
|
||||
|
||||
assert(names[index] == "channelId");
|
||||
member.channelId = std::stoull(values[index++]);
|
||||
|
||||
assert(names[index] == "until");
|
||||
member.until = std::chrono::system_clock::time_point{} + std::chrono::milliseconds{std::stoll(values[index++])};
|
||||
|
||||
assert(index == length);
|
||||
} catch (std::exception& ex) {
|
||||
result.pop_back();
|
||||
logError(this->getServerId(), "Failed to parse client group assignment for group {}: {}. Set index: {}, Column: {}",
|
||||
group->groupId(),
|
||||
ex.what(),
|
||||
set_index - 1,
|
||||
index - 1
|
||||
);
|
||||
return;
|
||||
}
|
||||
});
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
vector<shared_ptr<GroupAssignment>> GroupManager::listGroupAssignments(ClientDbId cldbId) {
|
||||
vector<std::shared_ptr<GroupAssignment>> result;
|
||||
sql::result res;
|
||||
|
||||
auto cached = resolve_cached_client(cldbId);
|
||||
if(cached) {
|
||||
{
|
||||
lock_guard lock{cached->lock};
|
||||
for(const auto &serverGroup : cached->server_groups)
|
||||
result.push_back(serverGroup);
|
||||
for(auto& channelGroup : cached->channel_groups)
|
||||
result.push_back(channelGroup.second);
|
||||
}
|
||||
|
||||
if(this->root){
|
||||
auto append = this->root->listGroupAssignments(cldbId);
|
||||
for(const auto &elm : append)
|
||||
result.push_back(elm);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
res = sql::command(this->sql, "SELECT `groupId`, `until`, `channelId` FROM `assignedGroups` WHERE `serverId` = :sid AND `cldbid` = :cldbid", variable{":sid", this->getServerId()}, variable{":cldbid", cldbId})
|
||||
.query([&](int length, char** value, char** column){
|
||||
shared_ptr<Group> group = nullptr;
|
||||
time_point<system_clock> until;
|
||||
uint64_t channelId = 0;
|
||||
|
||||
for(int index = 0; index < length; index++){
|
||||
if(value[index] == nullptr) {
|
||||
logError(this->getServerId(), string() + "Invalid value at " + column[index]);
|
||||
continue;
|
||||
}
|
||||
if(strcmp(column[index], "groupId") == 0){
|
||||
group = this->findGroup(stoll(value[index]));
|
||||
} else if(strcmp(column[index], "until") == 0){
|
||||
until = time_point<system_clock>() + milliseconds(stoll(value[index]));
|
||||
} else if(strcmp(column[index], "channelId") == 0){
|
||||
channelId = stoll(value[index]);
|
||||
} else cerr << "Invalid column " << column[index] << endl;
|
||||
}
|
||||
if(!group)
|
||||
return 0;
|
||||
|
||||
shared_ptr<GroupAssignment> assignment = std::make_shared<GroupAssignment>();
|
||||
assignment->parent = nullptr;
|
||||
assignment->group = group;
|
||||
assignment->until = until;
|
||||
assignment->channelId = channelId;
|
||||
assignment->server = this->getServerId();
|
||||
result.push_back(assignment);
|
||||
return 0;
|
||||
});
|
||||
(LOG_SQL_CMD)(res);
|
||||
|
||||
if(this->root){
|
||||
auto append = this->root->listGroupAssignments(cldbId);
|
||||
for(const auto &elm : append)
|
||||
result.push_back(elm);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
std::shared_ptr<CachedClient> GroupManager::resolve_cached_client(ClientDbId client_database_id) {
|
||||
{
|
||||
lock_guard lock(this->cacheLock);
|
||||
for(auto& entry : this->cachedClients)
|
||||
if(entry->client_database_id == client_database_id)
|
||||
return entry;
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
std::vector<std::shared_ptr<GroupAssignment>> GroupManager::getAssignedServerGroups(ClientDbId cldbid) {
|
||||
auto cached = this->resolve_cached_client(cldbid);
|
||||
sql::result res;
|
||||
std::vector<std::shared_ptr<GroupAssignment>> result;
|
||||
if(this->root) {
|
||||
auto client_groups = this->root->getAssignedServerGroups(cldbid);
|
||||
result.insert(result.begin(), client_groups.begin(), client_groups.end());
|
||||
}
|
||||
|
||||
if(cached) {
|
||||
lock_guard cache_lock{cached->lock};
|
||||
result.insert(result.end(), cached->server_groups.begin(), cached->server_groups.end());
|
||||
return result;
|
||||
}
|
||||
|
||||
debugMessage(this->getServerId(), "Query client groups for client {} on server {}.", cldbid, this->getServerId());
|
||||
res = sql::command(this->sql, "SELECT `groupId`, `until` FROM `assignedGroups` WHERE `serverId` = :sid AND `cldbid` = :cldbid AND `channelId` = 0", variable{":sid", this->getServerId()}, variable{":cldbid", cldbid}).query([&](int length, char** value, char** column){
|
||||
shared_ptr<Group> group = nullptr;
|
||||
time_point<system_clock> until;
|
||||
|
||||
for(int index = 0; index < length; index++) {
|
||||
if(value[index] == nullptr) {
|
||||
logError(this->getServerId(), string() + "Invalid value at " + column[index]);
|
||||
continue;
|
||||
}
|
||||
if(strcmp(column[index], "groupId") == 0 && value[index] != nullptr){
|
||||
group = this->findGroup(stoll(value[index]));
|
||||
} else if(strcmp(column[index], "until") == 0){
|
||||
until = time_point<system_clock>() + milliseconds(stoll(value[index] == nullptr ? "0" : value[index]));
|
||||
} else cerr << "Invalid column " << column[index] << endl;
|
||||
}
|
||||
if(!group)
|
||||
return 0;
|
||||
|
||||
shared_ptr<GroupAssignment> assignment = std::make_shared<GroupAssignment>();
|
||||
assignment->parent = nullptr;
|
||||
assignment->group = group;
|
||||
assignment->until = until;
|
||||
assignment->server = this->getServerId();
|
||||
result.push_back(assignment);
|
||||
return 0;
|
||||
});
|
||||
LOG_SQL_CMD(res);
|
||||
return result;
|
||||
}
|
||||
|
||||
std::vector<std::shared_ptr<GroupAssignment>> GroupManager::getServerGroups(ClientDbId cldbid, server::ClientType type) {
|
||||
auto result = this->getAssignedServerGroups(cldbid);
|
||||
if(result.empty()) return this->defaultServerGroupGroupAssignments(cldbid, type);
|
||||
return result;
|
||||
}
|
||||
|
||||
std::vector<std::shared_ptr<GroupAssignment>> GroupManager::defaultServerGroupGroupAssignments(ClientDbId client, ClientType type) {
|
||||
std::vector<std::shared_ptr<GroupAssignment>> result;
|
||||
if(type == ClientType::CLIENT_QUERY && this->root) {
|
||||
auto root = this->root->defaultServerGroupGroupAssignments(client, type);
|
||||
result.insert(result.begin(), root.begin(), root.end());
|
||||
} else if(type == ClientType::CLIENT_MUSIC) {
|
||||
threads::MutexLock lock(this->cacheLock);
|
||||
|
||||
auto server = this->server.lock();
|
||||
auto id =
|
||||
server ?
|
||||
server->properties()[property::VIRTUALSERVER_DEFAULT_MUSIC_GROUP].as_or<GroupId>(0) :
|
||||
serverInstance->properties()[property::SERVERINSTANCE_TEMPLATE_MUSICDEFAULT_GROUP].as_or<GroupId>(0);
|
||||
auto group = this->findGroupLocal(id);
|
||||
if(group) {
|
||||
result.push_back(std::make_shared<GroupAssignment>(nullptr, this->getServerId(), 0, group, time_point<system_clock>()));
|
||||
return result;
|
||||
}
|
||||
}
|
||||
result.push_back(std::make_shared<GroupAssignment>(nullptr, this->getServerId(), 0, this->defaultGroup(GroupTarget::GROUPTARGET_SERVER), time_point<system_clock>()));
|
||||
return result;
|
||||
}
|
||||
|
||||
std::shared_ptr<GroupAssignment> GroupManager::getChannelGroupExact(ClientDbId cldbId, const std::shared_ptr<BasicChannel>& channel, bool assign_default) {
|
||||
auto cached = resolve_cached_client(cldbId);
|
||||
if(cached) {
|
||||
lock_guard cache_lock(cached->lock);
|
||||
if(cached->channel_groups.count(channel->channelId()) > 0) {
|
||||
return cached->channel_groups[channel->channelId()];
|
||||
} else
|
||||
return assign_default ? this->defaultChannelGroupAssignment(cldbId, channel) : nullptr;
|
||||
}
|
||||
|
||||
std::shared_ptr<GroupAssignment> result;
|
||||
auto res = sql::command(this->sql, "SELECT `groupId`, `until` FROM `assignedGroups` WHERE `serverId` = :sid AND `cldbid` = :cldbid AND `channelId` = :chid", variable{":sid", this->getServerId()}, variable{":cldbid", cldbId}, variable{":chid", channel->channelId()}).query([&](int length, char** value, char** column){
|
||||
shared_ptr<Group> group = nullptr;
|
||||
time_point<system_clock> until;
|
||||
|
||||
for(int index = 0; index < length; index++){
|
||||
if(value[index] == nullptr) {
|
||||
logError(this->getServerId(), string() + "Invalid value at " + column[index]);
|
||||
continue;
|
||||
}
|
||||
if(strcmp(column[index], "groupId") == 0){
|
||||
group = this->findGroup(stoll(value[index]));
|
||||
} else if(strcmp(column[index], "until") == 0){
|
||||
until = time_point<system_clock>() + milliseconds(stoll(value[index]));
|
||||
} else cerr << "Invalid column " << column[index] << endl;
|
||||
}
|
||||
if(!group)
|
||||
return 0;
|
||||
|
||||
shared_ptr<GroupAssignment> assignment = std::make_shared<GroupAssignment>();
|
||||
assignment->parent = nullptr;
|
||||
assignment->group = group;
|
||||
assignment->until = until;
|
||||
assignment->server = this->getServerId();
|
||||
assignment->channelId = channel->channelId();
|
||||
result = std::move(assignment);
|
||||
return 0;
|
||||
});
|
||||
(LOG_SQL_CMD)(res);
|
||||
|
||||
return !result && assign_default ? this->defaultChannelGroupAssignment(cldbId, channel) : result;
|
||||
}
|
||||
|
||||
std::shared_ptr<GroupAssignment> GroupManager::getChannelGroup(ClientDbId cldbId, const shared_ptr<BasicChannel> &channel, bool assign_default) {
|
||||
shared_ptr<GroupAssignment> group;
|
||||
std::shared_ptr<BasicChannel> inheritance_channel = channel;
|
||||
while(inheritance_channel && !group) {
|
||||
group = this->getChannelGroupExact(cldbId, inheritance_channel, false);
|
||||
if(!group) {
|
||||
auto inheritance = inheritance_channel->permissions()->permission_value_flagged(permission::b_channel_group_inheritance_end);
|
||||
if(inheritance.has_value && inheritance.value == 1)
|
||||
break;
|
||||
|
||||
inheritance_channel = inheritance_channel->parent();
|
||||
}
|
||||
}
|
||||
return !group && assign_default ? this->defaultChannelGroupAssignment(cldbId, channel) : group;
|
||||
}
|
||||
|
||||
std::shared_ptr<GroupAssignment> GroupManager::defaultChannelGroupAssignment(ClientDbId cldbId, const std::shared_ptr<BasicChannel> &channel) {
|
||||
return std::make_shared<GroupAssignment>(nullptr, this->getServerId(), channel->channelId(), this->defaultGroup(GroupTarget::GROUPTARGET_CHANNEL), time_point<system_clock>());
|
||||
}
|
||||
|
||||
void GroupManager::addServerGroup(ClientDbId cldbId, std::shared_ptr<Group> group, time_point<system_clock> until) {
|
||||
/*
|
||||
if(!this->isLocalGroup(group)) {
|
||||
if(this->root) this->root->addServerGroup(cldbId, group, until);
|
||||
return;
|
||||
}
|
||||
if(hasServerGroup(cldbId, group)) return;
|
||||
*/
|
||||
|
||||
auto cached = resolve_cached_client(cldbId);
|
||||
if(cached) {
|
||||
lock_guard cache_lock(cached->lock);
|
||||
cached->server_groups.push_back(std::make_shared<GroupAssignment>(cached.get(), this->getServerId(), 0, group, until));
|
||||
}
|
||||
|
||||
sql::command(this->sql, "INSERT INTO `assignedGroups` (`serverId`, `cldbid`, `groupId`, `channelId`, `until`) VALUES (:sid, :cldbid, :gid, :chid, :until)",
|
||||
variable{":sid", this->getServerId()},
|
||||
variable{":cldbid", cldbId},
|
||||
variable{":gid", group->groupId()},
|
||||
variable{":chid", 0},
|
||||
variable{":until", time_point_cast<milliseconds>(until).time_since_epoch().count()})
|
||||
.executeLater().waitAndGetLater(LOG_SQL_CMD, {1, "future failed"});
|
||||
}
|
||||
|
||||
void GroupManager::removeServerGroup(ClientDbId cldbId, std::shared_ptr<Group> group) {
|
||||
if(!this->hasServerGroupAssigned(cldbId, group)) return;
|
||||
auto cached = resolve_cached_client(cldbId);
|
||||
if(cached) {
|
||||
lock_guard cache_lock(cached->lock);
|
||||
cached->server_groups.erase(std::remove_if(cached->server_groups.begin(), cached->server_groups.end(), [&](const std::shared_ptr<GroupAssignment>& group_assignment) {
|
||||
return group_assignment->group == group;
|
||||
}), cached->server_groups.end());
|
||||
}
|
||||
|
||||
sql::command(this->sql, "DELETE FROM `assignedGroups` WHERE `serverId` = :sid AND `cldbid` = :cldbid AND `groupId` = :gid AND `channelId` = :chid",
|
||||
variable{":sid", this->getServerId()},
|
||||
variable{":cldbid", cldbId},
|
||||
variable{":gid", group->groupId()},
|
||||
variable{":chid", 0})
|
||||
.executeLater().waitAndGetLater(LOG_SQL_CMD, {1, "future failed"});
|
||||
}
|
||||
|
||||
void GroupManager::setChannelGroup(ClientDbId cldbId, std::shared_ptr<Group> group, std::shared_ptr<BasicChannel> channel, time_point<system_clock> until) {
|
||||
auto old_group = getChannelGroupExact(cldbId, channel, false);
|
||||
if(old_group) {
|
||||
if(old_group->group == group) return;
|
||||
} else if(!group) return;
|
||||
|
||||
auto default_group = !group || group == this->defaultGroup(GroupTarget::GROUPTARGET_CHANNEL);
|
||||
auto cached = resolve_cached_client(cldbId);
|
||||
if(cached) {
|
||||
lock_guard cache_lock(cached->lock);
|
||||
if(default_group)
|
||||
cached->channel_groups.erase(channel->channelId());
|
||||
else
|
||||
cached->channel_groups[channel->channelId()] = std::make_shared<GroupAssignment>(cached.get(), this->getServerId(), channel->channelId(), group, until);
|
||||
}
|
||||
|
||||
sql::command(this->sql, "DELETE FROM `assignedGroups` WHERE `serverId` = :sid AND `cldbid` = :cldbid AND `channelId` = :chid",
|
||||
variable{":sid", this->getServerId()},
|
||||
variable{":cldbid", cldbId},
|
||||
variable{":chid", channel->channelId()})
|
||||
.executeLater().waitAndGetLater(LOG_SQL_CMD, {1, "future failed"});
|
||||
|
||||
if(!default_group) {
|
||||
sql::command(this->sql, "INSERT INTO `assignedGroups` (`serverId`, `cldbid`, `groupId`, `channelId`, `until`) VALUES (:sid, :cldbid, :gid, :chid, :until)",
|
||||
variable{":sid", this->getServerId()},
|
||||
variable{":cldbid", cldbId},
|
||||
variable{":gid", group->groupId()},
|
||||
variable{":chid", channel->channelId()},
|
||||
variable{":until", time_point_cast<milliseconds>(until).time_since_epoch().count()})
|
||||
.executeLater().waitAndGetLater(LOG_SQL_CMD, {1, "future failed"});
|
||||
}
|
||||
}
|
|
@ -921,7 +921,7 @@ bool InstanceHandler::validate_default_groups() {
|
|||
auto& assignments = this->group_manager_->assignments();
|
||||
auto client_assignments = assignments.server_groups_of_client(GroupAssignmentCalculateMode::GLOBAL, this->globalServerAdmin->getClientDatabaseId());
|
||||
if(client_assignments.empty()) {
|
||||
assignments.add_server_group(this->globalServerAdmin->getClientDatabaseId(), group_instance->group_id());
|
||||
assignments.add_server_group(this->globalServerAdmin->getClientDatabaseId(), group_instance->group_id(), false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
|
||||
#include "./InstanceHandler.h"
|
||||
#include "./groups/GroupManager.h"
|
||||
#include "./PermissionCalculator.h"
|
||||
|
||||
using namespace ts;
|
||||
using namespace ts::server;
|
||||
|
@ -24,53 +25,11 @@ permission::v2::PermissionFlaggedValue InstancePermissionHelper::calculate_permi
|
|||
std::vector<std::pair<permission::PermissionType, permission::v2::PermissionFlaggedValue> > InstancePermissionHelper::calculate_permissions(
|
||||
const std::deque<permission::PermissionType> &permissions,
|
||||
ClientDbId cldbid,
|
||||
ClientType /* type */,
|
||||
ChannelId /* channel */,
|
||||
ClientType type,
|
||||
ChannelId channel,
|
||||
bool granted,
|
||||
std::shared_ptr<CalculateCache> /* cache */
|
||||
) const {
|
||||
std::vector<std::pair<permission::PermissionType, permission::v2::PermissionFlaggedValue>> result{};
|
||||
|
||||
/* TODO: Use same algorithm as on normal servers */
|
||||
std::vector<std::shared_ptr<groups::ServerGroup>> assigned_groups{};
|
||||
{
|
||||
using groups::GroupAssignmentCalculateMode;
|
||||
using groups::GroupCalculateMode;
|
||||
|
||||
auto group_manager = this->instance->group_manager();
|
||||
auto& assignment_manager = group_manager->assignments();
|
||||
|
||||
auto assignments = assignment_manager.server_groups_of_client(GroupAssignmentCalculateMode::GLOBAL, cldbid);
|
||||
if(assignments.empty()) {
|
||||
assignments.push_back(this->instance->properties()[property::SERVERINSTANCE_GUEST_SERVERQUERY_GROUP].as_save<GroupId>());
|
||||
}
|
||||
|
||||
assigned_groups.reserve(assignments.size());
|
||||
for(const auto& group_id : assignments) {
|
||||
auto group = group_manager->server_groups()->find_group(GroupCalculateMode::GLOBAL, group_id);
|
||||
if(!group) {
|
||||
continue;
|
||||
}
|
||||
|
||||
assigned_groups.push_back(std::move(group));
|
||||
}
|
||||
}
|
||||
|
||||
for(const auto& permission : permissions) {
|
||||
permission::v2::PermissionFlaggedValue value{0, false};
|
||||
|
||||
for(const auto &gr : assigned_groups){
|
||||
auto group_permissions = gr->permissions();
|
||||
auto flagged_permissions = granted ? group_permissions->permission_granted_flagged(permission) : group_permissions->permission_value_flagged(permission);
|
||||
if(flagged_permissions.has_value) {
|
||||
if(!value.has_value || flagged_permissions.value > value.value || flagged_permissions.value == -1) {
|
||||
value = flagged_permissions;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
result.emplace_back(permission, value);
|
||||
}
|
||||
|
||||
return result;
|
||||
ClientPermissionCalculator calculator{nullptr, cldbid, type, channel};
|
||||
return calculator.calculate_permissions(permissions, granted);
|
||||
}
|
|
@ -1,11 +1,10 @@
|
|||
#include <netdb.h>
|
||||
#include <deque>
|
||||
#include <tuple>
|
||||
#include <fstream>
|
||||
#include <log/LogUtils.h>
|
||||
#include "InstanceHandler.h"
|
||||
#include "src/client/InternalClient.h"
|
||||
#include "src/server/QueryServer.h"
|
||||
#include "./groups/GroupManager.h"
|
||||
|
||||
using namespace std;
|
||||
using namespace std::chrono;
|
||||
|
@ -23,9 +22,9 @@ struct GroupInfo {
|
|||
*/
|
||||
int target;
|
||||
std::deque<string> properties;
|
||||
string name;
|
||||
std::string name;
|
||||
/* permission type, value, granted, skip, negate */
|
||||
deque<tuple<permission::PermissionType, permission::PermissionValue, permission::PermissionValue, bool, bool>> permissions;
|
||||
std::deque<std::tuple<permission::PermissionType, permission::PermissionValue, permission::PermissionValue, bool, bool>> permissions;
|
||||
};
|
||||
|
||||
/* TODO may use a transaction here? */
|
||||
|
@ -123,14 +122,41 @@ bool InstanceHandler::setupDefaultGroups() {
|
|||
for(const auto& info : groups) {
|
||||
debugMessage(LOG_INSTANCE, "Creating default group {} with type {}", info->name, to_string(info->target));
|
||||
//Query groups
|
||||
auto group = this->group_manager_->createGroup(
|
||||
info->target != 2 ? GroupTarget::GROUPTARGET_SERVER : GroupTarget::GROUPTARGET_CHANNEL,
|
||||
info->target == 0 ? GroupType::GROUP_TYPE_QUERY : GroupType::GROUP_TYPE_TEMPLATE,
|
||||
info->name
|
||||
);
|
||||
|
||||
std::shared_ptr<groups::Group> created_group{};
|
||||
groups::GroupCreateResult create_result{};
|
||||
|
||||
if(info->target == 2) {
|
||||
std::shared_ptr<groups::ChannelGroup> c_group{};
|
||||
create_result = serverInstance->group_manager()->channel_groups()->create_group(groups::GroupType::GROUP_TYPE_TEMPLATE, info->name, c_group);
|
||||
created_group = c_group;
|
||||
} else {
|
||||
std::shared_ptr<groups::ChannelGroup> s_group{};
|
||||
create_result = serverInstance->group_manager()->channel_groups()->create_group(info->target == 0 ? groups::GroupType::GROUP_TYPE_QUERY : groups::GroupType::GROUP_TYPE_TEMPLATE, info->name, s_group);
|
||||
created_group = s_group;
|
||||
}
|
||||
|
||||
switch(create_result) {
|
||||
case groups::GroupCreateResult::SUCCESS:
|
||||
break;
|
||||
|
||||
case groups::GroupCreateResult::DATABASE_ERROR:
|
||||
logCritical(LOG_INSTANCE, "Failed to insert template group {} (Database error)", info->name);
|
||||
return false;
|
||||
|
||||
case groups::GroupCreateResult::NAME_TOO_LONG:
|
||||
case groups::GroupCreateResult::NAME_ALREADY_IN_USED:
|
||||
case groups::GroupCreateResult::NAME_TOO_SHORT:
|
||||
logCritical(LOG_INSTANCE, "Failed to insert template group {} (Name issue)", info->name);
|
||||
return false;
|
||||
|
||||
default:
|
||||
logCritical(LOG_INSTANCE, "Failed to insert template group {} (Unkown error)", info->name);
|
||||
return false;
|
||||
}
|
||||
|
||||
for(auto perm : info->permissions) {
|
||||
group->permissions()->set_permission(get<0>(perm), {get<1>(perm), get<2>(perm)}, get<1>(perm) == permNotGranted ? permission::v2::do_nothing : permission::v2::set_value, get<2>(perm) == permNotGranted ? permission::v2::do_nothing : permission::v2::set_value, get<3>(perm), get<4>(perm));
|
||||
created_group->permissions()->set_permission(get<0>(perm), {get<1>(perm), get<2>(perm)}, get<1>(perm) == permNotGranted ? permission::v2::do_nothing : permission::v2::set_value, get<2>(perm) == permNotGranted ? permission::v2::do_nothing : permission::v2::set_value, get<3>(perm), get<4>(perm));
|
||||
}
|
||||
|
||||
for(const auto& property : info->properties) {
|
||||
|
@ -138,7 +164,7 @@ bool InstanceHandler::setupDefaultGroups() {
|
|||
if(prop.is_undefined()) {
|
||||
logCritical(LOG_INSTANCE, "Invalid template property name: " + property);
|
||||
} else {
|
||||
this->properties()[prop] = group->groupId();
|
||||
this->properties()[prop] = created_group->group_id();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -19,10 +19,18 @@ ClientPermissionCalculator::ClientPermissionCalculator(DataClient *client, Chann
|
|||
|
||||
auto server = client->getServer();
|
||||
if(server && channel_id > 0) {
|
||||
std::shared_lock channel_lock{server->get_channel_tree_lock()};
|
||||
auto channel = server->getChannelTree()->findChannel(channel_id);
|
||||
channel_lock.unlock();
|
||||
std::shared_ptr<BasicChannel> channel{};
|
||||
try {
|
||||
std::shared_lock channel_lock{server->get_channel_tree_lock()};
|
||||
channel = server->getChannelTree()->findChannel(channel_id);
|
||||
} catch (std::system_error& e) {
|
||||
if(e.code() != std::errc::resource_deadlock_would_occur) {
|
||||
throw;
|
||||
}
|
||||
|
||||
/* tree already write locked, no need to lock it again */
|
||||
channel = server->getChannelTree()->findChannel(channel_id);
|
||||
}
|
||||
if(channel) {
|
||||
this->channel_permissions = channel->permissions();
|
||||
}
|
||||
|
@ -56,10 +64,18 @@ ClientPermissionCalculator::ClientPermissionCalculator(
|
|||
this->group_manager_ = server->group_manager();
|
||||
this->default_channel_group = [server]{ return server->default_channel_group(); };
|
||||
|
||||
std::shared_lock channel_lock{server->get_channel_tree_lock()};
|
||||
auto channel = server->getChannelTree()->findChannel(channel_id);
|
||||
channel_lock.unlock();
|
||||
std::shared_ptr<BasicChannel> channel{};
|
||||
try {
|
||||
std::shared_lock channel_lock{server->get_channel_tree_lock()};
|
||||
channel = server->getChannelTree()->findChannel(channel_id);
|
||||
} catch (std::system_error& e) {
|
||||
if(e.code() != std::errc::resource_deadlock_would_occur) {
|
||||
throw;
|
||||
}
|
||||
|
||||
/* tree already write locked, no need to lock it again */
|
||||
channel = server->getChannelTree()->findChannel(channel_id);
|
||||
}
|
||||
if(channel) {
|
||||
this->channel_permissions = channel->permissions();
|
||||
}
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
#include "VirtualServerManager.h"
|
||||
#include "src/server/VoiceServer.h"
|
||||
#include "InstanceHandler.h"
|
||||
#include "InstanceHandler.h"
|
||||
#include "./groups/GroupManager.h"
|
||||
|
||||
//TODO: When using the new command builder make sure you're using a std::deque as the underlying bulk type!
|
||||
|
||||
|
@ -979,10 +979,13 @@ bool VirtualServerManager::createServerSnapshot(Command &cmd, shared_ptr<Virtual
|
|||
//List groups
|
||||
{
|
||||
cmd[index]["server_groups"] = "";
|
||||
for(const auto& group : server->group_manager()->availableServerGroups(false)) {
|
||||
cmd[index]["id"] = group->groupId();
|
||||
cmd[index]["name"] = group->name();
|
||||
if(!writePermissions(group->permissions(), cmd, index, version, permission::teamspeak::SERVER, error)) break;
|
||||
auto server_groups = server->group_manager()->server_groups();
|
||||
for(const auto& group : server_groups->available_groups(groups::GroupCalculateMode::LOCAL)) {
|
||||
cmd[index]["id"] = group->group_id();
|
||||
cmd[index]["name"] = group->display_name();
|
||||
if(!writePermissions(group->permissions(), cmd, index, version, permission::teamspeak::SERVER, error)) {
|
||||
break;
|
||||
}
|
||||
cmd[index++]["end_group"] = "";
|
||||
}
|
||||
cmd[index++]["end_groups"] = "";
|
||||
|
@ -1001,10 +1004,13 @@ bool VirtualServerManager::createServerSnapshot(Command &cmd, shared_ptr<Virtual
|
|||
//List groups
|
||||
{
|
||||
cmd[index]["channel_groups"] = "";
|
||||
for(const auto& group : server->group_manager()->availableChannelGroups(false)) {
|
||||
cmd[index]["id"] = group->groupId();
|
||||
cmd[index]["name"] = group->name();
|
||||
if(!writePermissions(group->permissions(), cmd, index, version, permission::teamspeak::SERVER, error)) break;
|
||||
auto server_groups = server->group_manager()->channel_groups();
|
||||
for(const auto& group : server_groups->available_groups(groups::GroupCalculateMode::LOCAL)) {
|
||||
cmd[index]["id"] = group->group_id();
|
||||
cmd[index]["name"] = group->display_name();
|
||||
if(!writePermissions(group->permissions(), cmd, index, version, permission::teamspeak::SERVER, error)) {
|
||||
break;
|
||||
}
|
||||
cmd[index++]["end_group"] = "";
|
||||
}
|
||||
cmd[index++]["end_groups"] = "";
|
||||
|
|
|
@ -550,7 +550,8 @@ void VirtualServer::client_move(
|
|||
|
||||
if (s_source_channel) {
|
||||
s_source_channel->properties()[property::CHANNEL_LAST_LEFT] = chrono::duration_cast<chrono::milliseconds>(chrono::system_clock::now().time_since_epoch()).count();
|
||||
this->group_manager()->assignments().cleanup_channel_temporary_assignment(target->getClientDatabaseId(), s_source_channel->channelId());
|
||||
this->group_manager()->assignments().cleanup_temporary_channel_assignment(target->getClientDatabaseId(),
|
||||
s_source_channel->channelId());
|
||||
|
||||
auto update = target->properties()[property::CLIENT_IS_TALKER].as_or<bool>(false) ||
|
||||
target->properties()[property::CLIENT_TALK_REQUEST].as_or<int64_t>(0) > 0;
|
||||
|
|
|
@ -976,8 +976,8 @@ bool VirtualServer::resetPermissions(std::string& new_permission_token) {
|
|||
std::map<GroupId, GroupId> server_group_mapping{};
|
||||
std::map<GroupId, GroupId> channel_group_mapping{};
|
||||
{
|
||||
this->group_manager()->server_groups()->reset_groups(serverInstance->group_manager(), server_group_mapping);
|
||||
this->group_manager()->channel_groups()->reset_groups(serverInstance->group_manager(), channel_group_mapping);
|
||||
this->group_manager()->server_groups()->reset_groups(server_group_mapping);
|
||||
this->group_manager()->channel_groups()->reset_groups(channel_group_mapping);
|
||||
this->group_manager()->assignments().reset_all();
|
||||
}
|
||||
|
||||
|
@ -1041,6 +1041,8 @@ bool VirtualServer::resetPermissions(std::string& new_permission_token) {
|
|||
}
|
||||
|
||||
void VirtualServer::ensureValidDefaultGroups() {
|
||||
/* TODO: FIXME: Impl! */
|
||||
#if 0
|
||||
auto default_server_group = this->group_manager()->defaultGroup(GROUPTARGET_SERVER, true);
|
||||
if(!default_server_group) {
|
||||
logError(this->serverId, "Missing server's default server group! (Id: {})", this->properties()[property::VIRTUALSERVER_DEFAULT_SERVER_GROUP].value());
|
||||
|
@ -1077,6 +1079,7 @@ void VirtualServer::ensureValidDefaultGroups() {
|
|||
logError(this->serverId, "Using {} ({}) instead!", admin_channel_group->groupId(), admin_channel_group->name());
|
||||
this->properties()[property::VIRTUALSERVER_DEFAULT_CHANNEL_ADMIN_GROUP] = admin_channel_group->groupId();
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void VirtualServer::send_text_message(const std::shared_ptr<BasicChannel> &channel, const std::shared_ptr<ConnectedClient> &sender, const std::string &message) {
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
#include "src/client/ConnectedClient.h"
|
||||
#include "src/InstanceHandler.h"
|
||||
#include "../manager/ConversationManager.h"
|
||||
#include "../groups/GroupManager.h"
|
||||
|
||||
using namespace std;
|
||||
using namespace ts;
|
||||
|
@ -575,11 +576,11 @@ void ServerChannelTree::on_channel_entry_deleted(const shared_ptr<BasicChannel>
|
|||
|
||||
auto server = this->server_ref.lock();
|
||||
if(server) {
|
||||
server->group_manager()->handleChannelDeleted(channel->channelId());
|
||||
server->group_manager()->assignments().handle_channel_deleted(channel->channelId());
|
||||
server->conversation_manager()->delete_conversation(channel->channelId());
|
||||
server->rtc_server().destroy_channel(server_channel->rtc_channel_id);
|
||||
} else {
|
||||
serverInstance->group_manager()->handleChannelDeleted(channel->channelId());
|
||||
serverInstance->group_manager()->assignments().handle_channel_deleted(channel->channelId());
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -61,7 +61,8 @@ void ConnectedClient::initialize_weak_reference(const std::shared_ptr<ConnectedC
|
|||
this->task_update_displayed_groups = multi_shot_task{serverInstance->general_task_executor(), "update displayed groups for " + this->getLoggingPeerIp(), [weak_self]{
|
||||
auto self = weak_self.lock();
|
||||
if(self) {
|
||||
self->update_displayed_client_groups();
|
||||
bool changed{false};
|
||||
self->update_displayed_client_groups(changed, changed);
|
||||
}
|
||||
}};
|
||||
}
|
||||
|
@ -487,6 +488,15 @@ bool ConnectedClient::notifyClientLeftView(
|
|||
invoker = this->server->serverRoot;
|
||||
}
|
||||
break;
|
||||
|
||||
case ViewReasonId::VREASON_USER_ACTION:
|
||||
case ViewReasonId::VREASON_SYSTEM:
|
||||
case ViewReasonId::VREASON_TIMEOUT:
|
||||
case ViewReasonId::VREASON_SERVER_STOPPED:
|
||||
case ViewReasonId::VREASON_SERVER_LEFT:
|
||||
case ViewReasonId::VREASON_CHANNEL_UPDATED:
|
||||
case ViewReasonId::VREASON_EDITED:
|
||||
case ViewReasonId::VREASON_SERVER_SHUTDOWN:
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
@ -818,7 +828,7 @@ void ConnectedClient::sendServerInit() {
|
|||
command["client_myteamspeak_id"] = this->properties()[property::CLIENT_MYTEAMSPEAK_ID].value();
|
||||
command["client_integrations"] = this->properties()[property::CLIENT_INTEGRATIONS].value();
|
||||
|
||||
if(ts::config::server::DefaultServerLicense == LicenseType::LICENSE_AUTOMATIC_INSTANCE){
|
||||
if(ts::config::server::DefaultServerLicense == LicenseType::LICENSE_AUTOMATIC_INSTANCE) {
|
||||
if(serverInstance->getVoiceServerManager()->usedSlots() <= 32)
|
||||
command["lt"] = LicenseType::LICENSE_NONE;
|
||||
else if(serverInstance->getVoiceServerManager()->usedSlots() <= 512)
|
||||
|
@ -1037,19 +1047,28 @@ void ConnectedClient::update_displayed_client_groups(bool& server_groups_changed
|
|||
if(!server_group_assignments.empty()) {
|
||||
server_group_assignments = server_group_assignments.substr(1);
|
||||
}
|
||||
|
||||
std::unique_lock view_lock{this->channel_lock};
|
||||
this->cached_server_groups = server_groups;
|
||||
}
|
||||
|
||||
{
|
||||
std::shared_ptr<BasicChannel> inherited_channel{this->currentChannel};
|
||||
|
||||
auto channel_group = group_manager->assignments().calculate_channel_group_of_client(groups::GroupAssignmentCalculateMode::GLOBAL, this->getClientDatabaseId(), inherited_channel);
|
||||
if(channel_group.has_value()) {
|
||||
assert(inherited_channel);
|
||||
channel_group_id = *channel_group;
|
||||
channel_inherit_id = inherited_channel->channelId();
|
||||
} else if(ref_server) {
|
||||
channel_group_id = ref_server->properties()[property::VIRTUALSERVER_DEFAULT_CHANNEL_GROUP].as_or<GroupId>(0);
|
||||
channel_inherit_id = 0;
|
||||
} else {
|
||||
channel_group_id = 0;
|
||||
channel_inherit_id = 0;
|
||||
}
|
||||
|
||||
this->cached_channel_group = channel_group_id;
|
||||
}
|
||||
|
||||
server_groups_changed = false;
|
||||
|
@ -1174,7 +1193,7 @@ void ConnectedClient::useToken(token::TokenId token_id) {
|
|||
}
|
||||
|
||||
if(action.type == token::ActionType::AddServerGroup) {
|
||||
auto result = this->server->group_manager()->assignments().add_server_group(this->getClientDatabaseId(), group->group_id());
|
||||
auto result = this->server->group_manager()->assignments().add_server_group(this->getClientDatabaseId(), group->group_id(), !group->is_permanent());
|
||||
switch(result) {
|
||||
case GroupAssignmentResult::SUCCESS:
|
||||
debugMessage(this->getServerId(), "{} Executing token action add server group for group {}.", CLIENT_STR_LOG_PREFIX, action.id1);
|
||||
|
@ -1260,15 +1279,16 @@ void ConnectedClient::useToken(token::TokenId token_id) {
|
|||
}
|
||||
|
||||
if(tree_registered && server_groups_changed) {
|
||||
for(const auto &viewer : this->server->getClients()) {
|
||||
if(viewer->isClientVisible(this->ref(), true)) {
|
||||
for(const auto& group : added_server_groups) {
|
||||
viewer->notifyServerGroupClientAdd(this->server->serverRoot, this->ref(), group);
|
||||
}
|
||||
|
||||
for(const auto& group : removed_server_groups) {
|
||||
viewer->notifyServerGroupClientRemove(this->server->serverRoot, this->ref(), group);
|
||||
}
|
||||
for(const auto& group : added_server_groups) {
|
||||
std::optional<ts::command_builder> notify{};
|
||||
for(const auto &viewer : this->server->getClients()) {
|
||||
viewer->notifyServerGroupClientAdd(notify, this->server->serverRoot, this->ref(), group->group_id());
|
||||
}
|
||||
}
|
||||
for(const auto& group : added_server_groups) {
|
||||
std::optional<ts::command_builder> notify{};
|
||||
for(const auto &viewer : this->server->getClients()) {
|
||||
viewer->notifyServerGroupClientRemove(notify, this->server->serverRoot, this->ref(), group->group_id());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -120,10 +120,11 @@ namespace ts {
|
|||
std::deque<std::shared_ptr<BasicChannel>> unsubscribeChannel(const std::deque<std::shared_ptr<BasicChannel>>& target, bool lock_channel);
|
||||
|
||||
bool isClientVisible(const std::shared_ptr<ConnectedClient>&, bool /* lock channel lock */);
|
||||
inline std::deque<std::weak_ptr<ConnectedClient>> getVisibleClients(bool lock_channel){
|
||||
inline std::deque<std::weak_ptr<ConnectedClient>> getVisibleClients(bool lock_channel) {
|
||||
std::shared_lock lock(this->channel_lock, std::defer_lock);
|
||||
if(lock_channel)
|
||||
if(lock_channel) {
|
||||
lock.lock();
|
||||
}
|
||||
return this->visibleClients;
|
||||
}
|
||||
|
||||
|
@ -162,20 +163,44 @@ namespace ts {
|
|||
inline void sendChannelMessage(const std::shared_ptr<ConnectedClient>& sender, const std::string& textMessage){
|
||||
this->notifyTextMessage(ChatMessageMode::TEXTMODE_CHANNEL, sender, this->currentChannel ? this->currentChannel->channelId() : 0, 0, std::chrono::system_clock::now(), textMessage);
|
||||
}
|
||||
//Group Client Groups
|
||||
virtual bool notifyServerGroupClientAdd(const std::shared_ptr<ConnectedClient> &invoker, const std::shared_ptr<ConnectedClient> &client, const std::shared_ptr<Group> &group);
|
||||
virtual bool notifyServerGroupClientRemove(std::shared_ptr<ConnectedClient> invoker, std::shared_ptr<ConnectedClient> client, std::shared_ptr<Group> group);
|
||||
/* invalid client id causes error: invalid clientID */
|
||||
/* invalid channel id causes error: invalid channelID */
|
||||
/* an invalid channel or not a client's channel causes: invalid channelID */
|
||||
|
||||
/**
|
||||
* Notify the client that a client has received a new server group.
|
||||
* If the target client isn't visible no notify will be send.
|
||||
*
|
||||
* Note:
|
||||
* 1. This method will lock the channel tree in shared mode!
|
||||
* 2. For TS3 clients if the client isn't in view the client will disconnect.
|
||||
*/
|
||||
virtual bool notifyServerGroupClientAdd(std::optional<ts::command_builder>& /* generated notify */, const std::shared_ptr<ConnectedClient> &/* invoker */, const std::shared_ptr<ConnectedClient> &/* target client */, const GroupId& /* group id */);
|
||||
|
||||
/**
|
||||
* Notify the client that a client has removed a server group.
|
||||
* If the target client isn't visible no notify will be send.
|
||||
*
|
||||
* Note:
|
||||
* 1. This method will lock the channel tree in shared mode!
|
||||
* 2. For TS3 clients if the client isn't in view the client will disconnect.
|
||||
*/
|
||||
virtual bool notifyServerGroupClientRemove(std::optional<ts::command_builder>& /* generated notify */, const std::shared_ptr<ConnectedClient> &/* invoker */, const std::shared_ptr<ConnectedClient> &/* target client */, const GroupId& /* group id */);
|
||||
|
||||
/**
|
||||
* Notify that a client has received a new channel group.
|
||||
* If the target client isn't visible no notify will be send.
|
||||
*
|
||||
* Note:
|
||||
* 1. This method will lock the channel tree in shared mode!
|
||||
* 2. For TS3 clients if the client isn't in view the client will disconnect.
|
||||
*/
|
||||
virtual bool notifyClientChannelGroupChanged(
|
||||
const std::shared_ptr<ConnectedClient>& invoker,
|
||||
const std::shared_ptr<ConnectedClient>& client,
|
||||
const std::shared_ptr<BasicChannel>& /*channel*/,
|
||||
const std::shared_ptr<BasicChannel>& /*inherited channel*/,
|
||||
const std::shared_ptr<Group>& group,
|
||||
bool lock_channel_tree /* server channel tree AND client tree must be at least read locked! */
|
||||
std::optional<ts::command_builder>& /* generated notify */,
|
||||
const std::shared_ptr<ConnectedClient>& /* invoker */,
|
||||
const std::shared_ptr<ConnectedClient>& /* target client */,
|
||||
const ChannelId& /* channel */,
|
||||
const ChannelId& /* inherited channel */,
|
||||
const GroupId& /* group id */
|
||||
);
|
||||
|
||||
//Group channel
|
||||
virtual bool notifyChannelMoved(const std::shared_ptr<BasicChannel> &channel, ChannelId order, const std::shared_ptr<ConnectedClient> &invoker);
|
||||
virtual bool notifyChannelDescriptionChanged(std::shared_ptr<BasicChannel> channel);
|
||||
|
@ -306,6 +331,8 @@ namespace ts {
|
|||
bool update_client_needed_permissions();
|
||||
|
||||
/**
|
||||
* Note: `server_groups_changed` and `channel_group_changed` could be equal.
|
||||
* If so it will be true if any changes have been made.
|
||||
* Attention: This method should never be called directly!
|
||||
* Use `task_update_displayed_groups` instead to schedule an update.
|
||||
*/
|
||||
|
@ -696,6 +723,13 @@ namespace ts {
|
|||
return std::string(notify);
|
||||
return "";
|
||||
}
|
||||
|
||||
command_result handleCommandGroupAdd(Command&, GroupTarget);
|
||||
command_result handleCommandGroupCopy(Command&, GroupTarget);
|
||||
command_result handleCommandGroupRename(Command&, GroupTarget);
|
||||
command_result handleCommandGroupDel(Command&, GroupTarget);
|
||||
|
||||
command_result executeGroupPermissionEdit(Command&, const std::vector<std::shared_ptr<groups::Group>>& /* groups */, const std::shared_ptr<VirtualServer>& /* target server */, permission::v2::PermissionUpdateType /* mode */);
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
|
|
|
@ -70,9 +70,9 @@ inline void build_group_notify(ts::command_builder& notify, bool is_channel_grou
|
|||
bulk.put_unchecked("type", (uint8_t) group->group_type());
|
||||
bulk.put_unchecked("name", group->display_name());
|
||||
bulk.put_unchecked("sortid", group->sort_id());
|
||||
bulk.put_unchecked("savedb", group->save_assignments());
|
||||
bulk.put_unchecked("savedb", group->is_permanent());
|
||||
bulk.put_unchecked("namemode", (uint8_t) group->name_mode());
|
||||
bulk.put_unchecked("iconid", group->icon_id());
|
||||
bulk.put_unchecked("iconid", (int32_t) group->icon_id());
|
||||
|
||||
auto modify_power = group->permissions()->permission_value_flagged(permission_modify);
|
||||
auto add_power = group->permissions()->permission_value_flagged(permission_add);
|
||||
|
@ -88,7 +88,7 @@ bool ConnectedClient::notifyServerGroupList(std::optional<ts::command_builder> &
|
|||
if(!generated_notify.has_value()) {
|
||||
auto server_ref = this->server;
|
||||
auto group_manager = server_ref ? server_ref->group_manager() : serverInstance->group_manager();
|
||||
auto available_groups = group_manager->channel_groups()->available_groups(groups::GroupCalculateMode::GLOBAL);
|
||||
auto available_groups = group_manager->server_groups()->available_groups(groups::GroupCalculateMode::GLOBAL);
|
||||
|
||||
build_group_notify(generated_notify.emplace(as_notify ? "notifyservergrouplist" : ""), false, available_groups);
|
||||
}
|
||||
|
@ -225,57 +225,95 @@ bool ConnectedClient::notifyTextMessage(ChatMessageMode mode, const shared_ptr<C
|
|||
return true;
|
||||
}
|
||||
|
||||
bool ConnectedClient::notifyServerGroupClientAdd(const shared_ptr<ConnectedClient> &invoker, const shared_ptr<ConnectedClient> &client, const shared_ptr<Group> &group) {
|
||||
Command cmd("notifyservergroupclientadded");
|
||||
INVOKER(cmd, invoker);
|
||||
|
||||
cmd["sgid"] = group->groupId();
|
||||
cmd["clid"] = client->getClientId();
|
||||
cmd["name"] = client->getDisplayName();
|
||||
cmd["cluid"] = client->getUid();
|
||||
|
||||
this->sendCommand(cmd);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ConnectedClient::notifyServerGroupClientRemove(std::shared_ptr<ConnectedClient> invoker, std::shared_ptr<ConnectedClient> client, std::shared_ptr<Group> group) {
|
||||
Command cmd("notifyservergroupclientdeleted");
|
||||
INVOKER(cmd, invoker);
|
||||
|
||||
cmd["sgid"] = group->groupId();
|
||||
cmd["clid"] = client->getClientId();
|
||||
cmd["name"] = client->getDisplayName();
|
||||
cmd["cluid"] = client->getUid();
|
||||
|
||||
this->sendCommand(cmd);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ConnectedClient::notifyClientChannelGroupChanged(
|
||||
bool ConnectedClient::notifyServerGroupClientAdd(
|
||||
std::optional<ts::command_builder>& notify,
|
||||
const std::shared_ptr<ConnectedClient> &invoker,
|
||||
const std::shared_ptr<ConnectedClient> &client,
|
||||
const std::shared_ptr<BasicChannel>& channel,
|
||||
const std::shared_ptr<BasicChannel>& inherited,
|
||||
const std::shared_ptr<Group>& group,
|
||||
bool lock_channel_tree) {
|
||||
assert(!lock_channel_tree); /* not supported */
|
||||
const std::shared_ptr<ConnectedClient> &target_client,
|
||||
const GroupId& group_id) {
|
||||
|
||||
if(!this->isClientVisible(client, false) && client != this) return false;
|
||||
if(client->getChannel() != channel) return false;
|
||||
/* Deny any client moves 'till we've send the notify */
|
||||
std::shared_lock<std::shared_mutex> channel_tree_lock{};
|
||||
if(this->server) {
|
||||
channel_tree_lock = std::shared_lock{this->server->channel_tree_lock};
|
||||
}
|
||||
|
||||
assert(client);
|
||||
assert(channel);
|
||||
assert(inherited);
|
||||
assert(group);
|
||||
Command cmd("notifyclientchannelgroupchanged");
|
||||
INVOKER(cmd, invoker);
|
||||
if(!this->isClientVisible(target_client, true)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
cmd["cgid"] = group->groupId();
|
||||
cmd["clid"] = client->getClientId();
|
||||
cmd["cid"] = channel->channelId();
|
||||
cmd["cgi"] = inherited ? inherited->channelId() : channel->channelId(); //The inherited channel
|
||||
if(!notify.has_value()) {
|
||||
notify.emplace("notifyservergroupclientadded");
|
||||
INVOKER_NEW((*notify), invoker);
|
||||
|
||||
this->sendCommand(cmd);
|
||||
notify->put_unchecked(0, "sgid", group_id);
|
||||
notify->put_unchecked(0, "clid", target_client->getClientId());
|
||||
notify->put_unchecked(0, "name", target_client->getDisplayName());
|
||||
notify->put_unchecked(0, "cluid", target_client->getUid());
|
||||
}
|
||||
|
||||
this->sendCommand(*notify);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ConnectedClient::notifyServerGroupClientRemove(
|
||||
std::optional<ts::command_builder>& notify,
|
||||
const std::shared_ptr<ConnectedClient> &invoker,
|
||||
const std::shared_ptr<ConnectedClient> &target_client,
|
||||
const GroupId& group_id) {
|
||||
|
||||
/* Deny any client moves 'till we've send the notify */
|
||||
std::shared_lock<std::shared_mutex> channel_tree_lock{};
|
||||
if(this->server) {
|
||||
channel_tree_lock = std::shared_lock{this->server->channel_tree_lock};
|
||||
}
|
||||
|
||||
if(!this->isClientVisible(target_client, true)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if(!notify.has_value()) {
|
||||
notify.emplace("notifyservergroupclientdeleted");
|
||||
INVOKER_NEW((*notify), invoker);
|
||||
|
||||
notify->put_unchecked(0, "sgid", group_id);
|
||||
notify->put_unchecked(0, "clid", target_client->getClientId());
|
||||
notify->put_unchecked(0, "name", target_client->getDisplayName());
|
||||
notify->put_unchecked(0, "cluid", target_client->getUid());
|
||||
}
|
||||
|
||||
this->sendCommand(*notify);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ConnectedClient::notifyClientChannelGroupChanged(std::optional<ts::command_builder> ¬ify,
|
||||
const std::shared_ptr<ConnectedClient> &invoker,
|
||||
const std::shared_ptr<ConnectedClient> &target_client,
|
||||
const ChannelId &channel_id,
|
||||
const ChannelId &inherited_channel_id,
|
||||
const GroupId &group_id) {
|
||||
/* Deny any client moves 'till we've send the notify */
|
||||
std::shared_lock<std::shared_mutex> channel_tree_lock{};
|
||||
if(this->server) {
|
||||
channel_tree_lock = std::shared_lock{this->server->channel_tree_lock};
|
||||
}
|
||||
|
||||
/* No need to check if the channel is visible since if this is the case the client would not be visible as well. */
|
||||
if(!this->isClientVisible(target_client, true)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if(!notify.has_value()) {
|
||||
notify.emplace("notifyclientchannelgroupchanged");
|
||||
INVOKER_NEW((*notify), invoker);
|
||||
|
||||
notify->put_unchecked(0, "cgid", group_id);
|
||||
notify->put_unchecked(0, "clid", target_client->getClientId());
|
||||
notify->put_unchecked(0, "name", target_client->getDisplayName());
|
||||
notify->put_unchecked(0, "cid", channel_id);
|
||||
notify->put_unchecked(0, "cgi", inherited_channel_id == 0 ? channel_id : inherited_channel_id);
|
||||
}
|
||||
|
||||
this->sendCommand(*notify);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
|
@ -8,6 +8,8 @@
|
|||
#include "../groups/GroupManager.h"
|
||||
#include "../groups/GroupAssignmentManager.h"
|
||||
|
||||
#include "../PermissionCalculator.h"
|
||||
|
||||
using namespace std;
|
||||
using namespace ts;
|
||||
using namespace ts::server;
|
||||
|
@ -138,6 +140,12 @@ bool DataClient::loadDataForCurrentServer() {
|
|||
this->properties()[property::CLIENT_UNREAD_MESSAGES] = ref_server->letters->unread_letter_count(this->getUid());
|
||||
}
|
||||
|
||||
if(this->server) {
|
||||
this->temporary_assignments_lock = server->group_manager()->assignments().create_tmp_assignment_lock(this->getClientDatabaseId());
|
||||
} else {
|
||||
this->temporary_assignments_lock = serverInstance->group_manager()->assignments().create_tmp_assignment_lock(this->getClientDatabaseId());
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -145,40 +153,16 @@ std::vector<std::pair<permission::PermissionType, permission::v2::PermissionFlag
|
|||
const std::deque<permission::PermissionType> &permissions,
|
||||
ChannelId channel,
|
||||
bool granted,
|
||||
std::shared_ptr<CalculateCache> cache) {
|
||||
if(permissions.empty()) {
|
||||
return {};
|
||||
}
|
||||
std::shared_ptr<CalculateCache>) {
|
||||
|
||||
if(!cache) {
|
||||
cache = std::make_shared<CalculateCache>();
|
||||
}
|
||||
|
||||
if(!cache->client_permissions) {
|
||||
/* so we don't have to load that shit later */
|
||||
cache->client_permissions = this->clientPermissions;
|
||||
}
|
||||
|
||||
if(channel == -1) {
|
||||
auto current_channel = this->currentChannel;
|
||||
channel = current_channel ? current_channel->channelId() : 0;
|
||||
}
|
||||
|
||||
auto ref_server = this->server;
|
||||
if(ref_server) {
|
||||
return ref_server->calculate_permissions(permissions, this->getClientDatabaseId(), this->getType(), channel, granted, cache);
|
||||
} else {
|
||||
return serverInstance->permission_helper().calculate_permissions(permissions, this->getClientDatabaseId(), this->getType(), channel, granted, cache);
|
||||
}
|
||||
ts::server::ClientPermissionCalculator calculator{this, channel};
|
||||
return calculator.calculate_permissions(permissions, granted);
|
||||
}
|
||||
|
||||
permission::v2::PermissionFlaggedValue DataClient::calculate_permission(
|
||||
permission::PermissionType permission, ChannelId channel, bool granted, std::shared_ptr<CalculateCache> cache) {
|
||||
auto result = this->calculate_permissions({permission}, channel, granted, cache);
|
||||
if(result.empty()) {
|
||||
return {0, false};
|
||||
}
|
||||
return result.back().second;
|
||||
permission::PermissionType permission, ChannelId channel, bool granted, std::shared_ptr<CalculateCache>) {
|
||||
ts::server::ClientPermissionCalculator calculator{this, channel};
|
||||
return calculator.calculate_permission(permission, granted);
|
||||
}
|
||||
|
||||
std::vector<std::shared_ptr<groups::ServerGroup>> DataClient::assignedServerGroups() {
|
||||
|
@ -208,17 +192,16 @@ std::vector<std::shared_ptr<groups::ServerGroup>> DataClient::assignedServerGrou
|
|||
return result;
|
||||
}
|
||||
|
||||
std::shared_ptr<groups::ChannelGroup> DataClient::assignedChannelGroup(const std::shared_ptr<BasicChannel> &channel) {
|
||||
std::shared_ptr<groups::ChannelGroup> DataClient::assignedChannelGroup(std::shared_ptr<BasicChannel> &channel) {
|
||||
auto ref_server = this->server;
|
||||
assert(channel);
|
||||
if(!channel || !ref_server) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
std::shared_ptr<BasicChannel> inherited_channel{channel};
|
||||
|
||||
std::shared_ptr<BasicChannel> original_channel{channel};
|
||||
auto group_manager = ref_server ? ref_server->group_manager() : serverInstance->group_manager();
|
||||
auto assigned_group_id = group_manager->assignments().calculate_channel_group_of_client(groups::GroupAssignmentCalculateMode::GLOBAL, this->getClientDatabaseId(), inherited_channel);
|
||||
auto assigned_group_id = group_manager->assignments().calculate_channel_group_of_client(groups::GroupAssignmentCalculateMode::GLOBAL, this->getClientDatabaseId(), channel);
|
||||
|
||||
std::shared_ptr<groups::ChannelGroup> result{};
|
||||
if(assigned_group_id.has_value()) {
|
||||
|
@ -227,6 +210,7 @@ std::shared_ptr<groups::ChannelGroup> DataClient::assignedChannelGroup(const std
|
|||
|
||||
if(!result) {
|
||||
result = ref_server->default_channel_group();
|
||||
channel = original_channel;
|
||||
}
|
||||
|
||||
assert(result);
|
||||
|
@ -252,7 +236,8 @@ bool DataClient::channelGroupAssigned(const shared_ptr<groups::ChannelGroup> &gr
|
|||
return false;
|
||||
}
|
||||
|
||||
return this->assignedChannelGroup(channel) == group;
|
||||
std::shared_ptr<BasicChannel> interited_channel{channel};
|
||||
return this->assignedChannelGroup(interited_channel) == group;
|
||||
}
|
||||
|
||||
std::string DataClient::getAvatarId() {
|
||||
|
|
|
@ -22,6 +22,7 @@ namespace ts {
|
|||
namespace groups {
|
||||
class ServerGroup;
|
||||
class ChannelGroup;
|
||||
typedef void TemporaryAssignmentsLock;
|
||||
}
|
||||
|
||||
class DataClient {
|
||||
|
@ -68,7 +69,7 @@ namespace ts {
|
|||
);
|
||||
|
||||
virtual std::vector<std::shared_ptr<groups::ServerGroup>> assignedServerGroups();
|
||||
virtual std::shared_ptr<groups::ChannelGroup> assignedChannelGroup(const std::shared_ptr<BasicChannel> &);
|
||||
virtual std::shared_ptr<groups::ChannelGroup> assignedChannelGroup(std::shared_ptr<BasicChannel> &);
|
||||
virtual bool serverGroupAssigned(const std::shared_ptr<groups::ServerGroup> &);
|
||||
virtual bool channelGroupAssigned(const std::shared_ptr<groups::ChannelGroup> &, const std::shared_ptr<BasicChannel> &);
|
||||
|
||||
|
@ -106,6 +107,7 @@ namespace ts {
|
|||
std::shared_ptr<PropertyManager> _properties;
|
||||
|
||||
std::shared_ptr<BasicChannel> currentChannel = nullptr;
|
||||
std::shared_ptr<groups::TemporaryAssignmentsLock> temporary_assignments_lock{};
|
||||
};
|
||||
}
|
||||
}
|
|
@ -16,6 +16,7 @@
|
|||
#include "../manager/ActionLogger.h"
|
||||
#include "./voice/VoiceClient.h"
|
||||
#include "../rtc/imports.h"
|
||||
#include "../groups/GroupManager.h"
|
||||
|
||||
using namespace std::chrono;
|
||||
using namespace ts;
|
||||
|
@ -25,7 +26,7 @@ using namespace ts::protocol;
|
|||
//#define PKT_LOG_VOICE
|
||||
//#define PKT_LOG_WHISPER
|
||||
|
||||
SpeakingClient::SpeakingClient(sql::SqlManager *a, const std::shared_ptr<VirtualServer> &b) : ConnectedClient(a, b), whisper_handler_{this} {
|
||||
SpeakingClient::SpeakingClient(sql::SqlManager *a, const std::shared_ptr<VirtualServer> &b) : ConnectedClient{a, b}, whisper_handler_{this} {
|
||||
speak_begin = std::chrono::system_clock::now();
|
||||
speak_last_packet = std::chrono::system_clock::now();
|
||||
};
|
||||
|
@ -122,8 +123,6 @@ command_result SpeakingClient::handleCommandClientInit(Command& cmd) {
|
|||
}
|
||||
|
||||
TIMING_STEP(timings, "db assign ");
|
||||
this->server->group_manager()->enableCache(this->getClientDatabaseId());
|
||||
TIMING_STEP(timings, "gr cache ");
|
||||
|
||||
const static vector<string> available_parameters = {
|
||||
"client_nickname",
|
||||
|
@ -437,10 +436,6 @@ void SpeakingClient::processJoin() {
|
|||
}
|
||||
|
||||
TIMING_STEP(timings, "server reg ");
|
||||
ref_server->group_manager()->cleanupAssignments(this->getClientDatabaseId());
|
||||
TIMING_STEP(timings, "grp cleanup");
|
||||
ref_server->group_manager()->update_server_group_property(this->ref(), true, nullptr);
|
||||
TIMING_STEP(timings, "grp apply ");
|
||||
|
||||
this->properties()[property::CLIENT_COUNTRY] = config::geo::countryFlag;
|
||||
if(geoloc::provider) {
|
||||
|
@ -574,7 +569,6 @@ void SpeakingClient::processLeave() {
|
|||
unique_lock server_channel_lock(this->server->channel_tree_lock);
|
||||
server->unregisterClient(ownLock, "disconnected", server_channel_lock); /* already moves client to void if needed */
|
||||
}
|
||||
server->group_manager()->disableCache(ownLock->getClientDatabaseId());
|
||||
server->music_manager_->cleanup_client_bots(this->getClientDatabaseId());
|
||||
//ref_server = nullptr; Removed caused nullptr exceptions
|
||||
}
|
||||
|
|
|
@ -9,11 +9,9 @@
|
|||
#include "../../manager/ActionLogger.h"
|
||||
|
||||
namespace ts::command::bulk_parser {
|
||||
template <bool kParseValue>
|
||||
class PermissionBulkParser {
|
||||
friend class PermissionBulkParser<false>;
|
||||
public:
|
||||
explicit PermissionBulkParser(ts::ParameterBulk& bulk) {
|
||||
explicit PermissionBulkParser(ts::ParameterBulk& bulk, bool parse_value) {
|
||||
if(bulk.has("permid")) {
|
||||
auto type = bulk["permid"].as<permission::PermissionType>();
|
||||
if ((type & PERM_ID_GRANT) != 0) {
|
||||
|
@ -35,7 +33,7 @@ namespace ts::command::bulk_parser {
|
|||
return;
|
||||
}
|
||||
|
||||
if(kParseValue) {
|
||||
if(parse_value) {
|
||||
if(!bulk.has("permvalue")) {
|
||||
this->error_.reset(ts::command_result{error::parameter_missing, "permvalue"});
|
||||
return;
|
||||
|
@ -180,7 +178,6 @@ namespace ts::command::bulk_parser {
|
|||
#endif
|
||||
};
|
||||
|
||||
template <bool kParseValue>
|
||||
class PermissionBulksParser {
|
||||
public:
|
||||
PermissionBulksParser(const PermissionBulksParser&) = delete;
|
||||
|
@ -220,7 +217,7 @@ namespace ts::command::bulk_parser {
|
|||
};
|
||||
|
||||
struct FilteredPermissionListIterable {
|
||||
typedef typename std::vector<PermissionBulkParser<kParseValue>>::const_iterator const_iterator;
|
||||
typedef typename std::vector<PermissionBulkParser>::const_iterator const_iterator;
|
||||
public:
|
||||
FilteredPermissionListIterable(const_iterator begin, const_iterator end) noexcept : begin_{begin}, end_{end} {}
|
||||
|
||||
|
@ -236,10 +233,11 @@ namespace ts::command::bulk_parser {
|
|||
const_iterator end_;
|
||||
};
|
||||
|
||||
explicit PermissionBulksParser(ts::Command& command) {
|
||||
explicit PermissionBulksParser(ts::Command& command, bool parse_value) : parse_value{parse_value} {
|
||||
this->permissions_.reserve(command.bulkCount());
|
||||
for(size_t index{0}; index < command.bulkCount(); index++)
|
||||
this->permissions_.emplace_back(command[index]);
|
||||
for(size_t index{0}; index < command.bulkCount(); index++) {
|
||||
this->permissions_.emplace_back(command[index], parse_value);
|
||||
}
|
||||
}
|
||||
|
||||
[[nodiscard]] inline bool validate(const std::shared_ptr<server::ConnectedClient>& issuer, ChannelId channel_id) {
|
||||
|
@ -247,19 +245,24 @@ namespace ts::command::bulk_parser {
|
|||
if(!ignore_granted_values) {
|
||||
auto max_value = issuer->calculate_permission(permission::i_permission_modify_power, channel_id, false);
|
||||
if(!max_value.has_value) {
|
||||
for(PermissionBulkParser<kParseValue>& permission : this->permissions_) {
|
||||
if(permission.has_error()) continue;
|
||||
for(PermissionBulkParser& permission : this->permissions_) {
|
||||
if(permission.has_error()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
permission.emplace_custom_error(ts::command_result{permission::i_permission_modify_power});
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
for(size_t index{0}; index < this->permissions_.size(); index++) {
|
||||
PermissionBulkParser<kParseValue>& permission = this->permissions_[index];
|
||||
if(permission.has_error()) continue;
|
||||
PermissionBulkParser& permission = this->permissions_[index];
|
||||
if(permission.has_error()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if(kParseValue && permission_require_granted_value(permission.permission_type()) && !permission::v2::permission_granted(permission.value(), max_value)) {
|
||||
if(this->parse_value && permission_require_granted_value(permission.permission_type()) && !permission::v2::permission_granted(permission.value(), max_value)) {
|
||||
permission.emplace_custom_error(ts::command_result{permission::i_permission_modify_power});
|
||||
continue;
|
||||
}
|
||||
|
@ -281,13 +284,15 @@ namespace ts::command::bulk_parser {
|
|||
assert(!std::exchange(this->result_created_, true));
|
||||
ts::command_result_bulk result{};
|
||||
|
||||
for(auto& permission : this->permissions_)
|
||||
for(auto& permission : this->permissions_) {
|
||||
result.insert_result(std::forward<ts::command_result>(permission.release_error()));
|
||||
}
|
||||
|
||||
return ts::command_result{std::move(result)};
|
||||
}
|
||||
private:
|
||||
std::vector<PermissionBulkParser<kParseValue>> permissions_{};
|
||||
bool parse_value;
|
||||
std::vector<PermissionBulkParser> permissions_{};
|
||||
#ifndef NDEBUG
|
||||
bool result_created_{false};
|
||||
#endif
|
||||
|
|
|
@ -134,418 +134,19 @@ command_result ConnectedClient::handleCommandChannelUnsubscribeAll(Command &cmd)
|
|||
}
|
||||
|
||||
command_result ConnectedClient::handleCommandChannelGroupAdd(Command &cmd) {
|
||||
CMD_RESET_IDLE;
|
||||
CMD_CHK_AND_INC_FLOOD_POINTS(5);
|
||||
ACTION_REQUIRES_GLOBAL_PERMISSION(permission::b_virtualserver_channelgroup_create, 1);
|
||||
|
||||
auto group_manager = this->server ? this->server->group_manager() : serverInstance->group_manager();
|
||||
|
||||
log::GroupType log_group_type;
|
||||
auto group_type = cmd["type"].as<groups::GroupType>();
|
||||
switch (group_type) {
|
||||
case groups::GroupType::GROUP_TYPE_QUERY:
|
||||
ACTION_REQUIRES_GLOBAL_PERMISSION(permission::b_serverinstance_modify_querygroup, 1);
|
||||
log_group_type = log::GroupType::QUERY;
|
||||
break;
|
||||
|
||||
case groups::GroupType::GROUP_TYPE_TEMPLATE:
|
||||
ACTION_REQUIRES_GLOBAL_PERMISSION(permission::b_serverinstance_modify_templates, 1);
|
||||
log_group_type = log::GroupType::TEMPLATE;
|
||||
break;
|
||||
|
||||
case groups::GroupType::GROUP_TYPE_NORMAL:
|
||||
if (!this->server) {
|
||||
return command_result{error::parameter_invalid, "you cant create normal groups on the template server!"};
|
||||
}
|
||||
log_group_type = log::GroupType::NORMAL;
|
||||
break;
|
||||
|
||||
case groups::GroupType::GROUP_TYPE_UNKNOWN:
|
||||
default:
|
||||
return command_result{error::parameter_invalid, "type"};
|
||||
}
|
||||
|
||||
std::shared_ptr<groups::ChannelGroup> group{};
|
||||
auto result = group_manager->channel_groups()->create_group(group_type, cmd["name"].string(), group);
|
||||
switch(result) {
|
||||
case groups::GroupCreateResult::SUCCESS:
|
||||
break;
|
||||
|
||||
case groups::GroupCreateResult::NAME_TOO_SHORT:
|
||||
case groups::GroupCreateResult::NAME_TOO_LONG:
|
||||
return command_result{error::parameter_invalid, "name"};
|
||||
|
||||
case groups::GroupCreateResult::NAME_ALREADY_IN_USED:
|
||||
return command_result{error::group_name_inuse};
|
||||
|
||||
case groups::GroupCreateResult::DATABASE_ERROR:
|
||||
default:
|
||||
return command_result{error::vs_critical};
|
||||
}
|
||||
assert(group);
|
||||
serverInstance->action_logger()->group_logger.log_group_create(this->getServerId(), this->ref(), log::GroupTarget::CHANNEL, log_group_type, group->group_id(), group->display_name(), 0, "");
|
||||
|
||||
{
|
||||
ts::command_builder notify{this->notify_response_command("notifychannelgroupadded")};
|
||||
notify.put_unchecked(0, "cgid", group->group_id());
|
||||
this->sendCommand(notify);
|
||||
}
|
||||
|
||||
if (group) {
|
||||
group->permissions()->set_permission(permission::b_group_is_permanent, {1, 0}, permission::v2::set_value, permission::v2::do_nothing);
|
||||
if (this->server) {
|
||||
this->server->enqueue_notify_channel_group_list();
|
||||
}
|
||||
} else {
|
||||
return command_result{error::group_invalid_id};
|
||||
}
|
||||
return command_result{error::ok};
|
||||
return this->handleCommandGroupAdd(cmd, GroupTarget::GROUPTARGET_CHANNEL);
|
||||
}
|
||||
|
||||
//name=Channel\sAdmin scgid=5 tcgid=4 type=1
|
||||
command_result ConnectedClient::handleCommandChannelGroupCopy(Command &cmd) {
|
||||
CMD_RESET_IDLE;
|
||||
CMD_CHK_AND_INC_FLOOD_POINTS(5);
|
||||
|
||||
auto ref_server = this->server;
|
||||
|
||||
auto group_manager = this->server ? this->server->group_manager() : serverInstance->group_manager();
|
||||
|
||||
auto source_group_id = cmd["scgid"].as<GroupId>();
|
||||
auto source_group = group_manager->channel_groups()->find_group(groups::GroupCalculateMode::GLOBAL, source_group_id);
|
||||
|
||||
if (!source_group) {
|
||||
return command_result{error::group_invalid_id, "invalid source group id"};
|
||||
}
|
||||
|
||||
const auto group_type_modifiable = [&](groups::GroupType type) {
|
||||
switch (type) {
|
||||
case groups::GroupType::GROUP_TYPE_TEMPLATE:
|
||||
if (!permission::v2::permission_granted(1, this->calculate_permission(permission::b_serverinstance_modify_templates, 0))) {
|
||||
return permission::b_serverinstance_modify_templates;
|
||||
}
|
||||
|
||||
break;
|
||||
case groups::GroupType::GROUP_TYPE_QUERY:
|
||||
if (!permission::v2::permission_granted(1, this->calculate_permission(permission::b_serverinstance_modify_querygroup, 0))) {
|
||||
return permission::b_serverinstance_modify_querygroup;
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case groups::GroupType::GROUP_TYPE_NORMAL:
|
||||
if (!permission::v2::permission_granted(1, this->calculate_permission(permission::b_virtualserver_channelgroup_create, 0))) {
|
||||
return permission::b_virtualserver_channelgroup_create;
|
||||
}
|
||||
break;
|
||||
|
||||
case groups::GroupType::GROUP_TYPE_UNKNOWN:
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return permission::undefined;
|
||||
};
|
||||
|
||||
{
|
||||
auto result = group_type_modifiable(source_group->group_type());
|
||||
if (result != permission::undefined) {
|
||||
return command_result{result};
|
||||
}
|
||||
}
|
||||
|
||||
auto global_update = false;
|
||||
if (cmd[0].has("tcgid") && cmd["tcgid"].as<GroupId>() != 0) {
|
||||
//Copy an existing group
|
||||
auto target_group_id = cmd["tcgid"].as<GroupId>();
|
||||
auto target_group = group_manager->channel_groups()->find_group(groups::GroupCalculateMode::GLOBAL, target_group_id);
|
||||
if (!target_group) {
|
||||
return command_result{error::group_invalid_id, "invalid target group"};
|
||||
}
|
||||
|
||||
if(!target_group->permission_granted(permission::i_channel_group_needed_modify_power, this->calculate_permission(permission::i_channel_group_modify_power, 0), true)) {
|
||||
return ts::command_result{permission::i_channel_group_needed_modify_power};
|
||||
}
|
||||
|
||||
{
|
||||
auto result = group_type_modifiable(target_group->group_type());
|
||||
if (result != permission::undefined) {
|
||||
return command_result{result};
|
||||
}
|
||||
}
|
||||
|
||||
auto result = group_manager->channel_groups()->copy_group_permissions(source_group_id, target_group_id);
|
||||
switch(result) {
|
||||
case groups::GroupCopyResult::SUCCESS:
|
||||
break;
|
||||
|
||||
case groups::GroupCopyResult::UNKNOWN_SOURCE_GROUP:
|
||||
return command_result{error::vs_critical, "internal unknown source group"};
|
||||
|
||||
case groups::GroupCopyResult::UNKNOWN_TARGET_GROUP:
|
||||
return command_result{error::vs_critical, "internal unknown target group"};
|
||||
|
||||
case groups::GroupCopyResult::DATABASE_ERROR:
|
||||
return command_result{error::vs_critical, "database error"};
|
||||
|
||||
case groups::GroupCopyResult::NAME_ALREADY_IN_USE:
|
||||
return command_result{error::group_name_inuse};
|
||||
|
||||
default:
|
||||
return command_result{error::vs_critical};
|
||||
}
|
||||
|
||||
log::GroupType log_group_type;
|
||||
switch (target_group->group_type()) {
|
||||
case groups::GroupType::GROUP_TYPE_QUERY:
|
||||
log_group_type = log::GroupType::QUERY;
|
||||
break;
|
||||
|
||||
case groups::GroupType::GROUP_TYPE_TEMPLATE:
|
||||
log_group_type = log::GroupType::TEMPLATE;
|
||||
break;
|
||||
|
||||
case groups::GroupType::GROUP_TYPE_NORMAL:
|
||||
case groups::GroupType::GROUP_TYPE_UNKNOWN:
|
||||
default:
|
||||
log_group_type = log::GroupType::NORMAL;
|
||||
break;
|
||||
}
|
||||
|
||||
serverInstance->action_logger()->group_logger.log_group_permission_copy(target_group->group_type() != groups::GroupType::GROUP_TYPE_NORMAL ? 0 : this->getServerId(),
|
||||
this->ref(), log::GroupTarget::CHANNEL, log_group_type, target_group->group_id(), target_group->display_name(), source_group->group_id(), source_group->display_name());
|
||||
|
||||
|
||||
global_update = !this->server || !group_manager->channel_groups()->find_group(groups::GroupCalculateMode::LOCAL, target_group_id);
|
||||
} else {
|
||||
//Copy a new group
|
||||
auto target_type = cmd["type"].as<groups::GroupType>();
|
||||
|
||||
log::GroupType log_group_type;
|
||||
switch (target_type) {
|
||||
case groups::GroupType::GROUP_TYPE_QUERY:
|
||||
log_group_type = log::GroupType::QUERY;
|
||||
break;
|
||||
|
||||
case groups::GroupType::GROUP_TYPE_TEMPLATE:
|
||||
log_group_type = log::GroupType::TEMPLATE;
|
||||
break;
|
||||
|
||||
case groups::GroupType::GROUP_TYPE_NORMAL:
|
||||
log_group_type = log::GroupType::NORMAL;
|
||||
break;
|
||||
|
||||
case groups::GroupType::GROUP_TYPE_UNKNOWN:
|
||||
default:
|
||||
return command_result{error::parameter_invalid, "type"};
|
||||
}
|
||||
|
||||
{
|
||||
auto result = group_type_modifiable(target_type);
|
||||
if (result != permission::undefined) {
|
||||
return command_result{result};
|
||||
}
|
||||
}
|
||||
|
||||
if (!ref_server && target_type == groups::GroupType::GROUP_TYPE_NORMAL) {
|
||||
return command_result{error::parameter_invalid, "You cant create normal groups on the template server!"};
|
||||
}
|
||||
|
||||
std::shared_ptr<groups::ChannelGroup> created_group{};
|
||||
auto result = group_manager->channel_groups()->copy_group(source_group_id, target_type, cmd["name"].string(), created_group);
|
||||
switch(result) {
|
||||
case groups::GroupCopyResult::SUCCESS:
|
||||
break;
|
||||
|
||||
case groups::GroupCopyResult::UNKNOWN_SOURCE_GROUP:
|
||||
return command_result{error::vs_critical, "internal unknown source group"};
|
||||
|
||||
case groups::GroupCopyResult::UNKNOWN_TARGET_GROUP:
|
||||
return command_result{error::vs_critical, "internal unknown target group"};
|
||||
|
||||
case groups::GroupCopyResult::DATABASE_ERROR:
|
||||
return command_result{error::vs_critical, "database error"};
|
||||
|
||||
case groups::GroupCopyResult::NAME_ALREADY_IN_USE:
|
||||
return command_result{error::group_name_inuse};
|
||||
|
||||
default:
|
||||
return command_result{error::vs_critical};
|
||||
}
|
||||
|
||||
assert(created_group);
|
||||
serverInstance->action_logger()->group_logger.log_group_create(target_type != groups::GroupType::GROUP_TYPE_NORMAL ? 0 : this->getServerId(),
|
||||
this->ref(), log::GroupTarget::CHANNEL, log_group_type, created_group->group_id(), cmd["name"], source_group->group_id(), source_group->display_name());
|
||||
|
||||
{
|
||||
ts::command_builder notify{this->notify_response_command("notifychannelgroupcopied")};
|
||||
notify.put_unchecked(0, "cgid", created_group->group_id());
|
||||
this->sendCommand(notify);
|
||||
}
|
||||
|
||||
global_update = !this->server;
|
||||
}
|
||||
|
||||
for (const auto &server : (global_update ? serverInstance->getVoiceServerManager()->serverInstances() : deque<shared_ptr<VirtualServer>>{this->server})) {
|
||||
if (server) {
|
||||
server->enqueue_notify_channel_group_list();
|
||||
}
|
||||
}
|
||||
return command_result{error::ok};
|
||||
return this->handleCommandGroupCopy(cmd, GroupTarget::GROUPTARGET_CHANNEL);
|
||||
}
|
||||
|
||||
command_result ConnectedClient::handleCommandChannelGroupRename(Command &cmd) {
|
||||
CMD_RESET_IDLE;
|
||||
CMD_CHK_AND_INC_FLOOD_POINTS(5);
|
||||
|
||||
auto group_manager = this->server ? this->server->group_manager() : serverInstance->group_manager();
|
||||
auto channel_group = group_manager->channel_groups()->find_group(groups::GroupCalculateMode::GLOBAL, cmd["cgid"].as<GroupId>());
|
||||
if (!channel_group) {
|
||||
return command_result{error::group_invalid_id};
|
||||
}
|
||||
|
||||
ACTION_REQUIRES_GROUP_PERMISSION(channel_group, permission::i_channel_group_needed_modify_power, permission::i_channel_group_modify_power, true);
|
||||
|
||||
auto type = channel_group->group_type();
|
||||
log::GroupType log_group_type;
|
||||
if (type == groups::GroupType::GROUP_TYPE_QUERY) {
|
||||
ACTION_REQUIRES_GLOBAL_PERMISSION(permission::b_serverinstance_modify_querygroup, 1);
|
||||
log_group_type = log::GroupType::QUERY;
|
||||
} else if (type == groups::GroupType::GROUP_TYPE_TEMPLATE) {
|
||||
ACTION_REQUIRES_GLOBAL_PERMISSION(permission::b_serverinstance_modify_templates, 1);
|
||||
log_group_type = log::GroupType::TEMPLATE;
|
||||
} else {
|
||||
log_group_type = log::GroupType::NORMAL;
|
||||
}
|
||||
|
||||
auto old_name = channel_group->display_name();
|
||||
auto result = group_manager->channel_groups()->rename_group(channel_group->group_id(), cmd["name"].string());
|
||||
switch(result) {
|
||||
case groups::GroupRenameResult::SUCCESS:
|
||||
break;
|
||||
|
||||
case groups::GroupRenameResult::INVALID_GROUP_ID:
|
||||
return ts::command_result{error::vs_critical, "internal invalid group id"};
|
||||
|
||||
case groups::GroupRenameResult::NAME_INVALID:
|
||||
return ts::command_result{error::parameter_invalid, "name"};
|
||||
|
||||
case groups::GroupRenameResult::NAME_ALREADY_USED:
|
||||
return ts::command_result{error::group_name_inuse};
|
||||
|
||||
case groups::GroupRenameResult::DATABASE_ERROR:
|
||||
default:
|
||||
return ts::command_result{error::vs_critical};
|
||||
}
|
||||
|
||||
serverInstance->action_logger()->group_logger.log_group_rename(this->getServerId(), this->ref(), log::GroupTarget::CHANNEL, log_group_type, channel_group->group_id(), channel_group->display_name(), old_name);
|
||||
if (this->server) {
|
||||
this->server->enqueue_notify_channel_group_list();
|
||||
}
|
||||
|
||||
return command_result{error::ok};
|
||||
return this->handleCommandGroupRename(cmd, GroupTarget::GROUPTARGET_CHANNEL);
|
||||
}
|
||||
|
||||
command_result ConnectedClient::handleCommandChannelGroupDel(Command &cmd) {
|
||||
CMD_RESET_IDLE;
|
||||
CMD_CHK_AND_INC_FLOOD_POINTS(5);
|
||||
|
||||
auto ref_server = this->server;
|
||||
auto group_manager = ref_server ? ref_server->group_manager() : serverInstance->group_manager();
|
||||
auto channel_group = group_manager->channel_groups()->find_group(groups::GroupCalculateMode::GLOBAL, cmd["cgid"].as<GroupId>());
|
||||
if (!channel_group) {
|
||||
return command_result{error::group_invalid_id};
|
||||
}
|
||||
|
||||
if(!channel_group->permission_granted(permission::i_channel_group_needed_modify_power, this->calculate_permission(permission::i_channel_group_modify_power, 0), true)) {
|
||||
return command_result{permission::i_channel_group_needed_modify_power};
|
||||
}
|
||||
|
||||
if (this->server) {
|
||||
if (this->server->properties()[property::VIRTUALSERVER_DEFAULT_CHANNEL_GROUP] == channel_group->group_id()) {
|
||||
return command_result{error::parameter_invalid, "Could not delete default channel group!"};
|
||||
}
|
||||
if (this->server->properties()[property::VIRTUALSERVER_DEFAULT_CHANNEL_ADMIN_GROUP] == channel_group->group_id()) {
|
||||
return command_result{error::parameter_invalid, "Could not delete default channel admin group!"};
|
||||
}
|
||||
}
|
||||
|
||||
if (serverInstance->properties()[property::SERVERINSTANCE_TEMPLATE_CHANNELDEFAULT_GROUP] == channel_group->group_id()) {
|
||||
return command_result{error::parameter_invalid, "Could not delete instance default channel group!"};
|
||||
}
|
||||
|
||||
if (serverInstance->properties()[property::SERVERINSTANCE_TEMPLATE_CHANNELADMIN_GROUP] == channel_group->group_id()) {
|
||||
return command_result{error::parameter_invalid, "Could not delete instance default channel admin group!"};
|
||||
}
|
||||
|
||||
log::GroupType log_group_type;
|
||||
switch (channel_group->group_type()) {
|
||||
case groups::GroupType::GROUP_TYPE_QUERY:
|
||||
ACTION_REQUIRES_GLOBAL_PERMISSION(permission::b_serverinstance_modify_querygroup, 1);
|
||||
log_group_type = log::GroupType::QUERY;
|
||||
break;
|
||||
|
||||
case groups::GroupType::GROUP_TYPE_TEMPLATE:
|
||||
ACTION_REQUIRES_GLOBAL_PERMISSION(permission::b_serverinstance_modify_templates, 1);
|
||||
log_group_type = log::GroupType::TEMPLATE;
|
||||
break;
|
||||
|
||||
case groups::GroupType::GROUP_TYPE_NORMAL:
|
||||
ACTION_REQUIRES_GLOBAL_PERMISSION(permission::b_virtualserver_channelgroup_delete, 1);
|
||||
log_group_type = log::GroupType::NORMAL;
|
||||
break;
|
||||
|
||||
case groups::GroupType::GROUP_TYPE_UNKNOWN:
|
||||
default:
|
||||
return ts::command_result{error::vs_critical};
|
||||
}
|
||||
|
||||
if (!cmd["force"].as<bool>()) {
|
||||
if(!group_manager->assignments().is_channel_group_empty(channel_group->group_id())) {
|
||||
return command_result{error::group_not_empty};
|
||||
}
|
||||
}
|
||||
|
||||
auto result = group_manager->channel_groups()->delete_group(channel_group->group_id());
|
||||
switch(result) {
|
||||
case groups::GroupDeleteResult::SUCCESS:
|
||||
break;
|
||||
|
||||
case groups::GroupDeleteResult::INVALID_GROUP_ID:
|
||||
case groups::GroupDeleteResult::DATABASE_ERROR:
|
||||
default:
|
||||
return ts::command_result{error::vs_critical};
|
||||
}
|
||||
|
||||
serverInstance->action_logger()->group_logger.log_group_delete(this->getServerId(), this->ref(), log::GroupTarget::SERVER, log_group_type, channel_group->group_id(), channel_group->display_name());
|
||||
if(ref_server) {
|
||||
auto channel_group_id = channel_group->group_id();
|
||||
|
||||
std::deque<std::shared_ptr<ConnectedClient>> affected_clients{};
|
||||
this->server->forEachClient([&](std::shared_ptr<ConnectedClient> client) {
|
||||
/* TODO: It might be faster to query all clients of the group and search within that list only */
|
||||
auto assigned_group = group_manager->assignments().exact_channel_group_of_client(groups::GroupAssignmentCalculateMode::GLOBAL, client->getClientDatabaseId(), client->getChannelId());
|
||||
if(assigned_group.has_value() && assigned_group->group_id == channel_group_id) {
|
||||
affected_clients.push_back(client);
|
||||
}
|
||||
});
|
||||
|
||||
ref_server->enqueue_notify_channel_group_list();
|
||||
ref_server->group_manager()->assignments().handle_channel_group_deleted(channel_group_id);
|
||||
|
||||
for(const auto& client : affected_clients) {
|
||||
/*
|
||||
* Now we can enqueue all the updates since handle_channel_group_deleted has already been called.
|
||||
* If not done in that order, the group might already got updated before we actiually deleted it from the group manager.
|
||||
*/
|
||||
client->task_update_displayed_groups.enqueue();
|
||||
client->task_update_needed_permissions.enqueue();
|
||||
client->task_update_channel_client_properties.enqueue();
|
||||
}
|
||||
|
||||
ref_server->tokenManager->handle_channel_group_deleted(channel_group_id);
|
||||
}
|
||||
|
||||
return command_result{error::ok};
|
||||
return this->handleCommandGroupDel(cmd, GroupTarget::GROUPTARGET_CHANNEL);
|
||||
}
|
||||
|
||||
command_result ConnectedClient::handleCommandChannelGroupList(Command &) {
|
||||
|
@ -614,95 +215,39 @@ command_result ConnectedClient::handleCommandChannelGroupPermList(Command &cmd)
|
|||
command_result ConnectedClient::handleCommandChannelGroupAddPerm(Command &cmd) {
|
||||
CMD_CHK_AND_INC_FLOOD_POINTS(5);
|
||||
|
||||
auto group_id = cmd["cgid"].as<GroupId>();
|
||||
|
||||
std::shared_ptr<groups::ChannelGroupManager> owning_manager{};
|
||||
auto group_manager = this->server ? this->server->group_manager() : serverInstance->group_manager();
|
||||
auto channelGroup = group_manager->channel_groups()->find_group(groups::GroupCalculateMode::GLOBAL, cmd["cgid"].as<GroupId>());
|
||||
if (!channelGroup) {
|
||||
return command_result{error::group_invalid_id};
|
||||
}
|
||||
ACTION_REQUIRES_GROUP_PERMISSION(channelGroup, permission::i_channel_group_needed_modify_power, permission::i_channel_group_modify_power, true);
|
||||
auto group = group_manager->channel_groups()->find_group_ext(owning_manager, groups::GroupCalculateMode::GLOBAL, group_id);
|
||||
|
||||
ts::command::bulk_parser::PermissionBulksParser<true> pparser{cmd};
|
||||
if (!pparser.validate(this->ref(), 0)) {
|
||||
return pparser.build_command_result();
|
||||
if(!group) {
|
||||
return ts::command_result{error::group_invalid_id};
|
||||
}
|
||||
|
||||
bool updateList{false};
|
||||
for (const auto &ppermission : pparser.iterate_valid_permissions()) {
|
||||
ppermission.apply_to(channelGroup->permissions(), permission::v2::PermissionUpdateType::set_value);
|
||||
ppermission.log_update(serverInstance->action_logger()->permission_logger,
|
||||
this->getServerId(),
|
||||
this->ref(),
|
||||
log::PermissionTarget::CHANNEL_GROUP,
|
||||
permission::v2::PermissionUpdateType::set_value,
|
||||
0, "",
|
||||
channelGroup->group_id(), channelGroup->display_name());
|
||||
ACTION_REQUIRES_GROUP_PERMISSION(group, permission::i_channel_group_needed_modify_power, permission::i_channel_group_modify_power, true);
|
||||
|
||||
updateList |= ppermission.is_group_property();
|
||||
}
|
||||
|
||||
if (this->server) {
|
||||
if (updateList) {
|
||||
this->server->enqueue_notify_channel_group_list();
|
||||
}
|
||||
|
||||
this->server->forEachClient([channelGroup](shared_ptr<ConnectedClient> cl) {
|
||||
unique_lock client_channel_lock(cl->channel_lock); /* while we're updating groups we dont want to change anything! */
|
||||
if (cl->channelGroupAssigned(channelGroup, cl->getChannel())) {
|
||||
cl->task_update_needed_permissions.enqueue();
|
||||
cl->join_state_id++; /* join permission may changed, all channels need to be recalculate dif needed */
|
||||
}
|
||||
client_channel_lock.unlock();
|
||||
|
||||
cl->task_update_channel_client_properties.enqueue();
|
||||
});
|
||||
}
|
||||
|
||||
return pparser.build_command_result();
|
||||
auto target_server = group_manager->channel_groups() == owning_manager ? this->server : nullptr;
|
||||
return this->executeGroupPermissionEdit(cmd, { group }, target_server, permission::v2::PermissionUpdateType::set_value);
|
||||
}
|
||||
|
||||
command_result ConnectedClient::handleCommandChannelGroupDelPerm(Command &cmd) {
|
||||
CMD_CHK_AND_INC_FLOOD_POINTS(5);
|
||||
|
||||
auto group_id = cmd["cgid"].as<GroupId>();
|
||||
|
||||
std::shared_ptr<groups::ChannelGroupManager> owning_manager{};
|
||||
auto group_manager = this->server ? this->server->group_manager() : serverInstance->group_manager();
|
||||
auto channelGroup = group_manager->channel_groups()->find_group(groups::GroupCalculateMode::GLOBAL, cmd["cgid"].as<GroupId>());
|
||||
if (!channelGroup) {
|
||||
return command_result{error::group_invalid_id};
|
||||
}
|
||||
ACTION_REQUIRES_GROUP_PERMISSION(channelGroup, permission::i_channel_group_needed_modify_power, permission::i_channel_group_modify_power, true);
|
||||
auto group = group_manager->channel_groups()->find_group_ext(owning_manager, groups::GroupCalculateMode::GLOBAL, group_id);
|
||||
|
||||
ts::command::bulk_parser::PermissionBulksParser<false> pparser{cmd};
|
||||
if (!pparser.validate(this->ref(), 0))
|
||||
return pparser.build_command_result();
|
||||
|
||||
bool updateList{false};
|
||||
for (const auto &ppermission : pparser.iterate_valid_permissions()) {
|
||||
ppermission.apply_to(channelGroup->permissions(), permission::v2::PermissionUpdateType::delete_value);
|
||||
ppermission.log_update(serverInstance->action_logger()->permission_logger,
|
||||
this->getServerId(),
|
||||
this->ref(),
|
||||
log::PermissionTarget::CHANNEL_GROUP,
|
||||
permission::v2::PermissionUpdateType::delete_value,
|
||||
0, "",
|
||||
channelGroup->group_id(), channelGroup->display_name());
|
||||
updateList |= ppermission.is_group_property();
|
||||
if(!group) {
|
||||
return ts::command_result{error::group_invalid_id};
|
||||
}
|
||||
|
||||
if (this->server) {
|
||||
if (updateList)
|
||||
this->server->enqueue_notify_channel_group_list();
|
||||
this->server->forEachClient([channelGroup](shared_ptr<ConnectedClient> cl) {
|
||||
unique_lock client_channel_lock(cl->channel_lock); /* while we're updating groups we dont want to change anything! */
|
||||
if (cl->channelGroupAssigned(channelGroup, cl->getChannel())) {
|
||||
cl->task_update_needed_permissions.enqueue();
|
||||
cl->join_state_id++; /* join permission may changed, all channels need to be recalculate dif needed */
|
||||
}
|
||||
client_channel_lock.unlock();
|
||||
ACTION_REQUIRES_GROUP_PERMISSION(group, permission::i_channel_group_needed_modify_power, permission::i_channel_group_modify_power, true);
|
||||
|
||||
cl->task_update_channel_client_properties.enqueue();
|
||||
});
|
||||
}
|
||||
|
||||
return pparser.build_command_result();
|
||||
auto target_server = group_manager->channel_groups() == owning_manager ? this->server : nullptr;
|
||||
return this->executeGroupPermissionEdit(cmd, { group }, target_server, permission::v2::PermissionUpdateType::delete_value);
|
||||
}
|
||||
|
||||
//TODO: Test if parent or previous is deleted!
|
||||
|
@ -2236,7 +1781,7 @@ command_result ConnectedClient::handleCommandChannelAddPerm(Command &cmd) {
|
|||
|
||||
ACTION_REQUIRES_CHANNEL_PERMISSION(channel, permission::i_channel_needed_permission_modify_power, permission::i_channel_permission_modify_power, true);
|
||||
|
||||
ts::command::bulk_parser::PermissionBulksParser<true> pparser{cmd};
|
||||
ts::command::bulk_parser::PermissionBulksParser pparser{cmd, true};
|
||||
if (!pparser.validate(this->ref(), channel->channelId())) {
|
||||
return pparser.build_command_result();
|
||||
}
|
||||
|
@ -2315,7 +1860,7 @@ command_result ConnectedClient::handleCommandChannelDelPerm(Command &cmd) {
|
|||
|
||||
ACTION_REQUIRES_CHANNEL_PERMISSION(channel, permission::i_channel_needed_permission_modify_power, permission::i_channel_permission_modify_power, true);
|
||||
|
||||
ts::command::bulk_parser::PermissionBulksParser<false> pparser{cmd};
|
||||
ts::command::bulk_parser::PermissionBulksParser pparser{cmd, false};
|
||||
if (!pparser.validate(this->ref(), channel->channelId()))
|
||||
return pparser.build_command_result();
|
||||
|
||||
|
@ -2428,7 +1973,7 @@ command_result ConnectedClient::handleCommandChannelClientDelPerm(Command &cmd)
|
|||
ACTION_REQUIRES_PERMISSION(permission::i_client_permission_modify_power, required_permissions, channel_id);
|
||||
}
|
||||
|
||||
ts::command::bulk_parser::PermissionBulksParser<false> pparser{cmd};
|
||||
ts::command::bulk_parser::PermissionBulksParser pparser{cmd, false};
|
||||
if (!pparser.validate(this->ref(), channel->channelId()))
|
||||
return pparser.build_command_result();
|
||||
|
||||
|
@ -2495,7 +2040,7 @@ command_result ConnectedClient::handleCommandChannelClientAddPerm(Command &cmd)
|
|||
auto required_permissions = this->server->calculate_permission(permission::i_client_needed_permission_modify_power, cmd["cldbid"], ClientType::CLIENT_TEAMSPEAK, channel_id);
|
||||
ACTION_REQUIRES_PERMISSION(permission::i_client_permission_modify_power, required_permissions, channel_id);
|
||||
|
||||
ts::command::bulk_parser::PermissionBulksParser<true> pparser{cmd};
|
||||
ts::command::bulk_parser::PermissionBulksParser pparser{cmd, true};
|
||||
if (!pparser.validate(this->ref(), channel->channelId()))
|
||||
return pparser.build_command_result();
|
||||
|
||||
|
|
|
@ -985,7 +985,7 @@ command_result ConnectedClient::handleCommandClientAddPerm(Command &cmd) {
|
|||
|
||||
ACTION_REQUIRES_GLOBAL_PERMISSION(permission::i_client_permission_modify_power, this->server->calculate_permission(permission::i_client_needed_permission_modify_power, cldbid, ClientType::CLIENT_TEAMSPEAK, 0));
|
||||
|
||||
ts::command::bulk_parser::PermissionBulksParser<true> pparser{cmd};
|
||||
ts::command::bulk_parser::PermissionBulksParser pparser{cmd, true};
|
||||
if(!pparser.validate(this->ref(), 0))
|
||||
return pparser.build_command_result();
|
||||
|
||||
|
@ -1028,7 +1028,7 @@ command_result ConnectedClient::handleCommandClientDelPerm(Command &cmd) {
|
|||
auto mgr = serverInstance->databaseHelper()->loadClientPermissionManager(this->getServerId(), cldbid);
|
||||
ACTION_REQUIRES_GLOBAL_PERMISSION(permission::i_client_permission_modify_power, this->server->calculate_permission(permission::i_client_needed_permission_modify_power, cldbid, ClientType::CLIENT_TEAMSPEAK, 0));
|
||||
|
||||
ts::command::bulk_parser::PermissionBulksParser<false> pparser{cmd};
|
||||
ts::command::bulk_parser::PermissionBulksParser pparser{cmd, false};
|
||||
if(!pparser.validate(this->ref(), 0))
|
||||
return pparser.build_command_result();
|
||||
|
||||
|
|
|
@ -0,0 +1,862 @@
|
|||
//
|
||||
// Created by WolverinDEV on 08/03/2021.
|
||||
//
|
||||
#include <memory>
|
||||
|
||||
#include <bitset>
|
||||
#include <algorithm>
|
||||
#include <openssl/sha.h>
|
||||
#include "../../build.h"
|
||||
#include "../ConnectedClient.h"
|
||||
#include "../InternalClient.h"
|
||||
#include "../../server/VoiceServer.h"
|
||||
#include "../voice/VoiceClient.h"
|
||||
#include "../../InstanceHandler.h"
|
||||
#include "../../server/QueryServer.h"
|
||||
#include "../music/MusicClient.h"
|
||||
#include "../query/QueryClient.h"
|
||||
#include "../../manager/ConversationManager.h"
|
||||
#include "../../manager/PermissionNameMapper.h"
|
||||
#include "../../manager/ActionLogger.h"
|
||||
#include "../../groups/GroupManager.h"
|
||||
|
||||
#include "helpers.h"
|
||||
#include "./bulk_parsers.h"
|
||||
|
||||
#include <log/LogUtils.h>
|
||||
#include <misc/base64.h>
|
||||
#include <misc/digest.h>
|
||||
#include <misc/rnd.h>
|
||||
#include <misc/scope_guard.h>
|
||||
#include <bbcode/bbcodes.h>
|
||||
|
||||
using namespace std::chrono;
|
||||
using namespace std;
|
||||
using namespace ts;
|
||||
using namespace ts::server;
|
||||
|
||||
|
||||
command_result ConnectedClient::handleCommandGroupAdd(Command &cmd, GroupTarget group_target) {
|
||||
CMD_RESET_IDLE;
|
||||
CMD_CHK_AND_INC_FLOOD_POINTS(5);
|
||||
|
||||
log::GroupTarget log_group_target;
|
||||
switch(group_target) {
|
||||
case GroupTarget::GROUPTARGET_SERVER:
|
||||
ACTION_REQUIRES_GLOBAL_PERMISSION(permission::b_virtualserver_channelgroup_create, 1);
|
||||
log_group_target = log::GroupTarget::SERVER;
|
||||
break;
|
||||
|
||||
case GroupTarget::GROUPTARGET_CHANNEL:
|
||||
ACTION_REQUIRES_GLOBAL_PERMISSION(permission::b_virtualserver_servergroup_create, 1);
|
||||
log_group_target = log::GroupTarget::CHANNEL;
|
||||
break;
|
||||
|
||||
default:
|
||||
return ts::command_result{error::vs_critical, "internal invalid group target"};
|
||||
}
|
||||
|
||||
auto group_manager = this->server ? this->server->group_manager() : serverInstance->group_manager();
|
||||
log::GroupType log_group_type;
|
||||
auto group_type = cmd[0].has("type") ? cmd["type"].as<groups::GroupType>() : groups::GroupType::GROUP_TYPE_NORMAL;
|
||||
switch (group_type) {
|
||||
case groups::GroupType::GROUP_TYPE_QUERY:
|
||||
ACTION_REQUIRES_GLOBAL_PERMISSION(permission::b_serverinstance_modify_querygroup, 1);
|
||||
log_group_type = log::GroupType::QUERY;
|
||||
break;
|
||||
|
||||
case groups::GroupType::GROUP_TYPE_TEMPLATE:
|
||||
ACTION_REQUIRES_GLOBAL_PERMISSION(permission::b_serverinstance_modify_templates, 1);
|
||||
log_group_type = log::GroupType::TEMPLATE;
|
||||
break;
|
||||
|
||||
case groups::GroupType::GROUP_TYPE_NORMAL:
|
||||
if (!this->server) {
|
||||
return command_result{error::parameter_invalid, "you cant create normal groups on the template server!"};
|
||||
}
|
||||
log_group_type = log::GroupType::NORMAL;
|
||||
break;
|
||||
|
||||
case groups::GroupType::GROUP_TYPE_UNKNOWN:
|
||||
default:
|
||||
return command_result{error::parameter_invalid, "type"};
|
||||
}
|
||||
|
||||
std::shared_ptr<groups::Group> group;
|
||||
groups::GroupCreateResult result;
|
||||
|
||||
std::string notify_name;
|
||||
std::string notify_id_key;
|
||||
switch(group_target) {
|
||||
case GroupTarget::GROUPTARGET_SERVER: {
|
||||
std::shared_ptr<groups::ServerGroup> s_group;
|
||||
result = group_manager->server_groups()->create_group(group_type, cmd["name"].string(), s_group);
|
||||
group = s_group;
|
||||
|
||||
notify_name = this->notify_response_command("notifyservergroupadded");
|
||||
notify_id_key = "sgid";
|
||||
break;
|
||||
}
|
||||
case GroupTarget::GROUPTARGET_CHANNEL: {
|
||||
std::shared_ptr<groups::ChannelGroup> c_group;
|
||||
result = group_manager->channel_groups()->create_group(group_type, cmd["name"].string(), c_group);
|
||||
group = c_group;
|
||||
|
||||
notify_name = this->notify_response_command("notifychannelgroupadded");
|
||||
notify_id_key = "cgid";
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
assert(false);
|
||||
result = groups::GroupCreateResult::DATABASE_ERROR;
|
||||
break;
|
||||
}
|
||||
|
||||
switch(result) {
|
||||
case groups::GroupCreateResult::SUCCESS:
|
||||
break;
|
||||
|
||||
case groups::GroupCreateResult::NAME_TOO_SHORT:
|
||||
case groups::GroupCreateResult::NAME_TOO_LONG:
|
||||
return command_result{error::parameter_invalid, "name"};
|
||||
|
||||
case groups::GroupCreateResult::NAME_ALREADY_IN_USED:
|
||||
return command_result{error::group_name_inuse};
|
||||
|
||||
case groups::GroupCreateResult::DATABASE_ERROR:
|
||||
default:
|
||||
return command_result{error::vs_critical};
|
||||
}
|
||||
|
||||
assert(group);
|
||||
assert(!notify_id_key.empty());
|
||||
serverInstance->action_logger()->group_logger.log_group_create(this->getServerId(), this->ref(), log_group_target, log_group_type, group->group_id(), group->display_name(), 0, "");
|
||||
|
||||
{
|
||||
ts::command_builder notify{notify_name};
|
||||
notify.put_unchecked(0, notify_id_key, group->group_id());
|
||||
this->sendCommand(notify);
|
||||
}
|
||||
|
||||
group->permissions()->set_permission(permission::b_group_is_permanent, {1, 0}, permission::v2::set_value, permission::v2::do_nothing);
|
||||
if (this->server) {
|
||||
switch(group_target) {
|
||||
case GroupTarget::GROUPTARGET_SERVER:
|
||||
this->server->enqueue_notify_server_group_list();
|
||||
break;
|
||||
|
||||
case GroupTarget::GROUPTARGET_CHANNEL:
|
||||
this->server->enqueue_notify_channel_group_list();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
std::deque<std::shared_ptr<VirtualServer>> server_updates{};
|
||||
if(!this->server) {
|
||||
server_updates = serverInstance->getVoiceServerManager()->serverInstances();
|
||||
} else {
|
||||
server_updates.push_back(this->server);
|
||||
}
|
||||
|
||||
for(const auto& server : server_updates) {
|
||||
if(!server) {
|
||||
continue;
|
||||
}
|
||||
|
||||
switch(group_target) {
|
||||
case GroupTarget::GROUPTARGET_SERVER:
|
||||
server->enqueue_notify_server_group_list();
|
||||
break;
|
||||
|
||||
case GroupTarget::GROUPTARGET_CHANNEL:
|
||||
server->enqueue_notify_channel_group_list();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return command_result{error::ok};
|
||||
}
|
||||
|
||||
command_result ConnectedClient::handleCommandGroupCopy(Command &cmd, GroupTarget group_target) {
|
||||
CMD_RESET_IDLE;
|
||||
CMD_CHK_AND_INC_FLOOD_POINTS(5);
|
||||
|
||||
auto ref_server = this->server;
|
||||
auto group_manager = ref_server ? ref_server->group_manager() : serverInstance->group_manager();
|
||||
|
||||
log::GroupTarget log_group_target;
|
||||
GroupId source_group_id, target_group_id;
|
||||
std::shared_ptr<groups::Group> source_group{}, target_group{};
|
||||
bool target_group_global;
|
||||
|
||||
switch(group_target) {
|
||||
case GroupTarget::GROUPTARGET_SERVER: {
|
||||
std::shared_ptr<groups::ServerGroupManager> owning_manager{};
|
||||
|
||||
source_group_id = cmd["ssgid"].as<GroupId>();
|
||||
target_group_id = cmd[0].has("tsgid") ? cmd["tsgid"].as<GroupId>() : 0;
|
||||
|
||||
source_group = group_manager->server_groups()->find_group(groups::GroupCalculateMode::GLOBAL, source_group_id);
|
||||
target_group = group_manager->server_groups()->find_group_ext(owning_manager, groups::GroupCalculateMode::GLOBAL, target_group_id);
|
||||
target_group_global = owning_manager != group_manager->server_groups();
|
||||
|
||||
log_group_target = log::GroupTarget::SERVER;
|
||||
break;
|
||||
}
|
||||
|
||||
case GroupTarget::GROUPTARGET_CHANNEL: {
|
||||
std::shared_ptr<groups::ChannelGroupManager> owning_manager{};
|
||||
|
||||
source_group_id = cmd["scgid"].as<GroupId>();
|
||||
target_group_id = cmd[0].has("tcgid") ? cmd["tcgid"].as<GroupId>() : 0;
|
||||
|
||||
source_group = group_manager->channel_groups()->find_group(groups::GroupCalculateMode::GLOBAL, source_group_id);
|
||||
target_group = group_manager->channel_groups()->find_group_ext(owning_manager, groups::GroupCalculateMode::GLOBAL, target_group_id);
|
||||
target_group_global = owning_manager != group_manager->channel_groups();
|
||||
|
||||
log_group_target = log::GroupTarget::CHANNEL;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!source_group) {
|
||||
return command_result{error::group_invalid_id, "invalid source group id"};
|
||||
}
|
||||
|
||||
if (target_group_id > 0 && !target_group) {
|
||||
return command_result{error::group_invalid_id, "invalid target group"};
|
||||
}
|
||||
|
||||
const auto group_type_modifiable = [&](groups::GroupType type) {
|
||||
switch (type) {
|
||||
case groups::GroupType::GROUP_TYPE_TEMPLATE:
|
||||
if (!permission::v2::permission_granted(1, this->calculate_permission(permission::b_serverinstance_modify_templates, 0))) {
|
||||
return permission::b_serverinstance_modify_templates;
|
||||
}
|
||||
|
||||
break;
|
||||
case groups::GroupType::GROUP_TYPE_QUERY:
|
||||
if (!permission::v2::permission_granted(1, this->calculate_permission(permission::b_serverinstance_modify_querygroup, 0))) {
|
||||
return permission::b_serverinstance_modify_querygroup;
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case groups::GroupType::GROUP_TYPE_NORMAL:
|
||||
if (!permission::v2::permission_granted(1, this->calculate_permission(permission::b_virtualserver_channelgroup_create, 0))) {
|
||||
return permission::b_virtualserver_channelgroup_create;
|
||||
}
|
||||
break;
|
||||
|
||||
case groups::GroupType::GROUP_TYPE_UNKNOWN:
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return permission::undefined;
|
||||
};
|
||||
|
||||
{
|
||||
auto result = group_type_modifiable(source_group->group_type());
|
||||
if (result != permission::undefined) {
|
||||
return command_result{result};
|
||||
}
|
||||
}
|
||||
|
||||
auto global_update = false;
|
||||
if (target_group) {
|
||||
if(!target_group->permission_granted(permission::i_channel_group_needed_modify_power, this->calculate_permission(permission::i_channel_group_modify_power, 0), true)) {
|
||||
return ts::command_result{permission::i_channel_group_needed_modify_power};
|
||||
}
|
||||
|
||||
{
|
||||
auto result = group_type_modifiable(target_group->group_type());
|
||||
if (result != permission::undefined) {
|
||||
return command_result{result};
|
||||
}
|
||||
}
|
||||
|
||||
groups::GroupCopyResult result;
|
||||
switch(group_target) {
|
||||
case GroupTarget::GROUPTARGET_SERVER:
|
||||
result = group_manager->server_groups()->copy_group_permissions(source_group_id, target_group_id);
|
||||
break;
|
||||
|
||||
case GroupTarget::GROUPTARGET_CHANNEL:
|
||||
result = group_manager->channel_groups()->copy_group_permissions(source_group_id, target_group_id);
|
||||
break;
|
||||
}
|
||||
|
||||
switch(result) {
|
||||
case groups::GroupCopyResult::SUCCESS:
|
||||
break;
|
||||
|
||||
case groups::GroupCopyResult::UNKNOWN_SOURCE_GROUP:
|
||||
return command_result{error::vs_critical, "internal unknown source group"};
|
||||
|
||||
case groups::GroupCopyResult::UNKNOWN_TARGET_GROUP:
|
||||
return command_result{error::vs_critical, "internal unknown target group"};
|
||||
|
||||
case groups::GroupCopyResult::DATABASE_ERROR:
|
||||
return command_result{error::vs_critical, "database error"};
|
||||
|
||||
case groups::GroupCopyResult::NAME_ALREADY_IN_USE:
|
||||
return command_result{error::group_name_inuse};
|
||||
|
||||
case groups::GroupCopyResult::NAME_INVALID:
|
||||
default:
|
||||
return command_result{error::vs_critical};
|
||||
}
|
||||
|
||||
log::GroupType log_group_type;
|
||||
switch (target_group->group_type()) {
|
||||
case groups::GroupType::GROUP_TYPE_QUERY:
|
||||
log_group_type = log::GroupType::QUERY;
|
||||
break;
|
||||
|
||||
case groups::GroupType::GROUP_TYPE_TEMPLATE:
|
||||
log_group_type = log::GroupType::TEMPLATE;
|
||||
break;
|
||||
|
||||
case groups::GroupType::GROUP_TYPE_NORMAL:
|
||||
case groups::GroupType::GROUP_TYPE_UNKNOWN:
|
||||
default:
|
||||
log_group_type = log::GroupType::NORMAL;
|
||||
break;
|
||||
}
|
||||
|
||||
serverInstance->action_logger()->group_logger.log_group_permission_copy(target_group->group_type() != groups::GroupType::GROUP_TYPE_NORMAL ? 0 : this->getServerId(),
|
||||
this->ref(), log_group_target, log_group_type, target_group->group_id(), target_group->display_name(), source_group->group_id(), source_group->display_name());
|
||||
|
||||
global_update = !this->server || target_group_global;
|
||||
} else {
|
||||
//Copy to a new group
|
||||
auto target_type = cmd["type"].as<groups::GroupType>();
|
||||
|
||||
log::GroupType log_group_type;
|
||||
switch (target_type) {
|
||||
case groups::GroupType::GROUP_TYPE_QUERY:
|
||||
log_group_type = log::GroupType::QUERY;
|
||||
break;
|
||||
|
||||
case groups::GroupType::GROUP_TYPE_TEMPLATE:
|
||||
log_group_type = log::GroupType::TEMPLATE;
|
||||
break;
|
||||
|
||||
case groups::GroupType::GROUP_TYPE_NORMAL:
|
||||
log_group_type = log::GroupType::NORMAL;
|
||||
break;
|
||||
|
||||
case groups::GroupType::GROUP_TYPE_UNKNOWN:
|
||||
default:
|
||||
return command_result{error::parameter_invalid, "type"};
|
||||
}
|
||||
|
||||
{
|
||||
auto result = group_type_modifiable(target_type);
|
||||
if (result != permission::undefined) {
|
||||
return command_result{result};
|
||||
}
|
||||
}
|
||||
|
||||
if (!ref_server && target_type == groups::GroupType::GROUP_TYPE_NORMAL) {
|
||||
return command_result{error::parameter_invalid, "You cant create normal groups on the template server!"};
|
||||
}
|
||||
|
||||
std::shared_ptr<groups::Group> created_group{};
|
||||
std::string notify_name, notify_id_key;
|
||||
groups::GroupCopyResult result;
|
||||
|
||||
switch(group_target) {
|
||||
case GroupTarget::GROUPTARGET_SERVER: {
|
||||
std::shared_ptr<groups::ServerGroup> s_group{};
|
||||
result = group_manager->server_groups()->copy_group(source_group_id, target_type, cmd["name"].string(), s_group);
|
||||
created_group = s_group;
|
||||
|
||||
notify_name = this->notify_response_command("notifyservergroupcopied");
|
||||
notify_id_key = "sgid";
|
||||
break;
|
||||
}
|
||||
case GroupTarget::GROUPTARGET_CHANNEL: {
|
||||
std::shared_ptr<groups::ChannelGroup> c_group{};
|
||||
result = group_manager->channel_groups()->copy_group(source_group_id, target_type, cmd["name"].string(), c_group);
|
||||
created_group = c_group;
|
||||
|
||||
notify_name = this->notify_response_command("notifychannelgroupcopied");
|
||||
notify_id_key = "cgid";
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
result = groups::GroupCopyResult::DATABASE_ERROR;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
switch(result) {
|
||||
case groups::GroupCopyResult::SUCCESS:
|
||||
break;
|
||||
|
||||
case groups::GroupCopyResult::UNKNOWN_SOURCE_GROUP:
|
||||
return command_result{error::vs_critical, "internal unknown source group"};
|
||||
|
||||
case groups::GroupCopyResult::UNKNOWN_TARGET_GROUP:
|
||||
return command_result{error::vs_critical, "internal unknown target group"};
|
||||
|
||||
case groups::GroupCopyResult::DATABASE_ERROR:
|
||||
return command_result{error::vs_critical, "database error"};
|
||||
|
||||
case groups::GroupCopyResult::NAME_ALREADY_IN_USE:
|
||||
return command_result{error::group_name_inuse};
|
||||
|
||||
case groups::GroupCopyResult::NAME_INVALID:
|
||||
return command_result{error::parameter_invalid, "name"};
|
||||
|
||||
default:
|
||||
return command_result{error::vs_critical};
|
||||
}
|
||||
|
||||
assert(!notify_name.empty());
|
||||
assert(created_group);
|
||||
serverInstance->action_logger()->group_logger.log_group_create(target_type != groups::GroupType::GROUP_TYPE_NORMAL ? 0 : this->getServerId(),
|
||||
this->ref(), log_group_target, log_group_type, created_group->group_id(), cmd["name"], source_group->group_id(), source_group->display_name());
|
||||
|
||||
{
|
||||
ts::command_builder notify{notify_name};
|
||||
notify.put_unchecked(0, notify_id_key, created_group->group_id());
|
||||
this->sendCommand(notify);
|
||||
}
|
||||
|
||||
global_update = !this->server;
|
||||
}
|
||||
|
||||
std::deque<std::shared_ptr<VirtualServer>> server_updates{};
|
||||
if(global_update) {
|
||||
server_updates = serverInstance->getVoiceServerManager()->serverInstances();
|
||||
} else {
|
||||
server_updates.push_back(this->server);
|
||||
}
|
||||
|
||||
for(const auto& server : server_updates) {
|
||||
if(!server) {
|
||||
continue;
|
||||
}
|
||||
|
||||
switch(group_target) {
|
||||
case GroupTarget::GROUPTARGET_SERVER:
|
||||
server->enqueue_notify_server_group_list();
|
||||
break;
|
||||
|
||||
case GroupTarget::GROUPTARGET_CHANNEL:
|
||||
server->enqueue_notify_channel_group_list();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return command_result{error::ok};
|
||||
}
|
||||
|
||||
command_result ConnectedClient::handleCommandGroupRename(Command &cmd, GroupTarget group_target) {
|
||||
CMD_RESET_IDLE;
|
||||
CMD_CHK_AND_INC_FLOOD_POINTS(5);
|
||||
|
||||
auto group_manager = this->server ? this->server->group_manager() : serverInstance->group_manager();
|
||||
|
||||
log::GroupTarget log_group_target;
|
||||
std::shared_ptr<groups::Group> group{};
|
||||
GroupId group_id;
|
||||
|
||||
switch(group_target) {
|
||||
case GroupTarget::GROUPTARGET_SERVER:
|
||||
group_id = cmd["sgid"].as<GroupId>();
|
||||
group = group_manager->server_groups()->find_group(groups::GroupCalculateMode::GLOBAL, group_id);
|
||||
|
||||
if(!group) {
|
||||
return ts::command_result{error::group_invalid_id};
|
||||
}
|
||||
|
||||
ACTION_REQUIRES_GROUP_PERMISSION(group, permission::i_server_group_needed_modify_power, permission::i_server_group_modify_power, true);
|
||||
|
||||
log_group_target = log::GroupTarget::SERVER;
|
||||
break;
|
||||
|
||||
case GroupTarget::GROUPTARGET_CHANNEL:
|
||||
group_id = cmd["cgid"].as<GroupId>();
|
||||
group = group_manager->channel_groups()->find_group(groups::GroupCalculateMode::GLOBAL, group_id);
|
||||
|
||||
if(!group) {
|
||||
return ts::command_result{error::group_invalid_id};
|
||||
}
|
||||
|
||||
ACTION_REQUIRES_GROUP_PERMISSION(group, permission::i_channel_group_needed_modify_power, permission::i_channel_group_modify_power, true);
|
||||
|
||||
log_group_target = log::GroupTarget::CHANNEL;
|
||||
break;
|
||||
|
||||
default:
|
||||
return ts::command_result{error::vs_critical, "internal invalid group target"};
|
||||
}
|
||||
assert(group);
|
||||
|
||||
auto type = group->group_type();
|
||||
log::GroupType log_group_type;
|
||||
if (type == groups::GroupType::GROUP_TYPE_QUERY) {
|
||||
ACTION_REQUIRES_GLOBAL_PERMISSION(permission::b_serverinstance_modify_querygroup, 1);
|
||||
log_group_type = log::GroupType::QUERY;
|
||||
} else if (type == groups::GroupType::GROUP_TYPE_TEMPLATE) {
|
||||
ACTION_REQUIRES_GLOBAL_PERMISSION(permission::b_serverinstance_modify_templates, 1);
|
||||
log_group_type = log::GroupType::TEMPLATE;
|
||||
} else {
|
||||
log_group_type = log::GroupType::NORMAL;
|
||||
}
|
||||
|
||||
auto old_name = group->display_name();
|
||||
groups::GroupRenameResult rename_result;
|
||||
switch(group_target) {
|
||||
case GroupTarget::GROUPTARGET_SERVER:
|
||||
rename_result = group_manager->server_groups()->rename_group(group->group_id(), cmd["name"].string());
|
||||
break;
|
||||
|
||||
case GroupTarget::GROUPTARGET_CHANNEL:
|
||||
rename_result = group_manager->channel_groups()->rename_group(group->group_id(), cmd["name"].string());
|
||||
break;
|
||||
|
||||
default:
|
||||
assert(false);
|
||||
rename_result = groups::GroupRenameResult::DATABASE_ERROR;
|
||||
break;
|
||||
}
|
||||
|
||||
switch(rename_result) {
|
||||
case groups::GroupRenameResult::SUCCESS:
|
||||
break;
|
||||
|
||||
case groups::GroupRenameResult::INVALID_GROUP_ID:
|
||||
return ts::command_result{error::vs_critical, "internal invalid group id"};
|
||||
|
||||
case groups::GroupRenameResult::NAME_INVALID:
|
||||
return ts::command_result{error::parameter_invalid, "name"};
|
||||
|
||||
case groups::GroupRenameResult::NAME_ALREADY_USED:
|
||||
return ts::command_result{error::group_name_inuse};
|
||||
|
||||
case groups::GroupRenameResult::DATABASE_ERROR:
|
||||
default:
|
||||
return ts::command_result{error::vs_critical};
|
||||
}
|
||||
|
||||
serverInstance->action_logger()->group_logger.log_group_rename(this->getServerId(), this->ref(), log_group_target, log_group_type, group->group_id(), group->display_name(), old_name);
|
||||
std::deque<std::shared_ptr<VirtualServer>> server_updates{};
|
||||
if(!this->server) {
|
||||
server_updates = serverInstance->getVoiceServerManager()->serverInstances();
|
||||
} else {
|
||||
server_updates.push_back(this->server);
|
||||
}
|
||||
|
||||
for(const auto& server : server_updates) {
|
||||
if(!server) {
|
||||
continue;
|
||||
}
|
||||
|
||||
switch(group_target) {
|
||||
case GroupTarget::GROUPTARGET_SERVER:
|
||||
server->enqueue_notify_server_group_list();
|
||||
break;
|
||||
|
||||
case GroupTarget::GROUPTARGET_CHANNEL:
|
||||
server->enqueue_notify_channel_group_list();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return command_result{error::ok};
|
||||
}
|
||||
|
||||
command_result ConnectedClient::handleCommandGroupDel(Command &cmd, GroupTarget group_target) {
|
||||
CMD_RESET_IDLE;
|
||||
CMD_CHK_AND_INC_FLOOD_POINTS(5);
|
||||
|
||||
auto ref_server = this->server;
|
||||
auto group_manager = ref_server ? ref_server->group_manager() : serverInstance->group_manager();
|
||||
|
||||
log::GroupTarget log_group_target;
|
||||
std::shared_ptr<groups::Group> group{};
|
||||
GroupId group_id;
|
||||
|
||||
switch (group_target) {
|
||||
case GroupTarget::GROUPTARGET_SERVER:
|
||||
group_id = cmd["sgid"].as<GroupId>();
|
||||
group = group_manager->server_groups()->find_group(groups::GroupCalculateMode::GLOBAL, group_id);
|
||||
|
||||
if(!group) {
|
||||
return ts::command_result{error::group_invalid_id};
|
||||
}
|
||||
|
||||
ACTION_REQUIRES_GROUP_PERMISSION(group, permission::i_server_group_needed_modify_power, permission::i_server_group_modify_power, true);
|
||||
|
||||
log_group_target = log::GroupTarget::SERVER;
|
||||
break;
|
||||
|
||||
case GroupTarget::GROUPTARGET_CHANNEL:
|
||||
group_id = cmd["cgid"].as<GroupId>();
|
||||
group = group_manager->channel_groups()->find_group(groups::GroupCalculateMode::GLOBAL, group_id);
|
||||
|
||||
if(!group) {
|
||||
return ts::command_result{error::group_invalid_id};
|
||||
}
|
||||
|
||||
ACTION_REQUIRES_GROUP_PERMISSION(group, permission::i_channel_group_needed_modify_power, permission::i_channel_group_modify_power, true);
|
||||
|
||||
log_group_target = log::GroupTarget::CHANNEL;
|
||||
break;
|
||||
|
||||
default:
|
||||
return ts::command_result{error::vs_critical, "internal invalid group target"};
|
||||
}
|
||||
assert(group);
|
||||
|
||||
/* Test if the group is one of the default group */
|
||||
switch (group_target) {
|
||||
case GroupTarget::GROUPTARGET_SERVER:
|
||||
if(this->server) {
|
||||
if(this->server->properties()[property::VIRTUALSERVER_DEFAULT_SERVER_GROUP] == group->group_id()) {
|
||||
return command_result{error::parameter_invalid, "Could not delete default server group!"};
|
||||
}
|
||||
}
|
||||
|
||||
if(serverInstance->properties()[property::SERVERINSTANCE_TEMPLATE_SERVERADMIN_GROUP] == group->group_id()) {
|
||||
return command_result{error::parameter_invalid, "Could not delete instance default server admin group!"};
|
||||
}
|
||||
|
||||
if(serverInstance->properties()[property::SERVERINSTANCE_TEMPLATE_SERVERDEFAULT_GROUP] == group->group_id()) {
|
||||
return command_result{error::parameter_invalid, "Could not delete instance default server group!"};
|
||||
}
|
||||
|
||||
if(serverInstance->properties()[property::SERVERINSTANCE_GUEST_SERVERQUERY_GROUP] == group->group_id()) {
|
||||
return command_result{error::parameter_invalid, "Could not delete instance default guest server query group!"};
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case GroupTarget::GROUPTARGET_CHANNEL:
|
||||
if (this->server) {
|
||||
if (this->server->properties()[property::VIRTUALSERVER_DEFAULT_CHANNEL_GROUP] == group->group_id()) {
|
||||
return command_result{error::parameter_invalid, "Could not delete default channel group!"};
|
||||
}
|
||||
if (this->server->properties()[property::VIRTUALSERVER_DEFAULT_CHANNEL_ADMIN_GROUP] == group->group_id()) {
|
||||
return command_result{error::parameter_invalid, "Could not delete default channel admin group!"};
|
||||
}
|
||||
}
|
||||
|
||||
if (serverInstance->properties()[property::SERVERINSTANCE_TEMPLATE_CHANNELDEFAULT_GROUP] == group->group_id()) {
|
||||
return command_result{error::parameter_invalid, "Could not delete instance default channel group!"};
|
||||
}
|
||||
|
||||
if(serverInstance->properties()[property::SERVERINSTANCE_TEMPLATE_CHANNELADMIN_GROUP] == group->group_id()) {
|
||||
return command_result{error::parameter_invalid, "Could not delete instance default channel admin group!"};
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
log::GroupType log_group_type;
|
||||
switch (group->group_type()) {
|
||||
case groups::GroupType::GROUP_TYPE_QUERY:
|
||||
ACTION_REQUIRES_GLOBAL_PERMISSION(permission::b_serverinstance_modify_querygroup, 1);
|
||||
log_group_type = log::GroupType::QUERY;
|
||||
break;
|
||||
|
||||
case groups::GroupType::GROUP_TYPE_TEMPLATE:
|
||||
ACTION_REQUIRES_GLOBAL_PERMISSION(permission::b_serverinstance_modify_templates, 1);
|
||||
log_group_type = log::GroupType::TEMPLATE;
|
||||
break;
|
||||
|
||||
case groups::GroupType::GROUP_TYPE_NORMAL:
|
||||
switch(group_target) {
|
||||
case GroupTarget::GROUPTARGET_SERVER:
|
||||
ACTION_REQUIRES_GLOBAL_PERMISSION(permission::b_virtualserver_servergroup_delete, 1);
|
||||
break;
|
||||
|
||||
case GroupTarget::GROUPTARGET_CHANNEL:
|
||||
ACTION_REQUIRES_GLOBAL_PERMISSION(permission::b_virtualserver_channelgroup_delete, 1);
|
||||
break;
|
||||
}
|
||||
|
||||
log_group_type = log::GroupType::NORMAL;
|
||||
break;
|
||||
|
||||
case groups::GroupType::GROUP_TYPE_UNKNOWN:
|
||||
default:
|
||||
return ts::command_result{error::vs_critical};
|
||||
}
|
||||
|
||||
auto force_delete = cmd[0].has("force") ? cmd["force"].as<bool>() : false;
|
||||
|
||||
groups::GroupDeleteResult result;
|
||||
switch (group_target) {
|
||||
case GroupTarget::GROUPTARGET_SERVER:
|
||||
if(!force_delete && !group_manager->assignments().is_server_group_empty(group->group_id())) {
|
||||
result = groups::GroupDeleteResult::GROUP_NOT_EMPTY;
|
||||
break;
|
||||
}
|
||||
|
||||
result = group_manager->server_groups()->delete_group(group->group_id());
|
||||
break;
|
||||
|
||||
case GroupTarget::GROUPTARGET_CHANNEL:
|
||||
if(!force_delete && !group_manager->assignments().is_channel_group_empty(group->group_id())) {
|
||||
result = groups::GroupDeleteResult::GROUP_NOT_EMPTY;
|
||||
break;
|
||||
}
|
||||
|
||||
result = group_manager->channel_groups()->delete_group(group->group_id());
|
||||
break;
|
||||
}
|
||||
|
||||
switch(result) {
|
||||
case groups::GroupDeleteResult::SUCCESS:
|
||||
break;
|
||||
|
||||
case groups::GroupDeleteResult::GROUP_NOT_EMPTY:
|
||||
return ts::command_result{error::group_not_empty};
|
||||
|
||||
case groups::GroupDeleteResult::INVALID_GROUP_ID:
|
||||
case groups::GroupDeleteResult::DATABASE_ERROR:
|
||||
default:
|
||||
return ts::command_result{error::vs_critical};
|
||||
}
|
||||
|
||||
serverInstance->action_logger()->group_logger.log_group_delete(this->getServerId(), this->ref(), log_group_target, log_group_type, group->group_id(), group->display_name());
|
||||
|
||||
std::deque<std::shared_ptr<VirtualServer>> server_updates{};
|
||||
if(!this->server) {
|
||||
server_updates = serverInstance->getVoiceServerManager()->serverInstances();
|
||||
} else {
|
||||
server_updates.push_back(this->server);
|
||||
}
|
||||
|
||||
for(const auto& server : server_updates) {
|
||||
if(!server) {
|
||||
continue;
|
||||
}
|
||||
|
||||
switch(group_target) {
|
||||
case GroupTarget::GROUPTARGET_SERVER:
|
||||
server->enqueue_notify_server_group_list();
|
||||
server->group_manager()->assignments().handle_server_group_deleted(group->group_id());
|
||||
server->tokenManager->handle_server_group_deleted(group->group_id());
|
||||
break;
|
||||
|
||||
case GroupTarget::GROUPTARGET_CHANNEL:
|
||||
server->enqueue_notify_channel_group_list();
|
||||
server->group_manager()->assignments().handle_channel_group_deleted(group->group_id());
|
||||
server->tokenManager->handle_channel_group_deleted(group->group_id());
|
||||
break;
|
||||
}
|
||||
|
||||
this->server->forEachClient([&](const std::shared_ptr<ConnectedClient>& client) {
|
||||
bool groups_changed;
|
||||
client->update_displayed_client_groups(groups_changed, groups_changed);
|
||||
|
||||
if(groups_changed) {
|
||||
client->task_update_needed_permissions.enqueue();
|
||||
client->task_update_channel_client_properties.enqueue();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
return command_result{error::ok};
|
||||
}
|
||||
|
||||
command_result ConnectedClient::executeGroupPermissionEdit(Command &cmd,
|
||||
const std::vector<std::shared_ptr<groups::Group>> &groups,
|
||||
const std::shared_ptr<VirtualServer> &target_server,
|
||||
permission::v2::PermissionUpdateType mode) {
|
||||
ts::command::bulk_parser::PermissionBulksParser pparser{cmd, mode == permission::v2::PermissionUpdateType::set_value};
|
||||
if (!pparser.validate(this->ref(), 0)) {
|
||||
return pparser.build_command_result();
|
||||
}
|
||||
|
||||
bool update_channel_group_list{false}, update_server_group_list{false};
|
||||
for(const auto& group : groups) {
|
||||
bool* update_group_list;
|
||||
log::PermissionTarget log_group_target;
|
||||
|
||||
if(dynamic_pointer_cast<groups::ServerGroup>(group)) {
|
||||
update_group_list = &update_server_group_list;
|
||||
log_group_target = log::PermissionTarget::SERVER_GROUP;
|
||||
} else {
|
||||
update_group_list = &update_channel_group_list;
|
||||
log_group_target = log::PermissionTarget::CHANNEL_GROUP;
|
||||
}
|
||||
|
||||
for (const auto &ppermission : pparser.iterate_valid_permissions()) {
|
||||
ppermission.apply_to(group->permissions(), mode);
|
||||
ppermission.log_update(serverInstance->action_logger()->permission_logger,
|
||||
this->getServerId(),
|
||||
this->ref(),
|
||||
log_group_target,
|
||||
mode,
|
||||
0, "",
|
||||
group->group_id(), group->display_name());
|
||||
|
||||
*update_group_list |= ppermission.is_group_property();
|
||||
}
|
||||
}
|
||||
|
||||
std::deque<std::shared_ptr<VirtualServer>> server_updates{};
|
||||
if(target_server) {
|
||||
server_updates.push_back(target_server);
|
||||
} else {
|
||||
server_updates = serverInstance->getVoiceServerManager()->serverInstances();
|
||||
}
|
||||
|
||||
for(const auto& server : server_updates) {
|
||||
if(!server) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if(update_server_group_list) {
|
||||
server->enqueue_notify_server_group_list();
|
||||
}
|
||||
|
||||
if(update_channel_group_list) {
|
||||
server->enqueue_notify_channel_group_list();
|
||||
}
|
||||
|
||||
btree::set<ClientId> updated_clients{};
|
||||
for(const auto& group : groups) {
|
||||
if(auto s_group{dynamic_pointer_cast<groups::ServerGroup>(group)}; s_group) {
|
||||
server->forEachClient([&](const std::shared_ptr<ConnectedClient> &client) {
|
||||
if(updated_clients.count(client->getClientId()) > 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
if(client->serverGroupAssigned(s_group)) {
|
||||
updated_clients.emplace(client->getClientId());
|
||||
client->task_update_channel_client_properties.enqueue();
|
||||
client->task_update_needed_permissions.enqueue();
|
||||
client->join_state_id++;
|
||||
}
|
||||
});
|
||||
} else if(auto c_group{dynamic_pointer_cast<groups::ChannelGroup>(group)}; c_group) {
|
||||
server->forEachClient([&](const std::shared_ptr<ConnectedClient> &client) {
|
||||
if(updated_clients.count(client->getClientId()) > 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto channel = client->getChannel();
|
||||
if(client->channelGroupAssigned(c_group, channel)) {
|
||||
updated_clients.emplace(client->getClientId());
|
||||
client->task_update_channel_client_properties.enqueue();
|
||||
client->task_update_needed_permissions.enqueue();
|
||||
client->join_state_id++;
|
||||
}
|
||||
});
|
||||
} else {
|
||||
assert(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return pparser.build_command_result();
|
||||
}
|
|
@ -461,7 +461,7 @@ command_result ConnectedClient::handleCommandSetClientChannelGroup(Command &cmd)
|
|||
}
|
||||
}
|
||||
|
||||
shared_lock server_channel_lock{this->server->channel_tree_lock}; /* ensure we dont get moved or somebody could move us */
|
||||
std::shared_lock server_channel_lock{this->server->channel_tree_lock}; /* ensure we dont get moved or somebody could move us */
|
||||
auto channel_id = cmd["cid"].as<ChannelId>();
|
||||
auto channel = this->server->channelTree->findChannel(channel_id);
|
||||
if (!channel) {
|
||||
|
@ -524,17 +524,23 @@ command_result ConnectedClient::handleCommandSetClientChannelGroup(Command &cmd)
|
|||
bool channel_group_changed, server_group_changed;
|
||||
targetClient->update_displayed_client_groups(server_group_changed, channel_group_changed);
|
||||
|
||||
auto client_channel = targetClient->getChannel();
|
||||
if(!client_channel) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if(channel_group_changed) {
|
||||
targetClient->task_update_needed_permissions.enqueue();
|
||||
targetClient->task_update_displayed_groups.enqueue();
|
||||
|
||||
std::optional<ts::command_builder> notify{};
|
||||
for (const auto &viewer : this->server->getClients()) {
|
||||
/* if in view will be tested within that method */
|
||||
std::shared_lock viewer_channel_lock{viewer->channel_lock, defer_lock};
|
||||
if(viewer != targetClient) {
|
||||
viewer_channel_lock.lock();
|
||||
}
|
||||
viewer->notifyClientChannelGroupChanged(this->ref(), targetClient, targetClient->getChannel(), channel, target_channel_group, false);
|
||||
viewer->notifyClientChannelGroupChanged(
|
||||
notify,
|
||||
this->ref(), targetClient,
|
||||
client_channel->channelId(),
|
||||
targetClient->properties()[property::CLIENT_CHANNEL_GROUP_INHERITED_CHANNEL_ID],
|
||||
targetClient->properties()[property::CLIENT_CHANNEL_GROUP_ID]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -2205,7 +2211,6 @@ command_result ConnectedClient::handleCommandPermOverview(Command &cmd) {
|
|||
return command_result{error::channel_invalid_id};
|
||||
}
|
||||
|
||||
auto channel_group = this->assignedChannelGroup(channel);
|
||||
auto permission_manager = serverInstance->databaseHelper()->loadClientPermissionManager(this->getServerId(), client_dbid);
|
||||
|
||||
Command result(this->getExternalType() == ClientType::CLIENT_TEAMSPEAK ? "notifypermoverview" : "");
|
||||
|
@ -2302,14 +2307,18 @@ command_result ConnectedClient::handleCommandPermOverview(Command &cmd) {
|
|||
}
|
||||
}
|
||||
|
||||
{
|
||||
auto permission_manager = channel_group->group->permissions();
|
||||
auto interited_channel{channel};
|
||||
auto channel_group = this->assignedChannelGroup(interited_channel);
|
||||
if(channel_group) {
|
||||
assert(interited_channel);
|
||||
|
||||
auto permission_manager = channel_group->permissions();
|
||||
for(const auto& permission_data : permission_manager->permissions()) {
|
||||
auto& permission = std::get<1>(permission_data);
|
||||
if(permission.flags.value_set) {
|
||||
result[index]["t"] = 3; /* channel group */
|
||||
result[index]["id1"] = channel_group->channelId;
|
||||
result[index]["id2"] = channel_group->group->groupId();
|
||||
result[index]["id1"] = interited_channel->channelId();
|
||||
result[index]["id2"] = channel_group->group_id();
|
||||
|
||||
result[index]["p"] = std::get<0>(permission_data);
|
||||
result[index]["v"] = permission.values.value;
|
||||
|
@ -2319,8 +2328,8 @@ command_result ConnectedClient::handleCommandPermOverview(Command &cmd) {
|
|||
}
|
||||
if(permission.flags.grant_set) {
|
||||
result[index]["t"] = 3; /* channel group */
|
||||
result[index]["id1"] = channel_group->channelId;
|
||||
result[index]["id2"] = channel_group->group->groupId();
|
||||
result[index]["id1"] = interited_channel->channelId();
|
||||
result[index]["id2"] = channel_group->group_id();
|
||||
|
||||
result[index]["p"] = (std::get<0>(permission_data) | PERM_ID_GRANT);
|
||||
result[index]["v"] = permission.values.grant;
|
||||
|
|
|
@ -557,7 +557,7 @@ command_result ConnectedClient::handleCommandPlaylistAddPerm(ts::Command &cmd) {
|
|||
if(auto perr = playlist->client_has_permissions(this->ref(), permission::i_playlist_needed_permission_modify_power, permission::i_playlist_permission_modify_power); perr)
|
||||
return command_result{perr};
|
||||
|
||||
ts::command::bulk_parser::PermissionBulksParser<true> pparser{cmd};
|
||||
ts::command::bulk_parser::PermissionBulksParser pparser{cmd, true};
|
||||
if(!pparser.validate(this->ref(), 0))
|
||||
return pparser.build_command_result();
|
||||
|
||||
|
@ -587,7 +587,7 @@ command_result ConnectedClient::handleCommandPlaylistDelPerm(ts::Command &cmd) {
|
|||
if(auto perr = playlist->client_has_permissions(this->ref(), permission::i_playlist_needed_permission_modify_power, permission::i_playlist_permission_modify_power); perr)
|
||||
return command_result{perr};
|
||||
|
||||
ts::command::bulk_parser::PermissionBulksParser<false> pparser{cmd};
|
||||
ts::command::bulk_parser::PermissionBulksParser pparser{cmd, false};
|
||||
if(!pparser.validate(this->ref(), 0))
|
||||
return pparser.build_command_result();
|
||||
|
||||
|
@ -719,7 +719,7 @@ command_result ConnectedClient::handleCommandPlaylistClientAddPerm(ts::Command &
|
|||
if(auto perr = playlist->client_has_permissions(this->ref(), permission::i_playlist_needed_permission_modify_power, permission::i_playlist_permission_modify_power); perr)
|
||||
return command_result{perr};
|
||||
|
||||
ts::command::bulk_parser::PermissionBulksParser<true> pparser{cmd};
|
||||
ts::command::bulk_parser::PermissionBulksParser pparser{cmd, true};
|
||||
if(!pparser.validate(this->ref(), this->getClientDatabaseId()))
|
||||
return pparser.build_command_result();
|
||||
|
||||
|
@ -753,7 +753,7 @@ command_result ConnectedClient::handleCommandPlaylistClientDelPerm(ts::Command &
|
|||
return command_result{perr};
|
||||
|
||||
|
||||
ts::command::bulk_parser::PermissionBulksParser<false> pparser{cmd};
|
||||
ts::command::bulk_parser::PermissionBulksParser pparser{cmd, false};
|
||||
if(!pparser.validate(this->ref(), this->getClientDatabaseId()))
|
||||
return pparser.build_command_result();
|
||||
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -6,6 +6,7 @@
|
|||
#include <pipes/errors.h>
|
||||
#include <misc/std_unique_ptr.h>
|
||||
#include <log/LogUtils.h>
|
||||
#include "../../groups/GroupAssignmentManager.h"
|
||||
|
||||
using namespace std;
|
||||
using namespace std::chrono;
|
||||
|
@ -631,9 +632,6 @@ void QueryClient::disconnect_from_virtual_server(const std::string& reason) {
|
|||
this->currentChannel = nullptr;
|
||||
}
|
||||
|
||||
old_server->group_manager()->disableCache(this->getClientDatabaseId());
|
||||
this->loadDataForCurrentServer();
|
||||
}
|
||||
|
||||
serverInstance->group_manager()->enableCache(this->getClientDatabaseId());
|
||||
}
|
|
@ -134,11 +134,21 @@ namespace ts::server {
|
|||
bool notifyClientChatComposing(const std::shared_ptr<ConnectedClient> &ptr) override;
|
||||
bool notifyClientChatClosed(const std::shared_ptr<ConnectedClient> &ptr) override;
|
||||
bool notifyTextMessage(ChatMessageMode mode, const std::shared_ptr<ConnectedClient> &sender, uint64_t targetId, ChannelId channel_id, const std::chrono::system_clock::time_point&, const std::string &textMessage) override;
|
||||
bool notifyServerGroupClientAdd(const std::shared_ptr<ConnectedClient> &invoker, const std::shared_ptr<ConnectedClient> &client, const std::shared_ptr<Group> &group) override;
|
||||
bool notifyServerGroupClientRemove(std::shared_ptr<ConnectedClient> invoker, std::shared_ptr<ConnectedClient> client, std::shared_ptr<Group> group) override;
|
||||
|
||||
bool notifyClientChannelGroupChanged(const std::shared_ptr<ConnectedClient> &invoker, const std::shared_ptr<ConnectedClient> &client, const std::shared_ptr<BasicChannel> &ptr, const std::shared_ptr<BasicChannel> &shared_ptr,
|
||||
const std::shared_ptr<Group> &group, bool lock_channel_tree) override;
|
||||
bool notifyServerGroupClientAdd(std::optional<ts::command_builder> &anOptional,
|
||||
const std::shared_ptr<ConnectedClient> &ptr,
|
||||
const std::shared_ptr<ConnectedClient> &sharedPtr,
|
||||
const GroupId &id) override;
|
||||
|
||||
bool notifyServerGroupClientRemove(std::optional<ts::command_builder> &anOptional,
|
||||
const std::shared_ptr<ConnectedClient> &ptr,
|
||||
const std::shared_ptr<ConnectedClient> &sharedPtr,
|
||||
const GroupId &id) override;
|
||||
|
||||
bool notifyClientChannelGroupChanged(std::optional<ts::command_builder> &anOptional,
|
||||
const std::shared_ptr<ConnectedClient> &ptr,
|
||||
const std::shared_ptr<ConnectedClient> &sharedPtr, const ChannelId &id,
|
||||
const ChannelId &channelId, const GroupId &groupId) override;
|
||||
|
||||
bool notifyChannelMoved(const std::shared_ptr<BasicChannel> &channel, ChannelId order, const std::shared_ptr<ConnectedClient> &invoker) override;
|
||||
bool notifyChannelCreate(const std::shared_ptr<BasicChannel> &channel, ChannelId orderId,
|
||||
|
|
|
@ -13,7 +13,7 @@
|
|||
#include <ThreadPool/Timer.h>
|
||||
#include <numeric>
|
||||
#include "src/manager/ActionLogger.h"
|
||||
|
||||
#include "../../groups/GroupManager.h"
|
||||
#include "src/client/command_handler/helpers.h"
|
||||
|
||||
using namespace std;
|
||||
|
@ -244,12 +244,12 @@ command_result QueryClient::handleCommandLogin(Command& cmd) {
|
|||
if(this->server) {
|
||||
{
|
||||
unique_lock tree_lock(this->server->channel_tree_lock);
|
||||
if(joined_channel)
|
||||
if(joined_channel) {
|
||||
this->server->client_move(this->ref(), nullptr, nullptr, "", ViewReasonId::VREASON_USER_ACTION, false, tree_lock);
|
||||
}
|
||||
this->server->unregisterClient(this->ref(), "login", tree_lock);
|
||||
}
|
||||
this->server->group_manager()->disableCache(this->getClientDatabaseId());
|
||||
} else serverInstance->group_manager()->disableCache(this->getClientDatabaseId());
|
||||
}
|
||||
|
||||
logMessage(LOG_QUERY, "Got new authenticated client. Username: {}, Unique-ID: {}, Bounded Server: {}", account->username, account->unique_id, account->bound_server);
|
||||
|
||||
|
@ -269,7 +269,6 @@ command_result QueryClient::handleCommandLogin(Command& cmd) {
|
|||
|
||||
DatabaseHelper::assignDatabaseId(this->sql, static_cast<ServerId>(target_server ? target_server->getServerId() : 0), this->ref());
|
||||
if(target_server) {
|
||||
target_server->group_manager()->enableCache(this->getClientDatabaseId());
|
||||
target_server->registerClient(this->ref());
|
||||
|
||||
{
|
||||
|
@ -293,7 +292,6 @@ command_result QueryClient::handleCommandLogin(Command& cmd) {
|
|||
this->task_update_needed_permissions.enqueue();
|
||||
}
|
||||
} else {
|
||||
serverInstance->group_manager()->enableCache(this->getClientDatabaseId());
|
||||
this->task_update_needed_permissions.enqueue();
|
||||
}
|
||||
|
||||
|
@ -318,15 +316,13 @@ command_result QueryClient::handleCommandLogout(Command &) {
|
|||
this->server->client_move(this->ref(), nullptr, nullptr, "", ViewReasonId::VREASON_USER_ACTION, false, tree_lock);
|
||||
this->server->unregisterClient(this->ref(), "logout", tree_lock);
|
||||
}
|
||||
this->server->group_manager()->disableCache(this->getClientDatabaseId());
|
||||
} else serverInstance->group_manager()->disableCache(this->getClientDatabaseId());
|
||||
}
|
||||
|
||||
this->properties()[property::CLIENT_UNIQUE_IDENTIFIER] = "UnknownQuery"; //TODO load from table
|
||||
this->properties()[property::CLIENT_NICKNAME] = string() + "ServerQuery#" + this->getLoggingPeerIp() + "/" + to_string(ntohs(this->getPeerPort()));
|
||||
DatabaseHelper::assignDatabaseId(this->sql, static_cast<ServerId>(this->server ? this->server->getServerId() : 0), this->ref());
|
||||
|
||||
if(this->server){
|
||||
this->server->group_manager()->enableCache(this->getClientDatabaseId());
|
||||
this->server->registerClient(this->ref());
|
||||
|
||||
{
|
||||
|
@ -347,7 +343,6 @@ command_result QueryClient::handleCommandLogout(Command &) {
|
|||
this->task_update_needed_permissions.enqueue();
|
||||
}
|
||||
} else {
|
||||
serverInstance->group_manager()->enableCache(this->getClientDatabaseId());
|
||||
this->task_update_needed_permissions.enqueue();
|
||||
}
|
||||
|
||||
|
@ -401,7 +396,6 @@ command_result QueryClient::handleCommandServerSelect(Command &cmd) {
|
|||
|
||||
DatabaseHelper::assignDatabaseId(this->sql, static_cast<ServerId>(this->server ? this->server->getServerId() : 0), this->ref());
|
||||
if(this->server) {
|
||||
this->server->group_manager()->enableCache(this->getClientDatabaseId());
|
||||
this->server->registerClient(this->ref());
|
||||
|
||||
{
|
||||
|
@ -420,7 +414,6 @@ command_result QueryClient::handleCommandServerSelect(Command &cmd) {
|
|||
this->task_update_needed_permissions.enqueue();
|
||||
}
|
||||
} else {
|
||||
serverInstance->group_manager()->enableCache(this->getClientDatabaseId());
|
||||
this->task_update_needed_permissions.enqueue();
|
||||
}
|
||||
|
||||
|
|
|
@ -75,20 +75,27 @@ bool QueryClient::notifyTextMessage(ChatMessageMode mode, const shared_ptr<Conne
|
|||
return ConnectedClient::notifyTextMessage(mode, sender, targetId, channel_id, tp, textMessage);
|
||||
}
|
||||
|
||||
bool QueryClient::notifyServerGroupClientAdd(const shared_ptr<ConnectedClient> &invoker, const shared_ptr<ConnectedClient> &client, const shared_ptr<Group> &group) {
|
||||
|
||||
bool QueryClient::notifyServerGroupClientAdd(optional<ts::command_builder> &anOptional,
|
||||
const shared_ptr<ConnectedClient> &ptr,
|
||||
const shared_ptr<ConnectedClient> &sharedPtr, const GroupId &id) {
|
||||
CHK_EVENT(QEVENTGROUP_CLIENT_GROUPS, QEVENTSPECIFIER_CLIENT_GROUPS_ADD);
|
||||
return ConnectedClient::notifyServerGroupClientAdd(invoker, client, group);
|
||||
return ConnectedClient::notifyServerGroupClientAdd(anOptional, ptr, sharedPtr, id);
|
||||
}
|
||||
|
||||
bool QueryClient::notifyServerGroupClientRemove(std::shared_ptr<ConnectedClient> invoker, std::shared_ptr<ConnectedClient> client, std::shared_ptr<Group> group) {
|
||||
bool QueryClient::notifyServerGroupClientRemove(optional<ts::command_builder> &anOptional,
|
||||
const shared_ptr<ConnectedClient> &ptr,
|
||||
const shared_ptr<ConnectedClient> &sharedPtr, const GroupId &id) {
|
||||
CHK_EVENT(QEVENTGROUP_CLIENT_GROUPS, QEVENTSPECIFIER_CLIENT_GROUPS_REMOVE);
|
||||
return ConnectedClient::notifyServerGroupClientRemove(invoker, client, group);
|
||||
return ConnectedClient::notifyServerGroupClientRemove(anOptional, ptr, sharedPtr, id);
|
||||
}
|
||||
|
||||
bool QueryClient::notifyClientChannelGroupChanged(const std::shared_ptr<ConnectedClient> &invoker, const std::shared_ptr<ConnectedClient> &client, const std::shared_ptr<BasicChannel> &ptr, const std::shared_ptr<BasicChannel> &shared_ptr,
|
||||
const std::shared_ptr<Group> &group, bool lock_channel_tree) {
|
||||
bool QueryClient::notifyClientChannelGroupChanged(optional<ts::command_builder> &anOptional,
|
||||
const shared_ptr <ConnectedClient> &ptr,
|
||||
const shared_ptr <ConnectedClient> &sharedPtr, const ChannelId &id,
|
||||
const ChannelId &channelId, const GroupId &groupId) {
|
||||
CHK_EVENT(QEVENTGROUP_CLIENT_GROUPS, QEVENTSPECIFIER_CLIENT_GROUPS_CHANNEL_CHANGED);
|
||||
return ConnectedClient::notifyClientChannelGroupChanged(invoker, client, ptr, shared_ptr, group, lock_channel_tree);
|
||||
return ConnectedClient::notifyClientChannelGroupChanged(anOptional, ptr, sharedPtr, id, channelId, groupId);
|
||||
}
|
||||
|
||||
bool QueryClient::notifyChannelMoved(const std::shared_ptr<BasicChannel> &channel, ChannelId order, const std::shared_ptr<ConnectedClient> &invoker) {
|
||||
|
|
|
@ -73,8 +73,8 @@ namespace ts::server::whisper {
|
|||
* This also updates the last head ptr.
|
||||
* @return True if the packet is a valid whisper packet. The payload ptr and payload length variables will be set
|
||||
*/
|
||||
bool validate_whisper_packet(const protocol::PacketParser& /* packet */, bool& /* matches last header */, void*& /* payload ptr */, size_t& /* payload length */);
|
||||
ts::command_result configure_rtc_clients(uint32_t /* stream id */, const std::vector<std::shared_ptr<SpeakingClient>>& /* clients */);
|
||||
[[nodiscard]] bool validate_whisper_packet(const protocol::PacketParser& /* packet */, bool& /* matches last header */, void*& /* payload ptr */, size_t& /* payload length */);
|
||||
[[nodiscard]] ts::command_result configure_rtc_clients(uint32_t /* stream id */, const std::vector<std::shared_ptr<SpeakingClient>>& /* clients */);
|
||||
|
||||
[[nodiscard]] size_t max_whisper_targets();
|
||||
};
|
||||
|
|
|
@ -144,6 +144,7 @@ void PacketEncoder::send_command(const std::string_view &command, bool low, std:
|
|||
|
||||
*packets_tail = packet;
|
||||
packets_tail = &packet->next;
|
||||
assert(!packet->next);
|
||||
|
||||
data_length -= bytes;
|
||||
if(data_length == 0) {
|
||||
|
@ -161,6 +162,7 @@ void PacketEncoder::send_command(const std::string_view &command, bool low, std:
|
|||
|
||||
packets_head = packet;
|
||||
packets_tail = &packet->next;
|
||||
assert(!packet->next);
|
||||
}
|
||||
|
||||
{
|
||||
|
|
|
@ -21,7 +21,7 @@ constexpr static auto kMaxWhisperClientNameLength{30};
|
|||
constexpr static auto kWhisperClientUniqueIdLength{28}; /* base64 encoded SHA1 hash */
|
||||
|
||||
VoiceClient::VoiceClient(const std::shared_ptr<VoiceServer>& server, const sockaddr_storage* address) :
|
||||
SpeakingClient(server->server->sql, server->server),
|
||||
SpeakingClient{server->server->sql, server->server},
|
||||
voice_server(server) {
|
||||
assert(address);
|
||||
memtrack::allocated<VoiceClient>(this);
|
||||
|
|
|
@ -227,7 +227,6 @@ bool WebClient::close_connection(const std::chrono::system_clock::time_point& ti
|
|||
unique_lock server_channel_lock(this->server->channel_tree_lock);
|
||||
this->server->unregisterClient(this->ref(), "disconnected", server_channel_lock);
|
||||
}
|
||||
this->server->group_manager()->disableCache(this->getClientDatabaseId());
|
||||
//this->server = nullptr;
|
||||
}
|
||||
|
||||
|
|
|
@ -22,26 +22,28 @@ namespace ts::server::groups {
|
|||
typedef uint32_t GroupIconId;
|
||||
|
||||
class AbstractGroupManager;
|
||||
|
||||
class Group {
|
||||
friend class AbstractGroupManager;
|
||||
|
||||
public:
|
||||
Group(ServerId /* server id */, GroupId /* id */, GroupType /* type */, std::string /* name */, std::shared_ptr<permission::v2::PermissionManager> /* permissions */);
|
||||
Group(ServerId /* server id */, GroupId /* id */, GroupType /* type */, std::string /* name */,
|
||||
std::shared_ptr<permission::v2::PermissionManager> /* permissions */);
|
||||
|
||||
virtual ~Group() = default;
|
||||
|
||||
/* information getters */
|
||||
[[nodiscard]] inline ServerId virtual_server_id() const { return this->virtual_server_id_; }
|
||||
|
||||
[[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 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::PermissionManager> 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 std::shared_ptr<permission::v2::PermissionManager>
|
||||
permissions() { return this->permissions_; }
|
||||
|
||||
[[nodiscard]] inline GroupNameMode name_mode() const {
|
||||
assert(this->permissions_);
|
||||
|
@ -75,17 +77,19 @@ namespace ts::server::groups {
|
|||
return data.has_value ? data.value : 0;
|
||||
}
|
||||
|
||||
[[nodiscard]] inline bool permission_granted(const permission::PermissionType& permission, const permission::v2::PermissionFlaggedValue& granted_value, bool require_granted_value) {
|
||||
[[nodiscard]] inline bool permission_granted(const permission::PermissionType &permission,
|
||||
const permission::v2::PermissionFlaggedValue &granted_value,
|
||||
bool require_granted_value) {
|
||||
auto permission_manager = this->permissions_;;
|
||||
assert(permission_manager);
|
||||
const auto data = permission_manager->permission_value_flagged(permission);
|
||||
if(!data.has_value) {
|
||||
if (!data.has_value) {
|
||||
return !require_granted_value || granted_value.has_value;
|
||||
}
|
||||
if(!granted_value.has_value) {
|
||||
if (!granted_value.has_value) {
|
||||
return false;
|
||||
}
|
||||
if(data.value == -1) {
|
||||
if (data.value == -1) {
|
||||
return granted_value.value == -1;
|
||||
}
|
||||
return granted_value.value >= data.value;
|
||||
|
@ -99,17 +103,19 @@ namespace ts::server::groups {
|
|||
|
||||
std::shared_ptr<permission::v2::PermissionManager> permissions_;
|
||||
|
||||
void set_permissions(const std::shared_ptr<permission::v2::PermissionManager>& /* permissions */);
|
||||
void set_permissions(const std::shared_ptr<permission::v2::PermissionManager> & /* permissions */);
|
||||
};
|
||||
|
||||
class ServerGroup : public Group {
|
||||
public:
|
||||
ServerGroup(ServerId /* server id */, GroupId /* id */, GroupType /* type */, std::string /* name */, std::shared_ptr<permission::v2::PermissionManager> /* permissions */);
|
||||
ServerGroup(ServerId /* server id */, GroupId /* id */, GroupType /* type */, std::string /* name */,
|
||||
std::shared_ptr<permission::v2::PermissionManager> /* permissions */);
|
||||
};
|
||||
|
||||
class ChannelGroup : public Group {
|
||||
public:
|
||||
ChannelGroup(ServerId /* server id */, GroupId /* id */, GroupType /* type */, std::string /* name */, std::shared_ptr<permission::v2::PermissionManager> /* permissions */);
|
||||
ChannelGroup(ServerId /* server id */, GroupId /* id */, GroupType /* type */, std::string /* name */,
|
||||
std::shared_ptr<permission::v2::PermissionManager> /* permissions */);
|
||||
};
|
||||
}
|
||||
DEFINE_TRANSFORMS(ts::server::groups::GroupType, uint8_t);
|
||||
|
|
|
@ -8,7 +8,30 @@
|
|||
|
||||
using namespace ts::server::groups;
|
||||
|
||||
GroupAssignmentManager::GroupAssignmentManager(GroupManager* handle) : manager_{handle} { }
|
||||
namespace ts::server::groups {
|
||||
struct InternalChannelGroupAssignment {
|
||||
InternalChannelGroupAssignment(ChannelId channel_id, GroupId group_id, bool t) : channel_id{channel_id}, group_id{group_id}, temporary_assignment{t} { }
|
||||
InternalChannelGroupAssignment(const InternalChannelGroupAssignment& other) = default;
|
||||
InternalChannelGroupAssignment(InternalChannelGroupAssignment&&) = default;
|
||||
InternalChannelGroupAssignment&operator=(const InternalChannelGroupAssignment&) = default;
|
||||
|
||||
ChannelId channel_id;
|
||||
GroupId group_id;
|
||||
bool temporary_assignment;
|
||||
};
|
||||
|
||||
struct InternalServerGroupAssignment {
|
||||
explicit InternalServerGroupAssignment(GroupId group_id, bool temporary) : group_id{group_id}, temporary_assignment{temporary} { }
|
||||
InternalServerGroupAssignment(const InternalServerGroupAssignment& other) = default;
|
||||
InternalServerGroupAssignment(InternalServerGroupAssignment&&) = default;
|
||||
InternalServerGroupAssignment&operator=(const InternalServerGroupAssignment&) = default;
|
||||
|
||||
GroupId group_id;
|
||||
bool temporary_assignment;
|
||||
};
|
||||
}
|
||||
|
||||
GroupAssignmentManager::GroupAssignmentManager(GroupManager* handle) : manager_{handle}, client_cache_lock{std::make_shared<std::mutex>()} { }
|
||||
GroupAssignmentManager::~GroupAssignmentManager() = default;
|
||||
|
||||
bool GroupAssignmentManager::initialize(std::string &error) {
|
||||
|
@ -25,8 +48,8 @@ ts::ServerId GroupAssignmentManager::server_id() {
|
|||
|
||||
bool GroupAssignmentManager::load_data(std::string &error) {
|
||||
if constexpr(kCacheAllClients) {
|
||||
std::lock_guard cache_lock{this->client_cache_lock};
|
||||
std::unique_ptr<ClientCache> current_entry{nullptr};
|
||||
std::lock_guard cache_lock{*this->client_cache_lock};
|
||||
std::shared_ptr<ClientCache> 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) {
|
||||
|
@ -55,14 +78,15 @@ bool GroupAssignmentManager::load_data(std::string &error) {
|
|||
this->client_cache.push_back(std::move(current_entry));
|
||||
|
||||
if(!current_entry) {
|
||||
current_entry = std::make_unique<ClientCache>();
|
||||
current_entry = std::make_shared<ClientCache>();
|
||||
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);
|
||||
if(channel_id) {
|
||||
current_entry->channel_group_assignments.push_back(std::make_unique<InternalChannelGroupAssignment>(channel_id, group_id, false));
|
||||
} else {
|
||||
current_entry->server_group_assignments.push_back(std::make_unique<InternalServerGroupAssignment>(group_id, false));
|
||||
}
|
||||
return 0;
|
||||
});
|
||||
|
||||
|
@ -79,7 +103,7 @@ bool GroupAssignmentManager::load_data(std::string &error) {
|
|||
}
|
||||
|
||||
void GroupAssignmentManager::unload_data() {
|
||||
std::lock_guard cache_lock{this->client_cache_lock};
|
||||
std::lock_guard cache_lock{*this->client_cache_lock};
|
||||
this->client_cache.clear();
|
||||
}
|
||||
|
||||
|
@ -87,7 +111,7 @@ void GroupAssignmentManager::enable_cache_for_client(GroupAssignmentCalculateMod
|
|||
if constexpr(!kCacheAllClients) {
|
||||
bool cache_exists{false};
|
||||
{
|
||||
std::lock_guard cache_lock{this->client_cache_lock};
|
||||
std::lock_guard cache_lock{*this->client_cache_lock};
|
||||
for(auto& client : this->client_cache)
|
||||
if(client->client_database_id == cldbid) {
|
||||
client->use_count++;
|
||||
|
@ -97,7 +121,7 @@ void GroupAssignmentManager::enable_cache_for_client(GroupAssignmentCalculateMod
|
|||
}
|
||||
|
||||
if(!cache_exists) {
|
||||
auto cache = std::make_unique<ClientCache>();
|
||||
auto cache = std::make_shared<ClientCache>();
|
||||
cache->client_database_id = cldbid;
|
||||
cache->use_count++;
|
||||
|
||||
|
@ -122,14 +146,15 @@ void GroupAssignmentManager::enable_cache_for_client(GroupAssignmentCalculateMod
|
|||
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);
|
||||
if(channel_id) {
|
||||
cache->channel_group_assignments.push_back(std::make_unique<InternalChannelGroupAssignment>(channel_id, group_id, false));
|
||||
} else {
|
||||
cache->server_group_assignments.push_back(std::make_unique<InternalServerGroupAssignment>(group_id, false));
|
||||
}
|
||||
return 0;
|
||||
});
|
||||
|
||||
std::lock_guard cache_lock{this->client_cache_lock};
|
||||
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) {
|
||||
|
@ -152,8 +177,8 @@ void GroupAssignmentManager::enable_cache_for_client(GroupAssignmentCalculateMod
|
|||
|
||||
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<ClientCache>& client) {
|
||||
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::shared_ptr<ClientCache>& client) {
|
||||
return client->client_database_id == cldbid;
|
||||
}), this->client_cache.end());
|
||||
}
|
||||
|
@ -169,13 +194,13 @@ std::vector<ts::GroupId> GroupAssignmentManager::server_groups_of_client(ts::ser
|
|||
std::vector<ts::GroupId> result{};
|
||||
bool cache_found{false};
|
||||
{
|
||||
std::lock_guard cache_lock{this->client_cache_lock};
|
||||
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);
|
||||
result.push_back(assignment->group_id);
|
||||
|
||||
cache_found = true;
|
||||
break;
|
||||
|
@ -222,12 +247,18 @@ std::vector<ChannelGroupAssignment> GroupAssignmentManager::exact_channel_groups
|
|||
std::vector<ChannelGroupAssignment> result{};
|
||||
bool cache_found{false};
|
||||
{
|
||||
std::lock_guard cache_lock{this->client_cache_lock};
|
||||
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());
|
||||
for(const auto& assignment : entry->channel_group_assignments) {
|
||||
result.push_back(ChannelGroupAssignment{
|
||||
.client_database_id = cldbid,
|
||||
.channel_id = assignment->channel_id,
|
||||
.group_id = assignment->group_id,
|
||||
});
|
||||
}
|
||||
cache_found = true;
|
||||
break;
|
||||
}
|
||||
|
@ -255,7 +286,12 @@ std::vector<ChannelGroupAssignment> GroupAssignmentManager::exact_channel_groups
|
|||
}
|
||||
if(!group_id || !channel_id) return 0;
|
||||
|
||||
result.emplace_back(channel_id, group_id, false);
|
||||
result.push_back(ChannelGroupAssignment{
|
||||
.client_database_id = cldbid,
|
||||
.channel_id = channel_id,
|
||||
.group_id = group_id,
|
||||
});
|
||||
|
||||
return 0;
|
||||
});
|
||||
LOG_SQL_CMD(res);
|
||||
|
@ -273,7 +309,7 @@ std::vector<ChannelGroupAssignment> GroupAssignmentManager::exact_channel_groups
|
|||
}
|
||||
|
||||
std::optional<ChannelGroupAssignment> GroupAssignmentManager::exact_channel_group_of_client(GroupAssignmentCalculateMode mode,
|
||||
ClientDbId client_database_id, ChannelId channel_id) {
|
||||
ClientDbId client_database_id, ChannelId channel_id) {
|
||||
/* TODO: Improve performance by not querying all groups */
|
||||
auto assignments = this->exact_channel_groups_of_client(mode, client_database_id);
|
||||
for(const auto& assignment : assignments) {
|
||||
|
@ -310,44 +346,87 @@ std::optional<ts::GroupId> GroupAssignmentManager::calculate_channel_group_of_cl
|
|||
return std::nullopt;
|
||||
}
|
||||
|
||||
std::deque<ts::ClientDbId> GroupAssignmentManager::server_group_clients(GroupId group_id) {
|
||||
std::deque<ts::ClientDbId> result{};
|
||||
if constexpr(kCacheAllClients) {
|
||||
std::lock_guard cache_lock{this->client_cache_lock};
|
||||
std::deque<ServerGroupAssignment> GroupAssignmentManager::server_group_clients(GroupId group_id, bool full_info) {
|
||||
std::deque<ServerGroupAssignment> result{};
|
||||
|
||||
if(kCacheAllClients && !full_info) {
|
||||
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;
|
||||
auto it = std::find_if(client->server_group_assignments.begin(), client->server_group_assignments.end(), [&](const std::unique_ptr<InternalServerGroupAssignment>& assignment) {
|
||||
return assignment->group_id == group_id;
|
||||
});
|
||||
if(it == client->server_group_assignments.end()) {
|
||||
continue;
|
||||
}
|
||||
result.push_back(client->client_database_id);
|
||||
result.push_back(ServerGroupAssignment{
|
||||
.client_database_id = client->client_database_id,
|
||||
.group_id = group_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;
|
||||
if(full_info) {
|
||||
constexpr static auto kSqlCommand{
|
||||
"SELECT `cldbid`, clients_server.client_unique_id, clients_server.client_nickname FROM `assignedGroups` "
|
||||
"LEFT JOIN `clients_server` ON `assignedGroups`.`serverId` = `clients_server`.`server_id` AND `assignedGroups`.`cldbid` = `clients_server`.`client_database_id` "
|
||||
"WHERE `serverId` = :sid AND `channelId` = 0 AND `groupId` = :gid;"
|
||||
};
|
||||
|
||||
result.push_back(cldbid);
|
||||
return 0;
|
||||
});
|
||||
LOG_SQL_CMD(res);
|
||||
std::string sql_command{kSqlCommand};
|
||||
auto sql = sql::command{this->sql_manager(), kSqlCommand, variable{":sid", this->server_id()}, variable{":gid", group_id}};
|
||||
LOG_SQL_CMD(sql.query([&](int length, std::string* values, std::string* names) {
|
||||
ServerGroupAssignment assignment{};
|
||||
|
||||
int index{0};
|
||||
try {
|
||||
assert(names[index] == "cldbid");
|
||||
assignment.client_database_id = std::stoull(values[index++]);
|
||||
|
||||
assert(names[index] == "client_unique_id");
|
||||
assignment.client_unique_id = values[index++];
|
||||
|
||||
assert(names[index] == "client_nickname");
|
||||
assignment.client_display_name = values[index++];
|
||||
|
||||
assert(index == length);
|
||||
} catch (std::exception& ex) {
|
||||
logError(this->server_id(), "Failed to parse client server group assignment at index {}: {}",
|
||||
index - 1,
|
||||
ex.what()
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
result.push_back(std::move(assignment));
|
||||
}));
|
||||
} 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(ServerGroupAssignment{
|
||||
.client_database_id = cldbid,
|
||||
.group_id = group_id,
|
||||
});
|
||||
return 0;
|
||||
});
|
||||
LOG_SQL_CMD(res);
|
||||
}
|
||||
}
|
||||
|
||||
if(auto parent = this->manager_->parent_manager(); parent) {
|
||||
auto parent_clients = parent->assignments().server_group_clients(group_id);
|
||||
auto parent_clients = parent->assignments().server_group_clients(group_id, full_info);
|
||||
result.insert(result.begin(), parent_clients.begin(), parent_clients.end());
|
||||
}
|
||||
|
||||
|
@ -407,60 +486,62 @@ std::deque<std::tuple<ts::GroupId, ts::ChannelId, ts::ClientDbId>> GroupAssignme
|
|||
return result;
|
||||
}
|
||||
|
||||
GroupAssignmentResult GroupAssignmentManager::add_server_group(ClientDbId client, GroupId group) {
|
||||
bool cache_verified{false};
|
||||
GroupAssignmentResult GroupAssignmentManager::add_server_group(ClientDbId client, GroupId group, bool temporary) {
|
||||
bool cache_registered{false};
|
||||
{
|
||||
std::lock_guard cache_lock{this->client_cache_lock};
|
||||
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;
|
||||
auto it = std::find_if(entry->server_group_assignments.begin(), entry->server_group_assignments.end(), [&](const std::unique_ptr<InternalServerGroupAssignment>& 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;
|
||||
|
||||
entry->server_group_assignments.push_back(std::make_unique<InternalServerGroupAssignment>(group, temporary));
|
||||
cache_registered = true;
|
||||
break;
|
||||
}
|
||||
|
||||
if(!cache_verified && kCacheAllClients) {
|
||||
if(!cache_registered && kCacheAllClients) {
|
||||
/* add the client to the cache */
|
||||
auto cache = std::make_unique<ClientCache>();
|
||||
auto cache = std::make_shared<ClientCache>();
|
||||
cache->client_database_id = client;
|
||||
cache->server_group_assignments.emplace_back(group);
|
||||
cache->server_group_assignments.push_back(std::make_unique<InternalServerGroupAssignment>(group, temporary));
|
||||
this->client_cache.push_back(std::move(cache));
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
|
||||
if(!temporary) {
|
||||
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)
|
||||
if(cache_registered) {
|
||||
command.executeLater().waitAndGetLater(LOG_SQL_CMD, {-1, "failed to insert group assignment into the database"});
|
||||
else {
|
||||
} 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};
|
||||
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;
|
||||
auto it = std::find_if(entry->server_group_assignments.begin(), entry->server_group_assignments.end(), [&](const std::unique_ptr<InternalServerGroupAssignment>& assignment) {
|
||||
return assignment->group_id == group;
|
||||
});
|
||||
if(it == entry->server_group_assignments.end()) {
|
||||
return GroupAssignmentResult::REMOVE_NOT_MEMBER_OF_GROUP;
|
||||
|
@ -495,24 +576,24 @@ GroupAssignmentResult GroupAssignmentManager::remove_server_group(ClientDbId cli
|
|||
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};
|
||||
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;
|
||||
auto it = std::find_if(entry->channel_group_assignments.begin(), entry->channel_group_assignments.end(), [&](const std::unique_ptr<InternalChannelGroupAssignment>& assignment) {
|
||||
return assignment->channel_id == channel_id;
|
||||
});
|
||||
if(it != entry->channel_group_assignments.end()) {
|
||||
if(group) {
|
||||
if(it->group_id == group)
|
||||
if((*it)->group_id == group)
|
||||
return GroupAssignmentResult::SET_ALREADY_MEMBER_OF_GROUP;
|
||||
it->group_id = 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);
|
||||
entry->channel_group_assignments.emplace_back(std::make_unique<InternalChannelGroupAssignment>(channel_id, group, temporary));
|
||||
}
|
||||
}
|
||||
cache_verified = true;
|
||||
|
@ -522,9 +603,9 @@ GroupAssignmentResult GroupAssignmentManager::set_channel_group(ClientDbId clien
|
|||
if(!cache_verified && kCacheAllClients) {
|
||||
if(group) {
|
||||
/* add the client to the cache */
|
||||
auto cache = std::make_unique<ClientCache>();
|
||||
auto cache = std::make_shared<ClientCache>();
|
||||
cache->client_database_id = client;
|
||||
cache->channel_group_assignments.emplace_back(channel_id, group, temporary);
|
||||
cache->channel_group_assignments.emplace_back(std::make_unique<InternalChannelGroupAssignment>(channel_id, group, temporary));
|
||||
this->client_cache.push_back(std::move(cache));
|
||||
} else {
|
||||
return GroupAssignmentResult::SUCCESS;
|
||||
|
@ -554,47 +635,137 @@ GroupAssignmentResult GroupAssignmentManager::set_channel_group(ClientDbId clien
|
|||
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};
|
||||
void GroupAssignmentManager::cleanup_temporary_channel_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;
|
||||
auto assignment = std::find_if(client->channel_group_assignments.begin(), client->channel_group_assignments.end(), [&](const std::unique_ptr<InternalChannelGroupAssignment>& assignment) {
|
||||
return assignment->channel_id == channel;
|
||||
});
|
||||
|
||||
if(assignment == client->channel_group_assignments.end()) {
|
||||
break;
|
||||
}
|
||||
|
||||
if(assignment->temporary_assignment) {
|
||||
if((*assignment)->temporary_assignment) {
|
||||
client->channel_group_assignments.erase(assignment);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool GroupAssignmentManager::is_server_group_empty(GroupId group_id) {
|
||||
bool result{true};
|
||||
if(kCacheAllClients) {
|
||||
std::lock_guard cache_lock{*this->client_cache_lock};
|
||||
for(auto& entry : this->client_cache) {
|
||||
for(auto& assignment : entry->server_group_assignments) {
|
||||
if(assignment->group_id == group_id) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
auto sql = sql::command{this->sql_manager(), "SELECT COUNT(*) FROM `assignedGroups` WHERE `serverId` = :sid AND `groupId` = :gid", variable{":sid", this->server_id()}, variable{":gid", group_id}};
|
||||
LOG_SQL_CMD(sql.query([&](int, std::string* values, std::string*) {
|
||||
result = std::stoul(values[0]) == 0;
|
||||
}));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
bool GroupAssignmentManager::is_channel_group_empty(GroupId group_id) {
|
||||
bool result{true};
|
||||
if(kCacheAllClients) {
|
||||
std::lock_guard cache_lock{*this->client_cache_lock};
|
||||
for(auto& entry : this->client_cache) {
|
||||
for(auto& assignment : entry->channel_group_assignments) {
|
||||
if(assignment->group_id == group_id) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
auto sql = sql::command{this->sql_manager(), "SELECT COUNT(*) FROM `assignedGroups` WHERE `serverId` = :sid AND `groupId` = :gid", variable{":sid", this->server_id()}, variable{":gid", group_id}};
|
||||
LOG_SQL_CMD(sql.query([&](int, std::string* values, std::string*) {
|
||||
result = std::stoul(values[0]) == 0;
|
||||
}));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
void GroupAssignmentManager::handle_channel_deleted(ChannelId channel_id) {
|
||||
auto sql = sql::command{this->sql_manager(), "DELETE FROM `assignedGroups` WHERE `serverId` = :sid AND `channelId` = :cid", variable{":sid", this->server_id()}, variable{":cid", channel_id}};
|
||||
sql.executeLater().waitAndGetLater(LOG_SQL_CMD, {-1, "failed to delete assignments for deleted channel"});
|
||||
|
||||
std::lock_guard cache_lock{*this->client_cache_lock};
|
||||
for(auto& entry : this->client_cache) {
|
||||
entry->channel_group_assignments.erase(std::remove_if(entry->channel_group_assignments.begin(), entry->channel_group_assignments.end(), [&](const std::unique_ptr<InternalChannelGroupAssignment>& assignment) {
|
||||
return assignment->channel_id == channel_id;
|
||||
}), entry->channel_group_assignments.end());
|
||||
}
|
||||
}
|
||||
|
||||
void GroupAssignmentManager::handle_server_group_deleted(GroupId group_id) {
|
||||
auto sql = sql::command{this->sql_manager(), "DELETE FROM `assignedGroups` WHERE `serverId` = :sid AND `groupId` = :gid", variable{":sid", this->server_id()}, variable{":gid", group_id}};
|
||||
sql.executeLater().waitAndGetLater(LOG_SQL_CMD, {-1, "failed to delete assignments for deleted server group"});
|
||||
|
||||
std::lock_guard cache_lock{*this->client_cache_lock};
|
||||
for(auto& entry : this->client_cache) {
|
||||
entry->server_group_assignments.erase(std::remove_if(entry->server_group_assignments.begin(), entry->server_group_assignments.end(), [&](const std::unique_ptr<InternalServerGroupAssignment>& assignment) {
|
||||
return assignment->group_id == group_id;
|
||||
}), entry->server_group_assignments.end());
|
||||
}
|
||||
}
|
||||
|
||||
void GroupAssignmentManager::handle_channel_group_deleted(GroupId group_id) {
|
||||
auto sql = sql::command{this->sql_manager(), "DELETE FROM `assignedGroups` WHERE `serverId` = :sid AND `groupId` = :gid", variable{":sid", this->server_id()}, variable{":gid", group_id}};
|
||||
sql.executeLater().waitAndGetLater(LOG_SQL_CMD, {-1, "failed to delete assignments for deleted channel group"});
|
||||
|
||||
std::lock_guard cache_lock{*this->client_cache_lock};
|
||||
for(auto& entry : this->client_cache) {
|
||||
entry->channel_group_assignments.erase(std::remove_if(entry->channel_group_assignments.begin(), entry->channel_group_assignments.end(), [&](const std::unique_ptr<InternalChannelGroupAssignment>& assignment) {
|
||||
return assignment->group_id == group_id;
|
||||
}), entry->channel_group_assignments.end());
|
||||
}
|
||||
}
|
||||
|
||||
void GroupAssignmentManager::reset_all() {
|
||||
auto sql = sql::command{this->sql_manager(), "DELETE FROM `assignedGroups` WHERE `serverId` = :sid", variable{":sid", this->server_id()}};
|
||||
sql.executeLater().waitAndGetLater(LOG_SQL_CMD, {-1, "failed to delete all assignments"});
|
||||
}
|
||||
|
||||
std::shared_ptr<TemporaryAssignmentsLock> GroupAssignmentManager::create_tmp_assignment_lock(ClientDbId cldbid) {
|
||||
std::shared_ptr<ClientCache> cache{};
|
||||
|
||||
std::lock_guard cache_lock{*this->client_cache_lock};
|
||||
for(const auto& entry : this->client_cache) {
|
||||
if(entry->client_database_id == cldbid) {
|
||||
cache = entry;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if(!cache) {
|
||||
cache = std::make_shared<ClientCache>();
|
||||
cache->client_database_id = cldbid;
|
||||
this->client_cache.push_back(cache);
|
||||
}
|
||||
|
||||
auto cache_mutex = this->client_cache_lock;
|
||||
std::shared_ptr<char> temp_assignment_lock{new char{}, [cache, cache_mutex](void* buffer) {
|
||||
delete (char*) buffer;
|
||||
|
||||
std::lock_guard cache_lock{*cache_mutex};
|
||||
cache->server_group_assignments.erase(std::remove_if(cache->server_group_assignments.begin(), cache->server_group_assignments.end(), [](const std::unique_ptr<InternalServerGroupAssignment>& assignment){
|
||||
return assignment->temporary_assignment;
|
||||
}), cache->server_group_assignments.end());
|
||||
|
||||
cache->channel_group_assignments.erase(std::remove_if(cache->channel_group_assignments.begin(), cache->channel_group_assignments.end(), [](const std::unique_ptr<InternalChannelGroupAssignment>& assignment){
|
||||
return assignment->temporary_assignment;
|
||||
}), cache->channel_group_assignments.end());
|
||||
}};
|
||||
|
||||
cache->temp_assignment_lock = temp_assignment_lock;
|
||||
return temp_assignment_lock;
|
||||
}
|
|
@ -31,25 +31,25 @@ namespace ts::server {
|
|||
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;
|
||||
ChannelGroupAssignment&operator=(const ChannelGroupAssignment&) = default;
|
||||
|
||||
ClientDbId client_database_id;
|
||||
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;
|
||||
ServerGroupAssignment&operator=(const ServerGroupAssignment&) = default;
|
||||
ClientDbId client_database_id{0};
|
||||
GroupId group_id{0};
|
||||
|
||||
GroupId group_id;
|
||||
/* both fields will only be set on extended info */
|
||||
std::optional<std::string> client_display_name{};
|
||||
std::optional<std::string> client_unique_id{};
|
||||
};
|
||||
|
||||
typedef void TemporaryAssignmentsLock;
|
||||
|
||||
struct InternalChannelGroupAssignment;
|
||||
struct InternalServerGroupAssignment;
|
||||
|
||||
enum struct GroupAssignmentResult {
|
||||
SUCCESS,
|
||||
ADD_ALREADY_MEMBER_OF_GROUP,
|
||||
|
@ -82,43 +82,44 @@ namespace ts::server {
|
|||
/**
|
||||
* Calculate the target channel group for the client.
|
||||
* The parameters `target channel` will contain the channel where the group has been inherited from.
|
||||
* Note: `target channel` will be altered if the resutl is empty.
|
||||
* Note: `target channel` will be altered if the result is empty.
|
||||
* @return The target channel group id
|
||||
*/
|
||||
[[nodiscard]] std::optional<GroupId> calculate_channel_group_of_client(GroupAssignmentCalculateMode /* mode */, ClientDbId /* client database id */, std::shared_ptr<BasicChannel>& /* target channel */);
|
||||
|
||||
[[nodiscard]] std::deque<ClientDbId> server_group_clients(GroupId /* group id */);
|
||||
[[nodiscard]] std::deque<ServerGroupAssignment> server_group_clients(GroupId /* group id */, bool /* full info */);
|
||||
[[nodiscard]] std::deque<std::tuple<GroupId, ChannelId, ClientDbId>> channel_group_list(GroupId /* group id */, ChannelId /* channel id */, ClientDbId /* client database id */);
|
||||
|
||||
[[nodiscard]] bool is_server_group_empty(GroupId /* group id */);
|
||||
[[nodiscard]] bool is_channel_group_empty(GroupId /* group id */);
|
||||
|
||||
/* change methods */
|
||||
GroupAssignmentResult add_server_group(ClientDbId /* client database id */, GroupId /* group id */);
|
||||
GroupAssignmentResult add_server_group(ClientDbId /* client database id */, GroupId /* group id */, bool /* temporary assignment */);
|
||||
GroupAssignmentResult remove_server_group(ClientDbId /* client database id */, GroupId /* group id */);
|
||||
|
||||
GroupAssignmentResult set_channel_group(ClientDbId /* client database id */, GroupId /* group id */, ChannelId /* channel id */, bool /* temporary assignment */);
|
||||
|
||||
void cleanup_assignments();
|
||||
void cleanup_channel_assignments(ChannelId /* channel */);
|
||||
void cleanup_channel_temporary_assignment(ClientDbId /* client database id */, ChannelId /* channel */);
|
||||
[[nodiscard]] std::shared_ptr<TemporaryAssignmentsLock> create_tmp_assignment_lock(ClientDbId /* client database id */);
|
||||
void cleanup_temporary_channel_assignment(ClientDbId /* client database id */, ChannelId /* channel */);
|
||||
|
||||
void handle_channel_deleted(ChannelId /* channel id */);
|
||||
void handle_server_group_deleted(GroupId /* group id */);
|
||||
void handle_channel_group_deleted(GroupId /* group id */);
|
||||
private:
|
||||
GroupManager* manager_;
|
||||
|
||||
struct ClientCache {
|
||||
ClientDbId client_database_id{0};
|
||||
size_t use_count{0};
|
||||
|
||||
std::deque<ChannelGroupAssignment> channel_group_assignments{};
|
||||
std::deque<ServerGroupAssignment> server_group_assignments{};
|
||||
std::deque<std::unique_ptr<InternalChannelGroupAssignment>> channel_group_assignments{};
|
||||
std::deque<std::unique_ptr<InternalServerGroupAssignment>> server_group_assignments{};
|
||||
|
||||
std::weak_ptr<TemporaryAssignmentsLock> temp_assignment_lock{};
|
||||
};
|
||||
|
||||
GroupManager* manager_;
|
||||
|
||||
std::mutex client_cache_lock;
|
||||
std::deque<std::unique_ptr<ClientCache>> client_cache{};
|
||||
std::shared_ptr<std::mutex> client_cache_lock{};
|
||||
std::deque<std::shared_ptr<ClientCache>> client_cache{};
|
||||
|
||||
|
||||
[[nodiscard]] sql::SqlManager* sql_manager();
|
||||
|
|
|
@ -51,11 +51,13 @@ void GroupManager::save_permissions() {
|
|||
auto time_server_groups = std::chrono::duration_cast<std::chrono::milliseconds>(timestamp_1 - timestamp_0).count();
|
||||
auto time_channel_groups = std::chrono::duration_cast<std::chrono::milliseconds>(timestamp_2 - timestamp_1).count();
|
||||
|
||||
debugMessage(this->server_id(), "Saved {}/{} server and {}/{} channel group permissions in {}ms or {}ms",
|
||||
saved_groups_server, total_groups_server,
|
||||
saved_groups_channel, total_groups_channel,
|
||||
time_server_groups, time_channel_groups
|
||||
);
|
||||
if(saved_groups_channel > 0 || saved_groups_channel > 0) {
|
||||
debugMessage(this->server_id(), "Saved {}/{} server and {}/{} channel group permissions in {}ms or {}ms",
|
||||
saved_groups_server, total_groups_server,
|
||||
saved_groups_channel, total_groups_channel,
|
||||
time_server_groups, time_channel_groups
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/* Abstract group manager */
|
||||
|
@ -114,47 +116,60 @@ void AbstractGroupManager::unload_data() {
|
|||
}
|
||||
}
|
||||
|
||||
void AbstractGroupManager::reset_groups(const std::shared_ptr<GroupManager> &template_provider, std::map<GroupId, GroupId> &mapping) {
|
||||
void AbstractGroupManager::reset_groups(std::map<GroupId, GroupId> &mapping) {
|
||||
std::lock_guard manage_lock{this->group_manage_mutex_};
|
||||
this->unload_data();
|
||||
|
||||
/* Delete all old groups */
|
||||
{
|
||||
/* FIXME: Only delete groups with our database target! */
|
||||
LOG_SQL_CMD(sql::command(this->sql_manager(), "DELETE FROM `permissions` WHERE `serverId` = :serverId AND `type` = :type",
|
||||
LOG_SQL_CMD(sql::command(this->sql_manager(), "DELETE FROM `permissions` WHERE `serverId` = :serverId AND `type` = :type AND `id` IN (SELECT `groupId` FROM `groups` WHERE `serverId` = :serverId AND AND `target` = :target)",
|
||||
variable{":serverId", this->server_id()},
|
||||
variable{":type", ts::permission::SQL_PERM_GROUP}).execute());
|
||||
LOG_SQL_CMD(sql::command(this->sql_manager(), "DELETE FROM `assignedGroups` WHERE `serverId` = :serverId",
|
||||
variable{":serverId", this->server_id()}).execute());
|
||||
LOG_SQL_CMD(sql::command(this->sql_manager(), "DELETE FROM `groups` WHERE `serverId` = :serverId",
|
||||
variable{":serverId", this->server_id()}).execute());
|
||||
variable{":type", ts::permission::SQL_PERM_GROUP},
|
||||
variable{":target", (uint8_t) this->database_target_}).execute());
|
||||
|
||||
LOG_SQL_CMD(sql::command(this->sql_manager(), "DELETE FROM `assignedGroups` WHERE `serverId` = :serverId AND `groupId` IN (SELECT `groupId` FROM `groups` WHERE `serverId` = :serverId AND AND `target` = :target)",
|
||||
variable{":serverId", this->server_id()},
|
||||
variable{":target", (uint8_t) this->database_target_}).execute());
|
||||
|
||||
LOG_SQL_CMD(sql::command(this->sql_manager(), "DELETE FROM `groups` WHERE `serverId` = :serverId AND `target` = :target",
|
||||
variable{":serverId", this->server_id()},
|
||||
variable{":target", (uint8_t) this->database_target_}
|
||||
).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);
|
||||
}
|
||||
}
|
||||
|
||||
void AbstractGroupManager::reset_groups(bool db_cleanup) {
|
||||
std::lock_guard manage_lock{this->group_manage_mutex_};
|
||||
this->unload_data();
|
||||
if(this->parent_manager_) {
|
||||
for(const auto& group : this->parent_manager_->groups_) {
|
||||
if(group->group_type() != GroupType::GROUP_TYPE_TEMPLATE) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if(db_cleanup) {
|
||||
/* FIXME: Only delete groups with our database target! */
|
||||
LOG_SQL_CMD(sql::command(this->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->sql_manager(), "DELETE FROM `assignedGroups` WHERE `serverId` = :serverId",
|
||||
variable{":serverId", this->server_id()}).execute());
|
||||
LOG_SQL_CMD(sql::command(this->sql_manager(), "DELETE FROM `groups` WHERE `serverId` = :serverId",
|
||||
variable{":serverId", this->server_id()}).execute());
|
||||
}
|
||||
//GroupCopyResult
|
||||
std::shared_ptr<Group> created_group{};
|
||||
auto result = this->copy_group_(group->group_id(), GroupType::GROUP_TYPE_NORMAL, group->display_name(), created_group);
|
||||
switch(result) {
|
||||
case GroupCopyResult::SUCCESS:
|
||||
break;
|
||||
|
||||
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);
|
||||
case GroupCopyResult::NAME_INVALID:
|
||||
case GroupCopyResult::NAME_ALREADY_IN_USE:
|
||||
case GroupCopyResult::UNKNOWN_TARGET_GROUP:
|
||||
case GroupCopyResult::UNKNOWN_SOURCE_GROUP:
|
||||
case GroupCopyResult::DATABASE_ERROR:
|
||||
logCritical(this->server_id(), "Failed to copy template group {}: {}", group->group_id(), (uint8_t) result);
|
||||
break;
|
||||
}
|
||||
|
||||
logTrace(this->server_id(), "Copied template group {}. New id: {}", group->group_id(), created_group->group_id());
|
||||
mapping[group->group_id()] = created_group->group_id();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
int AbstractGroupManager::insert_group_from_sql(int length, std::string *values, std::string *names) {
|
||||
GroupId group_id{0};
|
||||
GroupType group_type{GroupType::GROUP_TYPE_UNKNOWN};
|
||||
|
@ -169,7 +184,7 @@ int AbstractGroupManager::insert_group_from_sql(int length, std::string *values,
|
|||
} else if(names[index] == "type") {
|
||||
group_type = (GroupType) std::stoull(values[index]);
|
||||
} else if(names[index] == "displayName") {
|
||||
group_name = names[index];
|
||||
group_name = values[index];
|
||||
}
|
||||
} catch(std::exception& ex) {
|
||||
logWarning(this->server_id(), "Failed to parse group from database. Failed to parse column {} (value: {})", names[index], values[index]);
|
||||
|
@ -190,20 +205,24 @@ int AbstractGroupManager::insert_group_from_sql(int length, std::string *values,
|
|||
return 0;
|
||||
}
|
||||
|
||||
void AbstractGroupManager::save_permissions() {
|
||||
std::unique_lock group_lock{this->groups_};
|
||||
void AbstractGroupManager::save_permissions(size_t &total_groups, size_t &saved_groups) {
|
||||
std::unique_lock group_lock{this->group_mutex_};
|
||||
auto groups = this->groups_;
|
||||
group_lock.unlock();
|
||||
|
||||
total_groups = groups.size();
|
||||
saved_groups = 0;
|
||||
|
||||
for(auto& group : groups) {
|
||||
auto permissions = group->permissions();
|
||||
if(permissions->require_db_updates()) {
|
||||
serverInstance->databaseHelper()->saveGroupPermissions(0, group->group_id(), (uint8_t) this->database_target_, permissions);
|
||||
serverInstance->databaseHelper()->saveGroupPermissions(this->server_id(), group->group_id(), (uint8_t) this->database_target_, permissions);
|
||||
saved_groups++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::shared_ptr<Group> AbstractGroupManager::find_group_(GroupCalculateMode mode, GroupId group_id) {
|
||||
std::shared_ptr<Group> AbstractGroupManager::find_group_(std::shared_ptr<AbstractGroupManager>& owning_manager, GroupCalculateMode mode, GroupId group_id) {
|
||||
{
|
||||
std::lock_guard glock{this->group_mutex_};
|
||||
auto it = std::find_if(this->groups_.begin(), this->groups_.end(), [&](const std::shared_ptr<Group>& group) {
|
||||
|
@ -211,12 +230,16 @@ std::shared_ptr<Group> AbstractGroupManager::find_group_(GroupCalculateMode mode
|
|||
});
|
||||
|
||||
if(it != this->groups_.end()) {
|
||||
owning_manager = this->shared_from_this();
|
||||
return *it;
|
||||
}
|
||||
}
|
||||
|
||||
if(mode == GroupCalculateMode::GLOBAL && this->parent_manager_) {
|
||||
return this->parent_manager_->find_group_(mode, group_id);
|
||||
owning_manager = this->parent_manager_;
|
||||
return this->parent_manager_->find_group_(owning_manager, mode, group_id);
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
|
@ -269,7 +292,8 @@ GroupCreateResult AbstractGroupManager::create_group_(GroupType type, const std:
|
|||
|
||||
auto group_id = res.last_insert_rowid();
|
||||
auto permissions = serverInstance->databaseHelper()->loadGroupPermissions(this->server_id(), group_id, (uint8_t) this->database_target_);
|
||||
auto group = std::make_shared<ServerGroup>(this->server_id(), group_id, type, name, permissions);
|
||||
auto group = this->allocate_group(group_id, type, name, permissions);
|
||||
|
||||
{
|
||||
std::lock_guard glock{this->group_mutex_};
|
||||
this->groups_.push_back(group);
|
||||
|
@ -291,6 +315,10 @@ GroupCopyResult AbstractGroupManager::copy_group_(GroupId source, GroupType targ
|
|||
|
||||
case GroupCreateResult::NAME_ALREADY_IN_USED:
|
||||
return GroupCopyResult::NAME_ALREADY_IN_USE;
|
||||
|
||||
case GroupCreateResult::NAME_TOO_LONG:
|
||||
case GroupCreateResult::NAME_TOO_SHORT:
|
||||
return GroupCopyResult::NAME_INVALID;
|
||||
}
|
||||
|
||||
assert(result);
|
||||
|
@ -300,35 +328,49 @@ GroupCopyResult AbstractGroupManager::copy_group_(GroupId source, GroupType targ
|
|||
GroupCopyResult AbstractGroupManager::copy_group_permissions_(GroupId source, GroupId target) {
|
||||
std::lock_guard manage_lock{this->group_manage_mutex_};
|
||||
|
||||
auto source_group = this->find_group_(groups::GroupCalculateMode::GLOBAL, source);
|
||||
std::shared_ptr<AbstractGroupManager> owning_manager{};
|
||||
auto source_group = this->find_group_(owning_manager, groups::GroupCalculateMode::GLOBAL, source);
|
||||
if(!source_group) {
|
||||
return GroupCopyResult::UNKNOWN_SOURCE_GROUP;
|
||||
}
|
||||
|
||||
auto target_group = this->find_group_(groups::GroupCalculateMode::GLOBAL, target);
|
||||
auto target_group = this->find_group_(owning_manager, 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;
|
||||
if(target_group->permissions()->require_db_updates()) {
|
||||
/* TODO: Somehow flush all pending changes */
|
||||
}
|
||||
|
||||
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();
|
||||
{
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
constexpr static auto kSqlCommand = "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";
|
||||
auto res = sql::command(this->sql_manager(), kSqlCommand,
|
||||
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();
|
||||
if(!res) {
|
||||
|
||||
LOG_SQL_CMD(res);
|
||||
return GroupCopyResult::DATABASE_ERROR;
|
||||
}
|
||||
}
|
||||
|
||||
target_group->set_permissions(serverInstance->databaseHelper()->loadGroupPermissions(this->server_id(), target, (uint8_t) this->database_target_));
|
||||
LOG_SQL_CMD(res);
|
||||
|
||||
return GroupCopyResult::SUCCESS;
|
||||
}
|
||||
|
||||
|
@ -338,7 +380,8 @@ GroupRenameResult AbstractGroupManager::rename_group_(GroupId group_id, const st
|
|||
return GroupRenameResult::NAME_INVALID;
|
||||
}
|
||||
|
||||
auto group = this->find_group_(groups::GroupCalculateMode::GLOBAL, group_id);
|
||||
std::shared_ptr<AbstractGroupManager> owning_manager{};
|
||||
auto group = this->find_group_(owning_manager, groups::GroupCalculateMode::GLOBAL, group_id);
|
||||
if(!group) {
|
||||
return GroupRenameResult::INVALID_GROUP_ID;
|
||||
}
|
||||
|
@ -360,7 +403,7 @@ GroupDeleteResult AbstractGroupManager::delete_group_(GroupId group_id) {
|
|||
|
||||
{
|
||||
std::unique_lock glock{this->group_mutex_};
|
||||
auto it = std::find_if(this->groups_.begin(), this->groups_.begin(), [&](const std::shared_ptr<Group>& group) {
|
||||
auto it = std::find_if(this->groups_.begin(), this->groups_.end(), [&](const std::shared_ptr<Group>& group) {
|
||||
return group->group_id() == group_id;
|
||||
});
|
||||
|
||||
|
@ -376,17 +419,13 @@ GroupDeleteResult AbstractGroupManager::delete_group_(GroupId group_id) {
|
|||
this->groups_.erase(it);
|
||||
}
|
||||
|
||||
sql::command(this->sql_manager(), "DELETE FROM WHERE `serverId` = :server AND `groupId` = :group_id AND `target` = :target",
|
||||
sql::command(this->sql_manager(), "DELETE FROM `groups` WHERE `serverId` = :server AND `groupId` = :group_id AND `target` = :target",
|
||||
variable{":server", this->server_id()},
|
||||
variable{":target", (uint8_t) this->database_target_},
|
||||
variable{":group_id", group_id}).executeLater().waitAndGetLater(LOG_SQL_CMD, {-1, "future failed"});
|
||||
return GroupDeleteResult::SUCCESS;
|
||||
}
|
||||
|
||||
void AbstractGroupManager::reset_groups_(const std::shared_ptr<GroupManager> &source, std::map<GroupId, GroupId> &mapping) {
|
||||
|
||||
}
|
||||
|
||||
/* Server group manager */
|
||||
ServerGroupManager::ServerGroupManager(const std::shared_ptr<GroupManager> &handle, std::shared_ptr<ServerGroupManager> parent)
|
||||
: AbstractGroupManager{handle->sql_manager(), DatabaseGroupTarget::SERVER, handle->server_id(), parent}
|
||||
|
@ -401,7 +440,7 @@ std::shared_ptr<Group> ServerGroupManager::allocate_group(GroupId id, GroupType
|
|||
|
||||
/* Channel group manager */
|
||||
ChannelGroupManager::ChannelGroupManager(const std::shared_ptr<GroupManager> &handle, std::shared_ptr<ChannelGroupManager> parent)
|
||||
: AbstractGroupManager{handle->sql_manager(), DatabaseGroupTarget::SERVER, handle->server_id(), parent}
|
||||
: AbstractGroupManager{handle->sql_manager(), DatabaseGroupTarget::CHANNEL, handle->server_id(), parent}
|
||||
{
|
||||
|
||||
}
|
||||
|
|
|
@ -34,6 +34,7 @@ namespace ts::server::groups {
|
|||
UNKNOWN_SOURCE_GROUP,
|
||||
UNKNOWN_TARGET_GROUP,
|
||||
NAME_ALREADY_IN_USE,
|
||||
NAME_INVALID,
|
||||
DATABASE_ERROR
|
||||
};
|
||||
|
||||
|
@ -48,11 +49,13 @@ namespace ts::server::groups {
|
|||
enum struct GroupDeleteResult {
|
||||
SUCCESS,
|
||||
INVALID_GROUP_ID,
|
||||
/* Artificial result, not used by the delete method but needed */
|
||||
GROUP_NOT_EMPTY,
|
||||
DATABASE_ERROR
|
||||
};
|
||||
|
||||
class GroupManager;
|
||||
class AbstractGroupManager {
|
||||
class AbstractGroupManager : public std::enable_shared_from_this<AbstractGroupManager> {
|
||||
friend class Group;
|
||||
friend class GroupAssignmentManager;
|
||||
public:
|
||||
|
@ -77,10 +80,9 @@ namespace ts::server::groups {
|
|||
void save_permissions(size_t& /* total groups */, size_t& /* saved groups */);
|
||||
|
||||
/**
|
||||
* Reset all known groups.
|
||||
* If the template group provider is empty no new groups will be created.
|
||||
* Reset all known groups and copy the template groups from our group parent (if it isn't null)
|
||||
*/
|
||||
void reset_groups(const std::shared_ptr<GroupManager>& /* template group provider */, std::map<GroupId, GroupId>& /* mapping */);
|
||||
void reset_groups(std::map<GroupId, GroupId>& /* mapping */);
|
||||
protected:
|
||||
std::shared_ptr<AbstractGroupManager> parent_manager_;
|
||||
DatabaseGroupTarget database_target_;
|
||||
|
@ -98,7 +100,7 @@ namespace ts::server::groups {
|
|||
[[nodiscard]] sql::SqlManager* sql_manager();
|
||||
[[nodiscard]] ServerId server_id();
|
||||
|
||||
[[nodiscard]] std::shared_ptr<Group> find_group_(GroupCalculateMode /* mode */, GroupId /* group id */);
|
||||
[[nodiscard]] std::shared_ptr<Group> find_group_(std::shared_ptr<AbstractGroupManager>& /* owning manager */, GroupCalculateMode /* mode */, GroupId /* group id */);
|
||||
[[nodiscard]] std::shared_ptr<Group> find_group_by_name_(GroupCalculateMode /* mode */, const std::string& /* group name */);
|
||||
[[nodiscard]] GroupCreateResult create_group_(GroupType type, const std::string& /* group name */, std::shared_ptr<Group>& /* result */);
|
||||
[[nodiscard]] GroupCopyResult copy_group_(GroupId /* group id */, GroupType /* target group type */, const std::string& /* target group name */, std::shared_ptr<Group>& /* result */);
|
||||
|
@ -140,7 +142,25 @@ namespace ts::server::groups {
|
|||
}
|
||||
|
||||
[[nodiscard]] inline std::shared_ptr<ServerGroup> find_group(GroupCalculateMode mode, GroupId group_id) {
|
||||
return this->cast_result(this->find_group_(mode, group_id));
|
||||
std::shared_ptr<AbstractGroupManager> owning_manager{};
|
||||
return this->cast_result(this->find_group_(owning_manager, mode, group_id));
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param owning_manager
|
||||
* @param mode
|
||||
* @param group_id
|
||||
* @return the group if found. `owning_manager` will be set to the owning manager.
|
||||
*/
|
||||
[[nodiscard]] inline std::shared_ptr<ServerGroup> find_group_ext(std::shared_ptr<ServerGroupManager>& owning_manager, GroupCalculateMode mode, GroupId group_id) {
|
||||
std::shared_ptr<AbstractGroupManager> owning_manager_;
|
||||
auto result = this->cast_result(this->find_group_(owning_manager_, mode, group_id));
|
||||
if(owning_manager_) {
|
||||
owning_manager = std::dynamic_pointer_cast<ServerGroupManager>(owning_manager_);
|
||||
assert(owning_manager);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
[[nodiscard]] inline std::shared_ptr<ServerGroup> find_group_by_name(GroupCalculateMode mode, const std::string& group_name) {
|
||||
|
@ -212,7 +232,25 @@ namespace ts::server::groups {
|
|||
}
|
||||
|
||||
[[nodiscard]] inline std::shared_ptr<ChannelGroup> find_group(GroupCalculateMode mode, GroupId group_id) {
|
||||
return this->cast_result(this->find_group_(mode, group_id));
|
||||
std::shared_ptr<AbstractGroupManager> owning_manager{};
|
||||
return this->cast_result(this->find_group_(owning_manager, mode, group_id));
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param owning_manager
|
||||
* @param mode
|
||||
* @param group_id
|
||||
* @return the group if found. `owning_manager` will be set to the owning manager.
|
||||
*/
|
||||
[[nodiscard]] inline std::shared_ptr<ChannelGroup> find_group_ext(std::shared_ptr<ChannelGroupManager>& owning_manager, GroupCalculateMode mode, GroupId group_id) {
|
||||
std::shared_ptr<AbstractGroupManager> owning_manager_;
|
||||
auto result = this->cast_result(this->find_group_(owning_manager_, mode, group_id));
|
||||
if(owning_manager_) {
|
||||
owning_manager = std::dynamic_pointer_cast<ChannelGroupManager>(owning_manager_);
|
||||
assert(owning_manager);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
[[nodiscard]] inline std::shared_ptr<ChannelGroup> find_group_by_name(GroupCalculateMode mode, const std::string& group_name) {
|
||||
|
|
|
@ -96,7 +96,6 @@ std::shared_ptr<server::MusicClient> MusicBotManager::createBot(ClientDbId owner
|
|||
|
||||
|
||||
if(!config::music::enabled) return nullptr;
|
||||
handle->group_manager()->enableCache(musicBot->getClientDatabaseId());
|
||||
handle->registerClient(musicBot);
|
||||
|
||||
{
|
||||
|
@ -147,7 +146,6 @@ void MusicBotManager::deleteBot(std::shared_ptr<server::MusicClient> musicBot) {
|
|||
handle->client_move(musicBot, nullptr, nullptr, "Music bot deleted", ViewReasonId::VREASON_SERVER_LEFT, true, server_channel_lock);
|
||||
handle->unregisterClient(musicBot, "bot deleted", server_channel_lock);
|
||||
}
|
||||
handle->group_manager()->disableCache(musicBot->getClientDatabaseId());
|
||||
serverInstance->databaseHelper()->deleteClient(handle, musicBot->getClientDatabaseId());
|
||||
serverInstance->databaseHelper()->deleteClient(nullptr, musicBot->getClientDatabaseId());
|
||||
|
||||
|
@ -241,7 +239,6 @@ int MusicBotManager::sqlCreateMusicBot(int length, std::string* values, std::str
|
|||
musicBot->properties()[property::CLIENT_LASTCONNECTED] = duration_cast<seconds>(system_clock::now().time_since_epoch()).count();
|
||||
}
|
||||
|
||||
handle->group_manager()->enableCache(musicBot->getClientDatabaseId());
|
||||
if(musicBot->getClientDatabaseId() != botId) logCritical(handle->getServerId(),"Invalid music bot id mapping!");
|
||||
|
||||
{
|
||||
|
|
|
@ -41,11 +41,6 @@ void QueryServer::unregisterConnection(const shared_ptr<QueryClient> &client) {
|
|||
}
|
||||
}
|
||||
|
||||
if(client->server) {
|
||||
client->server->group_manager()->disableCache(client->getClientDatabaseId());
|
||||
} else {
|
||||
serverInstance->group_manager()->disableCache(client->getClientDatabaseId());
|
||||
}
|
||||
/* client->handle = nullptr; */
|
||||
}
|
||||
|
||||
|
|
|
@ -13,6 +13,7 @@
|
|||
#include "../InstanceHandler.h"
|
||||
#include "../ShutdownHelper.h"
|
||||
#include "../server/QueryServer.h"
|
||||
#include "../groups/GroupManager.h"
|
||||
|
||||
#ifdef HAVE_JEMALLOC
|
||||
#include <jemalloc/jemalloc.h>
|
||||
|
@ -293,7 +294,7 @@ namespace terminal::chandler {
|
|||
return false;
|
||||
}
|
||||
|
||||
auto group = server->group_manager()->findGroup(groupId);
|
||||
auto group = server->group_manager()->server_groups()->find_group(groups::GroupCalculateMode::GLOBAL, groupId);
|
||||
if(!group) {
|
||||
handle.response.emplace_back("Could not resolve server group!");
|
||||
return false;
|
||||
|
|
|
@ -8,6 +8,16 @@ general server & channel groups
|
|||
Notified channel & server member add/remove powers
|
||||
Group edit if the notify<server/channel> group triggers
|
||||
|
||||
channelgroupclientlist
|
||||
Icon IDs seem to be buggy with groups
|
||||
group permission copy dosn't work
|
||||
permission reset
|
||||
|
||||
TODO: Delete the group header
|
||||
servergroupclientlist
|
||||
|
||||
server default group edit
|
||||
channelgroupclientlist
|
||||
temporary group/channel assignments
|
||||
Test with `kCacheAllClients` and without `kCacheAllClients`
|
||||
|
||||
TODO:
|
||||
- Delete the group header
|
Loading…
Reference in New Issue