Teaspeak-Server/server/src/client/command_handler/server.cpp

1172 lines
59 KiB
C++

//
// Created by wolverindev on 26.01.20.
//
#include <memory>
#include <spdlog/sinks/rotating_file_sink.h>
#include <iostream>
#include <bitset>
#include <algorithm>
#include <openssl/sha.h>
#include "../../build.h"
#include "../ConnectedClient.h"
#include "../InternalClient.h"
#include "../../server/file/FileServer.h"
#include "../../server/VoiceServer.h"
#include "../voice/VoiceClient.h"
#include "PermissionRegister.h"
#include "../../InstanceHandler.h"
#include "../../server/QueryServer.h"
#include "../file/FileClient.h"
#include "../music/MusicClient.h"
#include "../query/QueryClient.h"
#include "../../weblist/WebListManager.h"
#include "../../manager/ConversationManager.h"
#include "../../manager/PermissionNameMapper.h"
#include <experimental/filesystem>
#include <cstdint>
#include <StringVariable.h>
#include "helpers.h"
#include <Properties.h>
#include <log/LogUtils.h>
#include <misc/sassert.h>
#include <misc/base64.h>
#include <misc/hex.h>
#include <misc/digest.h>
#include <misc/rnd.h>
#include <misc/timer.h>
#include <misc/strobf.h>
#include <misc/scope_guard.h>
#include <bbcode/bbcodes.h>
namespace fs = std::experimental::filesystem;
using namespace std::chrono;
using namespace std;
using namespace ts;
using namespace ts::server;
using namespace ts::server::tokens;
#define QUERY_PASSWORD_LENGTH 12
command_result ConnectedClient::handleCommandServerGetVariables(Command &cmd) {
CMD_REQ_SERVER;
this->notifyServerUpdated(_this.lock());
return command_result{error::ok};
}
#define SERVEREDIT_CHK_PROP_CACHED(name, perm, type)\
else if(key == name) { \
ACTION_REQUIRES_GLOBAL_PERMISSION(perm, 1); \
if(toApplay.count(key) == 0) toApplay[key] = cmd[key].as<std::string>(); \
if(!cmd[0][key].castable<type>()) return command_result{error::parameter_invalid};
#define SERVEREDIT_CHK_PROP2(name, perm, type_a, type_b)\
else if(key == name) { \
ACTION_REQUIRES_GLOBAL_PERMISSION(perm, 1); \
if(toApplay.count(key) == 0) toApplay[key] = cmd[key].as<std::string>(); \
if(!cmd[0][key].castable<type_a>() && !!cmd[0][key].castable<type_b>()) return command_result{error::parameter_invalid};
command_result ConnectedClient::handleCommandServerEdit(Command &cmd) {
CMD_CHK_AND_INC_FLOOD_POINTS(5);
if (cmd[0].has("sid") && this->getServerId() != cmd["sid"].as<ServerId>())
return command_result{error::server_invalid_id};
auto target_server = this->server;
if(cmd[0].has("sid")) {
target_server = serverInstance->getVoiceServerManager()->findServerById(cmd["sid"]);
if(!target_server && cmd["sid"].as<ServerId>() != 0) return command_result{error::server_invalid_id};
}
auto cache = make_shared<CalculateCache>();
map<string, string> toApplay;
for (auto &key : cmd[0].keys()) {
if (key == "sid") continue;
SERVEREDIT_CHK_PROP_CACHED("virtualserver_name", permission::b_virtualserver_modify_name, string) }
SERVEREDIT_CHK_PROP_CACHED("virtualserver_name_phonetic", permission::b_virtualserver_modify_name, string) }
SERVEREDIT_CHK_PROP_CACHED("virtualserver_maxclients", permission::b_virtualserver_modify_maxclients, size_t)
if (cmd["virtualserver_maxclients"].as<size_t>() > 1024)
return command_result{error::accounting_slot_limit_reached, "Do you really need more that 1024 slots?"};
} SERVEREDIT_CHK_PROP_CACHED("virtualserver_reserved_slots", permission::b_virtualserver_modify_reserved_slots, size_t) }
SERVEREDIT_CHK_PROP_CACHED("virtualserver_max_channels", permission::b_virtualserver_modify_maxchannels, size_t)
if(cmd["virtualserver_max_channels"].as<size_t>() > 8192)
return command_result{error::channel_protocol_limit_reached};
}
SERVEREDIT_CHK_PROP_CACHED("virtualserver_icon_id", permission::b_virtualserver_modify_icon_id, int64_t) }
SERVEREDIT_CHK_PROP_CACHED("virtualserver_channel_temp_delete_delay_default", permission::b_virtualserver_modify_channel_temp_delete_delay_default, ChannelId) }
SERVEREDIT_CHK_PROP_CACHED("virtualserver_codec_encryption_mode", permission::b_virtualserver_modify_codec_encryption_mode, int) }
SERVEREDIT_CHK_PROP_CACHED("virtualserver_default_server_group", permission::b_virtualserver_modify_default_servergroup, GroupId)
if(target_server) {
auto group = target_server->groups->findGroup(cmd["virtualserver_default_server_group"].as<GroupId>());
if (!group || group->target() != GROUPTARGET_SERVER) return command_result{error::parameter_invalid};
}
} SERVEREDIT_CHK_PROP_CACHED("virtualserver_default_channel_group", permission::b_virtualserver_modify_default_channelgroup, GroupId)
if(target_server) {
auto group = target_server->groups->findGroup(cmd["virtualserver_default_channel_group"].as<GroupId>());
if (!group || group->target() != GROUPTARGET_CHANNEL) return command_result{error::parameter_invalid};
}
} SERVEREDIT_CHK_PROP_CACHED("virtualserver_default_channel_admin_group", permission::b_virtualserver_modify_default_channeladmingroup, GroupId)
if(target_server) {
auto group = target_server->groups->findGroup(cmd["virtualserver_default_channel_admin_group"].as<GroupId>());
if (!group || group->target() != GROUPTARGET_CHANNEL) return command_result{error::parameter_invalid};
}
} SERVEREDIT_CHK_PROP_CACHED("virtualserver_default_music_group", permission::b_virtualserver_modify_default_musicgroup, GroupId)
if(target_server) {
auto group = target_server->groups->findGroup(cmd["virtualserver_default_music_group"].as<GroupId>());
if (!group || group->target() != GROUPTARGET_SERVER) return command_result{error::parameter_invalid};
}
}
SERVEREDIT_CHK_PROP_CACHED("virtualserver_priority_speaker_dimm_modificator", permission::b_virtualserver_modify_priority_speaker_dimm_modificator, float) }
SERVEREDIT_CHK_PROP_CACHED("virtualserver_port", permission::b_virtualserver_modify_port, uint16_t) }
SERVEREDIT_CHK_PROP_CACHED("virtualserver_host", permission::b_virtualserver_modify_host, string) }
SERVEREDIT_CHK_PROP_CACHED("virtualserver_web_host", permission::b_virtualserver_modify_port, uint16_t) }
SERVEREDIT_CHK_PROP_CACHED("virtualserver_web_port", permission::b_virtualserver_modify_host, string) }
SERVEREDIT_CHK_PROP_CACHED("virtualserver_hostbanner_url", permission::b_virtualserver_modify_hostbanner, string) }
SERVEREDIT_CHK_PROP_CACHED("virtualserver_hostbanner_gfx_url", permission::b_virtualserver_modify_hostbanner, string) }
SERVEREDIT_CHK_PROP_CACHED("virtualserver_hostbanner_gfx_interval", permission::b_virtualserver_modify_hostbanner, int) }
SERVEREDIT_CHK_PROP_CACHED("virtualserver_hostbanner_mode", permission::b_virtualserver_modify_hostbanner, int) }
SERVEREDIT_CHK_PROP_CACHED("virtualserver_hostbutton_tooltip", permission::b_virtualserver_modify_hostbutton, string) }
SERVEREDIT_CHK_PROP_CACHED("virtualserver_hostbutton_url", permission::b_virtualserver_modify_hostbutton, string) }
SERVEREDIT_CHK_PROP_CACHED("virtualserver_hostbutton_gfx_url", permission::b_virtualserver_modify_hostbutton, string) }
SERVEREDIT_CHK_PROP_CACHED("virtualserver_hostmessage", permission::b_virtualserver_modify_hostmessage, string) }
SERVEREDIT_CHK_PROP_CACHED("virtualserver_hostmessage_mode", permission::b_virtualserver_modify_hostmessage, int) }
SERVEREDIT_CHK_PROP_CACHED("virtualserver_welcomemessage", permission::b_virtualserver_modify_welcomemessage, string) }
SERVEREDIT_CHK_PROP_CACHED("virtualserver_weblist_enabled", permission::b_virtualserver_modify_weblist, bool)
if (target_server && target_server->running()) {
if (cmd["virtualserver_weblist_enabled"].as<bool>())
serverInstance->getWebList()->enable_report(target_server);
else
serverInstance->getWebList()->disable_report(target_server);
debugMessage(target_server->getServerId(), "Changed weblist state to -> {}",
cmd["virtualserver_weblist_enabled"].as<bool>() ? "activated" : "disabled");
}
} SERVEREDIT_CHK_PROP_CACHED("virtualserver_needed_identity_security_level", permission::b_virtualserver_modify_needed_identity_security_level, int) }
SERVEREDIT_CHK_PROP_CACHED("virtualserver_antiflood_points_tick_reduce", permission::b_virtualserver_modify_antiflood, uint64_t) }
SERVEREDIT_CHK_PROP_CACHED("virtualserver_antiflood_points_needed_command_block", permission::b_virtualserver_modify_antiflood, uint64_t) }
SERVEREDIT_CHK_PROP_CACHED("virtualserver_antiflood_points_needed_ip_block", permission::b_virtualserver_modify_antiflood, uint64_t) }
SERVEREDIT_CHK_PROP_CACHED("virtualserver_complain_autoban_count", permission::b_virtualserver_modify_complain, uint64_t) }
SERVEREDIT_CHK_PROP_CACHED("virtualserver_complain_autoban_time", permission::b_virtualserver_modify_complain, uint64_t) }
SERVEREDIT_CHK_PROP_CACHED("virtualserver_complain_remove_time", permission::b_virtualserver_modify_complain, uint64_t) }
SERVEREDIT_CHK_PROP_CACHED("virtualserver_autostart", permission::b_virtualserver_modify_autostart, bool) }
SERVEREDIT_CHK_PROP_CACHED("virtualserver_min_clients_in_channel_before_forced_silence", permission::b_virtualserver_modify_channel_forced_silence, int) }
SERVEREDIT_CHK_PROP_CACHED("virtualserver_log_client", permission::b_virtualserver_modify_log_settings, bool) }
SERVEREDIT_CHK_PROP_CACHED("virtualserver_log_query", permission::b_virtualserver_modify_log_settings, bool) }
SERVEREDIT_CHK_PROP_CACHED("virtualserver_log_channel", permission::b_virtualserver_modify_log_settings, bool) }
SERVEREDIT_CHK_PROP_CACHED("virtualserver_log_permissions", permission::b_virtualserver_modify_log_settings, bool) }
SERVEREDIT_CHK_PROP_CACHED("virtualserver_log_server", permission::b_virtualserver_modify_log_settings, bool) }
SERVEREDIT_CHK_PROP_CACHED("virtualserver_log_filetransfer", permission::b_virtualserver_modify_log_settings, bool) }
SERVEREDIT_CHK_PROP_CACHED("virtualserver_min_client_version", permission::b_virtualserver_modify_min_client_version, uint64_t) }
SERVEREDIT_CHK_PROP_CACHED("virtualserver_min_android_version", permission::b_virtualserver_modify_min_client_version, uint64_t) }
SERVEREDIT_CHK_PROP_CACHED("virtualserver_min_ios_version", permission::b_virtualserver_modify_min_client_version, uint64_t) }
SERVEREDIT_CHK_PROP_CACHED("virtualserver_music_bot_limit", permission::b_virtualserver_modify_music_bot_limit, int) }
SERVEREDIT_CHK_PROP_CACHED("virtualserver_flag_password", permission::b_virtualserver_modify_password, bool)
if (cmd["virtualserver_flag_password"].as<bool>() && !cmd[0].has("virtualserver_password"))
return command_result{error::parameter_invalid};
} SERVEREDIT_CHK_PROP_CACHED("virtualserver_password", permission::b_virtualserver_modify_password, string)
if(cmd["virtualserver_password"].string().empty()) {
toApplay["virtualserver_flag_password"] = "0";
} else {
toApplay["virtualserver_flag_password"] = "1";
if(this->getType() == CLIENT_QUERY)
toApplay["virtualserver_password"] = base64::encode(digest::sha1(cmd["virtualserver_password"].string()));
}
}
SERVEREDIT_CHK_PROP_CACHED("virtualserver_default_client_description", permission::b_virtualserver_modify_default_messages, string) }
SERVEREDIT_CHK_PROP_CACHED("virtualserver_default_channel_description", permission::b_virtualserver_modify_default_messages, string) }
SERVEREDIT_CHK_PROP_CACHED("virtualserver_default_channel_topic", permission::b_virtualserver_modify_default_messages, string) }
SERVEREDIT_CHK_PROP_CACHED("virtualserver_country_code", permission::b_virtualserver_modify_country_code, string) }
SERVEREDIT_CHK_PROP2("virtualserver_max_download_total_bandwidth", permission::b_virtualserver_modify_ft_settings, uint64_t, int64_t)
if(cmd["virtualserver_max_download_total_bandwidth"].string().find('-') == string::npos)
toApplay["virtualserver_max_download_total_bandwidth"] = to_string((int64_t) cmd["virtualserver_max_download_total_bandwidth"].as<uint64_t>());
else
toApplay["virtualserver_max_download_total_bandwidth"] = to_string(cmd["virtualserver_max_download_total_bandwidth"].as<int64_t>());
}
SERVEREDIT_CHK_PROP2("virtualserver_max_upload_total_bandwidth", permission::b_virtualserver_modify_ft_settings, uint64_t, int64_t)
if(cmd["virtualserver_max_upload_total_bandwidth"].string().find('-') == string::npos)
toApplay["virtualserver_max_upload_total_bandwidth"] = to_string((int64_t) cmd["virtualserver_max_upload_total_bandwidth"].as<uint64_t>());
else
toApplay["virtualserver_max_upload_total_bandwidth"] = to_string(cmd["virtualserver_max_upload_total_bandwidth"].as<int64_t>());
}
SERVEREDIT_CHK_PROP2("virtualserver_download_quota", permission::b_virtualserver_modify_ft_quotas, uint64_t, int64_t)
if(cmd["virtualserver_download_quota"].string().find('-') == string::npos)
toApplay["virtualserver_download_quota"] = to_string((int64_t) cmd["virtualserver_download_quota"].as<uint64_t>());
else
toApplay["virtualserver_download_quota"] = to_string(cmd["virtualserver_download_quota"].as<int64_t>());
}
SERVEREDIT_CHK_PROP2("virtualserver_upload_quota", permission::b_virtualserver_modify_ft_quotas, uint64_t, int64_t)
if(cmd["virtualserver_upload_quota"].string().find('-') == string::npos)
toApplay["virtualserver_upload_quota"] = to_string((int64_t) cmd["virtualserver_upload_quota"].as<uint64_t>());
else
toApplay["virtualserver_upload_quota"] = to_string(cmd["virtualserver_upload_quota"].as<int64_t>());
}
else {
logError(target_server ? target_server->getServerId() : 0, "Client " + this->getDisplayName() + " tried to change a not existing server properties. (" + key + ")");
//return command_result{error::not_implemented};
}
}
std::deque<std::string> keys;
bool group_update = false;
for (const auto& elm : toApplay) {
auto info = property::impl::info<property::VirtualServerProperties>(elm.first);
if(*info == property::VIRTUALSERVER_UNDEFINED) {
logCritical(target_server ? target_server->getServerId() : 0, "Missing server property " + elm.first);
continue;
}
if(!info->validate_input(elm.second)) {
logError(target_server ? target_server->getServerId() : 0, "Client " + this->getDisplayName() + " tried to change a property to an invalid value. (Value: '" + elm.second + "', Property: '" + info->name + "')");
continue;
}
if(target_server)
target_server->properties()[info] = elm.second;
else
(*serverInstance->getDefaultServerProperties())[info] = elm.second;
keys.push_back(elm.first);
group_update |= *info == property::VIRTUALSERVER_DEFAULT_SERVER_GROUP || *info == property::VIRTUALSERVER_DEFAULT_CHANNEL_GROUP || *info == property::VIRTUALSERVER_DEFAULT_MUSIC_GROUP;
}
if(target_server) {
if (group_update)
target_server->forEachClient([&](const shared_ptr<ConnectedClient>& client) {
if(target_server->notifyClientPropertyUpdates(client, target_server->groups->update_server_group_property(client, true, client->getChannel()))) {
if(client->update_cached_permissions()) /* update cached calculated permissions */
client->sendNeededPermissions(false); /* cached permissions had changed, notify the client */
}
});
if (!keys.empty())
target_server->notifyServerEdited(_this.lock(), keys);
}
return command_result{error::ok};
}
command_result ConnectedClient::handleCommandServerRequestConnectionInfo(Command &) {
CMD_REQ_SERVER;
ACTION_REQUIRES_GLOBAL_PERMISSION(permission::b_virtualserver_connectioninfo_view, 1);
Command notify("notifyserverconnectioninfo");
auto statistics = this->server->getServerStatistics()->statistics();
auto report = this->server->getServerStatistics()->dataReport();
notify[0]["connection_filetransfer_bandwidth_sent"] = report.file_send;
notify[0]["connection_filetransfer_bandwidth_received"] = report.file_recv;
notify[0]["connection_filetransfer_bytes_sent_total"] = (*statistics)[property::CONNECTION_FILETRANSFER_BYTES_SENT_TOTAL].as<string>();
notify[0]["connection_filetransfer_bytes_received_total"] = (*statistics)[property::CONNECTION_FILETRANSFER_BYTES_RECEIVED_TOTAL].as<string>();
notify[0]["connection_filetransfer_bytes_sent_month"] = this->server->properties()[property::VIRTUALSERVER_MONTH_BYTES_DOWNLOADED].as<string>();
notify[0]["connection_filetransfer_bytes_received_month"] = this->server->properties()[property::VIRTUALSERVER_MONTH_BYTES_UPLOADED].as<string>();
notify[0]["connection_packets_sent_total"] = (*statistics)[property::CONNECTION_PACKETS_SENT_TOTAL].as<string>();
notify[0]["connection_bytes_sent_total"] = (*statistics)[property::CONNECTION_BYTES_SENT_TOTAL].as<string>();
notify[0]["connection_packets_received_total"] = (*statistics)[property::CONNECTION_PACKETS_RECEIVED_TOTAL].as<string>();
notify[0]["connection_bytes_received_total"] = (*statistics)[property::CONNECTION_BYTES_RECEIVED_TOTAL].as<string>();
notify[0]["connection_bandwidth_sent_last_second_total"] = report.send_second;
notify[0]["connection_bandwidth_sent_last_minute_total"] = report.send_minute;
notify[0]["connection_bandwidth_received_last_second_total"] = report.recv_second;
notify[0]["connection_bandwidth_received_last_minute_total"] = report.recv_minute;
notify[0]["connection_connected_time"] = this->server->properties()[property::VIRTUALSERVER_UPTIME].as<string>();
notify[0]["connection_packetloss_total"] = this->server->averagePacketLoss();
notify[0]["connection_ping"] = this->server->averagePing();
this->sendCommand(notify);
return command_result{error::ok};
}
command_result ConnectedClient::handleCommandServerGroupList(Command &) {
CMD_RESET_IDLE;
CMD_CHK_AND_INC_FLOOD_POINTS(5);
ACTION_REQUIRES_GLOBAL_PERMISSION(permission::b_virtualserver_servergroup_list, 1);
this->notifyServerGroupList();
this->command_times.servergrouplist = system_clock::now();
return command_result{error::ok};
}
//servergroupadd name=TestGroup type=1
command_result ConnectedClient::handleCommandServerGroupAdd(Command &cmd) {
CMD_RESET_IDLE;
CMD_CHK_AND_INC_FLOOD_POINTS(5);
ACTION_REQUIRES_GLOBAL_PERMISSION(permission::b_virtualserver_servergroup_create, 1);
if(cmd["name"].string().empty()) return command_result{error::parameter_invalid};
if(cmd["type"].as<GroupType>() == GroupType::GROUP_TYPE_QUERY) {
ACTION_REQUIRES_GLOBAL_PERMISSION(permission::b_serverinstance_modify_querygroup, 1);
} else if(cmd["type"].as<GroupType>() == GroupType::GROUP_TYPE_TEMPLATE) {
ACTION_REQUIRES_GLOBAL_PERMISSION(permission::b_serverinstance_modify_templates, 1);
} else if(!this->server) return command_result{error::parameter_invalid, "you cant create normal groups on the template server!"};
auto group_manager = this->server ? this->server->getGroupManager() : serverInstance->getGroupManager().get();
for(const auto& gr : group_manager->availableServerGroups(true))
if(gr->name() == cmd["name"].string() && gr->target() == GroupTarget::GROUPTARGET_SERVER) return command_result{error::parameter_invalid, "Group already exists"};
auto group = group_manager->createGroup(GroupTarget::GROUPTARGET_SERVER, cmd["type"].as<GroupType>(), cmd["name"].string());
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->forEachClient([](shared_ptr<ConnectedClient> cl) {
cl->notifyServerGroupList();
});
} else return command_result{error::vs_critical};
return command_result{error::ok};
}
command_result ConnectedClient::handleCommandServerGroupCopy(Command &cmd) {
CMD_RESET_IDLE;
CMD_CHK_AND_INC_FLOOD_POINTS(5);
auto ref_server = this->server;
ACTION_REQUIRES_GLOBAL_PERMISSION(permission::b_virtualserver_servergroup_create, 1);
auto group_manager = this->server ? this->server->groups : serverInstance->getGroupManager().get();
auto source_group_id = cmd["ssgid"].as<GroupId>();
auto source_group = group_manager->findGroup(source_group_id);
if(!source_group || source_group->target() != GROUPTARGET_SERVER)
return command_result{error::group_invalid_id};
const auto group_type_modificable = [&](GroupType type) {
switch(type) {
case 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 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;
default:
break;
}
return permission::undefined;
};
{
auto result = group_type_modificable(source_group->type());
if(result != permission::undefined)
return command_result{result};
}
auto global_update = false;
if(cmd[0].has("tsgid") && cmd["tsgid"].as<GroupId>() != 0) {
//Copy an existing group
auto target_group = group_manager->findGroup(cmd["tsgid"]);
if(!target_group || target_group->target() != GROUPTARGET_SERVER)
return command_result{error::group_invalid_id};
{
auto result = group_type_modificable(target_group->type());
if(result != permission::undefined)
return command_result{result};
}
if(!target_group->permission_granted(permission::i_server_group_needed_modify_power, this->calculate_permission(permission::i_server_group_modify_power, 0), true))
return command_result{permission::i_server_group_modify_power};
if(!group_manager->copyGroupPermissions(source_group, target_group))
return command_result{error::vs_critical, "failed to copy group permissions"};
global_update = !this->server || !group_manager->isLocalGroup(target_group);
} else {
//Copy a new group
auto target_type = cmd["type"].as<GroupType>();
{
auto result = group_type_modificable(target_type);
if(result != permission::undefined)
return command_result{result};
}
if(!ref_server && target_type == GroupType::GROUP_TYPE_NORMAL)
return command_result{error::parameter_invalid, "You cant create normal groups on the template server!"};
if(!group_manager->findGroup(GroupTarget::GROUPTARGET_SERVER, cmd["name"].string()).empty())
return command_result{error::group_name_inuse, "You cant create normal groups on the template server!"};
auto target_group_id = group_manager->copyGroup(source_group, target_type, cmd["name"], target_type != GroupType::GROUP_TYPE_NORMAL ? 0 : this->getServerId());
if(target_group_id == 0)
return command_result{error::vs_critical, "failed to copy group"};
if(this->getType() == ClientType::CLIENT_QUERY) {
Command notify("");
notify["sgid"] = target_group_id;
this->sendCommand(notify);
}
global_update = !this->server || !group_manager->isLocalGroup(group_manager->findGroup(target_group_id));
}
for(const auto& server : (global_update ? serverInstance->getVoiceServerManager()->serverInstances() : deque<shared_ptr<VirtualServer>>{this->server}))
if(server)
server->forEachClient([](shared_ptr<ConnectedClient> cl) {
cl->notifyServerGroupList();
});
return command_result{error::ok};
}
command_result ConnectedClient::handleCommandServerGroupRename(Command &cmd) {
CMD_RESET_IDLE;
CMD_CHK_AND_INC_FLOOD_POINTS(5);
auto group_manager = this->server ? this->server->getGroupManager() : serverInstance->getGroupManager().get();
auto serverGroup = group_manager->findGroup(cmd["sgid"].as<GroupId>());
if (!serverGroup || serverGroup->target() != GROUPTARGET_SERVER) return command_result{error::group_invalid_id};
ACTION_REQUIRES_GROUP_PERMISSION(serverGroup, permission::i_server_group_needed_modify_power, permission::i_server_group_modify_power, true);
auto type = serverGroup->type();
if(type == GroupType::GROUP_TYPE_QUERY) {
ACTION_REQUIRES_GLOBAL_PERMISSION(permission::b_serverinstance_modify_querygroup, 1);
} else if(type == GroupType::GROUP_TYPE_TEMPLATE) {
ACTION_REQUIRES_GLOBAL_PERMISSION(permission::b_serverinstance_modify_templates, 1);
}
group_manager->renameGroup(serverGroup, cmd["name"].string());
if(this->server)
this->server->forEachClient([](shared_ptr<ConnectedClient> cl) {
cl->notifyServerGroupList();
});
return command_result{error::ok};
}
//servergroupdel sgid=2 force=0
command_result ConnectedClient::handleCommandServerGroupDel(Command &cmd) {
CMD_RESET_IDLE;
CMD_CHK_AND_INC_FLOOD_POINTS(5);
ACTION_REQUIRES_GLOBAL_PERMISSION(permission::b_virtualserver_servergroup_delete, 1);
auto group_manager = this->server ? this->server->getGroupManager() : serverInstance->getGroupManager().get();
auto serverGroup = group_manager->findGroup(cmd["sgid"].as<GroupId>());
if (!serverGroup || serverGroup->target() != GROUPTARGET_SERVER) return command_result{error::group_invalid_id};
if(this->server && this->server->properties()[property::VIRTUALSERVER_DEFAULT_SERVER_GROUP] == serverGroup->groupId())
return command_result{error::parameter_invalid, "Could not delete default server group!"};
if(serverInstance->properties()[property::SERVERINSTANCE_TEMPLATE_SERVERADMIN_GROUP] == serverGroup->groupId())
return command_result{error::parameter_invalid, "Could not delete instance default server admin group!"};
if(serverInstance->properties()[property::SERVERINSTANCE_TEMPLATE_SERVERDEFAULT_GROUP] == serverGroup->groupId())
return command_result{error::parameter_invalid, "Could not delete instance default server group!"};
if(serverInstance->properties()[property::SERVERINSTANCE_GUEST_SERVERQUERY_GROUP] == serverGroup->groupId())
return command_result{error::parameter_invalid, "Could not delete instance default guest server query group!"};
auto type = serverGroup->type();
if(type == GroupType::GROUP_TYPE_QUERY) {
ACTION_REQUIRES_GLOBAL_PERMISSION(permission::b_serverinstance_modify_querygroup, 1);
} else if(type == GroupType::GROUP_TYPE_TEMPLATE) {
ACTION_REQUIRES_GLOBAL_PERMISSION(permission::b_serverinstance_modify_templates, 1);
}
if (!cmd["force"].as<bool>())
if (!group_manager->listGroupMembers(serverGroup, false).empty())
return command_result{error::database_empty_result, "group not empty!"};
if (group_manager->deleteGroup(serverGroup)) {
if(this->server)
this->server->forEachClient([&](shared_ptr<ConnectedClient> cl) {
if(this->server->notifyClientPropertyUpdates(cl, this->server->groups->update_server_group_property(cl, true, cl->getChannel()))) {
if(cl->update_cached_permissions()) /* update cached calculated permissions */
cl->sendNeededPermissions(false); /* cached permissions had changed, notify the client */
}
cl->notifyServerGroupList();
});
}
return command_result{error::ok};
}
//servergroupclientlist sgid=2
//notifyservergroupclientlist sgid=6 cldbid=2 client_nickname=WolverinDEV client_unique_identifier=xxjnc14LmvTk+Lyrm8OOeo4tOqw=
command_result ConnectedClient::handleCommandServerGroupClientList(Command &cmd) {
CMD_RESET_IDLE;
CMD_CHK_AND_INC_FLOOD_POINTS(5);
ACTION_REQUIRES_GLOBAL_PERMISSION(permission::b_virtualserver_servergroup_client_list, 1);
auto server = cmd[0].has("sid") && cmd["sid"] == 0 ? nullptr : this->server;
auto groupManager = server ? this->server->groups : serverInstance->getGroupManager().get();
auto serverGroup = groupManager->findGroup(cmd["sgid"].as<GroupId>());
if (!serverGroup || serverGroup->target() != GROUPTARGET_SERVER) return command_result{error::group_invalid_id};
Command notify(this->getExternalType() == ClientType::CLIENT_TEAMSPEAK ? "notifyservergroupclientlist" : "");
notify["sgid"] = cmd["sgid"].as<GroupId>();
int index = 0;
for (const auto &clientEntry : groupManager->listGroupMembers(serverGroup)) {
notify[index]["cldbid"] = clientEntry->cldbId;
notify[index]["client_nickname"] = clientEntry->displayName;
notify[index]["client_unique_identifier"] = clientEntry->uid;
index++;
}
this->sendCommand(notify);
return command_result{error::ok};
}
command_result ConnectedClient::handleCommandServerGroupAddClient(Command &cmd) {
CMD_RESET_IDLE;
CMD_CHK_AND_INC_FLOOD_POINTS(25);
auto target_server = cmd[0].has("sid") && cmd["sid"] == 0 ? nullptr : this->server;
auto group_manager = target_server ? this->server->groups : serverInstance->getGroupManager().get();
auto target_cldbid = cmd["cldbid"].as<ClientDbId>();
if (!serverInstance->databaseHelper()->validClientDatabaseId(target_server, cmd["cldbid"])) return command_result{error::client_invalid_id, "invalid cldbid"};
auto needed_client_permission = this->server->calculate_permission(permission::i_client_needed_permission_modify_power, target_cldbid, ClientType::CLIENT_TEAMSPEAK, 0);
if(needed_client_permission.has_value) {
if(!permission::v2::permission_granted(needed_client_permission, this->calculate_permission(permission::i_client_permission_modify_power, 0)))
return command_result{permission::i_client_needed_permission_modify_power};
}
vector<std::shared_ptr<Group>> target_groups;
vector<std::shared_ptr<Group>> applied_groups;
target_groups.reserve(cmd.bulkCount());
auto continue_on_error = cmd.hasParm("continueonerror");
{
auto permission_add_power = this->calculate_permission(permission::i_server_group_member_add_power, -1);
auto permission_self_add_power = this->calculate_permission(permission::i_server_group_member_add_power, -1);
for(auto index = 0; index < cmd.bulkCount(); index++) {
auto group_id = cmd[index]["sgid"];
if(!group_id.castable<GroupId>() && continue_on_error)
continue;
auto gid = group_id.as<GroupId>();
auto group = group_manager->findGroup(gid);
if(!group) {
if(continue_on_error)
continue;
return command_result{error::group_invalid_id};
}
if(!target_server && group->target() != GroupTarget::GROUPTARGET_SERVER && group->type() != GroupType::GROUP_TYPE_QUERY)
return command_result{error::parameter_invalid};
if(find(target_groups.begin(), target_groups.end(), group) != target_groups.end()) {
if(continue_on_error)
continue;
return command_result{error::client_is_already_member_of_group};
}
/* permission tests */
if(!group->permission_granted(permission::i_server_group_needed_member_add_power, permission_add_power, true)) {
if(target_cldbid != this->getClientDatabaseId()) {
if(continue_on_error)
continue;
return command_result{permission::i_server_group_member_add_power};
}
if(!group->permission_granted(permission::i_server_group_needed_member_add_power, permission_self_add_power, true)) {
if(continue_on_error)
continue;
return command_result{permission::i_server_group_self_add_power};
}
}
target_groups.push_back(std::move(group));
}
}
applied_groups.reserve(target_groups.size());
if(target_groups.empty()) return command_result{error::ok};
else if(target_groups.size() == 1) {
/* speed up thing, don't try to load any cache */
auto group = target_groups[0];
if(group_manager->hasServerGroupAssigned(target_cldbid, group))
return command_result{error::client_is_already_member_of_group};
group_manager->addServerGroup(target_cldbid, group);
applied_groups.push_back(group);
} else {
group_manager->enableCache(target_cldbid);
scope_exit_callback cache_disable{[group_manager, target_cldbid]{
group_manager->disableCache(target_cldbid);
}};
for(const auto& group : target_groups) {
if(group_manager->hasServerGroupAssigned(target_cldbid, group)) {
if(continue_on_error)
continue;
return command_result{error::client_is_already_member_of_group};
}
group_manager->addServerGroup(target_cldbid, group);
applied_groups.push_back(group);
}
}
for(const auto& _server : target_server ? std::deque<shared_ptr<VirtualServer>>{target_server} : serverInstance->getVoiceServerManager()->serverInstances()) {
for (const auto &targetClient : _server->findClientsByCldbId(target_cldbid)) {
if (_server->notifyClientPropertyUpdates(targetClient, _server->groups->update_server_group_property(targetClient, true, targetClient->getChannel()))) {
for (const auto &client : _server->getClients()) {
if(client->isClientVisible(targetClient, true) || client == targetClient)
for(const auto& group : applied_groups)
client->notifyServerGroupClientAdd(_this.lock(), targetClient, group);
}
if(targetClient->update_cached_permissions()) /* update cached calculated permissions */
targetClient->sendNeededPermissions(false); /* cached permissions had changed, notify the client */
targetClient->updateChannelClientProperties(true, true);
}
}
}
return command_result{error::ok};
}
command_result ConnectedClient::handleCommandServerGroupDelClient(Command &cmd) {
CMD_RESET_IDLE;
CMD_CHK_AND_INC_FLOOD_POINTS(25);
auto target_server = cmd[0].has("sid") && cmd["sid"] == 0 ? nullptr : this->server;
auto group_manager = target_server ? this->server->groups : serverInstance->getGroupManager().get();
auto target_cldbid = cmd["cldbid"].as<ClientDbId>();
if (!serverInstance->databaseHelper()->validClientDatabaseId(target_server, cmd["cldbid"])) return command_result{error::client_invalid_id, "invalid cldbid"};
auto needed_client_permission = this->server->calculate_permission(permission::i_client_needed_permission_modify_power, target_cldbid, ClientType::CLIENT_TEAMSPEAK, 0);
if(needed_client_permission.has_value) {
if(!permission::v2::permission_granted(needed_client_permission, this->calculate_permission(permission::i_client_permission_modify_power, 0)))
return command_result{permission::i_client_needed_permission_modify_power};
}
vector<std::shared_ptr<Group>> target_groups;
vector<std::shared_ptr<Group>> applied_groups;
target_groups.reserve(cmd.bulkCount());
auto continue_on_error = cmd.hasParm("continueonerror");
{
auto permission_remove_power = this->calculate_permission(permission::i_server_group_member_remove_power, -1);
auto permission_self_remove_power = this->calculate_permission(permission::i_server_group_member_remove_power, -1);
for(auto index = 0; index < cmd.bulkCount(); index++) {
auto group_id = cmd[index]["sgid"];
if(!group_id.castable<GroupId>() && continue_on_error)
continue;
auto gid = group_id.as<GroupId>();
auto group = group_manager->findGroup(gid);
if(!group) {
if(continue_on_error)
continue;
return command_result{error::group_invalid_id};
}
if(!target_server && group->target() != GroupTarget::GROUPTARGET_SERVER && group->type() != GroupType::GROUP_TYPE_QUERY)
return command_result{error::group_invalid_id};
if(find(target_groups.begin(), target_groups.end(), group) != target_groups.end()) {
if(continue_on_error)
continue;
return command_result{error::parameter_invalid, "duplicate server group for id " + to_string(gid)};
}
/* permission tests */
if(!group->permission_granted(permission::i_server_group_needed_member_remove_power, permission_remove_power, true)) {
if(target_cldbid != this->getClientDatabaseId()) {
if(continue_on_error)
continue;
return command_result{permission::i_server_group_member_remove_power};
}
if(!group->permission_granted(permission::i_server_group_needed_member_remove_power, permission_self_remove_power, true)) {
if(continue_on_error)
continue;
return command_result{permission::i_server_group_self_remove_power};
}
}
target_groups.push_back(std::move(group));
}
}
applied_groups.reserve(target_groups.size());
if(target_groups.empty()) return command_result{error::ok};
else if(target_groups.size() == 1) {
/* speed up thing, don't try to load any cache */
auto group = target_groups[0];
auto assignment = group_manager->get_group_assignment(target_cldbid, group);
if(!assignment) {
return command_result{error::client_is_not_member_of_group};
}
if(assignment->server != (target_server ? target_server->getServerId() : 0)) {
return command_result{error::group_not_assigned_over_this_server};
}
group_manager->removeServerGroup(target_cldbid, group);
applied_groups.push_back(group);
} else {
group_manager->enableCache(target_cldbid);
scope_exit_callback cache_disable{[group_manager, target_cldbid]{
group_manager->disableCache(target_cldbid);
}};
for(const auto& group : target_groups) {
auto assignment = group_manager->get_group_assignment(target_cldbid, group);
if(!assignment) {
if(continue_on_error)
continue;
return command_result{error::client_is_not_member_of_group};
}
if(assignment->server != (target_server ? target_server->getServerId() : 0)) {
if(continue_on_error)
continue;
return command_result{error::group_not_assigned_over_this_server};
}
applied_groups.push_back(group);
group_manager->removeServerGroup(target_cldbid, group);
}
}
for(const auto& _server : target_server ? std::deque<shared_ptr<VirtualServer>>{target_server} : serverInstance->getVoiceServerManager()->serverInstances()) {
for (const auto &targetClient : _server->findClientsByCldbId(target_cldbid)) {
if (_server->notifyClientPropertyUpdates(targetClient, _server->groups->update_server_group_property(targetClient, true, targetClient->getChannel()))) {
for (const auto &client : _server->getClients()) {
if(client->isClientVisible(targetClient, true) || client == targetClient)
for(const auto& group : applied_groups)
client->notifyServerGroupClientRemove(_this.lock(), targetClient, group);
}
if(targetClient->update_cached_permissions()) /* update cached calculated permissions */
targetClient->sendNeededPermissions(false); /* cached permissions had changed, notify the client */
targetClient->updateChannelClientProperties(true, true);
}
}
}
return command_result{error::ok};
}
command_result ConnectedClient::handleCommandServerGroupPermList(Command &cmd) {
CMD_RESET_IDLE;
CMD_CHK_AND_INC_FLOOD_POINTS(5);
ACTION_REQUIRES_GLOBAL_PERMISSION(permission::b_virtualserver_servergroup_permission_list, 1);
auto serverGroup = (this->server ? this->server->groups : serverInstance->getGroupManager().get())->findGroup(cmd["sgid"].as<GroupId>());
if (!serverGroup) return command_result{error::group_invalid_id};
if(this->getType() == ClientType::CLIENT_TEAMSPEAK && this->command_times.last_notify + this->command_times.notify_timeout < system_clock::now()) {
this->sendTSPermEditorWarning();
}
if (!this->notifyGroupPermList(serverGroup, cmd.hasParm("permsid"))) return command_result{error::database_empty_result};
return command_result{error::ok};
}
command_result ConnectedClient::handleCommandServerGroupAddPerm(Command &cmd) {
CMD_RESET_IDLE;
CMD_CHK_AND_INC_FLOOD_POINTS(5);
auto serverGroup = (this->server ? this->server->groups : serverInstance->getGroupManager().get())->findGroup(cmd["sgid"].as<GroupId>());
if (!serverGroup) return command_result{error::group_invalid_id};
if (serverGroup->target() != GROUPTARGET_SERVER) return command_result{error::parameter_invalid};
ACTION_REQUIRES_GROUP_PERMISSION(serverGroup, permission::i_server_group_needed_modify_power, permission::i_server_group_modify_power, 1);
auto type = serverGroup->type();
if(type == GroupType::GROUP_TYPE_QUERY) {
ACTION_REQUIRES_GLOBAL_PERMISSION(permission::b_serverinstance_modify_querygroup, 1);
} else if(type == GroupType::GROUP_TYPE_TEMPLATE) {
ACTION_REQUIRES_GLOBAL_PERMISSION(permission::b_serverinstance_modify_templates, 1);
}
auto max_value = this->calculate_permission(permission::i_permission_modify_power, 0, true);
if(!max_value.has_value) return command_result{permission::i_permission_modify_power};
auto ignore_granted_values = permission::v2::permission_granted(1, this->calculate_permission(permission::b_permission_modify_power_ignore, 0));
bool conOnError = cmd[0].has("continueonerror");
bool checkTp = false;
bool sgroupUpdate = false;
auto permissions = serverGroup->permissions();
for (int index = 0; index < cmd.bulkCount(); index++) {
PARSE_PERMISSION(cmd);
//permvalue='1' permnegated='0' permskip='0'
auto val = cmd[index]["permvalue"].as<permission::PermissionValue>();
if(permission_require_granted_value(permType) && !permission::v2::permission_granted(val, max_value)) {
if(conOnError) continue;
return command_result{permission::i_permission_modify_power};
}
if(!ignore_granted_values && !permission::v2::permission_granted(1, this->calculate_permission(permType, 0, true))) {
if(conOnError) continue;
return command_result{permission::i_permission_modify_power};
}
if (grant) {
permissions->set_permission(permType, {0, cmd[index]["permvalue"]}, permission::v2::PermissionUpdateType::do_nothing, permission::v2::PermissionUpdateType::set_value);
} else {
permissions->set_permission(
permType,
{cmd[index]["permvalue"], 0},
permission::v2::PermissionUpdateType::set_value,
permission::v2::PermissionUpdateType::do_nothing,
cmd[index]["permskip"].as<bool>() ? 1 : 0,
cmd[index]["permnegated"].as<bool>() ? 1 : 0
);
sgroupUpdate |= permission_is_group_property(permType);
checkTp |= permission_is_client_property(permType);
}
}
if(sgroupUpdate)
serverGroup->apply_properties_from_permissions();
//TODO may update for every server?
if(this->server) {
auto lock = this->_this.lock();
auto server = this->server;
threads::Thread([checkTp, sgroupUpdate, serverGroup, lock, server]() {
if(sgroupUpdate)
server->forEachClient([](shared_ptr<ConnectedClient> cl) {
cl->notifyServerGroupList();
});
server->forEachClient([serverGroup, checkTp](shared_ptr<ConnectedClient> cl) {
if (cl->serverGroupAssigned(serverGroup)) {
if(cl->update_cached_permissions()) /* update cached calculated permissions */
cl->sendNeededPermissions(false); /* cached permissions had changed, notify the client */
if (checkTp)
cl->updateChannelClientProperties(true, true);
cl->join_state_id++; /* join permission may changed, all channels need to be recalculate dif needed */
}
});
}).detach();
}
return command_result{error::ok};
}
command_result ConnectedClient::handleCommandServerGroupDelPerm(Command &cmd) {
CMD_RESET_IDLE;
CMD_CHK_AND_INC_FLOOD_POINTS(5);
auto serverGroup = (this->server ? this->server->groups : serverInstance->getGroupManager().get())->findGroup(cmd["sgid"].as<GroupId>());
if (!serverGroup) return command_result{error::group_invalid_id};
if (serverGroup->target() != GROUPTARGET_SERVER) return command_result{error::parameter_invalid};
ACTION_REQUIRES_GROUP_PERMISSION(serverGroup, permission::i_server_group_needed_modify_power, permission::i_server_group_modify_power, 1);
auto type = serverGroup->type();
if(type == GroupType::GROUP_TYPE_QUERY) {
ACTION_REQUIRES_GLOBAL_PERMISSION(permission::b_serverinstance_modify_querygroup, 1);
} else if(type == GroupType::GROUP_TYPE_TEMPLATE) {
ACTION_REQUIRES_GLOBAL_PERMISSION(permission::b_serverinstance_modify_templates, 1);
}
auto ignore_granted_values = permission::v2::permission_granted(1, this->calculate_permission(permission::b_permission_modify_power_ignore, 0));
bool conOnError = cmd[0].has("continueonerror");
bool checkTp = false;
auto sgroupUpdate = false;
for (int index = 0; index < cmd.bulkCount(); index++) {
PARSE_PERMISSION(cmd);
if(!ignore_granted_values && !permission::v2::permission_granted(0, this->calculate_permission(permType, 0, true))) {
if(conOnError) continue;
return command_result{permission::i_permission_modify_power};
}
if (grant) {
serverGroup->permissions()->set_permission(permType, permission::v2::empty_permission_values, permission::v2::PermissionUpdateType::do_nothing, permission::v2::PermissionUpdateType::delete_value);
} else {
serverGroup->permissions()->set_permission(
permType,
permission::v2::empty_permission_values,
permission::v2::PermissionUpdateType::delete_value,
permission::v2::PermissionUpdateType::do_nothing
);
sgroupUpdate |= permission_is_group_property(permType);
checkTp |= permission_is_client_property(permType);
}
}
if(sgroupUpdate)
serverGroup->apply_properties_from_permissions();
if(this->server) {
auto lock = this->_this.lock();
auto server = this->server;
threads::Thread([checkTp, sgroupUpdate, serverGroup, lock, server]() {
if(sgroupUpdate)
server->forEachClient([](shared_ptr<ConnectedClient> cl) {
cl->notifyServerGroupList();
});
server->forEachClient([serverGroup, checkTp](shared_ptr<ConnectedClient> cl) {
if (cl->serverGroupAssigned(serverGroup)) {
if(cl->update_cached_permissions()) /* update cached calculated permissions */
cl->sendNeededPermissions(false); /* cached permissions had changed, notify the client */
if (checkTp)
cl->updateChannelClientProperties(true, true);
cl->join_state_id++; /* join permission may changed, all channels need to be recalculate dif needed */
}
});
}).detach();
}
return command_result{error::ok};
}
command_result ConnectedClient::handleCommandServerGroupAutoAddPerm(ts::Command& cmd) {
CMD_RESET_IDLE;
CMD_CHK_AND_INC_FLOOD_POINTS(25);
auto ref_server = this->server;
auto group_manager = ref_server ? this->server->groups : &*serverInstance->getGroupManager();
deque<shared_ptr<Group>> groups;
for(const auto& group : group_manager->availableGroups(false)) {
if(group->updateType() == cmd["sgtype"].as<permission::PermissionValue>() && group->target() == GROUPTARGET_SERVER) {
if(group->permission_granted(permission::i_server_group_needed_modify_power, this->calculate_permission(permission::i_server_group_modify_power, 0), true)) {
auto type = group->type();
if(type == GroupType::GROUP_TYPE_QUERY) {
if(!permission::v2::permission_granted(1, this->calculate_permission(permission::b_serverinstance_modify_querygroup, 0)))
continue;
} else if(type == GroupType::GROUP_TYPE_TEMPLATE) {
if(!permission::v2::permission_granted(1, this->calculate_permission(permission::b_serverinstance_modify_templates, 0)))
continue;
}
groups.push_back(group);//sgtype
}
}
}
if(groups.empty())
return command_result{error::ok};
auto max_value = this->calculate_permission(permission::i_permission_modify_power, 0, true);
if(!max_value.has_value) return command_result{permission::i_permission_modify_power};
auto ignore_granted_values = permission::v2::permission_granted(1, this->calculate_permission(permission::b_permission_modify_power_ignore, 0));
bool conOnError = cmd[0].has("continueonerror");
bool checkTp = false;
bool sgroupUpdate = false;
for (int index = 0; index < cmd.bulkCount(); index++) {
PARSE_PERMISSION(cmd);
//permvalue='1' permnegated='0' permskip='0'end
auto val = cmd[index]["permvalue"].as<permission::PermissionValue>();
if(permission_require_granted_value(permType) && !permission::v2::permission_granted(val, max_value)) {
if(conOnError) continue;
return command_result{permission::i_permission_modify_power};
}
if(!ignore_granted_values && !permission::v2::permission_granted(1, this->calculate_permission(permType, 0, true))) {
if(conOnError) continue;
return command_result{permission::i_permission_modify_power};
}
for(const auto& serverGroup : groups) {
if (grant) {
serverGroup->permissions()->set_permission(permType, {0, cmd[index]["permvalue"]}, permission::v2::PermissionUpdateType::do_nothing, permission::v2::PermissionUpdateType::set_value);
} else {
serverGroup->permissions()->set_permission(
permType,
{cmd[index]["permvalue"], 0},
permission::v2::PermissionUpdateType::set_value,
permission::v2::PermissionUpdateType::do_nothing,
cmd[index]["permskip"].as<bool>() ? 1 : 0,
cmd[index]["permnegated"].as<bool>() ? 1 : 0
);
}
}
sgroupUpdate |= permission_is_group_property(permType);
checkTp |= permission_is_client_property(permType);
}
if(sgroupUpdate)
for(auto& group : groups)
group->apply_properties_from_permissions();
auto lock = this->_this.lock();
if(ref_server) {
threads::Thread([checkTp, sgroupUpdate, groups, lock, ref_server]() {
if(sgroupUpdate)
ref_server->forEachClient([](shared_ptr<ConnectedClient> cl) {
cl->notifyServerGroupList();
});
ref_server->forEachClient([groups, checkTp](shared_ptr<ConnectedClient> cl) {
for(const auto& serverGroup : groups) {
if (cl->serverGroupAssigned(serverGroup)) {
if(cl->update_cached_permissions()) {/* update cached calculated permissions */
cl->sendNeededPermissions(false); /* cached permissions had changed, notify the client */
}
if (checkTp) {
cl->updateChannelClientProperties(true, true);
}
cl->join_state_id++; /* join permission may changed, all channels need to be recalculate if needed */
break;
}
}
});
}).detach();
}
return command_result{error::ok};
}
command_result ConnectedClient::handleCommandServerGroupAutoDelPerm(ts::Command& cmd) {
CMD_RESET_IDLE;
CMD_CHK_AND_INC_FLOOD_POINTS(25);
auto ref_server = this->server;
auto group_manager = ref_server ? this->server->groups : &*serverInstance->getGroupManager();
deque<shared_ptr<Group>> groups;
for(const auto& group : group_manager->availableGroups(false)) {
if(group->updateType() == cmd["sgtype"].as<permission::PermissionValue>() && group->target() == GROUPTARGET_SERVER) {
if(group->permission_granted(permission::i_server_group_needed_modify_power, this->calculate_permission(permission::i_server_group_modify_power, 0), true)) {
auto type = group->type();
if(type == GroupType::GROUP_TYPE_QUERY) {
if(!permission::v2::permission_granted(1, this->calculate_permission(permission::b_serverinstance_modify_querygroup, 0)))
continue;
} else if(type == GroupType::GROUP_TYPE_TEMPLATE) {
if(!permission::v2::permission_granted(1, this->calculate_permission(permission::b_serverinstance_modify_templates, 0)))
continue;
}
groups.push_back(group);//sgtype
}
}
}
if(groups.empty()) return command_result{error::ok};
auto ignore_granted_values = permission::v2::permission_granted(1, this->calculate_permission(permission::b_permission_modify_power_ignore, 0));
bool conOnError = cmd[0].has("continueonerror");
bool checkTp = false;
auto sgroupUpdate = false;
for (int index = 0; index < cmd.bulkCount(); index++) {
PARSE_PERMISSION(cmd);
if(!ignore_granted_values && !permission::v2::permission_granted(0, this->calculate_permission(permType, 0, true))) {
if(conOnError) continue;
return command_result{permission::i_permission_modify_power};
}
for(const auto& serverGroup : groups) {
if (grant) {
serverGroup->permissions()->set_permission(permType, permission::v2::empty_permission_values, permission::v2::PermissionUpdateType::do_nothing, permission::v2::PermissionUpdateType::delete_value);
} else {
serverGroup->permissions()->set_permission(
permType,
permission::v2::empty_permission_values,
permission::v2::PermissionUpdateType::delete_value,
permission::v2::PermissionUpdateType::do_nothing
);
sgroupUpdate |= permission_is_group_property(permType);
}
}
checkTp |= permission_is_client_property(permType);
}
if(sgroupUpdate) {
for(auto& group : groups)
group->apply_properties_from_permissions();
}
if(ref_server) {
auto lock = this->_this.lock();
threads::Thread([checkTp, sgroupUpdate, groups, lock, ref_server]() {
if(sgroupUpdate)
ref_server->forEachClient([](shared_ptr<ConnectedClient> cl) {
cl->notifyServerGroupList();
});
ref_server->forEachClient([groups, checkTp](shared_ptr<ConnectedClient> cl) {
for(const auto& serverGroup : groups) {
if (cl->serverGroupAssigned(serverGroup)) {
if(cl->update_cached_permissions()) /* update cached calculated permissions */
cl->sendNeededPermissions(false); /* cached permissions had changed, notify the client */
if (checkTp)
cl->updateChannelClientProperties(true, true);
cl->join_state_id++; /* join permission may changed, all channels need to be recalculate dif needed */
break;
}
}
});
}).detach();
}
return command_result{error::ok};
}
command_result ConnectedClient::handleCommandServerGroupsByClientId(Command &cmd) {
CMD_RESET_IDLE;
ClientDbId cldbid = cmd["cldbid"];
if(!serverInstance->databaseHelper()->validClientDatabaseId(this->server, cldbid)) return command_result{error::client_invalid_id};
Command result(this->getExternalType() == CLIENT_TEAMSPEAK ? "notifyservergroupsbyclientid" : "");
int index = 0;
if (this->server) {
for (const auto &group : this->server->groups->getAssignedServerGroups(cldbid)) {
result[index]["name"] = group->group->name();
result[index]["sgid"] = group->group->groupId();
result[index]["cldbid"] = cldbid;
index++;
}
} else {
for (const auto &group : serverInstance->getGroupManager()->getAssignedServerGroups(cldbid)) {
result[index]["name"] = group->group->name();
result[index]["sgid"] = group->group->groupId();
result[index]["cldbid"] = cldbid;
index++;
}
}
if (index == 0) return command_result{error::database_empty_result};
this->sendCommand(result);
return command_result{error::ok};
}