Merged with master
This commit is contained in:
		
						commit
						244b7dc0cb
					
				@ -1 +1 @@
 | 
				
			|||||||
Subproject commit 6dd8f87281e6fd60df0c5babfc1303df74ce1c0a
 | 
					Subproject commit 2c928229b1aab0306a02d2b7820fd93f3a3623b9
 | 
				
			||||||
@ -171,7 +171,7 @@ int GroupManager::insertGroupFromDb(int count, char **values, char **column) {
 | 
				
			|||||||
        else cerr << "Invalid group table row " << column[index] << endl;
 | 
					        else cerr << "Invalid group table row " << column[index] << endl;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    if(groupId == 0 || target == 0xff || type == 0xff || targetName.empty()) {
 | 
					    if((size_t) groupId == 0 || (size_t) target == 0xff || (size_t) type == 0xff || targetName.empty()) {
 | 
				
			||||||
        logCritical(this->getServerId(), "Found invalid group ad database! (GroupId " + to_string(groupId) + ", Target " + to_string(target) + ", Type " + to_string(type) + ", Name '" + targetName + "')");
 | 
					        logCritical(this->getServerId(), "Found invalid group ad database! (GroupId " + to_string(groupId) + ", Target " + to_string(target) + ", Type " + to_string(type) + ", Name '" + targetName + "')");
 | 
				
			||||||
        return 0;
 | 
					        return 0;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
@ -225,11 +225,15 @@ int GroupManager::insertGroupFromDb(int count, char **values, char **column) {
 | 
				
			|||||||
    return 0;
 | 
					    return 0;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
void GroupManager::handleChannelDeleted(std::shared_ptr<BasicChannel>  channel) {
 | 
					void GroupManager::handleChannelDeleted(const ChannelId& channel_id) {
 | 
				
			||||||
    cacheLock.lock();
 | 
						unique_lock cache_lock(this->cacheLock);
 | 
				
			||||||
    for(const auto &entry : this->cachedClients)
 | 
						auto cached_clients = std::vector<shared_ptr<CachedClient>>{this->cachedClients.begin(), this->cachedClients.end()};
 | 
				
			||||||
        entry->channelGroups.erase(channel->channelId());
 | 
						cache_lock.unlock();
 | 
				
			||||||
    cacheLock.unlock();
 | 
					
 | 
				
			||||||
 | 
						for(auto& entry : cached_clients) {
 | 
				
			||||||
 | 
							lock_guard entry_lock(entry->lock);
 | 
				
			||||||
 | 
							entry->channel_groups.erase(channel_id);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
bool GroupManager::isLocalGroup(std::shared_ptr<Group> gr) {
 | 
					bool GroupManager::isLocalGroup(std::shared_ptr<Group> gr) {
 | 
				
			||||||
@ -373,24 +377,11 @@ bool GroupManager::deleteAllGroups() {
 | 
				
			|||||||
	LOG_SQL_CMD(sql::command(this->sql, "DELETE FROM `assignedGroups` WHERE `serverId` = :sid", variable{":sid", this->getServerId()}).execute());
 | 
						LOG_SQL_CMD(sql::command(this->sql, "DELETE FROM `assignedGroups` WHERE `serverId` = :sid", variable{":sid", this->getServerId()}).execute());
 | 
				
			||||||
	LOG_SQL_CMD(sql::command(this->sql, "DELETE FROM `permissions` WHERE `serverId` = :sid AND `type` = :type", variable{":sid", this->getServerId()}, variable{":type", SQL_PERM_GROUP}).execute());
 | 
						LOG_SQL_CMD(sql::command(this->sql, "DELETE FROM `permissions` WHERE `serverId` = :sid AND `type` = :type", variable{":sid", this->getServerId()}, variable{":type", SQL_PERM_GROUP}).execute());
 | 
				
			||||||
	{
 | 
						{
 | 
				
			||||||
		threads::MutexLock lock(this->cacheLock);
 | 
							lock_guard cache_lock(this->cacheLock);
 | 
				
			||||||
		for(const auto& entry : this->cachedClients) {
 | 
							for(const auto& entry : this->cachedClients) {
 | 
				
			||||||
			threads::MutexLock lock_entry(entry->lock);
 | 
								lock_guard entry_lock(entry->lock);
 | 
				
			||||||
			bool iterate = true;
 | 
								entry->server_groups.clear();
 | 
				
			||||||
			while(iterate) {
 | 
								entry->channel_groups.clear();
 | 
				
			||||||
				iterate = false;
 | 
					 | 
				
			||||||
				for(const auto& assignment : entry->channelGroups) {
 | 
					 | 
				
			||||||
					if(std::find(this->groups.begin(), this->groups.end(), assignment.second->group) != this->groups.end()) {
 | 
					 | 
				
			||||||
						entry->channelGroups.erase(assignment.first);
 | 
					 | 
				
			||||||
						iterate = true;
 | 
					 | 
				
			||||||
						break;
 | 
					 | 
				
			||||||
					}
 | 
					 | 
				
			||||||
				}
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
			entry->serverGroups.erase(std::remove_if(entry->serverGroups.begin(), entry->serverGroups.end(), [&](const shared_ptr<GroupAssignment>& assignment){
 | 
					 | 
				
			||||||
				return std::find(this->groups.begin(), this->groups.end(), assignment->group) != this->groups.end();
 | 
					 | 
				
			||||||
			}), entry->serverGroups.end());
 | 
					 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	this->groups.clear();
 | 
						this->groups.clear();
 | 
				
			||||||
@ -405,6 +396,25 @@ bool GroupManager::deleteGroup(std::shared_ptr<Group> group) {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    this->groups.erase(std::find(this->groups.begin(), this->groups.end(), group));
 | 
					    this->groups.erase(std::find(this->groups.begin(), this->groups.end(), group));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /* erase the group out of our cache */
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							lock_guard cache_lock(this->cacheLock);
 | 
				
			||||||
 | 
							for(auto& entry : this->cachedClients) {
 | 
				
			||||||
 | 
								lock_guard entry_lock(entry->lock);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								entry->server_groups.erase(std::remove_if(entry->server_groups.begin(), entry->server_groups.end(), [&](const std::shared_ptr<GroupAssignment>& group_assignment) {
 | 
				
			||||||
 | 
									return group_assignment->group == group;
 | 
				
			||||||
 | 
								}), entry->server_groups.end());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								for(auto it = entry->channel_groups.begin(); it != entry->channel_groups.end();) {
 | 
				
			||||||
 | 
									if(it->second->group == group)
 | 
				
			||||||
 | 
										it = entry->channel_groups.erase(it);
 | 
				
			||||||
 | 
									else
 | 
				
			||||||
 | 
										it++;
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    bool flag_sql = false;
 | 
					    bool flag_sql = false;
 | 
				
			||||||
    auto res = sql::command(this->sql, "DELETE FROM `groups` WHERE `serverId` = :sid AND `groupId` = :gid", variable{":sid", this->getServerId()}, variable{":gid", group->groupId()}).execute();
 | 
					    auto res = sql::command(this->sql, "DELETE FROM `groups` WHERE `serverId` = :sid AND `groupId` = :gid", variable{":sid", this->getServerId()}, variable{":gid", group->groupId()}).execute();
 | 
				
			||||||
	LOG_SQL_CMD(res);
 | 
						LOG_SQL_CMD(res);
 | 
				
			||||||
@ -418,33 +428,6 @@ bool GroupManager::deleteGroup(std::shared_ptr<Group> group) {
 | 
				
			|||||||
	if(flag_sql)
 | 
						if(flag_sql)
 | 
				
			||||||
		logError(this->getServerId(), "Could not delete group {} ({}) from database. May leader to invalid data", group->name(), group->groupId());
 | 
							logError(this->getServerId(), "Could not delete group {} ({}) from database. May leader to invalid data", group->name(), group->groupId());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    this->cacheLock.lock();
 | 
					 | 
				
			||||||
    for(const auto &entry : this->cachedClients){
 | 
					 | 
				
			||||||
    	bool iterate = true;
 | 
					 | 
				
			||||||
    	while(iterate) {
 | 
					 | 
				
			||||||
		    iterate = false;
 | 
					 | 
				
			||||||
		    for(const auto &assignment : entry->serverGroups){
 | 
					 | 
				
			||||||
			    if(assignment->group == group){
 | 
					 | 
				
			||||||
				    entry->serverGroups.erase(std::find(entry->serverGroups.begin(), entry->serverGroups.end(), assignment));
 | 
					 | 
				
			||||||
				    iterate = true;
 | 
					 | 
				
			||||||
				    break;
 | 
					 | 
				
			||||||
			    }
 | 
					 | 
				
			||||||
		    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	    }
 | 
					 | 
				
			||||||
	    iterate = true;
 | 
					 | 
				
			||||||
	    while(iterate) {
 | 
					 | 
				
			||||||
		    iterate = false;
 | 
					 | 
				
			||||||
		    for(const auto& pair : entry->channelGroups){
 | 
					 | 
				
			||||||
			    if(pair.second->group == group){
 | 
					 | 
				
			||||||
				    entry->channelGroups.erase(pair.first);
 | 
					 | 
				
			||||||
				    iterate = true;
 | 
					 | 
				
			||||||
				    break;
 | 
					 | 
				
			||||||
			    }
 | 
					 | 
				
			||||||
		    }
 | 
					 | 
				
			||||||
	    }
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
    this->cacheLock.unlock();
 | 
					 | 
				
			||||||
    return true;
 | 
					    return true;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -518,95 +501,93 @@ void GroupManager::cleanupAssignments(ClientDbId client) {
 | 
				
			|||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
template <typename DataType>
 | 
					 | 
				
			||||||
struct DBLoadCacheParmStruct {
 | 
					 | 
				
			||||||
    GroupManager* manager;
 | 
					 | 
				
			||||||
    DataType* data;
 | 
					 | 
				
			||||||
};
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void GroupManager::enableCache(const ClientDbId& client_database_id) {
 | 
				
			||||||
void GroupManager::enableCache(std::shared_ptr<server::ConnectedClient> client) {
 | 
					 | 
				
			||||||
    if(this->root)
 | 
					    if(this->root)
 | 
				
			||||||
    	this->root->enableCache(client);
 | 
					    	this->root->enableCache(client_database_id);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    shared_ptr<CachedClient> entry = std::make_shared<CachedClient>();
 | 
					    unique_lock cache_lock(this->cacheLock);
 | 
				
			||||||
    entry->client = client;
 | 
					    /* test if we're already having the client */
 | 
				
			||||||
 | 
					    for(auto& entry : this->cachedClients)
 | 
				
			||||||
 | 
					    	if(entry->client_database_id == client_database_id) {
 | 
				
			||||||
 | 
								entry->use_count++;
 | 
				
			||||||
 | 
								return; /* client already cached, no need to cache client */
 | 
				
			||||||
 | 
					    	}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    DBLoadCacheParmStruct<CachedClient> parm = {this, entry.get()};
 | 
					    auto entry = std::make_shared<CachedClient>();
 | 
				
			||||||
 | 
					    entry->client_database_id = client_database_id;
 | 
				
			||||||
 | 
						entry->use_count++;
 | 
				
			||||||
 | 
					    this->cachedClients.push_back(entry);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    auto res = sql::command(this->sql, "SELECT `groupId`, `channelId`, `until` FROM `assignedGroups` WHERE `serverId` = :sid AND `cldbid` = :cldbid", variable{":sid", this->getServerId()}, variable{":cldbid", client->getClientDatabaseId()}).query([&](DBLoadCacheParmStruct<CachedClient>* parms, int length, char** value, char** column) {
 | 
						lock_guard client_cache_lock(entry->lock); /* lock the client because we're currently loading the cache. */
 | 
				
			||||||
 | 
						cache_lock.unlock();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    auto res = sql::command(this->sql, "SELECT `groupId`, `channelId`, `until` FROM `assignedGroups` WHERE `serverId` = :sid AND `cldbid` = :cldbid", variable{":sid", this->getServerId()}, variable{":cldbid", client_database_id})
 | 
				
			||||||
 | 
					    		.query([&](int length, std::string* value, std::string* column) {
 | 
				
			||||||
        shared_ptr<Group> group = nullptr;
 | 
					        shared_ptr<Group> group = nullptr;
 | 
				
			||||||
        time_point<system_clock> until;
 | 
					        time_point<system_clock> until;
 | 
				
			||||||
        ChannelId channelId = 0;
 | 
					        ChannelId channelId = 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        for(int index = 0; index < length; index++){
 | 
					        for(int index = 0; index < length; index++){
 | 
				
			||||||
            if(value[index] == nullptr) {
 | 
					            try {
 | 
				
			||||||
                logError(this->getServerId(), string() + "Invalid value at " + column[index]);
 | 
						            if(column[index] == "groupId"){
 | 
				
			||||||
 | 
							            group = this->findGroup(stoll(value[index]));
 | 
				
			||||||
 | 
						            } else if(column[index] == "until"){
 | 
				
			||||||
 | 
							            until = time_point<system_clock>() + milliseconds(stoll(value[index]));
 | 
				
			||||||
 | 
						            } else if(column[index] == "channelId"){
 | 
				
			||||||
 | 
							            channelId = stoll(value[index]);
 | 
				
			||||||
 | 
						            } else {
 | 
				
			||||||
 | 
						            	logError(this->getServerId(), "Unknown column in group assignment query: {}", column[index]);
 | 
				
			||||||
	            	continue;
 | 
						            	continue;
 | 
				
			||||||
	            }
 | 
						            }
 | 
				
			||||||
            if(strcmp(column[index], "groupId") == 0){
 | 
					            } catch(std::exception& ex) {
 | 
				
			||||||
                group = parms->manager->findGroup(stoll(value[index]));
 | 
					            	logError(this->getServerId(), "Failed to load group assignment from database for client {}. Column {} contains an invalid value: {}", client_database_id, column[index], value[index]);
 | 
				
			||||||
            } else if(strcmp(column[index], "until") == 0){
 | 
					 | 
				
			||||||
                until = time_point<system_clock>() + milliseconds(stoll(value[index]));
 | 
					 | 
				
			||||||
            } else if(strcmp(column[index], "channelId") == 0){
 | 
					 | 
				
			||||||
                channelId = stoll(value[index]);
 | 
					 | 
				
			||||||
            } else cerr << "Invalid column " << column[index] << endl;
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
        if(!group)
 | 
					 | 
				
			||||||
            	return 0;
 | 
					            	return 0;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        if(!group) {
 | 
				
			||||||
 | 
						        return 0;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        shared_ptr<GroupAssignment> assignment = std::make_shared<GroupAssignment>();
 | 
					        auto assignment = std::make_shared<GroupAssignment>();
 | 
				
			||||||
        assignment->group = group;
 | 
					        assignment->group = group;
 | 
				
			||||||
        assignment->until = until;
 | 
					        assignment->until = until;
 | 
				
			||||||
        assignment->parent = parms->data;
 | 
					        assignment->parent = &*entry;
 | 
				
			||||||
	    assignment->channelId = channelId;
 | 
						    assignment->channelId = channelId;
 | 
				
			||||||
	    assignment->server = this->getServerId();
 | 
						    assignment->server = this->getServerId();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        if(channelId == 0)
 | 
					        if(channelId == 0)
 | 
				
			||||||
            parms->data->serverGroups.push_back(assignment);
 | 
						        entry->server_groups.push_back(assignment);
 | 
				
			||||||
        else parms->data->channelGroups[channelId] = assignment;
 | 
					        else
 | 
				
			||||||
 | 
					        	entry->channel_groups[channelId] = assignment;
 | 
				
			||||||
        return 0;
 | 
					        return 0;
 | 
				
			||||||
    }, &parm);
 | 
					    });
 | 
				
			||||||
 | 
					 | 
				
			||||||
    this->cacheLock.lock();
 | 
					 | 
				
			||||||
    this->cachedClients.push_back(entry);
 | 
					 | 
				
			||||||
    this->cacheLock.unlock();
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
void GroupManager::disableCache(const shared_ptr<ConnectedClient> &client) {
 | 
					//FIXME: This method till get far more often then it should be. We should add a flag if the group cache is loaded for each std::shared_ptr<ConnectedClient> instance
 | 
				
			||||||
    if(this->root) this->root->disableCache(client);
 | 
					void GroupManager::disableCache(const ClientDbId& client_database_id) {
 | 
				
			||||||
 | 
					    if(this->root)
 | 
				
			||||||
 | 
					    	this->root->disableCache(client_database_id);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    threads::MutexLock lock(this->cacheLock);
 | 
						lock_guard cache_lock(this->cacheLock);
 | 
				
			||||||
    bool found = false;
 | 
						this->cachedClients.erase(std::remove_if(this->cachedClients.begin(), this->cachedClients.end(), [&](const std::shared_ptr<CachedClient>& client) {
 | 
				
			||||||
    for(const auto& entry : this->cachedClients){
 | 
							if(client->client_database_id != client_database_id)
 | 
				
			||||||
        if(entry->client == client){
 | 
								return false;
 | 
				
			||||||
            this->cachedClients.erase(std::find(this->cachedClients.begin(), this->cachedClients.end(), entry));
 | 
					
 | 
				
			||||||
            found = true;
 | 
							lock_guard client_lock{client->lock};
 | 
				
			||||||
            break;
 | 
							return (--client->use_count) == 0;
 | 
				
			||||||
        }
 | 
						}), this->cachedClients.end());
 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
    if(!found)
 | 
					 | 
				
			||||||
        ;//debugMessage("Tried to attempt to delete a not existing cached manager. (" + manager->getDisplayName() + ")");
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
void GroupManager::clearCache() {
 | 
					void GroupManager::clearCache() {
 | 
				
			||||||
    if(this->root) this->root->clearCache();
 | 
					    if(this->root) this->root->clearCache();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    threads::MutexLock lock(cacheLock);
 | 
					    lock_guard lock(this->cacheLock);
 | 
				
			||||||
    this->cachedClients.clear();
 | 
					    this->cachedClients.clear();
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
bool GroupManager::isClientCached(const shared_ptr<ConnectedClient> &client) {
 | 
					bool GroupManager::isClientCached(const ClientDbId& client_database_id) {
 | 
				
			||||||
    {
 | 
						return this->resolve_cached_client(client_database_id) == nullptr;
 | 
				
			||||||
        threads::MutexLock lock(cacheLock);
 | 
					 | 
				
			||||||
        for(const auto &entry : this->cachedClients){
 | 
					 | 
				
			||||||
            if(entry->client == client){
 | 
					 | 
				
			||||||
                return true;
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    if(this->root) return this->root->isClientCached(client);
 | 
					 | 
				
			||||||
    return false;
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
typedef std::vector<std::shared_ptr<GroupMember>> ResList;
 | 
					typedef std::vector<std::shared_ptr<GroupMember>> ResList;
 | 
				
			||||||
@ -647,19 +628,17 @@ std::vector<std::shared_ptr<GroupMember>> GroupManager::listGroupMembers(std::sh
 | 
				
			|||||||
    return result;
 | 
					    return result;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
typedef DBLoadCacheParmStruct<vector<std::shared_ptr<GroupAssignment>>> GAGroupCache;
 | 
					 | 
				
			||||||
vector<shared_ptr<GroupAssignment>> GroupManager::listGroupAssignments(ClientDbId cldbId) {
 | 
					vector<shared_ptr<GroupAssignment>> GroupManager::listGroupAssignments(ClientDbId cldbId) {
 | 
				
			||||||
    vector<std::shared_ptr<GroupAssignment>> result;
 | 
					    vector<std::shared_ptr<GroupAssignment>> result;
 | 
				
			||||||
    GAGroupCache parm = {this, &result};
 | 
					 | 
				
			||||||
    sql::result res;
 | 
					    sql::result res;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    auto cached = resolveCached(cldbId);
 | 
					    auto cached = resolve_cached_client(cldbId);
 | 
				
			||||||
    if(cached) {
 | 
					    if(cached) {
 | 
				
			||||||
	    {
 | 
						    {
 | 
				
			||||||
		    threads::MutexLock l(cached->lock);
 | 
							    lock_guard lock{cached->lock};
 | 
				
			||||||
		    for(const auto &serverGroup : cached->serverGroups)
 | 
							    for(const auto &serverGroup : cached->server_groups)
 | 
				
			||||||
			    result.push_back(serverGroup);
 | 
								    result.push_back(serverGroup);
 | 
				
			||||||
		    for(auto& channelGroup : cached->channelGroups)
 | 
							    for(auto& channelGroup : cached->channel_groups)
 | 
				
			||||||
			    result.push_back(channelGroup.second);
 | 
								    result.push_back(channelGroup.second);
 | 
				
			||||||
	    }
 | 
						    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -673,7 +652,8 @@ vector<shared_ptr<GroupAssignment>> GroupManager::listGroupAssignments(ClientDbI
 | 
				
			|||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    res = sql::command(this->sql, "SELECT `groupId`, `until`, `channelId` FROM `assignedGroups` WHERE `serverId` = :sid AND `cldbid` = :cldbid", variable{":sid", this->getServerId()}, variable{":cldbid", cldbId}).query([&](GAGroupCache* parms, int length, char** value, char** column){
 | 
					    res = sql::command(this->sql, "SELECT `groupId`, `until`, `channelId` FROM `assignedGroups` WHERE `serverId` = :sid AND `cldbid` = :cldbid", variable{":sid", this->getServerId()}, variable{":cldbid", cldbId})
 | 
				
			||||||
 | 
					    		.query([&](int length, char** value, char** column){
 | 
				
			||||||
        shared_ptr<Group> group = nullptr;
 | 
					        shared_ptr<Group> group = nullptr;
 | 
				
			||||||
        time_point<system_clock> until;
 | 
					        time_point<system_clock> until;
 | 
				
			||||||
        uint64_t channelId = 0;
 | 
					        uint64_t channelId = 0;
 | 
				
			||||||
@ -684,7 +664,7 @@ vector<shared_ptr<GroupAssignment>> GroupManager::listGroupAssignments(ClientDbI
 | 
				
			|||||||
                continue;
 | 
					                continue;
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
            if(strcmp(column[index], "groupId") == 0){
 | 
					            if(strcmp(column[index], "groupId") == 0){
 | 
				
			||||||
                group = parms->manager->findGroup(stoll(value[index]));
 | 
					                group = this->findGroup(stoll(value[index]));
 | 
				
			||||||
            } else if(strcmp(column[index], "until") == 0){
 | 
					            } else if(strcmp(column[index], "until") == 0){
 | 
				
			||||||
                until = time_point<system_clock>() + milliseconds(stoll(value[index]));
 | 
					                until = time_point<system_clock>() + milliseconds(stoll(value[index]));
 | 
				
			||||||
            } else if(strcmp(column[index], "channelId") == 0){
 | 
					            } else if(strcmp(column[index], "channelId") == 0){
 | 
				
			||||||
@ -700,9 +680,9 @@ vector<shared_ptr<GroupAssignment>> GroupManager::listGroupAssignments(ClientDbI
 | 
				
			|||||||
        assignment->until = until;
 | 
					        assignment->until = until;
 | 
				
			||||||
        assignment->channelId = channelId;
 | 
					        assignment->channelId = channelId;
 | 
				
			||||||
        assignment->server = this->getServerId();
 | 
					        assignment->server = this->getServerId();
 | 
				
			||||||
        parms->data->push_back(assignment);
 | 
					        result.push_back(assignment);
 | 
				
			||||||
        return 0;
 | 
					        return 0;
 | 
				
			||||||
    }, &parm);
 | 
					    });
 | 
				
			||||||
    (LOG_SQL_CMD)(res);
 | 
					    (LOG_SQL_CMD)(res);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    if(this->root){
 | 
					    if(this->root){
 | 
				
			||||||
@ -714,18 +694,19 @@ vector<shared_ptr<GroupAssignment>> GroupManager::listGroupAssignments(ClientDbI
 | 
				
			|||||||
    return result;
 | 
					    return result;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
std::shared_ptr<CachedClient> GroupManager::resolveCached(ClientDbId cldbId) {
 | 
					std::shared_ptr<CachedClient> GroupManager::resolve_cached_client(ClientDbId client_database_id) {
 | 
				
			||||||
    threads::MutexLock lock(this->cacheLock);
 | 
						{
 | 
				
			||||||
    for(const auto& cl : this->cachedClients)
 | 
							lock_guard lock(this->cacheLock);
 | 
				
			||||||
        if(cl->client->getClientDatabaseId() == cldbId){
 | 
							for(auto& entry : this->cachedClients)
 | 
				
			||||||
            return cl;
 | 
								if(entry->client_database_id == client_database_id)
 | 
				
			||||||
 | 
									return entry;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	return nullptr;
 | 
						return nullptr;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
typedef DBLoadCacheParmStruct<std::vector<std::shared_ptr<GroupAssignment>>> SGroupCache;
 | 
					 | 
				
			||||||
std::vector<std::shared_ptr<GroupAssignment>> GroupManager::getAssignedServerGroups(ClientDbId cldbid) {
 | 
					std::vector<std::shared_ptr<GroupAssignment>> GroupManager::getAssignedServerGroups(ClientDbId cldbid) {
 | 
				
			||||||
	auto cached = this->resolveCached(cldbid);
 | 
						auto cached = this->resolve_cached_client(cldbid);
 | 
				
			||||||
	sql::result res;
 | 
						sql::result res;
 | 
				
			||||||
	std::vector<std::shared_ptr<GroupAssignment>> result;
 | 
						std::vector<std::shared_ptr<GroupAssignment>> result;
 | 
				
			||||||
	if(this->root) {
 | 
						if(this->root) {
 | 
				
			||||||
@ -733,17 +714,15 @@ std::vector<std::shared_ptr<GroupAssignment>> GroupManager::getAssignedServerGro
 | 
				
			|||||||
		result.insert(result.begin(), root.begin(), root.end());
 | 
							result.insert(result.begin(), root.begin(), root.end());
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	SGroupCache parm = {this, &result};
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if(cached) {
 | 
						if(cached) {
 | 
				
			||||||
		threads::MutexLock l(cached->lock);
 | 
							lock_guard cache_lock{cached->lock};
 | 
				
			||||||
		for(const auto &elm : cached->serverGroups) result.push_back(elm);
 | 
							result.insert(result.end(), cached->server_groups.begin(), cached->server_groups.end());
 | 
				
			||||||
		return result;
 | 
							return result;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	debugMessage("DB query groups! for -> " + to_string(cldbid) + " - server " + to_string(this->getServerId()));
 | 
						debugMessage("DB query groups! for -> " + to_string(cldbid) + " - server " + to_string(this->getServerId()));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	res = sql::command(this->sql, "SELECT `groupId`, `until` FROM `assignedGroups` WHERE `serverId` = :sid AND `cldbid` = :cldbid AND `channelId` = 0", variable{":sid", this->getServerId()}, variable{":cldbid", cldbid}).query([&](SGroupCache* parms, int length, char** value, char** column){
 | 
						res = sql::command(this->sql, "SELECT `groupId`, `until` FROM `assignedGroups` WHERE `serverId` = :sid AND `cldbid` = :cldbid AND `channelId` = 0", variable{":sid", this->getServerId()}, variable{":cldbid", cldbid}).query([&](int length, char** value, char** column){
 | 
				
			||||||
		shared_ptr<Group> group = nullptr;
 | 
							shared_ptr<Group> group = nullptr;
 | 
				
			||||||
		time_point<system_clock> until;
 | 
							time_point<system_clock> until;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -753,7 +732,7 @@ std::vector<std::shared_ptr<GroupAssignment>> GroupManager::getAssignedServerGro
 | 
				
			|||||||
				continue;
 | 
									continue;
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
			if(strcmp(column[index], "groupId") == 0 && value[index] != nullptr){
 | 
								if(strcmp(column[index], "groupId") == 0 && value[index] != nullptr){
 | 
				
			||||||
				group = parms->manager->findGroup(stoll(value[index]));
 | 
									group = this->findGroup(stoll(value[index]));
 | 
				
			||||||
			} else if(strcmp(column[index], "until") == 0){
 | 
								} else if(strcmp(column[index], "until") == 0){
 | 
				
			||||||
				until = time_point<system_clock>() + milliseconds(stoll(value[index] == nullptr ? "0" : value[index]));
 | 
									until = time_point<system_clock>() + milliseconds(stoll(value[index] == nullptr ? "0" : value[index]));
 | 
				
			||||||
			} else cerr << "Invalid column " << column[index] << endl;
 | 
								} else cerr << "Invalid column " << column[index] << endl;
 | 
				
			||||||
@ -766,9 +745,9 @@ std::vector<std::shared_ptr<GroupAssignment>> GroupManager::getAssignedServerGro
 | 
				
			|||||||
		assignment->group = group;
 | 
							assignment->group = group;
 | 
				
			||||||
		assignment->until = until;
 | 
							assignment->until = until;
 | 
				
			||||||
		assignment->server = this->getServerId();
 | 
							assignment->server = this->getServerId();
 | 
				
			||||||
		parms->data->push_back(assignment);
 | 
							result.push_back(assignment);
 | 
				
			||||||
		return 0;
 | 
							return 0;
 | 
				
			||||||
	}, &parm);
 | 
						});
 | 
				
			||||||
	LOG_SQL_CMD(res);
 | 
						LOG_SQL_CMD(res);
 | 
				
			||||||
	return result;
 | 
						return result;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
@ -802,20 +781,18 @@ std::vector<std::shared_ptr<GroupAssignment>> GroupManager::defaultServerGroupGr
 | 
				
			|||||||
	return result;
 | 
						return result;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
typedef DBLoadCacheParmStruct<std::shared_ptr<GroupAssignment>> CGroupCache;
 | 
					 | 
				
			||||||
std::shared_ptr<GroupAssignment> GroupManager::getChannelGroupExact(ClientDbId cldbId, const std::shared_ptr<BasicChannel>& channel, bool assign_default) {
 | 
					std::shared_ptr<GroupAssignment> GroupManager::getChannelGroupExact(ClientDbId cldbId, const std::shared_ptr<BasicChannel>& channel, bool assign_default) {
 | 
				
			||||||
	auto cached = resolveCached(cldbId);
 | 
						auto cached = resolve_cached_client(cldbId);
 | 
				
			||||||
	if(cached) {
 | 
						if(cached) {
 | 
				
			||||||
		threads::MutexLock l(cached->lock);
 | 
							lock_guard cache_lock(cached->lock);
 | 
				
			||||||
		if(cached->channelGroups.count(channel->channelId()) > 0) {
 | 
							if(cached->channel_groups.count(channel->channelId()) > 0) {
 | 
				
			||||||
			return cached->channelGroups[channel->channelId()];
 | 
								return cached->channel_groups[channel->channelId()];
 | 
				
			||||||
		} else
 | 
							} else
 | 
				
			||||||
			return assign_default ? this->defaultChannelGroupAssignment(cldbId, channel) : nullptr;
 | 
								return assign_default ? this->defaultChannelGroupAssignment(cldbId, channel) : nullptr;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	std::shared_ptr<GroupAssignment> result;
 | 
						std::shared_ptr<GroupAssignment> result;
 | 
				
			||||||
	CGroupCache parm = {this, &result};
 | 
						auto res = sql::command(this->sql, "SELECT `groupId`, `until` FROM `assignedGroups` WHERE `serverId` = :sid AND `cldbid` = :cldbid AND `channelId` = :chid", variable{":sid", this->getServerId()}, variable{":cldbid", cldbId}, variable{":chid", channel->channelId()}).query([&](int length, char** value, char** column){
 | 
				
			||||||
	auto res = sql::command(this->sql, "SELECT `groupId`, `until` FROM `assignedGroups` WHERE `serverId` = :sid AND `cldbid` = :cldbid AND `channelId` = :chid", variable{":sid", this->getServerId()}, variable{":cldbid", cldbId}, variable{":chid", channel->channelId()}).query([&](CGroupCache* parms, int length, char** value, char** column){
 | 
					 | 
				
			||||||
		shared_ptr<Group> group = nullptr;
 | 
							shared_ptr<Group> group = nullptr;
 | 
				
			||||||
		time_point<system_clock> until;
 | 
							time_point<system_clock> until;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -825,7 +802,7 @@ std::shared_ptr<GroupAssignment> GroupManager::getChannelGroupExact(ClientDbId c
 | 
				
			|||||||
				continue;
 | 
									continue;
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
			if(strcmp(column[index], "groupId") == 0){
 | 
								if(strcmp(column[index], "groupId") == 0){
 | 
				
			||||||
				group = parms->manager->findGroup(stoll(value[index]));
 | 
									group = this->findGroup(stoll(value[index]));
 | 
				
			||||||
			} else if(strcmp(column[index], "until") == 0){
 | 
								} else if(strcmp(column[index], "until") == 0){
 | 
				
			||||||
				until = time_point<system_clock>() + milliseconds(stoll(value[index]));
 | 
									until = time_point<system_clock>() + milliseconds(stoll(value[index]));
 | 
				
			||||||
			} else cerr << "Invalid column " << column[index] << endl;
 | 
								} else cerr << "Invalid column " << column[index] << endl;
 | 
				
			||||||
@ -839,9 +816,9 @@ std::shared_ptr<GroupAssignment> GroupManager::getChannelGroupExact(ClientDbId c
 | 
				
			|||||||
		assignment->until = until;
 | 
							assignment->until = until;
 | 
				
			||||||
		assignment->server = this->getServerId();
 | 
							assignment->server = this->getServerId();
 | 
				
			||||||
		assignment->channelId = channel->channelId();
 | 
							assignment->channelId = channel->channelId();
 | 
				
			||||||
		*parms->data = assignment;
 | 
							result = std::move(assignment);
 | 
				
			||||||
		return 0;
 | 
							return 0;
 | 
				
			||||||
	}, &parm);
 | 
						});
 | 
				
			||||||
	(LOG_SQL_CMD)(res);
 | 
						(LOG_SQL_CMD)(res);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	return !result && assign_default ? this->defaultChannelGroupAssignment(cldbId, channel) : result;
 | 
						return !result && assign_default ? this->defaultChannelGroupAssignment(cldbId, channel) : result;
 | 
				
			||||||
@ -876,10 +853,10 @@ void GroupManager::addServerGroup(ClientDbId cldbId, std::shared_ptr<Group> grou
 | 
				
			|||||||
    if(hasServerGroup(cldbId, group)) return;
 | 
					    if(hasServerGroup(cldbId, group)) return;
 | 
				
			||||||
    */
 | 
					    */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    auto cached = resolveCached(cldbId);
 | 
					    auto cached = resolve_cached_client(cldbId);
 | 
				
			||||||
    if(cached) {
 | 
					    if(cached) {
 | 
				
			||||||
	    threads::MutexLock l(cached->lock);
 | 
						    lock_guard cache_lock(cached->lock);
 | 
				
			||||||
	    cached->serverGroups.push_back(std::make_shared<GroupAssignment>(cached.get(), this->getServerId(), 0, group, until));
 | 
						    cached->server_groups.push_back(std::make_shared<GroupAssignment>(cached.get(), this->getServerId(), 0, group, until));
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    sql::command(this->sql, "INSERT INTO `assignedGroups` (`serverId`, `cldbid`, `groupId`, `channelId`, `until`) VALUES (:sid, :cldbid, :gid, :chid, :until)",
 | 
					    sql::command(this->sql, "INSERT INTO `assignedGroups` (`serverId`, `cldbid`, `groupId`, `channelId`, `until`) VALUES (:sid, :cldbid, :gid, :chid, :until)",
 | 
				
			||||||
@ -893,14 +870,12 @@ void GroupManager::addServerGroup(ClientDbId cldbId, std::shared_ptr<Group> grou
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
void GroupManager::removeServerGroup(ClientDbId cldbId, std::shared_ptr<Group> group) {
 | 
					void GroupManager::removeServerGroup(ClientDbId cldbId, std::shared_ptr<Group> group) {
 | 
				
			||||||
    if(!this->hasServerGroupAssigned(cldbId, group)) return;
 | 
					    if(!this->hasServerGroupAssigned(cldbId, group)) return;
 | 
				
			||||||
    auto cached = resolveCached(cldbId);
 | 
					    auto cached = resolve_cached_client(cldbId);
 | 
				
			||||||
    if(cached) {
 | 
					    if(cached) {
 | 
				
			||||||
	    threads::MutexLock l(cached->lock);
 | 
						    lock_guard cache_lock(cached->lock);
 | 
				
			||||||
        for(const auto &entry : cached->serverGroups)
 | 
						    cached->server_groups.erase(std::remove_if(cached->server_groups.begin(), cached->server_groups.end(), [&](const std::shared_ptr<GroupAssignment>& group_assignment) {
 | 
				
			||||||
            if(entry->group == group){
 | 
						    	return group_assignment->group == group;
 | 
				
			||||||
                cached->serverGroups.erase(std::find(cached->serverGroups.begin(), cached->serverGroups.end(), entry));
 | 
						    }), cached->server_groups.end());
 | 
				
			||||||
                break;
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    sql::command(this->sql, "DELETE FROM `assignedGroups` WHERE `serverId` =  :sid AND `cldbid` = :cldbid AND `groupId` = :gid AND `channelId` = :chid",
 | 
					    sql::command(this->sql, "DELETE FROM `assignedGroups` WHERE `serverId` =  :sid AND `cldbid` = :cldbid AND `groupId` = :gid AND `channelId` = :chid",
 | 
				
			||||||
@ -918,13 +893,13 @@ void GroupManager::setChannelGroup(ClientDbId cldbId, std::shared_ptr<Group> gro
 | 
				
			|||||||
	} else if(!group) return;
 | 
						} else if(!group) return;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	auto default_group = !group || group == this->defaultGroup(GroupTarget::GROUPTARGET_CHANNEL);
 | 
						auto default_group = !group || group == this->defaultGroup(GroupTarget::GROUPTARGET_CHANNEL);
 | 
				
			||||||
    auto cached = resolveCached(cldbId);
 | 
					    auto cached = resolve_cached_client(cldbId);
 | 
				
			||||||
    if(cached) {
 | 
					    if(cached) {
 | 
				
			||||||
	    threads::MutexLock l(cached->lock);
 | 
						    lock_guard cache_lock(cached->lock);
 | 
				
			||||||
	    if(default_group)
 | 
						    if(default_group)
 | 
				
			||||||
		    cached->channelGroups.erase(channel->channelId());
 | 
							    cached->channel_groups.erase(channel->channelId());
 | 
				
			||||||
	    else
 | 
						    else
 | 
				
			||||||
	        cached->channelGroups[channel->channelId()] = std::make_shared<GroupAssignment>(cached.get(), this->getServerId(), channel->channelId(), group, until);
 | 
						        cached->channel_groups[channel->channelId()] = std::make_shared<GroupAssignment>(cached.get(), this->getServerId(), channel->channelId(), group, until);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    sql::command(this->sql, "DELETE FROM `assignedGroups` WHERE `serverId` =  :sid AND `cldbid` = :cldbid AND `channelId` = :chid",
 | 
					    sql::command(this->sql, "DELETE FROM `assignedGroups` WHERE `serverId` =  :sid AND `cldbid` = :cldbid AND `channelId` = :chid",
 | 
				
			||||||
 | 
				
			|||||||
@ -17,7 +17,7 @@ namespace ts {
 | 
				
			|||||||
        class ConnectedClient;
 | 
					        class ConnectedClient;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    class CachedClient;
 | 
						struct CachedClient;
 | 
				
			||||||
    class GroupManager;
 | 
					    class GroupManager;
 | 
				
			||||||
    class Group;
 | 
					    class Group;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -66,10 +66,14 @@ namespace ts {
 | 
				
			|||||||
    };
 | 
					    };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    struct CachedClient {
 | 
					    struct CachedClient {
 | 
				
			||||||
        std::shared_ptr<server::ConnectedClient> client;
 | 
					        ClientDbId client_database_id;
 | 
				
			||||||
        std::vector<std::shared_ptr<GroupAssignment>> serverGroups;
 | 
					
 | 
				
			||||||
        std::map<ts::ChannelId, std::shared_ptr<GroupAssignment>> channelGroups;
 | 
					        std::vector<std::shared_ptr<GroupAssignment>> server_groups;
 | 
				
			||||||
        threads::Mutex lock;
 | 
					        std::map<ts::ChannelId, std::shared_ptr<GroupAssignment>> channel_groups;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        size_t use_count = 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        std::mutex lock; /* never lock this lock before the general client cache! */
 | 
				
			||||||
    };
 | 
					    };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    class Group {
 | 
					    class Group {
 | 
				
			||||||
@ -160,6 +164,12 @@ namespace ts {
 | 
				
			|||||||
                return false;
 | 
					                return false;
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            std::shared_ptr<GroupAssignment> get_group_assignment(const ClientDbId& client_database_id, const std::shared_ptr<Group>& group) {
 | 
				
			||||||
 | 
						            for(const auto& assign : this->getAssignedServerGroups(client_database_id))
 | 
				
			||||||
 | 
						            	if(assign->group == group) return assign;
 | 
				
			||||||
 | 
						            return nullptr;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            std::vector<std::shared_ptr<GroupAssignment>> getServerGroups(ClientDbId cldbid, server::ClientType type);
 | 
					            std::vector<std::shared_ptr<GroupAssignment>> getServerGroups(ClientDbId cldbid, server::ClientType type);
 | 
				
			||||||
            inline bool hasServerGroup(uint64_t cldbId, server::ClientType type, const std::shared_ptr<Group>& group){
 | 
					            inline bool hasServerGroup(uint64_t cldbId, server::ClientType type, const std::shared_ptr<Group>& group){
 | 
				
			||||||
                for(const auto& assign : this->getServerGroups(cldbId, type)) if(assign->group == group) return true;
 | 
					                for(const auto& assign : this->getServerGroups(cldbId, type)) if(assign->group == group) return true;
 | 
				
			||||||
@ -200,15 +210,15 @@ namespace ts {
 | 
				
			|||||||
            std::shared_ptr<Group> defaultGroup(GroupTarget type, bool enforce_property = false);
 | 
					            std::shared_ptr<Group> defaultGroup(GroupTarget type, bool enforce_property = false);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            std::deque<property::ClientProperties> update_server_group_property(const std::shared_ptr<server::ConnectedClient> &client, bool channel_lock);
 | 
					            std::deque<property::ClientProperties> update_server_group_property(const std::shared_ptr<server::ConnectedClient> &client, bool channel_lock);
 | 
				
			||||||
            void enableCache(std::shared_ptr<server::ConnectedClient> client);
 | 
					            void enableCache(const ClientDbId& /* client database id */); /* if this called disableCache(...) MUST be called to decrease the reference count */
 | 
				
			||||||
            void disableCache(const std::shared_ptr<server::ConnectedClient> &client);
 | 
					            void disableCache(const ClientDbId& /* client database id */);
 | 
				
			||||||
            bool isClientCached(const std::shared_ptr<server::ConnectedClient> &client);
 | 
					            bool isClientCached(const ClientDbId& /* client database id */);
 | 
				
			||||||
            void clearCache();
 | 
					            void clearCache();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            bool isLocalGroup(std::shared_ptr<Group>);
 | 
					            bool isLocalGroup(std::shared_ptr<Group>);
 | 
				
			||||||
        protected:
 | 
					        protected:
 | 
				
			||||||
            void handleChannelDeleted(std::shared_ptr<BasicChannel> );
 | 
					            void handleChannelDeleted(const ChannelId& /* channel id */);
 | 
				
			||||||
        private:
 | 
					        private:
 | 
				
			||||||
            std::shared_ptr<GroupManager> root = nullptr;
 | 
					            std::shared_ptr<GroupManager> root = nullptr;
 | 
				
			||||||
		    std::weak_ptr<server::TSServer> server;
 | 
							    std::weak_ptr<server::TSServer> server;
 | 
				
			||||||
@ -221,6 +231,6 @@ namespace ts {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
            int insertGroupFromDb(int count, char** values, char** column);
 | 
					            int insertGroupFromDb(int count, char** values, char** column);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            inline std::shared_ptr<CachedClient> resolveCached(ClientDbId cldbId);
 | 
					            inline std::shared_ptr<CachedClient> resolve_cached_client(ClientDbId client_database_id);
 | 
				
			||||||
    };
 | 
					    };
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
@ -532,10 +532,10 @@ void ServerChannelTree::on_channel_entry_deleted(const shared_ptr<BasicChannel>
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
	auto server = this->server.lock();
 | 
						auto server = this->server.lock();
 | 
				
			||||||
	if(server) {
 | 
						if(server) {
 | 
				
			||||||
		server->getGroupManager()->handleChannelDeleted(channel);
 | 
							server->getGroupManager()->handleChannelDeleted(channel->channelId());
 | 
				
			||||||
		server->conversation_manager()->delete_conversation(channel->channelId());
 | 
							server->conversation_manager()->delete_conversation(channel->channelId());
 | 
				
			||||||
	} else
 | 
						} else
 | 
				
			||||||
		serverInstance->getGroupManager()->handleChannelDeleted(channel);
 | 
							serverInstance->getGroupManager()->handleChannelDeleted(channel->channelId());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	auto sql_result = sql::command(this->sql, "DELETE FROM `channels` WHERE `serverId` = '" + to_string(this->getServerId()) + "' AND `channelId` = '" + to_string(channel->channelId()) + "'").execute();
 | 
						auto sql_result = sql::command(this->sql, "DELETE FROM `channels` WHERE `serverId` = '" + to_string(this->getServerId()) + "' AND `channelId` = '" + to_string(channel->channelId()) + "'").execute();
 | 
				
			||||||
 | 
				
			|||||||
@ -32,6 +32,7 @@
 | 
				
			|||||||
#include <misc/rnd.h>
 | 
					#include <misc/rnd.h>
 | 
				
			||||||
#include <misc/timer.h>
 | 
					#include <misc/timer.h>
 | 
				
			||||||
#include <misc/strobf.h>
 | 
					#include <misc/strobf.h>
 | 
				
			||||||
 | 
					#include <misc/scope_guard.h>
 | 
				
			||||||
#include <bbcode/bbcodes.h>
 | 
					#include <bbcode/bbcodes.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
namespace fs = std::experimental::filesystem;
 | 
					namespace fs = std::experimental::filesystem;
 | 
				
			||||||
@ -202,8 +203,8 @@ CommandResult ConnectedClient::handleCommand(Command &cmd) {
 | 
				
			|||||||
    else if (command == "servergroupdel") return this->handleCommandServerGroupDel(cmd);
 | 
					    else if (command == "servergroupdel") return this->handleCommandServerGroupDel(cmd);
 | 
				
			||||||
    else if (command == "servergrouprename") return this->handleCommandServerGroupRename(cmd);
 | 
					    else if (command == "servergrouprename") return this->handleCommandServerGroupRename(cmd);
 | 
				
			||||||
    else if (command == "servergroupclientlist") return this->handleCommandServerGroupClientList(cmd);
 | 
					    else if (command == "servergroupclientlist") return this->handleCommandServerGroupClientList(cmd);
 | 
				
			||||||
    else if (command == "servergroupaddclient") return this->handleCommandServerGroupAddClient(cmd);
 | 
					    else if (command == "servergroupaddclient" || command == "clientaddservergroup") return this->handleCommandServerGroupAddClient(cmd);
 | 
				
			||||||
    else if (command == "servergroupdelclient") return this->handleCommandServerGroupDelClient(cmd);
 | 
					    else if (command == "servergroupdelclient" || command == "clientdelservergroup") return this->handleCommandServerGroupDelClient(cmd);
 | 
				
			||||||
    else if (command == "servergrouppermlist") return this->handleCommandServerGroupPermList(cmd);
 | 
					    else if (command == "servergrouppermlist") return this->handleCommandServerGroupPermList(cmd);
 | 
				
			||||||
    else if (command == "servergroupaddperm") return this->handleCommandServerGroupAddPerm(cmd);
 | 
					    else if (command == "servergroupaddperm") return this->handleCommandServerGroupAddPerm(cmd);
 | 
				
			||||||
    else if (command == "servergroupdelperm") return this->handleCommandServerGroupDelPerm(cmd);
 | 
					    else if (command == "servergroupdelperm") return this->handleCommandServerGroupDelPerm(cmd);
 | 
				
			||||||
@ -2731,39 +2732,107 @@ CommandResult ConnectedClient::handleCommandServerGroupAddClient(Command &cmd) {
 | 
				
			|||||||
    CMD_RESET_IDLE;
 | 
					    CMD_RESET_IDLE;
 | 
				
			||||||
    CMD_CHK_AND_INC_FLOOD_POINTS(25);
 | 
					    CMD_CHK_AND_INC_FLOOD_POINTS(25);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    auto server = cmd[0].has("sid") && cmd["sid"] == 0 ? nullptr : this->server;
 | 
					    auto target_server = cmd[0].has("sid") && cmd["sid"] == 0 ? nullptr : this->server;
 | 
				
			||||||
    auto groupManager = server ? this->server->groups : serverInstance->getGroupManager().get();
 | 
					    auto group_manager = target_server ? this->server->groups : serverInstance->getGroupManager().get();
 | 
				
			||||||
    auto serverGroup = groupManager->findGroup(cmd["sgid"].as<GroupId>());
 | 
					 | 
				
			||||||
    if (!serverGroup) return {findError("parameter_invalid"), "invalid server group id"};
 | 
					 | 
				
			||||||
    if((server != this->server || !this->server) && serverGroup->target() != GroupTarget::GROUPTARGET_SERVER && serverGroup->type() != GroupType::GROUP_TYPE_QUERY) return {findError("parameter_invalid"), "invalid group type"};
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
	auto target_cldbid = cmd["cldbid"].as<ClientDbId>();
 | 
						auto target_cldbid = cmd["cldbid"].as<ClientDbId>();
 | 
				
			||||||
	{
 | 
						if (!serverInstance->databaseHelper()->validClientDatabaseId(target_server, cmd["cldbid"])) return {findError("client_invalid_id"), "invalid cldbid"};
 | 
				
			||||||
		if(!serverGroup->permission_granted(permission::i_server_group_needed_member_add_power, this->calculate_permission_value(permission::i_server_group_member_add_power, -1), true)) {
 | 
					 | 
				
			||||||
			if(target_cldbid != this->getClientDatabaseId())
 | 
					 | 
				
			||||||
				return CommandResultPermissionError{permission::i_server_group_member_add_power};
 | 
					 | 
				
			||||||
			if(!serverGroup->permission_granted(permission::i_server_group_needed_member_add_power, this->calculate_permission_value(permission::i_server_group_self_add_power, -1), true))
 | 
					 | 
				
			||||||
				return CommandResultPermissionError{permission::i_server_group_self_add_power};
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
	auto needed_client_permission = this->server->calculatePermission(permission::PERMTEST_ORDERED, target_cldbid, permission::i_client_needed_permission_modify_power, ClientType::CLIENT_TEAMSPEAK, nullptr);
 | 
						auto needed_client_permission = this->server->calculatePermission(permission::PERMTEST_ORDERED, target_cldbid, permission::i_client_needed_permission_modify_power, ClientType::CLIENT_TEAMSPEAK, nullptr);
 | 
				
			||||||
	if(needed_client_permission != permNotGranted) {
 | 
						if(needed_client_permission != permNotGranted) {
 | 
				
			||||||
		if(!this->permission_granted(this->permissionValue(permission::i_client_permission_modify_power), needed_client_permission))
 | 
							if(!this->permission_granted(this->permissionValue(permission::i_client_permission_modify_power), needed_client_permission))
 | 
				
			||||||
			return CommandResultPermissionError{permission::i_client_needed_permission_modify_power};
 | 
								return CommandResultPermissionError{permission::i_client_needed_permission_modify_power};
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    vector<std::shared_ptr<Group>> target_groups;
 | 
				
			||||||
 | 
						vector<std::shared_ptr<Group>> applied_groups;
 | 
				
			||||||
 | 
						target_groups.reserve(cmd.bulkCount());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						auto continue_on_error = cmd.hasParm("continueonerror");
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							auto permission_add_power = this->calculate_permission_value(permission::i_server_group_member_add_power, -1);
 | 
				
			||||||
 | 
							auto permission_self_add_power = this->calculate_permission_value(permission::i_server_group_member_add_power, -1);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							for(auto index = 0; index < cmd.bulkCount(); index++) {
 | 
				
			||||||
 | 
								auto group_id = cmd[index]["sgid"];
 | 
				
			||||||
 | 
								if(!group_id.castable<GroupId>() && continue_on_error)
 | 
				
			||||||
 | 
									continue;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								auto gid = group_id.as<GroupId>();
 | 
				
			||||||
 | 
								auto group = group_manager->findGroup(gid);
 | 
				
			||||||
 | 
								if(!group) {
 | 
				
			||||||
 | 
									if(continue_on_error)
 | 
				
			||||||
 | 
										continue;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
									return {findError("parameter_invalid"), "missing server group for id " + to_string(gid)};
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    if (!serverInstance->databaseHelper()->validClientDatabaseId(server, cmd["cldbid"])) return {findError("client_invalid_id"), "invalid cldbid"};
 | 
								if(!target_server && group->target() != GroupTarget::GROUPTARGET_SERVER && group->type() != GroupType::GROUP_TYPE_QUERY)
 | 
				
			||||||
    if(groupManager->hasServerGroupAssigned(cmd["cldbid"], serverGroup))  return {findError("parameter_invalid"), "Client is already member of this group"};
 | 
									return {findError("parameter_invalid"), "invalid server group type for id " + to_string(gid)};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    groupManager->addServerGroup(target_cldbid, serverGroup);
 | 
								if(find(target_groups.begin(), target_groups.end(), group) != target_groups.end()) {
 | 
				
			||||||
 | 
									if(continue_on_error)
 | 
				
			||||||
 | 
										continue;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    for(const auto& _server : server ? std::deque<shared_ptr<TSServer>>{server} : serverInstance->getVoiceServerManager()->serverInstances()) {
 | 
									return {findError("parameter_invalid"), "duplicate server group for id " + to_string(gid)};
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								/* permission tests */
 | 
				
			||||||
 | 
								if(!group->permission_granted(permission::i_server_group_needed_member_add_power, permission_add_power, true)) {
 | 
				
			||||||
 | 
									if(target_cldbid != this->getClientDatabaseId()) {
 | 
				
			||||||
 | 
										if(continue_on_error)
 | 
				
			||||||
 | 
											continue;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
										return CommandResultPermissionError{permission::i_server_group_member_add_power};
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
									if(!group->permission_granted(permission::i_server_group_needed_member_add_power, permission_self_add_power, true)) {
 | 
				
			||||||
 | 
										if(continue_on_error)
 | 
				
			||||||
 | 
											continue;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
										return CommandResultPermissionError{permission::i_server_group_self_add_power};
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								target_groups.push_back(std::move(group));
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						applied_groups.reserve(target_groups.size());
 | 
				
			||||||
 | 
						if(target_groups.empty()) return CommandResult::Success;
 | 
				
			||||||
 | 
						else if(target_groups.size() == 1) {
 | 
				
			||||||
 | 
							/* speed up thing, don't try to load any cache */
 | 
				
			||||||
 | 
							auto group = target_groups[0];
 | 
				
			||||||
 | 
							if(group_manager->hasServerGroupAssigned(target_cldbid, group))
 | 
				
			||||||
 | 
								return {findError("parameter_invalid"), "Client is already member of server group " + to_string(group->groupId())};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							group_manager->addServerGroup(target_cldbid, group);
 | 
				
			||||||
 | 
							applied_groups.push_back(group);
 | 
				
			||||||
 | 
						} else {
 | 
				
			||||||
 | 
							group_manager->enableCache(target_cldbid);
 | 
				
			||||||
 | 
							scope_exit_callback cache_disable{[group_manager, target_cldbid]{
 | 
				
			||||||
 | 
								group_manager->disableCache(target_cldbid);
 | 
				
			||||||
 | 
							}};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							for(const auto& group : target_groups) {
 | 
				
			||||||
 | 
								if(group_manager->hasServerGroupAssigned(target_cldbid, group)) {
 | 
				
			||||||
 | 
									if(continue_on_error)
 | 
				
			||||||
 | 
										continue;
 | 
				
			||||||
 | 
									return {findError("parameter_invalid"), "Client is already member of server group " + to_string(group->groupId())};
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								group_manager->addServerGroup(target_cldbid, group);
 | 
				
			||||||
 | 
								applied_groups.push_back(group);
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    for(const auto& _server : target_server ? std::deque<shared_ptr<TSServer>>{target_server} : serverInstance->getVoiceServerManager()->serverInstances()) {
 | 
				
			||||||
        for (const auto &targetClient : _server->findClientsByCldbId(target_cldbid)) {
 | 
					        for (const auto &targetClient : _server->findClientsByCldbId(target_cldbid)) {
 | 
				
			||||||
            if (_server->notifyClientPropertyUpdates(targetClient, _server->groups->update_server_group_property(targetClient, true))) {
 | 
					            if (_server->notifyClientPropertyUpdates(targetClient, _server->groups->update_server_group_property(targetClient, true))) {
 | 
				
			||||||
                for (const auto &client : _server->getClients()) {
 | 
					                for (const auto &client : _server->getClients()) {
 | 
				
			||||||
                    if(client->isClientVisible(targetClient, true) || client == targetClient)
 | 
					                    if(client->isClientVisible(targetClient, true) || client == targetClient)
 | 
				
			||||||
                    	client->notifyServerGroupClientAdd(_this.lock(), targetClient, serverGroup);
 | 
					                    	for(const auto& group : applied_groups)
 | 
				
			||||||
 | 
							                    client->notifyServerGroupClientAdd(_this.lock(), targetClient, group);
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
	            if(targetClient->update_cached_permissions()) /* update cached calculated permissions */
 | 
						            if(targetClient->update_cached_permissions()) /* update cached calculated permissions */
 | 
				
			||||||
		            targetClient->sendNeededPermissions(false); /* cached permissions had changed, notify the client */
 | 
							            targetClient->sendNeededPermissions(false); /* cached permissions had changed, notify the client */
 | 
				
			||||||
@ -2779,41 +2848,118 @@ CommandResult ConnectedClient::handleCommandServerGroupDelClient(Command &cmd) {
 | 
				
			|||||||
	CMD_RESET_IDLE;
 | 
						CMD_RESET_IDLE;
 | 
				
			||||||
	CMD_CHK_AND_INC_FLOOD_POINTS(25);
 | 
						CMD_CHK_AND_INC_FLOOD_POINTS(25);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    auto server = cmd[0].has("sid") && cmd["sid"] == 0 ? nullptr : this->server;
 | 
						auto target_server = cmd[0].has("sid") && cmd["sid"] == 0 ? nullptr : this->server;
 | 
				
			||||||
    auto groupManager = server ? this->server->groups : serverInstance->getGroupManager().get();
 | 
						auto group_manager = target_server ? this->server->groups : serverInstance->getGroupManager().get();
 | 
				
			||||||
    auto serverGroup = groupManager->findGroup(cmd["sgid"].as<GroupId>());
 | 
					 | 
				
			||||||
    if (!serverGroup) return {findError("parameter_invalid"), "invalid server group id"};
 | 
					 | 
				
			||||||
    if((server != this->server || !this->server) && serverGroup->target() != GroupTarget::GROUPTARGET_SERVER && serverGroup->type() != GroupType::GROUP_TYPE_QUERY) return {findError("parameter_invalid"), "invalid group type"};
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
	auto target_cldbid = cmd["cldbid"].as<ClientDbId>();
 | 
						auto target_cldbid = cmd["cldbid"].as<ClientDbId>();
 | 
				
			||||||
	{
 | 
						if (!serverInstance->databaseHelper()->validClientDatabaseId(target_server, cmd["cldbid"])) return {findError("client_invalid_id"), "invalid cldbid"};
 | 
				
			||||||
		if(!serverGroup->permission_granted(permission::i_server_group_needed_member_remove_power, this->calculate_permission_value(permission::i_server_group_member_remove_power, -1), true)) {
 | 
					 | 
				
			||||||
			if(target_cldbid != this->getClientDatabaseId())
 | 
					 | 
				
			||||||
				return CommandResultPermissionError{permission::i_server_group_member_remove_power};
 | 
					 | 
				
			||||||
			if(!serverGroup->permission_granted(permission::i_server_group_needed_member_remove_power, this->calculate_permission_value(permission::i_server_group_self_remove_power, -1), true))
 | 
					 | 
				
			||||||
				return CommandResultPermissionError{permission::i_server_group_self_remove_power};
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
	auto needed_client_permission = this->server->calculatePermission(permission::PERMTEST_ORDERED, target_cldbid, permission::i_client_needed_permission_modify_power, ClientType::CLIENT_TEAMSPEAK, nullptr);
 | 
						auto needed_client_permission = this->server->calculatePermission(permission::PERMTEST_ORDERED, target_cldbid, permission::i_client_needed_permission_modify_power, ClientType::CLIENT_TEAMSPEAK, nullptr);
 | 
				
			||||||
	if(needed_client_permission != permNotGranted) {
 | 
						if(needed_client_permission != permNotGranted) {
 | 
				
			||||||
		if(!this->permission_granted(this->permissionValue(permission::i_client_permission_modify_power), needed_client_permission))
 | 
							if(!this->permission_granted(this->permissionValue(permission::i_client_permission_modify_power), needed_client_permission))
 | 
				
			||||||
			return CommandResultPermissionError{permission::i_client_needed_permission_modify_power};
 | 
								return CommandResultPermissionError{permission::i_client_needed_permission_modify_power};
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						vector<std::shared_ptr<Group>> target_groups;
 | 
				
			||||||
 | 
						vector<std::shared_ptr<Group>> applied_groups;
 | 
				
			||||||
 | 
						target_groups.reserve(cmd.bulkCount());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						auto continue_on_error = cmd.hasParm("continueonerror");
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							auto permission_remove_power = this->calculate_permission_value(permission::i_server_group_member_remove_power, -1);
 | 
				
			||||||
 | 
							auto permission_self_remove_power = this->calculate_permission_value(permission::i_server_group_member_remove_power, -1);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							for(auto index = 0; index < cmd.bulkCount(); index++) {
 | 
				
			||||||
 | 
								auto group_id = cmd[index]["sgid"];
 | 
				
			||||||
 | 
								if(!group_id.castable<GroupId>() && continue_on_error)
 | 
				
			||||||
 | 
									continue;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								auto gid = group_id.as<GroupId>();
 | 
				
			||||||
 | 
								auto group = group_manager->findGroup(gid);
 | 
				
			||||||
 | 
								if(!group) {
 | 
				
			||||||
 | 
									if(continue_on_error)
 | 
				
			||||||
 | 
										continue;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
									return {findError("parameter_invalid"), "missing server group for id " + to_string(gid)};
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    if (!serverInstance->databaseHelper()->validClientDatabaseId(server, cmd["cldbid"])) return {findError("client_invalid_id"), "invalid cldbid"};
 | 
								if(!target_server && group->target() != GroupTarget::GROUPTARGET_SERVER && group->type() != GroupType::GROUP_TYPE_QUERY)
 | 
				
			||||||
    if(!groupManager->hasServerGroupAssigned(cmd["cldbid"], serverGroup)) return {findError("parameter_invalid"), "Client isn't a member of this group"};
 | 
									return {findError("parameter_invalid"), "invalid server group type for id " + to_string(gid)};
 | 
				
			||||||
    for(const auto& assignment : groupManager->listGroupAssignments(cmd["cldbid"]))
 | 
					 | 
				
			||||||
        if(assignment->group == serverGroup && assignment->server != this->getServerId()) return {findError("parameter_invalid"), "Group wasn't assigned over this server (Assigned server: " + to_string(assignment->server) + ")"};
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
    groupManager->removeServerGroup(target_cldbid, serverGroup);
 | 
								if(find(target_groups.begin(), target_groups.end(), group) != target_groups.end()) {
 | 
				
			||||||
 | 
									if(continue_on_error)
 | 
				
			||||||
 | 
										continue;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    for(const auto& _server : server ? std::deque<shared_ptr<TSServer>>{server} : serverInstance->getVoiceServerManager()->serverInstances()) {
 | 
									return {findError("parameter_invalid"), "duplicate server group for id " + to_string(gid)};
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								/* permission tests */
 | 
				
			||||||
 | 
								if(!group->permission_granted(permission::i_server_group_needed_member_remove_power, permission_remove_power, true)) {
 | 
				
			||||||
 | 
									if(target_cldbid != this->getClientDatabaseId()) {
 | 
				
			||||||
 | 
										if(continue_on_error)
 | 
				
			||||||
 | 
											continue;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
										return CommandResultPermissionError{permission::i_server_group_member_remove_power};
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
									if(!group->permission_granted(permission::i_server_group_needed_member_remove_power, permission_self_remove_power, true)) {
 | 
				
			||||||
 | 
										if(continue_on_error)
 | 
				
			||||||
 | 
											continue;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
										return CommandResultPermissionError{permission::i_server_group_self_remove_power};
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								target_groups.push_back(std::move(group));
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						applied_groups.reserve(target_groups.size());
 | 
				
			||||||
 | 
						if(target_groups.empty()) return CommandResult::Success;
 | 
				
			||||||
 | 
						else if(target_groups.size() == 1) {
 | 
				
			||||||
 | 
							/* speed up thing, don't try to load any cache */
 | 
				
			||||||
 | 
							auto group = target_groups[0];
 | 
				
			||||||
 | 
							auto assignment = group_manager->get_group_assignment(target_cldbid, group);
 | 
				
			||||||
 | 
							if(!assignment) {
 | 
				
			||||||
 | 
								return {findError("parameter_invalid"), "Client is not member of server group " + to_string(group->groupId())};
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							if(assignment->server != (target_server ? target_server->getServerId() : 0)) {
 | 
				
			||||||
 | 
								return {findError("parameter_invalid"), "Group assignment for group " + to_string(assignment->group->groupId()) + " hasn't been made over the target server. Assignment origin server id " + to_string(assignment->server)};
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							group_manager->removeServerGroup(target_cldbid, group);
 | 
				
			||||||
 | 
							applied_groups.push_back(group);
 | 
				
			||||||
 | 
						} else {
 | 
				
			||||||
 | 
							group_manager->enableCache(target_cldbid);
 | 
				
			||||||
 | 
							scope_exit_callback cache_disable{[group_manager, target_cldbid]{
 | 
				
			||||||
 | 
								group_manager->disableCache(target_cldbid);
 | 
				
			||||||
 | 
							}};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							for(const auto& group : target_groups) {
 | 
				
			||||||
 | 
								auto assignment = group_manager->get_group_assignment(target_cldbid, group);
 | 
				
			||||||
 | 
								if(!assignment) {
 | 
				
			||||||
 | 
									if(continue_on_error)
 | 
				
			||||||
 | 
										continue;
 | 
				
			||||||
 | 
									return {findError("parameter_invalid"), "Client is not member of server group " + to_string(group->groupId())};
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								if(assignment->server != (target_server ? target_server->getServerId() : 0)) {
 | 
				
			||||||
 | 
									if(continue_on_error)
 | 
				
			||||||
 | 
										continue;
 | 
				
			||||||
 | 
									return {findError("parameter_invalid"), "Group assignment for group " + to_string(assignment->group->groupId()) + " hasn't been made over the target server. Assignment origin server id " + to_string(assignment->server)};
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								applied_groups.push_back(group);
 | 
				
			||||||
 | 
								group_manager->removeServerGroup(target_cldbid, group);
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						for(const auto& _server : target_server ? std::deque<shared_ptr<TSServer>>{target_server} : serverInstance->getVoiceServerManager()->serverInstances()) {
 | 
				
			||||||
		for (const auto &targetClient : _server->findClientsByCldbId(target_cldbid)) {
 | 
							for (const auto &targetClient : _server->findClientsByCldbId(target_cldbid)) {
 | 
				
			||||||
			if (_server->notifyClientPropertyUpdates(targetClient, _server->groups->update_server_group_property(targetClient, true))) {
 | 
								if (_server->notifyClientPropertyUpdates(targetClient, _server->groups->update_server_group_property(targetClient, true))) {
 | 
				
			||||||
				for (const auto &client : _server->getClients()) {
 | 
									for (const auto &client : _server->getClients()) {
 | 
				
			||||||
					if(client->isClientVisible(targetClient, true) || client == targetClient)
 | 
										if(client->isClientVisible(targetClient, true) || client == targetClient)
 | 
				
			||||||
			            client->notifyServerGroupClientRemove(_this.lock(), targetClient, serverGroup);
 | 
											for(const auto& group : applied_groups)
 | 
				
			||||||
 | 
												client->notifyServerGroupClientRemove(_this.lock(), targetClient, group);
 | 
				
			||||||
				}
 | 
									}
 | 
				
			||||||
				if(targetClient->update_cached_permissions()) /* update cached calculated permissions */
 | 
									if(targetClient->update_cached_permissions()) /* update cached calculated permissions */
 | 
				
			||||||
					targetClient->sendNeededPermissions(false); /* cached permissions had changed, notify the client */
 | 
										targetClient->sendNeededPermissions(false); /* cached permissions had changed, notify the client */
 | 
				
			||||||
@ -7729,3 +7875,6 @@ CommandResult ConnectedClient::handleCommandConversationMessageDelete(ts::Comman
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
@ -347,7 +347,7 @@ CommandResult SpeakingClient::handleCommandClientInit(Command& cmd) {
 | 
				
			|||||||
	TIMING_START(timings);
 | 
						TIMING_START(timings);
 | 
				
			||||||
	if(!DatabaseHelper::assignDatabaseId(this->server->getSql(), this->server->getServerId(), _this.lock())) return {findError("vs_critical"), "Could not assign database id!"};
 | 
						if(!DatabaseHelper::assignDatabaseId(this->server->getSql(), this->server->getServerId(), _this.lock())) return {findError("vs_critical"), "Could not assign database id!"};
 | 
				
			||||||
	TIMING_STEP(timings, "db assign  ");
 | 
						TIMING_STEP(timings, "db assign  ");
 | 
				
			||||||
	this->server->getGroupManager()->enableCache(_this.lock());
 | 
						this->server->getGroupManager()->enableCache(this->getClientDatabaseId());
 | 
				
			||||||
	TIMING_STEP(timings, "gr cache   ");
 | 
						TIMING_STEP(timings, "gr cache   ");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	const static vector<string> available_parameters = {
 | 
						const static vector<string> available_parameters = {
 | 
				
			||||||
@ -717,7 +717,7 @@ void SpeakingClient::processLeave() {
 | 
				
			|||||||
			unique_lock server_channel_lock(this->server->channel_tree_lock);
 | 
								unique_lock server_channel_lock(this->server->channel_tree_lock);
 | 
				
			||||||
			server->unregisterClient(ownLock, "disconnected", server_channel_lock); /* already moves client to void if needed */
 | 
								server->unregisterClient(ownLock, "disconnected", server_channel_lock); /* already moves client to void if needed */
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		server->groups->disableCache(ownLock);
 | 
							server->groups->disableCache(ownLock->getClientDatabaseId());
 | 
				
			||||||
		server->musicManager->cleanup_client_bots(this->getClientDatabaseId());
 | 
							server->musicManager->cleanup_client_bots(this->getClientDatabaseId());
 | 
				
			||||||
		//ref_server = nullptr; Removed caused nullptr exceptions
 | 
							//ref_server = nullptr; Removed caused nullptr exceptions
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
				
			|||||||
@ -159,7 +159,7 @@ bool QueryClient::closeConnection(const std::chrono::system_clock::time_point& f
 | 
				
			|||||||
			unique_lock channel_lock(this->server->channel_tree_lock);
 | 
								unique_lock channel_lock(this->server->channel_tree_lock);
 | 
				
			||||||
			this->server->unregisterClient(_this.lock(), "disconnected", channel_lock);
 | 
								this->server->unregisterClient(_this.lock(), "disconnected", channel_lock);
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		this->server->groups->disableCache(_this.lock());
 | 
							this->server->groups->disableCache(this->getClientDatabaseId());
 | 
				
			||||||
		this->server = nullptr;
 | 
							this->server = nullptr;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -234,7 +234,7 @@ void QueryClient::disconnectFinal() {
 | 
				
			|||||||
		    unique_lock channel_lock(this->server->channel_tree_lock);
 | 
							    unique_lock channel_lock(this->server->channel_tree_lock);
 | 
				
			||||||
		    this->server->unregisterClient(_this.lock(), "disconnected", channel_lock);
 | 
							    this->server->unregisterClient(_this.lock(), "disconnected", channel_lock);
 | 
				
			||||||
	    }
 | 
						    }
 | 
				
			||||||
        this->server->groups->disableCache(_this.lock());
 | 
					        this->server->groups->disableCache(this->getClientDatabaseId());
 | 
				
			||||||
        this->server = nullptr;
 | 
					        this->server = nullptr;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
@ -175,8 +175,8 @@ CommandResult QueryClient::handleCommandLogin(Command& cmd) {
 | 
				
			|||||||
				this->server->client_move(this->ref(), nullptr, nullptr, "", ViewReasonId::VREASON_USER_ACTION, false, tree_lock);
 | 
									this->server->client_move(this->ref(), nullptr, nullptr, "", ViewReasonId::VREASON_USER_ACTION, false, tree_lock);
 | 
				
			||||||
			this->server->unregisterClient(_this.lock(), "login", tree_lock);
 | 
								this->server->unregisterClient(_this.lock(), "login", tree_lock);
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		this->server->groups->disableCache(_this.lock());
 | 
							this->server->groups->disableCache(this->getClientDatabaseId());
 | 
				
			||||||
	} else serverInstance->getGroupManager()->disableCache(_this.lock());
 | 
						} else serverInstance->getGroupManager()->disableCache(this->getClientDatabaseId());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	logMessage(LOG_QUERY, "Got new authenticated client. Username: {}, Unique-ID: {}, Bounded Server: {}", account->username, account->unique_id, account->bound_server);
 | 
						logMessage(LOG_QUERY, "Got new authenticated client. Username: {}, Unique-ID: {}, Bounded Server: {}", account->username, account->unique_id, account->bound_server);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -196,7 +196,7 @@ CommandResult QueryClient::handleCommandLogin(Command& cmd) {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
	DatabaseHelper::assignDatabaseId(this->sql, static_cast<ServerId>(target_server ? target_server->getServerId() : 0), _this.lock());
 | 
						DatabaseHelper::assignDatabaseId(this->sql, static_cast<ServerId>(target_server ? target_server->getServerId() : 0), _this.lock());
 | 
				
			||||||
	if(target_server) {
 | 
						if(target_server) {
 | 
				
			||||||
		target_server->groups->enableCache(_this.lock());
 | 
							target_server->groups->enableCache(this->getClientDatabaseId());
 | 
				
			||||||
		target_server->registerClient(_this.lock());
 | 
							target_server->registerClient(_this.lock());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		{
 | 
							{
 | 
				
			||||||
@ -219,7 +219,7 @@ CommandResult QueryClient::handleCommandLogin(Command& cmd) {
 | 
				
			|||||||
		else
 | 
							else
 | 
				
			||||||
			this->update_cached_permissions();
 | 
								this->update_cached_permissions();
 | 
				
			||||||
	} else {
 | 
						} else {
 | 
				
			||||||
		serverInstance->getGroupManager()->enableCache(_this.lock());
 | 
							serverInstance->getGroupManager()->enableCache(this->getClientDatabaseId());
 | 
				
			||||||
		this->update_cached_permissions();
 | 
							this->update_cached_permissions();
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -243,15 +243,15 @@ CommandResult QueryClient::handleCommandLogout(Command &) {
 | 
				
			|||||||
				this->server->client_move(this->ref(), nullptr, nullptr, "", ViewReasonId::VREASON_USER_ACTION, false, tree_lock);
 | 
									this->server->client_move(this->ref(), nullptr, nullptr, "", ViewReasonId::VREASON_USER_ACTION, false, tree_lock);
 | 
				
			||||||
			this->server->unregisterClient(_this.lock(), "logout", tree_lock);
 | 
								this->server->unregisterClient(_this.lock(), "logout", tree_lock);
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		this->server->groups->disableCache(_this.lock());
 | 
							this->server->groups->disableCache(this->getClientDatabaseId());
 | 
				
			||||||
	} else serverInstance->getGroupManager()->disableCache(_this.lock());
 | 
						} else serverInstance->getGroupManager()->disableCache(this->getClientDatabaseId());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	this->properties()[property::CLIENT_UNIQUE_IDENTIFIER] = "UnknownQuery"; //TODO load from table
 | 
						this->properties()[property::CLIENT_UNIQUE_IDENTIFIER] = "UnknownQuery"; //TODO load from table
 | 
				
			||||||
	this->properties()[property::CLIENT_NICKNAME] = string() + "ServerQuery#" + this->getLoggingPeerIp() + "/" + to_string(ntohs(this->getPeerPort()));
 | 
						this->properties()[property::CLIENT_NICKNAME] = string() + "ServerQuery#" + this->getLoggingPeerIp() + "/" + to_string(ntohs(this->getPeerPort()));
 | 
				
			||||||
	DatabaseHelper::assignDatabaseId(this->sql, static_cast<ServerId>(this->server ? this->server->getServerId() : 0), _this.lock());
 | 
						DatabaseHelper::assignDatabaseId(this->sql, static_cast<ServerId>(this->server ? this->server->getServerId() : 0), _this.lock());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if(this->server){
 | 
						if(this->server){
 | 
				
			||||||
		this->server->groups->enableCache(_this.lock());
 | 
							this->server->groups->enableCache(this->getClientDatabaseId());
 | 
				
			||||||
		this->server->registerClient(this->ref());
 | 
							this->server->registerClient(this->ref());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		{
 | 
							{
 | 
				
			||||||
@ -272,7 +272,7 @@ CommandResult QueryClient::handleCommandLogout(Command &) {
 | 
				
			|||||||
			this->update_cached_permissions();
 | 
								this->update_cached_permissions();
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	} else {
 | 
						} else {
 | 
				
			||||||
		serverInstance->getGroupManager()->enableCache(_this.lock());
 | 
							serverInstance->getGroupManager()->enableCache(this->getClientDatabaseId());
 | 
				
			||||||
		this->update_cached_permissions();
 | 
							this->update_cached_permissions();
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -320,10 +320,10 @@ CommandResult QueryClient::handleCommandServerSelect(Command &cmd) {
 | 
				
			|||||||
					this->server->client_move(this->ref(), nullptr, nullptr, "", ViewReasonId::VREASON_USER_ACTION, false, tree_lock);
 | 
										this->server->client_move(this->ref(), nullptr, nullptr, "", ViewReasonId::VREASON_USER_ACTION, false, tree_lock);
 | 
				
			||||||
				this->server->unregisterClient(_this.lock(), "server switch", tree_lock);
 | 
									this->server->unregisterClient(_this.lock(), "server switch", tree_lock);
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
			server_locked->groups->disableCache(_this.lock());
 | 
								server_locked->groups->disableCache(this->getClientDatabaseId());
 | 
				
			||||||
			this->channels->reset();
 | 
								this->channels->reset();
 | 
				
			||||||
		} else
 | 
							} else
 | 
				
			||||||
			serverInstance->getGroupManager()->disableCache(_this.lock());
 | 
								serverInstance->getGroupManager()->disableCache(this->getClientDatabaseId());
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	this->resetEventMask();
 | 
						this->resetEventMask();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -339,7 +339,7 @@ CommandResult QueryClient::handleCommandServerSelect(Command &cmd) {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
	DatabaseHelper::assignDatabaseId(this->sql, static_cast<ServerId>(this->server ? this->server->getServerId() : 0), _this.lock());
 | 
						DatabaseHelper::assignDatabaseId(this->sql, static_cast<ServerId>(this->server ? this->server->getServerId() : 0), _this.lock());
 | 
				
			||||||
	if(this->server) {
 | 
						if(this->server) {
 | 
				
			||||||
		this->server->groups->enableCache(_this.lock());
 | 
							this->server->groups->enableCache(this->getClientDatabaseId());
 | 
				
			||||||
		this->server->registerClient(_this.lock());
 | 
							this->server->registerClient(_this.lock());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		{
 | 
							{
 | 
				
			||||||
@ -357,7 +357,7 @@ CommandResult QueryClient::handleCommandServerSelect(Command &cmd) {
 | 
				
			|||||||
		else
 | 
							else
 | 
				
			||||||
			this->update_cached_permissions();
 | 
								this->update_cached_permissions();
 | 
				
			||||||
	} else {
 | 
						} else {
 | 
				
			||||||
		serverInstance->getGroupManager()->enableCache(_this.lock());
 | 
							serverInstance->getGroupManager()->enableCache(this->getClientDatabaseId());
 | 
				
			||||||
		this->update_cached_permissions();
 | 
							this->update_cached_permissions();
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	this->updateChannelClientProperties(true, true);
 | 
						this->updateChannelClientProperties(true, true);
 | 
				
			||||||
 | 
				
			|||||||
@ -180,7 +180,7 @@ bool WebClient::closeConnection(const std::chrono::system_clock::time_point& tim
 | 
				
			|||||||
			unique_lock server_channel_lock(this->server->channel_tree_lock);
 | 
								unique_lock server_channel_lock(this->server->channel_tree_lock);
 | 
				
			||||||
			this->server->unregisterClient(_this.lock(), "disconnected", server_channel_lock);
 | 
								this->server->unregisterClient(_this.lock(), "disconnected", server_channel_lock);
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		this->server->groups->disableCache(_this.lock());
 | 
							this->server->groups->disableCache(this->getClientDatabaseId());
 | 
				
			||||||
		//this->server = nullptr;
 | 
							//this->server = nullptr;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
@ -83,7 +83,7 @@ std::shared_ptr<server::MusicClient> MusicBotManager::createBot(ClientDbId owner
 | 
				
			|||||||
    (LOG_SQL_CMD)(sql::command(handle->getSql(), "INSERT INTO `musicbots` (`serverId`, `botId`, `uniqueId`, `owner`) VALUES (:sid, :botId, :uid, :owner)",
 | 
					    (LOG_SQL_CMD)(sql::command(handle->getSql(), "INSERT INTO `musicbots` (`serverId`, `botId`, `uniqueId`, `owner`) VALUES (:sid, :botId, :uid, :owner)",
 | 
				
			||||||
                                                     variable{":sid", handle->getServerId()}, variable{":botId", musicBot->getClientDatabaseId()}, variable{":uid", musicBot->getUid()}, variable{":owner", owner}).execute());
 | 
					                                                     variable{":sid", handle->getServerId()}, variable{":botId", musicBot->getClientDatabaseId()}, variable{":uid", musicBot->getUid()}, variable{":owner", owner}).execute());
 | 
				
			||||||
    musicBot->properties()[property::CLIENT_OWNER] = owner;
 | 
					    musicBot->properties()[property::CLIENT_OWNER] = owner;
 | 
				
			||||||
    handle->groups->enableCache(musicBot);
 | 
					    handle->groups->enableCache(musicBot->getClientDatabaseId());
 | 
				
			||||||
    musicBot->setDisplayName("Im a music bot!");
 | 
					    musicBot->setDisplayName("Im a music bot!");
 | 
				
			||||||
    musicBot->properties()[property::CLIENT_LASTCONNECTED] = duration_cast<seconds>(system_clock::now().time_since_epoch()).count();
 | 
					    musicBot->properties()[property::CLIENT_LASTCONNECTED] = duration_cast<seconds>(system_clock::now().time_since_epoch()).count();
 | 
				
			||||||
	musicBot->properties()[property::CLIENT_CREATED] = duration_cast<seconds>(system_clock::now().time_since_epoch()).count();
 | 
						musicBot->properties()[property::CLIENT_CREATED] = duration_cast<seconds>(system_clock::now().time_since_epoch()).count();
 | 
				
			||||||
@ -139,7 +139,7 @@ void MusicBotManager::deleteBot(std::shared_ptr<server::MusicClient> musicBot) {
 | 
				
			|||||||
        handle->client_move(musicBot, nullptr, nullptr, "Music bot deleted", ViewReasonId::VREASON_SERVER_LEFT, true, server_channel_lock);
 | 
					        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);
 | 
					        handle->unregisterClient(musicBot, "bot deleted", server_channel_lock);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    handle->groups->disableCache(musicBot);
 | 
					    handle->groups->disableCache(musicBot->getClientDatabaseId());
 | 
				
			||||||
    serverInstance->databaseHelper()->deleteClient(handle, musicBot->getClientDatabaseId());
 | 
					    serverInstance->databaseHelper()->deleteClient(handle, musicBot->getClientDatabaseId());
 | 
				
			||||||
    serverInstance->databaseHelper()->deleteClient(nullptr, musicBot->getClientDatabaseId());
 | 
					    serverInstance->databaseHelper()->deleteClient(nullptr, musicBot->getClientDatabaseId());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -233,7 +233,7 @@ int MusicBotManager::sqlCreateMusicBot(int length, std::string* values, std::str
 | 
				
			|||||||
        musicBot->properties()[property::CLIENT_LASTCONNECTED] = duration_cast<seconds>(system_clock::now().time_since_epoch()).count();
 | 
					        musicBot->properties()[property::CLIENT_LASTCONNECTED] = duration_cast<seconds>(system_clock::now().time_since_epoch()).count();
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	handle->groups->enableCache(musicBot);
 | 
						handle->groups->enableCache(musicBot->getClientDatabaseId());
 | 
				
			||||||
    if(musicBot->getClientDatabaseId() != botId) logCritical("Invalid music bot id mapping!");
 | 
					    if(musicBot->getClientDatabaseId() != botId) logCritical("Invalid music bot id mapping!");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
 | 
				
			|||||||
@ -45,9 +45,9 @@ void QueryServer::unregisterConnection(const shared_ptr<QueryClient> &client) {
 | 
				
			|||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    if(client->server) {
 | 
					    if(client->server) {
 | 
				
			||||||
	    client->server->getGroupManager()->disableCache(client);
 | 
						    client->server->getGroupManager()->disableCache(client->getClientDatabaseId());
 | 
				
			||||||
    } else {
 | 
					    } else {
 | 
				
			||||||
    	serverInstance->getGroupManager()->disableCache(client);
 | 
					    	serverInstance->getGroupManager()->disableCache(client->getClientDatabaseId());
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    /* client->handle = nullptr; */
 | 
					    /* client->handle = nullptr; */
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										2
									
								
								shared
									
									
									
									
									
								
							
							
								
								
								
								
								
								
									
									
								
							
						
						
									
										2
									
								
								shared
									
									
									
									
									
								
							@ -1 +1 @@
 | 
				
			|||||||
Subproject commit 316afd9f5649448798f1e15fbd2cac171623aaac
 | 
					Subproject commit 4a9d8f132fe9ca3411e2e3c245922ad778c56aa0
 | 
				
			||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user