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

View File

@ -25,6 +25,7 @@ namespace ts::server {
*/ */
class ClientPermissionCalculator { class ClientPermissionCalculator {
public: public:
/* When providing the pointer to the channel the channel tree **should** not be locked in any way! */
explicit ClientPermissionCalculator(DataClient* /* client */, const std::shared_ptr<BasicChannel>& /* target channel */); explicit ClientPermissionCalculator(DataClient* /* client */, const std::shared_ptr<BasicChannel>& /* target channel */);
explicit ClientPermissionCalculator(DataClient* /* client */, ChannelId /* target channel id */); explicit ClientPermissionCalculator(DataClient* /* client */, ChannelId /* target channel id */);
explicit ClientPermissionCalculator( explicit ClientPermissionCalculator(

View File

@ -1,5 +1,6 @@
#include <cstring> #include <cstring>
#include <protocol/buffers.h> #include <protocol/buffers.h>
#include "./PermissionCalculator.h"
#include "client/voice/VoiceClient.h" #include "client/voice/VoiceClient.h"
#include "client/InternalClient.h" #include "client/InternalClient.h"
#include "VirtualServer.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) auto current_time_seconds = std::chrono::duration_cast<seconds>(std::chrono::system_clock::now().time_since_epoch()).count();
this->properties()[property::VIRTUALSERVER_LAST_CLIENT_DISCONNECT] = duration_cast<seconds>(system_clock::now().time_since_epoch()).count(); switch(cl->getType()) {
else if(cl->getType() == ClientType::CLIENT_QUERY) case ClientType::CLIENT_TEAMSPEAK:
this->properties()[property::VIRTUALSERVER_LAST_QUERY_DISCONNECT] = duration_cast<seconds>(system_clock::now().time_since_epoch()).count(); 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(); 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); this->client_move(cl, nullptr, nullptr, reason, ViewReasonId::VREASON_SERVER_LEFT, false, chan_tree_lock);
}
} }
serverInstance->databaseHelper()->saveClientPermissions(this->ref(), cl->getClientDatabaseId(), cl->clientPermissions); 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) { 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::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(); auto requested_channel_path = client->properties()[property::CLIENT_DEFAULT_CHANNEL].value();
if(!requested_channel_path.empty()) { if(!requested_channel_path.empty()) {
if (requested_channel_path[0] == '/' && requested_channel_path.find_first_not_of("0123456789", 1) == std::string::npos) { 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()); debugMessage(this->getServerId(), "{} Using channel {} as default client channel.", client->getLoggingPrefix(), channel->channelId());
if(join) { if(join) {
server_channel_lock.unlock(); this->client_move(client, channel, nullptr, "", ViewReasonId::VREASON_USER_ACTION, false, server_channel_lock);
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);
} else { } else {
client->currentChannel = channel; client->currentChannel = channel;
} }
return true; 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. * 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 * 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( 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, shared_ptr<ts::BasicChannel> target_channel,
const std::shared_ptr<ts::server::ConnectedClient> &invoker, const std::shared_ptr<ts::server::ConnectedClient> &invoker,
const std::string &reason_message, const std::string &reason_message,
@ -427,15 +445,16 @@ void VirtualServer::client_move(
if(!server_channel_write_lock.owns_lock()) { if(!server_channel_write_lock.owns_lock()) {
server_channel_write_lock.unlock(); server_channel_write_lock.unlock();
} }
TIMING_STEP(timings, "chan tree l"); TIMING_STEP(timings, "chan tree l");
if(target->currentChannel == target_channel) { if(target_client->currentChannel == target_channel) {
return; return;
} }
/* first step: verify thew source and target channel */ /* first step: verify thew source and target channel */
auto s_target_channel = dynamic_pointer_cast<ServerChannel>(target_channel); auto s_target_channel = dynamic_pointer_cast<ServerChannel>(target_channel);
auto s_source_channel = dynamic_pointer_cast<ServerChannel>(target->currentChannel); auto s_source_channel = dynamic_pointer_cast<ServerChannel>(target_client->currentChannel);
assert(!target->currentChannel || s_source_channel != nullptr); assert(!target_client->currentChannel || s_source_channel != nullptr);
std::deque<property::ClientProperties> updated_client_properties{}; std::deque<property::ClientProperties> updated_client_properties{};
if(target_channel) { 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; auto l_source_channel = s_source_channel ? this->channelTree->findLinkedChannel(s_source_channel->channelId()) : nullptr;
TIMING_STEP(timings, "channel res"); 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) { 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 */ /* 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)) for(const auto& channel : target_client->channel_tree->show_channel(l_target_channel, success)) {
target->notifyChannelShow(channel->channel(), channel->previous_channel); target_client->notifyChannelShow(channel->channel(), channel->previous_channel);
sassert(success); }
if(!success)
return;
target->subscribeChannel({target_channel}, false, true); sassert(success);
if(!success) {
return;
}
target_client->subscribeChannel({ target_channel }, false, true);
} }
TIMING_STEP(timings, "target show"); TIMING_STEP(timings, "target show");
if(s_source_channel) {
s_source_channel->unregister_client(target_client);
}
if(target_channel) { if(target_channel) {
this->forEachClient([&](const shared_ptr<ConnectedClient>& client) { ClientPermissionCalculator target_client_permissions{&*target_client, target_channel};
if (!notify_client && client == target) return; 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); /* ct_... is for client channel tree */
auto chan_target = client->channel_tree->find_channel(target_channel); this->forEachClient([&](const std::shared_ptr<ConnectedClient>& client) {
if (!notify_client && client == target_client) {
return;
}
if(chan_target) { bool move_target_client_visible{true};
auto chan_source = client->channel_tree->find_channel(s_source_channel); if(target_client->getType() == ClientType::CLIENT_QUERY) {
if(chan_source) { auto query_view_power = client->calculate_permission(permission::i_client_serverquery_view_power, target_channel->channelId());
if (chan_target->subscribed || client == target) { move_target_client_visible = permission::v2::permission_granted(needed_view_power, query_view_power);
if (client == target || client->isClientVisible(target, false)) { }
client->notifyClientMoved(target, s_target_channel, reason_id, reason_message, invoker, false);
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 { } 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)){ } else if(client->isClientVisible(target_client, false)){
//Client got out of view /* Client has been moved into an unsubscribed channel */
client->notifyClientLeftView(target, s_target_channel, reason_id, reason_message.empty() ? string("view left") : reason_message, invoker, false); client->notifyClientLeftView(target_client, s_target_channel, reason_id, reason_message.empty() ? string("view left") : reason_message, invoker, false);
} }
} else { } else if(ct_target_channel->subscribed) {
if(client == target && client->getType() != ClientType::CLIENT_INTERNAL && client->getType() != ClientType::CLIENT_MUSIC) /* Target client entered the view from an invisible channel */
logCritical(this->getServerId(), "{} Client enters visibility twice!", CLIENT_STR_LOG_PREFIX_(client)); client->notifyClientEnterView(target_client, invoker, reason_message, s_target_channel, ViewReasonId::VREASON_USER_ACTION, nullptr, false);
//Client entered view
if(chan_target->subscribed)
client->notifyClientEnterView(target, invoker, reason_message, s_target_channel, ViewReasonId::VREASON_USER_ACTION, nullptr, false);
} }
} else { } else {
/* target channel isn't visible => so client gone out of view */ if(client->isClientVisible(target_client, false)) {
if(client == target && client->getType() != ClientType::CLIENT_INTERNAL && client->getType() != ClientType::CLIENT_MUSIC) /* Client has been moved out of view into an invisible channel */
logCritical(this->getServerId(), "{} Moving own client into a not visible channel! This shall not happen!", CLIENT_STR_LOG_PREFIX_(client)); if(reason_id == ViewReasonId::VREASON_USER_ACTION) {
//Test for in view? (Notify already does but nvm) client->notifyClientLeftView(target_client, nullptr, ViewReasonId::VREASON_SERVER_LEFT, reason_message.empty() ? "joined a hidden channel" : reason_message, invoker, false);
} else {
if(client->isClientVisible(target, false)){ client->notifyClientLeftView(target_client, nullptr, ViewReasonId::VREASON_SERVER_LEFT, reason_message.empty() ? "moved to a hidden channel" : reason_message, invoker, 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(s_source_channel) { s_target_channel->register_client(target_client);
s_source_channel->unregister_client(target); if(auto client{dynamic_pointer_cast<SpeakingClient>(target_client)}; client) {
}
s_target_channel->register_client(target);
if(auto client{dynamic_pointer_cast<SpeakingClient>(target)}; client) {
this->rtc_server().assign_channel(client->rtc_client_id, s_target_channel->rtc_channel_id); 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 */ /* Start normal broadcasting, what the client expects */
this->rtc_server().start_broadcast_audio(client->rtc_client_id, 1); this->rtc_server().start_broadcast_audio(client->rtc_client_id, 1);
} }
} else { } else {
/* client left the server */ /* client left the server */
if(target->currentChannel) { if(target_client->currentChannel) {
for(const auto& client : this->getClients()) { for(const auto& client : this->getClients()) {
if(!client || client == target) if(!client || client == target_client)
continue; continue;
unique_lock client_channel_lock(client->channel_tree_mutex); unique_lock client_channel_lock(client->channel_tree_mutex);
if(client->isClientVisible(target, false)) if(client->isClientVisible(target_client, false)) {
client->notifyClientLeftView(target, nullptr, reason_id, reason_message, invoker, 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)}; client) {
if(auto client{dynamic_pointer_cast<SpeakingClient>(target)}; client) {
this->rtc_server().assign_channel(client->rtc_client_id, 0); this->rtc_server().assign_channel(client->rtc_client_id, 0);
} }
} }
} }
TIMING_STEP(timings, "notify view"); 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!) */ /* 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"); TIMING_STEP(timings, "lock own tr");
if (s_source_channel) { 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(); 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->getClientDatabaseId(), this->group_manager()->assignments().cleanup_temporary_channel_assignment(target_client->getClientDatabaseId(), s_source_channel->channelId());
s_source_channel->channelId());
auto update = target->properties()[property::CLIENT_IS_TALKER].as_or<bool>(false) || if(target_client->properties()[property::CLIENT_IS_TALKER].update_value("0")) {
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] = "";
updated_client_properties.push_back(property::CLIENT_IS_TALKER); 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); 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); updated_client_properties.push_back(property::CLIENT_TALK_REQUEST_MSG);
} }
TIMING_STEP(timings, "src chan up"); TIMING_STEP(timings, "src chan up");
} }
if (s_target_channel) { if (s_target_channel) {
target->task_update_needed_permissions.enqueue(); target_client->task_update_needed_permissions.enqueue();
target->task_update_displayed_groups.enqueue(); target_client->task_update_displayed_groups.enqueue();
TIMING_STEP(timings, "perm gr upd"); TIMING_STEP(timings, "perm gr upd");
if(s_source_channel) { if(s_source_channel) {
deque<ChannelId> deleted; 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()); deleted.push_back(channel->channelId());
} }
if(!deleted.empty()) { if(!deleted.empty()) {
target->notifyChannelHide(deleted, false); target_client->notifyChannelHide(deleted, false);
} }
auto i_source_channel = s_source_channel->channelId(); auto i_source_channel = s_source_channel->channelId();
if(std::find(deleted.begin(), deleted.end(), i_source_channel) == deleted.end()) { 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)) { 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)) { 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)", 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 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(); client_channel_lock.unlock();
/* both methods lock if they require stuff */ /* 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"); TIMING_STEP(timings, "notify cpro");
if(s_target_channel) { 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"); 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));
} }

View File

@ -191,9 +191,15 @@ namespace ts {
bool notifyServerEdited(std::shared_ptr<ConnectedClient>, std::deque<std::string> keys); bool notifyServerEdited(std::shared_ptr<ConnectedClient>, std::deque<std::string> keys);
bool notifyClientPropertyUpdates(std::shared_ptr<ConnectedClient>, const std::deque<const property::PropertyDescription*>& keys, bool selfNotify = true); /* execute only with at least channel tree read lock! */ bool notifyClientPropertyUpdates(std::shared_ptr<ConnectedClient>, const std::deque<const property::PropertyDescription*>& keys, bool selfNotify = true); /* execute only with at least channel tree read lock! */
inline bool notifyClientPropertyUpdates(const std::shared_ptr<ConnectedClient>& client, const std::deque<property::ClientProperties>& keys, bool selfNotify = true) { inline bool notifyClientPropertyUpdates(const std::shared_ptr<ConnectedClient>& client, const std::deque<property::ClientProperties>& keys, bool selfNotify = true) {
if(keys.empty()) return false; if(keys.empty()) {
return false;
}
std::deque<const property::PropertyDescription*> _keys{}; std::deque<const property::PropertyDescription*> _keys{};
for(const auto& key : keys) _keys.push_back(&property::describe(key)); for(const auto& key : keys) {
_keys.push_back(&property::describe(key));
}
return this->notifyClientPropertyUpdates(client, _keys, selfNotify); return this->notifyClientPropertyUpdates(client, _keys, selfNotify);
}; };

View File

@ -335,7 +335,21 @@ void ConnectedClient::subscribeChannel(const std::deque<std::shared_ptr<BasicCha
for(const auto& target_channel : subscribed_channels) { for(const auto& target_channel : subscribed_channels) {
/* getClientsByChannel() does not acquire the server channel tree mutex */ /* getClientsByChannel() does not acquire the server channel tree mutex */
auto channel_clients = ref_server->getClientsByChannel(target_channel); auto channel_clients = ref_server->getClientsByChannel(target_channel);
visible_clients.insert(visible_clients.end(), channel_clients.begin(), channel_clients.end());
auto target_view_power = this->calculate_permission(permission::i_client_serverquery_view_power, target_channel->channelId());
for(const auto& client : channel_clients) {
if(client->getType() == ClientType::CLIENT_QUERY) {
if(!target_view_power.has_power()) {
continue;
}
if(!permission::v2::permission_granted(client->calculate_permission(permission::i_client_needed_serverquery_view_power, target_channel->channelId()), target_view_power)) {
continue;
}
}
visible_clients.push_back(client);
}
} }
if(!visible_clients.empty()) { if(!visible_clients.empty()) {

View File

@ -586,27 +586,32 @@ bool ConnectedClient::notifyChannelHide(const std::deque<ChannelId> &channel_ids
} }
bool ConnectedClient::notifyChannelShow(const std::shared_ptr<ts::BasicChannel> &channel, ts::ChannelId orderId) { bool ConnectedClient::notifyChannelShow(const std::shared_ptr<ts::BasicChannel> &channel, ts::ChannelId orderId) {
if(!channel) if(!channel) {
return false; return false;
}
auto result = false; auto result = false;
if(this->getType() == ClientType::CLIENT_TEAMSPEAK) { //Voice hasn't that event if(this->getType() == ClientType::CLIENT_TEAMSPEAK) {
/* The TeamSpeak 3 client dosn't know about hidden channels */
result = this->notifyChannelCreate(channel, orderId, this->server->serverRoot); result = this->notifyChannelCreate(channel, orderId, this->server->serverRoot);
} else { } else {
Command notify("notifychannelshow"); Command notify("notifychannelshow");
for (auto &prop : channel->properties()->list_properties(property::FLAG_CHANNEL_VARIABLE | property::FLAG_CHANNEL_VIEW, this->getType() == CLIENT_TEAMSPEAK ? property::FLAG_NEW : (uint16_t) 0)) { for (auto &prop : channel->properties()->list_properties(property::FLAG_CHANNEL_VARIABLE | property::FLAG_CHANNEL_VIEW, (uint16_t) 0)) {
if(prop.type() == property::CHANNEL_ORDER) { if(prop.type() == property::CHANNEL_ORDER) {
notify[prop.type().name] = orderId; notify[prop.type().name] = orderId;
} else if(prop.type() == property::CHANNEL_DESCRIPTION) { } else if(prop.type() == property::CHANNEL_DESCRIPTION) {
continue; continue;
} } else {
else
notify[prop.type().name] = prop.value(); notify[prop.type().name] = prop.value();
}
} }
this->sendCommand(notify); this->sendCommand(notify);
} }
if(result && this->subscribeToAll)
if(result && this->subscribeToAll) {
this->subscribeChannel({channel}, false, true); this->subscribeChannel({channel}, false, true);
}
return true; return true;
} }

View File

@ -618,7 +618,7 @@ void QueryClient::disconnect_from_virtual_server(const std::string& reason) {
auto old_server = std::exchange(this->server, nullptr); auto old_server = std::exchange(this->server, nullptr);
if(old_server) { if(old_server) {
{ {
std::unique_lock tree_lock(old_server->channel_tree_mutex); std::unique_lock tree_lock{old_server->channel_tree_mutex};
if(this->currentChannel) { if(this->currentChannel) {
old_server->client_move(this->ref(), nullptr, nullptr, "", ViewReasonId::VREASON_USER_ACTION, false, tree_lock); old_server->client_move(this->ref(), nullptr, nullptr, "", ViewReasonId::VREASON_USER_ACTION, false, tree_lock);
} }
@ -626,12 +626,12 @@ void QueryClient::disconnect_from_virtual_server(const std::string& reason) {
old_server->unregisterClient(this->ref(), reason, tree_lock); old_server->unregisterClient(this->ref(), reason, tree_lock);
} }
{
std::lock_guard channel_lock{this->channel_tree_mutex};
this->channel_tree->reset();
this->currentChannel = nullptr;
}
this->loadDataForCurrentServer(); this->loadDataForCurrentServer();
} }
{
std::lock_guard channel_lock{this->channel_tree_mutex};
this->channel_tree->reset();
this->currentChannel = nullptr;
}
} }

View File

@ -189,8 +189,6 @@ namespace ts::server {
command_result handleCommandServerSelect(Command &); command_result handleCommandServerSelect(Command &);
command_result handleCommandServerInfo(Command&); command_result handleCommandServerInfo(Command&);
command_result handleCommandChannelList(Command&); command_result handleCommandChannelList(Command&);
command_result handleCommandJoin(Command&);
command_result handleCommandLeft(Command&);
command_result handleCommandServerList(Command&); command_result handleCommandServerList(Command&);
command_result handleCommandServerCreate(Command&); command_result handleCommandServerCreate(Command&);
@ -209,7 +207,6 @@ namespace ts::server {
command_result handleCommandServerIdGetByPort(Command&); command_result handleCommandServerIdGetByPort(Command&);
command_result handleCommandServerSnapshotDeploy(Command&);
command_result handleCommandServerSnapshotDeployNew(const command_parser&); command_result handleCommandServerSnapshotDeployNew(const command_parser&);
command_result handleCommandServerSnapshotCreate(Command&); command_result handleCommandServerSnapshotCreate(Command&);
command_result handleCommandServerProcessStop(Command&); command_result handleCommandServerProcessStop(Command&);

View File

@ -123,10 +123,6 @@ command_result QueryClient::handleCommand(Command& cmd) {
return this->handleCommandLogin(cmd); return this->handleCommandLogin(cmd);
case string_hash("logout"): case string_hash("logout"):
return this->handleCommandLogout(cmd); return this->handleCommandLogout(cmd);
case string_hash("join"):
return this->handleCommandJoin(cmd);
case string_hash("left"):
return this->handleCommandLeft(cmd);
case string_hash("globalmessage"): case string_hash("globalmessage"):
case string_hash("gm"): case string_hash("gm"):
return this->handleCommandGlobalMessage(cmd); return this->handleCommandGlobalMessage(cmd);
@ -151,9 +147,6 @@ command_result QueryClient::handleCommand(Command& cmd) {
case string_hash("bindinglist"): case string_hash("bindinglist"):
return this->handleCommandBindingList(cmd); return this->handleCommandBindingList(cmd);
case string_hash("serversnapshotdeploy"): { case string_hash("serversnapshotdeploy"): {
#if 0
return this->handleCommandServerSnapshotDeploy(cmd);
#else
auto cmd_str = cmd.build(); auto cmd_str = cmd.build();
ts::command_parser parser{cmd_str}; ts::command_parser parser{cmd_str};
if(!parser.parse(true)) { if(!parser.parse(true)) {
@ -161,7 +154,6 @@ command_result QueryClient::handleCommand(Command& cmd) {
} }
return this->handleCommandServerSnapshotDeployNew(parser); return this->handleCommandServerSnapshotDeployNew(parser);
#endif
} }
case string_hash("serversnapshotcreate"): case string_hash("serversnapshotcreate"):
return this->handleCommandServerSnapshotCreate(cmd); return this->handleCommandServerSnapshotCreate(cmd);
@ -260,10 +252,13 @@ command_result QueryClient::handleCommandLogin(Command& cmd) {
auto target_server = this->server; /* keep the server alive 'ill we've joined the server */ auto target_server = this->server; /* keep the server alive 'ill we've joined the server */
if(account->bound_server) { if(account->bound_server) {
target_server = serverInstance->getVoiceServerManager()->findServerById(account->bound_server); target_server = serverInstance->getVoiceServerManager()->findServerById(account->bound_server);
if(target_server != this->server) if(target_server != this->server) {
joined_channel = nullptr; joined_channel = nullptr;
if(!target_server) }
return command_result{error::server_invalid_id, "server does not exists anymore"};
if(!target_server) {
return command_result{error::server_invalid_id, "bound server does not exists"};
}
} }
this->server = target_server; this->server = target_server;
@ -273,8 +268,10 @@ command_result QueryClient::handleCommandLogin(Command& cmd) {
{ {
shared_lock server_tree_lock(target_server->channel_tree_mutex); shared_lock server_tree_lock(target_server->channel_tree_mutex);
if(joined_channel) /* needs only notify if we were already on that server within a channel */ if(joined_channel) {
/* needs only notify if we were already on that server within a channel */
target_server->notifyClientPropertyUpdates(this->ref(), deque<property::ClientProperties>{property::CLIENT_NICKNAME, property::CLIENT_UNIQUE_IDENTIFIER}); target_server->notifyClientPropertyUpdates(this->ref(), deque<property::ClientProperties>{property::CLIENT_NICKNAME, property::CLIENT_UNIQUE_IDENTIFIER});
}
unique_lock client_tree_lock(this->channel_tree_mutex); unique_lock client_tree_lock(this->channel_tree_mutex);
this->channel_tree->reset(); this->channel_tree->reset();
@ -283,13 +280,10 @@ command_result QueryClient::handleCommandLogin(Command& cmd) {
} }
if(joined_channel) { if(joined_channel) {
unique_lock tree_lock(this->server->channel_tree_mutex); std::unique_lock tree_lock{this->server->channel_tree_mutex};
if(joined_channel) this->server->client_move(this->ref(), joined_channel, nullptr, "", ViewReasonId::VREASON_USER_ACTION, false, tree_lock);
this->server->client_move(this->ref(), joined_channel, nullptr, "", ViewReasonId::VREASON_USER_ACTION, false, tree_lock);
} else if(!permission::v2::permission_granted(1, this->calculate_permission(permission::b_virtualserver_select_godmode, 1))) {
this->server->assignDefaultChannel(this->ref(), true);
} else { } else {
this->task_update_needed_permissions.enqueue(); this->server->assignDefaultChannel(this->ref(), true);
} }
} else { } else {
this->task_update_needed_permissions.enqueue(); this->task_update_needed_permissions.enqueue();
@ -312,8 +306,9 @@ command_result QueryClient::handleCommandLogout(Command &) {
if(this->server){ if(this->server){
{ {
unique_lock tree_lock(this->server->channel_tree_mutex); unique_lock tree_lock(this->server->channel_tree_mutex);
if(joined) if(joined) {
this->server->client_move(this->ref(), nullptr, nullptr, "", ViewReasonId::VREASON_USER_ACTION, false, tree_lock); this->server->client_move(this->ref(), nullptr, nullptr, "", ViewReasonId::VREASON_USER_ACTION, false, tree_lock);
}
this->server->unregisterClient(this->ref(), "logout", tree_lock); this->server->unregisterClient(this->ref(), "logout", tree_lock);
} }
} }
@ -337,10 +332,8 @@ command_result QueryClient::handleCommandLogout(Command &) {
if(joined) { if(joined) {
unique_lock server_channel_w_lock(this->server->channel_tree_mutex, defer_lock); unique_lock server_channel_w_lock(this->server->channel_tree_mutex, defer_lock);
this->server->client_move(this->ref(), joined, nullptr, "", ViewReasonId::VREASON_USER_ACTION, false, server_channel_w_lock); this->server->client_move(this->ref(), joined, nullptr, "", ViewReasonId::VREASON_USER_ACTION, false, server_channel_w_lock);
} else if(!permission::v2::permission_granted(1, this->calculate_permission(permission::b_virtualserver_select_godmode, 1))) {
this->server->assignDefaultChannel(this->ref(), true);
} else { } else {
this->task_update_needed_permissions.enqueue(); this->server->assignDefaultChannel(this->ref(), true);
} }
} else { } else {
this->task_update_needed_permissions.enqueue(); this->task_update_needed_permissions.enqueue();
@ -354,65 +347,113 @@ command_result QueryClient::handleCommandLogout(Command &) {
command_result QueryClient::handleCommandServerSelect(Command &cmd) { command_result QueryClient::handleCommandServerSelect(Command &cmd) {
CMD_RESET_IDLE; CMD_RESET_IDLE;
shared_ptr<VirtualServer> target;
if(cmd[0].has("port")){ std::optional<ServerId> target_server_id{};
std::shared_ptr<VirtualServer> target{};
if(cmd[0].has("port")) {
target = serverInstance->getVoiceServerManager()->findServerByPort(cmd["port"].as<uint16_t>()); target = serverInstance->getVoiceServerManager()->findServerByPort(cmd["port"].as<uint16_t>());
} else if(cmd[0].has("sid")) { } else if(cmd[0].has("sid")) {
target = serverInstance->getVoiceServerManager()->findServerById(cmd["sid"].as<ServerId>()); target_server_id = std::make_optional(cmd["sid"].as<ServerId>());
} } else {
for(const auto& parm : cmd[0].keys()) {
for(auto& parm : cmd[0].keys()) if(parm.length() < 6 && parm.find_first_not_of("0123456789") == string::npos) {
if(parm.find_first_not_of("0123456789") == string::npos) target_server_id = std::make_optional(std::stoul(parm));
target = serverInstance->getVoiceServerManager()->findServerById(static_cast<ServerId>(stol(parm))); break;
}
if(!target && (!cmd[0].has("0") && (!cmd[0].has("sid") || !cmd["sid"] == 0))) return command_result{error::server_invalid_id, "Invalid server id"};
if(target && target->getState() != ServerState::ONLINE && target->getState() != ServerState::OFFLINE) return command_result{error::server_is_not_running};
if(target == this->server) return command_result{error::ok};
auto old_server_id = this->getServerId();
if(target) {
if(this->query_account && this->query_account->bound_server > 0) {
if(target->getServerId() != this->query_account->bound_server)
return command_result{error::server_invalid_id, "You're a server bound query, and the target server isn't your origin."};
} else {
auto allowed = target->calculate_permission(permission::b_virtualserver_select, this->getClientDatabaseId(), this->getType(), 0);
if(!permission::v2::permission_granted(1, allowed)) return command_result{permission::b_virtualserver_select};
} }
} }
this->disconnect_from_virtual_server("server switch"); if(!target && target_server_id.has_value()) {
this->resetEventMask(); target = serverInstance->getVoiceServerManager()->findServerById(*target_server_id);
//register at current server
{
//unique_lock server_lock(this->server_lock);
/* We dont need to lock the server because only one command can be executed at the time. Everything else should copy the server once and test the copy and use that ref then */
this->server = target;
} }
if(cmd[0].has("client_nickname")) if(target_server_id.has_value()) {
this->properties()[property::CLIENT_NICKNAME] = cmd["client_nickname"].string(); if(*target_server_id > 0) {
target = serverInstance->getVoiceServerManager()->findServerById(*target_server_id);
if(!target) {
return ts::command_result{error::server_invalid_id};
}
}
} else if(target) {
target_server_id = std::make_optional(target->getServerId());
} else {
return ts::command_result{error::server_invalid_id};
}
if(target == this->server) {
return ts::command_result{error::ok};
}
auto old_server_id = this->getServerId();
if(target) {
if(target->getState() != ServerState::ONLINE && target->getState() != ServerState::OFFLINE) {
return command_result{error::server_is_not_running};
}
if(this->query_account && this->query_account->bound_server > 0) {
if(target->getServerId() != this->query_account->bound_server) {
return command_result{error::server_invalid_id, "You're a server bound query, and the target server isn't your origin."};
}
} else {
auto allowed = target->calculate_permission(permission::b_virtualserver_select, this->getClientDatabaseId(), this->getType(), 0);
if(!permission::v2::permission_granted(1, allowed)) {
return command_result{permission::b_virtualserver_select};
}
}
}
this->resetEventMask();
this->disconnect_from_virtual_server("server switch");
this->server = target;
auto target_client_nickname = cmd["client_nickname"].optional_string();
if(target_client_nickname.has_value()) {
this->properties()[property::CLIENT_NICKNAME] = *target_client_nickname;
}
#if 0
command_result QueryClient::handleCommandJoin(Command &) {
CMD_REQ_SERVER;
CMD_RESET_IDLE;
if(this->server->state != ServerState::ONLINE)
return command_result{error::server_is_not_running};
if(this->currentChannel)
return command_result{error::server_already_joined, "already joined!"};
this->server->assignDefaultChannel(this->ref(), true);
return command_result{error::ok};
}
command_result QueryClient::handleCommandLeft(Command&) {
CMD_REQ_SERVER;
CMD_REQ_CHANNEL;
CMD_RESET_IDLE;
ACTION_REQUIRES_GLOBAL_PERMISSION(permission::b_virtualserver_select_godmode, 1);
unique_lock server_channel_lock(this->server->channel_tree_mutex);
this->server->client_move(this->ref(), nullptr, nullptr, "leaving", ViewReasonId::VREASON_SERVER_LEFT, true, server_channel_lock);
return command_result{error::ok};
}
#endif
DatabaseHelper::assignDatabaseId(this->sql, static_cast<ServerId>(this->server ? this->server->getServerId() : 0), this->ref()); DatabaseHelper::assignDatabaseId(this->sql, static_cast<ServerId>(this->server ? this->server->getServerId() : 0), this->ref());
if(this->server) { if(this->server) {
this->server->registerClient(this->ref()); this->server->registerClient(this->ref());
{ {
shared_lock server_channel_lock(target->channel_tree_mutex); std::shared_lock server_channel_lock{target->channel_tree_mutex};
unique_lock client_channel_lock(this->channel_tree_mutex); std::unique_lock client_channel_lock{this->channel_tree_mutex};
this->subscribeToAll = true; this->subscribeToAll = true;
this->channel_tree->insert_channels(this->server->channelTree->tree_head(), true, false); this->channel_tree->insert_channels(this->server->channelTree->tree_head(), true, false);
this->subscribeChannel(this->server->channelTree->channels(), false, false); this->subscribeChannel(this->server->channelTree->channels(), false, false);
} }
auto negated_enforce_join = permission::v2::permission_granted(1, this->calculate_permission(permission::b_virtualserver_select_godmode, 1)); this->server->assignDefaultChannel(this->ref(), true);
if(!negated_enforce_join) {
this->server->assignDefaultChannel(this->ref(), true);
} else {
this->task_update_needed_permissions.enqueue();
}
} else { } else {
this->task_update_needed_permissions.enqueue(); this->task_update_needed_permissions.enqueue();
} }
@ -422,30 +463,6 @@ command_result QueryClient::handleCommandServerSelect(Command &cmd) {
return command_result{error::ok}; return command_result{error::ok};
} }
command_result QueryClient::handleCommandJoin(Command &) {
CMD_REQ_SERVER;
CMD_RESET_IDLE;
if(this->server->state != ServerState::ONLINE)
return command_result{error::server_is_not_running};
if(this->currentChannel)
return command_result{error::server_already_joined, "already joined!"};
this->server->assignDefaultChannel(this->ref(), true);
return command_result{error::ok};
}
command_result QueryClient::handleCommandLeft(Command&) {
CMD_REQ_SERVER;
CMD_REQ_CHANNEL;
CMD_RESET_IDLE;
ACTION_REQUIRES_GLOBAL_PERMISSION(permission::b_virtualserver_select_godmode, 1);
unique_lock server_channel_lock(this->server->channel_tree_mutex);
this->server->client_move(this->ref(), nullptr, nullptr, "leaving", ViewReasonId::VREASON_SERVER_LEFT, true, server_channel_lock);
return command_result{error::ok};
}
command_result QueryClient::handleCommandServerInfo(Command &) { command_result QueryClient::handleCommandServerInfo(Command &) {
CMD_RESET_IDLE; CMD_RESET_IDLE;
ACTION_REQUIRES_GLOBAL_PERMISSION(permission::b_virtualserver_info_view, 1); ACTION_REQUIRES_GLOBAL_PERMISSION(permission::b_virtualserver_info_view, 1);
@ -664,10 +681,18 @@ command_result QueryClient::handleCommandServerCreate(Command& cmd) {
auto start = system_clock::now(); auto start = system_clock::now();
threads::MutexLock lock(serverInstance->getVoiceServerManager()->server_create_lock); threads::MutexLock lock(serverInstance->getVoiceServerManager()->server_create_lock);
auto instances = serverInstance->getVoiceServerManager()->serverInstances(); auto instances = serverInstance->getVoiceServerManager()->serverInstances();
if(config::server::max_virtual_server != -1 && instances.size() > config::server::max_virtual_server) if(config::server::max_virtual_server != -1 && instances.size() > config::server::max_virtual_server) {
return command_result{error::server_max_vs_reached, "You reached the via config.yml enabled virtual server limit."}; return command_result{error::server_max_vs_reached, "You reached the via config.yml enabled virtual server limit."};
if(instances.size() >= 65535) }
/*
* 2 ^ 16 = 65536
* We're using one less since we're using the server with id 65535 as snapshot deploy server.
*/
if(instances.size() >= 65535) {
return command_result{error::server_max_vs_reached, "You cant create anymore virtual servers. (Software limit reached)"}; return command_result{error::server_max_vs_reached, "You cant create anymore virtual servers. (Software limit reached)"};
}
{ {
auto end = system_clock::now(); auto end = system_clock::now();
time_wait = duration_cast<milliseconds>(end - start); time_wait = duration_cast<milliseconds>(end - start);
@ -702,10 +727,10 @@ command_result QueryClient::handleCommandServerCreate(Command& cmd) {
} }
if(!cmd.hasParm("offline")) { if(!cmd.hasParm("offline")) {
auto _start = system_clock::now(); auto start_ = system_clock::now();
if(!server->start(startError)); if(!server->start(startError));
auto _end = system_clock::now(); auto end_ = system_clock::now();
time_start = duration_cast<milliseconds>(_end - _start); time_start = duration_cast<milliseconds>(end_ - start_);
} }
auto end = system_clock::now(); auto end = system_clock::now();
@ -906,83 +931,6 @@ command_result QueryClient::handleCommandBindingList(Command& cmd) {
return command_result{error::ok}; return command_result{error::ok};
} }
//TODO with mapping!
command_result QueryClient::handleCommandServerSnapshotDeploy(Command& cmd) {
#if 0
CMD_RESET_IDLE;
auto start = system_clock::now();
string error;
string host = "0.0.0.0";
uint16_t port = 0;
if(this->server) {
ACTION_REQUIRES_GLOBAL_PERMISSION(permission::b_virtualserver_snapshot_deploy, 1);
host = this->server->properties()[property::VIRTUALSERVER_HOST].as<string>();
port = this->server->properties()[property::VIRTUALSERVER_PORT].as<uint16_t>();
} else {
ACTION_REQUIRES_INSTANCE_PERMISSION(permission::b_virtualserver_snapshot_deploy, 1);
}
auto hash = cmd["hash"].string();
if(hash.empty()) return command_result{error::parameter_invalid, "Invalid hash (not present)"};
debugMessage(this->getServerId(), "Serversnapshot calculated hash: {}", hash);
bool mapping = cmd.hasParm("mapping");
bool ignore_hash = cmd.hasParm("ignorehash");
cmd.clear_parameters();
cmd.pop_bulk();
auto str = cmd.build().substr(strlen("serversnapshotdeploy "));
if(!ignore_hash) {
auto buildHash = base64::encode(digest::sha1(str));
if(buildHash != hash)
return command_result{error::parameter_invalid, "Invalid hash (Expected: \"" + hash + "\", Got: \"" + buildHash + "\")"};
}
unique_lock server_create_lock(serverInstance->getVoiceServerManager()->server_create_lock);
{
auto instances = serverInstance->getVoiceServerManager()->serverInstances();
if(config::server::max_virtual_server != -1 && instances.size() > config::server::max_virtual_server)
return command_result{error::server_max_vs_reached, "You reached the via config.yml enabled virtual server limit."};
if(instances.size() >= 65535)
return command_result{error::server_max_vs_reached, "You cant create anymore virtual servers. (Software limit reached)"};
}
if(port == 0)
port = serverInstance->getVoiceServerManager()->next_available_port(host);
auto result = serverInstance->getVoiceServerManager()->createServerFromSnapshot(this->server, host, port, cmd, error);
server_create_lock.unlock();
auto end = system_clock::now();
Command res("");
if(!result){
logError(this->getServerId(), "Could not apply server snapshot: {}", error);
res["success"] = false;
res["sid"] = 0;
res["message"] = error;
} else {
res["success"] = true;
res["sid"] = result->getServerId();
res["message"] = "";
if(!result->start(error)){
res["error_start"] = error;
res["started"] = false;
} else {
res["error_start"] = "";
res["started"] = true;
}
serverInstance->action_logger()->server_logger.log_server_create(result->getServerId(), this->ref(), log::ServerCreateReason::SNAPSHOT_DEPLOY);
}
res["time"] = duration_cast<milliseconds>(end - start).count();
this->sendCommand(res);
return command_result{error::ok};
#else
return command_result{error::command_not_found};
#endif
}
command_result QueryClient::handleCommandServerSnapshotDeployNew(const ts::command_parser &command) { command_result QueryClient::handleCommandServerSnapshotDeployNew(const ts::command_parser &command) {
CMD_RESET_IDLE; CMD_RESET_IDLE;
@ -1014,6 +962,7 @@ command_result QueryClient::handleCommandServerSnapshotDeployNew(const ts::comma
return command_result{error::vs_critical, error}; return command_result{error::vs_critical, error};
} }
/* TODO: Send mapping */
ts::command_builder notify{""}; ts::command_builder notify{""};
notify.put_unchecked(0, "virtualserver_port", server->properties()[property::VIRTUALSERVER_PORT].value()); notify.put_unchecked(0, "virtualserver_port", server->properties()[property::VIRTUALSERVER_PORT].value());
notify.put_unchecked(0, "sid", server->getServerId()); notify.put_unchecked(0, "sid", server->getServerId());

View File

@ -22,11 +22,13 @@ constexpr static ServerId kSnapshotServerId{0xFFFF};
VirtualServerManager::SnapshotDeployResult VirtualServerManager::deploy_snapshot(std::string &error, std::shared_ptr<VirtualServer>& server, const command_parser &command) { VirtualServerManager::SnapshotDeployResult VirtualServerManager::deploy_snapshot(std::string &error, std::shared_ptr<VirtualServer>& server, const command_parser &command) {
if(!server) { if(!server) {
auto instance_count = this->serverInstances().size(); auto instance_count = this->serverInstances().size();
if(config::server::max_virtual_server != -1 && instance_count > config::server::max_virtual_server) if(config::server::max_virtual_server != -1 && instance_count > config::server::max_virtual_server) {
return SnapshotDeployResult::REACHED_CONFIG_SERVER_LIMIT; return SnapshotDeployResult::REACHED_CONFIG_SERVER_LIMIT;
}
if(instance_count >= 65534) if(instance_count >= 65534) {
return SnapshotDeployResult::REACHED_SOFTWARE_SERVER_LIMIT; return SnapshotDeployResult::REACHED_SOFTWARE_SERVER_LIMIT;
}
} }
bool success{true}; bool success{true};
@ -37,6 +39,7 @@ VirtualServerManager::SnapshotDeployResult VirtualServerManager::deploy_snapshot
std::string server_host{server ? server->properties()[property::VIRTUALSERVER_HOST].value() : config::binding::DefaultVoiceHost}; std::string server_host{server ? server->properties()[property::VIRTUALSERVER_HOST].value() : config::binding::DefaultVoiceHost};
uint16_t server_port{server ? server->properties()[property::VIRTUALSERVER_PORT].as_unchecked<uint16_t>() : this->next_available_port(server_host)}; uint16_t server_port{server ? server->properties()[property::VIRTUALSERVER_PORT].as_unchecked<uint16_t>() : this->next_available_port(server_host)};
this->delete_server_in_db(kSnapshotServerId, false); this->delete_server_in_db(kSnapshotServerId, false);
auto result = sql::command{this->handle->getSql(), "INSERT INTO `servers` (`serverId`, `host`, `port`) VALUES (:sid, :host, :port)"} auto result = sql::command{this->handle->getSql(), "INSERT INTO `servers` (`serverId`, `host`, `port`) VALUES (:sid, :host, :port)"}
.value(":sid", kSnapshotServerId) .value(":sid", kSnapshotServerId)