Fixed the query client view power

This commit is contained in:
WolverinDEV
2021-04-14 16:00:02 +02:00
parent 6b774d3a80
commit 25b454ad0e
9 changed files with 283 additions and 278 deletions
+120 -90
View File
@@ -1,5 +1,6 @@
#include <cstring>
#include <protocol/buffers.h>
#include "./PermissionCalculator.h"
#include "client/voice/VoiceClient.h"
#include "client/InternalClient.h"
#include "VirtualServer.h"
@@ -111,17 +112,35 @@ bool VirtualServer::unregisterClient(shared_ptr<ConnectedClient> cl, std::string
}
}
if(cl->getType() == ClientType::CLIENT_TEAMSPEAK || cl->getType() == ClientType::CLIENT_WEB)
this->properties()[property::VIRTUALSERVER_LAST_CLIENT_DISCONNECT] = duration_cast<seconds>(system_clock::now().time_since_epoch()).count();
else if(cl->getType() == ClientType::CLIENT_QUERY)
this->properties()[property::VIRTUALSERVER_LAST_QUERY_DISCONNECT] = duration_cast<seconds>(system_clock::now().time_since_epoch()).count();
auto current_time_seconds = std::chrono::duration_cast<seconds>(std::chrono::system_clock::now().time_since_epoch()).count();
switch(cl->getType()) {
case ClientType::CLIENT_TEAMSPEAK:
case ClientType::CLIENT_TEASPEAK:
case ClientType::CLIENT_WEB:
this->properties()[property::VIRTUALSERVER_LAST_CLIENT_DISCONNECT] = current_time_seconds;
break;
case ClientType::CLIENT_QUERY:
this->properties()[property::VIRTUALSERVER_LAST_QUERY_DISCONNECT] = current_time_seconds;
break;
case ClientType::CLIENT_MUSIC:
case ClientType::CLIENT_INTERNAL:
case ClientType::MAX:
default:
break;
}
{
if(!chan_tree_lock.owns_lock())
if(!chan_tree_lock.owns_lock()) {
chan_tree_lock.lock();
}
if(cl->currentChannel) //We dont have to make him invisible if he hasnt even a channel
if(cl->currentChannel) {
//We dont have to make him invisible if he hasnt even a channel
this->client_move(cl, nullptr, nullptr, reason, ViewReasonId::VREASON_SERVER_LEFT, false, chan_tree_lock);
}
}
serverInstance->databaseHelper()->saveClientPermissions(this->ref(), cl->getClientDatabaseId(), cl->clientPermissions);
@@ -175,9 +194,9 @@ void VirtualServer::unregisterInternalClient(std::shared_ptr<ConnectedClient> cl
}
bool VirtualServer::assignDefaultChannel(const shared_ptr<ConnectedClient>& client, bool join) {
std::shared_lock server_channel_lock{this->channel_tree_mutex};
std::shared_ptr<BasicChannel> channel{};
std::unique_lock server_channel_lock{this->channel_tree_mutex};
auto requested_channel_path = client->properties()[property::CLIENT_DEFAULT_CHANNEL].value();
if(!requested_channel_path.empty()) {
if (requested_channel_path[0] == '/' && requested_channel_path.find_first_not_of("0123456789", 1) == std::string::npos) {
@@ -244,12 +263,11 @@ bool VirtualServer::assignDefaultChannel(const shared_ptr<ConnectedClient>& clie
debugMessage(this->getServerId(), "{} Using channel {} as default client channel.", client->getLoggingPrefix(), channel->channelId());
if(join) {
server_channel_lock.unlock();
unique_lock server_channel_w_lock(this->channel_tree_mutex);
this->client_move(client, channel, nullptr, "", ViewReasonId::VREASON_USER_ACTION, false, server_channel_w_lock);
this->client_move(client, channel, nullptr, "", ViewReasonId::VREASON_USER_ACTION, false, server_channel_lock);
} else {
client->currentChannel = channel;
}
return true;
}
@@ -412,10 +430,10 @@ void VirtualServer::delete_channel(shared_ptr<ts::ServerChannel> channel, const
/*
* This method had previously owned the clients command lock but that's not really needed.
* Everything which is related to the server channel tree or the client channel tree should be locked with
* the appropiate mutexes.
* the appropriate mutexes.
*/
void VirtualServer::client_move(
const shared_ptr<ts::server::ConnectedClient> &target,
const shared_ptr<ts::server::ConnectedClient> &target_client,
shared_ptr<ts::BasicChannel> target_channel,
const std::shared_ptr<ts::server::ConnectedClient> &invoker,
const std::string &reason_message,
@@ -427,15 +445,16 @@ void VirtualServer::client_move(
if(!server_channel_write_lock.owns_lock()) {
server_channel_write_lock.unlock();
}
TIMING_STEP(timings, "chan tree l");
if(target->currentChannel == target_channel) {
if(target_client->currentChannel == target_channel) {
return;
}
/* first step: verify thew source and target channel */
auto s_target_channel = dynamic_pointer_cast<ServerChannel>(target_channel);
auto s_source_channel = dynamic_pointer_cast<ServerChannel>(target->currentChannel);
assert(!target->currentChannel || s_source_channel != nullptr);
auto s_source_channel = dynamic_pointer_cast<ServerChannel>(target_client->currentChannel);
assert(!target_client->currentChannel || s_source_channel != nullptr);
std::deque<property::ClientProperties> updated_client_properties{};
if(target_channel) {
@@ -449,147 +468,157 @@ void VirtualServer::client_move(
auto l_source_channel = s_source_channel ? this->channelTree->findLinkedChannel(s_source_channel->channelId()) : nullptr;
TIMING_STEP(timings, "channel res");
/* second step: show the target channel to the client if its not shown and let him subscibe to the channel */
/* second step: show the target channel to the client if its not shown and let him subscribe to the channel */
if(target_channel && notify_client) {
unique_lock client_channel_lock(target->channel_tree_mutex);
std::unique_lock client_channel_lock{target_client->channel_tree_mutex};
bool success = false;
bool success{false};
/* TODO: Use a bunk here and not a notify for every single */
for(const auto& channel : target->channel_tree->show_channel(l_target_channel, success))
target->notifyChannelShow(channel->channel(), channel->previous_channel);
sassert(success);
if(!success)
return;
for(const auto& channel : target_client->channel_tree->show_channel(l_target_channel, success)) {
target_client->notifyChannelShow(channel->channel(), channel->previous_channel);
}
target->subscribeChannel({target_channel}, false, true);
sassert(success);
if(!success) {
return;
}
target_client->subscribeChannel({ target_channel }, false, true);
}
TIMING_STEP(timings, "target show");
if(s_source_channel) {
s_source_channel->unregister_client(target_client);
}
if(target_channel) {
this->forEachClient([&](const shared_ptr<ConnectedClient>& client) {
if (!notify_client && client == target) return;
ClientPermissionCalculator target_client_permissions{&*target_client, target_channel};
auto needed_view_power = target_client_permissions.calculate_permission(permission::i_client_needed_serverquery_view_power);
unique_lock client_channel_lock(client->channel_tree_mutex);
auto chan_target = client->channel_tree->find_channel(target_channel);
/* ct_... is for client channel tree */
this->forEachClient([&](const std::shared_ptr<ConnectedClient>& client) {
if (!notify_client && client == target_client) {
return;
}
if(chan_target) {
auto chan_source = client->channel_tree->find_channel(s_source_channel);
if(chan_source) {
if (chan_target->subscribed || client == target) {
if (client == target || client->isClientVisible(target, false)) {
client->notifyClientMoved(target, s_target_channel, reason_id, reason_message, invoker, false);
bool move_target_client_visible{true};
if(target_client->getType() == ClientType::CLIENT_QUERY) {
auto query_view_power = client->calculate_permission(permission::i_client_serverquery_view_power, target_channel->channelId());
move_target_client_visible = permission::v2::permission_granted(needed_view_power, query_view_power);
}
std::unique_lock client_channel_lock{client->channel_tree_mutex};
auto ct_target_channel = move_target_client_visible ? client->channel_tree->find_channel(target_channel) : nullptr;
if(ct_target_channel) {
auto ct_source_channel = client->channel_tree->find_channel(s_source_channel);
if(ct_source_channel) {
/* Source and target channel are visible for the client. Just a "normal" move. */
if (ct_target_channel->subscribed || client == target_client) {
if (client == target_client || client->isClientVisible(target_client, false)) {
client->notifyClientMoved(target_client, s_target_channel, reason_id, reason_message, invoker, false);
} else {
client->notifyClientEnterView(target, invoker, reason_message, s_target_channel, reason_id, s_source_channel, false);
client->notifyClientEnterView(target_client, invoker, reason_message, s_target_channel, reason_id, s_source_channel, false);
}
} else if(client->isClientVisible(target, false)){
//Client got out of view
client->notifyClientLeftView(target, s_target_channel, reason_id, reason_message.empty() ? string("view left") : reason_message, invoker, false);
} else if(client->isClientVisible(target_client, false)){
/* Client has been moved into an unsubscribed channel */
client->notifyClientLeftView(target_client, s_target_channel, reason_id, reason_message.empty() ? string("view left") : reason_message, invoker, false);
}
} else {
if(client == target && client->getType() != ClientType::CLIENT_INTERNAL && client->getType() != ClientType::CLIENT_MUSIC)
logCritical(this->getServerId(), "{} Client enters visibility twice!", CLIENT_STR_LOG_PREFIX_(client));
//Client entered view
if(chan_target->subscribed)
client->notifyClientEnterView(target, invoker, reason_message, s_target_channel, ViewReasonId::VREASON_USER_ACTION, nullptr, false);
} else if(ct_target_channel->subscribed) {
/* Target client entered the view from an invisible channel */
client->notifyClientEnterView(target_client, invoker, reason_message, s_target_channel, ViewReasonId::VREASON_USER_ACTION, nullptr, false);
}
} else {
/* target channel isn't visible => so client gone out of view */
if(client == target && client->getType() != ClientType::CLIENT_INTERNAL && client->getType() != ClientType::CLIENT_MUSIC)
logCritical(this->getServerId(), "{} Moving own client into a not visible channel! This shall not happen!", CLIENT_STR_LOG_PREFIX_(client));
//Test for in view? (Notify already does but nvm)
if(client->isClientVisible(target, false)){
//Client got out of view
if(reason_id == ViewReasonId::VREASON_USER_ACTION)
client->notifyClientLeftView(target, nullptr, ViewReasonId::VREASON_SERVER_LEFT, reason_message.empty() ? "joined a hidden channel" : reason_message, invoker, false);
else
client->notifyClientLeftView(target, nullptr, ViewReasonId::VREASON_SERVER_LEFT, reason_message.empty() ? "moved to a hidden channel" : reason_message, invoker, false);
if(client->isClientVisible(target_client, false)) {
/* Client has been moved out of view into an invisible channel */
if(reason_id == ViewReasonId::VREASON_USER_ACTION) {
client->notifyClientLeftView(target_client, nullptr, ViewReasonId::VREASON_SERVER_LEFT, reason_message.empty() ? "joined a hidden channel" : reason_message, invoker, false);
} else {
client->notifyClientLeftView(target_client, nullptr, ViewReasonId::VREASON_SERVER_LEFT, reason_message.empty() ? "moved to a hidden channel" : reason_message, invoker, false);
}
}
}
});
if(s_source_channel) {
s_source_channel->unregister_client(target);
}
s_target_channel->register_client(target);
if(auto client{dynamic_pointer_cast<SpeakingClient>(target)}; client) {
s_target_channel->register_client(target_client);
if(auto client{dynamic_pointer_cast<SpeakingClient>(target_client)}; client) {
this->rtc_server().assign_channel(client->rtc_client_id, s_target_channel->rtc_channel_id);
}
if(auto client{dynamic_pointer_cast<VoiceClient>(target)}; client) {
if(auto client{dynamic_pointer_cast<VoiceClient>(target_client)}; client) {
/* Start normal broadcasting, what the client expects */
this->rtc_server().start_broadcast_audio(client->rtc_client_id, 1);
}
} else {
/* client left the server */
if(target->currentChannel) {
if(target_client->currentChannel) {
for(const auto& client : this->getClients()) {
if(!client || client == target)
if(!client || client == target_client)
continue;
unique_lock client_channel_lock(client->channel_tree_mutex);
if(client->isClientVisible(target, false))
client->notifyClientLeftView(target, nullptr, reason_id, reason_message, invoker, false);
if(client->isClientVisible(target_client, false)) {
client->notifyClientLeftView(target_client, nullptr, reason_id, reason_message, invoker, false);
}
}
s_source_channel->unregister_client(target);
if(auto client{dynamic_pointer_cast<SpeakingClient>(target)}; client) {
if(auto client{dynamic_pointer_cast<SpeakingClient>(target_client)}; client) {
this->rtc_server().assign_channel(client->rtc_client_id, 0);
}
}
}
TIMING_STEP(timings, "notify view");
target->currentChannel = target_channel;
target_client->currentChannel = target_channel;
/* third step: update stuff for the client (remember: the client cant execute anything at the moment!) */
unique_lock client_channel_lock{target->channel_tree_mutex};
unique_lock client_channel_lock{target_client->channel_tree_mutex};
TIMING_STEP(timings, "lock own tr");
if (s_source_channel) {
s_source_channel->properties()[property::CHANNEL_LAST_LEFT] = chrono::duration_cast<chrono::milliseconds>(chrono::system_clock::now().time_since_epoch()).count();
this->group_manager()->assignments().cleanup_temporary_channel_assignment(target->getClientDatabaseId(),
s_source_channel->channelId());
s_source_channel->properties()[property::CHANNEL_LAST_LEFT] = std::chrono::duration_cast<chrono::milliseconds>(std::chrono::system_clock::now().time_since_epoch()).count();
this->group_manager()->assignments().cleanup_temporary_channel_assignment(target_client->getClientDatabaseId(), s_source_channel->channelId());
auto update = target->properties()[property::CLIENT_IS_TALKER].as_or<bool>(false) ||
target->properties()[property::CLIENT_TALK_REQUEST].as_or<int64_t>(0) > 0;
if(update) {
target->properties()[property::CLIENT_IS_TALKER] = 0;
target->properties()[property::CLIENT_TALK_REQUEST] = 0;
target->properties()[property::CLIENT_TALK_REQUEST_MSG] = "";
if(target_client->properties()[property::CLIENT_IS_TALKER].update_value("0")) {
updated_client_properties.push_back(property::CLIENT_IS_TALKER);
}
if(target_client->properties()[property::CLIENT_TALK_REQUEST].update_value("0")) {
updated_client_properties.push_back(property::CLIENT_TALK_REQUEST);
}
if(target_client->properties()[property::CLIENT_TALK_REQUEST_MSG].update_value("")) {
updated_client_properties.push_back(property::CLIENT_TALK_REQUEST_MSG);
}
TIMING_STEP(timings, "src chan up");
}
if (s_target_channel) {
target->task_update_needed_permissions.enqueue();
target->task_update_displayed_groups.enqueue();
target_client->task_update_needed_permissions.enqueue();
target_client->task_update_displayed_groups.enqueue();
TIMING_STEP(timings, "perm gr upd");
if(s_source_channel) {
deque<ChannelId> deleted;
for(const auto& channel : target->channel_tree->test_channel(l_source_channel, l_target_channel)) {
for(const auto& channel : target_client->channel_tree->test_channel(l_source_channel, l_target_channel)) {
deleted.push_back(channel->channelId());
}
if(!deleted.empty()) {
target->notifyChannelHide(deleted, false);
target_client->notifyChannelHide(deleted, false);
}
auto i_source_channel = s_source_channel->channelId();
if(std::find(deleted.begin(), deleted.end(), i_source_channel) == deleted.end()) {
auto source_channel_sub_power = target->calculate_permission(permission::i_channel_subscribe_power, i_source_channel);
auto source_channel_sub_power = target_client->calculate_permission(permission::i_channel_subscribe_power, i_source_channel);
if(!s_source_channel->permission_granted(permission::i_channel_needed_subscribe_power, source_channel_sub_power, false)) {
auto source_channel_sub_power_ignore = target->calculate_permission(permission::b_channel_ignore_subscribe_power, i_source_channel);
auto source_channel_sub_power_ignore = target_client->calculate_permission(permission::b_channel_ignore_subscribe_power, i_source_channel);
if(!permission::v2::permission_granted(1, source_channel_sub_power_ignore)) {
logTrace(this->serverId, "Force unsubscribing of client {} for channel {}/{}. (Channel switch and no permissions)",
CLIENT_STR_LOG_PREFIX_(target), s_source_channel->name(),
CLIENT_STR_LOG_PREFIX_(target_client), s_source_channel->name(),
i_source_channel
);
target->unsubscribeChannel({s_source_channel}, false); //Unsubscribe last channel (hasn't permissions)
target_client->unsubscribeChannel({ s_source_channel }, false); //Unsubscribe last channel (hasn't permissions)
}
}
}
@@ -597,12 +626,13 @@ void VirtualServer::client_move(
}
}
client_channel_lock.unlock();
/* both methods lock if they require stuff */
this->notifyClientPropertyUpdates(target, updated_client_properties, s_source_channel ? true : false);
this->notifyClientPropertyUpdates(target_client, updated_client_properties, s_source_channel ? true : false);
TIMING_STEP(timings, "notify cpro");
if(s_target_channel) {
target->updateChannelClientProperties(false, s_source_channel ? true : false);
target_client->updateChannelClientProperties(false, s_source_channel ? true : false);
TIMING_STEP(timings, "notify_t_pr");
}
debugMessage(this->getServerId(), "{} Client move timings: {}", CLIENT_STR_LOG_PREFIX_(target), TIMING_FINISH(timings));
debugMessage(this->getServerId(), "{} Client move timings: {}", CLIENT_STR_LOG_PREFIX_(target_client), TIMING_FINISH(timings));
}