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

1239 lines
63 KiB
C++

//
// Created by wolverindev on 26.01.20.
//
#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 "../../weblist/WebListManager.h"
#include "../../manager/ConversationManager.h"
#include "../../manager/PermissionNameMapper.h"
#include "../../manager/ActionLogger.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;
using namespace ts::token;
#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};
}
ServerId serverId = target_server ? target_server->serverId : 0;
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) {
const auto& info = property::find<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: '" + std::string{info.name} + "')");
continue;
}
auto property = target_server ? target_server->properties()[info] : (*serverInstance->getDefaultServerProperties())[info];
if(property.value() == elm.second)
continue;
auto old_value = property.value();
property = elm.second;
serverInstance->action_logger()->server_edit_logger.log_server_edit(serverId, this->ref(), info, old_value, 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);
ts::command_builder result{"notifyserverconnectioninfo"};
auto first_bulk = result.bulk(0);
auto total_stats = this->server->getServerStatistics()->total_stats();
auto minute_report = this->server->getServerStatistics()->minute_stats();
auto second_report = this->server->getServerStatistics()->second_stats();
auto network_report = this->server->generate_network_report();
first_bulk.put_unchecked(property::CONNECTION_FILETRANSFER_BANDWIDTH_SENT, minute_report.file_bytes_sent);
first_bulk.put_unchecked(property::CONNECTION_FILETRANSFER_BANDWIDTH_RECEIVED, minute_report.file_bytes_received);
first_bulk.put_unchecked(property::CONNECTION_FILETRANSFER_BYTES_SENT_TOTAL, this->server->properties()[property::VIRTUALSERVER_TOTAL_BYTES_DOWNLOADED].as<string>());
first_bulk.put_unchecked(property::CONNECTION_FILETRANSFER_BYTES_RECEIVED_TOTAL, this->server->properties()[property::VIRTUALSERVER_TOTAL_BYTES_UPLOADED].as<string>());
first_bulk.put_unchecked("connection_filetransfer_bytes_sent_month", this->server->properties()[property::VIRTUALSERVER_MONTH_BYTES_DOWNLOADED].as<string>());
first_bulk.put_unchecked("connection_filetransfer_bytes_received_month", this->server->properties()[property::VIRTUALSERVER_MONTH_BYTES_UPLOADED].as<string>());
first_bulk.put_unchecked(property::CONNECTION_PACKETS_SENT_TOTAL, std::accumulate(total_stats.connection_packets_sent.begin(), total_stats.connection_packets_sent.end(), (size_t) 0U));
first_bulk.put_unchecked(property::CONNECTION_BYTES_SENT_TOTAL, std::accumulate(total_stats.connection_bytes_sent.begin(), total_stats.connection_bytes_sent.end(), (size_t) 0U));
first_bulk.put_unchecked(property::CONNECTION_PACKETS_RECEIVED_TOTAL, std::accumulate(total_stats.connection_packets_received.begin(), total_stats.connection_packets_received.end(), (size_t) 0U));
first_bulk.put_unchecked(property::CONNECTION_BYTES_RECEIVED_TOTAL, std::accumulate(total_stats.connection_bytes_received.begin(), total_stats.connection_bytes_received.end(), (size_t) 0U));
first_bulk.put_unchecked(property::CONNECTION_BANDWIDTH_SENT_LAST_SECOND_TOTAL, std::accumulate(second_report.connection_bytes_sent.begin(), second_report.connection_bytes_sent.end(), (size_t) 0U));
first_bulk.put_unchecked(property::CONNECTION_BANDWIDTH_SENT_LAST_MINUTE_TOTAL, std::accumulate(minute_report.connection_bytes_sent.begin(), minute_report.connection_bytes_sent.end(), (size_t) 0U));
first_bulk.put_unchecked(property::CONNECTION_BANDWIDTH_RECEIVED_LAST_SECOND_TOTAL, std::accumulate(second_report.connection_bytes_received.begin(), second_report.connection_bytes_received.end(), (size_t) 0U));
first_bulk.put_unchecked(property::CONNECTION_BANDWIDTH_RECEIVED_LAST_MINUTE_TOTAL, std::accumulate(minute_report.connection_bytes_received.begin(), minute_report.connection_bytes_received.end(), (size_t) 0U));
first_bulk.put_unchecked(property::CONNECTION_CONNECTED_TIME, this->server->properties()[property::VIRTUALSERVER_UPTIME].as<string>());
first_bulk.put_unchecked(property::CONNECTION_PACKETLOSS_TOTAL, network_report.average_loss);
first_bulk.put_unchecked(property::CONNECTION_PING, network_report.average_ping);
this->sendCommand(result);
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->getType() != ClientType::CLIENT_QUERY);
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, "name"};
}
log::GroupType log_group_type;
if(!cmd[0].has("type")) {
cmd["type"] = GroupType::GROUP_TYPE_NORMAL;
}
if(cmd["type"].as<GroupType>() == GroupType::GROUP_TYPE_QUERY) {
ACTION_REQUIRES_GLOBAL_PERMISSION(permission::b_serverinstance_modify_querygroup, 1);
log_group_type = log::GroupType::QUERY;
} else if(cmd["type"].as<GroupType>() == GroupType::GROUP_TYPE_TEMPLATE) {
ACTION_REQUIRES_GLOBAL_PERMISSION(permission::b_serverinstance_modify_templates, 1);
log_group_type = log::GroupType::TEMPLATE;
} else {
if(!this->server) {
return command_result{error::parameter_invalid, "you cant create normal groups on the template server!"};
}
log_group_type = log::GroupType::NORMAL;
}
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) {
return command_result{error::vs_critical};
}
group->permissions()->set_permission(permission::b_group_is_permanent, {1,0}, permission::v2::set_value, permission::v2::do_nothing);
{
ts::command_builder notify{this->notify_response_command("notifyservergroupadded")};
notify.put_unchecked(0, "sgid", group->groupId());
this->sendCommand(notify);
}
if(this->server) {
if(this->getType() == ClientType::CLIENT_QUERY) {
this->server->forEachClient([&](const shared_ptr<ConnectedClient>& cl) {
if(cl == this) {
return;
}
cl->notifyServerGroupList();
});
} else {
this->server->forEachClient([&](const shared_ptr<ConnectedClient>& cl) {
cl->notifyServerGroupList();
});
}
}
serverInstance->action_logger()->group_logger.log_group_create(this->getServerId(), this->ref(), log::GroupTarget::SERVER, log_group_type, group->groupId(), group->name(), 0, "");
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;
case GroupType::GROUP_TYPE_NORMAL:
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"};
log::GroupType log_group_type;
switch (target_group->type()) {
case GroupType::GROUP_TYPE_QUERY:
log_group_type = log::GroupType::QUERY;
break;
case GroupType::GROUP_TYPE_TEMPLATE:
log_group_type = log::GroupType::TEMPLATE;
break;
case GroupType::GROUP_TYPE_NORMAL:
log_group_type = log::GroupType::NORMAL;
break;
default:
return command_result{error::parameter_invalid, "type"};
}
serverInstance->action_logger()->group_logger.log_group_permission_copy(target_group->type() != GroupType::GROUP_TYPE_NORMAL ? 0 : this->getServerId(),
this->ref(), log::GroupTarget::SERVER, log_group_type, target_group->groupId(), target_group->name(), source_group->groupId(), source_group->name());
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};
}
log::GroupType log_group_type;
switch (target_type) {
case GroupType::GROUP_TYPE_QUERY:
log_group_type = log::GroupType::QUERY;
break;
case GroupType::GROUP_TYPE_TEMPLATE:
log_group_type = log::GroupType::TEMPLATE;
break;
case GroupType::GROUP_TYPE_NORMAL:
log_group_type = log::GroupType::NORMAL;
break;
default:
return command_result{error::parameter_invalid, "type"};
}
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"};
serverInstance->action_logger()->group_logger.log_group_create(target_type != GroupType::GROUP_TYPE_NORMAL ? 0 : this->getServerId(),
this->ref(), log::GroupTarget::SERVER, log_group_type, target_group_id, cmd["name"].string(), source_group->groupId(), source_group->name());
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();
log::GroupType log_group_type;
if(type == GroupType::GROUP_TYPE_QUERY) {
ACTION_REQUIRES_GLOBAL_PERMISSION(permission::b_serverinstance_modify_querygroup, 1);
log_group_type = log::GroupType::QUERY;
} else if(type == 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 = serverGroup->name();
group_manager->renameGroup(serverGroup, cmd["name"].string());
serverInstance->action_logger()->group_logger.log_group_rename(this->getServerId(), this->ref(), log::GroupTarget::SERVER, log_group_type, serverGroup->groupId(), serverGroup->name(), old_name);
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};
ACTION_REQUIRES_GROUP_PERMISSION(serverGroup, permission::i_server_group_needed_modify_power, permission::i_server_group_modify_power, true);
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();
log::GroupType log_group_type;
if(type == GroupType::GROUP_TYPE_QUERY) {
ACTION_REQUIRES_GLOBAL_PERMISSION(permission::b_serverinstance_modify_querygroup, 1);
log_group_type = log::GroupType::QUERY;
} else if(type == 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;
}
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)) {
serverInstance->action_logger()->group_logger.log_group_delete(this->getServerId(), this->ref(), log::GroupTarget::SERVER, log_group_type, serverGroup->groupId(), serverGroup->name());
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++;
}
if(index == 0 && this->getType() != ClientType::CLIENT_TEAMSPEAK) {
return ts::command_result{error::database_empty_result};
}
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_self_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);
}
}
std::shared_ptr<ConnectedClient> connected_client{};
for(const auto& _server : target_server ? std::deque<shared_ptr<VirtualServer>>{target_server} : serverInstance->getVoiceServerManager()->serverInstances()) {
for (const auto &targetClient : _server->findClientsByCldbId(target_cldbid)) {
connected_client = targetClient;
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);
}
}
}
for(const auto& group : applied_groups) {
serverInstance->action_logger()->group_assignment_logger.log_group_assignment_add(target_server ? target_server->getServerId() : 0,
this->ref(), log::GroupTarget::SERVER,
group->groupId(), group->name(),
target_cldbid, connected_client ? connected_client->getDisplayName() : ""
);
}
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_self_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);
}
}
std::shared_ptr<ConnectedClient> connected_client{};
for(const auto& _server : target_server ? std::deque<shared_ptr<VirtualServer>>{target_server} : serverInstance->getVoiceServerManager()->serverInstances()) {
for (const auto &targetClient : _server->findClientsByCldbId(target_cldbid)) {
connected_client = targetClient;
ConnectedLockedClient clock{targetClient};
if(!clock) continue;
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);
}
}
}
for(const auto& group : applied_groups) {
serverInstance->action_logger()->group_assignment_logger.log_group_assignment_remove(target_server ? target_server->getServerId() : 0,
this->ref(), log::GroupTarget::SERVER,
group->groupId(), group->name(),
target_cldbid, connected_client ? connected_client->getDisplayName() : ""
);
}
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);
/* We don't need this. The modify permissions only apply when creating/editing/renaming the groups itself
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);
}
*/
command::bulk_parser::PermissionBulksParser<true> pparser{cmd};
if(!pparser.validate(this->ref(), 0))
return pparser.build_command_result();
bool update_talk_power{false}, update_server_group_list{false};
auto permissions = serverGroup->permissions();
for(const auto& ppermission : pparser.iterate_valid_permissions()) {
ppermission.apply_to(serverGroup->permissions(), permission::v2::PermissionUpdateType::set_value);
ppermission.log_update(serverInstance->action_logger()->permission_logger,
this->getServerId(),
this->ref(),
log::PermissionTarget::SERVER_GROUP,
permission::v2::PermissionUpdateType::set_value,
serverGroup->groupId(), serverGroup->name(),
0, ""
);
update_server_group_list |= ppermission.is_group_property();
update_talk_power |= ppermission.is_client_view_property();
}
if(update_server_group_list)
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([update_talk_power, update_server_group_list, serverGroup, lock, server]() {
if(update_server_group_list)
server->forEachClient([](shared_ptr<ConnectedClient> cl) {
cl->notifyServerGroupList();
});
server->forEachClient([serverGroup, update_talk_power](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 (update_talk_power)
cl->updateChannelClientProperties(true, true);
cl->join_state_id++; /* join permission may changed, all channels need to be recalculate dif needed */
}
});
}).detach();
}
return pparser.build_command_result();
}
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);
/* We don't need this. The modify permissions only apply when creating/editing/renaming the groups itself
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);
}
*/
command::bulk_parser::PermissionBulksParser<false> pparser{cmd};
if(!pparser.validate(this->ref(), 0))
return pparser.build_command_result();
bool update_talk_power{false}, update_server_group_list{false};
for(const auto& ppermission : pparser.iterate_valid_permissions()) {
ppermission.apply_to(serverGroup->permissions(), permission::v2::PermissionUpdateType::delete_value);
ppermission.log_update(serverInstance->action_logger()->permission_logger,
this->getServerId(),
this->ref(),
log::PermissionTarget::SERVER_GROUP,
permission::v2::PermissionUpdateType::delete_value,
serverGroup->groupId(), serverGroup->name(),
0, ""
);
update_server_group_list |= ppermission.is_group_property();
update_talk_power |= ppermission.is_client_view_property();
}
if(update_server_group_list)
serverGroup->apply_properties_from_permissions();
if(this->server) {
auto lock = this->_this.lock();
auto server = this->server;
threads::Thread([update_talk_power, update_server_group_list, serverGroup, lock, server]() {
if(update_server_group_list)
server->forEachClient([](shared_ptr<ConnectedClient> cl) {
cl->notifyServerGroupList();
});
server->forEachClient([serverGroup, update_talk_power](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 (update_talk_power)
cl->updateChannelClientProperties(true, true);
cl->join_state_id++; /* join permission may changed, all channels need to be recalculate dif needed */
}
});
}).detach();
}
return pparser.build_command_result();
}
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};
command::bulk_parser::PermissionBulksParser<true> pparser{cmd};
if(!pparser.validate(this->ref(), 0))
return pparser.build_command_result();
bool update_clients{false}, update_server_group_list{false};
for(const auto& ppermission : pparser.iterate_valid_permissions()) {
for(const auto& serverGroup : groups) {
ppermission.apply_to(serverGroup->permissions(), permission::v2::PermissionUpdateType::set_value);
ppermission.log_update(serverInstance->action_logger()->permission_logger,
this->getServerId(),
this->ref(),
log::PermissionTarget::SERVER_GROUP,
permission::v2::PermissionUpdateType::set_value,
serverGroup->groupId(), serverGroup->name(),
0, ""
);
}
update_server_group_list |= ppermission.is_group_property();
update_clients |= ppermission.is_client_view_property();
}
if(update_server_group_list)
for(auto& group : groups)
group->apply_properties_from_permissions();
auto lock = this->_this.lock();
if(ref_server) {
threads::Thread([update_clients, update_server_group_list, groups, lock, ref_server]() {
if(update_server_group_list)
ref_server->forEachClient([](shared_ptr<ConnectedClient> cl) {
cl->notifyServerGroupList();
});
ref_server->forEachClient([groups, update_clients](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 (update_clients) {
cl->updateChannelClientProperties(true, true);
}
cl->join_state_id++; /* join permission may changed, all channels need to be recalculate if needed */
break;
}
}
});
}).detach();
}
return pparser.build_command_result();
}
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};
command::bulk_parser::PermissionBulksParser<false> pparser{cmd};
if(!pparser.validate(this->ref(), 0))
return pparser.build_command_result();
bool update_clients{false}, update_server_group_list{false};
for(const auto& ppermission : pparser.iterate_valid_permissions()) {
for(const auto& serverGroup : groups) {
ppermission.apply_to(serverGroup->permissions(), permission::v2::PermissionUpdateType::delete_value);
ppermission.log_update(serverInstance->action_logger()->permission_logger,
this->getServerId(),
this->ref(),
log::PermissionTarget::SERVER_GROUP,
permission::v2::PermissionUpdateType::delete_value,
serverGroup->groupId(), serverGroup->name(),
0, ""
);
}
update_server_group_list |= ppermission.is_group_property();
update_clients |= ppermission.is_client_view_property();
}
if(update_server_group_list) {
for(auto& group : groups)
group->apply_properties_from_permissions();
}
if(ref_server) {
auto lock = this->_this.lock();
threads::Thread([update_clients, update_server_group_list, groups, lock, ref_server]() {
if(update_server_group_list)
ref_server->forEachClient([](shared_ptr<ConnectedClient> cl) {
cl->notifyServerGroupList();
});
ref_server->forEachClient([groups, update_clients](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 (update_clients)
cl->updateChannelClientProperties(true, true);
cl->join_state_id++; /* join permission may changed, all channels need to be recalculate dif needed */
break;
}
}
});
}).detach();
}
return pparser.build_command_result();
}
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};
}