diff --git a/CMakeLists.txt b/CMakeLists.txt index eb0d0f0..b971994 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -56,7 +56,7 @@ find_package(CXXTerminal REQUIRED) find_package(StringVariable REQUIRED) find_package(yaml-cpp REQUIRED) find_package(jsoncpp REQUIRED) -find_package(Ed25519 REQUIRED) +find_package(ed25519 REQUIRED) find_package(DataPipes REQUIRED) find_package(Opus REQUIRED) find_package(spdlog REQUIRED) diff --git a/server/CMakeLists.txt b/server/CMakeLists.txt index 388be22..dcbf82c 100644 --- a/server/CMakeLists.txt +++ b/server/CMakeLists.txt @@ -267,7 +267,7 @@ target_link_libraries(TeaSpeakServer tommath::static jsoncpp_lib - ${ed25519_LIBRARIES_STATIC} + ${Ed25519_LIBRARIES_STATIC} zstd::libzstd_static ) diff --git a/server/src/PermissionCalculator.cpp b/server/src/PermissionCalculator.cpp index 9c35470..b7b958d 100644 --- a/server/src/PermissionCalculator.cpp +++ b/server/src/PermissionCalculator.cpp @@ -96,7 +96,7 @@ void ClientPermissionCalculator::initialize_client(DataClient* client) { this->virtual_server_id = client->getServerId(); this->client_database_id = client->getClientDatabaseId(); this->client_type = client->getType(); - this->client_permissions = client->permissions(); + this->client_permissions_ = client->permissions(); auto server = client->getServer(); if(server) { @@ -139,19 +139,8 @@ std::vector> ClientPermissionC std::vector> result; result.reserve(permissions.size()); - if(!this->client_permissions) { - this->client_permissions = serverInstance->databaseHelper()->loadClientPermissionManager(this->virtual_server_id, this->client_database_id); - if(!this->client_permissions) { - logCritical(this->virtual_server_id, "Failed to load client permissions for client {}", this->client_database_id); - for(size_t index{0}; index < permissions.size(); index++) { - result.emplace_back(permissions[index], PermissionFlaggedValue{ 0, false }); - } - return result; - } - } - - bool have_skip_permission{false}; - int skip_permission_type{-1}; /* -1 := unset | 0 := skip, not explicit | 1 := skip, explicit */ + auto client_permissions = this->client_permissions(); + assert(client_permissions); /* * server_group_data[0] := Server group id @@ -164,34 +153,6 @@ std::vector> ClientPermissionC std::vector server_group_data; GroupData* active_server_group; - /* function to calculate skip permission */ - auto calculate_skip = [&]{ - skip_permission_type = 0; - /* test for skip permission within the client permission manager */ - { - auto skip_value = this->client_permissions->permission_value_flagged(permission::b_client_skip_channelgroup_permissions); - if(skip_value.has_value) { - have_skip_permission = skip_value.value == 1; - skip_permission_type = 1; - logTrace(this->virtual_server_id, "[Permission] Found skip permission in client permissions. Value: {}", have_skip_permission); - } - } - /* test for skip permission within all server groups */ - if(skip_permission_type != 1) { - for(const auto& assignment : this->assigned_server_groups()) { - auto group_permissions = assignment->permissions(); - auto flagged_value = group_permissions->permission_value_flagged(permission::b_client_skip_channelgroup_permissions); - if(flagged_value.has_value) { - have_skip_permission |= flagged_value.value == 1; - if(have_skip_permission) { - logTrace(this->virtual_server_id, "[Permission] Found skip permission in client server group. Group: {} ({}), Value: {}", assignment->group_id(), assignment->display_name(), have_skip_permission); - break; - } - } - } - } - }; - auto initialize_group_data = [&](const permission::PermissionType& permission_type) { server_group_data_initialized = true; active_server_group = nullptr; @@ -253,10 +214,10 @@ std::vector> ClientPermissionC for(const auto& permission : permissions) { server_group_data_initialized = false; /* reset all group data */ - auto client_permission_flags = this->client_permissions->permission_flags(permission); + auto client_permission_flags = client_permissions->permission_flags(permission); /* lets try to resolve the channel specific permission */ if(this->channel_id_ > 0 && client_permission_flags.channel_specific) { - auto data = this->client_permissions->channel_permission(permission, this->channel_id_); + auto data = client_permissions->channel_permission(permission, this->channel_id_); if(calculate_granted ? data.flags.grant_set : data.flags.value_set) { result.push_back({permission, {calculate_granted ? data.values.grant : data.values.value, true}}); logTrace(this->virtual_server_id, "[Permission] Calculation for client {} of permission {} returned {} (Client channel permission)", this->client_database_id, permission::resolvePermissionData(permission)->name, data.values.value); @@ -265,16 +226,9 @@ std::vector> ClientPermissionC } - bool skip_channel_permissions = this->channel_id_ == 0; + bool skip_channel_permissions = this->channel_id_ == 0 || this->has_global_skip_permission(); if(!skip_channel_permissions) { - /* look if somewhere is the skip permission flag set */ - if(skip_permission_type == -1) {/* initialize skip flag */ - calculate_skip(); - } - skip_channel_permissions = have_skip_permission; - } - if(!skip_channel_permissions) { - /* okey we've no global skip. Then now lookup the groups and the client permissions */ + /* We dont have a global skip flag. Lets see if the target permission has skip enabled */ if(calculate_granted ? client_permission_flags.grant_set : client_permission_flags.value_set) { /* okey the client has the permission, this counts */ skip_channel_permissions = client_permission_flags.skip; @@ -319,7 +273,7 @@ std::vector> ClientPermissionC } if(calculate_granted ? client_permission_flags.grant_set : client_permission_flags.value_set) { - auto client_value = this->client_permissions->permission_values(permission); + auto client_value = client_permissions->permission_values(permission); result.push_back({permission, {calculate_granted ? client_value.grant : client_value.value, true}}); logTrace(this->virtual_server_id, "[Permission] Calculation for client {} of permission {} returned {} (Client permission)", this->client_database_id, permission::resolvePermissionData(permission)->name, client_value.value); continue; @@ -336,12 +290,25 @@ std::vector> ClientPermissionC } logTrace(this->virtual_server_id, "[Permission] Calculation for client {} of permission {} returned in no permission.", this->client_database_id, permission::resolvePermissionData(permission)->name); - result.push_back({permission, {permNotGranted, false}}); + result.push_back({permission, { permNotGranted, false }}); } return result; } +const std::shared_ptr & ClientPermissionCalculator::client_permissions() { + if(!this->client_permissions_) { + this->client_permissions_ = serverInstance->databaseHelper()->loadClientPermissionManager(this->virtual_server_id, this->client_database_id); + + if(!this->client_permissions_) { + logCritical(this->virtual_server_id, "Failed to load client permissions for client {}. Using empty permission set.", this->client_database_id); + this->client_permissions_ = std::make_shared(); + } + } + + return this->client_permissions_; +} + const std::vector>& ClientPermissionCalculator::assigned_server_groups() { if(this->assigned_server_groups_.has_value()) { return *this->assigned_server_groups_; @@ -391,4 +358,47 @@ const std::shared_ptr& ClientPermissionCalculator::assigne *this->assigned_channel_group_ = channel_group; return *this->assigned_channel_group_; +} + +bool ClientPermissionCalculator::has_global_skip_permission() { + /* test for skip permission within the client permission manager */ + { + auto client_permissions = this->client_permissions(); + auto skip_value = client_permissions->permission_value_flagged(permission::b_client_skip_channelgroup_permissions); + if(skip_value.has_value) { + this->skip_enabled = std::make_optional(permission::v2::permission_granted(1, skip_value)); + logTrace(this->virtual_server_id, "[Permission] Found skip permission in client permissions. Value: {}", *this->skip_enabled); + } + } + + /* test for skip permission within all server groups */ + if(!this->skip_enabled.has_value()) { + for(const auto& assignment : this->assigned_server_groups()) { + auto group_permissions = assignment->permissions(); + auto flagged_value = group_permissions->permission_value_flagged(permission::b_client_skip_channelgroup_permissions); + if(flagged_value.has_value) { + this->skip_enabled = std::make_optional(permission::v2::permission_granted(1, flagged_value)); + if(*this->skip_enabled) { + logTrace(this->virtual_server_id, "[Permission] Found skip permission in client server group. Group: {} ({})", assignment->group_id(), assignment->display_name()); + break; + } + } + } + } + + if(!this->skip_enabled.has_value()) { + this->skip_enabled = std::make_optional(false); + } + + return *this->skip_enabled; +} + +bool ClientPermissionCalculator::permission_granted(const permission::PermissionType &permission, + const permission::PermissionValue &required_value, bool granted) { + return this->permission_granted(permission, { required_value, true }, granted); +} + +bool ClientPermissionCalculator::permission_granted(const permission::PermissionType &permission, + const permission::v2::PermissionFlaggedValue &required_value, bool granted) { + return permission::v2::permission_granted(required_value, this->calculate_permission(permission, granted)); } \ No newline at end of file diff --git a/server/src/PermissionCalculator.h b/server/src/PermissionCalculator.h index f3cce9e..42eb12e 100644 --- a/server/src/PermissionCalculator.h +++ b/server/src/PermissionCalculator.h @@ -40,7 +40,7 @@ namespace ts::server { * @param granted * @return */ - permission::v2::PermissionFlaggedValue calculate_permission( + [[nodiscard]] permission::v2::PermissionFlaggedValue calculate_permission( permission::PermissionType, bool granted = false ); @@ -52,10 +52,37 @@ namespace ts::server { * @param calculate_granted * @return */ - std::vector> calculate_permissions( + [[nodiscard]] std::vector> calculate_permissions( const std::deque&, bool calculate_granted = false ); + + /** + * Test if the target has the target permission granted. + * If the client does not have any value assigned for the permission `false` will be returned. + */ + [[nodiscard]] bool permission_granted( + const permission::PermissionType& /* target permission */, + const permission::PermissionValue& /* required value */, + bool /* granted permission */ = false + ); + + /** + * Test if the target has the target permission granted. + * If the client does not have any value assigned for the permission, + * `true` will be returned if the `required value` contains no value + * otherwise false will be returned. + * + * This method should be used when testing permissions which are allowed by default except if they're + * specified otherwise. An example permission would be if we're testing against the channel needed join power. + */ + [[nodiscard]] bool permission_granted( + const permission::PermissionType& /* target permission */, + const permission::v2::PermissionFlaggedValue& /* required value */, + bool /* granted permission */ = false + ); + + //const PermissionValue& required, const PermissionFlaggedValue& given, bool requires_given = true private: /* given fields */ ServerId virtual_server_id; @@ -68,10 +95,9 @@ namespace ts::server { std::function()> default_server_group{[]{ return nullptr; }}; /* fields which will be set when calculating permissions */ - std::shared_ptr client_permissions{}; + std::shared_ptr client_permissions_{}; - bool global_skip{false}; - bool global_skip_set{false}; + std::optional skip_enabled{}; std::optional> assigned_channel_group_{}; std::optional>> assigned_server_groups_{}; @@ -81,5 +107,7 @@ namespace ts::server { [[nodiscard]] const std::vector>& assigned_server_groups(); [[nodiscard]] const std::shared_ptr& assigned_channel_group(); + [[nodiscard]] const std::shared_ptr& client_permissions(); + [[nodiscard]] bool has_global_skip_permission(); }; } \ No newline at end of file diff --git a/server/src/TS3ServerClientManager.cpp b/server/src/TS3ServerClientManager.cpp index 2fa8f03..0778442 100644 --- a/server/src/TS3ServerClientManager.cpp +++ b/server/src/TS3ServerClientManager.cpp @@ -175,7 +175,7 @@ void VirtualServer::unregisterInternalClient(std::shared_ptr cl } bool VirtualServer::assignDefaultChannel(const shared_ptr& client, bool join) { - std::shared_lock server_channel_lock{this->channel_tree_lock}; + std::shared_lock server_channel_lock{this->channel_tree_mutex}; std::shared_ptr channel{}; auto requested_channel_path = client->properties()[property::CLIENT_DEFAULT_CHANNEL].value(); @@ -206,7 +206,7 @@ bool VirtualServer::assignDefaultChannel(const shared_ptr& clie debugMessage(this->getServerId(), "{} Allowing client to join channel {} because the token he used explicitly allowed it.", client->getLoggingPrefix(), channel->channelId()); if(whitelist_entry->second != "ignore") { - if (!channel->passwordMatch(client_channel_password, true)) { + if (!channel->verify_password(std::make_optional(client_channel_password), true)) { if (!permission::v2::permission_granted(1, client->calculate_permission(permission::b_channel_join_ignore_password, channel->channelId()))) { channel = nullptr; goto skip_permissions; @@ -222,7 +222,7 @@ bool VirtualServer::assignDefaultChannel(const shared_ptr& clie goto skip_permissions; } - if (!channel->passwordMatch(client->properties()[property::CLIENT_DEFAULT_CHANNEL_PASSWORD], true)) { + if (!channel->verify_password(std::make_optional(client->properties()[property::CLIENT_DEFAULT_CHANNEL_PASSWORD].value()), true)) { if(!permission::v2::permission_granted(1, client->calculate_permission(permission::b_channel_join_ignore_password, channel->channelId()))) { debugMessage(this->getServerId(), "{} Tried to join channel {} but hasn't given the right channel password.", client->getLoggingPrefix(), channel->channelId()); channel = nullptr; @@ -245,7 +245,7 @@ bool VirtualServer::assignDefaultChannel(const shared_ptr& 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_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 { client->currentChannel = channel; @@ -268,14 +268,14 @@ void VirtualServer::testBanStateChange(const std::shared_ptr& i void VirtualServer::notify_client_ban(const shared_ptr &target, const std::shared_ptr &invoker, const std::string &reason, size_t time) { /* the target is not allowed to execute anything; Must before channel tree lock because the target may waits for us to finish the channel stuff */ lock_guard command_lock(target->command_lock); - unique_lock server_channel_lock(this->channel_tree_lock); /* we're "moving" a client! */ + unique_lock server_channel_lock(this->channel_tree_mutex); /* we're "moving" a client! */ if(target->currentChannel) { for(const auto& client : this->getClients()) { if(!client || client == target) continue; - unique_lock client_channel_lock(client->channel_lock); + unique_lock client_channel_lock(client->channel_tree_mutex); if(client->isClientVisible(target, false)) client->notifyClientLeftViewBanned(target, reason, invoker, time, false); } @@ -285,7 +285,7 @@ void VirtualServer::notify_client_ban(const shared_ptr &target, } /* now disconnect the target itself */ - unique_lock client_channel_lock(target->channel_lock); + unique_lock client_channel_lock(target->channel_tree_mutex); target->notifyClientLeftViewBanned(target, reason, invoker, time, false); target->currentChannel = nullptr; } @@ -298,19 +298,19 @@ void VirtualServer::notify_client_kick( if(target_channel) { /* use the move! */ - unique_lock server_channel_lock(this->channel_tree_lock, defer_lock); + unique_lock server_channel_lock(this->channel_tree_mutex, defer_lock); this->client_move(target, target_channel, invoker, reason, ViewReasonId::VREASON_CHANNEL_KICK, true, server_channel_lock); } else { /* the target is not allowed to execute anything; Must before channel tree lock because the target may waits for us to finish the channel stuff */ lock_guard command_lock(target->command_lock); - unique_lock server_channel_lock(this->channel_tree_lock); /* we're "moving" a client! */ + unique_lock server_channel_lock(this->channel_tree_mutex); /* we're "moving" a client! */ if(target->currentChannel) { for(const auto& client : this->getClients()) { if(!client || client == target) continue; - unique_lock client_channel_lock(client->channel_lock); + unique_lock client_channel_lock(client->channel_tree_mutex); if(client->isClientVisible(target, false)) client->notifyClientLeftViewKicked(target, nullptr, reason, invoker, false); } @@ -323,7 +323,7 @@ void VirtualServer::notify_client_kick( } /* now disconnect the target itself */ - unique_lock client_channel_lock(target->channel_lock); + unique_lock client_channel_lock(target->channel_tree_mutex); target->notifyClientLeftViewKicked(target, nullptr, reason, invoker, false); target->currentChannel = nullptr; } @@ -361,7 +361,6 @@ void VirtualServer::delete_channel(shared_ptr channel, const channel->deleted = true; } auto default_channel = this->channelTree->getDefaultChannel(); - tree_lock.unlock(); deque> command_locks; for(const auto& client : clients) { @@ -373,7 +372,8 @@ void VirtualServer::delete_channel(shared_ptr channel, const } if(!tree_lock.owns_lock()) { - tree_lock.lock(); /* no clients left within that tree */ + /* This case should never happen. client_move should never unlock the tree lock! */ + tree_lock.lock(); } command_locks.clear(); @@ -384,8 +384,8 @@ void VirtualServer::delete_channel(shared_ptr channel, const } this->forEachClient([&](const shared_ptr& client) { - unique_lock client_channel_lock(client->channel_lock); - client->notifyChannelDeleted(client->channels->delete_channel_root(channel), invoker); + unique_lock client_channel_lock(client->channel_tree_mutex); + client->notifyChannelDeleted(client->channel_tree->delete_channel_root(channel), invoker); }); { @@ -451,11 +451,11 @@ void VirtualServer::client_move( /* second step: show the target channel to the client if its not shown and let him subscibe to the channel */ if(target_channel && notify_client) { - unique_lock client_channel_lock(target->channel_lock); + unique_lock client_channel_lock(target->channel_tree_mutex); bool success = false; /* TODO: Use a bunk here and not a notify for every single */ - for(const auto& channel : target->channels->show_channel(l_target_channel, success)) + for(const auto& channel : target->channel_tree->show_channel(l_target_channel, success)) target->notifyChannelShow(channel->channel(), channel->previous_channel); sassert(success); if(!success) @@ -469,11 +469,11 @@ void VirtualServer::client_move( this->forEachClient([&](const shared_ptr& client) { if (!notify_client && client == target) return; - unique_lock client_channel_lock(client->channel_lock); - auto chan_target = client->channels->find_channel(target_channel); + unique_lock client_channel_lock(client->channel_tree_mutex); + auto chan_target = client->channel_tree->find_channel(target_channel); if(chan_target) { - auto chan_source = client->channels->find_channel(s_source_channel); + 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)) { @@ -527,7 +527,7 @@ void VirtualServer::client_move( if(!client || client == target) continue; - unique_lock client_channel_lock(client->channel_lock); + unique_lock client_channel_lock(client->channel_tree_mutex); if(client->isClientVisible(target, false)) client->notifyClientLeftView(target, nullptr, reason_id, reason_message, invoker, false); } @@ -541,11 +541,9 @@ void VirtualServer::client_move( TIMING_STEP(timings, "notify view"); target->currentChannel = target_channel; - server_channel_write_lock.unlock(); /* third step: update stuff for the client (remember: the client cant execute anything at the moment!) */ - shared_lock server_channel_read_lock(this->channel_tree_lock); - unique_lock client_channel_lock(target->channel_lock); + unique_lock client_channel_lock{target->channel_tree_mutex}; TIMING_STEP(timings, "lock own tr"); if (s_source_channel) { @@ -573,7 +571,7 @@ void VirtualServer::client_move( if(s_source_channel) { deque deleted; - for(const auto& channel : target->channels->test_channel(l_source_channel, l_target_channel)) { + for(const auto& channel : target->channel_tree->test_channel(l_source_channel, l_target_channel)) { deleted.push_back(channel->channelId()); } @@ -586,7 +584,7 @@ void VirtualServer::client_move( auto source_channel_sub_power = target->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); - if(!permission::v2::permission_granted(1, source_channel_sub_power_ignore, true)) { + 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(), i_source_channel diff --git a/server/src/TS3ServerHeartbeat.cpp b/server/src/TS3ServerHeartbeat.cpp index 3e87836..b57aa22 100644 --- a/server/src/TS3ServerHeartbeat.cpp +++ b/server/src/TS3ServerHeartbeat.cpp @@ -173,7 +173,7 @@ void VirtualServer::executeServerTick() { { BEGIN_TIMINGS(); - unique_lock channel_lock(this->channel_tree_lock); + unique_lock channel_lock(this->channel_tree_mutex); auto channels = this->channelTree->channels(); channel_lock.unlock(); diff --git a/server/src/VirtualServer.cpp b/server/src/VirtualServer.cpp index 58df99d..c3c4eb8 100644 --- a/server/src/VirtualServer.cpp +++ b/server/src/VirtualServer.cpp @@ -616,7 +616,7 @@ void VirtualServer::preStop(const std::string& reason) { } for(const auto& cl : this->getClients()) { - unique_lock channel_lock(cl->channel_lock); + unique_lock channel_lock(cl->channel_tree_mutex); if (cl->currentChannel) { auto vc = dynamic_pointer_cast(cl); if(vc) { @@ -822,22 +822,36 @@ std::vector> VirtualServer::getClients() { return clients; } +/* Note: This method **should** not lock the channel tree else we've a lot to do! */ deque> VirtualServer::getClientsByChannel(std::shared_ptr channel) { assert(this); auto s_channel = dynamic_pointer_cast(channel); - if(!s_channel) return {}; /* what had we done wrong here... :D */ + assert(s_channel); - shared_lock client_lock(s_channel->client_lock); + if(!s_channel) { + return {}; + } + + std::shared_lock client_lock{s_channel->client_lock}; auto weak_clients = s_channel->clients; client_lock.unlock(); std::deque> result; for(const auto& weak_client : weak_clients) { auto client = weak_client.lock(); - if(!client) continue; - if(client->connectionState() != ConnectionState::CONNECTED) continue; - if(client->getChannel() != channel) continue; /* to be sure */ + if(!client) { + continue; + } + + if(client->connectionState() != ConnectionState::CONNECTED) { + continue; + } + + if(client->getChannel() != channel) { + /* This should not happen! */ + continue; + } result.push_back(move(client)); } @@ -847,7 +861,7 @@ deque> VirtualServer::getClientsByChannel(std::share deque> VirtualServer::getClientsByChannelRoot(const std::shared_ptr &root, bool lock) { assert(this); - shared_lock channel_lock(this->channel_tree_lock, defer_lock); + shared_lock channel_lock(this->channel_tree_mutex, defer_lock); if(lock) channel_lock.lock(); @@ -860,6 +874,41 @@ deque> VirtualServer::getClientsByChannelRoot(const return result; } +size_t VirtualServer::countChannelRootClients(const std::shared_ptr &root, size_t limit, bool lock_channel_tree) { + std::shared_lock channel_lock{this->channel_tree_mutex, defer_lock}; + if(lock_channel_tree) { + channel_lock.lock(); + } + + size_t result{0}; + for(const auto& channel : this->channelTree->channels(root)) { + auto channel_clients = this->getClientsByChannel(channel); + result += channel_clients.size(); + + if(result >= limit) { + return limit; + } + } + + return result; +} + +bool VirtualServer::isChannelRootEmpty(const std::shared_ptr &root, bool lock_channel_tree) { + std::shared_lock channel_lock{this->channel_tree_mutex, defer_lock}; + if(lock_channel_tree) { + channel_lock.lock(); + } + + for(const auto& channel : this->channelTree->channels(root)) { + auto channel_clients = this->getClientsByChannel(channel); + if(!channel_clients.empty()) { + return false; + } + } + + return true; +} + bool VirtualServer::notifyServerEdited(std::shared_ptr invoker, deque keys) { if(!invoker) return false; @@ -886,7 +935,7 @@ bool VirtualServer::notifyServerEdited(std::shared_ptr invoker, bool VirtualServer::notifyClientPropertyUpdates(std::shared_ptr client, const deque& keys, bool selfNotify) { if(keys.empty() || !client) return false; this->forEachClient([&](const shared_ptr& cl) { - shared_lock client_channel_lock(cl->channel_lock); + shared_lock client_channel_lock(cl->channel_tree_mutex); if(cl->isClientVisible(client, false) || (cl == client && selfNotify)) cl->notifyClientUpdated(client, keys, false); }); @@ -1137,7 +1186,7 @@ void VirtualServer::update_channel_from_permissions(const std::shared_ptrforEachClient([&](const std::shared_ptr& cl) { - shared_lock client_channel_lock(cl->channel_lock); + shared_lock client_channel_lock(cl->channel_tree_mutex); cl->notifyChannelEdited(channel, property_updates, issuer, false); }); } @@ -1151,10 +1200,10 @@ void VirtualServer::update_channel_from_permissions(const std::shared_ptrcurrentChannel) sassert(l_target); { - unique_lock client_channel_lock(cl->channel_lock); + unique_lock client_channel_lock(cl->channel_tree_mutex); deque deleted; - for(const auto& [flag_visible, channel] : cl->channels->update_channel(l_source, l_target)) { + for(const auto& [flag_visible, channel] : cl->channel_tree->update_channel(l_source, l_target)) { if(flag_visible) { cl->notifyChannelShow(channel->channel(), channel->previous_channel); } else { diff --git a/server/src/VirtualServer.h b/server/src/VirtualServer.h index c38a8aa..83eb1ac 100644 --- a/server/src/VirtualServer.h +++ b/server/src/VirtualServer.h @@ -155,6 +155,8 @@ namespace ts { std::vector> getClients(); std::deque> getClientsByChannel(std::shared_ptr); std::deque> getClientsByChannelRoot(const std::shared_ptr &, bool lock_channel_tree); + [[nodiscard]] size_t countChannelRootClients(const std::shared_ptr &, size_t /* limit */, bool /* lock the channel tree */); + [[nodiscard]] bool isChannelRootEmpty(const std::shared_ptr &, bool lock_channel_tree); template std::vector> getClientsByChannel(const std::shared_ptr& ch) { @@ -278,7 +280,7 @@ namespace ts { inline int voice_encryption_mode() { return this->_voice_encryption_mode; } inline std::shared_ptr conversation_manager() { return this->conversation_manager_; } - inline auto& get_channel_tree_lock() { return this->channel_tree_lock; } + inline auto& get_channel_tree_lock() { return this->channel_tree_mutex; } void update_channel_from_permissions(const std::shared_ptr& /* channel */, const std::shared_ptr& /* issuer */); @@ -333,7 +335,7 @@ namespace ts { int _voice_encryption_mode = 2; /* */ ServerChannelTree* channelTree = nullptr; - std::shared_mutex channel_tree_lock; /* lock if access channel tree! */ + std::shared_mutex channel_tree_mutex; /* lock if access channel tree! */ std::shared_ptr groups_manager_{}; diff --git a/server/src/client/ConnectedClient.cpp b/server/src/client/ConnectedClient.cpp index 10b718b..1780db2 100644 --- a/server/src/client/ConnectedClient.cpp +++ b/server/src/client/ConnectedClient.cpp @@ -13,7 +13,6 @@ #include "../InstanceHandler.h" #include "../PermissionCalculator.h" #include "../groups/GroupManager.h" -#include "../groups/Group.h" #include using namespace std; @@ -28,7 +27,7 @@ ConnectedClient::ConnectedClient(sql::SqlManager* db, const std::shared_ptrremote_address, 0, sizeof(this->remote_address)); connectionStatistics = make_shared(server ? server->getServerStatistics() : nullptr); - channels = make_shared(this); + channel_tree = make_shared(this); } ConnectedClient::~ConnectedClient() { @@ -234,8 +233,8 @@ void ConnectedClient::updateChannelClientProperties(bool lock_channel_tree, bool this->channels_view_power = permission_channel_view_power; this->channels_ignore_view = permission_channel_ignore_view_power; - shared_lock server_channel_lock(server_ref->channel_tree_lock, defer_lock); - unique_lock client_channel_lock(this->channel_lock, defer_lock); + shared_lock server_channel_lock(server_ref->channel_tree_mutex, defer_lock); + unique_lock client_channel_lock(this->channel_tree_mutex, defer_lock); if(lock_channel_tree) { /* first read lock server channel tree */ @@ -246,7 +245,7 @@ void ConnectedClient::updateChannelClientProperties(bool lock_channel_tree, bool /* might have been changed since we locked the tree */ if(channel) { deque deleted; - for(const auto& update_entry : this->channels->update_channel_path(server_ref->channelTree->tree_head(), server_ref->channelTree->find_linked_entry(channel->channelId()))) { + for(const auto& update_entry : this->channel_tree->update_channel_path(server_ref->channelTree->tree_head(), server_ref->channelTree->find_linked_entry(channel->channelId()))) { if(update_entry.first) { this->notifyChannelShow(update_entry.second->channel(), update_entry.second->previous_channel); } else { @@ -272,7 +271,7 @@ void ConnectedClient::updateTalkRights(permission::v2::PermissionFlaggedValue ta } void ConnectedClient::resetIdleTime() { - this->idleTimestamp = chrono::system_clock::now(); + this->idleTimestamp = std::chrono::system_clock::now(); } void ConnectedClient::increaseFloodPoints(uint16_t num) { @@ -286,108 +285,129 @@ bool ConnectedClient::shouldFloodBlock() { this->server->properties()[property::VIRTUALSERVER_ANTIFLOOD_POINTS_NEEDED_COMMAND_BLOCK].as_or(150); } -std::deque> ConnectedClient::subscribeChannel(const std::deque>& targets, bool lock_channel, bool enforce) { - deque> subscribed_channels; +void ConnectedClient::subscribeChannel(const std::deque>& targets, bool lock_channel, bool enforce) { + std::deque> subscribed_channels; auto ref_server = this->server; - if(!ref_server) - return {}; + if(!ref_server) { + return; + } auto general_granted = enforce || permission::v2::permission_granted(1, this->calculate_permission(permission::b_channel_ignore_subscribe_power, 0)); { - shared_lock server_channel_lock(ref_server->channel_tree_lock, defer_lock); - unique_lock client_channel_lock(this->channel_lock, defer_lock); + std::shared_lock server_channel_lock{ref_server->channel_tree_mutex, defer_lock}; + std::unique_lock client_channel_lock{this->channel_tree_mutex, defer_lock}; if(lock_channel) { server_channel_lock.lock(); client_channel_lock.lock(); } - auto cache = make_shared(); - for (const auto& channel : targets) { - auto local_channel = this->channels->find_channel(channel); - if(!local_channel) continue; //Not visible - if(local_channel->subscribed) continue; //Already subscribed + for (const auto& targetChannel : targets) { + auto local_channel = this->channel_tree->find_channel(targetChannel); + if(!local_channel) { + /* The target channel isn't visible. */ + continue; + } - if(!general_granted && channel != this->currentChannel) { - auto granted_permission = this->calculate_permission(permission::i_channel_subscribe_power, channel->channelId()); + if(local_channel->subscribed) { + /* We've already subscribed to that channel. */ + continue; + } - if(!channel->permission_granted(permission::i_channel_needed_subscribe_power, granted_permission, false)) { - auto ignore_power = this->calculate_permission(permission::b_channel_ignore_subscribe_power, channel->channelId()); - if(!ignore_power.has_value || ignore_power.value < 1) + if(!general_granted && targetChannel != this->currentChannel) { + auto required_subscribe_power = targetChannel->permissions()->permission_value_flagged(permission::i_channel_needed_subscribe_power); + required_subscribe_power.clear_flag_on_zero(); + + ClientPermissionCalculator permissionCalculator{this, targetChannel}; + if(!permissionCalculator.permission_granted(permission::i_channel_subscribe_power, required_subscribe_power)) { + if(!permissionCalculator.permission_granted(permission::b_channel_ignore_subscribe_power, 1)) { + /* The target client hasn't permissions to view the channel nor ignore the subscribe power */ continue; + } } } local_channel->subscribed = true; - subscribed_channels.push_back(channel); + subscribed_channels.push_back(targetChannel); } - deque> visible_clients; + std::deque> visible_clients{}; for(const auto& target_channel : subscribed_channels) { - /* ref_server->getClientsByChannel only acquires channel client lock */ - for(const auto& client : ref_server->getClientsByChannel(target_channel)) { - visible_clients.push_back(client); - } + /* getClientsByChannel() does not acquire the server channel tree mutex */ + auto channel_clients = ref_server->getClientsByChannel(target_channel); + visible_clients.insert(visible_clients.end(), channel_clients.begin(), channel_clients.end()); } - this->notifyClientEnterView(visible_clients, ViewReasonSystem); + if(!visible_clients.empty()) { + this->notifyClientEnterView(visible_clients, ViewReasonSystem); + } - if (!subscribed_channels.empty()) + if (!subscribed_channels.empty()) { this->notifyChannelSubscribed(subscribed_channels); + } } - - return subscribed_channels; } -std::deque> ConnectedClient::unsubscribeChannel(const std::deque>& targets, bool lock_channel) { +void ConnectedClient::unsubscribeChannel(const std::deque>& targets, bool lock_channel) { auto ref_server = this->server; - if(!ref_server) - return {}; - - deque > unsubscribed_channels; + if(!ref_server) { + return; + } + std::deque > unsubscribed_channels; { - shared_lock server_channel_lock(ref_server->channel_tree_lock, defer_lock); - unique_lock client_channel_lock(this->channel_lock, defer_lock); + std::shared_lock server_channel_lock{ref_server->channel_tree_mutex, defer_lock}; + std::unique_lock client_channel_lock{this->channel_tree_mutex, defer_lock}; if(lock_channel) { server_channel_lock.lock(); client_channel_lock.lock(); } for (const auto& channel : targets) { - if(this->currentChannel == channel) continue; + if(this->currentChannel == channel) { + /* Do not unsubscribe from our own channel. */ + continue; + } - auto chan = this->channels->find_channel(channel); - if(!chan || !chan->subscribed) continue; - chan->subscribed = false; + auto local_channel = this->channel_tree->find_channel(channel); + if(!local_channel || !local_channel->subscribed) { + continue; + } - /* ref_server->getClientsByChannel only acquires channel client lock */ + local_channel->subscribed = false; + + /* getClientsByChannel() does not acquire the server channel tree mutex */ auto clients = this->server->getClientsByChannel(channel); - this->visibleClients.erase(std::remove_if(this->visibleClients.begin(), this->visibleClients.end(), [&, clients](const weak_ptr& weak) { - auto c = weak.lock(); - if(!c) { - logError(this->getServerId(), "{} Got \"dead\" client in visible client list! This can cause a remote client disconnect within the future!", CLIENT_STR_LOG_PREFIX); + this->visibleClients.erase(std::remove_if(this->visibleClients.begin(), this->visibleClients.end(), [&, clients](const std::weak_ptr& weak) { + auto visible_client = weak.lock(); + if(!visible_client) { return true; } - return std::find(clients.begin(), clients.end(), c) != clients.end(); + return std::find(clients.begin(), clients.end(), visible_client) != clients.end(); }), this->visibleClients.end()); unsubscribed_channels.push_back(channel); } - if (!unsubscribed_channels.empty()) + if (!unsubscribed_channels.empty()) { this->notifyChannelUnsubscribed(unsubscribed_channels); + } } - - return unsubscribed_channels; } bool ConnectedClient::isClientVisible(const std::shared_ptr& client, bool lock) { - auto client_list = this->getVisibleClients(lock); - for(const auto& entry : client_list) - if(entry.lock() == client) + std::shared_lock tree_lock(this->channel_tree_mutex, std::defer_lock); + if(lock) { + tree_lock.lock(); + } + + for(const auto& entry : this->visibleClients) { + if(entry.lock() == client) { return true; + } + } + return false; } @@ -714,18 +734,18 @@ inline void send_channels(ConnectedClient* client, ChannelIT begin, const Channe } void ConnectedClient::sendChannelList(bool lock_channel_tree) { - shared_lock server_channel_lock(this->server->channel_tree_lock, defer_lock); - unique_lock client_channel_lock(this->channel_lock, defer_lock); + shared_lock server_channel_lock(this->server->channel_tree_mutex, defer_lock); + unique_lock client_channel_lock(this->channel_tree_mutex, defer_lock); if(lock_channel_tree) { server_channel_lock.lock(); client_channel_lock.lock(); } - auto channels = this->channels->insert_channels(this->server->channelTree->tree_head(), true, false); + auto channels = this->channel_tree->insert_channels(this->server->channelTree->tree_head(), true, false); if(this->currentChannel) { bool send_success; - for(const auto& channel : this->channels->show_channel(this->server->channelTree->find_linked_entry(this->currentChannel->channelId()), send_success)) + for(const auto& channel : this->channel_tree->show_channel(this->server->channelTree->find_linked_entry(this->currentChannel->channelId()), send_success)) channels.push_back(channel); if(!send_success) logCritical(this->getServerId(), "ConnectedClient::sendChannelList => failed to insert default channel!"); @@ -768,23 +788,6 @@ void ConnectedClient::sendChannelList(bool lock_channel_tree) { this->sendCommand(Command("channellistfinished")); } -void ConnectedClient::sendChannelDescription(const std::shared_ptr& channel, bool lock) { - shared_lock tree_lock(this->channel_lock, defer_lock); - if(lock) - tree_lock.lock(); - - if(!this->channels->channel_visible(channel)) return; - - auto limit = this->getType() == CLIENT_TEAMSPEAK ? 8192 : 131130; - - auto description = channel->properties()[property::CHANNEL_DESCRIPTION].value(); - Command cmd("notifychanneledited"); - cmd["cid"] = channel->channelId(); - cmd["reasonid"] = 9; - cmd["channel_description"] = description.size() > limit ? description.substr(0, limit) : description; - this->sendCommand(cmd, true); -} - void ConnectedClient::tick_server(const std::chrono::system_clock::time_point &time) { ALARM_TIMER(A1, "ConnectedClient::tick", milliseconds(2)); if(this->state == ConnectionState::CONNECTED) { @@ -1067,7 +1070,7 @@ void ConnectedClient::update_displayed_client_groups(bool& server_groups_changed server_group_assignments = "0"; } - std::unique_lock view_lock{this->channel_lock}; + std::unique_lock view_lock{this->channel_tree_mutex}; this->cached_server_groups = server_groups; } @@ -1134,7 +1137,7 @@ do { \ permission::PermissionType ConnectedClient::calculate_and_get_join_state(const std::shared_ptr& channel) { std::shared_ptr ventry; { - shared_lock view_lock(this->channel_lock); + shared_lock view_lock(this->channel_tree_mutex); ventry = this->channel_view()->find_channel(channel); if(!ventry) { return permission::i_channel_view_power; diff --git a/server/src/client/ConnectedClient.h b/server/src/client/ConnectedClient.h index cb9dd72..5e42d92 100644 --- a/server/src/client/ConnectedClient.h +++ b/server/src/client/ConnectedClient.h @@ -114,19 +114,18 @@ namespace ts { inline std::shared_ptr getChannel(){ return this->currentChannel; } inline ChannelId getChannelId(){ auto channel = this->currentChannel; return channel ? channel->channelId() : 0; } - //bool channelSubscribed(const std::shared_ptr&); - /* if lock_channel == false then channel_lock must be write locked! */ - std::deque> subscribeChannel(const std::deque>& target, bool lock_channel, bool /* enforce */); - /* if lock_channel == false then channel_lock must be write locked! */ - std::deque> unsubscribeChannel(const std::deque>& target, bool lock_channel); + /* If called without locking the client channel tree **must** be write locked */ + void subscribeChannel(const std::deque>& target, bool /* lock server and client channel tree */, bool /* enforce */); + /* If called without locking the client channel tree **must** be write locked */ + void unsubscribeChannel(const std::deque>& target, bool /* lock server and client channel tree */); bool isClientVisible(const std::shared_ptr&, bool /* lock channel lock */); inline std::deque> getVisibleClients(bool lock_channel) { - std::shared_lock lock(this->channel_lock, std::defer_lock); + std::shared_lock lock(this->channel_tree_mutex, std::defer_lock); if(lock_channel) { lock.lock(); } - assert(mutex_shared_locked(this->channel_lock)); + assert(mutex_shared_locked(this->channel_tree_mutex)); return this->visibleClients; } @@ -305,12 +304,12 @@ namespace ts { return this->connectionStatistics; } - inline std::shared_ptr channel_view() { return this->channels; } + inline std::shared_ptr channel_view() { return this->channel_tree; } [[nodiscard]] inline std::shared_ptr ref() { return this->_this.lock(); } [[nodiscard]] inline std::weak_ptr weak_ref() { return this->_this; } - std::shared_mutex& get_channel_lock() { return this->channel_lock; } + std::shared_mutex& get_channel_lock() { return this->channel_tree_mutex; } /* Attention: Ensure that channel_lock has been locked */ [[nodiscard]] inline std::vector& current_server_groups() { return this->cached_server_groups; } @@ -414,8 +413,8 @@ namespace ts { bool block_flood = true; FloodPoints floodPoints = 0; - std::shared_ptr channels; - std::shared_mutex channel_lock; + std::shared_ptr channel_tree{}; + std::shared_mutex channel_tree_mutex{}; /* The permission overview which the client itself has (for basic client actions ) */ std::mutex client_needed_permissions_lock; @@ -690,7 +689,6 @@ namespace ts { void sendChannelList(bool lock_channel_tree); void sendServerInit(); void sendTSPermEditorWarning(); - void sendChannelDescription(const std::shared_ptr&, bool lock_tree); bool handleTextMessage(ChatMessageMode, std::string, const std::shared_ptr& /* sender target */); diff --git a/server/src/client/ConnectedClientNotifyHandler.cpp b/server/src/client/ConnectedClientNotifyHandler.cpp index 7c67802..15d1ad9 100644 --- a/server/src/client/ConnectedClientNotifyHandler.cpp +++ b/server/src/client/ConnectedClientNotifyHandler.cpp @@ -234,7 +234,7 @@ bool ConnectedClient::notifyServerGroupClientAdd( /* Deny any client moves 'till we've send the notify */ std::shared_lock channel_tree_lock{}; if(this->server) { - channel_tree_lock = std::shared_lock{this->server->channel_tree_lock}; + channel_tree_lock = std::shared_lock{this->server->channel_tree_mutex}; } if(!this->isClientVisible(target_client, true)) { @@ -264,7 +264,7 @@ bool ConnectedClient::notifyServerGroupClientRemove( /* Deny any client moves 'till we've send the notify */ std::shared_lock channel_tree_lock{}; if(this->server) { - channel_tree_lock = std::shared_lock{this->server->channel_tree_lock}; + channel_tree_lock = std::shared_lock{this->server->channel_tree_mutex}; } if(!this->isClientVisible(target_client, true)) { @@ -294,7 +294,7 @@ bool ConnectedClient::notifyClientChannelGroupChanged(std::optional channel_tree_lock{}; if(this->server) { - channel_tree_lock = std::shared_lock{this->server->channel_tree_lock}; + channel_tree_lock = std::shared_lock{this->server->channel_tree_mutex}; } /* No need to check if the channel is visible since if this is the case the client would not be visible as well. */ @@ -442,8 +442,8 @@ bool ConnectedClient::notifyClientMoved(const shared_ptr &clien assert(client->getClientId() > 0); assert(client->currentChannel); assert(target_channel); - sassert(mutex_shared_locked(this->channel_lock)); - sassert(mutex_shared_locked(client->channel_lock)); + sassert(mutex_shared_locked(this->channel_tree_mutex)); + sassert(mutex_shared_locked(client->channel_tree_mutex)); assert(this->isClientVisible(client, false) || &*client == this); Command mv("notifyclientmoved"); @@ -462,12 +462,12 @@ bool ConnectedClient::notifyClientMoved(const shared_ptr &clien } bool ConnectedClient::notifyClientUpdated(const std::shared_ptr &client, const deque &props, bool lock) { - std::shared_lock channel_lock(this->channel_lock, defer_lock); + std::shared_lock channel_lock(this->channel_tree_mutex, defer_lock); if(lock) { channel_lock.lock(); } - sassert(mutex_shared_locked(this->channel_lock)); + sassert(mutex_shared_locked(this->channel_tree_mutex)); if(!this->isClientVisible(client, false) && client != this) return false; @@ -515,7 +515,7 @@ bool ConnectedClient::notifyClientChatClosed(const shared_ptr & } bool ConnectedClient::notifyChannelMoved(const std::shared_ptr &channel, ChannelId order, const std::shared_ptr &invoker) { - if(!channel || !this->channels->channel_visible(channel)) return false; + if(!channel || !this->channel_tree->channel_visible(channel)) return false; Command notify("notifychannelmoved"); INVOKER(notify, invoker); @@ -546,13 +546,13 @@ bool ConnectedClient::notifyChannelHide(const std::deque &channel_ids if(channel_ids.empty()) return true; if(this->getType() == ClientType::CLIENT_TEAMSPEAK) { //Voice hasnt that event - shared_lock server_channel_lock(this->server->channel_tree_lock, defer_lock); - unique_lock client_channel_lock(this->channel_lock, defer_lock); + shared_lock server_channel_lock(this->server->channel_tree_mutex, defer_lock); + unique_lock client_channel_lock(this->channel_tree_mutex, defer_lock); if(lock_channel_tree) { server_channel_lock.lock(); client_channel_lock.lock(); } else { - assert(mutex_locked(this->channel_lock)); + assert(mutex_locked(this->channel_tree_mutex)); } deque> clients_to_remove; @@ -611,7 +611,7 @@ bool ConnectedClient::notifyChannelShow(const std::shared_ptr } bool ConnectedClient::notifyChannelDescriptionChanged(std::shared_ptr channel) { - if(!this->channels->channel_visible(channel)) return false; + if(!this->channel_tree->channel_visible(channel)) return false; Command notifyDesChanges("notifychanneldescriptionchanged"); notifyDesChanges["cid"] = channel->channelId(); this->sendCommand(notifyDesChanges); @@ -619,7 +619,7 @@ bool ConnectedClient::notifyChannelDescriptionChanged(std::shared_ptr channel) { - if(!this->channels->channel_visible(channel)) return false; + if(!this->channel_tree->channel_visible(channel)) return false; Command notifyDesChanges("notifychannelpasswordchanged"); notifyDesChanges["cid"] = channel->channelId(); this->sendCommand(notifyDesChanges); @@ -630,7 +630,7 @@ bool ConnectedClient::notifyClientEnterView(const std::shared_ptrgetClientId() > 0); sassert(to); sassert(!lock_channel_tree); /* we don't support locking */ - sassert(mutex_locked(this->channel_lock)); + sassert(mutex_locked(this->channel_tree_mutex)); sassert(!this->isClientVisible(client, false) || &*client == this); switch (reasonId) { @@ -676,9 +676,11 @@ bool ConnectedClient::notifyClientEnterView(const std::shared_ptr> &clients, const ts::ViewReasonSystemT &_vrs) { - if(clients.empty()) + if(clients.empty()) { return true; - assert(mutex_locked(this->channel_lock)); + } + + assert(mutex_locked(this->channel_tree_mutex)); Command cmd("notifycliententerview"); @@ -691,8 +693,9 @@ bool ConnectedClient::notifyClientEnterView(const std::dequeisClientVisible(client, false)) + if(this->isClientVisible(client, false)) { continue; + } auto channel = client->getChannel(); sassert(!channel || channel->channelId() != 0); @@ -731,7 +734,7 @@ bool ConnectedClient::notifyChannelEdited( const std::vector &properties, const std::shared_ptr &invoker, bool) { - auto v_channel = this->channels->find_channel(channel->channelId()); + auto v_channel = this->channel_tree->find_channel(channel->channelId()); if(!v_channel) return false; //Not visible? Important do not remove! bool send_description_change{false}; diff --git a/server/src/client/SpeakingClient.cpp b/server/src/client/SpeakingClient.cpp index b39881e..f7f9eda 100644 --- a/server/src/client/SpeakingClient.cpp +++ b/server/src/client/SpeakingClient.cpp @@ -43,7 +43,7 @@ bool SpeakingClient::shouldReceiveVoice(const std::shared_ptr & if(this->properties()[property::CLIENT_OUTPUT_MUTED].as_or(false)) return false; { - shared_lock client_lock(this->channel_lock); + shared_lock client_lock(this->channel_tree_mutex); for(const auto& entry : this->mutedClients) if(entry.lock() == sender) return false; @@ -55,7 +55,7 @@ bool SpeakingClient::shouldReceiveVoiceWhisper(const std::shared_ptrshouldReceiveVoice(sender)) return false; - return permission::v2::permission_granted(this->cpmerission_needed_whisper_power, sender->cpmerission_whisper_power, false); + return permission::v2::permission_granted(this->cpmerission_needed_whisper_power, sender->cpmerission_whisper_power); } bool SpeakingClient::should_handle_voice_packet(size_t) { @@ -396,16 +396,14 @@ command_result SpeakingClient::handleCommandClientInit(Command& cmd) { } /* must be triggered while helding an execute lock */ -//Note: Client permissions are may not really void SpeakingClient::processJoin() { TIMING_START(timings); - auto ref_server = this->server; - assert(ref_server); - this->resetIdleTime(); /* don't process any commands */ std::lock_guard command_lock_{this->command_lock}; + auto ref_server = this->server; + assert(ref_server); if(this->state != ConnectionState::INIT_HIGH) { logError(this->getServerId(), "{} Invalid processJoin() connection state!", CLIENT_STR_LOG_PREFIX); @@ -507,10 +505,10 @@ void SpeakingClient::processJoin() { this->properties()[property::CLIENT_CHANNEL_GROUP_ID] = "0"; this->properties()[property::CLIENT_TALK_POWER] = "0"; - unique_lock server_channel_lock(this->server->channel_tree_lock); + unique_lock server_channel_lock(this->server->channel_tree_mutex); this->server->client_move(this->ref(), channel, nullptr, "", ViewReasonId::VREASON_USER_ACTION, false, server_channel_lock); if(this->getType() != ClientType::CLIENT_TEAMSPEAK) { - std::lock_guard own_channel_lock{this->channel_lock}; + std::lock_guard own_channel_lock{this->channel_tree_mutex}; this->subscribeChannel({this->currentChannel}, false, true); /* su "improve" the TS3 clients join speed we send the channel clients a bit later, when the TS3 client gets his own client variables */ } } @@ -571,7 +569,7 @@ void SpeakingClient::processLeave() { if(server){ logMessage(this->getServerId(), "Voice client {}/{} ({}) from {} left.", this->getClientDatabaseId(), this->getUid(), this->getDisplayName(), this->getLoggingPeerIp() + ":" + to_string(this->getPeerPort())); { - unique_lock server_channel_lock(this->server->channel_tree_lock); + unique_lock server_channel_lock(this->server->channel_tree_mutex); server->unregisterClient(ownLock, "disconnected", server_channel_lock); /* already moves client to void if needed */ } server->music_manager_->cleanup_client_bots(this->getClientDatabaseId()); @@ -822,7 +820,7 @@ command_result SpeakingClient::handleCommandBroadcastVideo(Command &command) { switch(type) { case rtc::VideoBroadcastType::Screen: - if(!permission::v2::permission_granted(1, this->calculate_permission(permission::b_video_screen, this->getChannelId()), false)) { + if(!permission::v2::permission_granted(1, this->calculate_permission(permission::b_video_screen, this->getChannelId()))) { return ts::command_result{permission::b_video_screen}; } @@ -830,7 +828,7 @@ command_result SpeakingClient::handleCommandBroadcastVideo(Command &command) { case rtc::VideoBroadcastType::Camera: - if(!permission::v2::permission_granted(1, this->calculate_permission(permission::b_video_camera, this->getChannelId()), false)) { + if(!permission::v2::permission_granted(1, this->calculate_permission(permission::b_video_camera, this->getChannelId()))) { return ts::command_result{permission::b_video_camera}; } @@ -934,7 +932,7 @@ command_result SpeakingClient::handleCommandBroadcastVideoJoin(Command &cmd) { auto permission_max_streams = this->calculate_permission(permission::i_video_max_streams, this->getChannelId()); if(permission_max_streams.has_value) { - if(!permission::v2::permission_granted(camera_streams + screen_streams, permission_max_streams, false)) { + if(!permission::v2::permission_granted(camera_streams + screen_streams, permission_max_streams)) { return ts::command_result{permission::i_video_max_streams}; } } @@ -943,7 +941,7 @@ command_result SpeakingClient::handleCommandBroadcastVideoJoin(Command &cmd) { case rtc::VideoBroadcastType::Camera: { const auto permission_max_camera_streams = this->calculate_permission(permission::i_video_max_camera_streams, this->getChannelId()); if(permission_max_camera_streams.has_value) { - if(!permission::v2::permission_granted(camera_streams, permission_max_camera_streams, false)) { + if(!permission::v2::permission_granted(camera_streams, permission_max_camera_streams)) { return ts::command_result{permission::i_video_max_camera_streams}; } } @@ -953,7 +951,7 @@ command_result SpeakingClient::handleCommandBroadcastVideoJoin(Command &cmd) { case rtc::VideoBroadcastType::Screen: { const auto permission_max_screen_streams = this->calculate_permission(permission::i_video_max_camera_streams, this->getChannelId()); if(permission_max_screen_streams.has_value) { - if(!permission::v2::permission_granted(screen_streams, permission_max_screen_streams, false)) { + if(!permission::v2::permission_granted(screen_streams, permission_max_screen_streams)) { return ts::command_result{permission::i_video_max_screen_streams}; } } diff --git a/server/src/client/command_handler/channel.cpp b/server/src/client/command_handler/channel.cpp index cec026c..86d7adf 100644 --- a/server/src/client/command_handler/channel.cpp +++ b/server/src/client/command_handler/channel.cpp @@ -1,7 +1,7 @@ #include #include "../../InstanceHandler.h" -#include "../../build.h" +#include "../../PermissionCalculator.h" #include "../../manager/ActionLogger.h" #include "../../manager/ConversationManager.h" #include "../../manager/PermissionNameMapper.h" @@ -19,37 +19,53 @@ #include #include "./bulk_parsers.h" -#include "helpers.h" #include #include #include #include #include -#include using namespace std::chrono; using namespace std; using namespace ts; using namespace ts::server; -/* -TODO: Log missing permissions? -{findError("parameter_invalid"), "could not resolve permission " + (cmd[index].has("permid") ? cmd[index]["permid"].as() : cmd[index]["permsid"].as())}; -*/ 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}; + std::shared_lock channel_lock{this->channel_tree_mutex}; + if(!this->channel_tree->channel_visible(channel)) { + return ts::command_result{error::channel_invalid_id}; } - this->sendChannelDescription(channel, true); + ClientPermissionCalculator permission_calculator{this, channel}; + if(!permission_calculator.permission_granted(permission::b_channel_ignore_description_view_power, 1)) { + auto required_view_power = channel->permissions()->permission_value_flagged(permission::i_channel_needed_description_view_power); + required_view_power.clear_flag_on_zero(); + + if(!permission_calculator.permission_granted(permission::i_channel_description_view_power, required_view_power)) { + return command_result{permission::i_channel_description_view_power}; + } + } + + auto channel_description = channel->properties()[property::CHANNEL_DESCRIPTION].value(); + auto channel_description_limit = this->getType() == CLIENT_TEAMSPEAK ? 8192 : 131130; + + if(channel_description.length() > channel_description_limit) { + channel_description = channel_description.substr(0, channel_description_limit - 3); + channel_description.append("..."); + } + + ts::command_builder notify{this->notify_response_command("notifychanneledited")}; + notify.put_unchecked(0, "cid", channel->channelId()); + notify.put_unchecked(0, "reasonid", "9"); + notify.put_unchecked(0, "channel_description", channel_description); + this->sendCommand(notify); + return command_result{error::ok}; } @@ -57,31 +73,46 @@ command_result ConnectedClient::handleCommandChannelSubscribe(Command &cmd) { CMD_REF_SERVER(ref_server); CMD_RESET_IDLE; - bool flood_points = false; - deque> channels; + bool flood_points{false}; + std::deque> target_channels{}; + //target_channels.reserve(cmd.bulkCount()); + + ts::command_result_bulk result{}; + result.emplace_result_n(cmd.bulkCount(), error::ok); { - shared_lock server_channel_lock(this->server->channel_tree_lock); - unique_lock client_channel_lock(this->channel_lock); + std::shared_lock server_channel_lock{this->server->channel_tree_mutex}; + std::lock_guard client_channel_lock{this->channel_tree_mutex}; - 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"}; + for (int index{0}; index < cmd.bulkCount(); index++) { + auto target_channel_id = cmd[index]["cid"].as(); + auto local_channel = this->channel_view()->find_channel(target_channel_id); + if (!local_channel) { + result.set_result(index, ts::command_result{error::channel_invalid_id}); + continue; + } - auto channel = this->server->channelTree->findChannel(cmd[index]["cid"].as()); - if (!channel) - return command_result{error::channel_invalid_id, "Cant resolve channel"}; + auto channel = this->server->channelTree->findChannel(target_channel_id); + if (!channel) { + result.set_result(index, ts::command_result{error::channel_invalid_id}); + continue; + } - channels.push_back(channel); - if (!flood_points && system_clock::now() - local_channel->view_timestamp > seconds(5)) { + target_channels.push_back(channel); + if (!flood_points && std::chrono::system_clock::now() - local_channel->view_timestamp > seconds(5)) { flood_points = true; - CMD_CHK_AND_INC_FLOOD_POINTS(15); + + this->increaseFloodPoints(15); + if(this->shouldFloodBlock()) { + result.set_result(index, ts::command_result{error::ban_flooding}); + continue; + } } } - if (!channels.empty()) - this->subscribeChannel(channels, false, false); + if (!target_channels.empty()) { + this->subscribeChannel(target_channels, false, false); + } } return command_result{error::ok}; @@ -92,8 +123,8 @@ command_result ConnectedClient::handleCommandChannelSubscribeAll(Command &cmd) { 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); + std::shared_lock server_channel_lock{this->server->channel_tree_mutex}; + std::lock_guard client_channel_lock{this->channel_tree_mutex}; this->subscribeChannel(this->server->channelTree->channels(), false, false); this->subscribeToAll = true; } @@ -104,20 +135,28 @@ 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); + ts::command_result_bulk result{}; + result.emplace_result_n(cmd.bulkCount(), error::ok); - deque> channels; + { + std::shared_lock server_channel_lock{this->server->channel_tree_mutex}; + std::lock_guard client_channel_lock{this->channel_tree_mutex}; + + std::deque> channels{}; for (int index = 0; index < cmd.bulkCount(); index++) { auto channel = this->server->channelTree->findChannel(cmd["cid"].as()); - if (!channel) continue; + if (!channel) { + result.set_result(index, ts::command_result{error::channel_invalid_id}); + continue; + } + channels.push_front(channel); } this->unsubscribeChannel(channels, false); } - return command_result{error::ok}; + + return command_result{std::move(result)}; } command_result ConnectedClient::handleCommandChannelUnsubscribeAll(Command &cmd) { @@ -125,11 +164,12 @@ command_result ConnectedClient::handleCommandChannelUnsubscribeAll(Command &cmd) 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); + std::shared_lock server_channel_lock{this->server->channel_tree_mutex}; + std::lock_guard client_channel_lock{this->channel_tree_mutex}; this->unsubscribeChannel(this->server->channelTree->channels(), false); this->subscribeToAll = false; } + return command_result{error::ok}; } @@ -257,7 +297,7 @@ command_result ConnectedClient::handleCommandChannelCreate(Command &cmd) { CMD_CHK_PARM_COUNT(1); auto target_tree = this->server ? this->server->channelTree : &*serverInstance->getChannelTree(); - std::shared_lock channel_tree_read_lock{this->server ? this->server->channel_tree_lock : serverInstance->getChannelTreeLock()}; + std::shared_lock channel_tree_read_lock{this->server ? this->server->channel_tree_mutex : serverInstance->getChannelTreeLock()}; ChannelId parent_channel_id = cmd[0].has("cpid") ? cmd["cpid"].as() : 0; #define test_permission(required, permission_type) \ @@ -610,7 +650,7 @@ command_result ConnectedClient::handleCommandChannelCreate(Command &cmd) { if (created_channel->channelType() == ChannelType::temporary && (this->getType() == ClientType::CLIENT_TEAMSPEAK || this->getType() == ClientType::CLIENT_WEB || this->getType() == ClientType::CLIENT_TEASPEAK)) { channel_tree_read_lock.unlock(); - std::unique_lock channel_tree_write_lock{this->server->channel_tree_lock}; + std::unique_lock channel_tree_write_lock{this->server->channel_tree_mutex}; this->server->client_move( this->ref(), created_channel, @@ -990,8 +1030,8 @@ ts::command_result ConnectedClient::execute_channel_edit(ChannelId& channel_id, } } - auto channel_tree = server ? server->getChannelTree() : &*serverInstance->getChannelTree(); - std::unique_lock channel_tree_lock{server ? server->channel_tree_lock : serverInstance->getChannelTreeLock()}; + auto target_channel_tree = server ? server->getChannelTree() : &*serverInstance->getChannelTree(); + std::unique_lock channel_tree_lock{server ? server->channel_tree_mutex : serverInstance->getChannelTreeLock()}; struct TemporaryCreatedChannel { ServerChannelTree* channel_tree; @@ -1002,7 +1042,7 @@ ts::command_result ConnectedClient::execute_channel_edit(ChannelId& channel_id, channel_tree->deleteChannelRoot(this->channel); } - } temporary_created_channel{channel_tree, nullptr}; + } temporary_created_channel{target_channel_tree, nullptr}; std::shared_ptr linked_channel; std::shared_ptr channel; @@ -1019,31 +1059,31 @@ ts::command_result ConnectedClient::execute_channel_edit(ChannelId& channel_id, { std::shared_ptr parent_channel{}; if(parent_id > 0) { - parent_channel = channel_tree->findChannel(parent_id); + parent_channel = target_channel_tree->findChannel(parent_id); if(!parent_channel) { return command_result{error::channel_invalid_id}; } } - if(channel_tree->findChannel(channel_name, parent_channel)) { + if(target_channel_tree->findChannel(channel_name, parent_channel)) { return ts::command_result{error::channel_name_inuse}; } } - channel = dynamic_pointer_cast(channel_tree->createChannel(parent_id, order_id, channel_name)); + channel = dynamic_pointer_cast(target_channel_tree->createChannel(parent_id, order_id, channel_name)); if(!channel) { return command_result{error::vs_critical, "channel create failed"}; } temporary_created_channel.channel = channel; - linked_channel = channel_tree->findLinkedChannel(channel->channelId()); + linked_channel = target_channel_tree->findLinkedChannel(channel->channelId()); if(!linked_channel) { return command_result{error::vs_critical, "missing linked channel"}; } channel_id = channel->channelId(); } else { - linked_channel = channel_tree->findLinkedChannel(channel_id); + linked_channel = target_channel_tree->findLinkedChannel(channel_id); channel = dynamic_pointer_cast(linked_channel->entry); } @@ -1173,7 +1213,7 @@ ts::command_result ConnectedClient::execute_channel_edit(ChannelId& channel_id, if(updating_name) { auto new_name = changed_values[property::CHANNEL_NAME]; - if(channel_tree->findChannel(new_name, channel->parent())) { + if(target_channel_tree->findChannel(new_name, channel->parent())) { return ts::command_result{error::channel_name_inuse}; } } @@ -1375,12 +1415,12 @@ ts::command_result ConnectedClient::execute_channel_edit(ChannelId& channel_id, ChannelId previous_channel_id{0}; if(updating_sort_order) { previous_channel_id = converter::from_string_view(changed_values[property::CHANNEL_ORDER]); - auto previous_channel = channel_tree->findChannel(previous_channel_id); + auto previous_channel = target_channel_tree->findChannel(previous_channel_id); if(!previous_channel && previous_channel_id != 0) { return command_result{error::channel_invalid_id}; } - if(!channel_tree->move_channel(channel, channel->parent(), previous_channel)) { + if(!target_channel_tree->move_channel(channel, channel->parent(), previous_channel)) { return ts::command_result{error::vs_critical, "failed to move channel"}; } } @@ -1395,7 +1435,7 @@ ts::command_result ConnectedClient::execute_channel_edit(ChannelId& channel_id, auto current_channel = children_left.front(); children_left.pop_front(); - for (const auto &child : channel_tree->channels(current_channel, 1)) { + for (const auto &child : target_channel_tree->channels(current_channel, 1)) { if (child == current_channel) { continue; } @@ -1415,7 +1455,7 @@ ts::command_result ConnectedClient::execute_channel_edit(ChannelId& channel_id, std::shared_ptr old_default_channel{}; if(updating_default) { - old_default_channel = channel_tree->getDefaultChannel(); + old_default_channel = target_channel_tree->getDefaultChannel(); if(old_default_channel) { old_default_channel->properties()[property::CHANNEL_FLAG_DEFAULT] = false; } @@ -1472,8 +1512,8 @@ ts::command_result ConnectedClient::execute_channel_edit(ChannelId& channel_id, if(updating_sort_order) { auto parent = channel->parent(); - linked_parent_channel = parent ? channel_tree->findLinkedChannel(parent->channelId()) : nullptr; - linked_previous_channel = channel_tree->findLinkedChannel(channel->previousChannelId()); + linked_parent_channel = parent ? target_channel_tree->findLinkedChannel(parent->channelId()) : nullptr; + linked_previous_channel = target_channel_tree->findLinkedChannel(channel->previousChannelId()); assert(!parent || linked_parent_channel); assert(linked_previous_channel || channel->previousChannelId() == 0); @@ -1505,7 +1545,7 @@ ts::command_result ConnectedClient::execute_channel_edit(ChannelId& channel_id, continue; } - std::unique_lock client_tree_lock{client->channel_lock}; + std::unique_lock client_tree_lock{client->channel_tree_mutex}; for(const auto& child_channel : child_channel_type_updates) { client->notifyChannelEdited(child_channel, child_channel_type_property_updates, self_ref, false); } @@ -1681,11 +1721,12 @@ command_result ConnectedClient::handleCommandChannelMove(Command &cmd) { 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) + unique_lock channel_lock(client->channel_tree_mutex); + 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); + auto actions = client->channel_tree->change_order(l_channel, l_parent, l_order); std::deque deletions; for (const auto &action : actions) { switch (action.first) { @@ -1705,8 +1746,10 @@ command_result ConnectedClient::handleCommandChannelMove(Command &cmd) { break; } } - if (!deletions.empty()) + + if (!deletions.empty()) { client->notifyChannelHide(deletions, false); + } }); } @@ -2002,12 +2045,12 @@ command_result ConnectedClient::handleCommandChannelClientDelPerm(Command &cmd) if (elm->currentChannel == channel) { elm->task_update_channel_client_properties.enqueue(); } else if (update_view) { - unique_lock client_channel_lock(this->channel_lock); + unique_lock client_channel_lock(this->channel_tree_mutex); 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()))) { + for (const auto &update_entry : elm->channel_tree->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 @@ -2070,12 +2113,12 @@ command_result ConnectedClient::handleCommandChannelClientAddPerm(Command &cmd) if (elm->currentChannel == channel) { elm->task_update_channel_client_properties.enqueue(); } else if (update_view) { - unique_lock client_channel_lock(this->channel_lock); + unique_lock client_channel_lock(this->channel_tree_mutex); 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()))) { + for (const auto &update_entry : elm->channel_tree->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 diff --git a/server/src/client/command_handler/client.cpp b/server/src/client/command_handler/client.cpp index cff4119..97a4e85 100644 --- a/server/src/client/command_handler/client.cpp +++ b/server/src/client/command_handler/client.cpp @@ -3,7 +3,6 @@ #include #include #include -#include "../../build.h" #include "../ConnectedClient.h" #include "../InternalClient.h" #include "../voice/VoiceClient.h" @@ -15,6 +14,7 @@ #include "../../manager/ConversationManager.h" #include "../../manager/PermissionNameMapper.h" #include "../../manager/ActionLogger.h" +#include "../../PermissionCalculator.h" #include #include "helpers.h" @@ -22,7 +22,6 @@ #include #include -#include #include #include #include @@ -39,7 +38,7 @@ command_result ConnectedClient::handleCommandClientGetVariables(Command &cmd) { CMD_REQ_SERVER; ConnectedLockedClient client{this->server->find_client_by_id(cmd["clid"].as())}; { - shared_lock tree_lock(this->channel_lock); + shared_lock tree_lock(this->channel_tree_mutex); if (!client || (client.client != this && !this->isClientVisible(client.client, false))) return command_result{error::client_invalid_id, ""}; @@ -131,7 +130,7 @@ command_result ConnectedClient::handleCommandClientGetIds(Command &cmd) { auto unique_id = cmd[index]["cluid"].as(); for(const auto& entry : client_list) { if(entry->getUid() == unique_id) { - if(!config::server::show_invisible_clients_as_online && !this->channels->channel_visible(entry->currentChannel, nullptr)) + if(!config::server::show_invisible_clients_as_online && !this->channel_tree->channel_visible(entry->currentChannel, nullptr)) continue; notify[result_index]["name"] = entry->getDisplayName(); @@ -160,22 +159,20 @@ command_result ConnectedClient::handleCommandClientMove(Command &cmd) { CMD_RESET_IDLE; CMD_CHK_AND_INC_FLOOD_POINTS(10); - shared_lock server_channel_r_lock(this->server->channel_tree_lock); + std::unique_lock server_channel_lock{this->server->channel_tree_mutex}; - auto channel = this->server->channelTree->findChannel(cmd["cid"].as()); - if (!channel) { + auto target_channel = this->server->channelTree->findChannel(cmd["cid"].as()); + if (!target_channel) { return command_result{error::channel_invalid_id}; } - auto permission_cache = make_shared(); - auto& channel_whitelist = this->join_whitelisted_channel; - auto whitelist_entry = std::find_if(channel_whitelist.begin(), channel_whitelist.end(), [&](const auto& entry) { return entry.first == channel->channelId(); }); + auto whitelist_entry = std::find_if(channel_whitelist.begin(), channel_whitelist.end(), [&](const auto& entry) { return entry.first == target_channel->channelId(); }); if(whitelist_entry != channel_whitelist.end()) { - debugMessage(this->getServerId(), "{} Allowing client to join channel {} because the token he used earlier explicitly allowed it.", this->getLoggingPrefix(), channel->channelId()); + debugMessage(this->getServerId(), "{} Allowing client to join channel {} because the token he used earlier explicitly allowed it.", this->getLoggingPrefix(), target_channel->channelId()); if(whitelist_entry->second != "ignore") { - if (!channel->passwordMatch(cmd["cpw"], true)) { - if (!permission::v2::permission_granted(1, this->calculate_permission(permission::b_channel_join_ignore_password, channel->channelId()))) { + if (!target_channel->verify_password(cmd["cpw"].optional_string(), this->getType() != ClientType::CLIENT_QUERY)) { + if (!permission::v2::permission_granted(1, this->calculate_permission(permission::b_channel_join_ignore_password, target_channel->channelId()))) { return command_result{error::channel_invalid_password}; } } @@ -185,13 +182,13 @@ command_result ConnectedClient::handleCommandClientMove(Command &cmd) { cmd["cpw"] = ""; } - if (!channel->passwordMatch(cmd["cpw"], true)) { - if (!permission::v2::permission_granted(1, this->calculate_permission(permission::b_channel_join_ignore_password, channel->channelId()))) { + if (!target_channel->verify_password(cmd["cpw"].optional_string(), this->getType() != ClientType::CLIENT_QUERY)) { + if (!permission::v2::permission_granted(1, this->calculate_permission(permission::b_channel_join_ignore_password, target_channel->channelId()))) { return command_result{error::channel_invalid_password}; } } - auto permission_error = this->calculate_and_get_join_state(channel); + auto permission_error = this->calculate_and_get_join_state(target_channel); if(permission_error != permission::unknown) { return command_result{permission_error}; } @@ -201,7 +198,7 @@ command_result ConnectedClient::handleCommandClientMove(Command &cmd) { command_result_bulk result{}; result.reserve(cmd.bulkCount()); - std::vector> clients{}; + std::vector> target_clients{}; for(size_t index{0}; index < cmd.bulkCount(); index++) { auto target_client_id = cmd[index]["clid"].as(); @@ -217,7 +214,7 @@ command_result ConnectedClient::handleCommandClientMove(Command &cmd) { continue; } } - if(target_client->getChannel() == channel) { + if(target_client->getChannel() == target_channel) { result.emplace_result(error::ok); continue; } @@ -227,96 +224,90 @@ command_result ConnectedClient::handleCommandClientMove(Command &cmd) { result.emplace_result(permission::i_client_move_power); continue; } - if(!permission::v2::permission_granted(target_client->calculate_permission(permission::i_client_needed_move_power, channel->channelId()), this->calculate_permission(permission::i_client_move_power, channel->channelId()))) { + if(!permission::v2::permission_granted(target_client->calculate_permission(permission::i_client_needed_move_power, target_channel->channelId()), this->calculate_permission(permission::i_client_move_power, target_channel->channelId()))) { result.emplace_result(permission::i_client_move_power); continue; } } - clients.emplace_back(std::move(target_client)); + target_clients.emplace_back(std::move(target_client)); result.emplace_result(error::ok); } + ClientPermissionCalculator own_permission_calculator{this, target_channel}; + /* FIXME: Some kind of invite key frags to prevent limit checking! */ - if (!channel->properties()[property::CHANNEL_FLAG_MAXCLIENTS_UNLIMITED].as_unchecked() || !channel->properties()[property::CHANNEL_FLAG_MAXFAMILYCLIENTS_UNLIMITED].as_unchecked()) { - if(!permission::v2::permission_granted(1, this->calculate_permission(permission::b_channel_join_ignore_maxclients, channel->channelId()))) { - if(!channel->properties()[property::CHANNEL_FLAG_MAXCLIENTS_UNLIMITED].as_unchecked()) { - auto maxClients = channel->properties()[property::CHANNEL_MAXCLIENTS].as_unchecked(); - if (maxClients >= 0 && maxClients < this->server->getClientsByChannel(channel).size() + clients.size()) { - return command_result{error::channel_maxclients_reached}; - } - } - if(!channel->properties()[property::CHANNEL_FLAG_MAXFAMILYCLIENTS_UNLIMITED].as_unchecked()) { - shared_ptr family_root; - - if(channel->properties()[property::CHANNEL_FLAG_MAXFAMILYCLIENTS_INHERITED].as_unchecked()) { - family_root = channel; - while(family_root && - family_root->properties()[property::CHANNEL_FLAG_MAXFAMILYCLIENTS_INHERITED].as_unchecked()) { - family_root = family_root->parent(); - } - } - if(family_root && !family_root->properties()[property::CHANNEL_FLAG_MAXFAMILYCLIENTS_UNLIMITED]) { //Could not be CHANNEL_FLAG_MAXFAMILYCLIENTS_INHERITED - auto maxClients = family_root->properties()[property::CHANNEL_MAXFAMILYCLIENTS].as_unchecked(); - auto client_count = 0; - for(const auto& entry : this->server->getClientsByChannelRoot(channel, false)) { - if(entry.get() != this) { - client_count++; //Dont count the client itself - } - } - - if (maxClients >= 0 && maxClients < client_count + clients.size()) { - return command_result{error::channel_maxfamily_reached}; - } - } - } + if (!target_channel->properties()[property::CHANNEL_FLAG_MAXCLIENTS_UNLIMITED].as_or(true)) { + auto max_clients = target_channel->properties()[property::CHANNEL_MAXCLIENTS].as_or(0); + auto channel_clients = this->server->getClientsByChannel(target_channel); + if(channel_clients.size() >= max_clients && !own_permission_calculator.permission_granted(permission::b_channel_join_ignore_maxclients, 1)) { + return command_result{error::channel_maxclients_reached}; } } - server_channel_r_lock.unlock(); - unique_lock server_channel_w_lock(this->server->channel_tree_lock); - std::vector> channels{}; - channels.reserve(clients.size()); + if(!target_channel->properties()[property::CHANNEL_FLAG_MAXFAMILYCLIENTS_UNLIMITED].as_or(true)) { + auto base_channel{target_channel}; + while(base_channel && base_channel->properties()[property::CHANNEL_FLAG_MAXFAMILYCLIENTS_INHERITED].as_or(false)) { + base_channel = base_channel->parent(); + } - for(auto& client : clients) { - auto oldChannel = client->getChannel(); - if(!oldChannel) { + if(base_channel) { + auto max_clients = target_channel->properties()[property::CHANNEL_MAXFAMILYCLIENTS].as_or(0); + auto current_client_count = this->server->countChannelRootClients(target_channel, max_clients, false); + if(current_client_count >= max_clients && !own_permission_calculator.permission_granted(permission::b_channel_join_ignore_maxclients, 1)) { + return command_result{error::channel_maxfamily_reached}; + } + } else { + /* This is kinda odd, I guess we've moved the channel and haven't cleared the inherited flag */ + } + } + + std::vector> channels{}; + channels.reserve(target_clients.size()); + + for(auto& client : target_clients) { + auto client_old_channel = dynamic_pointer_cast(client->getChannel()); + if(!client_old_channel) { continue; } this->server->client_move( client.client, - channel, + target_channel, client.client == this ? nullptr : this->ref(), "", client.client == this ? ViewReasonId::VREASON_USER_ACTION : ViewReasonId::VREASON_MOVED, true, - server_channel_w_lock + server_channel_lock ); - serverInstance->action_logger()->client_channel_logger.log_client_move(this->getServerId(), this->ref(), client->ref(), channel->channelId(), channel->name(), oldChannel->channelId(), oldChannel->name()); + serverInstance->action_logger()->client_channel_logger.log_client_move(this->getServerId(), this->ref(), client->ref(), target_channel->channelId(), target_channel->name(), client_old_channel->channelId(), client_old_channel->name()); - if(std::find_if(channels.begin(), channels.end(), [&](const std::shared_ptr& channel) { return &*channel == &*oldChannel; }) == channels.end()) { - channels.push_back(oldChannel); + if(std::find_if(channels.begin(), channels.end(), [&](const std::shared_ptr& channel) { return &*channel == &*client_old_channel; }) == channels.end()) { + channels.push_back(client_old_channel); } } for(const auto& oldChannel : channels) { - if(!server_channel_w_lock.owns_lock()) { - server_channel_w_lock.lock(); + if(!server_channel_lock.owns_lock()) { + server_channel_lock.lock(); } - if(oldChannel->channelType() == ChannelType::temporary && - oldChannel->properties()[property::CHANNEL_DELETE_DELAY].as_unchecked() == 0) { - if(this->server->getClientsByChannelRoot(oldChannel, false).empty()) { - this->server->delete_channel(dynamic_pointer_cast(oldChannel), this->ref(), "temporary auto delete", server_channel_w_lock, true); - } + if(oldChannel->channelType() != ChannelType::temporary) { + continue; } - if(server_channel_w_lock.owns_lock()) { - server_channel_w_lock.unlock(); + if(oldChannel->properties()[property::CHANNEL_DELETE_DELAY].as_unchecked() > 0) { + continue; } + + if(!this->server->isChannelRootEmpty(oldChannel, false)) { + continue; + } + + this->server->delete_channel(oldChannel, this->ref(), "temporary auto delete", server_channel_lock, true); } - return command_result{std::forward(result)}; + + return command_result{std::move(result)}; } command_result ConnectedClient::handleCommandClientPoke(Command &cmd) { @@ -388,7 +379,7 @@ command_result ConnectedClient::handleCommandClientChatClosed(Command &cmd) { ConnectedLockedClient client{this->server->find_client_by_id(cmd["clid"].as())}; if (!client) return command_result{error::client_invalid_id}; { - unique_lock channel_lock(this->channel_lock); + unique_lock channel_lock(this->channel_tree_mutex); this->open_private_conversations.erase(remove_if(this->open_private_conversations.begin(), this->open_private_conversations.end(), [&](const weak_ptr& weak) { return weak.lock() == client; }), this->open_private_conversations.end()); @@ -760,7 +751,7 @@ command_result ConnectedClient::handleCommandClientMute(Command &cmd) { if (!client || client->getClientId() == this->getClientId()) return command_result{error::client_invalid_id}; { - unique_lock channel_lock(this->channel_lock); + unique_lock channel_lock(this->channel_tree_mutex); for(const auto& weak : this->mutedClients) if(weak.lock() == client) return command_result{error::ok}; this->mutedClients.push_back(client.client); @@ -780,7 +771,7 @@ command_result ConnectedClient::handleCommandClientUnmute(Command &cmd) { if (!client || client->getClientId() == this->getClientId()) return command_result{error::client_invalid_id}; { - unique_lock channel_lock(this->channel_lock); + unique_lock channel_lock(this->channel_tree_mutex); this->mutedClients.erase(std::remove_if(this->mutedClients.begin(), this->mutedClients.end(), [&](const weak_ptr& weak) { auto c = weak.lock(); return !c || c == client; diff --git a/server/src/client/command_handler/file.cpp b/server/src/client/command_handler/file.cpp index ed83a05..3f38931 100644 --- a/server/src/client/command_handler/file.cpp +++ b/server/src/client/command_handler/file.cpp @@ -61,8 +61,10 @@ command_result ConnectedClient::handleCommandFTGetFileList(Command &cmd) { if (!channel) return command_result{error::channel_invalid_id}; - if (!channel->passwordMatch(cmd["cpw"]) && !permission::v2::permission_granted(1, this->calculate_permission(permission::b_ft_ignore_password, channel->channelId()))) - return cmd["cpw"].string().empty() ? command_result{permission::b_ft_ignore_password} : command_result{error::channel_invalid_password}; + auto channel_password = cmd["cpw"].optional_string(); + if (!channel->verify_password(channel_password, this->getType() != ClientType::CLIENT_QUERY) && !permission::v2::permission_granted(1, this->calculate_permission(permission::b_ft_ignore_password, channel->channelId()))) { + return channel_password.has_value() ? command_result{error::channel_invalid_password} : command_result{permission::b_ft_ignore_password}; + } if(!channel->permission_granted(permission::i_ft_needed_file_browse_power, this->calculate_permission(permission::i_ft_file_browse_power, channel->channelId()), true)) return command_result{permission::i_ft_file_browse_power}; @@ -184,13 +186,15 @@ command_result ConnectedClient::handleCommandFTCreateDir(Command &cmd) { if(!virtual_file_server) return command_result{error::file_virtual_server_not_registered}; auto& file_system = file::server()->file_system(); - std::shared_lock channel_tree_lock{this->server->channel_tree_lock}; + std::shared_lock channel_tree_lock{this->server->channel_tree_mutex}; auto channel = this->server->channelTree->findChannel(cmd["cid"].as()); if (!channel) return command_result{error::channel_invalid_id}; - if (!channel->passwordMatch(cmd["cpw"]) && !permission::v2::permission_granted(1, this->calculate_permission(permission::b_ft_ignore_password, channel->channelId()))) - return cmd["cpw"].string().empty() ? command_result{permission::b_ft_ignore_password} : command_result{error::channel_invalid_password}; + auto channel_password = cmd["cpw"].optional_string(); + if (!channel->verify_password(channel_password, this->getType() != ClientType::CLIENT_QUERY) && !permission::v2::permission_granted(1, this->calculate_permission(permission::b_ft_ignore_password, channel->channelId()))) { + return channel_password.has_value() ? command_result{error::channel_invalid_password} : command_result{permission::b_ft_ignore_password}; + } if(!channel->permission_granted(permission::i_ft_needed_directory_create_power, this->calculate_permission(permission::i_ft_directory_create_power, channel->channelId()), true)) return command_result{permission::i_ft_directory_create_power}; @@ -242,13 +246,15 @@ command_result ConnectedClient::handleCommandFTDeleteFile(Command &cmd) { auto file_path = cmd["path"].string(); std::shared_ptr> delete_response{}; if (cmd[0].has("cid") && cmd["cid"] != 0) { - std::shared_lock channel_tree_lock{this->server->channel_tree_lock}; + std::shared_lock channel_tree_lock{this->server->channel_tree_mutex}; auto channel = this->server->channelTree->findChannel(cmd["cid"].as()); if (!channel) return command_result{error::channel_invalid_id}; - if (!channel->passwordMatch(cmd["cpw"]) && !permission::v2::permission_granted(1, this->calculate_permission(permission::b_ft_ignore_password, channel->channelId()))) - return cmd["cpw"].string().empty() ? command_result{permission::b_ft_ignore_password} : command_result{error::channel_invalid_password}; + auto channel_password = cmd["cpw"].optional_string(); + if (!channel->verify_password(channel_password, this->getType() != ClientType::CLIENT_QUERY) && !permission::v2::permission_granted(1, this->calculate_permission(permission::b_ft_ignore_password, channel->channelId()))) { + return channel_password.has_value() ? command_result{error::channel_invalid_password} : command_result{permission::b_ft_ignore_password}; + } if(!channel->permission_granted(permission::i_ft_needed_file_delete_power, this->calculate_permission(permission::i_ft_file_delete_power, channel->channelId()), true)) return command_result{permission::i_ft_file_delete_power}; @@ -447,7 +453,7 @@ command_result ConnectedClient::handleCommandFTGetFileInfo(ts::Command &cmd) { currentChannel = this->server->channelTree->findChannel(cmd[index]["cid"].as()); if(currentChannel) { const auto password = cmd[index]["cpw"].string(); - if (!currentChannel->passwordMatch(password) && !permission::v2::permission_granted(1, this->calculate_permission(permission::b_ft_ignore_password, currentChannel->channelId()))) { + if (!currentChannel->verify_password(std::make_optional(password), this->getType() != ClientType::CLIENT_QUERY) && !permission::v2::permission_granted(1, this->calculate_permission(permission::b_ft_ignore_password, currentChannel->channelId()))) { if(password.empty()) error_error = error::channel_invalid_password; else @@ -667,13 +673,15 @@ command_result ConnectedClient::handleCommandFTInitUpload(ts::Command &cmd) { ChannelId log_channel_id{0}; if(cmd[0].has("cid") && cmd["cid"] != 0) { //Channel - std::shared_lock channel_tree_lock{this->server->channel_tree_lock}; + std::shared_lock channel_tree_lock{this->server->channel_tree_mutex}; auto channel = this->server->channelTree->findChannel(cmd["cid"].as()); if (!channel) return command_result{error::channel_invalid_id, "Cant resolve channel"}; - if (!channel->passwordMatch(cmd["cpw"]) && !permission::v2::permission_granted(1, this->calculate_permission(permission::b_ft_ignore_password, channel->channelId()))) - return cmd["cpw"].string().empty() ? command_result{permission::b_ft_ignore_password} : command_result{error::channel_invalid_password}; + auto channel_password = cmd["cpw"].optional_string(); + if (!channel->verify_password(channel_password, this->getType() != ClientType::CLIENT_QUERY) && !permission::v2::permission_granted(1, this->calculate_permission(permission::b_ft_ignore_password, channel->channelId()))) { + return channel_password.has_value() ? command_result{error::channel_invalid_password} : command_result{permission::b_ft_ignore_password}; + } ACTION_REQUIRES_CHANNEL_PERMISSION(channel, permission::i_ft_needed_file_upload_power, permission::i_ft_file_upload_power, true); transfer_response = file::server()->file_transfer().initialize_channel_transfer(file::transfer::Transfer::DIRECTION_UPLOAD, virtual_file_server, channel->channelId(), info); @@ -810,13 +818,15 @@ command_result ConnectedClient::handleCommandFTInitDownload(ts::Command &cmd) { ChannelId log_channel_id{0}; if(cmd[0].has("cid") && cmd["cid"] != 0) { //Channel - std::shared_lock channel_tree_lock{this->server->channel_tree_lock}; + std::shared_lock channel_tree_lock{this->server->channel_tree_mutex}; auto channel = this->server->channelTree->findChannel(cmd["cid"].as()); if (!channel) return command_result{error::channel_invalid_id}; - if (!channel->passwordMatch(cmd["cpw"]) && !permission::v2::permission_granted(1, this->calculate_permission(permission::b_ft_ignore_password, channel->channelId()))) - return cmd["cpw"].string().empty() ? command_result{permission::b_ft_ignore_password} : command_result{error::channel_invalid_password}; + auto channel_password = cmd["cpw"].optional_string(); + if (!channel->verify_password(channel_password, this->getType() != ClientType::CLIENT_QUERY) && !permission::v2::permission_granted(1, this->calculate_permission(permission::b_ft_ignore_password, channel->channelId()))) { + return channel_password.has_value() ? command_result{error::channel_invalid_password} : command_result{permission::b_ft_ignore_password}; + } ACTION_REQUIRES_CHANNEL_PERMISSION(channel, permission::i_ft_needed_file_download_power, permission::i_ft_file_download_power, true); transfer_response = file::server()->file_transfer().initialize_channel_transfer(file::transfer::Transfer::DIRECTION_DOWNLOAD, virtual_file_server, channel->channelId(), info); @@ -912,13 +922,15 @@ command_result ConnectedClient::handleCommandFTRenameFile(ts::Command &cmd) { auto channel_id = cmd["cid"].as(); auto target_channel_id = cmd[0].has("tcid") ? cmd["tcid"].as() : channel_id; - std::shared_lock channel_tree_lock{this->server->channel_tree_lock}; + std::shared_lock channel_tree_lock{this->server->channel_tree_mutex}; auto channel = this->server->channelTree->findChannel(channel_id); if (!channel) return command_result{error::channel_invalid_id}; - if (!channel->passwordMatch(cmd["cpw"]) && !permission::v2::permission_granted(1, this->calculate_permission(permission::b_ft_ignore_password, channel->channelId()))) - return cmd["cpw"].string().empty() ? command_result{permission::b_ft_ignore_password} : command_result{error::channel_invalid_password}; + auto channel_password = cmd["cpw"].optional_string(); + if (!channel->verify_password(channel_password, this->getType() != ClientType::CLIENT_QUERY) && !permission::v2::permission_granted(1, this->calculate_permission(permission::b_ft_ignore_password, channel->channelId()))) { + return channel_password.has_value() ? command_result{error::channel_invalid_password} : command_result{permission::b_ft_ignore_password}; + } ACTION_REQUIRES_CHANNEL_PERMISSION(channel, permission::i_ft_needed_file_rename_power, permission::i_ft_file_rename_power, true); @@ -927,8 +939,10 @@ command_result ConnectedClient::handleCommandFTRenameFile(ts::Command &cmd) { if (!targetChannel) return command_result{error::channel_invalid_id}; - if (!channel->passwordMatch(cmd["tcpw"]) && !permission::v2::permission_granted(1, this->calculate_permission(permission::b_ft_ignore_password, channel->channelId()))) - return cmd["tcpw"].string().empty() ? command_result{permission::b_ft_ignore_password} : command_result{error::channel_invalid_password}; + auto channel_password = cmd["cpw"].optional_string(); + if (!channel->verify_password(channel_password, this->getType() != ClientType::CLIENT_QUERY) && !permission::v2::permission_granted(1, this->calculate_permission(permission::b_ft_ignore_password, channel->channelId()))) { + return channel_password.has_value() ? command_result{error::channel_invalid_password} : command_result{permission::b_ft_ignore_password}; + } ACTION_REQUIRES_CHANNEL_PERMISSION(targetChannel, permission::i_ft_needed_file_rename_power, permission::i_ft_file_rename_power, true); } diff --git a/server/src/client/command_handler/groups.cpp b/server/src/client/command_handler/groups.cpp index dd9949f..927d690 100644 --- a/server/src/client/command_handler/groups.cpp +++ b/server/src/client/command_handler/groups.cpp @@ -5,8 +5,6 @@ #include #include -#include -#include "../../build.h" #include "../ConnectedClient.h" #include "../InternalClient.h" #include "../../server/VoiceServer.h" @@ -23,11 +21,8 @@ #include "helpers.h" #include "./bulk_parsers.h" -#include -#include #include #include -#include #include using namespace std::chrono; diff --git a/server/src/client/command_handler/helpers.h b/server/src/client/command_handler/helpers.h index d4f4d00..9139d15 100644 --- a/server/src/client/command_handler/helpers.h +++ b/server/src/client/command_handler/helpers.h @@ -44,14 +44,14 @@ do { \ /* Helper methods for channel resolve */ #define RESOLVE_CHANNEL_R(command, force) \ auto channel_tree = this->server ? this->server->channelTree : serverInstance->getChannelTree().get();\ -shared_lock channel_tree_read_lock(this->server ? this->server->channel_tree_lock : serverInstance->getChannelTreeLock());\ +shared_lock channel_tree_read_lock(this->server ? this->server->channel_tree_mutex : serverInstance->getChannelTreeLock());\ auto channel_id = command.as(); \ auto l_channel = channel_id ? channel_tree->findLinkedChannel(command.as()) : nullptr; \ if (!l_channel && (channel_id != 0 || force)) return command_result{error::channel_invalid_id, "Cant resolve channel"}; \ #define RESOLVE_CHANNEL_W(command, force) \ auto channel_tree = this->server ? this->server->channelTree : serverInstance->getChannelTree().get();\ -unique_lock channel_tree_write_lock(this->server ? this->server->channel_tree_lock : serverInstance->getChannelTreeLock());\ +unique_lock channel_tree_write_lock(this->server ? this->server->channel_tree_mutex : serverInstance->getChannelTreeLock());\ auto channel_id = command.as(); \ auto l_channel = channel_id ? channel_tree->findLinkedChannel(command.as()) : nullptr; \ if (!l_channel && (channel_id != 0 || force)) return command_result{error::channel_invalid_id, "Cant resolve channel"}; \ diff --git a/server/src/client/command_handler/misc.cpp b/server/src/client/command_handler/misc.cpp index 4cd1a68..e7fdb2f 100644 --- a/server/src/client/command_handler/misc.cpp +++ b/server/src/client/command_handler/misc.cpp @@ -462,7 +462,7 @@ command_result ConnectedClient::handleCommandSetClientChannelGroup(Command &cmd) } } - std::shared_lock server_channel_lock{this->server->channel_tree_lock}; /* ensure we dont get moved or somebody could move us */ + std::shared_lock server_channel_lock{this->server->channel_tree_mutex}; /* ensure we dont get moved or somebody could move us */ auto channel_id = cmd["cid"].as(); auto channel = this->server->channelTree->findChannel(channel_id); if (!channel) { @@ -579,7 +579,7 @@ command_result ConnectedClient::handleCommandSendTextMessage(Command &cmd) { bool chat_open{false}; { - std::unique_lock self_channel_lock{this->channel_lock}; + std::unique_lock self_channel_lock{this->channel_tree_mutex}; this->open_private_conversations.erase(std::remove_if(this->open_private_conversations.begin(), this->open_private_conversations.end(), [](const weak_ptr& weak) { return !weak.lock(); }), this->open_private_conversations.end()); @@ -597,7 +597,7 @@ command_result ConnectedClient::handleCommandSendTextMessage(Command &cmd) { ACTION_REQUIRES_PERMISSION(permission::b_client_even_textmessage_send, 1, this->getChannelId()); } - if(!permission::v2::permission_granted(target->calculate_permission(permission::i_client_needed_private_textmessage_power, target->getClientId()), this->calculate_permission(permission::i_client_private_textmessage_power, this->getClientId()), false)) { + if(!permission::v2::permission_granted(target->calculate_permission(permission::i_client_needed_private_textmessage_power, target->getClientId()), this->calculate_permission(permission::i_client_private_textmessage_power, this->getClientId()))) { return command_result{permission::i_client_private_textmessage_power}; } @@ -608,7 +608,7 @@ command_result ConnectedClient::handleCommandSendTextMessage(Command &cmd) { } { - std::unique_lock self_channel_lock{this->channel_lock}; + std::unique_lock self_channel_lock{this->channel_tree_mutex}; this->open_private_conversations.push_back(target.client); } } @@ -636,7 +636,7 @@ command_result ConnectedClient::handleCommandSendTextMessage(Command &cmd) { channel_id = this->currentChannel->channelId(); } - if(!permission::v2::permission_granted(1, this->calculate_permission(permission::b_client_channel_textmessage_send, channel_id), false)) \ + if(!permission::v2::permission_granted(1, this->calculate_permission(permission::b_client_channel_textmessage_send, channel_id))) \ return command_result{permission::b_client_channel_textmessage_send}; if(channel == this->currentChannel) { @@ -917,9 +917,9 @@ command_result ConnectedClient::handleCommandBanClient(Command &cmd) { ban_ids.push_back(_id); } - auto b_ban_name = permission::v2::permission_granted(1, this->calculate_permission(permission::b_client_ban_name, 0), false); - auto b_ban_ip = permission::v2::permission_granted(1, this->calculate_permission(permission::b_client_ban_ip, 0), false); - auto b_ban_hwid = permission::v2::permission_granted(1, this->calculate_permission(permission::b_client_ban_hwid, 0), false); + auto b_ban_name = permission::v2::permission_granted(1, this->calculate_permission(permission::b_client_ban_name, 0)); + auto b_ban_ip = permission::v2::permission_granted(1, this->calculate_permission(permission::b_client_ban_ip, 0)); + auto b_ban_hwid = permission::v2::permission_granted(1, this->calculate_permission(permission::b_client_ban_hwid, 0)); auto max_ban_time = this->calculate_permission(permission::i_client_ban_max_bantime, 0); if(!max_ban_time.has_value) return command_result{permission::i_client_ban_max_bantime}; @@ -1351,7 +1351,7 @@ void check_token_action_permissions(const std::shared_ptr& clie if(permission::v2::permission_granted(1, client->calculate_permission(permission::b_channel_join_ignore_password, target_channel->channelId()))) { action.text = "ignore"; - } else if(!target_channel->passwordMatch(action.text, true)) { + } else if(!target_channel->verify_password(action.text, true)) { action.type = ActionType::ActionIgnore; result.set_result(index, ts::command_result{error::channel_invalid_password}); break; @@ -1374,8 +1374,11 @@ command_result ConnectedClient::handleCommandTokenAdd(Command &cmd) { CMD_CHK_AND_INC_FLOOD_POINTS(5); auto client_tokens = this->server->tokenManager->client_token_count(this->getClientDatabaseId()); - if(!permission::v2::permission_granted(client_tokens + 1, this->calculate_permission(permission::i_virtualserver_token_limit, 0), true)) { - return ts::command_result{permission::i_virtualserver_token_limit}; + auto token_limit = this->calculate_permission(permission::i_virtualserver_token_limit, 0); + if(token_limit.has_value) { + if(!permission::v2::permission_granted(client_tokens + 1, token_limit)) { + return ts::command_result{permission::i_virtualserver_token_limit}; + } } std::vector actions{}; @@ -1766,8 +1769,7 @@ command_result ConnectedClient::handleCommandVerifyChannelPassword(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"}; - std::string password = cmd["password"]; - if (!channel->passwordMatch(password, false)) return command_result{error::server_invalid_password}; + if (!channel->verify_password(cmd["password"].optional_string(), this->getType() != ClientType::CLIENT_QUERY)) return command_result{error::server_invalid_password}; return command_result{error::ok}; } @@ -3071,7 +3073,7 @@ command_result ConnectedClient::handleCommandConversationHistory(ts::Command &co if(conversation_id > 0) { /* test if we're able to see the channel */ { - shared_lock channel_view_lock(this->channel_lock); + shared_lock channel_view_lock(this->channel_tree_mutex); auto channel = this->channel_view()->find_channel(conversation_id); if(!channel) return command_result{error::conversation_invalid_id}; @@ -3091,16 +3093,14 @@ command_result ConnectedClient::handleCommandConversationHistory(ts::Command &co /* test if there is a channel password or join power which denies that we see the conversation */ { - shared_lock channel_view_lock(ref_server->channel_tree_lock); + shared_lock channel_view_lock(ref_server->channel_tree_mutex); auto channel = ref_server->getChannelTree()->findChannel(conversation_id); if(!channel) /* should never happen! */ return command_result{error::conversation_invalid_id}; - if(!command[0].has("cpw")) - command[0]["cpw"] = ""; - - if (!channel->passwordMatch(command["cpw"], true)) + if (!channel->verify_password(command["cpw"].optional_string(), this->getType() != ClientType::CLIENT_QUERY)) { ACTION_REQUIRES_PERMISSION(permission::b_channel_join_ignore_password, 1, channel->channelId()); + } auto error = this->calculate_and_get_join_state(channel); if(error) return command_result{error}; @@ -3197,7 +3197,7 @@ command_result ConnectedClient::handleCommandConversationFetch(ts::Command &cmd) if(conversation_id > 0) { /* test if we're able to see the channel */ { - shared_lock channel_view_lock(this->channel_lock); + shared_lock channel_view_lock(this->channel_tree_mutex); auto channel = this->channel_view()->find_channel(conversation_id); if(!channel) { auto error = findError("conversation_invalid_id"); @@ -3229,7 +3229,7 @@ command_result ConnectedClient::handleCommandConversationFetch(ts::Command &cmd) /* test if there is a channel password or join power which denies that we see the conversation */ { - shared_lock channel_view_lock(ref_server->channel_tree_lock); + shared_lock channel_view_lock(ref_server->channel_tree_mutex); auto channel = ref_server->getChannelTree()->findChannel(conversation_id); if(!channel) { /* should never happen! */ auto error = findError("conversation_invalid_id"); @@ -3238,16 +3238,14 @@ command_result ConnectedClient::handleCommandConversationFetch(ts::Command &cmd) continue; } - if(!bulk.has("cpw")) - bulk["cpw"] = ""; - - if (!channel->passwordMatch(bulk["cpw"], true)) + if (!channel->verify_password( bulk.has("cpw") ? std::make_optional(bulk["cpw"].string()) : std::nullopt, this->getType() != ClientType::CLIENT_QUERY)) { if(!permission::v2::permission_granted(1, this->calculate_permission(permission::b_channel_join_ignore_password, 1, channel->channelId()))) { auto error = findError("channel_invalid_password"); result_bulk["error_id"] = error.errorId; result_bulk["error_msg"] = error.message; continue; } + } if(auto error_perm = this->calculate_and_get_join_state(channel); error_perm) { auto error = findError("server_insufficeient_permissions"); @@ -3296,7 +3294,7 @@ command_result ConnectedClient::handleCommandConversationMessageDelete(ts::Comma /* test if we're able to see the channel */ { - shared_lock channel_view_lock(this->channel_lock); + shared_lock channel_view_lock(this->channel_tree_mutex); auto channel = this->channel_view()->find_channel(current_conversation_id); if(!channel) return command_result{error::conversation_invalid_id}; @@ -3304,16 +3302,14 @@ command_result ConnectedClient::handleCommandConversationMessageDelete(ts::Comma /* test if there is a channel password or join power which denies that we see the conversation */ { - shared_lock channel_view_lock(ref_server->channel_tree_lock); + shared_lock channel_view_lock(ref_server->channel_tree_mutex); auto channel = ref_server->getChannelTree()->findChannel(current_conversation_id); if(!channel) return command_result{error::conversation_invalid_id}; - if(!bulk.has("cpw")) - bulk["cpw"] = ""; - - if (!channel->passwordMatch(bulk["cpw"], true)) + if (!channel->verify_password( bulk.has("cpw") ? std::make_optional(bulk["cpw"].string()) : std::nullopt, this->getType() != ClientType::CLIENT_QUERY)) { ACTION_REQUIRES_PERMISSION(permission::b_channel_join_ignore_password, 1, channel->channelId()); + } if (!permission::v2::permission_granted(1, this->calculate_permission(permission::b_channel_conversation_message_delete, channel->channelId()))) return command_result{permission::b_channel_conversation_message_delete}; diff --git a/server/src/client/command_handler/music.cpp b/server/src/client/command_handler/music.cpp index e1948d0..71e80fe 100644 --- a/server/src/client/command_handler/music.cpp +++ b/server/src/client/command_handler/music.cpp @@ -112,7 +112,7 @@ command_result ConnectedClient::handleCommandMusicBotCreate(Command& cmd) { return command_result{permission::b_client_music_create_temporary}; } - shared_lock server_channel_lock(this->server->channel_tree_lock); + shared_lock server_channel_lock(this->server->channel_tree_mutex); auto channel = cmd[0].has("cid") ? this->server->channelTree->findChannel(cmd["cid"]) : this->currentChannel; if(!channel) { if(cmd[0].has("cid")) return command_result{error::channel_invalid_id}; @@ -135,7 +135,7 @@ command_result ConnectedClient::handleCommandMusicBotCreate(Command& cmd) { { server_channel_lock.unlock(); - unique_lock server_channel_w_lock(this->server->channel_tree_lock); + unique_lock server_channel_w_lock(this->server->channel_tree_mutex); this->server->client_move( bot, channel, diff --git a/server/src/client/music/MusicClient.cpp b/server/src/client/music/MusicClient.cpp index e5e6d89..60bd9be 100644 --- a/server/src/client/music/MusicClient.cpp +++ b/server/src/client/music/MusicClient.cpp @@ -137,7 +137,7 @@ void MusicClient::initialize_bot() { this->server->registerClient(this->ref()); } { - unique_lock server_channel_lock(this->server->channel_tree_lock); + unique_lock server_channel_lock(this->server->channel_tree_mutex); this->server->client_move(this->ref(), channel, nullptr, "", ViewReasonId::VREASON_USER_ACTION, true, server_channel_lock); } this->playback.volume_modifier = this->properties()[property::CLIENT_PLAYER_VOLUME]; diff --git a/server/src/client/query/QueryClient.cpp b/server/src/client/query/QueryClient.cpp index da9bf67..a4a4bd1 100644 --- a/server/src/client/query/QueryClient.cpp +++ b/server/src/client/query/QueryClient.cpp @@ -618,7 +618,7 @@ void QueryClient::disconnect_from_virtual_server(const std::string& reason) { auto old_server = std::exchange(this->server, nullptr); if(old_server) { { - std::unique_lock tree_lock(old_server->channel_tree_lock); + std::unique_lock tree_lock(old_server->channel_tree_mutex); if(this->currentChannel) { old_server->client_move(this->ref(), nullptr, nullptr, "", ViewReasonId::VREASON_USER_ACTION, false, tree_lock); } @@ -627,8 +627,8 @@ void QueryClient::disconnect_from_virtual_server(const std::string& reason) { } { - std::lock_guard channel_lock{this->channel_lock}; - this->channels->reset(); + std::lock_guard channel_lock{this->channel_tree_mutex}; + this->channel_tree->reset(); this->currentChannel = nullptr; } diff --git a/server/src/client/query/QueryClientCommands.cpp b/server/src/client/query/QueryClientCommands.cpp index c8b4ef1..a1c6fa8 100644 --- a/server/src/client/query/QueryClientCommands.cpp +++ b/server/src/client/query/QueryClientCommands.cpp @@ -243,7 +243,7 @@ command_result QueryClient::handleCommandLogin(Command& cmd) { auto joined_channel = this->currentChannel; if(this->server) { { - unique_lock tree_lock(this->server->channel_tree_lock); + unique_lock tree_lock(this->server->channel_tree_mutex); if(joined_channel) { this->server->client_move(this->ref(), nullptr, nullptr, "", ViewReasonId::VREASON_USER_ACTION, false, tree_lock); } @@ -272,18 +272,18 @@ command_result QueryClient::handleCommandLogin(Command& cmd) { target_server->registerClient(this->ref()); { - shared_lock server_tree_lock(target_server->channel_tree_lock); + 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 */ target_server->notifyClientPropertyUpdates(this->ref(), deque{property::CLIENT_NICKNAME, property::CLIENT_UNIQUE_IDENTIFIER}); - unique_lock client_tree_lock(this->channel_lock); - this->channels->reset(); - this->channels->insert_channels(target_server->channelTree->tree_head(), true, false); + unique_lock client_tree_lock(this->channel_tree_mutex); + this->channel_tree->reset(); + this->channel_tree->insert_channels(target_server->channelTree->tree_head(), true, false); this->subscribeChannel(this->server->channelTree->channels(), false, false); } if(joined_channel) { - unique_lock tree_lock(this->server->channel_tree_lock); + 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); } else if(!permission::v2::permission_granted(1, this->calculate_permission(permission::b_virtualserver_select_godmode, 1))) { @@ -311,7 +311,7 @@ command_result QueryClient::handleCommandLogout(Command &) { auto joined = this->currentChannel; if(this->server){ { - unique_lock tree_lock(this->server->channel_tree_lock); + unique_lock tree_lock(this->server->channel_tree_mutex); if(joined) this->server->client_move(this->ref(), nullptr, nullptr, "", ViewReasonId::VREASON_USER_ACTION, false, tree_lock); this->server->unregisterClient(this->ref(), "logout", tree_lock); @@ -326,16 +326,16 @@ command_result QueryClient::handleCommandLogout(Command &) { this->server->registerClient(this->ref()); { - shared_lock server_channel_r_lock(this->server->channel_tree_lock); - unique_lock client_channel_lock(this->channel_lock); + shared_lock server_channel_r_lock(this->server->channel_tree_mutex); + unique_lock client_channel_lock(this->channel_tree_mutex); - this->channels->reset(); - this->channels->insert_channels(this->server->channelTree->tree_head(), true, false); + this->channel_tree->reset(); + this->channel_tree->insert_channels(this->server->channelTree->tree_head(), true, false); this->subscribeChannel(this->server->channelTree->channels(), false, false); } if(joined) { - unique_lock server_channel_w_lock(this->server->channel_tree_lock, 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); } else if(!permission::v2::permission_granted(1, this->calculate_permission(permission::b_virtualserver_select_godmode, 1))) { this->server->assignDefaultChannel(this->ref(), true); @@ -399,11 +399,11 @@ command_result QueryClient::handleCommandServerSelect(Command &cmd) { this->server->registerClient(this->ref()); { - shared_lock server_channel_lock(target->channel_tree_lock); - unique_lock client_channel_lock(this->channel_lock); + shared_lock server_channel_lock(target->channel_tree_mutex); + unique_lock client_channel_lock(this->channel_tree_mutex); this->subscribeToAll = true; - this->channels->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); } @@ -441,7 +441,7 @@ command_result QueryClient::handleCommandLeft(Command&) { CMD_RESET_IDLE; ACTION_REQUIRES_GLOBAL_PERMISSION(permission::b_virtualserver_select_godmode, 1); - unique_lock server_channel_lock(this->server->channel_tree_lock); + 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}; } @@ -536,8 +536,8 @@ command_result QueryClient::handleCommandChannelList(Command& cmd) { ACTION_REQUIRES_GLOBAL_PERMISSION(permission::b_virtualserver_channel_list, 1); int index = 0; - shared_lock channel_lock(this->server ? this->server->channel_tree_lock : serverInstance->getChannelTreeLock()); - auto entries = this->server ? this->channels->channels() : serverInstance->getChannelTree()->channels(); + shared_lock channel_lock(this->server ? this->server->channel_tree_mutex : serverInstance->getChannelTreeLock()); + auto entries = this->server ? this->channel_tree->channels() : serverInstance->getChannelTree()->channels(); channel_lock.unlock(); command_builder result{"", 1024, entries.size()}; diff --git a/server/src/client/query/QueryClientNotify.cpp b/server/src/client/query/QueryClientNotify.cpp index e7bd58c..14e0fc7 100644 --- a/server/src/client/query/QueryClientNotify.cpp +++ b/server/src/client/query/QueryClientNotify.cpp @@ -131,7 +131,7 @@ bool QueryClient::notifyChannelDeleted(const std::deque &deque, const bool QueryClient::notifyClientEnterView(const std::deque> &deque, const ViewReasonSystemT &t) { if(!this->eventActive(QueryEventGroup::QEVENTGROUP_CLIENT_VIEW, QueryEventSpecifier::QEVENTSPECIFIER_CLIENT_VIEW_JOIN)) { - assert(mutex_locked(this->channel_lock)); + assert(mutex_locked(this->channel_tree_mutex)); this->visibleClients.insert(this->visibleClients.end(), deque.begin(), deque.end()); return true; } else @@ -155,7 +155,7 @@ bool QueryClient::notifyClientMoved(const std::shared_ptr &clie bool QueryClient::notifyClientLeftView(const std::shared_ptr &client, const std::shared_ptr &target_channel, ViewReasonId reasonId, const std::string &reasonMessage, std::shared_ptr invoker, bool lock_channel_tree) { if(!this->eventActive(QueryEventGroup::QEVENTGROUP_CLIENT_VIEW, QueryEventSpecifier::QEVENTSPECIFIER_CLIENT_VIEW_LEAVE)) { - std::unique_lock tree_lock{this->channel_lock, std::defer_lock}; + std::unique_lock tree_lock{this->channel_tree_mutex, std::defer_lock}; if(lock_channel_tree) { tree_lock.lock(); } @@ -175,7 +175,7 @@ bool QueryClient::notifyClientLeftView(const std::shared_ptr &c bool QueryClient::notifyClientLeftView(const std::deque> &clients, const std::string &string, bool lock_channel_tree, const ViewReasonServerLeftT &t) { if(!this->eventActive(QueryEventGroup::QEVENTGROUP_CLIENT_VIEW, QueryEventSpecifier::QEVENTSPECIFIER_CLIENT_VIEW_LEAVE)) { - std::unique_lock tree_lock{this->channel_lock, std::defer_lock}; + std::unique_lock tree_lock{this->channel_tree_mutex, std::defer_lock}; if(lock_channel_tree) { tree_lock.lock(); } @@ -195,7 +195,7 @@ bool QueryClient::notifyClientLeftView(const std::deque &client, const std::shared_ptr &target_channel, const std::string &message, std::shared_ptr invoker, bool lock_channel_tree) { if(!this->eventActive(QueryEventGroup::QEVENTGROUP_CLIENT_VIEW, QueryEventSpecifier::QEVENTSPECIFIER_CLIENT_VIEW_LEAVE)) { - std::unique_lock tree_lock{this->channel_lock, std::defer_lock}; + std::unique_lock tree_lock{this->channel_tree_mutex, std::defer_lock}; if(lock_channel_tree) { tree_lock.lock(); } @@ -215,7 +215,7 @@ bool QueryClient::notifyClientLeftViewKicked(const std::shared_ptr &client, const std::string &message, std::shared_ptr invoker, size_t length, bool lock_channel_tree) { if(!this->eventActive(QueryEventGroup::QEVENTGROUP_CLIENT_VIEW, QueryEventSpecifier::QEVENTSPECIFIER_CLIENT_VIEW_LEAVE)) { - std::unique_lock tree_lock{this->channel_lock, std::defer_lock}; + std::unique_lock tree_lock{this->channel_tree_mutex, std::defer_lock}; if(lock_channel_tree) { tree_lock.lock(); } diff --git a/server/src/client/voice/VoiceClient.cpp b/server/src/client/voice/VoiceClient.cpp index 020f722..a600287 100644 --- a/server/src/client/voice/VoiceClient.cpp +++ b/server/src/client/voice/VoiceClient.cpp @@ -139,7 +139,7 @@ bool VoiceClient::disconnect(ts::ViewReasonId reason_id, const std::string &reas } if(notify_viewer && this->server) { - unique_lock channel_lock(this->server->channel_tree_lock); + unique_lock channel_lock(this->server->channel_tree_mutex); this->server->client_move(this->ref(), nullptr, invoker, reason, reason_id, false, channel_lock); } else { threads::MutexLock lock(this->command_lock); diff --git a/server/src/client/voice/VoiceClientCommandHandler.cpp b/server/src/client/voice/VoiceClientCommandHandler.cpp index bc31634..a593b4e 100644 --- a/server/src/client/voice/VoiceClientCommandHandler.cpp +++ b/server/src/client/voice/VoiceClientCommandHandler.cpp @@ -110,12 +110,12 @@ command_result VoiceClient::handleCommandClientInit(Command &cmd) { command_result VoiceClient::handleCommandClientDisconnect(Command& cmd) { auto reason = cmd["reasonmsg"].size() > 0 ? cmd["reasonmsg"].as() : ""; { - std::shared_lock own_lock{this->channel_lock}; + std::shared_lock own_lock{this->channel_tree_mutex}; this->notifyClientLeftView(this->ref(), nullptr, VREASON_SERVER_LEFT, reason, nullptr, false); //Before we're moving us out of the channel tree! } if(this->state == CONNECTED) { - std::unique_lock channel_lock{this->server->channel_tree_lock}; + std::unique_lock channel_lock{this->server->channel_tree_mutex}; this->server->client_move(this->ref(), nullptr, nullptr, reason, VREASON_SERVER_LEFT, true, channel_lock); } logMessage(this->getServerId(), "{} Got remote disconnect with the reason '{}'", CLIENT_STR_LOG_PREFIX, reason); diff --git a/server/src/client/web/WebClient.cpp b/server/src/client/web/WebClient.cpp index 90f1196..fe63c4d 100644 --- a/server/src/client/web/WebClient.cpp +++ b/server/src/client/web/WebClient.cpp @@ -224,7 +224,7 @@ bool WebClient::close_connection(const std::chrono::system_clock::time_point& ti if(this->server){ { - unique_lock server_channel_lock(this->server->channel_tree_lock); + unique_lock server_channel_lock(this->server->channel_tree_mutex); this->server->unregisterClient(this->ref(), "disconnected", server_channel_lock); } //this->server = nullptr; @@ -539,9 +539,9 @@ bool WebClient::disconnect(const std::string &reason) { serverInstance->action_logger()->client_channel_logger.log_client_leave(this->getServerId(), this->ref(), old_channel->channelId(), old_channel->name()); } { - unique_lock server_channel_lock(this->server->channel_tree_lock); + unique_lock server_channel_lock(this->server->channel_tree_mutex); { - unique_lock own_lock(this->channel_lock); + unique_lock own_lock(this->channel_tree_mutex); this->notifyClientLeftViewKicked(this->ref(), nullptr, reason, this->server->serverRoot, false); } this->server->client_move(this->ref(), nullptr, this->server->serverRoot, reason, ViewReasonId::VREASON_SERVER_KICK, false, server_channel_lock); diff --git a/server/src/music/MusicBotManager.cpp b/server/src/music/MusicBotManager.cpp index b5c3e39..f92a2ef 100644 --- a/server/src/music/MusicBotManager.cpp +++ b/server/src/music/MusicBotManager.cpp @@ -142,7 +142,7 @@ void MusicBotManager::deleteBot(std::shared_ptr musicBot) { MusicBotManager::adjustTickPool(); { - unique_lock server_channel_lock(handle->channel_tree_lock); + unique_lock server_channel_lock(handle->channel_tree_mutex); handle->client_move(musicBot, nullptr, nullptr, "Music bot deleted", ViewReasonId::VREASON_SERVER_LEFT, true, server_channel_lock); handle->unregisterClient(musicBot, "bot deleted", server_channel_lock); } @@ -274,7 +274,7 @@ void MusicBotManager::disconnectBots() { for(const auto& bot : this->music_bots) { if(bot->currentChannel) { - unique_lock server_channel_lock(handle->channel_tree_lock); + unique_lock server_channel_lock(handle->channel_tree_mutex); handle->client_move(bot, nullptr, nullptr, "Music bot deleted", ViewReasonId::VREASON_SERVER_LEFT, true, server_channel_lock); } bot->server = nullptr; diff --git a/server/src/music/PlaylistPermissions.cpp b/server/src/music/PlaylistPermissions.cpp index a87dc97..33b906d 100644 --- a/server/src/music/PlaylistPermissions.cpp +++ b/server/src/music/PlaylistPermissions.cpp @@ -13,17 +13,31 @@ PlaylistPermissions::PlaylistPermissions(std::shared_ptr& client) { auto val = this->_permissions->channel_permission(permission, client->getClientDatabaseId()); - if(val.flags.value_set) + if(val.flags.value_set) { return {val.values.value, true}; + } return client->calculate_permission(permission, 0); } permission::PermissionType PlaylistPermissions::client_has_permissions( - const std::shared_ptr &client, ts::permission::PermissionType needed_permission, ts::permission::PermissionType granted_permission, uint8_t flags) { - if(this->is_playlist_owner(client->getClientDatabaseId())) return permission::ok; + const std::shared_ptr &client, + ts::permission::PermissionType needed_permission, + ts::permission::PermissionType granted_permission, + uint8_t flags +) { + if(this->is_playlist_owner(client->getClientDatabaseId())) { + return permission::ok; + } - return permission::v2::permission_granted( - this->permission_manager()->permission_value_flagged(needed_permission), - this->calculate_client_specific_permissions(granted_permission, client), - (flags & do_no_require_granted) == 0) ? permission::ok : granted_permission; + const auto client_permission = this->calculate_client_specific_permissions(granted_permission, client); + auto playlist_permission = this->permission_manager()->permission_value_flagged(needed_permission); + + if((flags & do_no_require_granted) == 0) { + playlist_permission.clear_flag_on_zero(); + } else if(!playlist_permission.has_value) { + playlist_permission.value = 0; + playlist_permission.has_value = true; + } + + return permission::v2::permission_granted(playlist_permission, client_permission) ? permission::ok : granted_permission; } \ No newline at end of file diff --git a/shared b/shared index 8dde5b1..8fb93d1 160000 --- a/shared +++ b/shared @@ -1 +1 @@ -Subproject commit 8dde5b1c23f0d84ca1b56a8c80389fb4e887b062 +Subproject commit 8fb93d1d9d938b78dcf34e7f7953e3d41504d00f