diff --git a/git-teaspeak b/git-teaspeak index d2b3e9b..ac79fda 160000 --- a/git-teaspeak +++ b/git-teaspeak @@ -1 +1 @@ -Subproject commit d2b3e9bcadea5130b8ae906d9a1e656351f59936 +Subproject commit ac79fda3b2d409e2fa023a5f38c380121cd7ee93 diff --git a/server/src/DatabaseHelper.cpp b/server/src/DatabaseHelper.cpp index fb1d7d6..857ef96 100644 --- a/server/src/DatabaseHelper.cpp +++ b/server/src/DatabaseHelper.cpp @@ -622,19 +622,19 @@ void DatabaseHelper::saveChannelPermissions(const std::shared_ptr DatabaseHelper::default_properties_client(std::shared_ptr properties, ClientType type){ + if(!properties) + properties = make_shared(); - _properties.register_property_type(); - _properties.register_property_type(); + properties->register_property_type(); + properties->register_property_type(); - if(type == ClientType::CLIENT_MUSIC){ - _properties[property::CLIENT_INPUT_HARDWARE] = true; - _properties[property::CLIENT_OUTPUT_HARDWARE] = true; - } else if(type == ClientType::CLIENT_QUERY) { - _properties[property::CLIENT_INPUT_HARDWARE] = true; - _properties[property::CLIENT_OUTPUT_HARDWARE] = true; + if(type == ClientType::CLIENT_MUSIC || type == ClientType::CLIENT_QUERY){ + (*properties)[property::CLIENT_INPUT_HARDWARE] = true; + (*properties)[property::CLIENT_OUTPUT_HARDWARE] = true; } + + return properties; } bool DatabaseHelper::assignDatabaseId(sql::SqlManager *sql, ServerId id, std::shared_ptr cl) { @@ -954,8 +954,7 @@ std::shared_ptr DatabaseHelper::loadChannelProperties(const shared_p } std::shared_ptr DatabaseHelper::loadClientProperties(const std::shared_ptr& server, ClientDbId cldbid, ClientType type) { - auto props = std::make_shared(); - assign_default_properties_client(props.get(), type); + auto props = DatabaseHelper::default_properties_client(nullptr, type); if(server) { props->operator[](property::CLIENT_DESCRIPTION) = server->properties()[property::VIRTUALSERVER_DEFAULT_CLIENT_DESCRIPTION].value(); } diff --git a/server/src/DatabaseHelper.h b/server/src/DatabaseHelper.h index ebe3b25..1f6eb2d 100644 --- a/server/src/DatabaseHelper.h +++ b/server/src/DatabaseHelper.h @@ -76,7 +76,7 @@ namespace ts { class DatabaseHelper { public: - static void assign_default_properties_client(Properties *, ClientType type); + static std::shared_ptr default_properties_client(std::shared_ptr /* properties */, ClientType /* type */); static bool assignDatabaseId(sql::SqlManager *, ServerId id, std::shared_ptr); explicit DatabaseHelper(sql::SqlManager*); diff --git a/server/src/TS3ServerClientManager.cpp b/server/src/TS3ServerClientManager.cpp index d24f64d..708e46d 100644 --- a/server/src/TS3ServerClientManager.cpp +++ b/server/src/TS3ServerClientManager.cpp @@ -523,8 +523,20 @@ void TSServer::client_move( if(!deleted.empty()) target->notifyChannelHide(deleted, false); - if(!s_source_channel->permission_granted(permission::i_channel_needed_subscribe_power, target->calculate_permission_value(permission::i_channel_subscribe_power, s_source_channel->channelId()), false)) - target->unsubscribeChannel({s_source_channel}, false); //Unsubscribe last channel (hasn't permissions) + auto i_source_channel = s_source_channel->channelId(); + if(std::find(deleted.begin(), deleted.end(), i_source_channel) == deleted.end()) { + auto source_channel_sub_power = target->calculate_permission_value(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_value(permission::b_channel_ignore_subscribe_power, i_source_channel); + if(!DataClient::permission_granted(source_channel_sub_power_ignore, 1, true)) { + 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 + ); + target->unsubscribeChannel({s_source_channel}, false); //Unsubscribe last channel (hasn't permissions) + } + } + } TIMING_STEP(timings, "src hide ts"); } } diff --git a/server/src/client/ConnectedClient.cpp b/server/src/client/ConnectedClient.cpp index ad82054..a9cdbb2 100644 --- a/server/src/client/ConnectedClient.cpp +++ b/server/src/client/ConnectedClient.cpp @@ -246,9 +246,10 @@ std::deque> ConnectedClient::subscribeChannel(cons if(!general_granted && channel != this->currentChannel) { auto granted_permission = this->calculate_permission_value(permission::i_channel_subscribe_power, channel->channelId()); - if((granted_permission.has_value && granted_permission.value == -1) || !channel->permission_granted(permission::i_channel_needed_subscribe_power, granted_permission, false)) { + + if(!channel->permission_granted(permission::i_channel_needed_subscribe_power, granted_permission, false)) { auto ignore_power = this->calculate_permission_value(permission::b_channel_ignore_subscribe_power, channel->channelId()); - if(!ignore_power.has_value && ignore_power.value < 1) + if(!ignore_power.has_value || ignore_power.value < 1) continue; } } diff --git a/server/src/client/ConnectedClientCommandHandler.cpp b/server/src/client/ConnectedClientCommandHandler.cpp index 9130901..081efda 100644 --- a/server/src/client/ConnectedClientCommandHandler.cpp +++ b/server/src/client/ConnectedClientCommandHandler.cpp @@ -5646,7 +5646,7 @@ CommandResult ConnectedClient::handleCommandMessageList(Command &cmd) { CMD_CHK_AND_INC_FLOOD_POINTS(5); auto msgList = this->server->letters->avariableLetters(this->getUid()); - if (msgList.empty()) return {findError("database_empty_result"), "no letters avaraible"}; + if (msgList.empty()) return {findError("database_empty_result"), "no letters available"}; Command notify(this->getExternalType() == CLIENT_TEAMSPEAK ? "notifymessagelist" : ""); diff --git a/server/src/client/DataClient.cpp b/server/src/client/DataClient.cpp index d6882c7..bcc2062 100644 --- a/server/src/client/DataClient.cpp +++ b/server/src/client/DataClient.cpp @@ -13,12 +13,9 @@ using namespace ts; using namespace ts::server; using namespace ts::permission; -extern ts::server::InstanceHandler* serverInstance; - DataClient::DataClient(sql::SqlManager* database, const std::shared_ptr& server) : server(server), sql(database) { assert(database); - this->_properties = std::make_shared(); - DatabaseHelper::assign_default_properties_client(this->_properties.get(), ClientType::CLIENT_INTERNAL); + this->_properties = DatabaseHelper::default_properties_client(nullptr, ClientType::CLIENT_INTERNAL); } DataClient::~DataClient() { @@ -28,26 +25,48 @@ DataClient::~DataClient() { bool DataClient::loadDataForCurrentServer() { //TODO for query if(this->getUid().empty()) return false; + auto ref_server = this->server; + auto server_id = ref_server ? ref_server->getServerId() : 0; + properties()[property::CLIENT_DATABASE_ID] = 0; properties()[property::CLIENT_CREATED] = 0; properties()[property::CLIENT_TOTALCONNECTIONS] = 0; - sql::command(this->sql, "SELECT `cldbid`,`firstConnect`,`connections` FROM `clients` WHERE `serverId` = :sid AND `clientUid`=:uid LIMIT 1", variable{":sid", this->server ? this->server->getServerId() : 0}, variable{":uid", this->getUid()}).query([&](DataClient* cl, int length, string* values, string* names){ - for (int index = 0; index < length; index++) { - logTrace(this->server ? this->server->getServerId() : 0, "Reading client (" + this->getUid() + ") property from client database table. (Key: " + names[index] + ", Value: " + values[index] + ")"); - if (names[index] == "cldbid") { - cl->properties()[property::CLIENT_DATABASE_ID] = string(values[index]); - } else if (names[index] == "firstConnect") { - cl->properties()[property::CLIENT_CREATED] = values[index]; - } else if (names[index] == "connections") { - cl->properties()[property::CLIENT_TOTALCONNECTIONS] = values[index]; - } else { - debugMessage(lstream << "Unknown row name '" << names[index] << "'" << endl); - } - } + ClientDbId client_db_id = 0; + sql::command(this->sql, "SELECT `cldbid`,`firstConnect`,`connections` FROM `clients` WHERE `serverId` = :sid AND `clientUid` = :uid LIMIT 1", + variable{":sid", server_id}, + variable{":uid", this->getUid()} + ).query([&](DataClient* cl, int length, string* values, string* names){ + for (int index = 0; index < length; index++) { + try { + if (names[index] == "cldbid") { + client_db_id = stoull(values[index]); + } else if (names[index] == "firstConnect") { + cl->properties()[property::CLIENT_CREATED] = values[index]; + } else if (names[index] == "connections") { + cl->properties()[property::CLIENT_TOTALCONNECTIONS] = values[index]; + } else { + logWarning(LOG_INSTANCE, "Received unknown column with name {} within client list", names[index]); + } + } catch(const std::exception& ex) { + logError(server_id, "Failed to load client {} base properties from database. Colum parsing for column {} failed. Value: {}. Message: {}", + this->getUid(), + names[index], + values[index], + ex.what() + ); + return 0; + } + } return 0; }, this); + + if(client_db_id == 0) + return false; + + this->properties()[property::CLIENT_DATABASE_ID] = client_db_id; /* do this before the property saving (it saved the cldbid as well!)*/ + //Load general properties deque copied; for(const auto& prop : this->_properties->list_properties()){ @@ -56,20 +75,19 @@ bool DataClient::loadDataForCurrentServer() { //TODO for query copied.push_back(prop); } - if(this->getClientDatabaseId() == 0) return false; - if(!this->server) { + if(!ref_server) { if(this->getType() == ClientType::CLIENT_WEB || this->getType() == ClientType::CLIENT_TEAMSPEAK) - logCritical("Got a voice or web manager, which is unbound to any server!"); + logCritical(LOG_INSTANCE, "Got a voice or web client, which is unbound to any server!"); return false; } - auto clType = this->getType(); - if(this->getType() == CLIENT_TEAMSPEAK || this->server) { - this->_properties = serverInstance->databaseHelper()->loadClientProperties(this->server, this->getClientDatabaseId(), clType); + auto client_type = this->getType(); + if(client_type == CLIENT_TEAMSPEAK || ref_server) { + this->_properties = serverInstance->databaseHelper()->loadClientProperties(ref_server, this->getClientDatabaseId(), client_type); } else { - this->_properties = std::make_shared(); - DatabaseHelper::assign_default_properties_client(this->_properties.get(), clType); - this->_properties->registerNotifyHandler([&](Property& prop){ + this->_properties = DatabaseHelper::default_properties_client(nullptr, client_type); + + this->_properties->registerNotifyHandler([&, server_id, client_db_id](Property& prop){ std::string query; if(prop.type() == property::CLIENT_TOTALCONNECTIONS) query = "UPDATE `clients` SET `connections` = :value WHERE `serverId` = :sid AND `cldbid` = :cldbid"; @@ -77,9 +95,12 @@ bool DataClient::loadDataForCurrentServer() { //TODO for query query = "UPDATE `clients` SET `lastName` = :value WHERE `serverId` = :sid AND `cldbid` = :cldbid"; else if(prop.type() == property::CLIENT_LASTCONNECTED) query = "UPDATE `clients` SET `lastConnect` = :value WHERE `serverId` = :sid AND `cldbid` = :cldbid"; - if(query.empty()) return; - debugMessage("[SQL] " + query + " - " + to_string(0) + " - " + prop.value() + " - " + to_string(this->getClientDatabaseId())); - sql::command(this->sql, query, variable{":sid", 0}, variable{":cldbid", this->getClientDatabaseId()}, variable{":value", prop.value()}).executeLater().waitAndGetLater(LOG_SQL_CMD, {1, "future failed"}); + else + return; + + debugMessage(server_id, "[Property] Updating general client table property for client {}. Key: {} Value: {}", client_db_id, prop.type().name, prop.value()); + sql::command(this->sql, query, variable{":sid", 0}, variable{":cldbid", client_db_id}, variable{":value", prop.value()}).executeLater() + .waitAndGetLater(LOG_SQL_CMD, {1, "failed to update general client properties"}); }); } @@ -91,35 +112,21 @@ bool DataClient::loadDataForCurrentServer() { //TODO for query } this->_properties->toggleSave(true); - /* - * //TODO What did this? - vector updatedProps; - if(this->server) { - auto cclient = dynamic_cast(this); - if(cclient){ - if(cclient->state == CONNECTED) - this->server->notifyClientPropertyUpdates(dynamic_cast(this)->_this.lock(), updatedProps); - } - } - */ - this->clientPermissions = serverInstance->databaseHelper()->loadClientPermissionManager(this->server, this->getClientDatabaseId()); + this->clientPermissions = serverInstance->databaseHelper()->loadClientPermissionManager(ref_server, this->getClientDatabaseId()); //Setup / fix stuff if(!this->properties()[property::CLIENT_FLAG_AVATAR].as().empty()){ if( - !this->server || - !serverInstance->getFileServer()->findFile("/avatar_" + this->getAvatarId(),serverInstance->getFileServer()->avatarDirectory(this->server))) { + !ref_server || + !serverInstance->getFileServer()->findFile("/avatar_" + this->getAvatarId(),serverInstance->getFileServer()->avatarDirectory(ref_server))) { if(config::server::delete_missing_icon_permissions) this->properties()[property::CLIENT_FLAG_AVATAR] = ""; } } - if(this->server){ - int ureadMessages = 0; - for(const auto &elm : this->server->letters->avariableLetters(this->getUid())) - if(!elm->read) ureadMessages++; - this->properties()[property::CLIENT_UNREAD_MESSAGES] = ureadMessages; - } + if(ref_server) + this->properties()[property::CLIENT_UNREAD_MESSAGES] = ref_server->letters->unread_letter_count(this->getUid()); + return true; } diff --git a/server/src/client/DataClient.h b/server/src/client/DataClient.h index ca6b5bf..7d78561 100644 --- a/server/src/client/DataClient.h +++ b/server/src/client/DataClient.h @@ -90,24 +90,29 @@ namespace ts { virtual permission::PermissionValue getPermissionGrantValue(permission::PermissionTestType test, permission::PermissionType, const std::shared_ptr& targetChannel = nullptr); virtual bool permissionGrantGranted(permission::PermissionTestType test, permission::PermissionType, permission::PermissionValue, const std::shared_ptr& targetChannel = nullptr, bool required = true); - inline bool permission_granted(permission::PermissionType, permission::PermissionValue, bool = false) = delete; /* we dont accept permission type enums! */ - inline bool permission_granted(ts::permission::PermissionValue value, ts::permission::PermissionValue required, bool enforce_required = true) { - if(required == permNotGranted || required == 0) { - if(enforce_required) - return value == -1 || value > 0; - else - return true; - } else if(value == permNotGranted) { - return false; - } else { - if(value == -1) - return true; - else if(value >= required) - return true; - } - return false; + template + inline bool permission_granted(T, permission::PermissionValue, bool = false) = delete; /* only permission values */ + [[deprecated]] __always_inline bool permission_granted(ts::permission::PermissionValue value, ts::permission::PermissionValue required, bool enforce_required = true) { + return DataClient::permission_granted({value, value != permNotGranted}, required, enforce_required); } + static inline bool permission_granted(ts::permission::v2::PermissionFlaggedValue value, ts::permission::PermissionValue required, bool enforce_required = true) { + if(required == permNotGranted || required == 0) { + if(enforce_required) + return value.value == -1 || value.value > 0; + else + return true; + } else if(!value.has_value) { + return false; + } else { + if(value.value == -1) + return true; + else if(value.value >= required) + return true; + } + return false; + } + virtual std::vector> assignedServerGroups(); virtual std::shared_ptr assignedChannelGroup(const std::shared_ptr &); virtual bool serverGroupAssigned(const std::shared_ptr &); diff --git a/server/src/manager/LetterManager.cpp b/server/src/manager/LetterManager.cpp index f6cb551..4bdf93d 100644 --- a/server/src/manager/LetterManager.cpp +++ b/server/src/manager/LetterManager.cpp @@ -14,6 +14,28 @@ using namespace ts::server; LetterManager::LetterManager(server::TSServer* server) : server(server) {} LetterManager::~LetterManager() {} +size_t LetterManager::unread_letter_count(const ts::ClientUid &client_unique_id) { + size_t result = 0; + auto res = sql::command(this->server->getSql(), "SELECT COUNT(*) FROM `letters` WHERE `serverId` = :sid AND `receiver` = :uid AND `read` = :false", + variable{":sid", this->server ? this->server->getServerId() : 0}, + variable{":uid", client_unique_id}, + variable{":false", 0} + ).query([&](int length, std::string* values, std::string* columns) { + if(length != 1) + return 1; + + try { + result = stoll(values[0]); + } catch(std::exception& ex) { + logError(this->server ? this->server->getServerId() : 0, "Failed to parse unread letter count: {}", ex.what()); + return 1; + } + return 0; + }); + (LOG_SQL_CMD)(res); + return result; +} + std::vector> LetterManager::avariableLetters(ClientUid cluid) { vector> result; diff --git a/server/src/manager/LetterManager.h b/server/src/manager/LetterManager.h index 52fa314..bf8f66c 100644 --- a/server/src/manager/LetterManager.h +++ b/server/src/manager/LetterManager.h @@ -30,6 +30,7 @@ namespace ts { ~LetterManager(); + size_t unread_letter_count(const ClientUid&); std::vector> avariableLetters(ClientUid); std::shared_ptr getFullLetter(LetterId); diff --git a/shared b/shared index 3b3574d..60ed95f 160000 --- a/shared +++ b/shared @@ -1 +1 @@ -Subproject commit 3b3574d54df493a8a767e2c691c8769468b758dc +Subproject commit 60ed95fb63463e71d423d555f316f071900d8fb6