1239 lines
63 KiB
C++
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};
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|