2002 lines
96 KiB
C++
2002 lines
96 KiB
C++
#include <memory>
|
|
|
|
#include <bitset>
|
|
#include <algorithm>
|
|
#include "../../build.h"
|
|
#include "../ConnectedClient.h"
|
|
#include "../InternalClient.h"
|
|
#include "../../server/VoiceServer.h"
|
|
#include "../voice/VoiceClient.h"
|
|
#include "PermissionManager.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 <cstdint>
|
|
|
|
#include "helpers.h"
|
|
#include "./bulk_parsers.h"
|
|
|
|
#include <Properties.h>
|
|
#include <log/LogUtils.h>
|
|
#include <misc/sassert.h>
|
|
#include <misc/base64.h>
|
|
#include <misc/digest.h>
|
|
#include <bbcode/bbcodes.h>
|
|
|
|
using namespace std::chrono;
|
|
using namespace std;
|
|
using namespace ts;
|
|
using namespace ts::server;
|
|
using namespace ts::token;
|
|
|
|
//{findError("parameter_invalid"), "could not resolve permission " + (cmd[index].has("permid") ? cmd[index]["permid"].as<string>() : cmd[index]["permsid"].as<string>())}; \
|
|
//TODO: Log missing permissions?
|
|
|
|
command_result ConnectedClient::handleCommandChannelGetDescription(Command &cmd) {
|
|
CMD_CHK_AND_INC_FLOOD_POINTS(0);
|
|
RESOLVE_CHANNEL_R(cmd["cid"], true);
|
|
auto channel = dynamic_pointer_cast<BasicChannel>(l_channel->entry);
|
|
assert(channel);
|
|
|
|
if(!permission::v2::permission_granted(1, this->calculate_permission(permission::b_channel_ignore_description_view_power, channel->channelId()))) {
|
|
auto view_power = this->calculate_permission(permission::i_channel_description_view_power, channel->channelId());
|
|
if(!channel->permission_granted(permission::i_channel_needed_description_view_power, view_power, false))
|
|
return command_result{permission::i_channel_description_view_power};
|
|
}
|
|
|
|
this->sendChannelDescription(channel, true);
|
|
return command_result{error::ok};
|
|
}
|
|
|
|
command_result ConnectedClient::handleCommandChannelSubscribe(Command &cmd) {
|
|
CMD_REF_SERVER(ref_server);
|
|
CMD_RESET_IDLE;
|
|
|
|
bool flood_points = false;
|
|
deque<shared_ptr<BasicChannel>> channels;
|
|
|
|
{
|
|
shared_lock server_channel_lock(this->server->channel_tree_lock);
|
|
unique_lock client_channel_lock(this->channel_lock);
|
|
|
|
for (int index = 0; index < cmd.bulkCount(); index++) {
|
|
auto local_channel = this->channel_view()->find_channel(cmd[index]["cid"].as<ChannelId>());
|
|
if(!local_channel)
|
|
return command_result{error::channel_invalid_id, "Cant resolve channel"};
|
|
|
|
auto channel = this->server->channelTree->findChannel(cmd[index]["cid"].as<ChannelId>());
|
|
if (!channel)
|
|
return command_result{error::channel_invalid_id, "Cant resolve channel"};
|
|
|
|
channels.push_back(channel);
|
|
if(!flood_points && system_clock::now() - local_channel->view_timestamp > seconds(5)) {
|
|
flood_points = true;
|
|
CMD_CHK_AND_INC_FLOOD_POINTS(15);
|
|
}
|
|
}
|
|
|
|
if(!channels.empty())
|
|
this->subscribeChannel(channels, false, false);
|
|
}
|
|
|
|
return command_result{error::ok};
|
|
}
|
|
|
|
command_result ConnectedClient::handleCommandChannelSubscribeAll(Command &cmd) {
|
|
CMD_REQ_SERVER;
|
|
CMD_CHK_AND_INC_FLOOD_POINTS(20);
|
|
|
|
{
|
|
shared_lock server_channel_lock(this->server->channel_tree_lock);
|
|
unique_lock client_channel_lock(this->channel_lock);
|
|
this->subscribeChannel(this->server->channelTree->channels(), false, false);
|
|
this->subscribeToAll = true;
|
|
}
|
|
return command_result{error::ok};
|
|
}
|
|
|
|
command_result ConnectedClient::handleCommandChannelUnsubscribe(Command &cmd) {
|
|
CMD_REQ_SERVER;
|
|
CMD_CHK_AND_INC_FLOOD_POINTS(5);
|
|
|
|
{
|
|
shared_lock server_channel_lock(this->server->channel_tree_lock);
|
|
unique_lock client_channel_lock(this->channel_lock);
|
|
|
|
deque<shared_ptr<BasicChannel>> channels;
|
|
for(int index = 0; index < cmd.bulkCount(); index++) {
|
|
auto channel = this->server->channelTree->findChannel(cmd["cid"].as<ChannelId>());
|
|
if(!channel) continue;
|
|
channels.push_front(channel);
|
|
}
|
|
|
|
this->unsubscribeChannel(channels, false);
|
|
}
|
|
return command_result{error::ok};
|
|
}
|
|
|
|
command_result ConnectedClient::handleCommandChannelUnsubscribeAll(Command &cmd) {
|
|
CMD_REQ_SERVER;
|
|
CMD_CHK_AND_INC_FLOOD_POINTS(25);
|
|
|
|
{
|
|
shared_lock server_channel_lock(this->server->channel_tree_lock);
|
|
unique_lock client_channel_lock(this->channel_lock);
|
|
this->unsubscribeChannel(this->server->channelTree->channels(), false);
|
|
this->subscribeToAll = false;
|
|
}
|
|
return command_result{error::ok};
|
|
}
|
|
|
|
command_result ConnectedClient::handleCommandChannelGroupAdd(Command &cmd) {
|
|
CMD_RESET_IDLE;
|
|
CMD_CHK_AND_INC_FLOOD_POINTS(5);
|
|
ACTION_REQUIRES_GLOBAL_PERMISSION(permission::b_virtualserver_channelgroup_create, 1);
|
|
|
|
auto group_manager = this->server ? this->server->getGroupManager() : serverInstance->getGroupManager().get();
|
|
|
|
log::GroupType log_group_type;
|
|
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;
|
|
}
|
|
|
|
if(cmd["name"].string().empty())
|
|
return command_result{error::parameter_invalid, "invalid group name"};
|
|
|
|
for(const auto& gr : group_manager->availableServerGroups(true))
|
|
if(gr->name() == cmd["name"].string() && gr->target() == GroupTarget::GROUPTARGET_CHANNEL)
|
|
return command_result{error::parameter_invalid, "Group already exists"};
|
|
|
|
auto group = group_manager->createGroup(GroupTarget::GROUPTARGET_CHANNEL, cmd["type"].as<GroupType>(), cmd["name"].string());
|
|
serverInstance->action_logger()->group_logger.log_group_create(this->getServerId(), this->ref(), log::GroupTarget::CHANNEL, log_group_type, group->groupId(), group->name(), 0, "");
|
|
|
|
{
|
|
ts::command_builder notify{this->notify_response_command("notifychannelgroupadded")};
|
|
notify.put_unchecked(0, "cgid", group->groupId());
|
|
this->sendCommand(notify);
|
|
}
|
|
|
|
if (group) {
|
|
group->permissions()->set_permission(permission::b_group_is_permanent, {1, 0}, permission::v2::set_value, permission::v2::do_nothing);
|
|
if(this->server) {
|
|
if(this->getType() == ClientType::CLIENT_QUERY) {
|
|
this->server->forEachClient([&](const std::shared_ptr<ConnectedClient>& cl) {
|
|
if(cl == this) {
|
|
return;
|
|
}
|
|
|
|
cl->notifyChannelGroupList();
|
|
});
|
|
} else {
|
|
this->server->forEachClient([](const std::shared_ptr<ConnectedClient>& cl) {
|
|
cl->notifyChannelGroupList();
|
|
});
|
|
}
|
|
}
|
|
} else return command_result{error::group_invalid_id};
|
|
return command_result{error::ok};
|
|
}
|
|
|
|
//name=Channel\sAdmin scgid=5 tcgid=4 type=1
|
|
command_result ConnectedClient::handleCommandChannelGroupCopy(Command &cmd) {
|
|
CMD_RESET_IDLE;
|
|
CMD_CHK_AND_INC_FLOOD_POINTS(5);
|
|
ACTION_REQUIRES_GLOBAL_PERMISSION(permission::b_virtualserver_channelgroup_create, 1);
|
|
|
|
auto ref_server = this->server;
|
|
|
|
auto group_manager = this->server ? this->server->groups : serverInstance->getGroupManager().get();
|
|
|
|
auto source_group_id = cmd["scgid"].as<GroupId>();
|
|
auto source_group = group_manager->findGroup(source_group_id);
|
|
|
|
if(!source_group || source_group->target() != GROUPTARGET_CHANNEL)
|
|
return command_result{error::group_invalid_id, "invalid source group"};
|
|
|
|
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("tcgid") && cmd["tcgid"].as<GroupId>() != 0) {
|
|
//Copy an existing group
|
|
auto target_group = group_manager->findGroup(cmd["tcgid"]);
|
|
if(!target_group || target_group->target() != GROUPTARGET_CHANNEL)
|
|
return command_result{error::group_invalid_id, "invalid target group"};
|
|
|
|
{
|
|
auto result = group_type_modificable(target_group->type());
|
|
if(result != permission::undefined)
|
|
return command_result{result};
|
|
}
|
|
|
|
if(!target_group->permission_granted(permission::i_channel_group_needed_modify_power, this->calculate_permission(permission::i_channel_group_modify_power, 0), true))
|
|
return command_result{permission::i_channel_group_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::CHANNEL, 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_CHANNEL, 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::CHANNEL, log_group_type, target_group_id, cmd["name"], source_group->groupId(), source_group->name());
|
|
|
|
if(this->getType() == ClientType::CLIENT_QUERY) {
|
|
Command notify("");
|
|
notify["cgid"] = 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->notifyChannelGroupList();
|
|
});
|
|
return command_result{error::ok};
|
|
}
|
|
|
|
command_result ConnectedClient::handleCommandChannelGroupRename(Command &cmd) {
|
|
CMD_RESET_IDLE;
|
|
CMD_CHK_AND_INC_FLOOD_POINTS(5);
|
|
|
|
auto group_manager = this->server ? this->server->getGroupManager() : serverInstance->getGroupManager().get();
|
|
auto channel_group = group_manager->findGroup(cmd["cgid"].as<GroupId>());
|
|
if (!channel_group || channel_group->target() != GROUPTARGET_CHANNEL)
|
|
return command_result{error::parameter_invalid, "invalid channel group id"};
|
|
ACTION_REQUIRES_GROUP_PERMISSION(channel_group, permission::i_channel_group_needed_modify_power, permission::i_channel_group_modify_power, true);
|
|
|
|
auto type = channel_group->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 = channel_group->name();
|
|
group_manager->renameGroup(channel_group, cmd["name"].string());
|
|
serverInstance->action_logger()->group_logger.log_group_rename(this->getServerId(), this->ref(), log::GroupTarget::CHANNEL, log_group_type, channel_group->groupId(), channel_group->name(), old_name);
|
|
|
|
if(this->server)
|
|
this->server->forEachClient([](shared_ptr<ConnectedClient> cl) {
|
|
cl->notifyChannelGroupList();
|
|
});
|
|
|
|
return command_result{error::ok};
|
|
}
|
|
|
|
command_result ConnectedClient::handleCommandChannelGroupDel(Command &cmd) {
|
|
CMD_RESET_IDLE;
|
|
CMD_CHK_AND_INC_FLOOD_POINTS(5);
|
|
ACTION_REQUIRES_GLOBAL_PERMISSION(permission::b_virtualserver_channelgroup_delete, 1);
|
|
|
|
auto group_manager = this->server ? this->server->getGroupManager() : serverInstance->getGroupManager().get();
|
|
auto channel_group = group_manager->findGroup(cmd["cgid"].as<GroupId>());
|
|
if (!channel_group || channel_group->target() != GROUPTARGET_CHANNEL) return command_result{error::parameter_invalid, "invalid channel group id"};
|
|
|
|
if(this->server) {
|
|
if(this->server->properties()[property::VIRTUALSERVER_DEFAULT_CHANNEL_GROUP] == channel_group->groupId())
|
|
return command_result{error::parameter_invalid, "Could not delete default channel group!"};
|
|
if(this->server->properties()[property::VIRTUALSERVER_DEFAULT_CHANNEL_ADMIN_GROUP] == channel_group->groupId())
|
|
return command_result{error::parameter_invalid, "Could not delete default channel admin group!"};
|
|
}
|
|
if(serverInstance->properties()[property::SERVERINSTANCE_TEMPLATE_CHANNELDEFAULT_GROUP] == channel_group->groupId())
|
|
return command_result{error::parameter_invalid, "Could not delete instance default channel group!"};
|
|
|
|
if(serverInstance->properties()[property::SERVERINSTANCE_TEMPLATE_CHANNELADMIN_GROUP] == channel_group->groupId())
|
|
return command_result{error::parameter_invalid, "Could not delete instance default channel admin group!"};
|
|
|
|
auto type = channel_group->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(channel_group, false).empty())
|
|
return command_result{error::database_empty_result, "group not empty!"};
|
|
|
|
if (group_manager->deleteGroup(channel_group)) {
|
|
serverInstance->action_logger()->group_logger.log_group_delete(this->getServerId(), this->ref(), log::GroupTarget::SERVER, log_group_type, channel_group->groupId(), channel_group->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->notifyChannelGroupList();
|
|
});
|
|
}
|
|
}
|
|
|
|
return command_result{error::ok};
|
|
}
|
|
|
|
command_result ConnectedClient::handleCommandChannelGroupList(Command &) {
|
|
CMD_RESET_IDLE;
|
|
CMD_CHK_AND_INC_FLOOD_POINTS(5);
|
|
ACTION_REQUIRES_GLOBAL_PERMISSION(permission::b_virtualserver_channelgroup_list, 1);
|
|
|
|
this->notifyChannelGroupList(this->getType() != ClientType::CLIENT_QUERY);
|
|
this->command_times.servergrouplist = system_clock::now();
|
|
return command_result{error::ok};
|
|
}
|
|
|
|
command_result ConnectedClient::handleCommandChannelGroupClientList(Command &cmd) {
|
|
CMD_REQ_SERVER;
|
|
CMD_RESET_IDLE;
|
|
CMD_CHK_AND_INC_FLOOD_POINTS(5);
|
|
auto target_channel_id = cmd[0].has("cid") ? cmd["cid"].as<ChannelId>() : 0;
|
|
if(target_channel_id > 0) {
|
|
ACTION_REQUIRES_PERMISSION(permission::b_virtualserver_channelgroup_client_list, 1, target_channel_id);
|
|
} else {
|
|
ACTION_REQUIRES_GLOBAL_PERMISSION(permission::b_virtualserver_channelgroup_client_list, 1);
|
|
}
|
|
Command result(this->getExternalType() == ClientType::CLIENT_TEAMSPEAK ? "notifychannelgroupclientlist" : "");
|
|
|
|
deque<variable> variables{variable{":sid", this->getServerId()}};
|
|
string query = "SELECT `groupId`, `cldbid`, `until`, `channelId` FROM `assignedGroups` WHERE `serverId` = :sid";
|
|
|
|
if(cmd[0].has("cgid") && cmd["cgid"].as<GroupId>() > 0) {
|
|
auto group = this->server->getGroupManager()->findGroup(cmd["cgid"]);
|
|
if(!group || group->target() != GroupTarget::GROUPTARGET_CHANNEL)
|
|
return command_result{error::parameter_invalid, "invalid channel group id"};
|
|
query += " AND `groupId` = :groupId";
|
|
variables.push_back({":groupId", cmd["cgid"].as<GroupId>()});
|
|
} else {
|
|
query += " AND `groupId` IN (SELECT `groupId` FROM `groups` WHERE `serverId` = :sid AND `target` = :target)";
|
|
variables.push_back({":target", GroupTarget::GROUPTARGET_CHANNEL});
|
|
}
|
|
if(cmd[0].has("cldbid") && cmd["cldbid"].as<ClientDbId>() > 0) {
|
|
query += " AND `cldbid` = :cldbid";
|
|
variables.push_back({":cldbid", cmd["cldbid"].as<ClientDbId>()});
|
|
}
|
|
if(cmd[0].has("cid") && cmd["cid"].as<ChannelId>() > 0) {
|
|
auto channel = this->server->getChannelTree()->findChannel(cmd["cid"]);
|
|
if(!channel)
|
|
return command_result{error::parameter_invalid, "invalid channel id"};
|
|
query += " AND `channelId` = :cid";
|
|
variables.push_back({":cid", cmd["cid"].as<ChannelId>()});
|
|
}
|
|
debugMessage(this->getServerId(), "Command channelgroupclientlist sql: {}", query);
|
|
|
|
auto command = sql::command(this->sql, query);
|
|
for(const auto& variable : variables)
|
|
command.value(variable);
|
|
|
|
int index = 0;
|
|
command.query([&](Command& command, int& index, int length, string* values, string* names) {
|
|
GroupId group_id = 0;
|
|
ChannelId channel_id = 0;
|
|
ClientDbId cldbid = 0;
|
|
for(int i = 0; i < length; i++) {
|
|
try {
|
|
if(names[i] == "groupId")
|
|
group_id = stoll(values[i]);
|
|
else if(names[i] == "cldbid")
|
|
cldbid = stoll(values[i]);
|
|
else if(names[i] == "channelId")
|
|
channel_id = stoll(values[i]);
|
|
} catch(std::exception& ex) {
|
|
logError(this->getServerId(), "Failed to parse db field {}", names[i]);
|
|
}
|
|
}
|
|
result[index]["cid"] = channel_id;
|
|
result[index]["cgid"] = group_id;
|
|
result[index]["cldbid"] = cldbid;
|
|
index++;
|
|
}, result, index);
|
|
if (index == 0) return command_result{error::database_empty_result};
|
|
this->sendCommand(result);
|
|
return command_result{error::ok};
|
|
}
|
|
|
|
command_result ConnectedClient::handleCommandChannelGroupPermList(Command &cmd) {
|
|
CMD_CHK_AND_INC_FLOOD_POINTS(5);
|
|
ACTION_REQUIRES_GLOBAL_PERMISSION(permission::b_virtualserver_channelgroup_permission_list, 1);
|
|
|
|
auto channelGroup = (this->server ? this->server->groups : serverInstance->getGroupManager().get())->findGroup(cmd["cgid"].as<GroupId>());
|
|
if (!channelGroup || channelGroup->target() != GROUPTARGET_CHANNEL) return command_result{error::parameter_invalid, "invalid channel group id"};
|
|
if (!this->notifyGroupPermList(channelGroup, cmd.hasParm("permsid"))) return command_result{error::database_empty_result};
|
|
|
|
if(this->getType() == ClientType::CLIENT_TEAMSPEAK && this->command_times.last_notify + this->command_times.notify_timeout < system_clock::now()) {
|
|
this->sendTSPermEditorWarning();
|
|
}
|
|
|
|
return command_result{error::ok};
|
|
}
|
|
|
|
command_result ConnectedClient::handleCommandChannelGroupAddPerm(Command &cmd) {
|
|
CMD_CHK_AND_INC_FLOOD_POINTS(5);
|
|
|
|
auto group_manager = this->server ? this->server->getGroupManager() : serverInstance->getGroupManager().get();
|
|
auto channelGroup = group_manager->findGroup(cmd["cgid"].as<GroupId>());
|
|
if (!channelGroup || channelGroup->target() != GROUPTARGET_CHANNEL) return command_result{error::parameter_invalid, "invalid channel group id"};
|
|
ACTION_REQUIRES_GROUP_PERMISSION(channelGroup, permission::i_channel_group_needed_modify_power, permission::i_channel_group_modify_power, true);
|
|
|
|
command::bulk_parser::PermissionBulksParser<true> pparser{cmd};
|
|
if(!pparser.validate(this->ref(), 0))
|
|
return pparser.build_command_result();
|
|
|
|
bool updateList{false};
|
|
for(const auto& ppermission : pparser.iterate_valid_permissions()) {
|
|
ppermission.apply_to(channelGroup->permissions(), permission::v2::PermissionUpdateType::set_value);
|
|
ppermission.log_update(serverInstance->action_logger()->permission_logger,
|
|
this->getServerId(),
|
|
this->ref(),
|
|
log::PermissionTarget::CHANNEL_GROUP,
|
|
permission::v2::PermissionUpdateType::set_value,
|
|
0, "",
|
|
channelGroup->groupId(), channelGroup->name()
|
|
);
|
|
|
|
updateList |= ppermission.is_group_property();
|
|
}
|
|
|
|
if(updateList)
|
|
channelGroup->apply_properties_from_permissions();
|
|
|
|
if(this->server) {
|
|
if(updateList)
|
|
this->server->forEachClient([](shared_ptr<ConnectedClient> cl) {
|
|
cl->notifyChannelGroupList();
|
|
});
|
|
this->server->forEachClient([channelGroup](shared_ptr<ConnectedClient> cl) {
|
|
unique_lock client_channel_lock(cl->channel_lock); /* while we're updating groups we dont want to change anything! */
|
|
if (cl->channelGroupAssigned(channelGroup, cl->getChannel())) {
|
|
if(cl->update_cached_permissions())
|
|
cl->sendNeededPermissions(false); /* update the needed permissions */
|
|
cl->updateChannelClientProperties(false, true);
|
|
cl->join_state_id++; /* join permission may changed, all channels need to be recalculate dif needed */
|
|
}
|
|
});
|
|
}
|
|
|
|
return pparser.build_command_result();
|
|
}
|
|
|
|
command_result ConnectedClient::handleCommandChannelGroupDelPerm(Command &cmd) {
|
|
CMD_CHK_AND_INC_FLOOD_POINTS(5);
|
|
auto group_manager = this->server ? this->server->getGroupManager() : serverInstance->getGroupManager().get();
|
|
auto channelGroup = group_manager->findGroup(cmd["cgid"].as<GroupId>());
|
|
if (!channelGroup || channelGroup->target() != GROUPTARGET_CHANNEL) return command_result{error::parameter_invalid, "invalid channel group id"};
|
|
ACTION_REQUIRES_GROUP_PERMISSION(channelGroup, permission::i_channel_group_needed_modify_power, permission::i_channel_group_modify_power, true);
|
|
|
|
command::bulk_parser::PermissionBulksParser<false> pparser{cmd};
|
|
if(!pparser.validate(this->ref(), 0))
|
|
return pparser.build_command_result();
|
|
|
|
bool updateList{false};
|
|
for(const auto& ppermission : pparser.iterate_valid_permissions()) {
|
|
ppermission.apply_to(channelGroup->permissions(), permission::v2::PermissionUpdateType::delete_value);
|
|
ppermission.log_update(serverInstance->action_logger()->permission_logger,
|
|
this->getServerId(),
|
|
this->ref(),
|
|
log::PermissionTarget::CHANNEL_GROUP,
|
|
permission::v2::PermissionUpdateType::delete_value,
|
|
0, "",
|
|
channelGroup->groupId(), channelGroup->name()
|
|
);
|
|
updateList |= ppermission.is_group_property();
|
|
}
|
|
|
|
if(updateList)
|
|
channelGroup->apply_properties_from_permissions();
|
|
|
|
if(this->server) {
|
|
if(updateList)
|
|
this->server->forEachClient([](shared_ptr<ConnectedClient> cl) {
|
|
cl->notifyChannelGroupList();
|
|
});
|
|
this->server->forEachClient([channelGroup](shared_ptr<ConnectedClient> cl) {
|
|
unique_lock client_channel_lock(cl->channel_lock); /* while we're updating groups we dont want to change anything! */
|
|
if (cl->channelGroupAssigned(channelGroup, cl->getChannel())) {
|
|
if(cl->update_cached_permissions()) /* update cached calculated permissions */
|
|
cl->sendNeededPermissions(false); /* cached permissions had changed, notify the client */
|
|
cl->updateChannelClientProperties(false, false);
|
|
cl->join_state_id++; /* join permission may changed, all channels need to be recalculate dif needed */
|
|
}
|
|
});
|
|
}
|
|
|
|
return pparser.build_command_result();
|
|
}
|
|
|
|
//TODO: Test if parent or previous is deleted!
|
|
command_result ConnectedClient::handleCommandChannelCreate(Command &cmd) {
|
|
CMD_RESET_IDLE;
|
|
CMD_CHK_AND_INC_FLOOD_POINTS(25);
|
|
CMD_CHK_PARM_COUNT(1);
|
|
|
|
std::shared_ptr<TreeView::LinkedTreeEntry> parent = nullptr;
|
|
std::shared_ptr<BasicChannel> created_channel = nullptr, old_default_channel;
|
|
|
|
auto target_tree = this->server ? this->server->channelTree : serverInstance->getChannelTree().get();
|
|
auto& tree_lock = this->server ? this->server->channel_tree_lock : serverInstance->getChannelTreeLock();
|
|
unique_lock tree_channel_lock(tree_lock);
|
|
|
|
if (cmd[0].has("cpid") && cmd["cpid"].as<ChannelId>() != 0 && cmd["cpid"].as<int>() != -1) {
|
|
parent = target_tree->findLinkedChannel(cmd["cpid"].as<ChannelId>());
|
|
if (!parent) return command_result{error::channel_invalid_id, "Cant resolve parent channel"};
|
|
}
|
|
ChannelId parent_channel_id = parent ? parent->entry->channelId() : 0;
|
|
|
|
#define test_permission(required, permission_type) \
|
|
do {\
|
|
if(!permission::v2::permission_granted(required, this->calculate_permission(permission_type, parent_channel_id, false, permission_cache))) \
|
|
return command_result{permission_type};\
|
|
} while(0)
|
|
|
|
|
|
//TODO: Use for this here the cache as well!
|
|
auto permission_cache = make_shared<CalculateCache>();
|
|
if(parent) test_permission(1, permission::b_channel_create_child);
|
|
if (cmd[0].has("channel_order")) test_permission(1, permission::b_channel_create_with_sortorder);
|
|
if(!cmd[0].has("channel_flag_permanent")) cmd[0]["channel_flag_permanent"] = false;
|
|
if(!cmd[0].has("channel_flag_semi_permanent")) cmd[0]["channel_flag_semi_permanent"] = false;
|
|
if(!cmd[0].has("channel_flag_default")) cmd[0]["channel_flag_default"] = false;
|
|
if(!cmd[0].has("channel_flag_password")) cmd[0]["channel_flag_password"] = false;
|
|
|
|
if (cmd[0]["channel_flag_permanent"].as<bool>()) test_permission(1, permission::b_channel_create_permanent);
|
|
else if (cmd[0]["channel_flag_semi_permanent"].as<bool>()) test_permission(1, permission::b_channel_create_semi_permanent);
|
|
else test_permission(1, permission::b_channel_create_temporary);
|
|
|
|
if (!cmd[0]["channel_flag_permanent"].as<bool>() && !this->server) return command_result{error::parameter_invalid, "You can only create a permanent channel"};
|
|
|
|
if (cmd[0]["channel_flag_default"].as<bool>()) test_permission(1, permission::b_channel_create_with_default);
|
|
if (cmd[0]["channel_flag_password"].as<bool>()) test_permission(1, permission::b_channel_create_with_password);
|
|
else if(permission::v2::permission_granted(1, this->calculate_permission(permission::b_channel_create_modify_with_force_password, parent_channel_id, false, permission_cache)))
|
|
return command_result{permission::b_channel_create_modify_with_force_password};
|
|
|
|
if(cmd[0].has("channel_password") && this->getType() == ClientType::CLIENT_QUERY)
|
|
cmd["channel_password"] = base64::decode(digest::sha1(cmd["channel_password"].string()));
|
|
if (cmd[0].has("channel_description")) test_permission(1, permission::b_channel_create_with_description);
|
|
if (cmd[0].has("channel_maxclients") || (cmd[0].has("channel_flag_maxclients_unlimited") && !cmd["channel_flag_maxclients_unlimited"].as<bool>())) {
|
|
test_permission(1, permission::b_channel_create_with_maxclients);
|
|
if(!cmd[0]["channel_flag_permanent"].as<bool>() && !cmd[0]["channel_flag_semi_permanent"].as<bool>()) {
|
|
cmd["channel_maxclients"] = -1;
|
|
cmd["channel_flag_maxclients_unlimited"] = 1;
|
|
}
|
|
}
|
|
if (cmd[0].has("channel_maxfamilyclients")) test_permission(1, permission::b_channel_create_with_maxfamilyclients);
|
|
if (cmd[0].has("channel_needed_talk_power")) test_permission(1,permission::b_channel_create_with_needed_talk_power);
|
|
if (cmd[0].has("channel_topic")) test_permission(1,permission::b_channel_create_with_topic);
|
|
|
|
if(cmd[0].has("channel_conversation_history_length")) {
|
|
auto value = cmd["channel_conversation_history_length"].as<int64_t>();
|
|
if(value == 0) {
|
|
test_permission(1, permission::b_channel_create_modify_conversation_history_unlimited);
|
|
} else {
|
|
test_permission(1, permission::i_channel_create_modify_conversation_history_length);
|
|
}
|
|
}
|
|
|
|
{
|
|
auto delete_delay = cmd[0].has("channel_delete_delay") ? cmd["channel_delete_delay"].as<permission::PermissionValue>() : 0UL;
|
|
if(delete_delay == 0) {
|
|
if(this->server)
|
|
cmd["channel_delete_delay"] = this->server->properties()[property::VIRTUALSERVER_CHANNEL_TEMP_DELETE_DELAY_DEFAULT].as<uint32_t>();
|
|
else
|
|
cmd["channel_delete_delay"] = 0;
|
|
} else {
|
|
test_permission(cmd["channel_delete_delay"].as<permission::PermissionValue>(), permission::i_channel_create_modify_with_temp_delete_delay);
|
|
}
|
|
}
|
|
#undef test_permission
|
|
|
|
{
|
|
size_t created_total = 0, created_tmp = 0, created_semi = 0, created_perm = 0;
|
|
auto own_cldbid = this->getClientDatabaseId();
|
|
for(const auto& channel : target_tree->channels()) {
|
|
created_total++;
|
|
if(channel->properties()[property::CHANNEL_CREATED_BY] == own_cldbid) {
|
|
if(channel->properties()[property::CHANNEL_FLAG_PERMANENT].as<bool>())
|
|
created_perm++;
|
|
else if(channel->properties()[property::CHANNEL_FLAG_SEMI_PERMANENT].as<bool>())
|
|
created_semi++;
|
|
else
|
|
created_tmp++;
|
|
}
|
|
}
|
|
|
|
if(this->server && created_total >= this->server->properties()[property::VIRTUALSERVER_MAX_CHANNELS].as<uint64_t>())
|
|
return command_result{error::channel_limit_reached};
|
|
|
|
auto max_channels = this->calculate_permission(permission::i_client_max_channels, parent_channel_id, false, permission_cache);
|
|
if(max_channels.has_value) {
|
|
if(!permission::v2::permission_granted(created_perm + created_semi + created_tmp + 1, max_channels))
|
|
return command_result{permission::i_client_max_channels};
|
|
}
|
|
|
|
if (cmd[0]["channel_flag_permanent"].as<bool>()) {
|
|
max_channels = this->calculate_permission(permission::i_client_max_permanent_channels, parent_channel_id, false, permission_cache);
|
|
|
|
if(max_channels.has_value) {
|
|
if(!permission::v2::permission_granted(created_perm + 1, max_channels))
|
|
return command_result{permission::i_client_max_permanent_channels};
|
|
}
|
|
}
|
|
else if (cmd[0]["channel_flag_semi_permanent"].as<bool>()) {
|
|
max_channels = this->calculate_permission(permission::i_client_max_semi_channels, parent_channel_id, false, permission_cache);
|
|
|
|
if(max_channels.has_value) {
|
|
if(!permission::v2::permission_granted(created_semi + 1, max_channels))
|
|
return command_result{permission::i_client_max_semi_channels};
|
|
}
|
|
}
|
|
else {
|
|
max_channels = this->calculate_permission(permission::i_client_max_temporary_channels, parent_channel_id, false, permission_cache);
|
|
|
|
if(max_channels.has_value) {
|
|
if(!permission::v2::permission_granted(created_tmp + 1, max_channels))
|
|
return command_result{permission::i_client_max_temporary_channels};
|
|
}
|
|
}
|
|
}
|
|
|
|
//TODO check voice (opus etc)
|
|
//bool enforce_permanent_parent = cmd[0]["channel_flag_default"].as<bool>(); //TODO check parents here
|
|
{ //Checkout the parent(s)
|
|
{
|
|
|
|
auto min_channel_deep = this->calculate_permission(permission::i_channel_min_depth, parent_channel_id, false, permission_cache);
|
|
auto max_channel_deep = this->calculate_permission(permission::i_channel_max_depth, parent_channel_id, false, permission_cache);
|
|
|
|
if(min_channel_deep.has_value || max_channel_deep.has_value) {
|
|
auto channel_deep = 0;
|
|
auto local_parent = parent;
|
|
while(local_parent) {
|
|
channel_deep++;
|
|
{
|
|
const auto typed_parent = dynamic_pointer_cast<ServerChannel>(local_parent->entry);
|
|
if(typed_parent->deleted) return command_result{error::channel_is_deleted, "One of the parents has been deleted"};
|
|
}
|
|
local_parent = local_parent->parent.lock();
|
|
}
|
|
|
|
if(min_channel_deep.has_value && (channel_deep < min_channel_deep.value && !min_channel_deep.has_infinite_power())) return command_result{permission::i_channel_min_depth};
|
|
if(max_channel_deep.has_value && !permission::v2::permission_granted(channel_deep, max_channel_deep)) return command_result{permission::i_channel_max_depth};
|
|
}
|
|
}
|
|
}
|
|
|
|
if(!cmd[0].has("channel_order")) {
|
|
auto last = parent ? parent->child_head : target_tree->tree_head();
|
|
while(last && last->next)
|
|
last = last->next;
|
|
if(last)
|
|
cmd["channel_order"] = last->entry->channelId();
|
|
} else {
|
|
|
|
}
|
|
|
|
if (cmd["channel_name"].string().length() < 1) return command_result{error::channel_name_inuse, "Invalid channel name"};
|
|
|
|
{
|
|
if (target_tree->findChannel(cmd["channel_name"].as<std::string>(), parent ? dynamic_pointer_cast<BasicChannel>(parent->entry) : nullptr)) return command_result{error::channel_name_inuse, "Name already in use"};
|
|
|
|
created_channel = target_tree->createChannel(parent ? parent->entry->channelId() : (ChannelId) 0, cmd[0].has("channel_order") ? cmd["channel_order"].as<ChannelId>() : (ChannelId) 0, cmd["channel_name"].as<std::string>());
|
|
}
|
|
|
|
if (!created_channel) return command_result{error::channel_invalid_flags, "Could not create channel"};
|
|
|
|
auto created_linked_channel = target_tree->findLinkedChannel(created_channel->channelId());
|
|
sassert(created_linked_channel);
|
|
|
|
if (cmd[0].has("channel_flag_default") && cmd["channel_flag_default"].as<bool>()) {
|
|
old_default_channel = target_tree->getDefaultChannel();
|
|
target_tree->setDefaultChannel(created_channel);
|
|
}
|
|
|
|
created_channel->properties()[property::CHANNEL_CREATED_BY] = this->getClientDatabaseId();
|
|
|
|
{
|
|
auto default_modify_power = this->calculate_permission(permission::i_channel_modify_power, parent_channel_id, false, permission_cache);
|
|
auto default_delete_power = this->calculate_permission(permission::i_channel_delete_power, parent_channel_id, false, permission_cache);
|
|
|
|
auto permission_manager = created_channel->permissions();
|
|
permission_manager->set_permission(
|
|
permission::i_channel_needed_modify_power,
|
|
{default_modify_power.has_value ? default_modify_power.value : 0, 0},
|
|
permission::v2::PermissionUpdateType::set_value,
|
|
permission::v2::PermissionUpdateType::do_nothing
|
|
);
|
|
permission_manager->set_permission(
|
|
permission::i_channel_needed_delete_power,
|
|
{default_delete_power.has_value ? default_delete_power.value : 0, 0},
|
|
permission::v2::PermissionUpdateType::set_value,
|
|
permission::v2::PermissionUpdateType::do_nothing
|
|
);
|
|
}
|
|
|
|
/* log channel create */
|
|
{
|
|
log::ChannelType log_channel_type;
|
|
switch (created_channel->channelType()) {
|
|
case ChannelType::permanent:
|
|
log_channel_type = log::ChannelType::PERMANENT;
|
|
break;
|
|
|
|
case ChannelType::semipermanent:
|
|
log_channel_type = log::ChannelType::SEMI_PERMANENT;
|
|
break;
|
|
|
|
case ChannelType::temporary:
|
|
log_channel_type = log::ChannelType::TEMPORARY;
|
|
break;
|
|
}
|
|
|
|
serverInstance->action_logger()->channel_logger.log_channel_create(this->getServerId(), this->ref(), created_channel->channelId(), log_channel_type);
|
|
}
|
|
|
|
for (auto &property_name : cmd[0].keys()) {
|
|
if (property_name == "channel_flag_default") continue;
|
|
if (property_name == "channel_order") continue;
|
|
if (property_name == "channel_name") continue;
|
|
if (property_name == "cpid") continue;
|
|
if (property_name == "cid") continue;
|
|
|
|
const auto &property = property::find<property::ChannelProperties>(property_name);
|
|
if(property == property::CHANNEL_UNDEFINED) {
|
|
logError(this->getServerId(), "Client " + this->getDisplayName() + " tried to change a not existing channel property " + property_name);
|
|
continue;
|
|
}
|
|
|
|
if(!property.validate_input(cmd[property_name].as<string>())) {
|
|
logError(this->getServerId(), "Client " + this->getDisplayName() + " tried to change a property to an invalid value. (Value: '" + cmd[property_name].as<string>() + "', Property: '" + std::string{property.name} + "')");
|
|
continue;
|
|
}
|
|
auto prop = created_channel->properties()[property];
|
|
auto old_value = prop.value();
|
|
auto new_value = cmd[property_name].as<std::string>();
|
|
if(old_value == new_value)
|
|
continue;
|
|
prop = new_value;
|
|
serverInstance->action_logger()->channel_logger.log_channel_edit(this->getServerId(), this->ref(), created_channel->channelId(), property, old_value, new_value);
|
|
}
|
|
if(created_channel->parent()) {
|
|
if(created_channel->parent()->channelType() > created_channel->channelType())
|
|
created_channel->setChannelType(created_channel->parent()->channelType());
|
|
}
|
|
|
|
if(this->server) {
|
|
const auto self_lock = _this.lock();
|
|
this->server->forEachClient([&, created_channel](const shared_ptr<ConnectedClient>& client) {
|
|
unique_lock client_channel_lock(client->channel_lock);
|
|
auto result = client->channels->add_channel(created_linked_channel);
|
|
if(!result) return;
|
|
|
|
client->notifyChannelCreate(created_channel, result->previous_channel, self_lock);
|
|
if(client == self_lock && this->getType() == ClientType::CLIENT_QUERY) {
|
|
Command notify("");
|
|
notify["cid"] = created_channel->channelId();
|
|
this->sendCommand(notify);
|
|
}
|
|
client->notifyChannelDescriptionChanged(created_channel);
|
|
if (client->subscribeToAll)
|
|
client->subscribeChannel({created_channel}, false, true);
|
|
|
|
if(old_default_channel) {
|
|
//TODO: Reminder: client channel tree must be at least read locked here!
|
|
client->notifyChannelEdited(old_default_channel, {property::CHANNEL_FLAG_DEFAULT}, self_lock, false);
|
|
}
|
|
});
|
|
|
|
GroupId adminGroup = this->server->properties()[property::VIRTUALSERVER_DEFAULT_CHANNEL_ADMIN_GROUP];
|
|
auto channel_admin_group = this->server->groups->findGroup(adminGroup);
|
|
if (!channel_admin_group) {
|
|
logError(this->getServerId(), "Missing server's default channel admin group! Using default channel group!");
|
|
channel_admin_group = this->server->groups->defaultGroup(GroupTarget::GROUPTARGET_CHANNEL);
|
|
}
|
|
|
|
/* FIXME: Log group assignment */
|
|
this->server->groups->setChannelGroup(this->getClientDatabaseId(), channel_admin_group, created_channel);
|
|
|
|
if (created_channel->channelType() == ChannelType::temporary && (this->getType() == ClientType::CLIENT_TEAMSPEAK || this->getType() == ClientType::CLIENT_WEB))
|
|
this->server->client_move(
|
|
this->ref(),
|
|
created_channel,
|
|
nullptr,
|
|
"channel created",
|
|
ViewReasonId::VREASON_USER_ACTION,
|
|
true,
|
|
tree_channel_lock
|
|
);
|
|
}
|
|
|
|
return command_result{error::ok};
|
|
}
|
|
|
|
command_result ConnectedClient::handleCommandChannelDelete(Command &cmd) {
|
|
CMD_RESET_IDLE;
|
|
CMD_CHK_AND_INC_FLOOD_POINTS(25);
|
|
|
|
RESOLVE_CHANNEL_W(cmd["cid"], true);
|
|
auto channel = dynamic_pointer_cast<ServerChannel>(l_channel->entry);
|
|
assert(channel);
|
|
if(channel->deleted) /* channel gets already removed */
|
|
return command_result{error::ok};
|
|
|
|
ACTION_REQUIRES_CHANNEL_PERMISSION(channel, permission::i_channel_needed_delete_power, permission::i_channel_delete_power, true);
|
|
for(const auto& ch : channel_tree->channels(channel)) {
|
|
if(ch->defaultChannel())
|
|
return command_result{error::channel_can_not_delete_default};
|
|
}
|
|
|
|
if(this->server) {
|
|
auto clients = this->server->getClientsByChannelRoot(channel, false);
|
|
if(!clients.empty())
|
|
ACTION_REQUIRES_PERMISSION(permission::b_channel_delete_flag_force, 1, channel->channelId());
|
|
|
|
this->server->delete_channel(channel, this->ref(), "channel deleted", channel_tree_write_lock, false);
|
|
} else {
|
|
auto deleted_channel_ids = channel_tree->deleteChannelRoot(channel);
|
|
for(const auto& channelId : deleted_channel_ids) {
|
|
serverInstance->action_logger()->channel_logger.log_channel_delete(0, this->ref(), channelId, channel->channelId() == channelId ? log::ChannelDeleteReason::USER_ACTION : log::ChannelDeleteReason::PARENT_DELETED);
|
|
}
|
|
this->notifyChannelDeleted(deleted_channel_ids, this->ref());
|
|
}
|
|
|
|
return command_result{error::ok};
|
|
}
|
|
|
|
/*
|
|
* 1. check for permission and basic requirements
|
|
* 2. Lock the channel tree in write mode if required
|
|
* 3. Apply changed, test for advanced requirements like channel name etc
|
|
* 4. notify everyone
|
|
*/
|
|
command_result ConnectedClient::handleCommandChannelEdit(Command &cmd) {
|
|
CMD_RESET_IDLE;
|
|
CMD_CHK_AND_INC_FLOOD_POINTS(25);
|
|
|
|
RESOLVE_CHANNEL_R(cmd["cid"], true);
|
|
auto channel = dynamic_pointer_cast<ServerChannel>(l_channel->entry);
|
|
assert(channel);
|
|
if(channel->deleted) {
|
|
/* channel gets already removed */
|
|
return command_result{error::ok};
|
|
}
|
|
|
|
std::deque<const property::PropertyDescription*> keys;
|
|
bool require_write_lock = false;
|
|
bool update_max_clients = false;
|
|
bool update_max_family_clients = false;
|
|
bool update_clients_in_channel = false;
|
|
bool update_password = false;
|
|
bool update_name = false;
|
|
/* Step 1 */
|
|
|
|
bool target_channel_type_changed = false;
|
|
ChannelType::ChannelType target_channel_type = channel->channelType();
|
|
ACTION_REQUIRES_CHANNEL_PERMISSION(channel, permission::i_channel_needed_modify_power, permission::i_channel_modify_power, true);
|
|
|
|
for (const auto &key : cmd[0].keys()) {
|
|
if(key == "cid")
|
|
continue;
|
|
if(key == "return_code")
|
|
continue;
|
|
|
|
const auto &property = property::find<property::ChannelProperties>(key);
|
|
if(property == property::CHANNEL_UNDEFINED) {
|
|
logError(this->getServerId(), R"({} Tried to edit a not existing channel property "{}" to "{}")", CLIENT_STR_LOG_PREFIX, key, cmd[key].string());
|
|
continue;
|
|
}
|
|
|
|
if((property.flags & property::FLAG_USER_EDITABLE) == 0) {
|
|
logError(this->getServerId(), "{} Tried to change a channel property which is not changeable. (Key: {}, Value: \"{}\")", CLIENT_STR_LOG_PREFIX, key, cmd[key].string());
|
|
continue;
|
|
}
|
|
|
|
if(!property.validate_input(cmd[key].as<string>())) {
|
|
logError(this->getServerId(), "{} Tried to change a channel property to an invalid value. (Key: {}, Value: \"{}\")", CLIENT_STR_LOG_PREFIX, key, cmd[key].string());
|
|
continue;
|
|
}
|
|
|
|
if(channel->properties()[property].as<string>() == cmd[key].as<string>())
|
|
continue; /* we dont need to update stuff which is the same */
|
|
|
|
if(key == "channel_icon_id") {
|
|
ACTION_REQUIRES_CHANNEL_PERMISSION(channel, permission::i_channel_needed_permission_modify_power, permission::i_channel_permission_modify_power, true);
|
|
} else if (key == "channel_order") {
|
|
ACTION_REQUIRES_PERMISSION(permission::b_channel_modify_sortorder, 1, channel_id);
|
|
require_write_lock = true;
|
|
} else if (key == "channel_flag_default") {
|
|
if(!cmd["channel_flag_default"].as<bool>())
|
|
return command_result{error::channel_invalid_flags};
|
|
|
|
ACTION_REQUIRES_GLOBAL_PERMISSION(permission::b_channel_modify_make_default, 1);
|
|
require_write_lock = true;
|
|
} else if (key == "channel_name") {
|
|
ACTION_REQUIRES_PERMISSION(permission::b_channel_modify_name, 1, channel_id);
|
|
if (count_characters(cmd["channel_name"]) < 1)
|
|
return command_result{error::channel_name_invalid};
|
|
if (count_characters(cmd["channel_name"]) > 40)
|
|
return command_result{error::channel_name_invalid};
|
|
require_write_lock = true;
|
|
update_name = true;
|
|
} else if (key == "channel_name_phonetic") {
|
|
ACTION_REQUIRES_PERMISSION(permission::b_channel_modify_name, 1, channel_id);
|
|
} else if (key == "channel_topic") {
|
|
ACTION_REQUIRES_PERMISSION(permission::b_channel_modify_topic, 1, channel_id);
|
|
} else if (key == "channel_description") {
|
|
ACTION_REQUIRES_PERMISSION(permission::b_channel_modify_description, 1, channel_id);
|
|
if(!permission::v2::permission_granted(1, this->calculate_permission(permission::b_client_use_bbcode_any, channel_id))) {
|
|
auto bbcode_image = bbcode::sloppy::has_image(cmd[key]);
|
|
auto bbcode_url = bbcode::sloppy::has_url(cmd[key]);
|
|
debugMessage(this->getServerId(), "Channel description contains bb codes: Image: {} URL: {}", bbcode_image, bbcode_url);
|
|
if(bbcode_image && !permission::v2::permission_granted(1, this->calculate_permission(permission::b_client_use_bbcode_image, channel_id)))
|
|
return command_result{permission::b_client_use_bbcode_image};
|
|
if(bbcode_url && !permission::v2::permission_granted(1, this->calculate_permission(permission::b_client_use_bbcode_url, channel_id)))
|
|
return command_result{permission::b_client_use_bbcode_url};
|
|
}
|
|
} else if (key == "channel_codec") {
|
|
ACTION_REQUIRES_PERMISSION(permission::b_channel_modify_codec, 1, channel_id);
|
|
auto value = cmd[key].as<uint32_t>();
|
|
if(!(value >= 4 && value <= 5)) {
|
|
return command_result{error::parameter_invalid, "channel_codec"};
|
|
}
|
|
} else if (key == "channel_codec_quality") {
|
|
ACTION_REQUIRES_PERMISSION(permission::b_channel_modify_codec_quality, 1, channel_id);
|
|
} else if (key == "channel_codec_is_unencrypted") {
|
|
if (cmd["channel_codec_is_unencrypted"].as<bool>())
|
|
ACTION_REQUIRES_PERMISSION(permission::b_channel_modify_make_codec_encrypted, 1, channel_id);
|
|
} else if (key == "channel_needed_talk_power") {
|
|
ACTION_REQUIRES_PERMISSION(permission::b_channel_modify_needed_talk_power, 1, channel_id);
|
|
update_clients_in_channel = true;
|
|
} else if (key == "channel_maxclients" || key == "channel_flag_maxclients_unlimited") {
|
|
ACTION_REQUIRES_PERMISSION(permission::b_channel_modify_maxclients, 1, channel_id);
|
|
require_write_lock = true;
|
|
update_max_clients = true;
|
|
} else if(key == "channel_maxfamilyclients" || key == "channel_flag_maxfamilyclients_unlimited" || key == "channel_flag_maxfamilyclients_inherited") {
|
|
ACTION_REQUIRES_PERMISSION(permission::b_channel_modify_maxfamilyclients, 1, channel_id);
|
|
require_write_lock = true;
|
|
update_max_family_clients = true;
|
|
} else if (key == "channel_flag_permanent" || key == "channel_flag_semi_permanent") {
|
|
if (cmd[0].has("channel_flag_permanent") && cmd["channel_flag_permanent"].as<bool>()) {
|
|
ACTION_REQUIRES_PERMISSION(permission::b_channel_modify_make_permanent, 1, channel_id);
|
|
target_channel_type = ChannelType::permanent;
|
|
} else if (cmd[0].has("channel_flag_semi_permanent") && cmd["channel_flag_semi_permanent"].as<bool>()) {
|
|
ACTION_REQUIRES_PERMISSION(permission::b_channel_modify_make_semi_permanent, 1, channel_id);
|
|
target_channel_type = ChannelType::semipermanent;
|
|
} else {
|
|
ACTION_REQUIRES_PERMISSION(permission::b_channel_modify_make_temporary, 1, channel_id);
|
|
target_channel_type = ChannelType::temporary;
|
|
}
|
|
target_channel_type_changed = true;
|
|
require_write_lock = true;
|
|
} else if (key == "channel_delete_delay") {
|
|
ACTION_REQUIRES_PERMISSION(permission::b_channel_modify_temp_delete_delay, cmd["channel_delete_delay"].as<permission::PermissionValue>(), channel_id);
|
|
} else if (key == "channel_password" || key == "channel_flag_password") {
|
|
ACTION_REQUIRES_PERMISSION(permission::b_channel_modify_password, 1, channel_id);
|
|
update_password = true;
|
|
} else if (key == "channel_conversation_history_length") {
|
|
auto value = cmd["channel_conversation_history_length"].as<int64_t>();
|
|
if(value == 0) {
|
|
ACTION_REQUIRES_PERMISSION(permission::b_channel_create_modify_conversation_history_unlimited, 1, channel_id);
|
|
} else {
|
|
ACTION_REQUIRES_PERMISSION(permission::i_channel_create_modify_conversation_history_length, 1, channel_id);
|
|
}
|
|
} else if (key == "channel_flag_conversation_private") {
|
|
auto value = cmd["channel_flag_conversation_private"].as<bool>();
|
|
if(value) {
|
|
ACTION_REQUIRES_PERMISSION(permission::b_channel_create_modify_conversation_mode_private, 1, channel_id);
|
|
cmd[property::name(property::CHANNEL_CONVERSATION_MODE)] = CHANNELCONVERSATIONMODE_PRIVATE;
|
|
} else {
|
|
ACTION_REQUIRES_PERMISSION(permission::b_channel_create_modify_conversation_mode_public, 1, channel_id);
|
|
cmd[property::name(property::CHANNEL_CONVERSATION_MODE)] = CHANNELCONVERSATIONMODE_PUBLIC;
|
|
}
|
|
keys.push_back(&property::describe(property::CHANNEL_CONVERSATION_MODE));
|
|
continue;
|
|
} else if (key == "channel_conversation_mode") {
|
|
auto value = cmd["channel_conversation_mode"].as<ChannelConversationMode>();
|
|
switch (value) {
|
|
case ChannelConversationMode::CHANNELCONVERSATIONMODE_PRIVATE:
|
|
ACTION_REQUIRES_PERMISSION(permission::b_channel_create_modify_conversation_mode_private, 1, channel_id);
|
|
break;
|
|
case ChannelConversationMode::CHANNELCONVERSATIONMODE_PUBLIC:
|
|
ACTION_REQUIRES_PERMISSION(permission::b_channel_create_modify_conversation_mode_public, 1, channel_id);
|
|
break;
|
|
case ChannelConversationMode::CHANNELCONVERSATIONMODE_NONE:
|
|
ACTION_REQUIRES_PERMISSION(permission::b_channel_create_modify_conversation_mode_none, 1, channel_id);
|
|
break;
|
|
default:
|
|
return command_result{error::parameter_invalid, "channel_conversation_mode"};
|
|
}
|
|
} else {
|
|
logCritical(
|
|
this->getServerId(),
|
|
"The client " + this->getDisplayName() + " tried to change a editable channel property but we haven't found a permission. Please report this error. (Channel property: {})",
|
|
key
|
|
);
|
|
continue;
|
|
}
|
|
keys.push_back(&property);
|
|
}
|
|
if(keys.empty())
|
|
return command_result{error::ok};
|
|
|
|
unique_lock server_channel_w_lock(this->server ? this->server->channel_tree_lock : serverInstance->getChannelTreeLock(), defer_lock);
|
|
if(require_write_lock) {
|
|
channel_tree_read_lock.unlock();
|
|
server_channel_w_lock.lock();
|
|
|
|
/* not that while we're waiting to edit the server the channel got deleted... fuck my english */
|
|
if(channel->deleted)
|
|
return command_result{error::ok};
|
|
}
|
|
|
|
/* test the password parameters */
|
|
if(update_password) {
|
|
if(!cmd[0].has("channel_password")) {
|
|
if(cmd[0].has("channel_flag_password") && cmd["channel_flag_password"].as<bool>())
|
|
return command_result{error::parameter_missing};
|
|
else
|
|
cmd["channel_password"] = ""; /* no password set */
|
|
keys.push_back(&property::describe(property::CHANNEL_PASSWORD));
|
|
}
|
|
if(!cmd[0].has("channel_flag_password")) {
|
|
cmd["channel_flag_password"] = !cmd["channel_password"].string().empty();
|
|
keys.push_back(&property::describe(property::CHANNEL_FLAG_PASSWORD));
|
|
}
|
|
|
|
if(cmd["channel_flag_password"].as<bool>()) {
|
|
if(cmd["channel_password"].string().empty())
|
|
return command_result{error::channel_invalid_flags}; /* we cant enable a password without a given password */
|
|
|
|
/* we've to "encode" the password */
|
|
if(this->getType() == ClientType ::CLIENT_QUERY)
|
|
cmd["channel_password"] = base64::encode(digest::sha1(cmd["channel_password"].string()));
|
|
} else {
|
|
cmd["channel_password"] = ""; /* flag password if false so we set the password to empty */
|
|
}
|
|
}
|
|
|
|
/* test the default channel update */
|
|
const auto target_will_be_default = cmd[0].has("channel_flag_default") ? cmd["channel_flag_default"].as<bool>() : channel->defaultChannel();
|
|
if(target_will_be_default) {
|
|
if(target_channel_type != ChannelType::permanent)
|
|
return command_result{error::channel_default_require_permanent}; /* default channel is not allowed to be non permanent */
|
|
|
|
if((cmd[0].has("channel_flag_password") && cmd["channel_flag_password"].as<bool>()) || channel->properties()[property::CHANNEL_FLAG_PASSWORD]) {
|
|
cmd["channel_flag_password"] = false;
|
|
cmd["channel_password"] = "";
|
|
keys.push_back(&property::describe(property::CHANNEL_FLAG_PASSWORD));
|
|
}
|
|
|
|
if(target_will_be_default) {
|
|
cmd["channel_maxclients"] = -1;
|
|
cmd["channel_flag_maxclients_unlimited"] = true;
|
|
keys.push_back(&property::describe(property::CHANNEL_MAXCLIENTS));
|
|
keys.push_back(&property::describe(property::CHANNEL_FLAG_MAXCLIENTS_UNLIMITED));
|
|
update_max_clients = true;
|
|
|
|
cmd["channel_maxfamilyclients"] = -1;
|
|
cmd["channel_flag_maxfamilyclients_inherited"] = false;
|
|
keys.push_back(&property::describe(property::CHANNEL_MAXFAMILYCLIENTS));
|
|
keys.push_back(&property::describe(property::CHANNEL_FLAG_MAXFAMILYCLIENTS_INHERITED));
|
|
update_max_family_clients = true;
|
|
}
|
|
}
|
|
|
|
/* "fix" max client for temporary channels */
|
|
if(target_channel_type_changed) {
|
|
if(target_channel_type == ChannelType::temporary) {
|
|
if(channel->properties()[property::CHANNEL_MAXCLIENTS].as<int>() != -1) {
|
|
cmd["channel_maxclients"] = -1;
|
|
cmd["channel_flag_maxclients_unlimited"] = true;
|
|
keys.push_back(&property::describe(property::CHANNEL_MAXCLIENTS));
|
|
keys.push_back(&property::describe(property::CHANNEL_FLAG_MAXCLIENTS_UNLIMITED));
|
|
update_max_clients = true;
|
|
}
|
|
if(channel->properties()[property::CHANNEL_MAXFAMILYCLIENTS].as<int>() != -1) {
|
|
cmd["channel_maxfamilyclients"] = -1;
|
|
cmd["channel_flag_maxfamilyclients_inherited"] = true;
|
|
keys.push_back(&property::describe(property::CHANNEL_MAXFAMILYCLIENTS));
|
|
keys.push_back(&property::describe(property::CHANNEL_FLAG_MAXFAMILYCLIENTS_INHERITED));
|
|
update_max_family_clients = true;
|
|
}
|
|
}
|
|
|
|
if(target_channel_type != ChannelType::permanent) {
|
|
/* test if any child is the default channel */
|
|
for(const auto& child : channel_tree->channels(channel))
|
|
if(child->defaultChannel())
|
|
return command_result{error::channel_default_require_permanent}; /* default channel is not allowed to be non permanent */
|
|
}
|
|
auto parent = channel->parent();
|
|
if(parent && parent->channelType() > target_channel_type)
|
|
return command_result{error::channel_parent_not_permanent};
|
|
}
|
|
|
|
/* test the max clients parameters */
|
|
if(update_max_clients) {
|
|
if(!cmd[0].has("channel_maxclients")) {
|
|
if(cmd[0].has("channel_flag_maxclients_unlimited") && cmd["channel_flag_maxclients_unlimited"].as<bool>())
|
|
cmd["channel_maxclients"] = -1;
|
|
else
|
|
return command_result{error::parameter_missing, "channel_maxclients"}; /* max clients must be specified */
|
|
keys.push_back(&property::describe(property::CHANNEL_MAXCLIENTS));
|
|
}
|
|
|
|
if(!cmd[0].has("channel_flag_maxclients_unlimited")) {
|
|
cmd["channel_flag_maxclients_unlimited"] = cmd["channel_maxclients"].as<int>() < 0;
|
|
keys.push_back(&property::describe(property::CHANNEL_FLAG_MAXCLIENTS_UNLIMITED));
|
|
}
|
|
|
|
if(cmd["channel_flag_maxclients_unlimited"].as<bool>() && cmd["channel_maxclients"].as<int>() != -1)
|
|
return command_result{error::channel_invalid_flags}; /* channel cant have a max client settings AND be unlimited as well */
|
|
|
|
if(!cmd["channel_flag_maxclients_unlimited"].as<bool>() && target_channel_type == ChannelType::temporary)
|
|
return command_result{error::channel_invalid_flags}; /* temporary channels cant have a limited user count */
|
|
}
|
|
|
|
/* test the max family clients parameters */
|
|
if(update_max_family_clients) {
|
|
//auto channel_maxfamilyclients = cmd[0].has("channel_maxfamilyclients") ? std::optional<int>{cmd["channel_maxfamilyclients"].as<int>()} : std::nullopt;
|
|
//auto channel_flag_maxfamilyclients_unlimited = cmd[0].has("channel_flag_maxfamilyclients_unlimited") ? std::optional<bool>{cmd["channel_flag_maxfamilyclients_unlimited"].as<bool>()} : std::nullopt;
|
|
//auto channel_flag_maxfamilyclients_inherited = cmd[0].has("channel_flag_maxfamilyclients_inherited") ? std::optional<bool>{cmd["channel_flag_maxfamilyclients_inherited"].as<bool>()} : std::nullopt;
|
|
|
|
/* update actual count from flags */
|
|
if(!cmd[0].has("channel_maxfamilyclients")) {
|
|
if(cmd[0].has("channel_flag_maxfamilyclients_unlimited") && cmd["channel_flag_maxfamilyclients_unlimited"].as<bool>()) {
|
|
cmd["channel_maxfamilyclients"] = -1;
|
|
keys.push_back(&property::describe(property::CHANNEL_MAXFAMILYCLIENTS));
|
|
} else if(cmd[0].has("channel_flag_maxfamilyclients_inherited") && cmd["channel_flag_maxfamilyclients_inherited"].as<bool>()) {
|
|
cmd["channel_maxfamilyclients"] = -1;
|
|
keys.push_back(&property::describe(property::CHANNEL_MAXFAMILYCLIENTS));
|
|
} else {
|
|
return command_result{error::parameter_missing, "channel_maxfamilyclients"}; /* since its not unlimited or inherited, channel_maxfamilyclients must be specified */
|
|
}
|
|
}
|
|
|
|
//Update the flags from channel_maxfamilyclients if needed
|
|
if(!cmd[0].has("channel_flag_maxfamilyclients_unlimited")) {
|
|
auto flag_inherited = cmd[0].has("channel_flag_maxfamilyclients_inherited") && cmd["channel_flag_maxfamilyclients_inherited"].as<bool>();
|
|
if(flag_inherited)
|
|
cmd["channel_flag_maxfamilyclients_unlimited"] = false;
|
|
else
|
|
cmd["channel_flag_maxfamilyclients_unlimited"] = cmd["channel_maxfamilyclients"].as<int>() < 0;
|
|
}
|
|
|
|
if(!cmd[0].has("channel_flag_maxfamilyclients_inherited")) {
|
|
auto flag_unlimited = cmd[0].has("channel_flag_maxfamilyclients_unlimited") && cmd["channel_flag_maxfamilyclients_unlimited"].as<bool>();
|
|
if(flag_unlimited)
|
|
cmd["channel_flag_maxfamilyclients_inherited"] = false;
|
|
else
|
|
cmd["channel_flag_maxfamilyclients_inherited"] = cmd["channel_maxfamilyclients"].as<int>() < 0;
|
|
}
|
|
|
|
/* final checkup */
|
|
if(cmd["channel_flag_maxfamilyclients_inherited"].as<bool>() && cmd["channel_flag_maxfamilyclients_unlimited"].as<bool>())
|
|
return command_result{error::channel_invalid_flags}; /* both at the same time are not possible */
|
|
|
|
if(cmd["channel_flag_maxfamilyclients_inherited"].as<bool>() && cmd["channel_maxfamilyclients"].as<int>() != -1)
|
|
return command_result{error::channel_invalid_flags}; /* flag inherited required max users to be -1 */
|
|
|
|
if(cmd["channel_flag_maxfamilyclients_unlimited"].as<bool>() && cmd["channel_maxfamilyclients"].as<int>() != -1)
|
|
return command_result{error::channel_invalid_flags}; /* flag unlimited required max users to be -1 */
|
|
|
|
if(cmd["channel_maxfamilyclients"].as<int>() != -1 && target_channel_type == ChannelType::temporary)
|
|
return command_result{error::channel_invalid_flags}; /* temporary channels cant have a limited user count */
|
|
}
|
|
|
|
/* test the channel name */
|
|
if(update_name) {
|
|
auto named_channel = channel_tree->findChannel(cmd["channel_name"].string(), channel->parent());
|
|
if (named_channel)
|
|
return command_result{error::channel_name_inuse};
|
|
}
|
|
|
|
auto self_ref = this->ref();
|
|
shared_ptr<BasicChannel> old_default_channel;
|
|
deque<shared_ptr<BasicChannel>> child_channel_updated;
|
|
for(const property::PropertyDescription* key : keys) {
|
|
if(*key == property::CHANNEL_ORDER) {
|
|
/* TODO: May move that up because if it fails may some other props have already be applied */
|
|
auto old_channel_order = channel->channelOrder();
|
|
if (!channel_tree->change_order(channel, cmd[std::string{key->name}]))
|
|
return command_result{error::channel_invalid_order, "Can't change order id"};
|
|
|
|
auto channel_parent = channel->parent() ? channel->parent()->channelId() : 0;
|
|
serverInstance->action_logger()->channel_logger.log_channel_move(this->getServerId(), this->ref(), channel->channelId(),
|
|
channel_parent, channel_parent,
|
|
old_channel_order, channel->channelOrder());
|
|
|
|
if(this->server) {
|
|
auto parent = channel->hasParent() ? channel_tree->findLinkedChannel(channel->parent()->channelId()) : nullptr;
|
|
auto previous = channel_tree->findLinkedChannel(channel->previousChannelId());
|
|
|
|
this->server->forEachClient([&](const shared_ptr<ConnectedClient>& cl) {
|
|
unique_lock client_channel_lock(cl->channel_lock);
|
|
|
|
auto actions = cl->channels->change_order(l_channel, parent, previous);
|
|
std::deque<ChannelId> deletions;
|
|
for(const auto& action : actions) {
|
|
switch (action.first) {
|
|
case ClientChannelView::NOTHING:
|
|
continue;
|
|
case ClientChannelView::ENTER_VIEW:
|
|
cl->notifyChannelShow(action.second->channel(), action.second->previous_channel);
|
|
break;
|
|
case ClientChannelView::DELETE_VIEW:
|
|
deletions.push_back(action.second->channelId());
|
|
break;
|
|
case ClientChannelView::MOVE:
|
|
cl->notifyChannelMoved(action.second->channel(), action.second->previous_channel, this->ref());
|
|
break;
|
|
case ClientChannelView::REORDER:
|
|
cl->notifyChannelEdited(action.second->channel(), {property::CHANNEL_ORDER}, self_ref, false);
|
|
break;
|
|
}
|
|
}
|
|
if(!deletions.empty()) {
|
|
cl->notifyChannelHide(deletions, false);
|
|
return; //Channel got deleted so we dont have to send the updates
|
|
}
|
|
});
|
|
}
|
|
|
|
/* property has already been updated as well the log has also been written */
|
|
continue;
|
|
} else if(*key == property::CHANNEL_FLAG_DEFAULT) {
|
|
old_default_channel = channel_tree->getDefaultChannel();
|
|
if(old_default_channel == channel) {
|
|
old_default_channel = nullptr;
|
|
continue;
|
|
}
|
|
|
|
if(!cmd[std::string{key->name}].as<bool>()) {
|
|
old_default_channel = nullptr;
|
|
continue;
|
|
}
|
|
|
|
channel_tree->setDefaultChannel(channel);
|
|
|
|
deque<shared_ptr<BasicChannel>> updated_channels;
|
|
shared_ptr<BasicChannel> current_channel = channel;
|
|
do {
|
|
if(current_channel->channelType() == ChannelType::permanent)
|
|
break;
|
|
|
|
current_channel->setChannelType(ChannelType::permanent);
|
|
if(current_channel != channel)
|
|
updated_channels.push_back(channel);
|
|
} while ((current_channel = current_channel->parent()));
|
|
|
|
if(this->server && !updated_channels.empty()) {
|
|
std::reverse(updated_channels.begin(), updated_channels.end());
|
|
auto this_ref = this->ref();
|
|
this->server->forEachClient([&](const shared_ptr<ConnectedClient>& client) {
|
|
unique_lock client_channel_lock(client->channel_lock);
|
|
for(const auto& channel : updated_channels) {
|
|
client->notifyChannelEdited(channel, {property::CHANNEL_FLAG_PERMANENT, property::CHANNEL_FLAG_SEMI_PERMANENT}, this_ref, false);
|
|
}
|
|
});
|
|
}
|
|
} else if(*key == property::CHANNEL_FLAG_PERMANENT || *key == property::CHANNEL_FLAG_SEMI_PERMANENT) {
|
|
if(target_channel_type_changed) { /* must be true else the key would not appere here */
|
|
target_channel_type_changed = false; /* we only need to check all subchannels once! */
|
|
|
|
|
|
{ /* check channel children */
|
|
deque<shared_ptr<BasicChannel>> channel_to_test = {channel};
|
|
|
|
while(!channel_to_test.empty()) {
|
|
auto current_channel = channel_to_test.front();
|
|
channel_to_test.pop_front();
|
|
|
|
for(const auto& child : channel_tree->channels(current_channel, 1)) {
|
|
if(child == current_channel)
|
|
continue;
|
|
|
|
if(child->channelType() < target_channel_type) {
|
|
child->setChannelType(target_channel_type);
|
|
channel_to_test.push_back(child);
|
|
child_channel_updated.push_back(child);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
} else if(*key == property::CHANNEL_CONVERSATION_HISTORY_LENGTH) {
|
|
//channel_conversation_history_length
|
|
auto conversation_manager = this->server->conversation_manager();
|
|
if(conversation_manager) {
|
|
auto conversation = conversation_manager->get(channel->channelId());
|
|
if(conversation)
|
|
conversation->set_history_length(cmd[std::string{key->name}]);
|
|
}
|
|
} else if(*key == property::CHANNEL_NEEDED_TALK_POWER) {
|
|
channel->permissions()->set_permission(permission::i_client_needed_talk_power, {cmd[key->name].as<int>(), 0}, permission::v2::set_value, permission::v2::do_nothing);
|
|
}
|
|
|
|
auto prop = channel->properties()[*key];
|
|
auto old_value = prop.value();
|
|
auto new_value = cmd[std::string{key->name}].string();
|
|
if(old_value == new_value)
|
|
continue;
|
|
|
|
prop = new_value;
|
|
serverInstance->action_logger()->channel_logger.log_channel_edit(this->getServerId(), this->ref(), channel->channelId(), *key, old_value, new_value);
|
|
}
|
|
if(this->server) {
|
|
vector<property::ChannelProperties> key_vector;
|
|
key_vector.reserve(keys.size());
|
|
for(const auto& key : keys)
|
|
key_vector.push_back((property::ChannelProperties) key->property_index);
|
|
|
|
auto self_rev = this->ref();
|
|
this->server->forEachClient([&](const shared_ptr<ConnectedClient>& client) {
|
|
unique_lock client_channel_lock(client->channel_lock);
|
|
|
|
for(const auto& channel : child_channel_updated)
|
|
client->notifyChannelEdited(channel, {property::CHANNEL_FLAG_PERMANENT, property::CHANNEL_FLAG_SEMI_PERMANENT}, self_rev, false);
|
|
|
|
client->notifyChannelEdited(channel, key_vector, self_rev, false);
|
|
|
|
if(old_default_channel) /* clients need to have one or more defualt channels... */
|
|
client->notifyChannelEdited(old_default_channel, {property::CHANNEL_FLAG_DEFAULT}, self_rev, false);
|
|
});
|
|
}
|
|
|
|
if(server_channel_w_lock.owns_lock())
|
|
server_channel_w_lock.unlock();
|
|
|
|
if(!channel_tree_read_lock.owns_lock())
|
|
channel_tree_read_lock.lock();
|
|
|
|
if(update_clients_in_channel && this->server) {
|
|
for(const auto& client : this->server->getClientsByChannel(channel))
|
|
client->updateChannelClientProperties(true, true); //TODO: May only update the talk power and not all?
|
|
}
|
|
return command_result{error::ok};
|
|
}
|
|
|
|
command_result ConnectedClient::handleCommandChannelMove(Command &cmd) {
|
|
CMD_RESET_IDLE;
|
|
CMD_CHK_AND_INC_FLOOD_POINTS(25);
|
|
RESOLVE_CHANNEL_W(cmd["cid"], true);
|
|
auto channel = dynamic_pointer_cast<ServerChannel>(l_channel->entry);
|
|
assert(channel);
|
|
if(channel->deleted)
|
|
return command_result{error::channel_is_deleted};
|
|
|
|
if(!cmd[0].has("order"))
|
|
cmd["order"] = 0;
|
|
|
|
auto l_parent = channel_tree->findLinkedChannel(cmd["cpid"]);
|
|
shared_ptr<TreeView::LinkedTreeEntry> l_order;
|
|
if(cmd[0].has("order")) {
|
|
l_order = channel_tree->findLinkedChannel(cmd["order"]);
|
|
if (!l_order && cmd["order"].as<ChannelId>() != 0) return command_result{error::channel_invalid_id};
|
|
} else {
|
|
l_order = l_parent ? l_parent->child_head : (this->server ? this->server->getChannelTree() : serverInstance->getChannelTree().get())->tree_head();
|
|
while(l_order && l_order->next)
|
|
l_order = l_order->next;
|
|
}
|
|
|
|
auto parent = l_parent ? dynamic_pointer_cast<ServerChannel>(l_parent->entry) : nullptr;
|
|
auto order = l_order ? dynamic_pointer_cast<ServerChannel>(l_order->entry) : nullptr;
|
|
|
|
if((parent && parent->deleted) || (order && order->deleted))
|
|
return command_result{error::channel_is_deleted, "parent channel order previous channel has been deleted"};
|
|
|
|
if(channel->parent() == parent && channel->channelOrder() == (order ? order->channelId() : 0))
|
|
return command_result{error::ok};
|
|
|
|
auto old_parent_channel_id = channel->parent() ? channel->parent()->channelId() : 0;
|
|
auto old_channel_order = channel->channelOrder();
|
|
|
|
bool change_parent{channel->parent() != parent};
|
|
bool change_order{(order ? order->channelId() : 0) != channel->channelOrder()};
|
|
|
|
if (change_parent)
|
|
ACTION_REQUIRES_PERMISSION(permission::b_channel_modify_parent, 1, channel_id);
|
|
|
|
if (change_order)
|
|
ACTION_REQUIRES_PERMISSION(permission::b_channel_modify_sortorder, 1, channel_id);
|
|
|
|
{
|
|
auto min_channel_deep = this->calculate_permission(permission::i_channel_min_depth, 0, false);
|
|
auto max_channel_deep = this->calculate_permission(permission::i_channel_max_depth, 0, false);
|
|
|
|
if(min_channel_deep.has_value || max_channel_deep.has_value) {
|
|
auto channel_deep = 0;
|
|
auto local_parent = l_parent;
|
|
while(local_parent) {
|
|
channel_deep++;
|
|
local_parent = local_parent->parent.lock();
|
|
}
|
|
|
|
if(min_channel_deep.has_value && (channel_deep < min_channel_deep.value && !min_channel_deep.has_infinite_power())) return command_result{permission::i_channel_min_depth};
|
|
if(max_channel_deep.has_value && !permission::v2::permission_granted(channel_deep, max_channel_deep)) return command_result{permission::i_channel_max_depth};
|
|
}
|
|
}
|
|
{
|
|
auto name = channel_tree->findChannel(channel->name(), parent);
|
|
if(name && name != channel) return command_result{error::channel_name_inuse};
|
|
}
|
|
debugMessage(this->getServerId(), "Moving channel {} from old [{} | {}] to [{} | {}]", channel->name(), channel->channelOrder(), channel->parent() ? channel->parent()->channelId() : 0, order ? order->channelId() : 0, parent ? parent->channelId() : 0);
|
|
|
|
if (!channel_tree->move_channel(channel, parent, order)) return command_result{error::channel_invalid_order, "Cant change order id"};
|
|
|
|
deque<shared_ptr<BasicChannel>> channel_type_updates;
|
|
{
|
|
auto flag_default = channel->defaultChannel();
|
|
auto current_channel = channel;
|
|
do {
|
|
if(flag_default) {
|
|
if(current_channel->channelType() != ChannelType::permanent) {
|
|
current_channel->setChannelType(ChannelType::permanent);
|
|
channel_type_updates.push_front(current_channel);
|
|
}
|
|
} else if(current_channel->hasParent()) {
|
|
if(current_channel->channelType() < current_channel->parent()->channelType()) {
|
|
current_channel->setChannelType(current_channel->parent()->channelType());
|
|
channel_type_updates.push_front(current_channel);
|
|
}
|
|
}
|
|
} while ((current_channel = dynamic_pointer_cast<ServerChannel>(current_channel->parent())));
|
|
}
|
|
|
|
/* log all the updates */
|
|
serverInstance->action_logger()->channel_logger.log_channel_move(this->getServerId(), this->ref(), channel->channelId(),
|
|
old_parent_channel_id, channel->parent() ? channel->parent()->channelId() : 0,
|
|
old_channel_order, channel->channelOrder());
|
|
|
|
for(const auto& type_update : channel_type_updates) {
|
|
serverInstance->action_logger()->channel_logger.log_channel_edit(this->getServerId(), this->ref(), type_update->channelId(),
|
|
property::describe(property::CHANNEL_FLAG_PERMANENT),
|
|
"",
|
|
type_update->properties()[property::CHANNEL_FLAG_PERMANENT].value()
|
|
);
|
|
serverInstance->action_logger()->channel_logger.log_channel_edit(this->getServerId(), this->ref(), type_update->channelId(),
|
|
property::describe(property::CHANNEL_FLAG_SEMI_PERMANENT),
|
|
"",
|
|
type_update->properties()[property::CHANNEL_FLAG_SEMI_PERMANENT].value()
|
|
);
|
|
}
|
|
|
|
if(this->server) {
|
|
auto self_rev = this->ref();
|
|
this->server->forEachClient([&](const shared_ptr<ConnectedClient>& client) {
|
|
unique_lock channel_lock(client->channel_lock);
|
|
for(const auto& type_update : channel_type_updates)
|
|
client->notifyChannelEdited(type_update, {property::CHANNEL_FLAG_PERMANENT, property::CHANNEL_FLAG_SEMI_PERMANENT}, self_rev, false);
|
|
|
|
auto actions = client->channels->change_order(l_channel, l_parent, l_order);
|
|
std::deque<ChannelId> deletions;
|
|
for(const auto& action : actions) {
|
|
switch (action.first) {
|
|
case ClientChannelView::NOTHING:
|
|
continue;
|
|
case ClientChannelView::ENTER_VIEW:
|
|
client->notifyChannelShow(action.second->channel(), action.second->previous_channel);
|
|
break;
|
|
case ClientChannelView::DELETE_VIEW:
|
|
deletions.push_back(action.second->channelId());
|
|
break;
|
|
case ClientChannelView::MOVE:
|
|
client->notifyChannelMoved(action.second->channel(), action.second->previous_channel, _this.lock());
|
|
break;
|
|
case ClientChannelView::REORDER:
|
|
client->notifyChannelEdited(action.second->channel(), {property::CHANNEL_ORDER}, self_rev, false);
|
|
break;
|
|
}
|
|
}
|
|
if(!deletions.empty())
|
|
client->notifyChannelHide(deletions, false);
|
|
});
|
|
}
|
|
|
|
return command_result{error::ok};
|
|
}
|
|
|
|
command_result ConnectedClient::handleCommandChannelPermList(Command &cmd) {
|
|
CMD_CHK_AND_INC_FLOOD_POINTS(5);
|
|
|
|
RESOLVE_CHANNEL_R(cmd["cid"], true);
|
|
auto channel = dynamic_pointer_cast<BasicChannel>(l_channel->entry);
|
|
assert(channel);
|
|
|
|
ACTION_REQUIRES_PERMISSION(permission::b_virtualserver_channel_permission_list, 1, channel->channelId());
|
|
|
|
if(this->getType() == ClientType::CLIENT_TEAMSPEAK && this->command_times.last_notify + this->command_times.notify_timeout < system_clock::now()) {
|
|
this->sendTSPermEditorWarning();
|
|
}
|
|
|
|
auto sids = cmd.hasParm("permsid");
|
|
|
|
Command result(this->notify_response_command("notifychannelpermlist"));
|
|
int index = 0;
|
|
result["cid"] = channel->channelId();
|
|
|
|
auto permission_mapper = serverInstance->getPermissionMapper();
|
|
auto type = this->getType();
|
|
auto permission_manager = channel->permissions();
|
|
for (const auto &permission_data : permission_manager->permissions()) {
|
|
auto& permission = std::get<1>(permission_data);
|
|
if(permission.flags.value_set) {
|
|
if(sids) {
|
|
result[index]["permsid"] = permission_mapper->permission_name(type, std::get<0>(permission_data));
|
|
} else {
|
|
result[index]["permid"] = std::get<0>(permission_data);
|
|
}
|
|
|
|
result[index]["permvalue"] = permission.values.value;
|
|
result[index]["permnegated"] = permission.flags.negate;
|
|
result[index]["permskip"] = permission.flags.skip;
|
|
index++;
|
|
}
|
|
if(permission.flags.grant_set) {
|
|
if(sids) {
|
|
result[index]["permsid"] = permission_mapper->permission_name_grant(type, std::get<0>(permission_data));
|
|
} else {
|
|
result[index]["permid"] = (uint16_t) (std::get<0>(permission_data) | PERM_ID_GRANT);
|
|
}
|
|
result[index]["permvalue"] = permission.values.grant;
|
|
result[index]["permnegated"] = 0;
|
|
result[index]["permskip"] = 0;
|
|
index++;
|
|
}
|
|
}
|
|
if(index == 0)
|
|
return command_result{error::database_empty_result};
|
|
|
|
this->sendCommand(result);
|
|
return command_result{error::ok};
|
|
}
|
|
|
|
//
|
|
//channel_icon_id=18446744073297259750
|
|
//channel_name
|
|
//channel_topic
|
|
//Desctiption has no extra parm
|
|
command_result ConnectedClient::handleCommandChannelAddPerm(Command &cmd) {
|
|
CMD_RESET_IDLE;
|
|
CMD_CHK_AND_INC_FLOOD_POINTS(5);
|
|
|
|
RESOLVE_CHANNEL_R(cmd["cid"], true);
|
|
auto channel = dynamic_pointer_cast<BasicChannel>(l_channel->entry);
|
|
assert(channel);
|
|
|
|
ACTION_REQUIRES_CHANNEL_PERMISSION(channel, permission::i_channel_needed_permission_modify_power, permission::i_channel_permission_modify_power, true);
|
|
|
|
command::bulk_parser::PermissionBulksParser<true> pparser{cmd};
|
|
if(!pparser.validate(this->ref(), channel->channelId()))
|
|
return pparser.build_command_result();
|
|
|
|
auto permission_manager = channel->permissions();
|
|
auto updateClients = false, update_join_permissions = false, update_channel_properties = false;
|
|
auto channelId = channel->channelId();
|
|
for(const auto& ppermission : pparser.iterate_valid_permissions()) {
|
|
ppermission.apply_to(permission_manager, permission::v2::PermissionUpdateType::set_value);
|
|
ppermission.log_update(serverInstance->action_logger()->permission_logger,
|
|
this->getServerId(),
|
|
this->ref(),
|
|
log::PermissionTarget::CHANNEL,
|
|
permission::v2::PermissionUpdateType::set_value,
|
|
channelId, channel->name(),
|
|
0, ""
|
|
);
|
|
|
|
updateClients |= ppermission.is_client_view_property();
|
|
update_join_permissions = ppermission.permission_type() == permission::i_channel_needed_join_power;
|
|
update_channel_properties |= channel->permission_require_property_update(ppermission.permission_type());
|
|
}
|
|
|
|
if(update_channel_properties && this->server)
|
|
this->server->update_channel_from_permissions(channel, this->ref());
|
|
|
|
if((updateClients || update_join_permissions) && this->server) {
|
|
this->server->forEachClient([&](std::shared_ptr<ConnectedClient> cl) {
|
|
if(updateClients && cl->currentChannel == channel)
|
|
cl->updateChannelClientProperties(true, true);
|
|
if(update_join_permissions)
|
|
cl->join_state_id++;
|
|
});
|
|
}
|
|
|
|
return pparser.build_command_result();
|
|
}
|
|
|
|
command_result ConnectedClient::handleCommandChannelDelPerm(Command &cmd) {
|
|
CMD_RESET_IDLE;
|
|
|
|
RESOLVE_CHANNEL_R(cmd["cid"], true)
|
|
auto channel = dynamic_pointer_cast<BasicChannel>(l_channel->entry);
|
|
assert(channel);
|
|
|
|
ACTION_REQUIRES_CHANNEL_PERMISSION(channel, permission::i_channel_needed_permission_modify_power, permission::i_channel_permission_modify_power, true);
|
|
|
|
command::bulk_parser::PermissionBulksParser<false> pparser{cmd};
|
|
if(!pparser.validate(this->ref(), channel->channelId()))
|
|
return pparser.build_command_result();
|
|
|
|
auto permission_manager = channel->permissions();
|
|
auto updateClients = false, update_join_permissions = false, update_channel_properties = false;
|
|
auto channelId = channel->channelId();
|
|
for(const auto& ppermission : pparser.iterate_valid_permissions()) {
|
|
ppermission.apply_to(permission_manager, permission::v2::PermissionUpdateType::delete_value);
|
|
ppermission.log_update(serverInstance->action_logger()->permission_logger,
|
|
this->getServerId(),
|
|
this->ref(),
|
|
log::PermissionTarget::CHANNEL,
|
|
permission::v2::PermissionUpdateType::delete_value,
|
|
channelId, channel->name(),
|
|
0, ""
|
|
);
|
|
|
|
updateClients |= ppermission.is_client_view_property();
|
|
update_join_permissions = ppermission.permission_type() == permission::i_channel_needed_join_power;
|
|
update_channel_properties |= channel->permission_require_property_update(ppermission.permission_type());
|
|
}
|
|
|
|
if(update_channel_properties && this->server)
|
|
this->server->update_channel_from_permissions(channel, this->ref());
|
|
|
|
if((updateClients || update_join_permissions) && this->server) {
|
|
this->server->forEachClient([&](std::shared_ptr<ConnectedClient> cl) {
|
|
if(updateClients && cl->currentChannel == channel)
|
|
cl->updateChannelClientProperties(true, true);
|
|
if(update_join_permissions)
|
|
cl->join_state_id++;
|
|
});
|
|
}
|
|
|
|
return pparser.build_command_result();
|
|
}
|
|
|
|
command_result ConnectedClient::handleCommandChannelClientPermList(Command &cmd) {
|
|
CMD_REQ_SERVER;
|
|
CMD_RESET_IDLE;
|
|
CMD_CHK_AND_INC_FLOOD_POINTS(5);
|
|
RESOLVE_CHANNEL_R(cmd["cid"], true);
|
|
auto channel = dynamic_pointer_cast<ServerChannel>(l_channel->entry);
|
|
if(!channel) return command_result{error::vs_critical};
|
|
ACTION_REQUIRES_PERMISSION(permission::b_virtualserver_channelclient_permission_list, 1, channel_id);
|
|
|
|
if(!serverInstance->databaseHelper()->validClientDatabaseId(this->server, cmd["cldbid"])) return command_result{error::client_invalid_id};
|
|
auto mgr = serverInstance->databaseHelper()->loadClientPermissionManager(this->server, cmd["cldbid"].as<ClientDbId>());
|
|
|
|
Command res(this->getExternalType() == CLIENT_TEAMSPEAK ? "notifychannelclientpermlist" : "");
|
|
|
|
auto permissions = mgr->channel_permissions(channel->channelId());
|
|
if(permissions.empty())
|
|
return command_result{error::database_empty_result};
|
|
|
|
int index = 0;
|
|
res[index]["cid"] = channel->channelId();
|
|
res[index]["cldbid"] = cmd["cldbid"].as<ClientDbId>();
|
|
|
|
auto sids = cmd.hasParm("permsid");
|
|
auto permission_mapper = serverInstance->getPermissionMapper();
|
|
auto type = this->getType();
|
|
|
|
for (const auto &permission_data : permissions) {
|
|
auto& permission = std::get<1>(permission_data);
|
|
if(permission.flags.value_set) {
|
|
if (sids)
|
|
res[index]["permsid"] = permission_mapper->permission_name(type, get<0>(permission_data));
|
|
else
|
|
res[index]["permid"] = get<0>(permission_data);
|
|
res[index]["permvalue"] = permission.values.value;
|
|
|
|
res[index]["permnegated"] = permission.flags.negate;
|
|
res[index]["permskip"] = permission.flags.skip;
|
|
index++;
|
|
}
|
|
|
|
|
|
if(permission.flags.grant_set) {
|
|
if (sids)
|
|
res[index]["permsid"] = permission_mapper->permission_name_grant(type, get<0>(permission_data));
|
|
else
|
|
res[index]["permid"] = (get<0>(permission_data) | PERM_ID_GRANT);
|
|
res[index]["permvalue"] = permission.values.grant;
|
|
res[index]["permnegated"] = 0;
|
|
res[index]["permskip"] = 0;
|
|
index++;
|
|
}
|
|
}
|
|
|
|
this->sendCommand(res);
|
|
return command_result{error::ok};
|
|
}
|
|
|
|
command_result ConnectedClient::handleCommandChannelClientDelPerm(Command &cmd) {
|
|
CMD_REF_SERVER(server_ref);
|
|
CMD_RESET_IDLE;
|
|
CMD_CHK_AND_INC_FLOOD_POINTS(5);
|
|
|
|
auto cldbid = cmd["cldbid"].as<ClientDbId>();
|
|
if (!serverInstance->databaseHelper()->validClientDatabaseId(this->server, cldbid))
|
|
return command_result{error::parameter_invalid, "Invalid manager db id"};
|
|
|
|
RESOLVE_CHANNEL_R(cmd["cid"], true);
|
|
auto channel = dynamic_pointer_cast<ServerChannel>(l_channel->entry);
|
|
if(!channel) return command_result{error::vs_critical};
|
|
|
|
auto mgr = serverInstance->databaseHelper()->loadClientPermissionManager(this->server, cldbid);
|
|
{
|
|
auto required_permissions = this->server->calculate_permission(permission::i_client_needed_permission_modify_power, cmd["cldbid"], ClientType::CLIENT_TEAMSPEAK, channel_id);
|
|
ACTION_REQUIRES_PERMISSION(permission::i_client_permission_modify_power, required_permissions, channel_id);
|
|
}
|
|
|
|
command::bulk_parser::PermissionBulksParser<false> pparser{cmd};
|
|
if(!pparser.validate(this->ref(), channel->channelId()))
|
|
return pparser.build_command_result();
|
|
|
|
bool update_view{false};
|
|
for(const auto& ppermission : pparser.iterate_valid_permissions()) {
|
|
ppermission.apply_to_channel(mgr, permission::v2::PermissionUpdateType::delete_value, channel->channelId());
|
|
ppermission.log_update(serverInstance->action_logger()->permission_logger,
|
|
this->getServerId(),
|
|
this->ref(),
|
|
log::PermissionTarget::CLIENT_CHANNEL,
|
|
permission::v2::PermissionUpdateType::delete_value,
|
|
cldbid, "",
|
|
channel->channelId(), channel->name()
|
|
);
|
|
update_view |= ppermission.is_client_view_property();
|
|
}
|
|
|
|
serverInstance->databaseHelper()->saveClientPermissions(this->server, cldbid, mgr);
|
|
|
|
auto onlineClients = this->server->findClientsByCldbId(cldbid);
|
|
if (!onlineClients.empty()) {
|
|
for (const auto &elm : onlineClients) {
|
|
if(elm->update_cached_permissions()) /* update cached calculated permissions */
|
|
elm->sendNeededPermissions(false); /* cached permissions had changed, notify the client */
|
|
|
|
if(elm->currentChannel == channel) {
|
|
elm->updateChannelClientProperties(true, true);
|
|
} else if(update_view) {
|
|
unique_lock client_channel_lock(this->channel_lock);
|
|
|
|
auto elm_channel = elm->currentChannel;
|
|
if(elm_channel) {
|
|
deque<ChannelId> deleted;
|
|
for(const auto& update_entry : elm->channels->update_channel_path(l_channel, this->server->channelTree->findLinkedChannel(elm->currentChannel->channelId()))) {
|
|
if(update_entry.first)
|
|
elm->notifyChannelShow(update_entry.second->channel(), update_entry.second->previous_channel);
|
|
else deleted.push_back(update_entry.second->channelId());
|
|
}
|
|
if(!deleted.empty())
|
|
elm->notifyChannelHide(deleted, false); /* we've locked the tree before */
|
|
}
|
|
}
|
|
|
|
elm->join_state_id++; /* join permission may changed, all channels need to be recalculate dif needed */
|
|
}
|
|
}
|
|
|
|
return pparser.build_command_result();
|
|
}
|
|
|
|
command_result ConnectedClient::handleCommandChannelClientAddPerm(Command &cmd) {
|
|
CMD_REF_SERVER(server_ref);
|
|
CMD_RESET_IDLE;
|
|
CMD_CHK_AND_INC_FLOOD_POINTS(5);
|
|
|
|
auto cldbid = cmd["cldbid"].as<ClientDbId>();
|
|
if (!serverInstance->databaseHelper()->validClientDatabaseId(this->server, cldbid))
|
|
return command_result{error::parameter_invalid, "Invalid client db id"};
|
|
|
|
RESOLVE_CHANNEL_R(cmd["cid"], true);
|
|
auto channel = dynamic_pointer_cast<ServerChannel>(l_channel->entry);
|
|
if(!channel) return command_result{error::vs_critical};
|
|
|
|
auto mgr = serverInstance->databaseHelper()->loadClientPermissionManager(this->server, cldbid);
|
|
auto required_permissions = this->server->calculate_permission(permission::i_client_needed_permission_modify_power, cmd["cldbid"], ClientType::CLIENT_TEAMSPEAK, channel_id);
|
|
ACTION_REQUIRES_PERMISSION(permission::i_client_permission_modify_power, required_permissions, channel_id);
|
|
|
|
command::bulk_parser::PermissionBulksParser<true> pparser{cmd};
|
|
if(!pparser.validate(this->ref(), channel->channelId()))
|
|
return pparser.build_command_result();
|
|
|
|
bool update_view{false};
|
|
for(const auto& ppermission : pparser.iterate_valid_permissions()) {
|
|
ppermission.apply_to_channel(mgr, permission::v2::PermissionUpdateType::set_value, channel->channelId());
|
|
ppermission.log_update(serverInstance->action_logger()->permission_logger,
|
|
this->getServerId(),
|
|
this->ref(),
|
|
log::PermissionTarget::CLIENT_CHANNEL,
|
|
permission::v2::PermissionUpdateType::set_value,
|
|
cldbid, "",
|
|
channel->channelId(), channel->name()
|
|
);
|
|
update_view |= ppermission.is_client_view_property();
|
|
}
|
|
|
|
serverInstance->databaseHelper()->saveClientPermissions(this->server, cldbid, mgr);
|
|
|
|
|
|
auto onlineClients = this->server->findClientsByCldbId(cldbid);
|
|
if (!onlineClients.empty())
|
|
for (const auto &elm : onlineClients) {
|
|
if (elm->update_cached_permissions()) /* update cached calculated permissions */
|
|
elm->sendNeededPermissions(false); /* cached permissions had changed, notify the client */
|
|
|
|
if(elm->currentChannel == channel) {
|
|
elm->updateChannelClientProperties(true, true);
|
|
} else if(update_view) {
|
|
unique_lock client_channel_lock(this->channel_lock);
|
|
|
|
auto elm_channel = elm->currentChannel;
|
|
if(elm_channel) {
|
|
deque<ChannelId> deleted;
|
|
for(const auto& update_entry : elm->channels->update_channel_path(l_channel, this->server->channelTree->findLinkedChannel(elm->currentChannel->channelId()))) {
|
|
if(update_entry.first)
|
|
elm->notifyChannelShow(update_entry.second->channel(), update_entry.second->previous_channel);
|
|
else deleted.push_back(update_entry.second->channelId());
|
|
}
|
|
if(!deleted.empty())
|
|
elm->notifyChannelHide(deleted, false); /* we've locked the tree before */
|
|
}
|
|
}
|
|
elm->join_state_id++; /* join permission may changed, all channels need to be recalculate dif needed */
|
|
}
|
|
|
|
return pparser.build_command_result();
|
|
}
|
|
|
|
|
|
command_result ConnectedClient::handleCommandChannelFind(Command &cmd) {
|
|
CMD_CHK_AND_INC_FLOOD_POINTS(5);
|
|
|
|
string pattern = cmd["pattern"];
|
|
std::transform(pattern.begin(), pattern.end(), pattern.begin(), ::tolower);
|
|
|
|
Command res("");
|
|
int index = 0;
|
|
for (const auto &cl : (this->server ? this->server->channelTree : serverInstance->getChannelTree().get())->channels()) {
|
|
string name = cl->name();
|
|
std::transform(name.begin(), name.end(), name.begin(), ::tolower);
|
|
if (name.find(pattern) != std::string::npos) {
|
|
res[index]["cid"] = cl->channelId();
|
|
res[index]["channel_name"] = cl->name();
|
|
index++;
|
|
}
|
|
}
|
|
if (index == 0) return command_result{error::database_empty_result};
|
|
this->sendCommand(res);
|
|
|
|
return command_result{error::ok};
|
|
}
|
|
|
|
command_result ConnectedClient::handleCommandChannelInfo(Command &cmd) {
|
|
std::shared_ptr<BasicChannel> channel = (this->server ? this->server->channelTree : serverInstance->getChannelTree().get())->findChannel(cmd["cid"].as<ChannelId>());
|
|
if (!channel) return command_result{error::channel_invalid_id, "Cant resolve channel"};
|
|
|
|
Command res("");
|
|
|
|
for (const auto &prop : channel->properties().list_properties(property::FLAG_CHANNEL_VIEW | property::FLAG_CHANNEL_VARIABLE, this->getType() == CLIENT_TEAMSPEAK ? property::FLAG_NEW : (uint16_t) 0))
|
|
res[prop.type().name] = prop.value();
|
|
|
|
res["seconds_empty"] = channel->emptySince();
|
|
res["pid"] = res["cpid"].string();
|
|
this->sendCommand(res);
|
|
|
|
return command_result{error::ok};
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|