#include #include #include #include #include #include #include #include #include #include #include "weblist/WebListManager.h" #include "./client/web/WebClient.h" #include "./client/voice/VoiceClient.h" #include "./client/InternalClient.h" #include "./client/music/MusicClient.h" #include "music/MusicBotManager.h" #include "server/VoiceServer.h" #include "server/file/FileServer.h" #include "server/QueryServer.h" #include "InstanceHandler.h" #include "Configuration.h" #include "VirtualServer.h" #include "src/manager/ConversationManager.h" #include using namespace std; using namespace std::chrono; using namespace ts; using namespace ts::server; using namespace ts::protocol; using namespace ts::buffer; #define ECC_TYPE_INDEX 5 #ifndef BUILD_VERSION #define BUILD_VERSION "Unknown build" #endif extern ts::server::InstanceHandler* serverInstance; VirtualServer::VirtualServer(uint16_t serverId, sql::SqlManager* database) : serverId(serverId), sql(database) { memtrack::allocated(this); } bool VirtualServer::initialize(bool test_properties) { assert(self.lock()); this->_properties = serverInstance->databaseHelper()->loadServerProperties(self.lock()); this->_properties->registerNotifyHandler([&](Property& prop){ if(prop.type() == property::VIRTUALSERVER_DISABLE_IP_SAVING) { this->_disable_ip_saving = prop.as(); return; } if(prop.type() == property::VIRTUALSERVER_CODEC_ENCRYPTION_MODE) { this->_voice_encryption_mode = prop.as(); return; } std::string sql; if(prop.type() == property::VIRTUALSERVER_HOST) sql = "UPDATE `servers` SET `host` = :value WHERE `serverId` = :sid"; else if(prop.type() == property::VIRTUALSERVER_PORT) sql = "UPDATE `servers` SET `port` = :value WHERE `serverId` = :sid"; if(sql.empty()) return; sql::command(this->sql, sql, variable{":sid", this->getServerId()}, variable{":value", prop.value()}) .executeLater().waitAndGetLater(LOG_SQL_CMD, sql::result{1, "future failed"}); }); this->properties()[property::VIRTUALSERVER_PLATFORM] = config::server::DefaultServerPlatform; this->properties()[property::VIRTUALSERVER_VERSION] = config::server::DefaultServerVersion; this->properties()[property::VIRTUALSERVER_ID] = serverId; this->_disable_ip_saving = this->properties()[property::VIRTUALSERVER_DISABLE_IP_SAVING]; if(!properties()[property::VIRTUALSERVER_KEYPAIR].as().empty()){ debugMessage(this->serverId, "Importing server keypair"); this->_serverKey = new ecc_key; auto bytes = base64::decode(properties()[property::VIRTUALSERVER_KEYPAIR].as()); int err; if((err = ecc_import(reinterpret_cast(bytes.data()), bytes.length(), this->_serverKey)) != CRYPT_OK){ logError(this->getServerId(), "Cant import key. ({} => {})", err, error_to_string(err)); logError(this->serverId, "Could not import server keypair! {} ({}). Generating new one!", err, error_to_string(err)); delete this->_serverKey; this->_serverKey = nullptr; properties()[property::VIRTUALSERVER_KEYPAIR] = ""; } } int err; if(!_serverKey){ debugMessage(this->serverId, "Generating new server keypair"); this->_serverKey = new ecc_key; prng_state state{}; if((err = ecc_make_key_ex(&state, find_prng("sprng"), this->_serverKey, <c_ecc_sets[ECC_TYPE_INDEX])) != CRYPT_OK){ logError(this->serverId, "Could not generate a server keypair! {} ({})", err, error_to_string(err)); delete this->_serverKey; this->_serverKey = nullptr; return false; } size_t bytesBufferLength = 1024; char bytesBuffer[bytesBufferLength]; if((err = ecc_export(reinterpret_cast(bytesBuffer), &bytesBufferLength, PK_PRIVATE, this->_serverKey)) != CRYPT_OK){ logError(this->serverId, "Could not export the server keypair (private)! {} ({})", err, error_to_string(err)); delete this->_serverKey; this->_serverKey = nullptr; return false; } properties()[property::VIRTUALSERVER_KEYPAIR] = base64_encode(bytesBuffer, bytesBufferLength); this->properties()[property::VIRTUALSERVER_CREATED] = duration_cast(system_clock::now().time_since_epoch()).count(); } if(_serverKey){ size_t bufferLength = 265; char buffer[bufferLength]; if((err = ecc_export(reinterpret_cast(buffer), &bufferLength, PK_PUBLIC, this->_serverKey)) != CRYPT_OK) logError(this->serverId, "Could not generate server uid! (Could not export the server keypair (public)! {} ({}))", err, error_to_string(err)); properties()[property::VIRTUALSERVER_UNIQUE_IDENTIFIER] = base64::encode(digest::sha1(base64::encode(buffer, bufferLength))); } this->_conversation_manager = make_shared(this->ref()); this->_conversation_manager->initialize(this->_conversation_manager); channelTree = new ServerChannelTree(self.lock(), this->sql); channelTree->loadChannelsFromDatabase(); this->groups = new GroupManager(self.lock(), this->sql, serverInstance->getGroupManager()); if(!this->groups->loadGroupFormDatabase()){ //TODO exception etc logCritical(this->serverId, "Cant setup group manager!"); return false; } if(channelTree->channel_count() == 0){ logMessage(this->serverId, "Creating new channel tree (Copy from server 0)"); LOG_SQL_CMD(sql::command(this->getSql(), "INSERT INTO `channels` (`serverId`, `channelId`, `type`, `parentId`) SELECT :serverId AS `serverId`, `channelId`, `type`, `parentId` FROM `channels` WHERE `serverId` = 0", variable{":serverId", this->serverId}).execute()); LOG_SQL_CMD(sql::command(this->getSql(), "INSERT INTO `properties` (`serverId`, `type`, `id`, `key`, `value`) SELECT :serverId AS `serverId`, `type`, `id`, `key`, `value` FROM `properties` WHERE `serverId` = 0 AND `type` = :type", variable{":serverId", this->serverId}, variable{":type", property::PROP_TYPE_CHANNEL}).execute()); LOG_SQL_CMD(sql::command(this->getSql(), "INSERT INTO `permissions` (`serverId`, `type`, `id`, `channelId`, `permId`, `value`, `grant`, `flag_skip`, `flag_negate`) " "SELECT :serverId AS `serverId`, `type`, `id`, `channelId`, `permId`, `value`, `grant`, `flag_skip`, `flag_negate` FROM `permissions` WHERE `serverId` = 0 AND `type` = :type", variable{":serverId", this->serverId}, variable{":type", permission::SQL_PERM_CHANNEL}).execute()); channelTree->loadChannelsFromDatabase(); if(channelTree->channel_count() == 0){ logCritical(this->serverId, "Failed to setup channel tree!"); return 0; } if(!channelTree->getDefaultChannel()) { logError(this->serverId, "Missing default channel! Using first one!"); channelTree->setDefaultChannel(channelTree->channels().front()); } } if(!channelTree->getDefaultChannel()) channelTree->setDefaultChannel(*channelTree->channels().begin()); auto default_channel = channelTree->getDefaultChannel(); assert(default_channel); if(default_channel->properties()[property::CHANNEL_FLAG_PASSWORD].as()) default_channel->properties()[property::CHANNEL_FLAG_PASSWORD] = false; this->tokenManager = new token::TokenManager(this); this->tokenManager->loadTokens(); this->complains = new ComplainManager(this); if(!this->complains->loadComplains()) logError(this->serverId, "Could not load complains"); //Setup new server if needed if(this->groups->availableServerGroups(false).empty() || this->groups->availableChannelGroups(false).empty()){ if(!this->properties()[property::VIRTUALSERVER_AUTOGENERATED_PRIVILEGEKEY].as().empty()) { logCritical(this->getServerId(), "Missing default groups. Applying permission reset!"); } string token; if(!this->resetPermissions(token)) logCritical(this->serverId, "Failed to reset server permissions! This could be fatal!"); logMessageFmt(true, this->serverId, "---------------------- Token ----------------------"); logMessageFmt(true, this->serverId, "{:^51}", "The server's serveradmin token:"); logMessageFmt(true, this->serverId, "{:^51}", token); logMessageFmt(true, this->serverId, ""); logMessageFmt(true, this->serverId, "{:^51}", "Note: This token could be used just once!"); logMessageFmt(true, this->serverId, "---------------------- Token ----------------------"); } if(test_properties) this->ensureValidDefaultGroups(); letters = new letter::LetterManager(this); serverStatistics = make_shared(serverInstance->getStatistics(), true); this->serverRoot = std::make_shared(this->sql, self.lock(), this->properties()[property::VIRTUALSERVER_NAME].as(), false); static_pointer_cast(this->serverRoot)->setSharedLock(this->serverRoot); this->properties().registerNotifyHandler([&](Property& property) { if(property.type() == property::VIRTUALSERVER_NAME) static_pointer_cast(this->serverRoot)->properties()[property::CLIENT_NICKNAME] = property.as(); }); this->serverRoot->server = nullptr; this->serverAdmin = std::make_shared(this->sql, self.lock(), "serveradmin", true); static_pointer_cast(this->serverAdmin)->setSharedLock(this->serverAdmin); DatabaseHelper::assignDatabaseId(this->sql, this->serverId, this->serverAdmin); this->serverAdmin->server = nullptr; this->registerInternalClient(this->serverAdmin); /* lets assign server id 0 */ if(serverInstance->getFileServer()) serverInstance->getFileServer()->setupServer(self.lock()); this->channelTree->printChannelTree([&](std::string msg){ debugMessage(this->serverId, msg); }); this->musicManager = make_shared(self.lock()); this->musicManager->_self = this->musicManager; this->musicManager->load_playlists(); this->musicManager->load_bots(); if(this->properties()[property::VIRTUALSERVER_ICON_ID] != (IconId) 0) if(!serverInstance->getFileServer()->iconExists(self.lock(), this->properties()[property::VIRTUALSERVER_ICON_ID])) { debugMessage(this->getServerId(), "Removing invalid icon id of server"); this->properties()[property::VIRTUALSERVER_ICON_ID] = 0; } for(const auto& type : vector{ property::VIRTUALSERVER_DOWNLOAD_QUOTA, property::VIRTUALSERVER_UPLOAD_QUOTA, property::VIRTUALSERVER_MAX_UPLOAD_TOTAL_BANDWIDTH, property::VIRTUALSERVER_MAX_DOWNLOAD_TOTAL_BANDWIDTH, }) { auto info = property::impl::info(type); auto prop = this->properties()[type]; if(prop.default_value() == prop.value()) continue; if(!info->validate_input(this->properties()[type].value())) { this->properties()[type] = info->default_value; logMessage(this->getServerId(), "Server property " + info->name + " contains an invalid value! Resetting it."); } } if(this->properties()[property::VIRTUALSERVER_FILEBASE].value().empty()) this->properties()[property::VIRTUALSERVER_FILEBASE] = serverInstance->getFileServer()->server_file_base(self.lock()); /* lets cleanup the conversations for not existent channels */ this->_conversation_manager->synchronize_channels(); return true; } VirtualServer::~VirtualServer() { memtrack::freed(this); delete this->tokenManager; delete this->groups; delete this->channelTree; delete this->letters; delete this->complains; this->_conversation_manager.reset(); if(this->_serverKey) ecc_free(this->_serverKey); delete this->_serverKey; } inline bool evaluateAddress4(const string &input, in_addr &address) { if(input == "0.0.0.0") { address.s_addr = INADDR_ANY; return true; }; auto record = gethostbyname(input.c_str()); if(!record) return false; address.s_addr = ((in_addr*) record->h_addr)->s_addr; return true; } inline bool evaluateAddress6(const string &input, in6_addr &address) { if(input == "::") { address = IN6ADDR_ANY_INIT; return true; }; auto record = gethostbyname2(input.c_str(), AF_INET6); if(!record) return false; address = *(in6_addr*) record->h_addr; return true; } inline string strip(std::string message) { while(!message.empty()) { if(message[0] == ' ') message = message.substr(1); else if(message[message.length() - 1] == ' ') message = message.substr(0, message.length() - 1); else break; } return message; } inline vector split_hosts(const std::string& message, char delimiter) { vector result; size_t found, index = 0; do { found = message.find(delimiter, index); result.push_back(strip(message.substr(index, found - index))); index = found + 1; } while(index != 0); return result; } bool VirtualServer::start(std::string& error) { { threads::Mutex lock(this->stateLock); if(this->state != ServerState::OFFLINE){ error = "Server isn't offline"; return false; } this->state = ServerState::BOOTING; } this->serverRoot->server = self.lock(); this->serverAdmin->server = self.lock(); { //Client delete after server stop/start lock_guard lock(this->clients.lock); for(auto& client : this->clients.clients) { if(!client) continue; if(client->getType() == ClientType::CLIENT_WEB || client->getType() == ClientType::CLIENT_TEAMSPEAK) { client.reset(); } } } auto host = this->properties()[property::VIRTUALSERVER_HOST].as(); if(config::binding::enforce_default_voice_host) host = config::binding::DefaultVoiceHost; if(host.empty()){ error = "invalid host (\"" + host + "\")"; this->stop("failed to start"); return false; } if(this->properties()[property::VIRTUALSERVER_PORT].as() <= 0){ error = "invalid port"; this->stop("failed to start"); return false; } deque> bindings; for(const auto& address : split_hosts(host, ',')) { auto entry = make_shared(); if(net::is_ipv4(address)) { sockaddr_in addr{}; memset(&addr, 0, sizeof(addr)); addr.sin_family = AF_INET; addr.sin_port = htons(this->properties()[property::VIRTUALSERVER_PORT].as()); if(!evaluateAddress4(address, addr.sin_addr)) { logError(this->serverId, "Fail to resolve v4 address info for \"{}\"", address); continue; } memcpy(&entry->address, &addr, sizeof(addr)); } else if(net::is_ipv6(address)) { sockaddr_in6 addr{}; memset(&addr, 0, sizeof(addr)); addr.sin6_family = AF_INET6; addr.sin6_port = htons(this->properties()[property::VIRTUALSERVER_PORT].as()); if(!evaluateAddress6(address, addr.sin6_addr)) { logError(this->serverId, "Fail to resolve v6 address info for \"{}\"", address); continue; } memcpy(&entry->address, &addr, sizeof(addr)); } else { logError(this->serverId, "Failed to determinate address type for \"{}\"", address); continue; } bindings.push_back(entry); } if(bindings.empty()) { error = "failed to resole any host!"; this->stop("failed to start"); return false; } //Setup voice server udpVoiceServer = make_shared(self.lock()); if(!udpVoiceServer->start(bindings, error)) { error = "could not start voice server. Message: " + error; this->stop("failed to start"); return false; } if(ts::config::web::activated && serverInstance->sslManager()->web_ssl_options()) { string web_host_string = this->properties()[property::VIRTUALSERVER_WEB_HOST]; if(web_host_string.empty()) web_host_string = this->properties()[property::VIRTUALSERVER_HOST].as(); auto web_port = this->properties()[property::VIRTUALSERVER_WEB_PORT].as(); if(web_port == 0) web_port = this->properties()[property::VIRTUALSERVER_PORT].as(); startTimestamp = std::chrono::system_clock::now(); #ifdef COMPILE_WEB_CLIENT webControlServer = new WebControlServer(self.lock()); auto web_bindings = net::resolve_bindings(web_host_string, web_port); deque> bindings; for(auto& binding : web_bindings) { if(!get<2>(binding).empty()) { logError(this->serverId, "[Web] Failed to resolve binding for {}: {}", get<0>(binding), get<2>(binding)); continue; } auto entry = make_shared(); memcpy(&entry->address, &get<1>(binding), sizeof(sockaddr_storage)); entry->file_descriptor = -1; entry->event_accept = nullptr; bindings.push_back(entry); } logMessage(this->serverId, "[Web] Starting server on {}:{}", web_host_string, web_port); if(!webControlServer->start(bindings, error)) { error = "could not start web server. Message: " + error; this->stop("failed to start"); return false; } #endif } //Startup ticking serverInstance->executeTick(this); if(this->properties()[property::VIRTUALSERVER_WEBLIST_ENABLED].as()) serverInstance->getWebList()->enable_report(this->self.lock()); properties()[property::VIRTUALSERVER_CLIENTS_ONLINE] = 0; properties()[property::VIRTUALSERVER_QUERYCLIENTS_ONLINE] = 0; properties()[property::VIRTUALSERVER_CHANNELS_ONLINE] = 0; properties()[property::VIRTUALSERVER_UPTIME] = 0; this->startTimestamp = system_clock::now(); this->musicManager->cleanup_semi_bots(); this->musicManager->connectBots(); { threads::MutexLock lock(this->stateLock); this->state = ServerState::ONLINE; } return true; } std::string VirtualServer::publicServerKey() { size_t keyBufferLength = 265; char keyBuffer[keyBufferLength]; if(ecc_export((unsigned char *) keyBuffer, &keyBufferLength, PK_PUBLIC, this->_serverKey) != CRYPT_OK) return ""; return base64::encode(string(keyBuffer, keyBufferLength)); } bool VirtualServer::running() { return this->state == ServerState::BOOTING || this->state == ServerState::ONLINE; } void VirtualServer::preStop(const std::string& reason) { { threads::MutexLock lock(this->stateLock); if(!this->running() && this->state != ServerState::SUSPENDING) return; this->state = ServerState::SUSPENDING; } for(const auto& cl : this->getClients()) { unique_lock channel_lock(cl->channel_lock); if (cl->currentChannel) { auto vc = dynamic_pointer_cast(cl); if(vc) { vc->disconnect(VREASON_SERVER_SHUTDOWN, reason, nullptr, false); } else { cl->notifyClientLeftView(cl, nullptr, ViewReasonId::VREASON_SERVER_SHUTDOWN, reason, nullptr, false); } } cl->visibleClients.clear(); cl->mutedClients.clear(); } } void VirtualServer::stop(const std::string& reason) { auto self_lock = this->self.lock(); assert(self_lock); { threads::MutexLock lock(this->stateLock); if(!this->running() && this->state != ServerState::SUSPENDING) return; this->state = ServerState::SUSPENDING; } this->preStop(reason); for(const auto& cl : this->getClients()) { //start disconnecting if(cl->getType() == CLIENT_TEAMSPEAK || cl->getType() == CLIENT_TEASPEAK || cl->getType() == CLIENT_WEB) { cl->close_connection(chrono::system_clock::now() + chrono::seconds(1)); } else if(cl->getType() == CLIENT_QUERY){ threads::MutexLock lock(cl->command_lock); cl->currentChannel = nullptr; continue; //We dont need to disconnect the query cl->server = nullptr; cl->loadDataForCurrentServer(); } else if(cl->getType() == CLIENT_MUSIC) { cl->disconnect(""); cl->currentChannel = nullptr; } else if(cl->getType() == CLIENT_INTERNAL) { } else { logError(this->serverId, "Got client with unknown type: " + to_string(cl->getType())); } } this->musicManager->disconnectBots(); serverInstance->cancelExecute(this); if(this->udpVoiceServer) this->udpVoiceServer->stop(); this->udpVoiceServer = nullptr; #ifdef COMPILE_WEB_CLIENT if(this->webControlServer) this->webControlServer->stop(); delete this->webControlServer; this->webControlServer = nullptr; #endif { auto list = serverInstance->getWebList(); if(list) list->disable_report(self_lock); } if(this->groups) { this->groups->clearCache(); } properties()[property::VIRTUALSERVER_CLIENTS_ONLINE] = 0; properties()[property::VIRTUALSERVER_QUERYCLIENTS_ONLINE] = 0; properties()[property::VIRTUALSERVER_CHANNELS_ONLINE] = 0; properties()[property::VIRTUALSERVER_UPTIME] = 0; { threads::MutexLock lock(this->stateLock); this->state = ServerState::OFFLINE; } this->serverRoot->server = nullptr; this->serverAdmin->server = nullptr; } size_t VirtualServer::onlineClients() { size_t result = 0; lock_guard lock(this->clients.lock); for(const auto &cl : this->clients.clients) { if(!cl) continue; if(cl->getType() == CLIENT_TEAMSPEAK || cl->getType() == CLIENT_QUERY) result++; } return result; } OnlineClientReport VirtualServer::onlineStats() { OnlineClientReport response{}; { lock_guard lock(this->clients.lock); for(const auto &cl : this->clients.clients) { if(!cl) continue; switch (cl->getType()) { case CLIENT_TEAMSPEAK: response.clients_ts++; break; case CLIENT_WEB: response.clients_web++; break; case CLIENT_QUERY: response.queries++; break; case CLIENT_MUSIC: response.bots++; break; default: break; } } } return response; } std::shared_ptr VirtualServer::find_client_by_id(uint16_t client_id) { lock_guard lock(this->clients.lock); if(this->clients.clients.size() > client_id) return this->clients.clients[client_id]; else return nullptr; } deque> VirtualServer::findClientsByCldbId(uint64_t cldbId) { deque> result; lock_guard lock(this->clients.lock); for(const auto &client : this->clients.clients) { if(!client) continue; if(client->getClientDatabaseId() == cldbId) result.push_back(client); } return result; } deque> VirtualServer::findClientsByUid(std::string uid) { lock_guard lock(this->clients.lock); deque> result; for(const auto &client : this->clients.clients) { if(!client) continue; if(client->getUid() == uid) { result.push_back(client); } } return result; } std::shared_ptr VirtualServer::findClient(std::string name, bool ignoreCase) { if(ignoreCase) { std::transform(name.begin(), name.end(), name.begin(), ::tolower); } { lock_guard lock(this->clients.lock); for(const auto& client : this->clients.clients) { if(!client) continue; string clName = client->getDisplayName(); if(ignoreCase) { std::transform(clName.begin(), clName.end(), clName.begin(), ::tolower); } if(clName == name) return client; } } return nullptr; } bool VirtualServer::forEachClient(std::function)> function) { for(const auto& elm : this->getClients()) { shared_lock close_lock(elm->finalDisconnectLock, try_to_lock_t{}); if(close_lock.owns_lock()) //If not locked than client is on the way to disconnect if(elm->state == ConnectionState::CONNECTED && elm->getType() != ClientType::CLIENT_INTERNAL) { function(elm); } } return true; } std::vector> VirtualServer::getClients() { vector> clients; { lock_guard lock(this->clients.lock); clients.reserve(this->clients.count); for(auto& client : this->clients.clients) { if(!client) continue; clients.push_back(client); } } return clients; } deque> VirtualServer::getClientsByChannel(std::shared_ptr channel) { assert(this); auto s_channel = dynamic_pointer_cast(channel); if(!s_channel) return {}; /* what had we done wrong here... :D */ shared_lock client_lock(s_channel->client_lock); auto weak_clients = s_channel->clients; client_lock.unlock(); std::deque> result; for(const auto& weak_client : weak_clients) { auto client = weak_client.lock(); if(!client) continue; if(client->connectionState() != ConnectionState::CONNECTED) continue; if(client->getChannel() != channel) continue; /* to be sure */ result.push_back(move(client)); } return result; } deque> VirtualServer::getClientsByChannelRoot(const std::shared_ptr &root, bool lock) { assert(this); shared_lock channel_lock(this->channel_tree_lock, defer_lock); if(lock) channel_lock.lock(); std::deque> result; for(const auto& channel : this->channelTree->channels(root)) { auto clients = this->getClientsByChannel(channel); result.insert(result.end(), clients.begin(), clients.end()); } return result; } bool VirtualServer::notifyServerEdited(std::shared_ptr invoker, deque keys) { if(!invoker) return false; Command cmd("notifyserveredited"); cmd["invokerid"] = invoker->getClientId(); cmd["invokername"] = invoker->getDisplayName(); cmd["invokeruid"] = invoker->getUid(); cmd["reasonid"] = ViewReasonId::VREASON_EDITED; for(const auto& key : keys) { auto info = property::impl::info(key); if(*info == property::VIRTUALSERVER_UNDEFINED) { logError(this->getServerId(), "Tried to broadcast a server update with an unknown info: " + key); continue; } cmd[key] = properties()[info].as(); } this->forEachClient([&cmd](shared_ptr client){ client->sendCommand(cmd); }); return true; } bool VirtualServer::notifyClientPropertyUpdates(std::shared_ptr client, const deque>& keys, bool selfNotify) { if(keys.empty()) return false; this->forEachClient([&](const shared_ptr& cl) { shared_lock client_channel_lock(client->channel_lock); if(cl->isClientVisible(client, false) || (cl == client && selfNotify)) cl->notifyClientUpdated(client, keys, false); }); return true; } void VirtualServer::broadcastMessage(std::shared_ptr invoker, std::string message) { if(!invoker) { logCritical(this->serverId, "Tried to broadcast with an invalid invoker!"); return; } this->forEachClient([&](shared_ptr cl){ cl->notifyTextMessage(ChatMessageMode::TEXTMODE_SERVER, invoker, 0, 0, system_clock::now(), message); }); } std::vector> CalculateCache::getGroupAssignments(VirtualServer* server, ClientDbId cldbid, ClientType type) { if(assignment_server_groups_set) return assignment_server_groups; assignment_server_groups = server->getGroupManager()->getServerGroups(cldbid, type); assignment_server_groups_set = true; return assignment_server_groups; } std::shared_ptr CalculateCache::getChannelAssignment(VirtualServer* server, ClientDbId client_dbid, ChannelId channel_id) { if(this->assignment_channel_group_set && this->assignment_channel_group_channel == channel_id) return this->assignment_channel_group; auto channel = this->getServerChannel(server, channel_id); assignment_channel_group = channel ? server->getGroupManager()->getChannelGroup(client_dbid, channel, true) : nullptr; assignment_channel_group_set = true; assignment_channel_group_channel = channel_id; return assignment_channel_group; } std::shared_ptr CalculateCache::getServerChannel(ts::server::VirtualServer *server, ts::ChannelId channel_id) { if(this->last_server_channel == channel_id) return this->server_channel; this->last_server_channel = channel_id; this->server_channel = server && channel_id > 0 ? server->getChannelTree()->findChannel(channel_id) : nullptr; return this->server_channel; } ts_always_inline bool channel_ignore_permission(ts::permission::PermissionType type) { return permission::i_icon_id == type; } vector> VirtualServer::calculate_permissions( const std::deque& permissions, ClientDbId client_dbid, ClientType client_type, ChannelId channel_id, bool calculate_granted, std::shared_ptr cache) { if(permissions.empty()) return {}; vector> result; result.reserve(permissions.size()); if(!cache) { cache = make_shared(); } if(!cache->client_permissions) { cache->client_permissions = serverInstance->databaseHelper()->loadClientPermissionManager(self.lock(), client_dbid); } bool have_skip_permission = false; int skip_permission_type = -1; /* -1 := unset | 0 := skip, not explicit | 1 := skip, explicit */ bool have_skip; /* * server_group_data[0] := Server group id * server_group_data[1] := Skip flag * server_group_data[2] := Negate flag * server_group_data[3] := Permission value */ typedef std::tuple GroupData; bool server_group_data_initialized = false; vector server_group_data; GroupData* active_server_group; /* function to calculate skip permission */ auto calculate_skip = [&]{ skip_permission_type = 0; /* test for skip permission within the client permission manager */ { auto skip_value = cache->client_permissions->permission_value_flagged(permission::b_client_skip_channelgroup_permissions); if(skip_value.has_value) { have_skip_permission = skip_value.value == 1; skip_permission_type = 1; logTrace(this->serverId, "[Permission] Found skip permission in client permissions. Value: {}", have_skip_permission); } } /* test for skip permission within all server groups */ if(skip_permission_type != 1) { for(const auto& assignment : cache->getGroupAssignments(this, client_dbid, client_type)) { auto group_permissions = assignment->group->permissions(); auto flagged_value = group_permissions->permission_value_flagged(permission::b_client_skip_channelgroup_permissions); if(flagged_value.has_value) { have_skip_permission |= flagged_value.value == 1; if(have_skip_permission) { logTrace(this->serverId, "[Permission] Found skip permission in client server group. Group: {} ({}), Value: {}", assignment->group->groupId(), assignment->group->name(), have_skip_permission); break; } } } } }; auto initialize_group_data = [&](const permission::PermissionType& permission_type) { server_group_data_initialized = true; active_server_group = nullptr; auto groups = cache->getGroupAssignments(this, client_dbid, client_type); server_group_data.resize(groups.size()); auto it = server_group_data.begin(); for(auto& group : groups) { auto group_permissions = group->group->permissions(); auto permission_flags = group_permissions->permission_flags(permission_type); auto flag_set = calculate_granted ? permission_flags.grant_set : permission_flags.value_set; if(!flag_set) continue; //TODO: Test if there is may a group channel permissions auto value = group_permissions->permission_values(permission_type); *it = std::make_tuple(group->group->groupId(), (bool) permission_flags.skip, (bool) permission_flags.negate, calculate_granted ? value.grant : value.value); it++; } if(it == server_group_data.begin()) return; /* no server group has that permission */ server_group_data.erase(it, server_group_data.end()); /* remove unneeded */ auto found_negate = false; for(auto& group : server_group_data) { if(std::get<2>(group)) { found_negate = true; break; } } if(found_negate) { server_group_data.erase(remove_if(server_group_data.begin(), server_group_data.end(), [](auto data) { return !std::get<2>(data); }), server_group_data.end()); logTrace(this->serverId, "[Permission] Found negate flag within server groups. Groups left: {}", server_group_data.size()); if(server_group_data.empty()) logTrace(this->serverId, "[Permission] After non negated groups have been kicked out the negated groups are empty! This should not happen! Permission: {}, Client ID: {}", permission_type, client_dbid); permission::PermissionValue current_lowest = 0; for(auto& group : server_group_data) { if(!active_server_group || (std::get<3>(group) < current_lowest && std::get<3>(group) != -1)) { current_lowest = std::get<3>(group); active_server_group = &group; } } } else { permission::PermissionValue current_highest = 0; for(auto& group : server_group_data) { if(!active_server_group || (std::get<3>(group) > current_highest || std::get<3>(group) == -1)) { current_highest = std::get<3>(group); active_server_group = &group; } } } }; for(const auto& permission : permissions) { if(permission == permission::b_client_skip_channelgroup_permissions) { if(skip_permission_type == -1) /* initialize skip flag */ calculate_skip(); result.push_back({permission, {have_skip_permission, skip_permission_type == 1}}); continue; } server_group_data_initialized = false; /* reset all group data */ auto client_permission_flags = cache->client_permissions->permission_flags(permission); /* lets try to resolve the channel specific permission */ if(channel_id > 0 && client_permission_flags.channel_specific) { auto data = cache->client_permissions->channel_permission(permission, channel_id); if(calculate_granted ? data.flags.grant_set : data.flags.value_set) { result.push_back({permission, {calculate_granted ? data.values.grant : data.values.value, true}}); logTrace(this->serverId, "[Permission] Calculation for client {} of permission {} returned {} (Client channel permission)", client_dbid, permission::resolvePermissionData(permission)->name, data.values.value); continue; } } have_skip = channel_id == 0; if(!have_skip) { /* look if somewhere is the skip permission flag set */ if(skip_permission_type == -1) /* initialize skip flag */ calculate_skip(); have_skip = have_skip_permission; } if(!have_skip) { /* okey we've no global skip. Then now lookup the groups and the client permissions */ if(calculate_granted ? client_permission_flags.grant_set : client_permission_flags.value_set) { /* okey the client has the permission, this counts */ have_skip = client_permission_flags.skip; } else { if(!server_group_data_initialized) initialize_group_data(permission); if(active_server_group) have_skip = std::get<1>(*active_server_group); } } if(!have_skip) { /* lookup the channel group */ { auto channel_assignment = cache->getChannelAssignment(this, client_dbid, channel_id); if(channel_assignment) { auto group_permissions = channel_assignment->group->permissions(); auto permission_flags = group_permissions->permission_flags(permission); auto flag_set = calculate_granted ? permission_flags.grant_set : permission_flags.value_set; if(flag_set) { auto value = group_permissions->permission_values(permission); result.push_back({permission, {calculate_granted ? value.grant : value.value, true}}); logTrace(this->serverId, "[Permission] Calculation for client {} of permission {} returned {} (Channel group permission)", client_dbid, permission::resolvePermissionData(permission)->name, calculate_granted ? value.grant : value.value); continue; } } } /* lookup the channel permissions. Whyever? */ { auto channel = cache->getServerChannel(this, channel_id); if(channel) { auto channel_permissions = channel->permissions(); auto data = calculate_granted ? channel_permissions->permission_granted_flagged(permission) : channel_permissions->permission_value_flagged(permission); if(data.has_value) { result.push_back({permission, {data.value, true}}); logTrace(this->serverId, "[Permission] Calculation for client {} of permission {} returned {} (Channel permission)", client_dbid, permission::resolvePermissionData(permission)->name, data.value); continue; } } } } if(calculate_granted ? client_permission_flags.grant_set : client_permission_flags.value_set) { auto client_value = cache->client_permissions->permission_values(permission); result.push_back({permission, {calculate_granted ? client_value.grant : client_value.value, true}}); logTrace(this->serverId, "[Permission] Calculation for client {} of permission {} returned {} (Client permission)", client_dbid, permission::resolvePermissionData(permission)->name, client_value.value); continue; } if(!server_group_data_initialized) initialize_group_data(permission); if(active_server_group) { result.push_back({permission, {get<3>(*active_server_group), true}}); logTrace(this->serverId, "[Permission] Calculation for client {} of permission {} returned {} (Server group permission of group {})", client_dbid, permission::resolvePermissionData(permission)->name, get<3>(*active_server_group), get<0>(*active_server_group)); continue; } logTrace(this->serverId, "[Permission] Calculation for client {} of permission {} returned in no permission.", client_dbid, permission::resolvePermissionData(permission)->name); result.push_back({permission, {permNotGranted, false}}); } return result; } permission::v2::PermissionFlaggedValue VirtualServer::calculate_permission( permission::PermissionType permission, ClientDbId cldbid, ClientType type, ChannelId channel, bool granted, std::shared_ptr cache) { auto result = this->calculate_permissions({permission}, cldbid, type, channel, granted, cache); if(result.empty()) return {0, false}; return result.front().second; } bool VirtualServer::verifyServerPassword(std::string password, bool hashed) { if(!this->properties()[property::VIRTUALSERVER_FLAG_PASSWORD].as()) return true; if(password.empty()) return false; if(!hashed){ char buffer[SHA_DIGEST_LENGTH]; SHA1(reinterpret_cast(password.data()), password.length(), reinterpret_cast(buffer)); password = base64_encode(string(buffer, SHA_DIGEST_LENGTH)); } return password == this->properties()[property::VIRTUALSERVER_PASSWORD].as(); } float VirtualServer::averagePacketLoss() { //TODO Average packet loss return 0.f; } float VirtualServer::averagePing() { float count = 0; float sum = 0; this->forEachClient([&count, &sum](shared_ptr client) { auto type = client->getType(); if(type == ClientType::CLIENT_TEAMSPEAK || type == ClientType::CLIENT_TEASPEAK) { count++; sum += duration_cast(dynamic_pointer_cast(client)->calculatePing()).count(); } #ifdef COMPILE_WEB_CLIENT else if(type == ClientType::CLIENT_WEB) { count++; sum += duration_cast(dynamic_pointer_cast(client)->client_ping()).count(); } #endif }); if(count == 0) return 0; return sum / count; } bool VirtualServer::resetPermissions(std::string& token) { LOG_SQL_CMD(sql::command(this->sql, "DELETE FROM `permissions` WHERE `serverId` = :serverId", variable{":serverId", this->serverId}).execute()); LOG_SQL_CMD(sql::command(this->sql, "DELETE FROM `assignedGroups` WHERE `serverId` = :serverId", variable{":serverId", this->serverId}).execute()); LOG_SQL_CMD(sql::command(this->sql, "DELETE FROM `groups` WHERE `serverId` = :serverId", variable{":serverId", this->serverId}).execute()); { threads::MutexLock lock(this->getGroupManager()->cacheLock); this->getGroupManager()->deleteAllGroups(); deque> saved_groups; for(const auto& group : serverInstance->getGroupManager()->availableGroups(false)){ if(group->type() != GroupType::GROUP_TYPE_TEMPLATE) continue; debugMessage(this->serverId, "Copy default group {{Id: {}, Type: {}, Target: {}, Name: {}}} to server", group->groupId(), group->type(), group->target(), group->name()); this->getGroupManager()->copyGroup(group, GroupType::GROUP_TYPE_NORMAL, group->name(), this->serverId); } } //Server admin auto default_server_admin = serverInstance->getGroupManager()->findGroup(serverInstance->properties()[property::SERVERINSTANCE_TEMPLATE_SERVERADMIN_GROUP].as()); auto default_server_music = serverInstance->getGroupManager()->findGroup(serverInstance->properties()[property::SERVERINSTANCE_TEMPLATE_MUSICDEFAULT_GROUP].as()); auto default_server_guest = serverInstance->getGroupManager()->findGroup(serverInstance->properties()[property::SERVERINSTANCE_TEMPLATE_SERVERDEFAULT_GROUP].as()); auto default_channel_admin = serverInstance->getGroupManager()->findGroup(serverInstance->properties()[property::SERVERINSTANCE_TEMPLATE_CHANNELADMIN_GROUP].as()); auto default_channel_guest = serverInstance->getGroupManager()->findGroup(serverInstance->properties()[property::SERVERINSTANCE_TEMPLATE_CHANNELDEFAULT_GROUP].as()); if(!default_server_guest) { logCritical(0, "Missing default server guest template group!"); assert(!serverInstance->getGroupManager()->availableChannelGroups().empty()); default_server_guest = serverInstance->getGroupManager()->availableServerGroups().front(); logCritical(0, "Using group {} as default server guest group for server {}.", default_server_guest->name(), this->serverId); } if(!default_channel_admin) { logCritical(0, "Missing default channel guest template group!"); assert(!serverInstance->getGroupManager()->availableChannelGroups().empty()); default_channel_admin = serverInstance->getGroupManager()->availableChannelGroups().front(); logCritical(0, "Using group {} as channel server guest group for server {}.", default_channel_admin->name(), this->serverId); } if(!default_server_music) { logCritical(0, "Missing default channel guest template group!"); assert(!serverInstance->getGroupManager()->availableChannelGroups().empty()); default_server_music = serverInstance->getGroupManager()->availableChannelGroups().front(); logCritical(0, "Using group {} as channel server guest group for server {}.", default_server_music->name(), this->serverId); } if(!default_server_admin) { logCritical(0, "Missing default server admin template group! Using default guest group ({})", default_server_guest->name()); default_server_admin = default_server_guest; } if(!default_channel_admin) { logCritical(0, "Missing default channel admin template group! Using default guest group ({})", default_channel_guest->name()); default_channel_admin = default_channel_guest; } this->properties()[property::VIRTUALSERVER_DEFAULT_SERVER_GROUP] = this->getGroupManager()->findGroup(GroupTarget::GROUPTARGET_SERVER, default_server_guest->name()).front()->groupId(); this->properties()[property::VIRTUALSERVER_DEFAULT_MUSIC_GROUP] = this->getGroupManager()->findGroup(GroupTarget::GROUPTARGET_SERVER, default_server_music->name()).front()->groupId(); this->properties()[property::VIRTUALSERVER_DEFAULT_CHANNEL_ADMIN_GROUP] = this->getGroupManager()->findGroup(GroupTarget::GROUPTARGET_CHANNEL, default_channel_admin->name()).front()->groupId(); this->properties()[property::VIRTUALSERVER_DEFAULT_CHANNEL_GROUP] = this->getGroupManager()->findGroup(GroupTarget::GROUPTARGET_CHANNEL, default_channel_guest->name()).front()->groupId(); auto token_admin = this->getGroupManager()->findGroup(GroupTarget::GROUPTARGET_SERVER, default_server_admin->name()).front()->groupId(); auto created = this->tokenManager->createToken(token::TOKEN_SERVER, token_admin, "Default server token for the server admin."); if(!created) { logCritical(this->serverId, "Failed to generate default serveradmin token!"); } else { token = created->token; this->properties()[property::VIRTUALSERVER_AUTOGENERATED_PRIVILEGEKEY] = token; this->properties()[property::VIRTUALSERVER_ASK_FOR_PRIVILEGEKEY] = true; } if(this->properties()[property::VIRTUALSERVER_ASK_FOR_PRIVILEGEKEY].as()) { auto requested_token = this->tokenManager->findToken(this->properties()[property::VIRTUALSERVER_AUTOGENERATED_PRIVILEGEKEY]); if(!requested_token) { logError(this->serverId, "Failed to resolve default token! Don't ask for privilege key anymore."); this->properties()[property::VIRTUALSERVER_ASK_FOR_PRIVILEGEKEY] = false; this->properties()[property::VIRTUALSERVER_AUTOGENERATED_PRIVILEGEKEY] = ""; } } this->ensureValidDefaultGroups(); for(const auto& client : this->getClients()) { if(client->getType() != ClientType::CLIENT_QUERY) { client->notifyServerGroupList(); client->notifyChannelGroupList(); } if(this->notifyClientPropertyUpdates(client, this->getGroupManager()->update_server_group_property(client, true, client->getChannel()))) { if(client->update_cached_permissions()) /* update cached calculated permissions */ client->sendNeededPermissions(false); /* cached permissions had changed, notify the client */ } client->updateChannelClientProperties(true, true); } return true; } void VirtualServer::ensureValidDefaultGroups() { auto default_server_group = this->getGroupManager()->defaultGroup(GROUPTARGET_SERVER, true); if(!default_server_group) { logError(this->serverId, "Missing server's default server group! (Id: {})", this->properties()[property::VIRTUALSERVER_DEFAULT_SERVER_GROUP].value()); default_server_group = this->getGroupManager()->availableServerGroups(false).front(); logError(this->serverId, "Using {} ({}) instead!", default_server_group->groupId(), default_server_group->name()); this->properties()[property::VIRTUALSERVER_DEFAULT_SERVER_GROUP] = default_server_group->groupId(); } auto default_music_group = this->getGroupManager()->defaultGroup(GROUPTARGET_SERVER, true); if(!default_music_group) { logError(this->serverId, "Missing server's default music group! (Id: {})", this->properties()[property::VIRTUALSERVER_DEFAULT_MUSIC_GROUP].value()); default_music_group = default_server_group; logError(this->serverId, "Using {} ({}) instead!", default_music_group->groupId(), default_music_group->name()); this->properties()[property::VIRTUALSERVER_DEFAULT_MUSIC_GROUP] = default_music_group->groupId(); } auto default_channel_group = this->getGroupManager()->defaultGroup(GROUPTARGET_CHANNEL, true); if(!default_channel_group) { logError(this->serverId, "Missing server's default channel group! (Id: {})", this->properties()[property::VIRTUALSERVER_DEFAULT_CHANNEL_GROUP].value()); default_channel_group = this->getGroupManager()->availableChannelGroups(false).front(); logError(this->serverId, "Using {} ({}) instead!", default_channel_group->groupId(), default_channel_group->name()); this->properties()[property::VIRTUALSERVER_DEFAULT_CHANNEL_GROUP] = default_channel_group->groupId(); } auto admin_channel_group = this->getGroupManager()->findGroupLocal(this->properties()[property::VIRTUALSERVER_DEFAULT_CHANNEL_ADMIN_GROUP].as_save()); if(!admin_channel_group) { logError(this->serverId, "Missing server's default channel admin group! (Id: {})", this->properties()[property::VIRTUALSERVER_DEFAULT_CHANNEL_ADMIN_GROUP].value()); admin_channel_group = this->getGroupManager()->availableChannelGroups(false).front(); logError(this->serverId, "Using {} ({}) instead!", admin_channel_group->groupId(), admin_channel_group->name()); this->properties()[property::VIRTUALSERVER_DEFAULT_CHANNEL_ADMIN_GROUP] = admin_channel_group->groupId(); } }