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

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};
}