#include #include #include #include "../../build.h" #include "../ConnectedClient.h" #include "../InternalClient.h" #include "src/server/file/LocalFileServer.h" #include "../../server/VoiceServer.h" #include "../voice/VoiceClient.h" #include "PermissionManager.h" #include "../../InstanceHandler.h" #include "../../server/QueryServer.h" #include "../file/FileClient.h" #include "../music/MusicClient.h" #include "../query/QueryClient.h" #include "../../weblist/WebListManager.h" #include "../../manager/ConversationManager.h" #include "../../manager/PermissionNameMapper.h" #include #include "helpers.h" #include "./bulk_parsers.h" #include #include #include #include #include #include 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() : cmd[index]["permsid"].as())}; \ //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(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> 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()); if(!local_channel) return command_result{error::channel_invalid_id, "Cant resolve channel"}; auto channel = this->server->channelTree->findChannel(cmd[index]["cid"].as()); 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> channels; for(int index = 0; index < cmd.bulkCount(); index++) { auto channel = this->server->channelTree->findChannel(cmd["cid"].as()); 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(); if(cmd["type"].as() == GroupType::GROUP_TYPE_NORMAL && !this->server) return command_result{error::parameter_invalid, "You cant create normal channel groups on the template server"}; 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(), cmd["name"].string()); if (group) { group->permissions()->set_permission(permission::b_group_is_permanent, {1, 0}, permission::v2::set_value, permission::v2::do_nothing); if(this->server) this->server->forEachClient([](shared_ptr 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(); 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; 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() != 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"}; global_update = !this->server || !group_manager->isLocalGroup(target_group); } else { //Copy a new group auto target_type = cmd["type"].as(); { auto result = group_type_modificable(target_type); if(result != permission::undefined) return command_result{result}; } if(!ref_server && target_type == GroupType::GROUP_TYPE_NORMAL) return command_result{error::parameter_invalid, "You cant create normal groups on the template server!"}; if(!group_manager->findGroup(GroupTarget::GROUPTARGET_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"}; 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>{this->server})) if(server) server->forEachClient([](shared_ptr 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()); 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); group_manager->renameGroup(channel_group, cmd["name"].string()); if(this->server) this->server->forEachClient([](shared_ptr 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()); 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!"}; if (!cmd["force"].as()) if (!group_manager->listGroupMembers(channel_group, false).empty()) return command_result{error::database_empty_result, "group not empty!"}; if (group_manager->deleteGroup(channel_group) && this->server) { this->server->forEachClient([&](shared_ptr 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->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() : 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 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() > 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()}); } 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() > 0) { query += " AND `cldbid` = :cldbid"; variables.push_back({":cldbid", cmd["cldbid"].as()}); } if(cmd[0].has("cid") && cmd["cid"].as() > 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()}); } 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()); 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()); 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 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); updateList |= ppermission.is_group_property(); } if(updateList) channelGroup->apply_properties_from_permissions(); if(this->server) { if(updateList) this->server->forEachClient([](shared_ptr cl) { cl->notifyChannelGroupList(); }); this->server->forEachClient([channelGroup](shared_ptr 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()); 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 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); updateList |= ppermission.is_group_property(); } if(updateList) channelGroup->apply_properties_from_permissions(); if(this->server) { if(updateList) this->server->forEachClient([](shared_ptr cl) { cl->notifyChannelGroupList(); }); this->server->forEachClient([channelGroup](shared_ptr 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 parent = nullptr; std::shared_ptr 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() != 0 && cmd["cpid"].as() != -1) { parent = target_tree->findLinkedChannel(cmd["cpid"].as()); 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(); 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()) test_permission(1, permission::b_channel_create_permanent); else if (cmd[0]["channel_flag_semi_permanent"].as()) 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() && !this->server) return command_result{error::parameter_invalid, "You can only create a permanent channel"}; if (cmd[0]["channel_flag_default"].as()) test_permission(1, permission::b_channel_create_with_default); if (cmd[0]["channel_flag_password"].as()) 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())) { test_permission(1, permission::b_channel_create_with_maxclients); if(!cmd[0]["channel_flag_permanent"].as() && !cmd[0]["channel_flag_semi_permanent"].as()) { 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(); 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() : 0UL; if(delete_delay == 0) { if(this->server) cmd["channel_delete_delay"] = this->server->properties()[property::VIRTUALSERVER_CHANNEL_TEMP_DELETE_DELAY_DEFAULT].as(); else cmd["channel_delete_delay"] = 0; } else { test_permission(cmd["channel_delete_delay"].as(), 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()) created_perm++; else if(channel->properties()[property::CHANNEL_FLAG_SEMI_PERMANENT].as()) created_semi++; else created_tmp++; } } if(this->server && created_total >= this->server->properties()[property::VIRTUALSERVER_MAX_CHANNELS].as()) 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()) { 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()) { 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(); //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(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(), parent ? dynamic_pointer_cast(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) 0, cmd["channel_name"].as()); } 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()) { 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 ); } for (auto &prop : cmd[0].keys()) { if (prop == "channel_flag_default") continue; if (prop == "channel_order") continue; if (prop == "channel_name") continue; if (prop == "cpid") continue; if (prop == "cid") continue; const auto &property = property::find(prop); if(property == property::CHANNEL_UNDEFINED) { logError(this->getServerId(), "Client " + this->getDisplayName() + " tried to change a not existing channel property " + prop); continue; } if(!property.validate_input(cmd[prop].as())) { logError(this->getServerId(), "Client " + this->getDisplayName() + " tried to change a property to an invalid value. (Value: '" + cmd[prop].as() + "', Property: '" + std::string{property.name} + "')"); continue; } created_channel->properties()[property] = cmd[prop].as(); } 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& 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); } 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(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); } else { auto channel_ids = channel_tree->deleteChannelRoot(channel); this->notifyChannelDeleted(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(l_channel->entry); assert(channel); if(channel->deleted) { /* channel gets already removed */ return command_result{error::ok}; } std::deque 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(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())) { 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() == cmd[key].as()) 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()) 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); } 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()) 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()) { 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()) { 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(), 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(); 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") { ACTION_REQUIRES_PERMISSION(permission::b_channel_create_modify_conversation_private, 1, channel_id); } 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); } 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()) 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()) { 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() : 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()) || 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() != -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() != -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()) 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() < 0; keys.push_back(&property::describe(property::CHANNEL_FLAG_MAXCLIENTS_UNLIMITED)); } if(cmd["channel_flag_maxclients_unlimited"].as() && cmd["channel_maxclients"].as() != -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() && 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{cmd["channel_maxfamilyclients"].as()} : std::nullopt; //auto channel_flag_maxfamilyclients_unlimited = cmd[0].has("channel_flag_maxfamilyclients_unlimited") ? std::optional{cmd["channel_flag_maxfamilyclients_unlimited"].as()} : std::nullopt; //auto channel_flag_maxfamilyclients_inherited = cmd[0].has("channel_flag_maxfamilyclients_inherited") ? std::optional{cmd["channel_flag_maxfamilyclients_inherited"].as()} : 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()) { 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()) { 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(); if(flag_inherited) cmd["channel_flag_maxfamilyclients_unlimited"] = false; else cmd["channel_flag_maxfamilyclients_unlimited"] = cmd["channel_maxfamilyclients"].as() < 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(); if(flag_unlimited) cmd["channel_flag_maxfamilyclients_inherited"] = false; else cmd["channel_flag_maxfamilyclients_inherited"] = cmd["channel_maxfamilyclients"].as() < 0; } /* final checkup */ if(cmd["channel_flag_maxfamilyclients_inherited"].as() && cmd["channel_flag_maxfamilyclients_unlimited"].as()) return command_result{error::channel_invalid_flags}; /* both at the same time are not possible */ if(cmd["channel_flag_maxfamilyclients_inherited"].as() && cmd["channel_maxfamilyclients"].as() != -1) return command_result{error::channel_invalid_flags}; /* flag inherited required max users to be -1 */ if(cmd["channel_flag_maxfamilyclients_unlimited"].as() && cmd["channel_maxfamilyclients"].as() != -1) return command_result{error::channel_invalid_flags}; /* flag unlimited required max users to be -1 */ if(cmd["channel_maxfamilyclients"].as() != -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 old_default_channel; deque> 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 */ if (!channel_tree->change_order(channel, cmd[std::string{key->name}])) return command_result{error::channel_invalid_order, "Can't change order id"}; 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& cl) { unique_lock client_channel_lock(cl->channel_lock); auto actions = cl->channels->change_order(l_channel, parent, previous); std::deque 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 } }); } } 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()) { old_default_channel = nullptr; continue; } channel_tree->setDefaultChannel(channel); deque> updated_channels; shared_ptr 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& 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> 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(), 0}, permission::v2::set_value, permission::v2::do_nothing); } channel->properties()[*key] = cmd[std::string{key->name}].string(); } if(this->server) { vector 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& 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(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 l_order; if(cmd[0].has("order")) { l_order = channel_tree->findLinkedChannel(cmd["order"]); if (!l_order && cmd["order"].as() != 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(l_parent->entry) : nullptr; auto order = l_order ? dynamic_pointer_cast(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}; if (channel->parent() != parent) ACTION_REQUIRES_PERMISSION(permission::b_channel_modify_parent, 1, channel_id); if ((order ? order->channelId() : 0) != channel->channelOrder()) 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> 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(current_channel->parent()))); } if(this->server) { auto self_rev = this->ref(); this->server->forEachClient([&](const shared_ptr& 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 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(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(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 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; for(const auto& ppermission : pparser.iterate_valid_permissions()) { ppermission.apply_to(permission_manager, permission::v2::PermissionUpdateType::set_value); 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 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(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 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; for(const auto& ppermission : pparser.iterate_valid_permissions()) { ppermission.apply_to(permission_manager, permission::v2::PermissionUpdateType::delete_value); 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 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(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()); 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(); 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(); 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(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 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()); 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 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(); 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(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 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()); 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 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 channel = (this->server ? this->server->channelTree : serverInstance->getChannelTree().get())->findChannel(cmd["cid"].as()); 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}; }