diff --git a/server/CMakeLists.txt b/server/CMakeLists.txt index 366ff6b..5bfd666 100644 --- a/server/CMakeLists.txt +++ b/server/CMakeLists.txt @@ -37,272 +37,272 @@ set(COMPILE_WEB_CLIENT "ON") set(CMAKE_VERBOSE_MAKEFILE ON) set(SERVER_SOURCE_FILES - main.cpp - tomcryptTest.cpp - MySQLLibSSLFix.c + main.cpp + tomcryptTest.cpp + MySQLLibSSLFix.c - src/client/ConnectedClient.cpp - src/client/voice/PrecomputedPuzzles.cpp - src/client/voice/VoiceClient.cpp - src/client/voice/VoiceClientHandschake.cpp - src/client/voice/VoiceClientCommandHandler.cpp - src/client/voice/VoiceClientPacketHandler.cpp - src/client/voice/VoiceClientView.cpp - src/TS3ServerClientManager.cpp - src/TSServer.cpp - src/TS3ServerHeartbeat.cpp - src/SignalHandler.cpp - src/server/VoiceServer.cpp - src/server/POWHandler.cpp - src/client/voice/VoiceClientConnection.cpp - src/client/ConnectedClientCommandHandler.cpp - src/client/ConnectedClientNotifyHandler.cpp - src/ServerManager.cpp - src/server/file/FileServer.cpp + src/client/ConnectedClient.cpp + src/client/voice/PrecomputedPuzzles.cpp + src/client/voice/VoiceClient.cpp + src/client/voice/VoiceClientHandschake.cpp + src/client/voice/VoiceClientCommandHandler.cpp + src/client/voice/VoiceClientPacketHandler.cpp + src/client/voice/VoiceClientView.cpp + src/TS3ServerClientManager.cpp + src/TSServer.cpp + src/TS3ServerHeartbeat.cpp + src/SignalHandler.cpp + src/server/VoiceServer.cpp + src/server/POWHandler.cpp + src/client/voice/VoiceClientConnection.cpp + src/client/ConnectedClientCommandHandler.cpp + src/client/ConnectedClientNotifyHandler.cpp + src/ServerManager.cpp + src/server/file/FileServer.cpp src/channel/ServerChannel.cpp src/channel/ClientChannelView.cpp - src/client/file/FileClient.cpp - src/client/file/FileClientIO.cpp - src/Group.cpp - src/manager/BanManager.cpp - src/client/InternalClient.cpp - #src/weblist/WeblistClient.cpp - #src/weblist/WebList.cpp + src/client/file/FileClient.cpp + src/client/file/FileClientIO.cpp + src/Group.cpp + src/manager/BanManager.cpp + src/client/InternalClient.cpp + #src/weblist/WeblistClient.cpp + #src/weblist/WebList.cpp - src/client/DataClient.cpp - src/server/QueryServer.cpp - src/client/query/QueryClient.cpp - src/client/query/QueryClientCommands.cpp - src/client/query/QueryClientNotify.cpp + src/client/DataClient.cpp + src/server/QueryServer.cpp + src/client/query/QueryClient.cpp + src/client/query/QueryClientCommands.cpp + src/client/query/QueryClientNotify.cpp - src/manager/IpListManager.cpp + src/manager/IpListManager.cpp - src/ConnectionStatistics.cpp + src/ConnectionStatistics.cpp - src/manager/TokeManager.cpp + src/manager/TokeManager.cpp - src/terminal/CommandHandler.cpp + src/terminal/CommandHandler.cpp - src/manager/ComplainManager.cpp - src/DatabaseHelper.cpp + src/manager/ComplainManager.cpp + src/DatabaseHelper.cpp - src/manager/LetterManager.cpp - src/manager/PermissionNameManager.cpp + src/manager/LetterManager.cpp + src/manager/PermissionNameManager.cpp - src/pinteraction/ApplicationInteraction.cpp - src/ServerManagerSnapshot.cpp - src/ServerManagerSnapshotDeploy.cpp - src/client/music/Song.cpp - src/music/PlayablePlaylist.cpp - src/InstanceHandler.cpp - src/InstanceHandlerSetup.cpp + src/pinteraction/ApplicationInteraction.cpp + src/ServerManagerSnapshot.cpp + src/ServerManagerSnapshotDeploy.cpp + src/client/music/Song.cpp + src/music/PlayablePlaylist.cpp + src/InstanceHandler.cpp + src/InstanceHandlerSetup.cpp - src/Configuration.cpp + src/Configuration.cpp - src/build.cpp + src/build.cpp - src/music/MusicPlaylist.cpp - src/client/music/MusicClient.cpp - src/client/music/MusicClientPlayer.cpp - src/client/ConnectedClientTextCommandHandler.cpp - src/music/MusicBotManager.cpp - src/client/music/internal_provider/channel_replay/ChannelProvider.cpp + src/music/MusicPlaylist.cpp + src/client/music/MusicClient.cpp + src/client/music/MusicClientPlayer.cpp + src/client/ConnectedClientTextCommandHandler.cpp + src/music/MusicBotManager.cpp + src/client/music/internal_provider/channel_replay/ChannelProvider.cpp - src/geo/GeoLocation.cpp - src/geo/IP2Location.cpp - src/geo/VPNBlocker.cpp + src/geo/GeoLocation.cpp + src/geo/IP2Location.cpp + src/geo/VPNBlocker.cpp - src/client/query/XMacroEventTypes.h + src/client/query/XMacroEventTypes.h - src/server/VoiceIOManager.cpp - src/server/WebIoManager.cpp - src/client/SpeakingClient.cpp + src/server/VoiceIOManager.cpp + src/server/WebIoManager.cpp + src/client/SpeakingClient.cpp - src/lincense/LicenseHelper.cpp - ../shared/src/ssl/SSLManager.cpp + src/lincense/LicenseHelper.cpp + ../shared/src/ssl/SSLManager.cpp - src/manager/SqlDataManager.cpp + src/manager/SqlDataManager.cpp - src/ShutdownHelper.cpp - src/client/music/MusicQueue.cpp - src/lincense/TeamSpeakLicense.cpp + src/ShutdownHelper.cpp + src/client/music/MusicQueue.cpp + src/lincense/TeamSpeakLicense.cpp - src/weblist/WebListManager.cpp - src/weblist/TeamSpeakWebClient.cpp + src/weblist/WebListManager.cpp + src/weblist/TeamSpeakWebClient.cpp - src/manager/ConversationManager.cpp + src/manager/ConversationManager.cpp ) if(COMPILE_WEB_CLIENT) - add_definitions(-DCOMPILE_WEB_CLIENT) + add_definitions(-DCOMPILE_WEB_CLIENT) - set(SERVER_SOURCE_FILES - ${SERVER_SOURCE_FILES} + set(SERVER_SOURCE_FILES + ${SERVER_SOURCE_FILES} - src/server/WebServer.cpp - src/client/web/WebClient.cpp -# src/server/web/WebRTCServer.cpp - src/client/web/WSWebClient.cpp - src/client/web/SampleHandler.cpp - src/client/SpeakingClientHandshake.cpp - src/client/web/VoiceBridge.cpp - ) + src/server/WebServer.cpp + src/client/web/WebClient.cpp +# src/server/web/WebRTCServer.cpp + src/client/web/WSWebClient.cpp + src/client/web/SampleHandler.cpp + src/client/SpeakingClientHandshake.cpp + src/client/web/VoiceBridge.cpp + ) endif() add_executable(PermHelper helpers/permgen.cpp) target_link_libraries(PermHelper - ${LIBRARY_PATH_ED255} + ${LIBRARY_PATH_ED255} - TeaSpeak #Static - TeaLicenseHelper #Static - TeaMusic #Static - ${LIBRARY_PATH_THREAD_POOL} #Static - ${LIBRARY_PATH_TERMINAL} #Static - ${LIBRARY_PATH_VARIBALES} - ${LIBRARY_PATH_YAML} - pthread - stdc++fs - ${LIBEVENT_PATH}/libevent.a - ${LIBEVENT_PATH}/libevent_pthreads.a - ${LIBRARY_PATH_OPUS} - ${LIBRARY_PATH_JSON} - ${LIBRARY_PATH_PROTOBUF} + TeaSpeak #Static + TeaLicenseHelper #Static + TeaMusic #Static + ${LIBRARY_PATH_THREAD_POOL} #Static + ${LIBRARY_PATH_TERMINAL} #Static + ${LIBRARY_PATH_VARIBALES} + ${LIBRARY_PATH_YAML} + pthread + stdc++fs + ${LIBEVENT_PATH}/libevent.a + ${LIBEVENT_PATH}/libevent_pthreads.a + ${LIBRARY_PATH_OPUS} + ${LIBRARY_PATH_JSON} + ${LIBRARY_PATH_PROTOBUF} - #${LIBWEBRTC_LIBRARIES} #ATTENTIAN! WebRTC does not work with crypto! (Already contains a crypto version) - ${LIBRARY_TOM_CRYPT} - ${LIBRARY_TOM_MATH} + #${LIBWEBRTC_LIBRARIES} #ATTENTIAN! WebRTC does not work with crypto! (Already contains a crypto version) + ${LIBRARY_TOM_CRYPT} + ${LIBRARY_TOM_MATH} - #We're forsed to use boringssl caused by the fact that boringssl is already within webrtc! + #We're forsed to use boringssl caused by the fact that boringssl is already within webrtc! - #Require a so - sqlite3 + #Require a so + sqlite3 - ${LIBRARY_PATH_BREAKPAD} - ${LIBRARY_PATH_JDBC} - ${LIBRARY_PATH_PROTOBUF} + ${LIBRARY_PATH_BREAKPAD} + ${LIBRARY_PATH_JDBC} + ${LIBRARY_PATH_PROTOBUF} - ${LIBRARY_PATH_DATA_PIPES} - ${LIBRARY_PATH_BORINGSSL_SSL} - ${LIBRARY_PATH_BORINGSSL_CRYPTO} - dl - jemalloc + ${LIBRARY_PATH_DATA_PIPES} + ${LIBRARY_PATH_BORINGSSL_SSL} + ${LIBRARY_PATH_BORINGSSL_CRYPTO} + dl + jemalloc ) add_executable(PermMapHelper helpers/PermMapGen.cpp) target_link_libraries(PermMapHelper - ${LIBRARY_PATH_ED255} + ${LIBRARY_PATH_ED255} - TeaSpeak #Static - TeaLicenseHelper #Static - TeaMusic #Static - ${LIBRARY_PATH_THREAD_POOL} #Static - ${LIBRARY_PATH_TERMINAL} #Static - ${LIBRARY_PATH_VARIBALES} - ${LIBRARY_PATH_YAML} - pthread - stdc++fs - ${LIBEVENT_PATH}/libevent.a - ${LIBEVENT_PATH}/libevent_pthreads.a - ${LIBRARY_PATH_OPUS} - ${LIBRARY_PATH_JSON} - ${LIBRARY_PATH_PROTOBUF} + TeaSpeak #Static + TeaLicenseHelper #Static + TeaMusic #Static + ${LIBRARY_PATH_THREAD_POOL} #Static + ${LIBRARY_PATH_TERMINAL} #Static + ${LIBRARY_PATH_VARIBALES} + ${LIBRARY_PATH_YAML} + pthread + stdc++fs + ${LIBEVENT_PATH}/libevent.a + ${LIBEVENT_PATH}/libevent_pthreads.a + ${LIBRARY_PATH_OPUS} + ${LIBRARY_PATH_JSON} + ${LIBRARY_PATH_PROTOBUF} - #${LIBWEBRTC_LIBRARIES} #ATTENTIAN! WebRTC does not work with crypto! (Already contains a crypto version) - ${LIBRARY_TOM_CRYPT} - ${LIBRARY_TOM_MATH} + #${LIBWEBRTC_LIBRARIES} #ATTENTIAN! WebRTC does not work with crypto! (Already contains a crypto version) + ${LIBRARY_TOM_CRYPT} + ${LIBRARY_TOM_MATH} - #We're forsed to use boringssl caused by the fact that boringssl is already within webrtc! + #We're forsed to use boringssl caused by the fact that boringssl is already within webrtc! - #Require a so - sqlite3 + #Require a so + sqlite3 - ${LIBRARY_PATH_BREAKPAD} - ${LIBRARY_PATH_JDBC} - ${LIBRARY_PATH_PROTOBUF} + ${LIBRARY_PATH_BREAKPAD} + ${LIBRARY_PATH_JDBC} + ${LIBRARY_PATH_PROTOBUF} - ${LIBRARY_PATH_DATA_PIPES} - ${LIBRARY_PATH_BORINGSSL_SSL} - ${LIBRARY_PATH_BORINGSSL_CRYPTO} - dl - jemalloc - ) + ${LIBRARY_PATH_DATA_PIPES} + ${LIBRARY_PATH_BORINGSSL_SSL} + ${LIBRARY_PATH_BORINGSSL_CRYPTO} + dl + jemalloc + ) SET(CPACK_PACKAGE_VERSION_MAJOR "1") SET(CPACK_PACKAGE_VERSION_MINOR "4") SET(CPACK_PACKAGE_VERSION_PATCH "4") if(BUILD_TYPE_NAME EQUAL OFF) - SET(CPACK_PACKAGE_VERSION_DATA "beta") + SET(CPACK_PACKAGE_VERSION_DATA "beta") elseif(BUILD_TYPE_NAME STREQUAL "") - SET(CPACK_PACKAGE_VERSION_DATA "") + SET(CPACK_PACKAGE_VERSION_DATA "") else() - SET(CPACK_PACKAGE_VERSION_DATA "-${BUILD_TYPE_NAME}") + SET(CPACK_PACKAGE_VERSION_DATA "-${BUILD_TYPE_NAME}") endif() if(BUILD_TYPE EQUAL OFF) - SET(BUILD_TYPE "1") + SET(BUILD_TYPE "1") endif() set_source_files_properties(src/build.cpp PROPERTIES - COMPILE_FLAGS "-DBUILD_MAJOR=${CPACK_PACKAGE_VERSION_MAJOR} -DBUILD_MINOR=${CPACK_PACKAGE_VERSION_MINOR} -DBUILD_PATCH=${CPACK_PACKAGE_VERSION_PATCH} -DBUILD_DATA=\"${CPACK_PACKAGE_VERSION_DATA}\" -DBUILD_TYPE=${BUILD_TYPE} -DBUILD_COUNT=0") + COMPILE_FLAGS "-DBUILD_MAJOR=${CPACK_PACKAGE_VERSION_MAJOR} -DBUILD_MINOR=${CPACK_PACKAGE_VERSION_MINOR} -DBUILD_PATCH=${CPACK_PACKAGE_VERSION_PATCH} -DBUILD_DATA=\"${CPACK_PACKAGE_VERSION_DATA}\" -DBUILD_TYPE=${BUILD_TYPE} -DBUILD_COUNT=0") file(WRITE repro/env/buildVersion.txt "${CPACK_PACKAGE_VERSION_MAJOR}.${CPACK_PACKAGE_VERSION_MINOR}.${CPACK_PACKAGE_VERSION_PATCH}${CPACK_PACKAGE_VERSION_DATA}") add_executable(TeaSpeakServer ${SERVER_SOURCE_FILES}) target_link_libraries(TeaSpeakServer - ${LIBRARY_PATH_THREAD_POOL} #Static - TeaSpeak #Static - TeaLicenseHelper #Static - TeaMusic #Static - ${LIBRARY_PATH_TERMINAL} #Static - ${LIBRARY_PATH_VARIBALES} - ${LIBRARY_PATH_YAML} - pthread - stdc++fs - ${LIBEVENT_PATH}/libevent.a - ${LIBEVENT_PATH}/libevent_pthreads.a - ${LIBRARY_PATH_OPUS} - ${LIBRARY_PATH_JSON} - ${LIBRARY_PATH_PROTOBUF} + ${LIBRARY_PATH_THREAD_POOL} #Static + TeaSpeak #Static + TeaLicenseHelper #Static + TeaMusic #Static + ${LIBRARY_PATH_TERMINAL} #Static + ${LIBRARY_PATH_VARIBALES} + ${LIBRARY_PATH_YAML} + pthread + stdc++fs + ${LIBEVENT_PATH}/libevent.a + ${LIBEVENT_PATH}/libevent_pthreads.a + ${LIBRARY_PATH_OPUS} + ${LIBRARY_PATH_JSON} + ${LIBRARY_PATH_PROTOBUF} - #We're forsed to use boringssl caused by the fact that boringssl is already within webrtc! + #We're forsed to use boringssl caused by the fact that boringssl is already within webrtc! - #Require a so - sqlite3 + #Require a so + sqlite3 - ${LIBRARY_PATH_BREAKPAD} - ${LIBRARY_PATH_PROTOBUF} + ${LIBRARY_PATH_BREAKPAD} + ${LIBRARY_PATH_PROTOBUF} - #${LIBWEBRTC_LIBRARIES} #ATTENTIAN! WebRTC does not work with crypto! (Already contains a crypto version) - ${LIBRARY_TOM_CRYPT} - ${LIBRARY_TOM_MATH} + #${LIBWEBRTC_LIBRARIES} #ATTENTIAN! WebRTC does not work with crypto! (Already contains a crypto version) + ${LIBRARY_TOM_CRYPT} + ${LIBRARY_TOM_MATH} - mysqlclient.a + mysqlclient.a - ${LIBRARY_PATH_ED255} + ${LIBRARY_PATH_ED255} ) if(${COMPILE_WEB_CLIENT}) - find_package(UsrSCTP REQUIRED) - target_link_libraries(TeaSpeakServer - ${LIBRARY_PATH_DATA_PIPES} - ${LIBRARY_PATH_NICE} - ${LIBRARY_PATH_GLIBC} - ) + find_package(UsrSCTP REQUIRED) + target_link_libraries(TeaSpeakServer + ${LIBRARY_PATH_DATA_PIPES} + ${LIBRARY_PATH_NICE} + ${LIBRARY_PATH_GLIBC} + ) - #target_link_options(TeaSpeakServer PUBLIC "-Wl,--no-as-needed" "${LIBRARY_PATH_DATA_PIPES}" "${LIBRARY_PATH_NICE}" "-Wl,--as-needed") + #target_link_options(TeaSpeakServer PUBLIC "-Wl,--no-as-needed" "${LIBRARY_PATH_DATA_PIPES}" "${LIBRARY_PATH_NICE}" "-Wl,--as-needed") endif() include_directories(${LIBRARY_PATH}/boringssl/include/) target_link_libraries(TeaSpeakServer ${LIBRARY_PATH_BORINGSSL_SSL} ${LIBRARY_PATH_BORINGSSL_CRYPTO} - dl - z + dl + z ) set(DISABLE_JEMALLOC ON) if(NOT DISABLE_JEMALLOC) - target_link_libraries(TeaSpeakServer - jemalloc - ) - add_definitions(-DHAVE_JEMALLOC) + target_link_libraries(TeaSpeakServer + jemalloc + ) + add_definitions(-DHAVE_JEMALLOC) endif() #Fix RPATH diff --git a/server/MySQLLibSSLFix.c b/server/MySQLLibSSLFix.c index 24ea803..5aa5dc6 100644 --- a/server/MySQLLibSSLFix.c +++ b/server/MySQLLibSSLFix.c @@ -19,15 +19,15 @@ const EVP_CIPHER *EVP_aes_192_cfb128(void){ return 0; } const EVP_CIPHER *EVP_aes_256_cfb128(void){ return 0; } int EVP_EncryptFinal(EVP_CIPHER_CTX *ctx, uint8_t *out, int *out_len) { - return EVP_EncryptFinal_ex(ctx, out, out_len); + return EVP_EncryptFinal_ex(ctx, out, out_len); } int EVP_DecryptFinal(EVP_CIPHER_CTX *ctx, unsigned char *out, int *out_len) { - return EVP_DecryptFinal_ex(ctx, out, out_len); + return EVP_DecryptFinal_ex(ctx, out, out_len); } int SSL_CTX_set_ciphersuites(SSL_CTX *ctx, const char *str) { - return 0; + return 0; } #define DTLSv1_get_timeout DTLSv1_get_timeout @@ -100,7 +100,7 @@ int SSL_CTX_set_ciphersuites(SSL_CTX *ctx, const char *str) { #define SSL_total_renegotiations SSL_total_renegotiations long SSL_CTX_ctrl(SSL_CTX *ctx, int cmd, long larg, void *parg) { - return 0; + return 0; } #ifdef __cplusplus diff --git a/server/VersionHelper.sh b/server/VersionHelper.sh index f354f0c..98b6ebf 100644 --- a/server/VersionHelper.sh +++ b/server/VersionHelper.sh @@ -1,11 +1,11 @@ #!/usr/bin/env bash if [ -e "build.data" ]; then - echo "File exists" - DATA=$(cat "build.data") + echo "File exists" + DATA=$(cat "build.data") else - echo "Create new file" - echo "0" > "build.data" + echo "Create new file" + echo "0" > "build.data" fi DATA=$(($DATA+1)) diff --git a/server/main.cpp b/server/main.cpp index 3654a52..b1f40ab 100644 --- a/server/main.cpp +++ b/server/main.cpp @@ -57,29 +57,29 @@ class CLIParser{ this->tokens.emplace_back(argv[i]); } - std::deque getCmdOptions(const std::string &option) const { - std::deque result; + std::deque getCmdOptions(const std::string &option) const { + std::deque result; - auto itr = this->tokens.begin(); - while(true) { - itr = std::find(itr, this->tokens.end(), option); - if (itr != this->tokens.end() && ++itr != this->tokens.end()){ - result.push_back(*itr); - itr++; - } else break; - } - return result; - } + auto itr = this->tokens.begin(); + while(true) { + itr = std::find(itr, this->tokens.end(), option); + if (itr != this->tokens.end() && ++itr != this->tokens.end()){ + result.push_back(*itr); + itr++; + } else break; + } + return result; + } - std::deque getCmdOptionsBegins(const std::string &option) const { - std::deque result; + std::deque getCmdOptionsBegins(const std::string &option) const { + std::deque result; - for(const auto& token : this->tokens) - if(token.find(option) == 0) - result.push_back(token); + for(const auto& token : this->tokens) + if(token.find(option) == 0) + result.push_back(token); - return result; - } + return result; + } const std::string& get_option(const std::string &option) const { auto itr = std::find(this->tokens.begin(), this->tokens.end(), option); @@ -104,29 +104,29 @@ void __raise_exception (void **addr, void *id); #define T(address) \ std::cout << "Testing: " << address << " => "; \ {\ - sockaddr_storage storage;\ - net::resolve_address(address, storage);\ - std::cout << manager.contains(storage) << std::endl;\ + sockaddr_storage storage;\ + net::resolve_address(address, storage);\ + std::cout << manager.contains(storage) << std::endl;\ } #define CONFIG_NAME "config.yml" const char *malloc_conf = ""; //retain:false"; //,dirty_decay_ms:0"; int main(int argc, char** argv) { #ifdef HAVE_JEMALLOC - (void*) malloc_conf; + (void*) malloc_conf; #endif CLIParser arguments(argc, argv); SSL_load_error_strings(); OpenSSL_add_ssl_algorithms(); - ts::permission::setup_permission_resolve(); + ts::permission::setup_permission_resolve(); - { - auto evthread_use_pthreads_result = evthread_use_pthreads(); - assert(evthread_use_pthreads_result == 0); - (void) evthread_use_pthreads_result; - } - terminal::install(); + { + auto evthread_use_pthreads_result = evthread_use_pthreads(); + assert(evthread_use_pthreads_result == 0); + (void) evthread_use_pthreads_result; + } + terminal::install(); if(!terminal::active()){ cerr << "could not setup terminal!" << endl; return -1; } assert(ts::property::impl::validateUnique()); @@ -135,87 +135,87 @@ int main(int argc, char** argv) { logMessageFmt(true, LOG_GENERAL, "Available command line parameters:"); logMessageFmt(true, LOG_GENERAL, HELP_FMT, "-h", "--help", "Shows this page"); logMessageFmt(true, LOG_GENERAL, HELP_FMT, "-q", "--set_query_password", "Changed the server admin query password"); - logMessageFmt(true, LOG_GENERAL, HELP_FMT, "-P=", "--property:=", "Override a config value manual"); - logMessageFmt(true, LOG_GENERAL, HELP_FMT, "-l", "--property-list", "List all available properties"); + logMessageFmt(true, LOG_GENERAL, HELP_FMT, "-P=", "--property:=", "Override a config value manual"); + logMessageFmt(true, LOG_GENERAL, HELP_FMT, "-l", "--property-list", "List all available properties"); terminal::uninstall(); return 0; } - if(arguments.cmdOptionExists("--property-list") || arguments.cmdOptionExists("-l")) { - logMessageFmt(true, LOG_GENERAL, "Available properties:"); - auto properties = ts::config::create_bindings(); - for(const auto& property : properties) { - logMessageFmt(true, LOG_GENERAL, " " + property->key); - for(const auto& entry : property->description) { - if(entry.first.empty()) { - for(const auto& line : entry.second) - logMessageFmt(true, LOG_GENERAL, " " + line); - } else { - logMessageFmt(true, LOG_GENERAL, " " + entry.first + ":"); - for(const auto& line : entry.second) - logMessageFmt(true, LOG_GENERAL, " " + line); - } - } - logMessageFmt(true, LOG_GENERAL, " " + property->value_description()); - } - return 0; - } + if(arguments.cmdOptionExists("--property-list") || arguments.cmdOptionExists("-l")) { + logMessageFmt(true, LOG_GENERAL, "Available properties:"); + auto properties = ts::config::create_bindings(); + for(const auto& property : properties) { + logMessageFmt(true, LOG_GENERAL, " " + property->key); + for(const auto& entry : property->description) { + if(entry.first.empty()) { + for(const auto& line : entry.second) + logMessageFmt(true, LOG_GENERAL, " " + line); + } else { + logMessageFmt(true, LOG_GENERAL, " " + entry.first + ":"); + for(const auto& line : entry.second) + logMessageFmt(true, LOG_GENERAL, " " + line); + } + } + logMessageFmt(true, LOG_GENERAL, " " + property->value_description()); + } + return 0; + } - if(!arguments.cmdOptionExists("--valgrind")) { - ts::syssignal::setup(); - } - ts::syssignal::setup_threads(); + if(!arguments.cmdOptionExists("--valgrind")) { + ts::syssignal::setup(); + } + ts::syssignal::setup_threads(); - map override_settings; - { - auto short_override = arguments.getCmdOptionsBegins("-P"); - for(const auto& entry : short_override) { - if(entry.length() < 2) continue; - auto ei = entry.find('='); - if(ei == string::npos || ei == 2) { - logErrorFmt(true, LOG_GENERAL, "Invalid command line parameter. (\"" + entry + "\")"); - return 1; - } + map override_settings; + { + auto short_override = arguments.getCmdOptionsBegins("-P"); + for(const auto& entry : short_override) { + if(entry.length() < 2) continue; + auto ei = entry.find('='); + if(ei == string::npos || ei == 2) { + logErrorFmt(true, LOG_GENERAL, "Invalid command line parameter. (\"" + entry + "\")"); + return 1; + } - auto key = entry.substr(2, ei - 2); - auto value = entry.substr(ei + 1); - override_settings[key] = value; - } - } - { - auto short_override = arguments.getCmdOptionsBegins("--property:"); - for(const auto& entry : short_override) { - if(entry.length() < 11) continue; - auto ei = entry.find('='); - if(ei == string::npos || ei == 11) { - logErrorFmt(true, LOG_GENERAL, "Invalid command line parameter. (\"" + entry + "\")"); - return 1; - } + auto key = entry.substr(2, ei - 2); + auto value = entry.substr(ei + 1); + override_settings[key] = value; + } + } + { + auto short_override = arguments.getCmdOptionsBegins("--property:"); + for(const auto& entry : short_override) { + if(entry.length() < 11) continue; + auto ei = entry.find('='); + if(ei == string::npos || ei == 11) { + logErrorFmt(true, LOG_GENERAL, "Invalid command line parameter. (\"" + entry + "\")"); + return 1; + } - auto key = entry.substr(11, ei - 11); - auto value = entry.substr(ei + 1); - override_settings[key] = value; - } - } + auto key = entry.substr(11, ei - 11); + auto value = entry.substr(ei + 1); + override_settings[key] = value; + } + } - { - auto bindings = ts::config::create_bindings(); - for(const auto& setting : bindings) { - for(auto it = override_settings.begin(); it != override_settings.end(); it++) { - if(it->first == setting->key) { - try { - setting->read_argument(it->second); - } catch(const std::exception& ex) { - logErrorFmt(true, LOG_GENERAL, "Failed to apply value for given property '" + it->first + "': " + ex.what()); - } - override_settings.erase(it); - break; - } - } - } - for(const auto& entry : override_settings) { - logMessageFmt(true, LOG_GENERAL, "Missing property " + entry.first + ". Value unused!"); - } - } + { + auto bindings = ts::config::create_bindings(); + for(const auto& setting : bindings) { + for(auto it = override_settings.begin(); it != override_settings.end(); it++) { + if(it->first == setting->key) { + try { + setting->read_argument(it->second); + } catch(const std::exception& ex) { + logErrorFmt(true, LOG_GENERAL, "Failed to apply value for given property '" + it->first + "': " + ex.what()); + } + override_settings.erase(it); + break; + } + } + } + for(const auto& entry : override_settings) { + logMessageFmt(true, LOG_GENERAL, "Missing property " + entry.first + ". Value unused!"); + } + } /* std::string error; if(!interaction::waitForAttach(error)){ @@ -244,20 +244,20 @@ int main(int argc, char** argv) { return 1; } if(register_prng(&sprng_desc) == -1) { - logCritical(LOG_GENERAL, "could not setup prng"); + logCritical(LOG_GENERAL, "could not setup prng"); return EXIT_FAILURE; } if (register_cipher(&rijndael_desc) == -1) { - logCritical(LOG_GENERAL, "could not setup rijndael"); + logCritical(LOG_GENERAL, "could not setup rijndael"); return EXIT_FAILURE; } testTomMath(); } - ts::server::SqlDataManager* sql = nullptr; - std::string errorMessage; + ts::server::SqlDataManager* sql = nullptr; + std::string errorMessage; shared_ptr logConfig = nullptr; - std::string line; + std::string line; logMessageFmt(true, LOG_GENERAL, "Loading configuration"); auto cfgErrors = ts::config::parseConfig(CONFIG_NAME); @@ -277,42 +277,42 @@ int main(int argc, char** argv) { logConfig->logPath = ts::config::log::path; logConfig->vs_group_size = ts::config::log::vs_size; logger::setup(logConfig); - threads::timer::function_log = [](const std::string& message, bool debug) { - if(debug) - debugMessage(LOG_GENERAL, message); - else - logWarning(LOG_GENERAL, message); - }; + threads::timer::function_log = [](const std::string& message, bool debug) { + if(debug) + debugMessage(LOG_GENERAL, message); + else + logWarning(LOG_GENERAL, message); + }; logger::updateLogLevels(); - if(ts::config::license_original && ts::config::license_original->data.type != license::LicenseType::DEMO){ - logMessageFmt(true, LOG_GENERAL, strobf("[]---------------------------------------------------------[]").string()); - logMessageFmt(true, LOG_GENERAL, strobf(" §aThank you for buying the TeaSpeak-§lPremium-§aSoftware! ").string()); - logMessageFmt(true, LOG_GENERAL, strobf(" §aLicense information:").string()); - logMessageFmt(true, LOG_GENERAL, strobf(" §aLicense owner : §e").string() + ts::config::license_original->owner()); - logMessageFmt(true, LOG_GENERAL, strobf(" §aLicense type : §e").string() + license::LicenseTypeNames[ts::config::license_original->data.type]); + if(ts::config::license_original && ts::config::license_original->data.type != license::LicenseType::DEMO){ + logMessageFmt(true, LOG_GENERAL, strobf("[]---------------------------------------------------------[]").string()); + logMessageFmt(true, LOG_GENERAL, strobf(" §aThank you for buying the TeaSpeak-§lPremium-§aSoftware! ").string()); + logMessageFmt(true, LOG_GENERAL, strobf(" §aLicense information:").string()); + logMessageFmt(true, LOG_GENERAL, strobf(" §aLicense owner : §e").string() + ts::config::license_original->owner()); + logMessageFmt(true, LOG_GENERAL, strobf(" §aLicense type : §e").string() + license::LicenseTypeNames[ts::config::license_original->data.type]); - if(ts::config::license_original->end().time_since_epoch().count() == 0){ - logMessageFmt(true, LOG_GENERAL, strobf(" §aLicense expires: §enever").string()); - } else { - char timeBuffer[32]; - time_t t = duration_cast(ts::config::license_original->end().time_since_epoch()).count(); - tm* stime = localtime(&t); - strftime(timeBuffer, 32, "%c", stime); - logMessageFmt(true, LOG_GENERAL, strobf(" §aLicense expires: §e").string() + string(timeBuffer)); - } - logMessageFmt(true, LOG_GENERAL, string() + strobf(" §aLicense valid : ").string() + (ts::config::license_original->isValid() ? strobf("§ayes").string() : strobf("§cno").string())); - logMessageFmt(true, LOG_GENERAL, strobf("[]---------------------------------------------------------[]").string()); - } + if(ts::config::license_original->end().time_since_epoch().count() == 0){ + logMessageFmt(true, LOG_GENERAL, strobf(" §aLicense expires: §enever").string()); + } else { + char timeBuffer[32]; + time_t t = duration_cast(ts::config::license_original->end().time_since_epoch()).count(); + tm* stime = localtime(&t); + strftime(timeBuffer, 32, "%c", stime); + logMessageFmt(true, LOG_GENERAL, strobf(" §aLicense expires: §e").string() + string(timeBuffer)); + } + logMessageFmt(true, LOG_GENERAL, string() + strobf(" §aLicense valid : ").string() + (ts::config::license_original->isValid() ? strobf("§ayes").string() : strobf("§cno").string())); + logMessageFmt(true, LOG_GENERAL, strobf("[]---------------------------------------------------------[]").string()); + } logMessage(LOG_GENERAL, "Starting TeaSpeak-Server v{}", build::version()->string(true)); - logMessage(LOG_GENERAL, "Starting music providers"); + logMessage(LOG_GENERAL, "Starting music providers"); - terminal::instance()->setPrompt("§aStarting server. §7[§aloading music§7]"); - if(ts::config::music::enabled && !arguments.cmdOptionExists("--no-providers")) { - ::music::manager::loadProviders("providers"); - ::music::manager::register_provider(::music::provider::ChannelProvider::create_provider()); - } + terminal::instance()->setPrompt("§aStarting server. §7[§aloading music§7]"); + if(ts::config::music::enabled && !arguments.cmdOptionExists("--no-providers")) { + ::music::manager::loadProviders("providers"); + ::music::manager::register_provider(::music::provider::ChannelProvider::create_provider()); + } terminal::instance()->setPrompt("§aStarting server. §7[§aloading geoloc§7]"); @@ -328,7 +328,7 @@ int main(int argc, char** argv) { logCritical(LOG_GENERAL,"Could not setup geoloc! Fallback to default flag!"); logCritical(LOG_GENERAL,"Message: {}", errorMessage); geoloc::provider = nullptr; - errorMessage = ""; + errorMessage = ""; } } if(ts::config::geo::vpn_block) { @@ -336,26 +336,26 @@ int main(int argc, char** argv) { if(geoloc::provider_vpn && !geoloc::provider_vpn->load(errorMessage)) { logCritical(LOG_GENERAL,"Could not setup vpn detector!"); - logCritical(LOG_GENERAL,"Message: {}", errorMessage); + logCritical(LOG_GENERAL,"Message: {}", errorMessage); geoloc::provider_vpn = nullptr; errorMessage = ""; } } terminal::instance()->setPrompt("§aStarting server. §7[§aloading sql§7]"); - sql = new ts::server::SqlDataManager(); - if(!sql->initialize(errorMessage)) { - logCriticalFmt(true, LOG_GENERAL, "Could not initialize SQL!"); - if(errorMessage.find("database is locked") != string::npos) { - logCriticalFmt(true, LOG_GENERAL, "----------------------------[ ATTENTION ]----------------------------"); + sql = new ts::server::SqlDataManager(); + if(!sql->initialize(errorMessage)) { + logCriticalFmt(true, LOG_GENERAL, "Could not initialize SQL!"); + if(errorMessage.find("database is locked") != string::npos) { + logCriticalFmt(true, LOG_GENERAL, "----------------------------[ ATTENTION ]----------------------------"); logCriticalFmt(true, LOG_GENERAL, "{:^69}", "You're database is already in use!"); - logCriticalFmt(true, LOG_GENERAL, "{:^69}", "Stop the other instance first!"); - logCriticalFmt(true, LOG_GENERAL, "----------------------------[ ATTENTION ]----------------------------"); - } else { + logCriticalFmt(true, LOG_GENERAL, "{:^69}", "Stop the other instance first!"); + logCriticalFmt(true, LOG_GENERAL, "----------------------------[ ATTENTION ]----------------------------"); + } else { logCriticalFmt(true, LOG_GENERAL, errorMessage); - } - goto stopApp; - } + } + goto stopApp; + } terminal::instance()->setPrompt("§aStarting server. §7[§astarting instance§7]"); @@ -364,23 +364,23 @@ int main(int argc, char** argv) { goto stopApp; if(arguments.cmdOptionExists("-q") || arguments.cmdOptionExists("--set_query_password")) { - auto password = arguments.cmdOptionExists("-q") ? arguments.get_option("-q") : arguments.get_option("--set_query_password"); - if(!password.empty()) { - logMessageFmt(true, LOG_GENERAL, "Updating server admin query password to \"{}\"", password); - auto accounts = serverInstance->getQueryServer()->find_query_accounts_by_unique_id(serverInstance->getInitialServerAdmin()->getUid()); - bool found = false; - for(const auto& account : accounts) { - if(account->bound_server != 0) continue; - if(!serverInstance->getQueryServer()->change_query_password(account, password)) { - logErrorFmt(true, LOG_GENERAL, "Failed to update server admin query password! (Internal error)"); - } - found = true; - break; - } - if(!found) { - logErrorFmt(true, LOG_GENERAL, "Failed to update server admin query password! Login does not exists!"); - } - } + auto password = arguments.cmdOptionExists("-q") ? arguments.get_option("-q") : arguments.get_option("--set_query_password"); + if(!password.empty()) { + logMessageFmt(true, LOG_GENERAL, "Updating server admin query password to \"{}\"", password); + auto accounts = serverInstance->getQueryServer()->find_query_accounts_by_unique_id(serverInstance->getInitialServerAdmin()->getUid()); + bool found = false; + for(const auto& account : accounts) { + if(account->bound_server != 0) continue; + if(!serverInstance->getQueryServer()->change_query_password(account, password)) { + logErrorFmt(true, LOG_GENERAL, "Failed to update server admin query password! (Internal error)"); + } + found = true; + break; + } + if(!found) { + logErrorFmt(true, LOG_GENERAL, "Failed to update server admin query password! Login does not exists!"); + } + } } terminal::instance()->setPrompt("§7> §f"); @@ -396,21 +396,21 @@ int main(int argc, char** argv) { stopApp: logMessageFmt(true, LOG_GENERAL, "Stopping application"); - ::music::manager::finalizeProviders(); + ::music::manager::finalizeProviders(); if(serverInstance) serverInstance->stopInstance(); delete serverInstance; - serverInstance = nullptr; + serverInstance = nullptr; - ts::music::MusicBotManager::shutdown(); + ts::music::MusicBotManager::shutdown(); if(sql) - sql->finalize(); - delete sql; - logMessageFmt(true, LOG_GENERAL, "Application suspend successful!"); + sql->finalize(); + delete sql; + logMessageFmt(true, LOG_GENERAL, "Application suspend successful!"); logger::uninstall(); terminal::uninstall(); - mainThreadDone = true; + mainThreadDone = true; return 0; } \ No newline at end of file diff --git a/server/src/Configuration.cpp b/server/src/Configuration.cpp index c0435da..64cf682 100644 --- a/server/src/Configuration.cpp +++ b/server/src/Configuration.cpp @@ -139,32 +139,32 @@ std::string config::music::command_prefix; #define WARN_SENSITIVE(path) COMMENT(path, "Do NOT TOUCH unless you're 100% sure!") class ConfigParseError : public exception { - public: - ConfigParseError(shared_ptr entry, std::string message) : binding(move(entry)), message(std::move(message)) { } + public: + ConfigParseError(shared_ptr entry, std::string message) : binding(move(entry)), message(std::move(message)) { } - const char *what() const noexcept { - return message.c_str(); - } + const char *what() const noexcept { + return message.c_str(); + } - shared_ptr entry() const { return this->binding; } - private: - shared_ptr binding; - std::string message; + shared_ptr entry() const { return this->binding; } + private: + shared_ptr binding; + std::string message; }; class PathNodeError : public exception { - public: - PathNodeError(std::string path, std::string message) : _message(std::move(message)), _path(std::move(path)) { } + public: + PathNodeError(std::string path, std::string message) : _message(std::move(message)), _path(std::move(path)) { } - const char *what() const noexcept { - return _message.c_str(); - } + const char *what() const noexcept { + return _message.c_str(); + } - const std::string path() const { return this->_path; } - const std::string message() const { return this->_message; } - private: - std::string _path; - std::string _message; + const std::string path() const { return this->_path; } + const std::string message() const { return this->_message; } + private: + std::string _path; + std::string _message; }; std::string escapeJsonString(const std::string& input) { @@ -246,13 +246,13 @@ vector resolveNode(const YAML::Node &root,const string& path, bool o auto entries = split(path, '.'); for(auto it = entries.begin(); it != entries.end(); it++) { - auto& back = result.back(); - if(back.IsMap() || it == entries.end() || back.IsNull()) - result.push_back(back[*it]); - else if(!back.IsDefined() || override_old) - result.push_back((back = YAML::Node(YAML::NodeType::Map))[*it]); - else - throw PathNodeError(path, "Node '" + *it + "' isnt a sequence"); + auto& back = result.back(); + if(back.IsMap() || it == entries.end() || back.IsNull()) + result.push_back(back[*it]); + else if(!back.IsDefined() || override_old) + result.push_back((back = YAML::Node(YAML::NodeType::Map))[*it]); + else + throw PathNodeError(path, "Node '" + *it + "' isnt a sequence"); } return result; } @@ -272,44 +272,44 @@ void remapValue(YAML::Node& node, const string &oldPath, const string &newPath){ } void build_comments(map>& map, const std::deque>& bindings) { - for(const auto& entry : bindings) { - for(const auto& message : entry->description) { - if(message.first.empty()) { - for(const auto& m : message.second) - map[entry->key].push_back(m); - } else { - map[entry->key].push_back(message.first + ":"); - for(const auto& m : message.second) - map[entry->key].push_back(" " + m); - } - } + for(const auto& entry : bindings) { + for(const auto& message : entry->description) { + if(message.first.empty()) { + for(const auto& m : message.second) + map[entry->key].push_back(m); + } else { + map[entry->key].push_back(message.first + ":"); + for(const auto& m : message.second) + map[entry->key].push_back(" " + m); + } + } - if(entry->value_description) - map[entry->key].push_back(entry->value_description()); - } + if(entry->value_description) + map[entry->key].push_back(entry->value_description()); + } } void read_bindings(YAML::Node& root, const std::deque>& bindings, uint8_t required_flags = 0) { - for(const auto& entry : bindings) { - if(entry->bounded_by == 2) continue; - if(required_flags > 0 && (entry->flags & required_flags) == 0) continue; + for(const auto& entry : bindings) { + if(entry->bounded_by == 2) continue; + if(required_flags > 0 && (entry->flags & required_flags) == 0) continue; - auto nodes = resolveNode(root, entry->key); - assert(!nodes.empty()); - assert(entry->read_config); + auto nodes = resolveNode(root, entry->key); + assert(!nodes.empty()); + assert(entry->read_config); - if((entry->flags & PREMIUM_ONLY) > 0 && !config::license->isPremium()) { - const auto default_value = entry->default_value(); - if(nodes.back().IsNull() || !nodes.back().IsDefined()) - entry->set_default(nodes.back()); + if((entry->flags & PREMIUM_ONLY) > 0 && !config::license->isPremium()) { + const auto default_value = entry->default_value(); + if(nodes.back().IsNull() || !nodes.back().IsDefined()) + entry->set_default(nodes.back()); - for(const auto& e : entry->default_value()) - entry->read_argument(e); - continue; - } + for(const auto& e : entry->default_value()) + entry->read_argument(e); + continue; + } - entry->read_config(nodes.back()); - } + entry->read_config(nodes.back()); + } } inline string apply_comments(stringstream &in, map>& comments); @@ -318,7 +318,7 @@ std::deque> create_local_bindings(int& version, st #define CURRENT_CONFIG_VERSION 14 static std::string _config_path; vector config::parseConfig(const std::string& path) { - _config_path = path; + _config_path = path; vector errors; saveConfig = false; @@ -333,21 +333,21 @@ vector config::parseConfig(const std::string& path) { } cfgStream.close(); - map> comments; + map> comments; try { - int config_version; - string teaspeak_license; - { - auto bindings = create_local_bindings(config_version, teaspeak_license); - read_bindings(config, bindings); - build_comments(comments, bindings); - } - if(config_version > CURRENT_CONFIG_VERSION) { - errors.push_back("Given config version is higher that currently supported config version!"); - errors.push_back("Decrease the version by hand to " + to_string(CURRENT_CONFIG_VERSION)); - errors.push_back("Attention: Decreasing the version could may lead to data loss!"); - return errors; - } + int config_version; + string teaspeak_license; + { + auto bindings = create_local_bindings(config_version, teaspeak_license); + read_bindings(config, bindings); + build_comments(comments, bindings); + } + if(config_version > CURRENT_CONFIG_VERSION) { + errors.push_back("Given config version is higher that currently supported config version!"); + errors.push_back("Decrease the version by hand to " + to_string(CURRENT_CONFIG_VERSION)); + errors.push_back("Attention: Decreasing the version could may lead to data loss!"); + return errors; + } { if(config_version != CURRENT_CONFIG_VERSION) { logMessage(LOG_GENERAL, "You're using an outdated config."); @@ -382,102 +382,102 @@ vector config::parseConfig(const std::string& path) { remapValue(config, "messages.mute.kick_invalid.command", "messages.kick_invalid.command"); remapValue(config, "messages.mute.kick_invalid.badges", "messages.kick_invalid.badges"); remapValue(config, "messages.level", "log.terminal_level"); - case 11: - remapValue(config, "general.database_url", "general.database.url"); - case 12: - { - auto nodes_certificate = YAML::Clone(resolveNode(config, "web.ssl.certificate").back()); //We'll clone here because we're overriding it later - auto nodes_key = resolveNode(config, "web.ssl.privatekey").back(); + case 11: + remapValue(config, "general.database_url", "general.database.url"); + case 12: + { + auto nodes_certificate = YAML::Clone(resolveNode(config, "web.ssl.certificate").back()); //We'll clone here because we're overriding it later + auto nodes_key = resolveNode(config, "web.ssl.privatekey").back(); - if(nodes_certificate.IsDefined() && nodes_key.IsDefined()) { - auto node_certificates_default = resolveNode(config, "web.ssl.certificate.default", true); - node_certificates_default.back() = YAML::Node(YAML::NodeType::Map); - node_certificates_default.back()["certificate"] = nodes_certificate; - node_certificates_default.back()["private_key"] = nodes_key; - } + if(nodes_certificate.IsDefined() && nodes_key.IsDefined()) { + auto node_certificates_default = resolveNode(config, "web.ssl.certificate.default", true); + node_certificates_default.back() = YAML::Node(YAML::NodeType::Map); + node_certificates_default.back()["certificate"] = nodes_certificate; + node_certificates_default.back()["private_key"] = nodes_key; + } - nodes_key = YAML::Node(YAML::NodeType::Undefined); - } - case 13: - { - auto nodes_key = resolveNode(config, "binding.query.host").back(); - if(nodes_key.IsDefined() && nodes_key.as() == "0.0.0.0") - nodes_key = nodes_key.as() + ",[::]"; - auto node_ft = resolveNode(config, "binding.file.host").back(); - if(node_ft.IsDefined() && node_ft.as() == "0.0.0.0") - node_ft = node_ft.as() + ",[::]"; - } - default: + nodes_key = YAML::Node(YAML::NodeType::Undefined); + } + case 13: + { + auto nodes_key = resolveNode(config, "binding.query.host").back(); + if(nodes_key.IsDefined() && nodes_key.as() == "0.0.0.0") + nodes_key = nodes_key.as() + ",[::]"; + auto node_ft = resolveNode(config, "binding.file.host").back(); + if(node_ft.IsDefined() && node_ft.as() == "0.0.0.0") + node_ft = node_ft.as() + ",[::]"; + } + default: break; } config["version"] = CURRENT_CONFIG_VERSION; - config_version = CURRENT_CONFIG_VERSION; + config_version = CURRENT_CONFIG_VERSION; } } //License parsing license_parsing: - { - string err; - if(teaspeak_license.empty() || teaspeak_license == "none") { - //Due to an implementation mistake every default license looks like this: - //AQA7CN26AAAAAMoLy9BIR2S9HyMeqBx7ZMUUc1rFXkt5YztwZCQRngncqtSs8hsQrzszzeNQSEcVXLtvIhm6m331OgjdugAAAADKbA25Oxab9/MK0xK3iZ8mUg+YkaZQkYS2Bj4Kcq2WFTCynv+sIfeYaei+VV8f/AJvxJDUfADJoSoGX85BIT3cTV+usRs3Adx0Ix/Wi5G4roL8Ypl0p8lYwELLGEVyEQf21rYMWOtVkQk2yoGuQikgLxr6p9sShKmAoES1lbHr8Xk= - //teaspeak_license = license::createLocalLicence(license::LicenseType::DEMO, system_clock::time_point(), "TeaSpeak"); - teaspeak_license = strobf("AQA7CN26AAAAAMoLy9BIR2S9HyMeqBx7ZMUUc1rFXkt5YztwZCQRngncqtSs8hsQrzszzeNQSEcVXLtvIhm6m331OgjdugAAAADKbA25Oxab9/MK0xK3iZ8mUg+YkaZQkYS2Bj4Kcq2WFTCynv+sIfeYaei+VV8f/AJvxJDUfADJoSoGX85BIT3cTV+usRs3Adx0Ix/Wi5G4roL8Ypl0p8lYwELLGEVyEQf21rYMWOtVkQk2yoGuQikgLxr6p9sShKmAoES1lbHr8Xk=").string(); - config::license = license::readLocalLicence(teaspeak_license, err); - } else { - config::license_original = license::readLocalLicence(teaspeak_license, err); - config::license = config::license_original; - } + { + string err; + if(teaspeak_license.empty() || teaspeak_license == "none") { + //Due to an implementation mistake every default license looks like this: + //AQA7CN26AAAAAMoLy9BIR2S9HyMeqBx7ZMUUc1rFXkt5YztwZCQRngncqtSs8hsQrzszzeNQSEcVXLtvIhm6m331OgjdugAAAADKbA25Oxab9/MK0xK3iZ8mUg+YkaZQkYS2Bj4Kcq2WFTCynv+sIfeYaei+VV8f/AJvxJDUfADJoSoGX85BIT3cTV+usRs3Adx0Ix/Wi5G4roL8Ypl0p8lYwELLGEVyEQf21rYMWOtVkQk2yoGuQikgLxr6p9sShKmAoES1lbHr8Xk= + //teaspeak_license = license::createLocalLicence(license::LicenseType::DEMO, system_clock::time_point(), "TeaSpeak"); + teaspeak_license = strobf("AQA7CN26AAAAAMoLy9BIR2S9HyMeqBx7ZMUUc1rFXkt5YztwZCQRngncqtSs8hsQrzszzeNQSEcVXLtvIhm6m331OgjdugAAAADKbA25Oxab9/MK0xK3iZ8mUg+YkaZQkYS2Bj4Kcq2WFTCynv+sIfeYaei+VV8f/AJvxJDUfADJoSoGX85BIT3cTV+usRs3Adx0Ix/Wi5G4roL8Ypl0p8lYwELLGEVyEQf21rYMWOtVkQk2yoGuQikgLxr6p9sShKmAoES1lbHr8Xk=").string(); + config::license = license::readLocalLicence(teaspeak_license, err); + } else { + config::license_original = license::readLocalLicence(teaspeak_license, err); + config::license = config::license_original; + } - if(!config::license){ - logErrorFmt(true, LOG_GENERAL, strobf("The given license isn't valid!").string()); - logErrorFmt(true, LOG_GENERAL, strobf("Falling back to the default license.").string()); - teaspeak_license = "none"; - goto license_parsing; - } - if(!config::license){ - errors.emplace_back(strobf("Invalid license code!").string()); - return errors; - } - if(!config::license->isValid()) { - if(config::license->data.type == license::LicenseType::INVALID) { - errors.emplace_back(strobf("Give license isn't valid!").string()); - return errors; - } + if(!config::license){ + logErrorFmt(true, LOG_GENERAL, strobf("The given license isn't valid!").string()); + logErrorFmt(true, LOG_GENERAL, strobf("Falling back to the default license.").string()); + teaspeak_license = "none"; + goto license_parsing; + } + if(!config::license){ + errors.emplace_back(strobf("Invalid license code!").string()); + return errors; + } + if(!config::license->isValid()) { + if(config::license->data.type == license::LicenseType::INVALID) { + errors.emplace_back(strobf("Give license isn't valid!").string()); + return errors; + } - logErrorFmt(true, LOG_GENERAL, strobf("The given license isn't valid!").string()); - logErrorFmt(true, LOG_GENERAL, strobf("Falling back to the default license.").string()); - teaspeak_license = "none"; - goto license_parsing; - } - } + logErrorFmt(true, LOG_GENERAL, strobf("The given license isn't valid!").string()); + logErrorFmt(true, LOG_GENERAL, strobf("Falling back to the default license.").string()); + teaspeak_license = "none"; + goto license_parsing; + } + } - { - auto bindings = create_bindings(); - read_bindings(config, bindings); - build_comments(comments, bindings); + { + auto bindings = create_bindings(); + read_bindings(config, bindings); + build_comments(comments, bindings); - for(const auto& entry : config::web::ice_servers) { - auto dp = entry.find(':'); - if(dp == string::npos) { - errors.emplace_back("Invalid ice server entry! Missing port"); - continue; - } - auto host = entry.substr(0, dp); - auto port = entry.substr(dp + 1); - if(port.find_last_not_of("0123456789") != string::npos) { - errors.push_back("Invalid ice server entry! Invalid port (" + port + ")"); - continue; - } + for(const auto& entry : config::web::ice_servers) { + auto dp = entry.find(':'); + if(dp == string::npos) { + errors.emplace_back("Invalid ice server entry! Missing port"); + continue; + } + auto host = entry.substr(0, dp); + auto port = entry.substr(dp + 1); + if(port.find_last_not_of("0123456789") != string::npos) { + errors.push_back("Invalid ice server entry! Invalid port (" + port + ")"); + continue; + } - try { - stoi(port); - } catch(std::exception& ex) { - errors.push_back("Invalid ice server entry! Invalid port (" + port + ")"); - } - } - } + try { + stoi(port); + } catch(std::exception& ex) { + errors.push_back("Invalid ice server entry! Invalid port (" + port + ")"); + } + } + } auto currentVersion = strobf("TeaSpeak ").string() + build::version()->string(true); if(currentVersion != config::server::DefaultServerVersion) { @@ -507,81 +507,81 @@ vector config::parseConfig(const std::string& path) { errors.push_back("Could not read config: " + ex.msg + " @" + to_string(ex.mark.line) + ":" + to_string(ex.mark.column)); return errors; } catch(const ConfigParseError& ex) { - errors.push_back("Failed to parse config entry \"" + ex.entry()->key + "\": " + ex.what()); - return errors; + errors.push_back("Failed to parse config entry \"" + ex.entry()->key + "\": " + ex.what()); + return errors; } catch(const PathNodeError& ex) { - errors.push_back("Expected sequence for path " + ex.path() + ": " + ex.message()); - return errors; + errors.push_back("Expected sequence for path " + ex.path() + ": " + ex.message()); + return errors; } return errors; } std::vector config::reload() { - vector errors; - saveConfig = false; + vector errors; + saveConfig = false; - ifstream cfgStream(_config_path); - YAML::Node config; - try { - config = YAML::Load(cfgStream); - } catch (const YAML::ParserException& ex){ - errors.emplace_back("Could not load config file: " + ex.msg + " @" + to_string(ex.mark.line) + ":" + to_string(ex.mark.column)); - return errors; - } + ifstream cfgStream(_config_path); + YAML::Node config; + try { + config = YAML::Load(cfgStream); + } catch (const YAML::ParserException& ex){ + errors.emplace_back("Could not load config file: " + ex.msg + " @" + to_string(ex.mark.line) + ":" + to_string(ex.mark.column)); + return errors; + } - try { - int config_version; - string teaspeak_license; - { - auto bindings = create_local_bindings(config_version, teaspeak_license); - read_bindings(config, bindings, 0); - } - if(config_version != CURRENT_CONFIG_VERSION) { - errors.emplace_back("Given config version is no equal to the initial one!"); - return errors; - } + try { + int config_version; + string teaspeak_license; + { + auto bindings = create_local_bindings(config_version, teaspeak_license); + read_bindings(config, bindings, 0); + } + if(config_version != CURRENT_CONFIG_VERSION) { + errors.emplace_back("Given config version is no equal to the initial one!"); + return errors; + } - auto bindings = create_bindings(); - read_bindings(config, bindings, FLAG_RELOADABLE); - } catch(const YAML::Exception& ex) { - errors.emplace_back("Could not read config: " + ex.msg + " @" + to_string(ex.mark.line) + ":" + to_string(ex.mark.column)); - return errors; - } catch(const ConfigParseError& ex) { - errors.emplace_back("Failed to parse config entry \"" + ex.entry()->key + "\": " + ex.what()); - return errors; - } catch(const PathNodeError& ex) { - errors.emplace_back("Expected sequence for path " + ex.path() + ": " + ex.message()); - return errors; - } + auto bindings = create_bindings(); + read_bindings(config, bindings, FLAG_RELOADABLE); + } catch(const YAML::Exception& ex) { + errors.emplace_back("Could not read config: " + ex.msg + " @" + to_string(ex.mark.line) + ":" + to_string(ex.mark.column)); + return errors; + } catch(const ConfigParseError& ex) { + errors.emplace_back("Failed to parse config entry \"" + ex.entry()->key + "\": " + ex.what()); + return errors; + } catch(const PathNodeError& ex) { + errors.emplace_back("Expected sequence for path " + ex.path() + ": " + ex.message()); + return errors; + } - return errors; + return errors; } void bind_string_description(const shared_ptr& _entry, std::string& target, const std::string& default_value) { - _entry->default_value = [default_value]() -> std::deque { return { default_value }; }; - _entry->value_description = [] { return "The value must be a string"; }; + _entry->default_value = [default_value]() -> std::deque { return { default_value }; }; + _entry->value_description = [] { return "The value must be a string"; }; } void bind_string_parse(const shared_ptr& _entry, std::string& target, const std::string& default_value) { - weak_ptr weak_entry = _entry; + weak_ptr weak_entry = _entry; - _entry->set_default = [weak_entry, default_value](YAML::Node& node) { - auto entry = weak_entry.lock(); - if(!entry) throw ConfigParseError(nullptr, "Entry handle got deleted!"); + _entry->set_default = [weak_entry, default_value](YAML::Node& node) { + auto entry = weak_entry.lock(); + if(!entry) throw ConfigParseError(nullptr, "Entry handle got deleted!"); - node = default_value; - }; + node = default_value; + }; - _entry->read_config = [weak_entry, default_value, &target](YAML::Node& node) { - auto entry = weak_entry.lock(); - if(!entry) throw ConfigParseError(nullptr, "Entry handle got deleted!"); + _entry->read_config = [weak_entry, default_value, &target](YAML::Node& node) { + auto entry = weak_entry.lock(); + if(!entry) throw ConfigParseError(nullptr, "Entry handle got deleted!"); if(!node.IsDefined() || node.IsNull()) { if((entry->flags & FLAG_REQUIRE) > 0) throw ConfigParseError(entry, "missing required setting"); else - entry->set_default(node); + entry->set_default(node); } try { target = node.as(); @@ -589,7 +589,7 @@ void bind_string_parse(const shared_ptr& _entry, std::string& targ } catch (const YAML::BadConversion& e) { throw ConfigParseError(entry, "Invalid node content. Requested was a string!"); } - }; + }; _entry->read_argument = [weak_entry, &target](const std::string& value) { auto entry = weak_entry.lock(); @@ -602,42 +602,42 @@ void bind_string_parse(const shared_ptr& _entry, std::string& targ template inline std::string type_name() { - int status; - std::string tname = typeid(T).name(); - char *demangled_name = abi::__cxa_demangle(tname.c_str(), NULL, NULL, &status); - if(status == 0) { - tname = demangled_name; - std::free(demangled_name); - } - return tname; + int status; + std::string tname = typeid(T).name(); + char *demangled_name = abi::__cxa_demangle(tname.c_str(), NULL, NULL, &status); + if(status == 0) { + tname = demangled_name; + std::free(demangled_name); + } + return tname; } template static typename std::enable_if::value, type_t>::type integral_parse(const std::string& value) { - try { - auto result = std::stoull(value); - if(result < numeric_limits::min()) throw std::out_of_range(""); - if(result > numeric_limits::max()) throw std::out_of_range(""); - return (type_t) result; - } catch(std::out_of_range& ex) { - throw YAML::BadConversion(YAML::Mark::null_mark()); - } catch(std::invalid_argument& ex) { - throw YAML::BadConversion(YAML::Mark::null_mark()); - } + try { + auto result = std::stoull(value); + if(result < numeric_limits::min()) throw std::out_of_range(""); + if(result > numeric_limits::max()) throw std::out_of_range(""); + return (type_t) result; + } catch(std::out_of_range& ex) { + throw YAML::BadConversion(YAML::Mark::null_mark()); + } catch(std::invalid_argument& ex) { + throw YAML::BadConversion(YAML::Mark::null_mark()); + } } template static typename std::enable_if::value, type_t>::type integral_parse(const std::string& value) { - try { - auto result = std::stoll(value); - if(result < numeric_limits::min()) throw std::out_of_range(""); - if(result > numeric_limits::max()) throw std::out_of_range(""); - return (type_t) result; - } catch(std::out_of_range& ex) { - throw YAML::BadConversion(YAML::Mark::null_mark()); - } catch(std::invalid_argument& ex) { - throw YAML::BadConversion(YAML::Mark::null_mark()); - } + try { + auto result = std::stoll(value); + if(result < numeric_limits::min()) throw std::out_of_range(""); + if(result > numeric_limits::max()) throw std::out_of_range(""); + return (type_t) result; + } catch(std::out_of_range& ex) { + throw YAML::BadConversion(YAML::Mark::null_mark()); + } catch(std::invalid_argument& ex) { + throw YAML::BadConversion(YAML::Mark::null_mark()); + } } template @@ -663,116 +663,116 @@ static map integral_mapping = { template ::value || std::is_enum::value, int>::type = 0> void bind_integral_description(const shared_ptr& _entry, type_t& target, type_t default_value, type_t min_value, type_t max_value) { - _entry->default_value = [default_value]() -> std::deque { return { to_string(default_value) }; }; - _entry->value_description = [min_value, max_value] { - auto type_name = ::type_name((type_t) 0))>(); - return "The value must be a " + (integral_mapping.count(type_name) > 0 ? integral_mapping[type_name] : type_name) + " between " + to_string(min_value) + " and " + to_string(max_value); - }; + _entry->default_value = [default_value]() -> std::deque { return { to_string(default_value) }; }; + _entry->value_description = [min_value, max_value] { + auto type_name = ::type_name((type_t) 0))>(); + return "The value must be a " + (integral_mapping.count(type_name) > 0 ? integral_mapping[type_name] : type_name) + " between " + to_string(min_value) + " and " + to_string(max_value); + }; } template ::value || std::is_enum::value, int>::type = 0> void bind_integral_parse(const shared_ptr& _entry, type_t& target, type_t default_value, type_t min_value, type_t max_value) { - weak_ptr weak_entry = _entry; + weak_ptr weak_entry = _entry; - _entry->set_default = [weak_entry, default_value](YAML::Node& node) { - auto entry = weak_entry.lock(); - if(!entry) throw ConfigParseError(nullptr, "Entry handle got deleted!"); + _entry->set_default = [weak_entry, default_value](YAML::Node& node) { + auto entry = weak_entry.lock(); + if(!entry) throw ConfigParseError(nullptr, "Entry handle got deleted!"); - node = enum_number_cast(default_value); - }; + node = enum_number_cast(default_value); + }; - _entry->read_config = [weak_entry, default_value, &target, min_value, max_value](YAML::Node& node) { - auto entry = weak_entry.lock(); - if(!entry) throw ConfigParseError(nullptr, "Entry handle got deleted!"); + _entry->read_config = [weak_entry, default_value, &target, min_value, max_value](YAML::Node& node) { + auto entry = weak_entry.lock(); + if(!entry) throw ConfigParseError(nullptr, "Entry handle got deleted!"); - if(!node.IsDefined() || node.IsNull()) { - if((entry->flags & FLAG_REQUIRE) > 0) - throw ConfigParseError(entry, "missing required setting"); - else - entry->set_default(node); - } - try { - auto str = node.as(); - auto value = (type_t) node.as((type_t) 0))>(); - if(value < min_value) throw ConfigParseError(entry, "Invalid value (" + to_string(value) + "). Received value substeps boundary (" + to_string(min_value) + ")"); - if(value > max_value) throw ConfigParseError(entry, "Invalid value (" + to_string(value) + "). Received value exceeds boundary (" + to_string(max_value) + ")"); + if(!node.IsDefined() || node.IsNull()) { + if((entry->flags & FLAG_REQUIRE) > 0) + throw ConfigParseError(entry, "missing required setting"); + else + entry->set_default(node); + } + try { + auto str = node.as(); + auto value = (type_t) node.as((type_t) 0))>(); + if(value < min_value) throw ConfigParseError(entry, "Invalid value (" + to_string(value) + "). Received value substeps boundary (" + to_string(min_value) + ")"); + if(value > max_value) throw ConfigParseError(entry, "Invalid value (" + to_string(value) + "). Received value exceeds boundary (" + to_string(max_value) + ")"); - target = value; - entry->bounded_by = 1; - } catch (const YAML::BadConversion& e) { - throw ConfigParseError(entry, "Invalid node content. Requested was a integral of type " + type_name() + "!"); - } - }; + target = value; + entry->bounded_by = 1; + } catch (const YAML::BadConversion& e) { + throw ConfigParseError(entry, "Invalid node content. Requested was a integral of type " + type_name() + "!"); + } + }; - _entry->read_argument = [weak_entry, &target, min_value, max_value](const std::string& string) { - auto entry = weak_entry.lock(); - if(!entry) throw ConfigParseError(nullptr, "Entry handle got deleted!"); - entry->bounded_by = 2; + _entry->read_argument = [weak_entry, &target, min_value, max_value](const std::string& string) { + auto entry = weak_entry.lock(); + if(!entry) throw ConfigParseError(nullptr, "Entry handle got deleted!"); + entry->bounded_by = 2; - try { - auto value = (type_t) integral_parse((type_t) 0))>(string); - if(value < min_value) throw ConfigParseError(entry, "Invalid value (" + to_string(value) + "). Received value substeps boundary (" + to_string(min_value) + ")"); - if(value > max_value) throw ConfigParseError(entry, "Invalid value (" + to_string(value) + "). Received value exceeds boundary (" + to_string(max_value) + ")"); + try { + auto value = (type_t) integral_parse((type_t) 0))>(string); + if(value < min_value) throw ConfigParseError(entry, "Invalid value (" + to_string(value) + "). Received value substeps boundary (" + to_string(min_value) + ")"); + if(value > max_value) throw ConfigParseError(entry, "Invalid value (" + to_string(value) + "). Received value exceeds boundary (" + to_string(max_value) + ")"); - target = value; - entry->bounded_by = 2; - } catch (const YAML::BadConversion& e) { - throw ConfigParseError(entry, "Invalid node content. Requested was a integral of type " + type_name() + "!"); - } - }; + target = value; + entry->bounded_by = 2; + } catch (const YAML::BadConversion& e) { + throw ConfigParseError(entry, "Invalid node content. Requested was a integral of type " + type_name() + "!"); + } + }; } void bind_vector_description(const shared_ptr& _entry, deque&, const deque& default_value) { - _entry->default_value = [default_value]() -> std::deque { return default_value; }; - _entry->value_description = [] { return "The value must be a sequence"; }; + _entry->default_value = [default_value]() -> std::deque { return default_value; }; + _entry->value_description = [] { return "The value must be a sequence"; }; } void bind_vector_parse(const shared_ptr& _entry, deque& target, const deque& default_value) { - weak_ptr weak_entry = _entry; + weak_ptr weak_entry = _entry; - _entry->set_default = [weak_entry, default_value](YAML::Node& node) { - auto entry = weak_entry.lock(); - if(!entry) throw ConfigParseError(nullptr, "Entry handle got deleted!"); + _entry->set_default = [weak_entry, default_value](YAML::Node& node) { + auto entry = weak_entry.lock(); + if(!entry) throw ConfigParseError(nullptr, "Entry handle got deleted!"); - node = YAML::Node(YAML::NodeType::Sequence); - for(const auto& entry : default_value) - node.push_back(entry); - }; + node = YAML::Node(YAML::NodeType::Sequence); + for(const auto& entry : default_value) + node.push_back(entry); + }; - _entry->read_config = [weak_entry, default_value, &target](YAML::Node& node) { - auto entry = weak_entry.lock(); - if(!entry) throw ConfigParseError(nullptr, "Entry handle got deleted!"); + _entry->read_config = [weak_entry, default_value, &target](YAML::Node& node) { + auto entry = weak_entry.lock(); + if(!entry) throw ConfigParseError(nullptr, "Entry handle got deleted!"); - if(!node.IsDefined() || node.IsNull()) { - if((entry->flags & FLAG_REQUIRE) > 0) - throw ConfigParseError(entry, "missing required setting"); - else { - entry->set_default(node); - } - } - if(!node.IsSequence()) - throw ConfigParseError(entry, "node requires to be a sequence"); + if(!node.IsDefined() || node.IsNull()) { + if((entry->flags & FLAG_REQUIRE) > 0) + throw ConfigParseError(entry, "missing required setting"); + else { + entry->set_default(node); + } + } + if(!node.IsSequence()) + throw ConfigParseError(entry, "node requires to be a sequence"); - try { - target.clear(); - for(const auto& element : node) { - target.push_back(element.as()); - } - } catch (const YAML::BadConversion& e) { - throw ConfigParseError(entry, "Invalid node sequence content"); - } - }; + try { + target.clear(); + for(const auto& element : node) { + target.push_back(element.as()); + } + } catch (const YAML::BadConversion& e) { + throw ConfigParseError(entry, "Invalid node sequence content"); + } + }; - _entry->read_argument = [weak_entry, &target](const std::string& string) { - auto entry = weak_entry.lock(); - if(!entry) throw ConfigParseError(nullptr, "Entry handle got deleted!"); - if(entry->bounded_by != 2) - target.clear(); - entry->bounded_by = 2; + _entry->read_argument = [weak_entry, &target](const std::string& string) { + auto entry = weak_entry.lock(); + if(!entry) throw ConfigParseError(nullptr, "Entry handle got deleted!"); + if(entry->bounded_by != 2) + target.clear(); + entry->bounded_by = 2; - target.push_back(string); - }; + target.push_back(string); + }; } struct GroupStackEntry { @@ -800,76 +800,76 @@ inline std::string join_path(const deque& stack, const std::string& entr #define BIND_GROUP(name) GroupStackEntry group_ ##name(group_stack, STR(name)); #define CREATE_BINDING(name, _flags) \ - auto binding = make_shared(); \ - binding->key = join_path(group_stack, name); \ - binding->flags = (_flags); \ - result.push_back(binding) + auto binding = make_shared(); \ + binding->key = join_path(group_stack, name); \ + binding->flags = (_flags); \ + result.push_back(binding) #define BIND_STRING(target, default) \ - bind_string_parse(binding, target, default); \ - bind_string_description(binding, target, default) + bind_string_parse(binding, target, default); \ + bind_string_description(binding, target, default) #define BIND_VECTOR(target, default) \ - bind_vector_parse(binding, target, default); \ - bind_vector_description(binding, target, default) + bind_vector_parse(binding, target, default); \ + bind_vector_description(binding, target, default) #define BIND_INTEGRAL(target, default, min, max) \ - bind_integral_parse::type>( \ - binding, \ - target, \ - (typename std::remove_reference::type) default, \ - (typename std::remove_reference::type) min, \ - (typename std::remove_reference::type) max \ - ); \ - bind_integral_description::type>( \ - binding, \ - target, \ - (typename std::remove_reference::type) default, \ - (typename std::remove_reference::type) min, \ - (typename std::remove_reference::type) max \ - ) + bind_integral_parse::type>( \ + binding, \ + target, \ + (typename std::remove_reference::type) default, \ + (typename std::remove_reference::type) min, \ + (typename std::remove_reference::type) max \ + ); \ + bind_integral_description::type>( \ + binding, \ + target, \ + (typename std::remove_reference::type) default, \ + (typename std::remove_reference::type) min, \ + (typename std::remove_reference::type) max \ + ) #define BIND_BOOL(target, default) BIND_INTEGRAL(target, default, false, true) #define ADD_DESCRIPTION(desc, ...) \ - for(const auto& entry : {desc, ##__VA_ARGS__}) \ - binding->description["Description"].emplace_back(entry) + for(const auto& entry : {desc, ##__VA_ARGS__}) \ + binding->description["Description"].emplace_back(entry) #define ADD_NOTE(desc, ...) \ - for(const auto& entry : {desc, ##__VA_ARGS__}) \ - binding->description["Notes"].emplace_back(entry) + for(const auto& entry : {desc, ##__VA_ARGS__}) \ + binding->description["Notes"].emplace_back(entry) #define ADD_NOTE_RELOADABLE() \ - binding->description["Notes"].emplace_back("This option could be reloaded while the instance is running.") + binding->description["Notes"].emplace_back("This option could be reloaded while the instance is running.") #define ADD_WARN(desc, ...) \ - for(const auto& entry : {desc, ##__VA_ARGS__}) \ - binding->description["Warning"].emplace_back(entry) + for(const auto& entry : {desc, ##__VA_ARGS__}) \ + binding->description["Warning"].emplace_back(entry) #define ADD_SENSITIVE() ADD_WARN("Do NOT TOUCH unless you're 100% sure!") std::deque> create_local_bindings(int& version, std::string& license) { - deque> result; - deque group_stack; + deque> result; + deque group_stack; - { - CREATE_BINDING("version", 0); - BIND_INTEGRAL(version, CURRENT_CONFIG_VERSION, 0, 10000000000); - ADD_DESCRIPTION("The current config version"); - ADD_WARN("This is an auto-generated id!", "Modification could cause data loss!"); - } - { - CREATE_BINDING("general.license", 0); - BIND_STRING(license, "none"); - ADD_DESCRIPTION("Insert here your TeaSpeak license code (if you have one)"); - } + { + CREATE_BINDING("version", 0); + BIND_INTEGRAL(version, CURRENT_CONFIG_VERSION, 0, 10000000000); + ADD_DESCRIPTION("The current config version"); + ADD_WARN("This is an auto-generated id!", "Modification could cause data loss!"); + } + { + CREATE_BINDING("general.license", 0); + BIND_STRING(license, "none"); + ADD_DESCRIPTION("Insert here your TeaSpeak license code (if you have one)"); + } - return result; + return result; } static std::deque> _create_bindings; std::deque> config::create_bindings() { - if(!_create_bindings.empty()) return _create_bindings; + if(!_create_bindings.empty()) return _create_bindings; deque> result; deque group_stack; @@ -877,79 +877,79 @@ std::deque> config::create_bindings() { { BIND_GROUP(general); - //CREATE_BINDING("database_url", 0); old - { - BIND_GROUP(database); - { - CREATE_BINDING("url", 0); - BIND_STRING(config::database::url, "sqlite://TeaData.sqlite"); - ADD_DESCRIPTION("Available urls:"); - ADD_DESCRIPTION(" sqlite://[file]"); - ADD_DESCRIPTION(" mysql://[host][:port]/[database][?propertyName1=propertyValue1[&propertyName2=propertyValue2]...]"); - ADD_DESCRIPTION(""); - ADD_DESCRIPTION("More info about about the mysql url could be found here: https://dev.mysql.com/doc/connector-j/5.1/en/connector-j-reference-configuration-properties.html"); - ADD_DESCRIPTION("There's also a new property called 'connections', which describes how many connections and queries could be executed synchronously"); - ADD_DESCRIPTION("MySQL example: mysql://localhost:3306/teaspeak?userName=root&password=mysecretpassword&connections=4"); - ADD_DESCRIPTION("Attention: If you're using MySQL you need at least 3 connections!"); - } + //CREATE_BINDING("database_url", 0); old + { + BIND_GROUP(database); + { + CREATE_BINDING("url", 0); + BIND_STRING(config::database::url, "sqlite://TeaData.sqlite"); + ADD_DESCRIPTION("Available urls:"); + ADD_DESCRIPTION(" sqlite://[file]"); + ADD_DESCRIPTION(" mysql://[host][:port]/[database][?propertyName1=propertyValue1[&propertyName2=propertyValue2]...]"); + ADD_DESCRIPTION(""); + ADD_DESCRIPTION("More info about about the mysql url could be found here: https://dev.mysql.com/doc/connector-j/5.1/en/connector-j-reference-configuration-properties.html"); + ADD_DESCRIPTION("There's also a new property called 'connections', which describes how many connections and queries could be executed synchronously"); + ADD_DESCRIPTION("MySQL example: mysql://localhost:3306/teaspeak?userName=root&password=mysecretpassword&connections=4"); + ADD_DESCRIPTION("Attention: If you're using MySQL you need at least 3 connections!"); + } - { - BIND_GROUP(sqlite); - { - CREATE_BINDING("locking_mode", 0); - BIND_STRING(config::database::sqlite::locking_mode, "EXCLUSIVE"); - ADD_DESCRIPTION("Sqlite database locking mode."); - ADD_DESCRIPTION("Set it to nothing (\"\") to use the default driver setting"); - ADD_DESCRIPTION("More information could be found here: https://www.sqlite.org/lockingv3.html"); - } - { - CREATE_BINDING("sync_mode", 0); - BIND_STRING(config::database::sqlite::sync_mode, "NORMAL"); - ADD_DESCRIPTION("Sqlite database synchronous mode."); - ADD_DESCRIPTION("Set it to nothing (\"\") to use the default driver setting"); - ADD_DESCRIPTION("More information could be found here: https://www.sqlite.org/pragma.html#pragma_synchronous"); - } - { - CREATE_BINDING("journal_mode", 0); - BIND_STRING(config::database::sqlite::journal_mode, "WAL"); - ADD_DESCRIPTION("Sqlite database journal mode."); - ADD_DESCRIPTION("Set it to nothing (\"\") to use the default driver setting"); - ADD_DESCRIPTION("More information could be found here: https://www.sqlite.org/pragma.html#pragma_journal_mode"); - } - } - } + { + BIND_GROUP(sqlite); + { + CREATE_BINDING("locking_mode", 0); + BIND_STRING(config::database::sqlite::locking_mode, "EXCLUSIVE"); + ADD_DESCRIPTION("Sqlite database locking mode."); + ADD_DESCRIPTION("Set it to nothing (\"\") to use the default driver setting"); + ADD_DESCRIPTION("More information could be found here: https://www.sqlite.org/lockingv3.html"); + } + { + CREATE_BINDING("sync_mode", 0); + BIND_STRING(config::database::sqlite::sync_mode, "NORMAL"); + ADD_DESCRIPTION("Sqlite database synchronous mode."); + ADD_DESCRIPTION("Set it to nothing (\"\") to use the default driver setting"); + ADD_DESCRIPTION("More information could be found here: https://www.sqlite.org/pragma.html#pragma_synchronous"); + } + { + CREATE_BINDING("journal_mode", 0); + BIND_STRING(config::database::sqlite::journal_mode, "WAL"); + ADD_DESCRIPTION("Sqlite database journal mode."); + ADD_DESCRIPTION("Set it to nothing (\"\") to use the default driver setting"); + ADD_DESCRIPTION("More information could be found here: https://www.sqlite.org/pragma.html#pragma_journal_mode"); + } + } + } { CREATE_BINDING("crash_path", 0); BIND_STRING(config::crash_path, "crash_dumps/"); ADD_DESCRIPTION("Define the folder where the crash dump files will be moved, when the server crashes"); } { - CREATE_BINDING("command_prefix", 0); - BIND_STRING(config::music::command_prefix, "."); - ADD_DESCRIPTION("The default channel chat command prefix"); - } - { - CREATE_BINDING("permission_mapping", 0); - BIND_STRING(config::permission_mapping_file, "resources/permission_mapping.txt"); - ADD_DESCRIPTION("Mapping for the permission names"); - ADD_SENSITIVE(); - } + CREATE_BINDING("command_prefix", 0); + BIND_STRING(config::music::command_prefix, "."); + ADD_DESCRIPTION("The default channel chat command prefix"); + } + { + CREATE_BINDING("permission_mapping", 0); + BIND_STRING(config::permission_mapping_file, "resources/permission_mapping.txt"); + ADD_DESCRIPTION("Mapping for the permission names"); + ADD_SENSITIVE(); + } } - { - BIND_GROUP(log) - { - CREATE_BINDING("level", 0); - BIND_INTEGRAL(config::log::logfileLevel, spdlog::level::debug, spdlog::level::trace, spdlog::level::off); - ADD_DESCRIPTION("The log level within the log files"); - ADD_DESCRIPTION("Available types:"); - ADD_DESCRIPTION(" 0: Trace"); - ADD_DESCRIPTION(" 1: Debug"); - ADD_DESCRIPTION(" 2: Info"); - ADD_DESCRIPTION(" 3: Warn"); - ADD_DESCRIPTION(" 4: Error"); - ADD_DESCRIPTION(" 5: Critical"); - ADD_DESCRIPTION(" 6: Off"); - } + { + BIND_GROUP(log) + { + CREATE_BINDING("level", 0); + BIND_INTEGRAL(config::log::logfileLevel, spdlog::level::debug, spdlog::level::trace, spdlog::level::off); + ADD_DESCRIPTION("The log level within the log files"); + ADD_DESCRIPTION("Available types:"); + ADD_DESCRIPTION(" 0: Trace"); + ADD_DESCRIPTION(" 1: Debug"); + ADD_DESCRIPTION(" 2: Info"); + ADD_DESCRIPTION(" 3: Warn"); + ADD_DESCRIPTION(" 4: Error"); + ADD_DESCRIPTION(" 5: Critical"); + ADD_DESCRIPTION(" 6: Off"); + } { CREATE_BINDING("terminal_level", 0); BIND_INTEGRAL(config::log::terminalLevel, spdlog::level::info, spdlog::level::trace, spdlog::level::off); @@ -963,57 +963,57 @@ std::deque> config::create_bindings() { ADD_DESCRIPTION(" 5: Critical"); ADD_DESCRIPTION(" 6: Off"); } - { - CREATE_BINDING("colored", 0); - BIND_BOOL(config::log::logfileColored, false); - ADD_DESCRIPTION("Disable/enable ascii codes within the log file"); - } - { - CREATE_BINDING("vs_size", 0); - BIND_INTEGRAL(config::log::vs_size, 0, 0, numeric_limits::max()); - ADD_DESCRIPTION("Virtual server log chunk size"); - } + { + CREATE_BINDING("colored", 0); + BIND_BOOL(config::log::logfileColored, false); + ADD_DESCRIPTION("Disable/enable ascii codes within the log file"); + } + { + CREATE_BINDING("vs_size", 0); + BIND_INTEGRAL(config::log::vs_size, 0, 0, numeric_limits::max()); + ADD_DESCRIPTION("Virtual server log chunk size"); + } { CREATE_BINDING("path", 0); BIND_STRING(config::log::path, "logs/log_${time}(%Y-%m-%d_%H:%M:%S)_${group}.log"); ADD_DESCRIPTION("The log file path"); } - } - { - BIND_GROUP(binding); - { - BIND_GROUP(voice); - { - CREATE_BINDING("default_host", 0); - BIND_STRING(config::binding::DefaultVoiceHost, "0.0.0.0,::"); - ADD_NOTE("Multibinding supported here! Host delimiter is \",\""); - } - { - CREATE_BINDING("enforce", 0); - BIND_BOOL(config::binding::enforce_default_voice_host, false); - ADD_NOTE("Enforce the default host for every virtual server. Ignoring the server specific host"); - } - } - { - BIND_GROUP(web); - { - CREATE_BINDING("default_host", 0); - BIND_STRING(config::binding::DefaultWebHost, "0.0.0.0,[::]"); - ADD_NOTE("Multibinding supported here! Host delimiter is \",\""); - } - } - { - BIND_GROUP(query); - { - CREATE_BINDING("port", 0); - BIND_INTEGRAL(config::binding::DefaultQueryPort, 10101, 1, 65535); - } + } + { + BIND_GROUP(binding); + { + BIND_GROUP(voice); + { + CREATE_BINDING("default_host", 0); + BIND_STRING(config::binding::DefaultVoiceHost, "0.0.0.0,::"); + ADD_NOTE("Multibinding supported here! Host delimiter is \",\""); + } + { + CREATE_BINDING("enforce", 0); + BIND_BOOL(config::binding::enforce_default_voice_host, false); + ADD_NOTE("Enforce the default host for every virtual server. Ignoring the server specific host"); + } + } + { + BIND_GROUP(web); + { + CREATE_BINDING("default_host", 0); + BIND_STRING(config::binding::DefaultWebHost, "0.0.0.0,[::]"); + ADD_NOTE("Multibinding supported here! Host delimiter is \",\""); + } + } + { + BIND_GROUP(query); + { + CREATE_BINDING("port", 0); + BIND_INTEGRAL(config::binding::DefaultQueryPort, 10101, 1, 65535); + } { CREATE_BINDING("host", 0); BIND_STRING(config::binding::DefaultQueryHost, "0.0.0.0,[::]"); - ADD_NOTE("Multibinding supported here! Host delimiter is \",\""); + ADD_NOTE("Multibinding supported here! Host delimiter is \",\""); } - } + } { BIND_GROUP(file); { @@ -1023,10 +1023,10 @@ std::deque> config::create_bindings() { { CREATE_BINDING("host", 0); BIND_STRING(config::binding::DefaultFileHost, "0.0.0.0,[::]"); - ADD_NOTE("Multibinding supported here! Host delimiter is \",\""); + ADD_NOTE("Multibinding supported here! Host delimiter is \",\""); } } - } + } { BIND_GROUP(query); { @@ -1042,7 +1042,7 @@ std::deque> config::create_bindings() { ADD_NOTE("If not like TeamSpeak then some applications may not recognize the Query"); ADD_NOTE("Default TeamSpeak 3 MOTD:"); ADD_NOTE("TS3\r\nWelcome to the TeamSpeak 3 ServerQuery interface, type \"help\" for a list of commands and \"help \" for information on a specific command.\r\n"); - ADD_NOTE("NOTE: Sometimes you have to append one \r\n more!"); + ADD_NOTE("NOTE: Sometimes you have to append one \r\n more!"); } { CREATE_BINDING("enableSSL", 0); @@ -1087,12 +1087,12 @@ std::deque> config::create_bindings() { BIND_BOOL(config::voice::suppress_myts_warnings, true); ADD_DESCRIPTION("Suppress the MyTS integration warnings"); } - { - CREATE_BINDING("allow_session_reinitialize", 0); - BIND_BOOL(config::voice::allow_session_reinitialize, true); - ADD_DESCRIPTION("Enable/disable fast session reinitialisation."); - ADD_SENSITIVE(); - } + { + CREATE_BINDING("allow_session_reinitialize", 0); + BIND_BOOL(config::voice::allow_session_reinitialize, true); + ADD_DESCRIPTION("Enable/disable fast session reinitialisation."); + ADD_SENSITIVE(); + } { CREATE_BINDING("rsa.puzzle_pool_size", 0); BIND_INTEGRAL(config::voice::DefaultPuzzlePrecomputeSize, 128, 1, 65536); @@ -1114,12 +1114,12 @@ std::deque> config::create_bindings() { ADD_NOTE("This option is highly recommended!"); ADD_SENSITIVE(); } - { - CREATE_BINDING("warn_on_permission_editor", 0); - BIND_BOOL(config::voice::warn_on_permission_editor, true); - ADD_DESCRIPTION("Enables/disabled the warning popup for the TeamSpeak 3 permission editor."); - ADD_NOTE("This option is highly recommended!"); - } + { + CREATE_BINDING("warn_on_permission_editor", 0); + BIND_BOOL(config::voice::warn_on_permission_editor, true); + ADD_DESCRIPTION("Enables/disabled the warning popup for the TeamSpeak 3 permission editor."); + ADD_NOTE("This option is highly recommended!"); + } } { CREATE_BINDING("connect_limit", 0); @@ -1148,14 +1148,14 @@ std::deque> config::create_bindings() { BIND_STRING(config::server::DefaultServerPlatform, strobf("Linux").string()); ADD_DESCRIPTION("The displayed platform to the client"); ADD_NOTE("This option is only for the premium version."); - ADD_NOTE_RELOADABLE(); + ADD_NOTE_RELOADABLE(); } { CREATE_BINDING("version", PREMIUM_ONLY | FLAG_RELOADABLE); BIND_STRING(config::server::DefaultServerVersion, strobf("TeaSpeak ").string() + build::version()->string(true)); ADD_DESCRIPTION("The displayed version to the client"); - ADD_NOTE("This option is only for the premium version."); - ADD_NOTE_RELOADABLE(); + ADD_NOTE("This option is only for the premium version."); + ADD_NOTE_RELOADABLE(); } { CREATE_BINDING("licence", PREMIUM_ONLY | FLAG_RELOADABLE); @@ -1171,8 +1171,8 @@ std::deque> config::create_bindings() { ADD_DESCRIPTION(" 6: Auto-License (Server based)"); ADD_DESCRIPTION(" 7: Auto-License (Instance based)"); ADD_NOTE("This option just work for non 3.2 clients!"); - ADD_NOTE("This option is only for the premium version."); - ADD_NOTE_RELOADABLE(); + ADD_NOTE("This option is only for the premium version."); + ADD_NOTE_RELOADABLE(); } { CREATE_BINDING("delete_old_bans", 0); @@ -1184,57 +1184,57 @@ std::deque> config::create_bindings() { BIND_BOOL(config::server::delete_missing_icon_permissions, true); ADD_DESCRIPTION("Enable/disable the deletion of invalid icon id permissions"); } - { - CREATE_BINDING("allow_weblist", 0); - BIND_BOOL(config::server::enable_teamspeak_weblist, true); - ADD_DESCRIPTION("Enable/disable weblist reports globally! (Server setting wount be disabled, they will be just not send)"); - } - { - CREATE_BINDING("strict_ut8_mode", 0); - BIND_BOOL(config::server::strict_ut8_mode, false); - ADD_DESCRIPTION("If enabled an error will be throws on invalid UTF-8 characters within the protocol (Query & Client)."); - ADD_DESCRIPTION("Else the property pair will be dropped silently!"); - } - { - CREATE_BINDING("show_invisible_clients", 0); - BIND_BOOL(config::server::show_invisible_clients_as_online, true); - ADD_DESCRIPTION("Allow anybody to send text messages to clients which are in invisible channels"); - } - { - CREATE_BINDING("disable_ip_saving", 0); - BIND_BOOL(config::server::disable_ip_saving, false); - ADD_DESCRIPTION("Disable the saving of IP addresses within the database"); - } - { - CREATE_BINDING("max_virtual_servers", FLAG_RELOADABLE); - BIND_INTEGRAL(config::server::max_virtual_server, 16, -1, 999999); - ADD_DESCRIPTION("Set the limit for maximal virtual servers. -1 means unlimited."); - ADD_NOTE_RELOADABLE(); - } - { - /* - BIND_GROUP(badges); - { - CREATE_BINDING("badges", 0); - BIND_BOOL(config::server::badges::allow_badges, true); - ADD_DESCRIPTION("Allow or disallow TeamSpeak badges"); - } - { - CREATE_BINDING("overwolf", 0); - BIND_BOOL(config::server::badges::allow_overwolf, true); - ADD_DESCRIPTION("Allow or disallow the Overwolf badge"); - } - */ - } - { - BIND_GROUP(authentication); - { - CREATE_BINDING("name", FLAG_RELOADABLE); - BIND_BOOL(config::server::authentication::name, false); - ADD_DESCRIPTION("Allow or disallow client authentication just by their name"); - ADD_NOTE_RELOADABLE(); - } - } + { + CREATE_BINDING("allow_weblist", 0); + BIND_BOOL(config::server::enable_teamspeak_weblist, true); + ADD_DESCRIPTION("Enable/disable weblist reports globally! (Server setting wount be disabled, they will be just not send)"); + } + { + CREATE_BINDING("strict_ut8_mode", 0); + BIND_BOOL(config::server::strict_ut8_mode, false); + ADD_DESCRIPTION("If enabled an error will be throws on invalid UTF-8 characters within the protocol (Query & Client)."); + ADD_DESCRIPTION("Else the property pair will be dropped silently!"); + } + { + CREATE_BINDING("show_invisible_clients", 0); + BIND_BOOL(config::server::show_invisible_clients_as_online, true); + ADD_DESCRIPTION("Allow anybody to send text messages to clients which are in invisible channels"); + } + { + CREATE_BINDING("disable_ip_saving", 0); + BIND_BOOL(config::server::disable_ip_saving, false); + ADD_DESCRIPTION("Disable the saving of IP addresses within the database"); + } + { + CREATE_BINDING("max_virtual_servers", FLAG_RELOADABLE); + BIND_INTEGRAL(config::server::max_virtual_server, 16, -1, 999999); + ADD_DESCRIPTION("Set the limit for maximal virtual servers. -1 means unlimited."); + ADD_NOTE_RELOADABLE(); + } + { + /* + BIND_GROUP(badges); + { + CREATE_BINDING("badges", 0); + BIND_BOOL(config::server::badges::allow_badges, true); + ADD_DESCRIPTION("Allow or disallow TeamSpeak badges"); + } + { + CREATE_BINDING("overwolf", 0); + BIND_BOOL(config::server::badges::allow_overwolf, true); + ADD_DESCRIPTION("Allow or disallow the Overwolf badge"); + } + */ + } + { + BIND_GROUP(authentication); + { + CREATE_BINDING("name", FLAG_RELOADABLE); + BIND_BOOL(config::server::authentication::name, false); + ADD_DESCRIPTION("Allow or disallow client authentication just by their name"); + ADD_NOTE_RELOADABLE(); + } + } } { BIND_GROUP(web); @@ -1244,58 +1244,58 @@ std::deque> config::create_bindings() { ADD_DESCRIPTION("Disable/enable the possibility to connect via the TeaSpeak web client"); ADD_NOTE("If you've disabled this feature the TeaClient wound be able to join too."); } - { - CREATE_BINDING("upnp", 0); - BIND_BOOL(config::web::enable_upnp, false); - ADD_DESCRIPTION("Disable/enable UPNP support"); - ADD_SENSITIVE(); - } + { + CREATE_BINDING("upnp", 0); + BIND_BOOL(config::web::enable_upnp, false); + ADD_DESCRIPTION("Disable/enable UPNP support"); + ADD_SENSITIVE(); + } { BIND_GROUP(ssl) - { - CREATE_BINDING("certificate", FLAG_RELOADABLE); - ADD_NOTE_RELOADABLE(); - binding->type = 4; + { + CREATE_BINDING("certificate", FLAG_RELOADABLE); + ADD_NOTE_RELOADABLE(); + binding->type = 4; - /* no terminal handling */ - binding->read_argument = [](const std::string&) { - logError(LOG_GENERAL, "Failed to parse ssl certificate. Its only possible to configure them via config!"); - }; - binding->default_value = []() -> deque { return {}; }; + /* no terminal handling */ + binding->read_argument = [](const std::string&) { + logError(LOG_GENERAL, "Failed to parse ssl certificate. Its only possible to configure them via config!"); + }; + binding->default_value = []() -> deque { return {}; }; - //Unused :) - binding->set_default = [](YAML::Node& node) { - auto default_node = node["default"]; - default_node["certificate"] = "default_certificate.pem"; - default_node["private_key"] = "default_privatekey.pem"; - }; + //Unused :) + binding->set_default = [](YAML::Node& node) { + auto default_node = node["default"]; + default_node["certificate"] = "default_certificate.pem"; + default_node["private_key"] = "default_privatekey.pem"; + }; - weak_ptr _binding = binding; - binding->read_config = [_binding](YAML::Node& node) { - auto b = _binding.lock(); - if(!b) return; - config::web::ssl::certificates.clear(); + weak_ptr _binding = binding; + binding->read_config = [_binding](YAML::Node& node) { + auto b = _binding.lock(); + if(!b) return; + config::web::ssl::certificates.clear(); - if(!node.IsDefined() || node.IsNull()) - return; + if(!node.IsDefined() || node.IsNull()) + return; - for(auto it = node.begin(); it != node.end(); it++) { - auto node_cert = it->second["certificate"]; - auto node_key = it->second["private_key"]; - if(!node_cert.IsDefined()) { - logError(LOG_GENERAL, "Failed to parse web certificate. Missing \"certificate\" key."); - continue; - } - if(!node_key.IsDefined()) { - logError(LOG_GENERAL, "Failed to parse web certificate. Missing \"private_key\" key."); - continue; - } + for(auto it = node.begin(); it != node.end(); it++) { + auto node_cert = it->second["certificate"]; + auto node_key = it->second["private_key"]; + if(!node_cert.IsDefined()) { + logError(LOG_GENERAL, "Failed to parse web certificate. Missing \"certificate\" key."); + continue; + } + if(!node_key.IsDefined()) { + logError(LOG_GENERAL, "Failed to parse web certificate. Missing \"private_key\" key."); + continue; + } - config::web::ssl::certificates.emplace_back(it->first.as(), node_key.as(), node_cert.as()); - } - }; - } - /* + config::web::ssl::certificates.emplace_back(it->first.as(), node_key.as(), node_cert.as()); + } + }; + } + /* { CREATE_BINDING("certificate", 0); BIND_STRING(config::web::ssl::certFile, "certs/default_certificate.pem"); @@ -1309,25 +1309,25 @@ std::deque> config::create_bindings() { */ } - { - CREATE_BINDING("webrtc.port_min", 0); - BIND_INTEGRAL(config::web::webrtc_port_min, 50000, 0, 65535); - ADD_DESCRIPTION("Define the port range within the web client and TeaClient operates in"); - ADD_DESCRIPTION("A port of zero stands for no limit"); - ADD_NOTE("These ports must opened to use the voice bridge (Protocol: UDP)"); - } - { - CREATE_BINDING("webrtc.port_max", 0); - BIND_INTEGRAL(config::web::webrtc_port_max, 56000, 0, 65535); - ADD_DESCRIPTION("Define the port range within the web client and TeaClient operates in"); - ADD_DESCRIPTION("A port of zero stands for no limit"); - ADD_NOTE("These ports must opened to use the voice bridge (Protocol: UDP)"); - } - { - CREATE_BINDING("webrtc.ice", 0); - BIND_VECTOR(config::web::ice_servers, {"stun.l.google.com:19302"}); - ADD_DESCRIPTION("A list of possible offered ice servers"); - } + { + CREATE_BINDING("webrtc.port_min", 0); + BIND_INTEGRAL(config::web::webrtc_port_min, 50000, 0, 65535); + ADD_DESCRIPTION("Define the port range within the web client and TeaClient operates in"); + ADD_DESCRIPTION("A port of zero stands for no limit"); + ADD_NOTE("These ports must opened to use the voice bridge (Protocol: UDP)"); + } + { + CREATE_BINDING("webrtc.port_max", 0); + BIND_INTEGRAL(config::web::webrtc_port_max, 56000, 0, 65535); + ADD_DESCRIPTION("Define the port range within the web client and TeaClient operates in"); + ADD_DESCRIPTION("A port of zero stands for no limit"); + ADD_NOTE("These ports must opened to use the voice bridge (Protocol: UDP)"); + } + { + CREATE_BINDING("webrtc.ice", 0); + BIND_VECTOR(config::web::ice_servers, {"stun.l.google.com:19302"}); + ADD_DESCRIPTION("A list of possible offered ice servers"); + } } { BIND_GROUP(geolocation); @@ -1335,14 +1335,14 @@ std::deque> config::create_bindings() { CREATE_BINDING("fallback_country", FLAG_RELOADABLE); BIND_STRING(config::geo::countryFlag, "DE"); ADD_DESCRIPTION("The fallback country if lookup fails"); - ADD_NOTE_RELOADABLE(); + ADD_NOTE_RELOADABLE(); + } + { + CREATE_BINDING("force_fallback_country", FLAG_RELOADABLE); + BIND_BOOL(config::geo::staticFlag, false); + ADD_DESCRIPTION("Enforce the default country and disable resolve"); + ADD_NOTE_RELOADABLE(); } - { - CREATE_BINDING("force_fallback_country", FLAG_RELOADABLE); - BIND_BOOL(config::geo::staticFlag, false); - ADD_DESCRIPTION("Enforce the default country and disable resolve"); - ADD_NOTE_RELOADABLE(); - } { CREATE_BINDING("mapping.file", 0); @@ -1385,39 +1385,39 @@ std::deque> config::create_bindings() { { CREATE_BINDING("voice.server_stop", FLAG_RELOADABLE); BIND_STRING(config::messages::serverStopped, "Server stopped"); - ADD_NOTE_RELOADABLE(); + ADD_NOTE_RELOADABLE(); } { CREATE_BINDING("application.stop", FLAG_RELOADABLE); BIND_STRING(config::messages::applicationStopped, "Application stopped"); - ADD_NOTE_RELOADABLE(); + ADD_NOTE_RELOADABLE(); } { CREATE_BINDING("application.crash", FLAG_RELOADABLE); BIND_STRING(config::messages::applicationCrashed, "Application crashed"); - ADD_NOTE_RELOADABLE(); + ADD_NOTE_RELOADABLE(); } { CREATE_BINDING("idle_time", FLAG_RELOADABLE); BIND_STRING(config::messages::idle_time_exceeded, "Idle time exceeded"); - ADD_NOTE_RELOADABLE(); + ADD_NOTE_RELOADABLE(); + } + { + CREATE_BINDING("teamspeak_permission_editor", FLAG_RELOADABLE); + BIND_STRING(config::messages::teamspeak_permission_editor, "\n[b][COLOR=#aa0000]ATTENTION[/COLOR][/b]:\nIt seems like you're trying to edit the TeaSpeak permissions with the TeamSpeak 3 client!\nThis is [b]really[/b] buggy due a bug within the client you're using.\n\nWe recommand to [b]use the [url=https://web.teaspeak.de/]TeaSpeak-Web[/url][/b] client or the [b][url=https://teaspeak.de/]TeaSpeak client[/url][/b].\nYatQA is a good option as well.\n\nTo disable/edit this message please edit the config.yml\nPlease note: Permission bugs, which will be reported wound be accepted."); + ADD_NOTE_RELOADABLE(); } - { - CREATE_BINDING("teamspeak_permission_editor", FLAG_RELOADABLE); - BIND_STRING(config::messages::teamspeak_permission_editor, "\n[b][COLOR=#aa0000]ATTENTION[/COLOR][/b]:\nIt seems like you're trying to edit the TeaSpeak permissions with the TeamSpeak 3 client!\nThis is [b]really[/b] buggy due a bug within the client you're using.\n\nWe recommand to [b]use the [url=https://web.teaspeak.de/]TeaSpeak-Web[/url][/b] client or the [b][url=https://teaspeak.de/]TeaSpeak client[/url][/b].\nYatQA is a good option as well.\n\nTo disable/edit this message please edit the config.yml\nPlease note: Permission bugs, which will be reported wound be accepted."); - ADD_NOTE_RELOADABLE(); - } { BIND_GROUP(mute); { CREATE_BINDING("mute_message", FLAG_RELOADABLE); BIND_STRING(config::messages::mute_notify_message, "Hey!\nI muted you!"); - ADD_NOTE_RELOADABLE(); + ADD_NOTE_RELOADABLE(); } { CREATE_BINDING("unmute_message", FLAG_RELOADABLE); BIND_STRING(config::messages::unmute_notify_message, "Hey!\nI unmuted you!"); - ADD_NOTE_RELOADABLE(); + ADD_NOTE_RELOADABLE(); } } { @@ -1425,17 +1425,17 @@ std::deque> config::create_bindings() { { CREATE_BINDING("hardware_id", FLAG_RELOADABLE); BIND_STRING(config::messages::kick_invalid_hardware_id, "Invalid hardware id. Protocol hacked?"); - ADD_NOTE_RELOADABLE(); + ADD_NOTE_RELOADABLE(); } { CREATE_BINDING("command", FLAG_RELOADABLE); BIND_STRING(config::messages::kick_invalid_hardware_id, "Invalid command. Protocol hacked?"); - ADD_NOTE_RELOADABLE(); + ADD_NOTE_RELOADABLE(); } { CREATE_BINDING("badges", FLAG_RELOADABLE); BIND_STRING(config::messages::kick_invalid_hardware_id, "Invalid badges. Protocol hacked?"); - ADD_NOTE_RELOADABLE(); + ADD_NOTE_RELOADABLE(); } } { @@ -1447,109 +1447,109 @@ std::deque> config::create_bindings() { ADD_DESCRIPTION(" - provider.name => Contains the provider of the ip which has been flaged as vps"); ADD_DESCRIPTION(" - provider.website => Contains the website provider of the ip which has been flaged as vps"); - ADD_NOTE_RELOADABLE(); + ADD_NOTE_RELOADABLE(); } - { - BIND_GROUP(shutdown); - { - CREATE_BINDING("scheduled", FLAG_RELOADABLE); - BIND_STRING(config::messages::shutdown::scheduled, "[b][color=#DA9100]Scheduled shutdown at ${time}(%Y-%m-%d %H:%M:%S)[/color][/b]"); - ADD_NOTE_RELOADABLE(); - } - { - CREATE_BINDING("interval", FLAG_RELOADABLE); - BIND_STRING(config::messages::shutdown::interval, "[b][color=red]Server instance shutting down in ${interval}[/color][/b]"); - ADD_DESCRIPTION("${interval} is defined via map in 'intervals'"); - ADD_NOTE_RELOADABLE(); - } - { - CREATE_BINDING("intervals", 0); - binding->default_value = []() { return deque{}; }; - binding->type = 4; + { + BIND_GROUP(shutdown); + { + CREATE_BINDING("scheduled", FLAG_RELOADABLE); + BIND_STRING(config::messages::shutdown::scheduled, "[b][color=#DA9100]Scheduled shutdown at ${time}(%Y-%m-%d %H:%M:%S)[/color][/b]"); + ADD_NOTE_RELOADABLE(); + } + { + CREATE_BINDING("interval", FLAG_RELOADABLE); + BIND_STRING(config::messages::shutdown::interval, "[b][color=red]Server instance shutting down in ${interval}[/color][/b]"); + ADD_DESCRIPTION("${interval} is defined via map in 'intervals'"); + ADD_NOTE_RELOADABLE(); + } + { + CREATE_BINDING("intervals", 0); + binding->default_value = []() { return deque{}; }; + binding->type = 4; - weak_ptr weak_binding = binding; - binding->read_config = [weak_binding](YAML::Node& node) { - auto bind = weak_binding.lock(); - if(!bind) return; + weak_ptr weak_binding = binding; + binding->read_config = [weak_binding](YAML::Node& node) { + auto bind = weak_binding.lock(); + if(!bind) return; - if(node.IsNull() || !node.IsDefined() || !node.IsMap()) - bind->set_default(node); + if(node.IsNull() || !node.IsDefined() || !node.IsMap()) + bind->set_default(node); - try { - auto intervals = node.as>(); - for(const auto& pair : intervals) - config::messages::shutdown::intervals.push_back({seconds(pair.first), pair.second}); - } catch(YAML::Exception& ex) { - logError(LOG_GENERAL, "Failed to parse shutdown intervals! Exception: {}", ex.what()); - } - }; + try { + auto intervals = node.as>(); + for(const auto& pair : intervals) + config::messages::shutdown::intervals.push_back({seconds(pair.first), pair.second}); + } catch(YAML::Exception& ex) { + logError(LOG_GENERAL, "Failed to parse shutdown intervals! Exception: {}", ex.what()); + } + }; - binding->set_default = [weak_binding](YAML::Node& node) { - auto bind = weak_binding.lock(); - if(!bind) return; + binding->set_default = [weak_binding](YAML::Node& node) { + auto bind = weak_binding.lock(); + if(!bind) return; - node = YAML::Node(); + node = YAML::Node(); - const static vector> intervals = { - {1, "1 second"}, - {2, "2 seconds"}, - {3, "3 seconds"}, - {4, "4 seconds"}, - {5, "5 seconds"}, - {10, "10 seconds"}, - {20, "20 seconds"}, - {30, "30 seconds"}, - {60, "1 minute"}, - {2 * 60, "2 minutes"}, - {3 * 60, "3 minutes"}, - {5 * 60, "5 minutes"}, - {10 * 60, "10 minutes"}, - {20 * 60, "20 minutes"}, - {30 * 60, "30 minutes"}, - {60 * 60, "1 hour"}, - {2 * 60 * 60, "2 hours"}, - }; + const static vector> intervals = { + {1, "1 second"}, + {2, "2 seconds"}, + {3, "3 seconds"}, + {4, "4 seconds"}, + {5, "5 seconds"}, + {10, "10 seconds"}, + {20, "20 seconds"}, + {30, "30 seconds"}, + {60, "1 minute"}, + {2 * 60, "2 minutes"}, + {3 * 60, "3 minutes"}, + {5 * 60, "5 minutes"}, + {10 * 60, "10 minutes"}, + {20 * 60, "20 minutes"}, + {30 * 60, "30 minutes"}, + {60 * 60, "1 hour"}, + {2 * 60 * 60, "2 hours"}, + }; - for(const auto& pair : intervals) - node[to_string(pair.first)] = pair.second; - }; - ADD_DESCRIPTION("Add or delete intervals as you want"); - } - { - CREATE_BINDING("now", FLAG_RELOADABLE); - BIND_STRING(config::messages::shutdown::now, "[b][color=red]Server instance shutting down in now[/color][/b]"); - ADD_NOTE_RELOADABLE(); - } - { - CREATE_BINDING("canceled", FLAG_RELOADABLE); - BIND_STRING(config::messages::shutdown::canceled, "[b][color=green]Scheduled instance shutdown canceled![/color][/b]"); - ADD_NOTE_RELOADABLE(); - } - } - { - BIND_GROUP(music); - { - CREATE_BINDING("song_announcement", FLAG_RELOADABLE); - BIND_STRING(config::messages::music::song_announcement, "Now replaying ${title} (${url}) added by ${invoker}"); - ADD_DESCRIPTION("${title} title of the song"); - ADD_DESCRIPTION("${description} description of the song"); - ADD_DESCRIPTION("${url} url of the song"); - ADD_DESCRIPTION("${invoker} link to the song adder"); - } - } - { - BIND_GROUP(timeout); - { - CREATE_BINDING("connection_reinitialized", FLAG_RELOADABLE); - BIND_STRING(config::messages::timeout::connection_reinitialized, "Connection lost"); - ADD_NOTE_RELOADABLE(); - } - { - CREATE_BINDING("packet_resend_failed", FLAG_RELOADABLE); - BIND_STRING(config::messages::timeout::packet_resend_failed, "Packet resend failed"); - ADD_NOTE_RELOADABLE(); - } - } + for(const auto& pair : intervals) + node[to_string(pair.first)] = pair.second; + }; + ADD_DESCRIPTION("Add or delete intervals as you want"); + } + { + CREATE_BINDING("now", FLAG_RELOADABLE); + BIND_STRING(config::messages::shutdown::now, "[b][color=red]Server instance shutting down in now[/color][/b]"); + ADD_NOTE_RELOADABLE(); + } + { + CREATE_BINDING("canceled", FLAG_RELOADABLE); + BIND_STRING(config::messages::shutdown::canceled, "[b][color=green]Scheduled instance shutdown canceled![/color][/b]"); + ADD_NOTE_RELOADABLE(); + } + } + { + BIND_GROUP(music); + { + CREATE_BINDING("song_announcement", FLAG_RELOADABLE); + BIND_STRING(config::messages::music::song_announcement, "Now replaying ${title} (${url}) added by ${invoker}"); + ADD_DESCRIPTION("${title} title of the song"); + ADD_DESCRIPTION("${description} description of the song"); + ADD_DESCRIPTION("${url} url of the song"); + ADD_DESCRIPTION("${invoker} link to the song adder"); + } + } + { + BIND_GROUP(timeout); + { + CREATE_BINDING("connection_reinitialized", FLAG_RELOADABLE); + BIND_STRING(config::messages::timeout::connection_reinitialized, "Connection lost"); + ADD_NOTE_RELOADABLE(); + } + { + CREATE_BINDING("packet_resend_failed", FLAG_RELOADABLE); + BIND_STRING(config::messages::timeout::packet_resend_failed, "Packet resend failed"); + ADD_NOTE_RELOADABLE(); + } + } } { BIND_GROUP(threads); @@ -1618,12 +1618,12 @@ std::deque> config::create_bindings() { ADD_DESCRIPTION("Max IO threads"); ADD_SENSITIVE(); } - { - CREATE_BINDING("bind_io_thread_to_kernel_thread", 0); - BIND_BOOL(config::threads::voice::bind_io_thread_to_kernel_thread, false); - ADD_DESCRIPTION("Bind each IO thread to one kernel thread to improve socket IO performance"); - ADD_SENSITIVE(); - } + { + CREATE_BINDING("bind_io_thread_to_kernel_thread", 0); + BIND_BOOL(config::threads::voice::bind_io_thread_to_kernel_thread, false); + ADD_DESCRIPTION("Bind each IO thread to one kernel thread to improve socket IO performance"); + ADD_SENSITIVE(); + } } } return _create_bindings = result; diff --git a/server/src/Configuration.h b/server/src/Configuration.h index f941d8c..094ed34 100644 --- a/server/src/Configuration.h +++ b/server/src/Configuration.h @@ -11,48 +11,48 @@ #include "../../license/shared/License.h" namespace YAML { - class Node; + class Node; } namespace ts { namespace config { struct EntryBinding { std::string key; std::map> description; - uint8_t flags = 0; + uint8_t flags = 0; - int type = 0; /* 0 = unbound | 1 = string | 2 = number | 3 = boolean | 4 = user defined */ + int type = 0; /* 0 = unbound | 1 = string | 2 = number | 3 = boolean | 4 = user defined */ int bounded_by = 0; /* 0 = unbound | 1 = config | 2 = command line */ std::function()> default_value; std::function value_description; - std::function set_default; + std::function set_default; std::function read_config; std::function read_argument; }; extern std::vector parseConfig(const std::string& /* path */); - extern std::vector reload(); + extern std::vector reload(); extern std::deque> create_bindings(); namespace database { - extern std::string url; + extern std::string url; namespace sqlite { - extern std::string journal_mode; - extern std::string locking_mode; - extern std::string sync_mode; + extern std::string journal_mode; + extern std::string locking_mode; + extern std::string sync_mode; } } extern std::shared_ptr license; - extern std::shared_ptr license_original; + extern std::shared_ptr license_original; extern bool experimental_31; extern std::string permission_mapping_file; namespace binding { - extern bool enforce_default_voice_host; - extern std::string DefaultVoiceHost; - extern std::string DefaultWebHost; + extern bool enforce_default_voice_host; + extern std::string DefaultVoiceHost; + extern std::string DefaultWebHost; extern std::string DefaultQueryHost; extern std::string DefaultFileHost; @@ -69,20 +69,20 @@ namespace ts { extern LicenseType DefaultServerLicense; - extern bool strict_ut8_mode; + extern bool strict_ut8_mode; extern bool enable_teamspeak_weblist; extern bool show_invisible_clients_as_online; extern bool disable_ip_saving; - namespace badges { - extern bool allow_overwolf; - extern bool allow_badges; - } + namespace badges { + extern bool allow_overwolf; + extern bool allow_badges; + } - namespace authentication { - extern bool name; - } + namespace authentication { + extern bool name; + } extern ssize_t max_virtual_server; } @@ -98,10 +98,10 @@ namespace ts { extern int clientConnectLimit; extern bool suppress_myts_warnings; - extern uint16_t default_voice_port; + extern uint16_t default_voice_port; - extern bool warn_on_permission_editor; - extern bool allow_session_reinitialize; + extern bool warn_on_permission_editor; + extern bool allow_session_reinitialize; } namespace geo { @@ -119,7 +119,7 @@ namespace ts { extern std::string motd; extern std::string newlineCharacter; - extern int sslMode; + extern int sslMode; namespace ssl { extern std::string keyFile; extern std::string certFile; @@ -147,24 +147,24 @@ namespace ts { extern std::string kick_vpn; - extern std::string teamspeak_permission_editor; + extern std::string teamspeak_permission_editor; namespace shutdown { - extern std::string scheduled; - extern std::string interval; - extern std::string now; - extern std::string canceled; + extern std::string scheduled; + extern std::string interval; + extern std::string now; + extern std::string canceled; - extern std::vector> intervals; + extern std::vector> intervals; } namespace music { - extern std::string song_announcement; + extern std::string song_announcement; } namespace timeout { - extern std::string packet_resend_failed; - extern std::string connection_reinitialized; + extern std::string packet_resend_failed; + extern std::string connection_reinitialized; } } @@ -172,14 +172,14 @@ namespace ts { extern bool activated; namespace ssl { - /* servername; private key file; public key file*/ - extern std::deque> certificates; + /* servername; private key file; public key file*/ + extern std::deque> certificates; } extern uint16_t webrtc_port_min; - extern uint16_t webrtc_port_max; - extern std::deque ice_servers; - extern bool enable_upnp; + extern uint16_t webrtc_port_max; + extern std::deque ice_servers; + extern bool enable_upnp; } namespace threads { @@ -194,7 +194,7 @@ namespace ts { extern size_t io_per_server; extern size_t io_limit; - extern bool bind_io_thread_to_kernel_thread; + extern bool bind_io_thread_to_kernel_thread; } namespace music { diff --git a/server/src/ConnectionStatistics.cpp b/server/src/ConnectionStatistics.cpp index 3eb64db..536d044 100644 --- a/server/src/ConnectionStatistics.cpp +++ b/server/src/ConnectionStatistics.cpp @@ -14,14 +14,14 @@ using namespace ts::stats; using namespace ts::protocol; ConnectionStatistics::ConnectionStatistics(const shared_ptr& handle, bool properties) : handle(handle) { - memtrack::allocated(this); + memtrack::allocated(this); - if(properties) { - this->properties = make_shared(); //TODO load etc? - this->properties->register_property_type(); - } + if(properties) { + this->properties = make_shared(); //TODO load etc? + this->properties->register_property_type(); + } - /* + /* this->properties->registerProperty("connection_packets_sent_speech", 0, PROP_STATISTIC); this->properties->registerProperty("connection_bytes_sent_speech", 0, PROP_STATISTIC); this->properties->registerProperty("connection_packets_received_speech", 0, PROP_STATISTIC); @@ -47,42 +47,42 @@ ConnectionStatistics::ConnectionStatistics(const shared_ptrproperties->registerProperty("connection_bandwidth_received_last_second_total", 0, PROP_STATISTIC); this->properties->registerProperty("connection_bandwidth_received_last_minute_total", 0, PROP_STATISTIC); - this->properties->registerProperty("connection_filetransfer_bandwidth_sent", 0, PROP_STATISTIC); - this->properties->registerProperty("connection_filetransfer_bandwidth_received", 0, PROP_STATISTIC); - this->properties->registerProperty("connection_filetransfer_bytes_sent_total", 0, PROP_STATISTIC); - this->properties->registerProperty("connection_filetransfer_bytes_received_total", 0, PROP_STATISTIC); - */ + this->properties->registerProperty("connection_filetransfer_bandwidth_sent", 0, PROP_STATISTIC); + this->properties->registerProperty("connection_filetransfer_bandwidth_received", 0, PROP_STATISTIC); + this->properties->registerProperty("connection_filetransfer_bytes_sent_total", 0, PROP_STATISTIC); + this->properties->registerProperty("connection_filetransfer_bytes_received_total", 0, PROP_STATISTIC); + */ } ConnectionStatistics::~ConnectionStatistics() { - memtrack::freed(this); + memtrack::freed(this); - { - lock_guard lock(this->history_lock_incoming); + { + lock_guard lock(this->history_lock_incoming); - for(auto entry : this->history_incoming) - if(entry->use_count.fetch_sub(1) == 1) - delete entry; - for(auto entry : this->history_file_incoming) - if(entry->use_count.fetch_sub(1) == 1) - delete entry; + for(auto entry : this->history_incoming) + if(entry->use_count.fetch_sub(1) == 1) + delete entry; + for(auto entry : this->history_file_incoming) + if(entry->use_count.fetch_sub(1) == 1) + delete entry; - this->history_incoming.clear(); - this->history_file_incoming.clear(); - } - { - lock_guard lock(this->history_lock_outgoing); + this->history_incoming.clear(); + this->history_file_incoming.clear(); + } + { + lock_guard lock(this->history_lock_outgoing); - for(auto entry : this->history_outgoing) - if(entry->use_count.fetch_sub(1) == 1) - delete entry; - for(auto entry : this->history_file_outgoing) - if(entry->use_count.fetch_sub(1) == 1) - delete entry; + for(auto entry : this->history_outgoing) + if(entry->use_count.fetch_sub(1) == 1) + delete entry; + for(auto entry : this->history_file_outgoing) + if(entry->use_count.fetch_sub(1) == 1) + delete entry; - this->history_outgoing.clear(); - this->history_file_outgoing.clear(); - } + this->history_outgoing.clear(); + this->history_file_outgoing.clear(); + } } std::shared_ptr ConnectionStatistics::statistics() { @@ -90,208 +90,208 @@ std::shared_ptr ConnectionStatistics::statistics() { } void ConnectionStatistics::logIncomingPacket(const category::value &category, size_t size) { - auto info_entry = new StatisticEntry{}; - info_entry->timestamp = system_clock::now(); - info_entry->size = uint16_t(size); + auto info_entry = new StatisticEntry{}; + info_entry->timestamp = system_clock::now(); + info_entry->size = uint16_t(size); - this->_log_incoming_packet(info_entry, category); + this->_log_incoming_packet(info_entry, category); } void ConnectionStatistics::_log_incoming_packet(ts::stats::StatisticEntry *info_entry, int8_t index) { - if(index >= 0 && index <= 3) { - this->connection_packets_received[index] ++; - this->connection_bytes_received[index] += info_entry->size; - } - this->connection_packets_received[0] ++; - this->connection_bytes_received[0] += info_entry->size; + if(index >= 0 && index <= 3) { + this->connection_packets_received[index] ++; + this->connection_bytes_received[index] += info_entry->size; + } + this->connection_packets_received[0] ++; + this->connection_bytes_received[0] += info_entry->size; - if(this->_measure_bandwidths) { - auto lock_count = info_entry->use_count++; - assert(lock_count >= 0); - (void) lock_count; + if(this->_measure_bandwidths) { + auto lock_count = info_entry->use_count++; + assert(lock_count >= 0); + (void) lock_count; - lock_guard lock(this->history_lock_incoming); - this->history_incoming.push_back(info_entry); - } - if(this->handle) - this->handle->_log_incoming_packet(info_entry, index); + lock_guard lock(this->history_lock_incoming); + this->history_incoming.push_back(info_entry); + } + if(this->handle) + this->handle->_log_incoming_packet(info_entry, index); } void ConnectionStatistics::logOutgoingPacket(const category::value &category, size_t size) { - auto info_entry = new StatisticEntry{}; - info_entry->timestamp = system_clock::now(); - info_entry->size = uint16_t(size); + auto info_entry = new StatisticEntry{}; + info_entry->timestamp = system_clock::now(); + info_entry->size = uint16_t(size); - this->_log_outgoing_packet(info_entry, category); + this->_log_outgoing_packet(info_entry, category); } void ConnectionStatistics::_log_outgoing_packet(ts::stats::StatisticEntry *info_entry, int8_t index) { - if(index >= 0 && index <= 3) { - this->connection_packets_sent[index] ++; - this->connection_bytes_sent[index] += info_entry->size; - } - this->connection_packets_sent[0] ++; - this->connection_bytes_sent[0] += info_entry->size; + if(index >= 0 && index <= 3) { + this->connection_packets_sent[index] ++; + this->connection_bytes_sent[index] += info_entry->size; + } + this->connection_packets_sent[0] ++; + this->connection_bytes_sent[0] += info_entry->size; - if(this->_measure_bandwidths) { - auto lock_count = info_entry->use_count++; - assert(lock_count >= 0); - (void) lock_count; + if(this->_measure_bandwidths) { + auto lock_count = info_entry->use_count++; + assert(lock_count >= 0); + (void) lock_count; - lock_guard lock(this->history_lock_outgoing); - this->history_outgoing.push_back(info_entry); - } - if(this->handle) - this->handle->_log_outgoing_packet(info_entry, index); + lock_guard lock(this->history_lock_outgoing); + this->history_outgoing.push_back(info_entry); + } + if(this->handle) + this->handle->_log_outgoing_packet(info_entry, index); } /* file transfer */ void ConnectionStatistics::logFileTransferIn(uint64_t bytes) { - auto info_entry = new StatisticEntry{}; - info_entry->timestamp = system_clock::now(); - info_entry->size = bytes; + auto info_entry = new StatisticEntry{}; + info_entry->timestamp = system_clock::now(); + info_entry->size = bytes; - this->_log_incoming_file_packet(info_entry); + this->_log_incoming_file_packet(info_entry); } void ConnectionStatistics::_log_incoming_file_packet(ts::stats::StatisticEntry *info_entry) { - this->file_bytes_received += info_entry->size; + this->file_bytes_received += info_entry->size; - if(this->_measure_bandwidths) { - auto lock_count = info_entry->use_count++; - assert(lock_count >= 0); - (void) lock_count; + if(this->_measure_bandwidths) { + auto lock_count = info_entry->use_count++; + assert(lock_count >= 0); + (void) lock_count; - lock_guard lock(this->history_lock_incoming); - this->history_file_incoming.push_back(info_entry); - } + lock_guard lock(this->history_lock_incoming); + this->history_file_incoming.push_back(info_entry); + } - if(this->handle) - this->handle->_log_incoming_file_packet(info_entry); + if(this->handle) + this->handle->_log_incoming_file_packet(info_entry); } void ConnectionStatistics::logFileTransferOut(uint64_t bytes) { - auto info_entry = new StatisticEntry{}; - info_entry->timestamp = system_clock::now(); - info_entry->size = bytes; + auto info_entry = new StatisticEntry{}; + info_entry->timestamp = system_clock::now(); + info_entry->size = bytes; - this->_log_outgoing_file_packet(info_entry); + this->_log_outgoing_file_packet(info_entry); } void ConnectionStatistics::_log_outgoing_file_packet(ts::stats::StatisticEntry *info_entry) { - this->file_bytes_sent += info_entry->size; + this->file_bytes_sent += info_entry->size; - if(this->_measure_bandwidths) { - auto lock_count = info_entry->use_count++; - assert(lock_count >= 0); - (void) lock_count; + if(this->_measure_bandwidths) { + auto lock_count = info_entry->use_count++; + assert(lock_count >= 0); + (void) lock_count; - lock_guard lock(this->history_lock_outgoing); - this->history_file_outgoing.push_back(info_entry); - } + lock_guard lock(this->history_lock_outgoing); + this->history_file_outgoing.push_back(info_entry); + } - if(this->handle) - this->handle->_log_outgoing_file_packet(info_entry); + if(this->handle) + this->handle->_log_outgoing_file_packet(info_entry); } void ConnectionStatistics::tick() { - StatisticEntry* entry; - { - auto timeout_min = system_clock::now() - minutes(1); + StatisticEntry* entry; + { + auto timeout_min = system_clock::now() - minutes(1); - lock_guard lock(this->history_lock_incoming); + lock_guard lock(this->history_lock_incoming); - while(!this->history_incoming.empty() && (entry = this->history_incoming[0])->timestamp < timeout_min) { - if(entry->use_count.fetch_sub(1) == 1) - delete entry; + while(!this->history_incoming.empty() && (entry = this->history_incoming[0])->timestamp < timeout_min) { + if(entry->use_count.fetch_sub(1) == 1) + delete entry; - this->history_incoming.pop_front(); - } + this->history_incoming.pop_front(); + } - while(!this->history_file_incoming.empty() && (entry = this->history_file_incoming[0])->timestamp < timeout_min) { - if(entry->use_count.fetch_sub(1) == 1) - delete entry; + while(!this->history_file_incoming.empty() && (entry = this->history_file_incoming[0])->timestamp < timeout_min) { + if(entry->use_count.fetch_sub(1) == 1) + delete entry; - this->history_file_incoming.pop_front(); - } - } - { - auto timeout_min = system_clock::now() - minutes(1); - lock_guard lock(this->history_lock_outgoing); + this->history_file_incoming.pop_front(); + } + } + { + auto timeout_min = system_clock::now() - minutes(1); + lock_guard lock(this->history_lock_outgoing); - while(!this->history_outgoing.empty() && (entry = this->history_outgoing[0])->timestamp < timeout_min) { - if(entry->use_count.fetch_sub(1) == 1) - delete entry; + while(!this->history_outgoing.empty() && (entry = this->history_outgoing[0])->timestamp < timeout_min) { + if(entry->use_count.fetch_sub(1) == 1) + delete entry; - this->history_outgoing.pop_front(); - } + this->history_outgoing.pop_front(); + } - while(!this->history_file_outgoing.empty() && (entry = this->history_file_outgoing[0])->timestamp < timeout_min) { - if(entry->use_count.fetch_sub(1) == 1) - delete entry; + while(!this->history_file_outgoing.empty() && (entry = this->history_file_outgoing[0])->timestamp < timeout_min) { + if(entry->use_count.fetch_sub(1) == 1) + delete entry; - this->history_file_outgoing.pop_front(); - } - } + this->history_file_outgoing.pop_front(); + } + } - if(this->properties) { - auto& _properties = *this->properties; + if(this->properties) { + auto& _properties = *this->properties; #define M(type, index) \ - _properties[property::CONNECTION_BYTES_SENT_ ##type] = (uint64_t) this->connection_bytes_sent[index]; \ - _properties[property::CONNECTION_PACKETS_SENT_ ##type] = (uint64_t) this->connection_packets_sent[index]; \ - _properties[property::CONNECTION_BYTES_RECEIVED_ ##type] = (uint64_t) this->connection_bytes_received[index]; \ - _properties[property::CONNECTION_PACKETS_RECEIVED_ ##type] = (uint64_t) this->connection_packets_received[index]; \ + _properties[property::CONNECTION_BYTES_SENT_ ##type] = (uint64_t) this->connection_bytes_sent[index]; \ + _properties[property::CONNECTION_PACKETS_SENT_ ##type] = (uint64_t) this->connection_packets_sent[index]; \ + _properties[property::CONNECTION_BYTES_RECEIVED_ ##type] = (uint64_t) this->connection_bytes_received[index]; \ + _properties[property::CONNECTION_PACKETS_RECEIVED_ ##type] = (uint64_t) this->connection_packets_received[index]; \ - M(TOTAL, 0); - M(CONTROL, 1); - M(KEEPALIVE, 2); - M(SPEECH, 3); + M(TOTAL, 0); + M(CONTROL, 1); + M(KEEPALIVE, 2); + M(SPEECH, 3); - _properties[property::CONNECTION_FILETRANSFER_BYTES_RECEIVED_TOTAL] = (uint64_t) this->file_bytes_received; - _properties[property::CONNECTION_FILETRANSFER_BYTES_SENT_TOTAL] = (uint64_t) this->file_bytes_sent; + _properties[property::CONNECTION_FILETRANSFER_BYTES_RECEIVED_TOTAL] = (uint64_t) this->file_bytes_received; + _properties[property::CONNECTION_FILETRANSFER_BYTES_SENT_TOTAL] = (uint64_t) this->file_bytes_sent; - _properties[property::CONNECTION_FILETRANSFER_BYTES_RECEIVED_TOTAL] = (uint64_t) this->file_bytes_received; - _properties[property::CONNECTION_FILETRANSFER_BYTES_SENT_TOTAL] = (uint64_t) this->file_bytes_sent; - } + _properties[property::CONNECTION_FILETRANSFER_BYTES_RECEIVED_TOTAL] = (uint64_t) this->file_bytes_received; + _properties[property::CONNECTION_FILETRANSFER_BYTES_SENT_TOTAL] = (uint64_t) this->file_bytes_sent; + } } DataSummery ConnectionStatistics::dataReport() { DataSummery report{}; - auto minTimeout = system_clock::now() - seconds(1); + auto minTimeout = system_clock::now() - seconds(1); - { - lock_guard lock(this->history_lock_incoming); + { + lock_guard lock(this->history_lock_incoming); - for(const auto& elm : this->history_incoming){ - if(elm->timestamp >= minTimeout) { - report.recv_second += elm->size; - } + for(const auto& elm : this->history_incoming){ + if(elm->timestamp >= minTimeout) { + report.recv_second += elm->size; + } - report.recv_minute += elm->size; - } + report.recv_minute += elm->size; + } - for(const auto& elm : this->history_file_incoming) { - report.file_recv += elm->size; - } - } + for(const auto& elm : this->history_file_incoming) { + report.file_recv += elm->size; + } + } - { - lock_guard lock(this->history_lock_outgoing); + { + lock_guard lock(this->history_lock_outgoing); - for(const auto& elm : this->history_outgoing){ - if(elm->timestamp >= minTimeout) { - report.send_second += elm->size; - } + for(const auto& elm : this->history_outgoing){ + if(elm->timestamp >= minTimeout) { + report.send_second += elm->size; + } - report.send_minute += elm->size; - } + report.send_minute += elm->size; + } - for(const auto& elm : this->history_file_outgoing) { - report.file_send += elm->size; - } - } + for(const auto& elm : this->history_file_outgoing) { + report.file_send += elm->size; + } + } report.recv_minute /= 60; report.send_minute /= 60; @@ -299,39 +299,39 @@ DataSummery ConnectionStatistics::dataReport() { } FullReport ConnectionStatistics::full_report() { - FullReport report{}; + FullReport report{}; - for(size_t index = 0 ; index < 4; index++) { - report.connection_bytes_sent[index] = (uint64_t) this->connection_bytes_sent[index]; - report.connection_packets_sent[index] = (uint64_t) this->connection_packets_sent[index]; - report.connection_bytes_received[index] = (uint64_t) this->connection_bytes_received[index]; - report.connection_packets_received[index] = (uint64_t) this->connection_packets_received[index]; - } + for(size_t index = 0 ; index < 4; index++) { + report.connection_bytes_sent[index] = (uint64_t) this->connection_bytes_sent[index]; + report.connection_packets_sent[index] = (uint64_t) this->connection_packets_sent[index]; + report.connection_bytes_received[index] = (uint64_t) this->connection_bytes_received[index]; + report.connection_packets_received[index] = (uint64_t) this->connection_packets_received[index]; + } - report.file_bytes_sent = this->file_bytes_sent; - report.file_bytes_received = this->file_bytes_received; + report.file_bytes_sent = this->file_bytes_sent; + report.file_bytes_received = this->file_bytes_received; - return report; + return report; } std::pair ConnectionStatistics::mark_file_bytes() { - std::pair result; + std::pair result; - { - lock_guard lock(this->history_lock_incoming); - if(this->mark_file_bytes_received < this->file_bytes_received) - result.second = this->file_bytes_received - this->mark_file_bytes_received; - this->mark_file_bytes_received = (uint64_t) this->file_bytes_received; - } + { + lock_guard lock(this->history_lock_incoming); + if(this->mark_file_bytes_received < this->file_bytes_received) + result.second = this->file_bytes_received - this->mark_file_bytes_received; + this->mark_file_bytes_received = (uint64_t) this->file_bytes_received; + } - { + { - lock_guard lock(this->history_lock_outgoing); + lock_guard lock(this->history_lock_outgoing); - if(this->mark_file_bytes_sent < this->file_bytes_sent) - result.first = this->file_bytes_sent - this->mark_file_bytes_sent; - this->mark_file_bytes_sent = (uint64_t) this->file_bytes_sent; - } + if(this->mark_file_bytes_sent < this->file_bytes_sent) + result.first = this->file_bytes_sent - this->mark_file_bytes_sent; + this->mark_file_bytes_sent = (uint64_t) this->file_bytes_sent; + } - return result; + return result; } \ No newline at end of file diff --git a/server/src/ConnectionStatistics.h b/server/src/ConnectionStatistics.h index e2fc2de..3507b99 100644 --- a/server/src/ConnectionStatistics.h +++ b/server/src/ConnectionStatistics.h @@ -29,59 +29,59 @@ namespace ts { }; struct FullReport { - uint64_t connection_packets_sent[4]{0, 0, 0, 0}; - uint64_t connection_bytes_sent[4]{0, 0, 0, 0}; - uint64_t connection_packets_received[4]{0, 0, 0, 0}; - uint64_t connection_bytes_received[4]{0, 0, 0, 0}; + uint64_t connection_packets_sent[4]{0, 0, 0, 0}; + uint64_t connection_bytes_sent[4]{0, 0, 0, 0}; + uint64_t connection_packets_received[4]{0, 0, 0, 0}; + uint64_t connection_bytes_received[4]{0, 0, 0, 0}; - uint64_t file_bytes_sent = 0; - uint64_t file_bytes_received = 0; + uint64_t file_bytes_sent = 0; + uint64_t file_bytes_received = 0; }; class ConnectionStatistics { public: - struct category { - enum value { - COMMAND, - ACK, - VOICE, - UNKNOWN - }; + struct category { + enum value { + COMMAND, + ACK, + VOICE, + UNKNOWN + }; - inline static category::value from_type(const protocol::PacketTypeInfo& type){ - if(type == protocol::PacketTypeInfo::Command || type == protocol::PacketTypeInfo::CommandLow) - return value::COMMAND; - else if(type == protocol::PacketTypeInfo::Ack || type == protocol::PacketTypeInfo::AckLow) - return value::ACK; - else if(type == protocol::PacketTypeInfo::Voice || type == protocol::PacketTypeInfo::VoiceWhisper) - return value::VOICE; - return value::UNKNOWN; - } - }; + inline static category::value from_type(const protocol::PacketTypeInfo& type){ + if(type == protocol::PacketTypeInfo::Command || type == protocol::PacketTypeInfo::CommandLow) + return value::COMMAND; + else if(type == protocol::PacketTypeInfo::Ack || type == protocol::PacketTypeInfo::AckLow) + return value::ACK; + else if(type == protocol::PacketTypeInfo::Voice || type == protocol::PacketTypeInfo::VoiceWhisper) + return value::VOICE; + return value::UNKNOWN; + } + }; explicit ConnectionStatistics(const std::shared_ptr& /* root */, bool /* spawn properties */); ~ConnectionStatistics(); std::shared_ptr statistics(); inline void logIncomingPacket(const protocol::ClientPacket& packet) { this->logIncomingPacket(category::from_type(packet.type()), packet.length()); } - void logIncomingPacket(const category::value& /* category */, size_t /* length */); - inline void logOutgoingPacket(const protocol::ServerPacket& packet) { this->logOutgoingPacket(category::from_type(packet.type()), packet.length()); } - void logOutgoingPacket(const category::value& /* category */, size_t /* length */); + void logIncomingPacket(const category::value& /* category */, size_t /* length */); + inline void logOutgoingPacket(const protocol::ServerPacket& packet) { this->logOutgoingPacket(category::from_type(packet.type()), packet.length()); } + void logOutgoingPacket(const category::value& /* category */, size_t /* length */); void logFileTransferIn(uint64_t); void logFileTransferOut(uint64_t); void tick(); DataSummery dataReport(); - FullReport full_report(); + FullReport full_report(); std::pair mark_file_bytes(); inline bool measure_bandwidths() { return this->_measure_bandwidths; } - void measure_bandwidths(bool flag) { this->_measure_bandwidths = flag; } + void measure_bandwidths(bool flag) { this->_measure_bandwidths = flag; } - inline bool has_properties() { return !!this->properties; } - private: + inline bool has_properties() { return !!this->properties; } + private: bool _measure_bandwidths = true; std::shared_ptr handle; std::shared_ptr properties; @@ -98,18 +98,18 @@ namespace ts { std::atomic mark_file_bytes_sent = 0; std::atomic mark_file_bytes_received = 0; - spin_lock history_lock_outgoing; - spin_lock history_lock_incoming; - std::deque history_file_incoming{}; - std::deque history_file_outgoing{}; - std::deque history_incoming{}; - std::deque history_outgoing{}; + spin_lock history_lock_outgoing; + spin_lock history_lock_incoming; + std::deque history_file_incoming{}; + std::deque history_file_outgoing{}; + std::deque history_incoming{}; + std::deque history_outgoing{}; - void _log_incoming_packet(StatisticEntry */* statistics */, int8_t /* type index */); - void _log_outgoing_packet(StatisticEntry* /* statistics */, int8_t /* type index */); + void _log_incoming_packet(StatisticEntry */* statistics */, int8_t /* type index */); + void _log_outgoing_packet(StatisticEntry* /* statistics */, int8_t /* type index */); - void _log_incoming_file_packet(StatisticEntry */* statistics */); - void _log_outgoing_file_packet(StatisticEntry* /* statistics */); + void _log_incoming_file_packet(StatisticEntry */* statistics */); + void _log_outgoing_file_packet(StatisticEntry* /* statistics */); }; } } \ No newline at end of file diff --git a/server/src/DatabaseHelper.cpp b/server/src/DatabaseHelper.cpp index ffcf896..ea300ab 100644 --- a/server/src/DatabaseHelper.cpp +++ b/server/src/DatabaseHelper.cpp @@ -34,18 +34,18 @@ void DatabaseHelper::tick() { } } - { - threads::MutexLock l(this->propsLock); - auto pcpy = this->cachedProperties; - for(const auto& mgr : pcpy){ - if(mgr->ownLock && system_clock::now() - mgr->lastAccess > minutes(5)) - mgr->ownLock.reset(); - if(mgr->properties.expired()) { - this->cachedProperties.erase(std::find(this->cachedProperties.begin(), this->cachedProperties.end(), mgr)); - delete mgr; - } - } - } + { + threads::MutexLock l(this->propsLock); + auto pcpy = this->cachedProperties; + for(const auto& mgr : pcpy){ + if(mgr->ownLock && system_clock::now() - mgr->lastAccess > minutes(5)) + mgr->ownLock.reset(); + if(mgr->properties.expired()) { + this->cachedProperties.erase(std::find(this->cachedProperties.begin(), this->cachedProperties.end(), mgr)); + delete mgr; + } + } + } } int collectData(deque>* list, int length, char** values, char** columns){ @@ -105,14 +105,14 @@ std::deque> DatabaseHelper::queryDatabaseInf std::deque> DatabaseHelper::queryDatabaseInfoByUid(const std::shared_ptr &server, std::deque list) { if(list.empty()) return {}; - deque> result; + deque> result; if(list.size() <= MAX_QUERY){ std::string query = "SELECT * FROM `clients` WHERE `serverId` = :serverId AND ("; for(const auto &elm : list) query += " `clientUid` = '" + elm + "' OR"; query = query.substr(0, query.length() - 3) + ")"; - logTrace(server ? server->getServerId() : 0, "[SQL] queryDatabseInfoByUid(...) -> {}", query); + logTrace(server ? server->getServerId() : 0, "[SQL] queryDatabseInfoByUid(...) -> {}", query); auto state = sql::command(this->sql, query, variable{":serverId", server ? server->getServerId() : 0}).query(function(collectData), &result); auto pf = LOG_SQL_CMD; pf(state); @@ -160,143 +160,143 @@ void DatabaseHelper::deleteClient(const std::shared_ptr& server, Clien } struct PermissionArguments { - sql::command& command; - PermissionManager* manager; - TSServer* server; + sql::command& command; + PermissionManager* manager; + TSServer* server; - inline int serverId() { return this->server ? this->server->getServerId() : 0; } + inline int serverId() { return this->server ? this->server->getServerId() : 0; } }; inline sql::result load_permissions(const std::shared_ptr& server, PermissionManager* manager, sql::command& command) { - auto start = system_clock::now(); + auto start = system_clock::now(); - auto data = PermissionArguments{command, manager, server.get()}; - return command.query([](PermissionArguments* data, int length, char** values, char** names){ - permission::PermissionType key = permission::PermissionType::undefined; - permission::PermissionValue value = 0, granted = 0; - bool negated = false, skipped = false; - std::shared_ptr channel; + auto data = PermissionArguments{command, manager, server.get()}; + return command.query([](PermissionArguments* data, int length, char** values, char** names){ + permission::PermissionType key = permission::PermissionType::undefined; + permission::PermissionValue value = 0, granted = 0; + bool negated = false, skipped = false; + std::shared_ptr channel; - int index; - try { - for(index = 0; index < length; index++) { - if(strcmp(names[index], "permId") == 0) { - key = permission::resolvePermissionData(values[index])->type; - if(key == permission::unknown){ - debugMessage(data->serverId(), "[SQL] Permission entry contains invalid permission type! Type: {} Command: {}", values[index], data->command.sqlCommand()); - return 0; - } - if(key == permission::undefined){ - debugMessage(data->serverId(), "[SQL] Permission entry contains undefined permission type! Type: {} Command: {}", values[index], data->command.sqlCommand()); - return 0; - } - } else if(strcmp(names[index], "channelId") == 0) { - auto channelId = stoull(values[index]); - if(channelId > 0) { - if(!data->server) - logError(data->serverId(), "[SQL] Cant find channel for channel bound permission (No server given)! Command: {} ChannelID: {}", data->command.sqlCommand(), values[index]); - else { - channel = data->server->getChannelTree()->findChannel(channelId); - if(!channel) - logError(data->serverId(), "[SQL] Cant find channel for channel bound permission! Command: {} ChannelID: {}", data->command.sqlCommand(), values[index]); - } - } - } else if(strcmp(names[index], "value") == 0) { - value = stoi(values[index]); - } else if(strcmp(names[index], "grant") == 0) { - granted = stoi(values[index]); - } else if(strcmp(names[index], "flag_skip") == 0) - skipped = strcmp(values[index], "1") == 0; - else if(strcmp(names[index], "flag_negate") == 0) - negated = strcmp(values[index], "1") == 0; - } - } catch(std::exception& ex) { - logError(data->serverId(), "[SQL] Cant load permissions! Failed to parse value! Command: {} Message: {} Key: {} Value: {}", data->command.sqlCommand(), ex.what(),names[index], values[index]); - return 0; - } + int index; + try { + for(index = 0; index < length; index++) { + if(strcmp(names[index], "permId") == 0) { + key = permission::resolvePermissionData(values[index])->type; + if(key == permission::unknown){ + debugMessage(data->serverId(), "[SQL] Permission entry contains invalid permission type! Type: {} Command: {}", values[index], data->command.sqlCommand()); + return 0; + } + if(key == permission::undefined){ + debugMessage(data->serverId(), "[SQL] Permission entry contains undefined permission type! Type: {} Command: {}", values[index], data->command.sqlCommand()); + return 0; + } + } else if(strcmp(names[index], "channelId") == 0) { + auto channelId = stoull(values[index]); + if(channelId > 0) { + if(!data->server) + logError(data->serverId(), "[SQL] Cant find channel for channel bound permission (No server given)! Command: {} ChannelID: {}", data->command.sqlCommand(), values[index]); + else { + channel = data->server->getChannelTree()->findChannel(channelId); + if(!channel) + logError(data->serverId(), "[SQL] Cant find channel for channel bound permission! Command: {} ChannelID: {}", data->command.sqlCommand(), values[index]); + } + } + } else if(strcmp(names[index], "value") == 0) { + value = stoi(values[index]); + } else if(strcmp(names[index], "grant") == 0) { + granted = stoi(values[index]); + } else if(strcmp(names[index], "flag_skip") == 0) + skipped = strcmp(values[index], "1") == 0; + else if(strcmp(names[index], "flag_negate") == 0) + negated = strcmp(values[index], "1") == 0; + } + } catch(std::exception& ex) { + logError(data->serverId(), "[SQL] Cant load permissions! Failed to parse value! Command: {} Message: {} Key: {} Value: {}", data->command.sqlCommand(), ex.what(),names[index], values[index]); + return 0; + } - if(key == permission::undefined) { - debugMessage(data->serverId(), "[SQL] Permission entry misses permission type! Command: {}", data->command.sqlCommand()); - return 0; - } + if(key == permission::undefined) { + debugMessage(data->serverId(), "[SQL] Permission entry misses permission type! Command: {}", data->command.sqlCommand()); + return 0; + } - auto permission = data->manager->registerPermission(key, value, channel); - permission->granted = granted; - permission->value = value; - permission->dbReference = true; - permission->flag_negate = negated; - permission->flag_skip = skipped; - return 0; - }, &data); + auto permission = data->manager->registerPermission(key, value, channel); + permission->granted = granted; + permission->value = value; + permission->dbReference = true; + permission->flag_negate = negated; + permission->flag_skip = skipped; + return 0; + }, &data); - auto end = system_clock::now(); - auto time = end - start; - logTrace(server ? server->getServerId() : 0, "[SQL] load_permissions(\"{}\") took {}ms", command.sqlCommand(), duration_cast(time).count()); + auto end = system_clock::now(); + auto time = end - start; + logTrace(server ? server->getServerId() : 0, "[SQL] load_permissions(\"{}\") took {}ms", command.sqlCommand(), duration_cast(time).count()); } inline sql::result load_permissions_v2(const std::shared_ptr& server, v2::PermissionManager* manager, sql::command& command, bool resolve_channel /* only used for client permissions (client channel permissions) */) { - auto start = system_clock::now(); + auto start = system_clock::now(); - auto server_id = server ? server->getServerId() : 0; - return command.query([&](int length, char** values, char** names){ - permission::PermissionType key = permission::PermissionType::undefined; - permission::PermissionValue value = permNotGranted, granted = permNotGranted; - bool negated = false, skipped = false; - std::shared_ptr channel; + auto server_id = server ? server->getServerId() : 0; + return command.query([&](int length, char** values, char** names){ + permission::PermissionType key = permission::PermissionType::undefined; + permission::PermissionValue value = permNotGranted, granted = permNotGranted; + bool negated = false, skipped = false; + std::shared_ptr channel; - int index; - try { - for(index = 0; index < length; index++) { - if(strcmp(names[index], "permId") == 0) { - key = permission::resolvePermissionData(values[index])->type; - if(key == permission::unknown){ - debugMessage(server_id, "[SQL] Permission entry contains invalid permission type! Type: {} Command: {}", values[index], command.sqlCommand()); - return 0; - } - if(key == permission::undefined){ - debugMessage(server_id, "[SQL] Permission entry contains undefined permission type! Type: {} Command: {}", values[index], command.sqlCommand()); - return 0; - } - } else if(strcmp(names[index], "channelId") == 0) { - if(resolve_channel) { - auto channelId = stoull(values[index]); - if(channelId > 0) { - if(!server) - logError(server_id, "[SQL] Cant find channel for channel bound permission (No server given)! Command: {} ChannelID: {}", command.sqlCommand(), values[index]); - else { - channel = server->getChannelTree()->findChannel(channelId); - if(!channel) - logError(server_id, "[SQL] Cant find channel for channel bound permission! Command: {} ChannelID: {}", command.sqlCommand(), values[index]); - } - } - } - } else if(strcmp(names[index], "value") == 0) { - value = stoi(values[index]); - } else if(strcmp(names[index], "grant") == 0) { - granted = stoi(values[index]); - } else if(strcmp(names[index], "flag_skip") == 0) - skipped = strcmp(values[index], "1") == 0; - else if(strcmp(names[index], "flag_negate") == 0) - negated = strcmp(values[index], "1") == 0; - } - } catch(std::exception& ex) { - logError(server_id, "[SQL] Cant load permissions! Failed to parse value! Command: {} Message: {} Key: {} Value: {}", command.sqlCommand(), ex.what(),names[index], values[index]); - return 0; - } + int index; + try { + for(index = 0; index < length; index++) { + if(strcmp(names[index], "permId") == 0) { + key = permission::resolvePermissionData(values[index])->type; + if(key == permission::unknown){ + debugMessage(server_id, "[SQL] Permission entry contains invalid permission type! Type: {} Command: {}", values[index], command.sqlCommand()); + return 0; + } + if(key == permission::undefined){ + debugMessage(server_id, "[SQL] Permission entry contains undefined permission type! Type: {} Command: {}", values[index], command.sqlCommand()); + return 0; + } + } else if(strcmp(names[index], "channelId") == 0) { + if(resolve_channel) { + auto channelId = stoull(values[index]); + if(channelId > 0) { + if(!server) + logError(server_id, "[SQL] Cant find channel for channel bound permission (No server given)! Command: {} ChannelID: {}", command.sqlCommand(), values[index]); + else { + channel = server->getChannelTree()->findChannel(channelId); + if(!channel) + logError(server_id, "[SQL] Cant find channel for channel bound permission! Command: {} ChannelID: {}", command.sqlCommand(), values[index]); + } + } + } + } else if(strcmp(names[index], "value") == 0) { + value = stoi(values[index]); + } else if(strcmp(names[index], "grant") == 0) { + granted = stoi(values[index]); + } else if(strcmp(names[index], "flag_skip") == 0) + skipped = strcmp(values[index], "1") == 0; + else if(strcmp(names[index], "flag_negate") == 0) + negated = strcmp(values[index], "1") == 0; + } + } catch(std::exception& ex) { + logError(server_id, "[SQL] Cant load permissions! Failed to parse value! Command: {} Message: {} Key: {} Value: {}", command.sqlCommand(), ex.what(),names[index], values[index]); + return 0; + } - if(key == permission::undefined) { - debugMessage(server_id, "[SQL] Permission entry misses permission type! Command: {}", command.sqlCommand()); - return 0; - } + if(key == permission::undefined) { + debugMessage(server_id, "[SQL] Permission entry misses permission type! Command: {}", command.sqlCommand()); + return 0; + } - if(!resolve_channel || !channel) - manager->load_permission(key, {value, granted}, skipped, negated, value != permNotGranted, granted != permNotGranted); - else - manager->load_permission(key, {value, granted}, channel->channelId(), skipped, negated, value != permNotGranted, granted != permNotGranted); - return 0; - }); + if(!resolve_channel || !channel) + manager->load_permission(key, {value, granted}, skipped, negated, value != permNotGranted, granted != permNotGranted); + else + manager->load_permission(key, {value, granted}, channel->channelId(), skipped, negated, value != permNotGranted, granted != permNotGranted); + return 0; + }); - auto end = system_clock::now(); - auto time = end - start; - logTrace(server_id, "[SQL] load_permissions(\"{}\") took {}ms", command.sqlCommand(), duration_cast(time).count()); + auto end = system_clock::now(); + auto time = end - start; + logTrace(server_id, "[SQL] load_permissions(\"{}\") took {}ms", command.sqlCommand(), duration_cast(time).count()); } #define UPDATE_COMMAND "UPDATE `permissions` SET `value` = :value, `grant` = :grant, `flag_skip` = :flag_skip, `flag_negate` = :flag_negate WHERE `serverId` = :serverId AND `type` = :type AND `id` = :id AND `permId` = :permId AND `channelId` = :chId" @@ -304,338 +304,338 @@ inline sql::result load_permissions_v2(const std::shared_ptr& server, #define DELETE_COMMAND "DELETE FROM `permissions` WHERE `serverId` = :serverId AND `type` = :type AND `id` = :id AND `permId` = :permId AND `channelId` = :chId" std::shared_ptr DatabaseHelper::loadClientPermissionManager(const std::shared_ptr& server, ClientDbId cldbid) { - auto server_id = server ? server->getServerId() : 0; + auto server_id = server ? server->getServerId() : 0; #ifndef DISABLE_CACHING - { - lock_guard lock(permManagerLock); - for(auto permMgr : this->cachedPermissionManagers) - if(permMgr->cldbid == cldbid && permMgr->sid == (server ? server->getServerId() : 0)) { - auto ptr = permMgr->manager.lock(); - if(!ptr){ - this->cachedPermissionManagers.erase(std::find(this->cachedPermissionManagers.begin(), this->cachedPermissionManagers.end(), permMgr)); - delete permMgr; - break; - } - permMgr->lastAccess = system_clock::now(); - return ptr; - } - } + { + lock_guard lock(permManagerLock); + for(auto permMgr : this->cachedPermissionManagers) + if(permMgr->cldbid == cldbid && permMgr->sid == (server ? server->getServerId() : 0)) { + auto ptr = permMgr->manager.lock(); + if(!ptr){ + this->cachedPermissionManagers.erase(std::find(this->cachedPermissionManagers.begin(), this->cachedPermissionManagers.end(), permMgr)); + delete permMgr; + break; + } + permMgr->lastAccess = system_clock::now(); + return ptr; + } + } #endif - logTrace(server_id, "[Permission] Loading client permission manager for client {}", cldbid); - auto permission_manager = std::make_shared(); - bool loaded = false; - if(this->use_startup_cache && server) { - shared_ptr entry; - { - threads::MutexLock lock(this->startup_lock); - for(const auto& entries : this->startup_entries) { - if(entries->sid == server->getServerId()) { - entry = entries; - break; - } - } - } - if(entry) { - for(const auto& perm : entry->permissions) { - if(perm->type == permission::SQL_PERM_USER && perm->id == cldbid) { - auto channel = perm->channelId > 0 ? server->getChannelTree()->findChannel(perm->channelId) : nullptr; + logTrace(server_id, "[Permission] Loading client permission manager for client {}", cldbid); + auto permission_manager = std::make_shared(); + bool loaded = false; + if(this->use_startup_cache && server) { + shared_ptr entry; + { + threads::MutexLock lock(this->startup_lock); + for(const auto& entries : this->startup_entries) { + if(entries->sid == server->getServerId()) { + entry = entries; + break; + } + } + } + if(entry) { + for(const auto& perm : entry->permissions) { + if(perm->type == permission::SQL_PERM_USER && perm->id == cldbid) { + auto channel = perm->channelId > 0 ? server->getChannelTree()->findChannel(perm->channelId) : nullptr; - if(channel) - permission_manager->load_permission(perm->permission->type, {perm->value, perm->grant}, channel->channelId(), perm->flag_skip, perm->flag_negate, perm->value != permNotGranted, perm->grant != permNotGranted); - else - permission_manager->load_permission(perm->permission->type, {perm->value, perm->grant}, perm->flag_skip, perm->flag_negate, perm->value != permNotGranted, perm->grant != permNotGranted); - } - } - loaded = true; - } - } + if(channel) + permission_manager->load_permission(perm->permission->type, {perm->value, perm->grant}, channel->channelId(), perm->flag_skip, perm->flag_negate, perm->value != permNotGranted, perm->grant != permNotGranted); + else + permission_manager->load_permission(perm->permission->type, {perm->value, perm->grant}, perm->flag_skip, perm->flag_negate, perm->value != permNotGranted, perm->grant != permNotGranted); + } + } + loaded = true; + } + } - if(!loaded) { - auto command = sql::command(this->sql, "SELECT `permId`, `value`, `channelId`, `grant`, `flag_skip`, `flag_negate` FROM `permissions` WHERE `serverId` = :serverId AND `type` = :type AND `id` = :id", - variable{":serverId", server ? server->getServerId() : 0}, - variable{":type", permission::SQL_PERM_USER}, - variable{":id", cldbid}); - LOG_SQL_CMD(load_permissions_v2(server, permission_manager.get(), command, true)); - } + if(!loaded) { + auto command = sql::command(this->sql, "SELECT `permId`, `value`, `channelId`, `grant`, `flag_skip`, `flag_negate` FROM `permissions` WHERE `serverId` = :serverId AND `type` = :type AND `id` = :id", + variable{":serverId", server ? server->getServerId() : 0}, + variable{":type", permission::SQL_PERM_USER}, + variable{":id", cldbid}); + LOG_SQL_CMD(load_permissions_v2(server, permission_manager.get(), command, true)); + } #ifndef DISABLE_CACHING - this->permManagerLock.lock(); - auto entry = new CachedPermissionManager(); - entry->sid = server_id; - entry->manager = permission_manager; - entry->ownLock = permission_manager; - entry->cldbid = cldbid; - entry->lastAccess = system_clock::now(); - this->cachedPermissionManagers.push_back(entry); - this->permManagerLock.unlock(); + this->permManagerLock.lock(); + auto entry = new CachedPermissionManager(); + entry->sid = server_id; + entry->manager = permission_manager; + entry->ownLock = permission_manager; + entry->cldbid = cldbid; + entry->lastAccess = system_clock::now(); + this->cachedPermissionManagers.push_back(entry); + this->permManagerLock.unlock(); #endif - return permission_manager; + return permission_manager; } void DatabaseHelper::saveClientPermissions(const std::shared_ptr &server, ts::ClientDbId client_dbid, const std::shared_ptr &permissions) { - const auto updates = permissions->flush_db_updates(); - if(updates.empty()) - return; + const auto updates = permissions->flush_db_updates(); + if(updates.empty()) + return; - auto server_id = server ? server->getServerId() : 0; - for(auto& update : updates) { - std::string query = update.flag_delete ? DELETE_COMMAND : (update.flag_db ? UPDATE_COMMAND : INSERT_COMMAND); + auto server_id = server ? server->getServerId() : 0; + for(auto& update : updates) { + std::string query = update.flag_delete ? DELETE_COMMAND : (update.flag_db ? UPDATE_COMMAND : INSERT_COMMAND); - auto permission_data = permission::resolvePermissionData(update.permission); - auto value = update.update_value == v2::delete_value ? permNotGranted : update.values.value; - auto grant = update.update_grant == v2::delete_value ? permNotGranted : update.values.grant; - logTrace(server_id, "[CHANNEL] Updating client permission for client {}: {}. New value: {}. New grant: {}. Query: {}", - client_dbid, - permission_data->name, - update.values.value, - update.values.grant, - query - ); - sql::command(this->sql, query, - variable{":serverId", server ? server->getServerId() : 0}, - variable{":id", client_dbid}, - variable{":chId", update.channel_id}, - variable{":type", permission::SQL_PERM_USER}, + auto permission_data = permission::resolvePermissionData(update.permission); + auto value = update.update_value == v2::delete_value ? permNotGranted : update.values.value; + auto grant = update.update_grant == v2::delete_value ? permNotGranted : update.values.grant; + logTrace(server_id, "[CHANNEL] Updating client permission for client {}: {}. New value: {}. New grant: {}. Query: {}", + client_dbid, + permission_data->name, + update.values.value, + update.values.grant, + query + ); + sql::command(this->sql, query, + variable{":serverId", server ? server->getServerId() : 0}, + variable{":id", client_dbid}, + variable{":chId", update.channel_id}, + variable{":type", permission::SQL_PERM_USER}, - variable{":permId", permission_data->name}, - variable{":value", value}, - variable{":grant", grant}, - variable{":flag_skip", update.flag_skip}, - variable{":flag_negate", update.flag_negate}) - .executeLater().waitAndGetLater(LOG_SQL_CMD, {-1, "future error"}); - } + variable{":permId", permission_data->name}, + variable{":value", value}, + variable{":grant", grant}, + variable{":flag_skip", update.flag_skip}, + variable{":flag_negate", update.flag_negate}) + .executeLater().waitAndGetLater(LOG_SQL_CMD, {-1, "future error"}); + } } std::shared_ptr DatabaseHelper::loadGroupPermissions(const std::shared_ptr& server, ts::GroupId group_id) { - auto result = std::make_shared(); - if(this->use_startup_cache && server) { - shared_ptr entry; - { - threads::MutexLock lock(this->startup_lock); - for(const auto& entries : this->startup_entries) { - if(entries->sid == server->getServerId()) { - entry = entries; - break; - } - } - } - if(entry) { - for(const auto& perm : entry->permissions) { - if(perm->type == permission::SQL_PERM_GROUP && perm->id == group_id) { - result->load_permission(perm->permission->type, {perm->value, perm->grant}, perm->flag_skip, perm->flag_negate, perm->value != permNotGranted, perm->grant != permNotGranted); - } - } - return result; - } - } + auto result = std::make_shared(); + if(this->use_startup_cache && server) { + shared_ptr entry; + { + threads::MutexLock lock(this->startup_lock); + for(const auto& entries : this->startup_entries) { + if(entries->sid == server->getServerId()) { + entry = entries; + break; + } + } + } + if(entry) { + for(const auto& perm : entry->permissions) { + if(perm->type == permission::SQL_PERM_GROUP && perm->id == group_id) { + result->load_permission(perm->permission->type, {perm->value, perm->grant}, perm->flag_skip, perm->flag_negate, perm->value != permNotGranted, perm->grant != permNotGranted); + } + } + return result; + } + } - //7931 - auto command = sql::command(this->sql, "SELECT `channelId`, `permId`, `value`, `grant`, `flag_skip`, `flag_negate` FROM `permissions` WHERE `serverId` = :serverId AND `type` = :type AND `id` = :id", - variable{":serverId", server ? server->getServerId() : 0}, - variable{":type", permission::SQL_PERM_GROUP}, - variable{":id", group_id}); - LOG_SQL_CMD(load_permissions_v2(server, result.get(), command, false)); + //7931 + auto command = sql::command(this->sql, "SELECT `channelId`, `permId`, `value`, `grant`, `flag_skip`, `flag_negate` FROM `permissions` WHERE `serverId` = :serverId AND `type` = :type AND `id` = :id", + variable{":serverId", server ? server->getServerId() : 0}, + variable{":type", permission::SQL_PERM_GROUP}, + variable{":id", group_id}); + LOG_SQL_CMD(load_permissions_v2(server, result.get(), command, false)); return result; } void DatabaseHelper::saveGroupPermissions(const std::shared_ptr &server, ts::GroupId group_id, const std::shared_ptr &permissions) { - const auto updates = permissions->flush_db_updates(); - if(updates.empty()) - return; + const auto updates = permissions->flush_db_updates(); + if(updates.empty()) + return; - auto server_id = server ? server->getServerId() : 0; - for(auto& update : updates) { - std::string query = update.flag_delete ? DELETE_COMMAND : (update.flag_db ? UPDATE_COMMAND : INSERT_COMMAND); + auto server_id = server ? server->getServerId() : 0; + for(auto& update : updates) { + std::string query = update.flag_delete ? DELETE_COMMAND : (update.flag_db ? UPDATE_COMMAND : INSERT_COMMAND); - auto permission_data = permission::resolvePermissionData(update.permission); - auto value = update.update_value == v2::delete_value ? permNotGranted : update.values.value; - auto grant = update.update_grant == v2::delete_value ? permNotGranted : update.values.grant; - logTrace(server_id, "[CHANNEL] Updating group permission for group {}: {}. New value: {}. New grant: {}. Query: {}", - group_id, - permission_data->name, - value, - grant, - query - ); - sql::command(this->sql, query, - variable{":serverId", server ? server->getServerId() : 0}, - variable{":id", group_id}, - variable{":chId", 0}, - variable{":type", permission::SQL_PERM_GROUP}, + auto permission_data = permission::resolvePermissionData(update.permission); + auto value = update.update_value == v2::delete_value ? permNotGranted : update.values.value; + auto grant = update.update_grant == v2::delete_value ? permNotGranted : update.values.grant; + logTrace(server_id, "[CHANNEL] Updating group permission for group {}: {}. New value: {}. New grant: {}. Query: {}", + group_id, + permission_data->name, + value, + grant, + query + ); + sql::command(this->sql, query, + variable{":serverId", server ? server->getServerId() : 0}, + variable{":id", group_id}, + variable{":chId", 0}, + variable{":type", permission::SQL_PERM_GROUP}, - variable{":permId", permission_data->name}, - variable{":value", value}, - variable{":grant", grant}, - variable{":flag_skip", update.flag_skip}, - variable{":flag_negate", update.flag_negate}) - .executeLater().waitAndGetLater(LOG_SQL_CMD, {-1, "future error"}); - } + variable{":permId", permission_data->name}, + variable{":value", value}, + variable{":grant", grant}, + variable{":flag_skip", update.flag_skip}, + variable{":flag_negate", update.flag_negate}) + .executeLater().waitAndGetLater(LOG_SQL_CMD, {-1, "future error"}); + } } std::shared_ptr DatabaseHelper::loadPlaylistPermissions(const std::shared_ptr &server, ts::PlaylistId playlist_id) { - shared_ptr result; - if(this->use_startup_cache && server) { - shared_ptr entry; - { - threads::MutexLock lock(this->startup_lock); - for(const auto& entries : this->startup_entries) { - if(entries->sid == server->getServerId()) { - entry = entries; - break; - } - } - } - if(entry) { - result = std::make_shared(); + shared_ptr result; + if(this->use_startup_cache && server) { + shared_ptr entry; + { + threads::MutexLock lock(this->startup_lock); + for(const auto& entries : this->startup_entries) { + if(entries->sid == server->getServerId()) { + entry = entries; + break; + } + } + } + if(entry) { + result = std::make_shared(); - for(const auto& perm : entry->permissions) { - if(perm->type == permission::SQL_PERM_PLAYLIST && perm->id == playlist_id) { - auto permission = result->registerPermission(perm->permission, perm->value, server->getChannelTree()->findChannel(perm->channelId)); - permission->granted = perm->grant; - permission->value = perm->value; - permission->dbReference = true; - permission->flag_negate = perm->flag_negate; - permission->flag_skip = perm->flag_skip; - } - } - } - } + for(const auto& perm : entry->permissions) { + if(perm->type == permission::SQL_PERM_PLAYLIST && perm->id == playlist_id) { + auto permission = result->registerPermission(perm->permission, perm->value, server->getChannelTree()->findChannel(perm->channelId)); + permission->granted = perm->grant; + permission->value = perm->value; + permission->dbReference = true; + permission->flag_negate = perm->flag_negate; + permission->flag_skip = perm->flag_skip; + } + } + } + } - if(!result) { - result = std::make_shared(); - auto command = sql::command(this->sql, "SELECT `channelId`, `permId`, `value`, `grant`, `flag_skip`, `flag_negate` FROM `permissions` WHERE `serverId` = :serverId AND `type` = :type AND `id` = :id", - variable{":serverId", server ? server->getServerId() : 0}, - variable{":type", permission::SQL_PERM_PLAYLIST}, - variable{":id", playlist_id}); - LOG_SQL_CMD(load_permissions(server, result.get(), command)); - } + if(!result) { + result = std::make_shared(); + auto command = sql::command(this->sql, "SELECT `channelId`, `permId`, `value`, `grant`, `flag_skip`, `flag_negate` FROM `permissions` WHERE `serverId` = :serverId AND `type` = :type AND `id` = :id", + variable{":serverId", server ? server->getServerId() : 0}, + variable{":type", permission::SQL_PERM_PLAYLIST}, + variable{":id", playlist_id}); + LOG_SQL_CMD(load_permissions(server, result.get(), command)); + } - assert(result); - weak_ptr weak_server = server; - auto server_id = server ? server->getServerId() : 0; + assert(result); + weak_ptr weak_server = server; + auto server_id = server ? server->getServerId() : 0; - result->registerUpdateHandler([&, weak_server, server_id, playlist_id](std::shared_ptr permission) { - auto server = weak_server.lock(); - if(!server && server_id != 0) { - logError(server_id, "Tried to update a playlist permission of a expired server!"); - return; - } + result->registerUpdateHandler([&, weak_server, server_id, playlist_id](std::shared_ptr permission) { + auto server = weak_server.lock(); + if(!server && server_id != 0) { + logError(server_id, "Tried to update a playlist permission of a expired server!"); + return; + } - std::string query; + std::string query; - /* - if(permission->deleted){ - query = "DELETE FROM `permissions` WHERE `serverId` = :serverId AND `id` = :id AND `type`= :type AND `permId`= :permId AND `channelId` = :chId"; - } else - */ - if(permission->dbReference){ - query = UPDATE_COMMAND; - } else { - permission->dbReference = true; - query = INSERT_COMMAND; - } + /* + if(permission->deleted){ + query = "DELETE FROM `permissions` WHERE `serverId` = :serverId AND `id` = :id AND `type`= :type AND `permId`= :permId AND `channelId` = :chId"; + } else + */ + if(permission->dbReference){ + query = UPDATE_COMMAND; + } else { + permission->dbReference = true; + query = INSERT_COMMAND; + } - logTrace(server ? server->getServerId() : 0, "[SQL] Executing group permission command: {}", query); - sql::command(this->sql, query, - variable{":serverId", server ? server->getServerId() : 0}, - variable{":id", playlist_id}, - variable{":type", permission::SQL_PERM_PLAYLIST}, - variable{":chId", permission->channelId()}, + logTrace(server ? server->getServerId() : 0, "[SQL] Executing group permission command: {}", query); + sql::command(this->sql, query, + variable{":serverId", server ? server->getServerId() : 0}, + variable{":id", playlist_id}, + variable{":type", permission::SQL_PERM_PLAYLIST}, + variable{":chId", permission->channelId()}, - variable{":permId", permission->type->name}, - variable{":value", permission->value}, - variable{":grant", permission->granted}, - variable{":flag_skip", permission->flag_skip}, - variable{":flag_negate", permission->flag_negate}) - .executeLater().waitAndGetLater(LOG_SQL_CMD, {-1, "failed to save playlist permission " + to_string(playlist_id)}); - }); + variable{":permId", permission->type->name}, + variable{":value", permission->value}, + variable{":grant", permission->granted}, + variable{":flag_skip", permission->flag_skip}, + variable{":flag_negate", permission->flag_negate}) + .executeLater().waitAndGetLater(LOG_SQL_CMD, {-1, "failed to save playlist permission " + to_string(playlist_id)}); + }); - return result; + return result; } std::shared_ptr DatabaseHelper::loadChannelPermissions(const std::shared_ptr& server, ts::ChannelId channel) { - auto result = std::make_shared(); - if(this->use_startup_cache && server) { - shared_ptr entry; - { - threads::MutexLock lock(this->startup_lock); - for(const auto& entries : this->startup_entries) { - if(entries->sid == server->getServerId()) { - entry = entries; - break; - } - } - } - if(entry) { - for(const auto& perm : entry->permissions) { - if(perm->type == permission::SQL_PERM_CHANNEL && perm->channelId == channel) { - result->load_permission(perm->permission->type, {perm->value, perm->grant}, perm->flag_skip, perm->flag_negate, perm->value != permNotGranted, perm->grant != permNotGranted); - } - } - return result; - } - } + auto result = std::make_shared(); + if(this->use_startup_cache && server) { + shared_ptr entry; + { + threads::MutexLock lock(this->startup_lock); + for(const auto& entries : this->startup_entries) { + if(entries->sid == server->getServerId()) { + entry = entries; + break; + } + } + } + if(entry) { + for(const auto& perm : entry->permissions) { + if(perm->type == permission::SQL_PERM_CHANNEL && perm->channelId == channel) { + result->load_permission(perm->permission->type, {perm->value, perm->grant}, perm->flag_skip, perm->flag_negate, perm->value != permNotGranted, perm->grant != permNotGranted); + } + } + return result; + } + } - auto command = sql::command(sql, "SELECT `permId`, `value`, `grant`, `flag_skip`, `flag_negate` FROM `permissions` WHERE `serverId` = :serverId AND `channelId` = :chid AND `type` = :type AND `id` = :id", - variable{":serverId", server ? server->getServerId() : 0}, - variable{":chid", channel}, - variable{":id", 0}, - variable{":type", permission::SQL_PERM_CHANNEL}); - LOG_SQL_CMD(load_permissions_v2(server, result.get(), command, false)); - return result; + auto command = sql::command(sql, "SELECT `permId`, `value`, `grant`, `flag_skip`, `flag_negate` FROM `permissions` WHERE `serverId` = :serverId AND `channelId` = :chid AND `type` = :type AND `id` = :id", + variable{":serverId", server ? server->getServerId() : 0}, + variable{":chid", channel}, + variable{":id", 0}, + variable{":type", permission::SQL_PERM_CHANNEL}); + LOG_SQL_CMD(load_permissions_v2(server, result.get(), command, false)); + return result; } void DatabaseHelper::saveChannelPermissions(const std::shared_ptr &server, ts::ChannelId channel_id, const std::shared_ptr &permissions) { - const auto updates = permissions->flush_db_updates(); - if(updates.empty()) - return; + const auto updates = permissions->flush_db_updates(); + if(updates.empty()) + return; - auto server_id = server ? server->getServerId() : 0; - for(auto& update : updates) { - std::string query = update.flag_delete ? DELETE_COMMAND : (update.flag_db ? UPDATE_COMMAND : INSERT_COMMAND); + auto server_id = server ? server->getServerId() : 0; + for(auto& update : updates) { + std::string query = update.flag_delete ? DELETE_COMMAND : (update.flag_db ? UPDATE_COMMAND : INSERT_COMMAND); - auto value = update.update_value == v2::delete_value ? permNotGranted : update.values.value; - auto grant = update.update_grant == v2::delete_value ? permNotGranted : update.values.grant; - auto permission_data = permission::resolvePermissionData(update.permission); - logTrace(server_id, "[CHANNEL] Updating channel permission for channel {}: {}. New value: {}. New grant: {}. Query: {}", - channel_id, - permission_data->name, - value, - grant, - query - ); - sql::command(this->sql, query, - variable{":serverId", server ? server->getServerId() : 0}, - variable{":id", 0}, - variable{":chId", channel_id}, - variable{":type", permission::SQL_PERM_CHANNEL}, + auto value = update.update_value == v2::delete_value ? permNotGranted : update.values.value; + auto grant = update.update_grant == v2::delete_value ? permNotGranted : update.values.grant; + auto permission_data = permission::resolvePermissionData(update.permission); + logTrace(server_id, "[CHANNEL] Updating channel permission for channel {}: {}. New value: {}. New grant: {}. Query: {}", + channel_id, + permission_data->name, + value, + grant, + query + ); + sql::command(this->sql, query, + variable{":serverId", server ? server->getServerId() : 0}, + variable{":id", 0}, + variable{":chId", channel_id}, + variable{":type", permission::SQL_PERM_CHANNEL}, - variable{":permId", permission_data->name}, - variable{":value", value}, - variable{":grant", grant}, - variable{":flag_skip", update.flag_skip}, - variable{":flag_negate", update.flag_negate}) - .executeLater().waitAndGetLater(LOG_SQL_CMD, {-1, "future error"}); - } + variable{":permId", permission_data->name}, + variable{":value", value}, + variable{":grant", grant}, + variable{":flag_skip", update.flag_skip}, + variable{":flag_negate", update.flag_negate}) + .executeLater().waitAndGetLater(LOG_SQL_CMD, {-1, "future error"}); + } } std::shared_ptr DatabaseHelper::default_properties_client(std::shared_ptr properties, ClientType type){ - if(!properties) - properties = make_shared(); + if(!properties) + properties = make_shared(); - properties->register_property_type(); - properties->register_property_type(); + properties->register_property_type(); + properties->register_property_type(); - if(type == ClientType::CLIENT_MUSIC || type == ClientType::CLIENT_QUERY){ - (*properties)[property::CLIENT_INPUT_HARDWARE] = true; - (*properties)[property::CLIENT_OUTPUT_HARDWARE] = true; - } + if(type == ClientType::CLIENT_MUSIC || type == ClientType::CLIENT_QUERY){ + (*properties)[property::CLIENT_INPUT_HARDWARE] = true; + (*properties)[property::CLIENT_OUTPUT_HARDWARE] = true; + } - return properties; + return properties; } bool DatabaseHelper::assignDatabaseId(sql::SqlManager *sql, ServerId id, std::shared_ptr cl) { @@ -664,9 +664,9 @@ bool DatabaseHelper::assignDatabaseId(sql::SqlManager *sql, ServerId id, std::sh res = insertTemplate.command().values(variable{":serverId", 0}, variable{":cldbid", cldbid}).execute(); //Insert global pf(res); if(!res) return false; - debugMessage(id, "Having new instance user on server {}. (Database ID: {} Name: {})", id, cldbid, cl->getDisplayName()); + debugMessage(id, "Having new instance user on server {}. (Database ID: {} Name: {})", id, cldbid, cl->getDisplayName()); } else { - debugMessage(id, "Having new server user on server {}. (Database ID: {} Name: {})", id, cldbid, cl->getDisplayName()); + debugMessage(id, "Having new server user on server {}. (Database ID: {} Name: {})", id, cldbid, cl->getDisplayName()); } if(id != 0){ //Else already inserted @@ -678,29 +678,29 @@ bool DatabaseHelper::assignDatabaseId(sql::SqlManager *sql, ServerId id, std::sh return assignDatabaseId(sql, id, cl); } - logTrace(id, "Loaded client from database. Database id: {} Unique id: {}", cl->getClientDatabaseId(), cl->getUid()); + logTrace(id, "Loaded client from database. Database id: {} Unique id: {}", cl->getClientDatabaseId(), cl->getUid()); return true; } inline sql::result load_properties(ServerId sid, deque>& properties, sql::command& command) { - auto start = system_clock::now(); + auto start = system_clock::now(); auto result = command.query([&](int length, string* values, string* names) { string key, value; property::PropertyType type = property::PROP_TYPE_UNKNOWN; for(int index = 0; index < length; index++) { - try { - if(names[index] == "key") key = values[index]; - else if(names[index] == "value") value = values[index]; - else if(names[index] == "type") type = (property::PropertyType) stoll(values[index]); - } catch(const std::exception& ex) { - logError(sid, "Failed to load parse property \"{}\". key: {}, value: {}, message: {}", key,names[index],values[index],ex.what()); - return 0; - } + try { + if(names[index] == "key") key = values[index]; + else if(names[index] == "value") value = values[index]; + else if(names[index] == "type") type = (property::PropertyType) stoll(values[index]); + } catch(const std::exception& ex) { + logError(sid, "Failed to load parse property \"{}\". key: {}, value: {}, message: {}", key,names[index],values[index],ex.what()); + return 0; + } } - const auto &info = property::impl::info_key(type, key); + const auto &info = property::impl::info_key(type, key); if(info->name == "undefined") { logError(sid, "Found unknown property in database! ({})", key); return 0; @@ -716,95 +716,95 @@ inline sql::result load_properties(ServerId sid, deque(); data->value = value; data->type = info; - properties.push_back(move(data)); + properties.push_back(move(data)); return 0; }); auto end = system_clock::now(); auto time = end - start; - logTrace(sid, "[SQL] load_properties(\"{}\") needs {}ms", command.sqlCommand(), duration_cast(time).count()); + logTrace(sid, "[SQL] load_properties(\"{}\") needs {}ms", command.sqlCommand(), duration_cast(time).count()); return result; } std::shared_ptr DatabaseHelper::loadServerProperties(const std::shared_ptr& server) { - auto props = std::make_shared(); + auto props = std::make_shared(); - props->register_property_type(); - (*props)[property::VIRTUALSERVER_HOST] = config::binding::DefaultVoiceHost; - (*props)[property::VIRTUALSERVER_WEB_HOST] = config::binding::DefaultWebHost; + props->register_property_type(); + (*props)[property::VIRTUALSERVER_HOST] = config::binding::DefaultVoiceHost; + (*props)[property::VIRTUALSERVER_WEB_HOST] = config::binding::DefaultWebHost; - bool loaded = false; - if(use_startup_cache && server) { - shared_ptr entry; - { - threads::MutexLock lock(this->startup_lock); - for(const auto& entries : this->startup_entries) { - if(entries->sid == server->getServerId()) { - entry = entries; - break; - } - } - } - if(entry) { - for(const auto& prop : entry->properties) { - if(prop->type == property::PROP_TYPE_SERVER && prop->id == 0) { - auto p = (*props)[prop->info]; - p = prop->value; - p.setModified(true); - p.setDbReference(true); - } - } - loaded = true; - } - } - if(!loaded) { - auto command = sql::command(this->sql, "SELECT `key`, `value`, `type` FROM properties WHERE `serverId` = :serverId AND `type` = :type", variable{":serverId", server ? server->getServerId() : 0}, variable{":type", property::PropertyType::PROP_TYPE_SERVER}); + bool loaded = false; + if(use_startup_cache && server) { + shared_ptr entry; + { + threads::MutexLock lock(this->startup_lock); + for(const auto& entries : this->startup_entries) { + if(entries->sid == server->getServerId()) { + entry = entries; + break; + } + } + } + if(entry) { + for(const auto& prop : entry->properties) { + if(prop->type == property::PROP_TYPE_SERVER && prop->id == 0) { + auto p = (*props)[prop->info]; + p = prop->value; + p.setModified(true); + p.setDbReference(true); + } + } + loaded = true; + } + } + if(!loaded) { + auto command = sql::command(this->sql, "SELECT `key`, `value`, `type` FROM properties WHERE `serverId` = :serverId AND `type` = :type", variable{":serverId", server ? server->getServerId() : 0}, variable{":type", property::PropertyType::PROP_TYPE_SERVER}); - deque> property_list; - LOG_SQL_CMD(load_properties(server ? server->getServerId() : 0, property_list, command)); - for(const auto& entry : property_list) { - auto prop = props->operator[](entry->type); - prop = entry->value; - prop.setModified(true); - prop.setDbReference(true); - } - } + deque> property_list; + LOG_SQL_CMD(load_properties(server ? server->getServerId() : 0, property_list, command)); + for(const auto& entry : property_list) { + auto prop = props->operator[](entry->type); + prop = entry->value; + prop.setModified(true); + prop.setDbReference(true); + } + } - weak_ptr weak = server; - ServerId serverId = server ? server->getServerId() : 0; - props->registerNotifyHandler([&, serverId, weak](Property& prop){ - if((prop.type().flags & property::FLAG_SAVE) == 0) { - prop.setModified(false); - return; - } - auto weak_server = weak.lock(); - if(!weak_server && serverId != 0) return; + weak_ptr weak = server; + ServerId serverId = server ? server->getServerId() : 0; + props->registerNotifyHandler([&, serverId, weak](Property& prop){ + if((prop.type().flags & property::FLAG_SAVE) == 0) { + prop.setModified(false); + return; + } + auto weak_server = weak.lock(); + if(!weak_server && serverId != 0) return; - string sql; - if(prop.hasDbReference()) - sql = "UPDATE `properties` SET `value` = :value WHERE `serverId` = :sid AND `type` = :type AND `id` = :id AND `key` = :key"; - else { - prop.setDbReference(true); - sql = "INSERT INTO `properties` (`serverId`, `type`, `id`, `key`, `value`) VALUES (:sid, :type, :id, :key, :value)"; - } + string sql; + if(prop.hasDbReference()) + sql = "UPDATE `properties` SET `value` = :value WHERE `serverId` = :sid AND `type` = :type AND `id` = :id AND `key` = :key"; + else { + prop.setDbReference(true); + sql = "INSERT INTO `properties` (`serverId`, `type`, `id`, `key`, `value`) VALUES (:sid, :type, :id, :key, :value)"; + } - logTrace(serverId, "Updating server property: " + prop.type().name + ". New value: " + prop.value() + ". Query: " + sql); - sql::command(this->sql, sql, - variable{":sid", serverId}, - variable{":type", property::PropertyType::PROP_TYPE_SERVER}, - variable{":id", 0}, - variable{":key", prop.type().name}, - variable{":value", prop.value()} - ).executeLater().waitAndGetLater(LOG_SQL_CMD, sql::result{1, "future failed"}); - }); - return props; + logTrace(serverId, "Updating server property: " + prop.type().name + ". New value: " + prop.value() + ". Query: " + sql); + sql::command(this->sql, sql, + variable{":sid", serverId}, + variable{":type", property::PropertyType::PROP_TYPE_SERVER}, + variable{":id", 0}, + variable{":key", prop.type().name}, + variable{":value", prop.value()} + ).executeLater().waitAndGetLater(LOG_SQL_CMD, sql::result{1, "future failed"}); + }); + return props; } std::shared_ptr DatabaseHelper::loadPlaylistProperties(const std::shared_ptr& server, PlaylistId id) { auto props = std::make_shared(); - props->register_property_type(); - (*props)[property::PLAYLIST_ID] = id; + props->register_property_type(); + (*props)[property::PLAYLIST_ID] = id; bool loaded = false; if(use_startup_cache && server) { @@ -831,16 +831,16 @@ std::shared_ptr DatabaseHelper::loadPlaylistProperties(const std::sh } } if(!loaded) { - auto command = sql::command(this->sql, "SELECT `key`, `value`, `type` FROM properties WHERE `serverId` = :serverId AND `type` = :type AND `id` = :id", variable{":serverId", server ? server->getServerId() : 0}, variable{":type", property::PropertyType::PROP_TYPE_PLAYLIST}, variable{":id", id}); + auto command = sql::command(this->sql, "SELECT `key`, `value`, `type` FROM properties WHERE `serverId` = :serverId AND `type` = :type AND `id` = :id", variable{":serverId", server ? server->getServerId() : 0}, variable{":type", property::PropertyType::PROP_TYPE_PLAYLIST}, variable{":id", id}); - deque> property_list; - LOG_SQL_CMD(load_properties(server ? server->getServerId() : 0, property_list, command)); - for(const auto& entry : property_list) { - auto prop = props->operator[](entry->type); - prop = entry->value; - prop.setModified(true); - prop.setDbReference(true); - } + deque> property_list; + LOG_SQL_CMD(load_properties(server ? server->getServerId() : 0, property_list, command)); + for(const auto& entry : property_list) { + auto prop = props->operator[](entry->type); + prop = entry->value; + prop.setModified(true); + prop.setDbReference(true); + } } weak_ptr weak = server; @@ -861,7 +861,7 @@ std::shared_ptr DatabaseHelper::loadPlaylistProperties(const std::sh sql = "INSERT INTO `properties` (`serverId`, `type`, `id`, `key`, `value`) VALUES (:sid, :type, :id, :key, :value)"; } - logTrace(serverId, "Updating playlist property for {}. Key: {} Value: {}", id, prop.type().name, prop.value()); + logTrace(serverId, "Updating playlist property for {}. Key: {} Value: {}", id, prop.type().name, prop.value()); sql::command(this->sql, sql, variable{":sid", serverId}, variable{":type", property::PropertyType::PROP_TYPE_PLAYLIST}, @@ -877,7 +877,7 @@ std::shared_ptr DatabaseHelper::loadChannelProperties(const shared_p ServerId serverId = server ? server->getServerId() : 0U; auto props = std::make_shared(); - props->register_property_type(); + props->register_property_type(); if(server) { props->operator[](property::CHANNEL_TOPIC) = server->properties()[property::VIRTUALSERVER_DEFAULT_CHANNEL_TOPIC].value(); props->operator[](property::CHANNEL_DESCRIPTION) = server->properties()[property::VIRTUALSERVER_DEFAULT_CHANNEL_DESCRIPTION].value(); @@ -910,13 +910,13 @@ std::shared_ptr DatabaseHelper::loadChannelProperties(const shared_p if(!loaded) { auto command = sql::command(this->sql, "SELECT `key`, `value`, `type` FROM properties WHERE `serverId` = :serverId AND `type` = :type AND `id` = :id", variable{":serverId", serverId}, variable{":type", property::PropertyType::PROP_TYPE_CHANNEL}, variable{":id", channel}); - deque> property_list; + deque> property_list; LOG_SQL_CMD(load_properties(serverId, property_list, command)); for(const auto& entry : property_list) { - auto prop = props->operator[](entry->type); - prop = entry->value; - prop.setModified(true); - prop.setDbReference(true); + auto prop = props->operator[](entry->type); + prop = entry->value; + prop.setModified(true); + prop.setDbReference(true); } } @@ -924,11 +924,11 @@ std::shared_ptr DatabaseHelper::loadChannelProperties(const shared_p props->registerNotifyHandler([&, weak, serverId, channel](Property& prop){ auto weak_server = weak.lock(); if(!weak_server && serverId != 0) - return; + return; if((prop.type().flags & property::FLAG_SAVE) == 0) - return; + return; if(!prop.isModified()) - return; + return; std::string query; if(prop.type() == property::CHANNEL_PID){ @@ -938,7 +938,7 @@ std::shared_ptr DatabaseHelper::loadChannelProperties(const shared_p } else { query = "UPDATE `properties` SET `value` = :value WHERE `serverId` = :serverId AND `id` = :id AND `key` = :key AND `type` = :type"; } - logTrace(serverId, "[CHANNEL] Updating channel property for channel {}: {}. New value: '{}'. Query: {}", channel, prop.type().name, prop.value(), query); + logTrace(serverId, "[CHANNEL] Updating channel property for channel {}: {}. New value: '{}'. Query: {}", channel, prop.type().name, prop.value(), query); sql::command(this->sql, query, variable{":serverId", serverId}, @@ -986,25 +986,25 @@ std::shared_ptr DatabaseHelper::loadClientProperties(const std::shar if(!loaded) { auto command = sql::command(this->sql, "SELECT `key`, `value`, `type` FROM properties WHERE `serverId` = :serverId AND (`type` = :type1 OR `type` = :type2) AND `id` = :id", variable{":serverId", server ? server->getServerId() : 0}, variable{":type1", property::PropertyType::PROP_TYPE_CONNECTION}, variable{":type2", property::PropertyType::PROP_TYPE_CLIENT}, variable{":id", cldbid}); - deque> property_list; - LOG_SQL_CMD(load_properties(server ? server->getServerId() : 0, property_list, command)); - for(const auto& entry : property_list) { - auto prop = props->operator[](entry->type); - prop = entry->value; - prop.setModified(true); - prop.setDbReference(true); - } + deque> property_list; + LOG_SQL_CMD(load_properties(server ? server->getServerId() : 0, property_list, command)); + for(const auto& entry : property_list) { + auto prop = props->operator[](entry->type); + prop = entry->value; + prop.setModified(true); + prop.setDbReference(true); + } } - weak_ptr weak_server = server; - auto server_id = server ? server->getServerId() : 0; + weak_ptr weak_server = server; + auto server_id = server ? server->getServerId() : 0; props->registerNotifyHandler([&, weak_server, server_id, cldbid, type](Property& prop){ //General save - auto server = weak_server.lock(); - if(!server && server_id != 0) { - logError(server_id, "Tried to update client permissions of a expired server!"); - return; - } + auto server = weak_server.lock(); + if(!server && server_id != 0) { + logError(server_id, "Tried to update client permissions of a expired server!"); + return; + } if(!prop.isModified()) return; if((prop.type().flags & property::FLAG_SAVE) == 0 && (type != ClientType::CLIENT_MUSIC || (prop.type().flags & property::FLAG_SAVE_MUSIC) == 0)) { @@ -1023,24 +1023,24 @@ std::shared_ptr DatabaseHelper::loadClientProperties(const std::shar prop.setDbReference(true); sql = "INSERT INTO `properties` (`serverId`, `type`, `id`, `key`, `value`) VALUES (:serverId, :type, :id, :key, :value)"; } - logTrace(server ? server->getServerId() : 0, "[Property] Changed property in db key: " + prop.type().name + " value: " + prop.value()); + logTrace(server ? server->getServerId() : 0, "[Property] Changed property in db key: " + prop.type().name + " value: " + prop.value()); sql::command(this->sql, sql, - variable{":serverId", server ? server->getServerId() : 0}, - variable{":type", prop.type().type_property}, - variable{":id", cldbid}, - variable{":key", prop.type().name}, - variable{":value", prop.value()} + variable{":serverId", server ? server->getServerId() : 0}, + variable{":type", prop.type().type_property}, + variable{":id", cldbid}, + variable{":key", prop.type().name}, + variable{":value", prop.value()} ).executeLater().waitAndGetLater(LOG_SQL_CMD, sql::result{1, "future failed"}); }); props->registerNotifyHandler([&, weak_server, server_id, cldbid](Property& prop){ - auto server = weak_server.lock(); - if(!server && server_id != 0) { - logError(server_id, "Tried to update client permissions of a expired server!"); - return; - } + auto server = weak_server.lock(); + if(!server && server_id != 0) { + logError(server_id, "Tried to update client permissions of a expired server!"); + return; + } - std::string query; + std::string query; if(prop.type() == property::CLIENT_TOTALCONNECTIONS) query = "UPDATE `clients` SET `connections` = :value WHERE `serverId` = :serverId AND `cldbid` = :cldbid"; else if(prop.type() == property::CLIENT_NICKNAME) @@ -1056,10 +1056,10 @@ std::shared_ptr DatabaseHelper::loadClientProperties(const std::shar } void DatabaseHelper::loadStartupCache() { - this->loadStartupPermissionCache(); + this->loadStartupPermissionCache(); this->loadStartupPropertyCache(); - this->use_startup_cache = true; + this->use_startup_cache = true; } size_t DatabaseHelper::cacheBinarySize() { @@ -1075,25 +1075,25 @@ size_t DatabaseHelper::cacheBinarySize() { for(const auto& e : entry->properties) { result += sizeof(e); result += sizeof(e.get()); - result += e->value.length(); + result += e->value.length(); } } return result; } void DatabaseHelper::clearStartupCache(ts::ServerId sid) { - if(sid == 0) { - threads::MutexLock lock(this->startup_lock); - this->startup_entries.clear(); - this->use_startup_cache = false; - } else { - threads::MutexLock lock(this->startup_lock); - /* - this->startup_entries.erase(std::remove_if(this->startup_entries.begin(), this->startup_entries.end(), [&](const shared_ptr& entry) { - return entry->sid == sid; - }), this->startup_entries.end()); - */ - } + if(sid == 0) { + threads::MutexLock lock(this->startup_lock); + this->startup_entries.clear(); + this->use_startup_cache = false; + } else { + threads::MutexLock lock(this->startup_lock); + /* + this->startup_entries.erase(std::remove_if(this->startup_entries.begin(), this->startup_entries.end(), [&](const shared_ptr& entry) { + return entry->sid == sid; + }), this->startup_entries.end()); + */ + } } //SELECT `serverId`, `type`, `id`, `key`, `value` FROM properties ORDER BY `serverId` @@ -1103,82 +1103,82 @@ struct StartupPermissionArgument { }; void DatabaseHelper::loadStartupPermissionCache() { - StartupPermissionArgument arg; - sql::command(this->sql, "SELECT `serverId`, `type`, `id`, `channelId`, `permId`, `value`, `grant`, `flag_skip`, `flag_negate` FROM permissions ORDER BY `serverId`").query([&](StartupPermissionArgument* arg, int length, char** values, char** names) { - auto key = permission::PermissionTypeEntry::unknown; - permission::PermissionValue value = permNotGranted, granted = permNotGranted; - permission::PermissionSqlType type = SQL_PERM_GROUP; - bool negated = false, skipped = false; - ChannelId channel = 0; - uint64_t id = 0; - ServerId serverId = 0; + StartupPermissionArgument arg; + sql::command(this->sql, "SELECT `serverId`, `type`, `id`, `channelId`, `permId`, `value`, `grant`, `flag_skip`, `flag_negate` FROM permissions ORDER BY `serverId`").query([&](StartupPermissionArgument* arg, int length, char** values, char** names) { + auto key = permission::PermissionTypeEntry::unknown; + permission::PermissionValue value = permNotGranted, granted = permNotGranted; + permission::PermissionSqlType type = SQL_PERM_GROUP; + bool negated = false, skipped = false; + ChannelId channel = 0; + uint64_t id = 0; + ServerId serverId = 0; - int index; - try { - for(index = 0; index < length; index++) { - if(strcmp(names[index], "permId") == 0) { - key = permission::resolvePermissionData(values[index]); - if(key->type == permission::unknown || key->type == permission::undefined) { - debugMessage(0, "[SQL] Permission entry contains invalid permission type! Type: {}", values[index]); - return 0; - } - } else if(strcmp(names[index], "channelId") == 0) { - channel = stoull(values[index]); - } else if(strcmp(names[index], "id") == 0) { - id = stoull(values[index]); - } else if(strcmp(names[index], "value") == 0) { - value = stoi(values[index]); - } else if(strcmp(names[index], "grant") == 0) { - granted = stoi(values[index]); - } else if(strcmp(names[index], "flag_skip") == 0) - skipped = strcmp(values[index], "1") == 0; - else if(strcmp(names[index], "flag_negate") == 0) - negated = strcmp(values[index], "1") == 0; - else if(strcmp(names[index], "serverId") == 0) - serverId = stoll(values[index]); - else if(strcmp(names[index], "type") == 0) - type = static_cast(stoll(values[index])); - } - } catch(std::exception& ex) { - logError(0, "[SQL] Cant load permissions! Failed to parse value! Message: {}. Key: {}, Value: \"{}\"", ex.what(),names[index],values[index]); - return 0; - } + int index; + try { + for(index = 0; index < length; index++) { + if(strcmp(names[index], "permId") == 0) { + key = permission::resolvePermissionData(values[index]); + if(key->type == permission::unknown || key->type == permission::undefined) { + debugMessage(0, "[SQL] Permission entry contains invalid permission type! Type: {}", values[index]); + return 0; + } + } else if(strcmp(names[index], "channelId") == 0) { + channel = stoull(values[index]); + } else if(strcmp(names[index], "id") == 0) { + id = stoull(values[index]); + } else if(strcmp(names[index], "value") == 0) { + value = stoi(values[index]); + } else if(strcmp(names[index], "grant") == 0) { + granted = stoi(values[index]); + } else if(strcmp(names[index], "flag_skip") == 0) + skipped = strcmp(values[index], "1") == 0; + else if(strcmp(names[index], "flag_negate") == 0) + negated = strcmp(values[index], "1") == 0; + else if(strcmp(names[index], "serverId") == 0) + serverId = stoll(values[index]); + else if(strcmp(names[index], "type") == 0) + type = static_cast(stoll(values[index])); + } + } catch(std::exception& ex) { + logError(0, "[SQL] Cant load permissions! Failed to parse value! Message: {}. Key: {}, Value: \"{}\"", ex.what(),names[index],values[index]); + return 0; + } - if(key == permission::PermissionTypeEntry::unknown) { - debugMessage(0, "[SQL] Permission entry misses permission type!"); - return 0; - } - if(serverId == 0) return 0; + if(key == permission::PermissionTypeEntry::unknown) { + debugMessage(0, "[SQL] Permission entry misses permission type!"); + return 0; + } + if(serverId == 0) return 0; - if(!arg->current_server || arg->current_server->sid != serverId) { + if(!arg->current_server || arg->current_server->sid != serverId) { arg->current_server = nullptr; - { - threads::MutexLock lock(this->startup_lock); - for(const auto& entry : this->startup_entries) { - if(entry->sid == serverId) { - arg->current_server = entry; - break; - } - } - if(!arg->current_server) { - arg->current_server = make_shared(); - arg->current_server->sid = serverId; - this->startup_entries.push_back(arg->current_server); - } - } - } + { + threads::MutexLock lock(this->startup_lock); + for(const auto& entry : this->startup_entries) { + if(entry->sid == serverId) { + arg->current_server = entry; + break; + } + } + if(!arg->current_server) { + arg->current_server = make_shared(); + arg->current_server->sid = serverId; + this->startup_entries.push_back(arg->current_server); + } + } + } - auto entry = make_unique(); - entry->permission = key; - entry->type = type; - entry->value = value; - entry->grant = granted; - entry->flag_negate = negated; - entry->flag_skip = skipped; - entry->id = id; - entry->channelId = channel; - arg->current_server->permissions.push_back(std::move(entry)); - return 0; + auto entry = make_unique(); + entry->permission = key; + entry->type = type; + entry->value = value; + entry->grant = granted; + entry->flag_negate = negated; + entry->flag_skip = skipped; + entry->id = id; + entry->channelId = channel; + arg->current_server->permissions.push_back(std::move(entry)); + return 0; }, &arg); } @@ -1191,41 +1191,41 @@ void DatabaseHelper::loadStartupPropertyCache() { uint64_t id = 0; for(int index = 0; index < length; index++) { try { - if(strcmp(names[index], "key") == 0) key = values[index]; - else if(strcmp(names[index], "value") == 0) value = values[index] == nullptr ? "" : values[index]; - else if(strcmp(names[index], "type") == 0) type = (property::PropertyType) stoll(values[index]); - else if(strcmp(names[index], "serverId") == 0) serverId = stoll(values[index]); - else if(strcmp(names[index], "id") == 0) id = stoll(values[index]); + if(strcmp(names[index], "key") == 0) key = values[index]; + else if(strcmp(names[index], "value") == 0) value = values[index] == nullptr ? "" : values[index]; + else if(strcmp(names[index], "type") == 0) type = (property::PropertyType) stoll(values[index]); + else if(strcmp(names[index], "serverId") == 0) serverId = stoll(values[index]); + else if(strcmp(names[index], "id") == 0) id = stoll(values[index]); } catch(const std::exception& ex) { - logError(0, "[SQL] Cant load property! Failed to parse value! Message: {}. Key: {}, Value: \"{}\"", ex.what(),names[index],values[index]); - return 0; + logError(0, "[SQL] Cant load property! Failed to parse value! Message: {}. Key: {}, Value: \"{}\"", ex.what(),names[index],values[index]); + return 0; } } auto info = property::impl::info_key(type, key); if(info == property::PropertyDescription::unknown) { - logError(serverId, "Invalid property ({} | {})", key, type); - return 0; + logError(serverId, "Invalid property ({} | {})", key, type); + return 0; } - if(serverId == 0) return 0; + if(serverId == 0) return 0; - if(!arg->current_server || arg->current_server->sid != serverId) { + if(!arg->current_server || arg->current_server->sid != serverId) { arg->current_server = nullptr; - { - threads::MutexLock lock(this->startup_lock); - for(const auto& entry : this->startup_entries) { - if(entry->sid == serverId) { - arg->current_server = entry; - break; - } - } - if(!arg->current_server) { - arg->current_server = make_shared(); - arg->current_server->sid = serverId; - this->startup_entries.push_back(arg->current_server); - } - } - } + { + threads::MutexLock lock(this->startup_lock); + for(const auto& entry : this->startup_entries) { + if(entry->sid == serverId) { + arg->current_server = entry; + break; + } + } + if(!arg->current_server) { + arg->current_server = make_shared(); + arg->current_server->sid = serverId; + this->startup_entries.push_back(arg->current_server); + } + } + } auto entry = make_unique(); entry->info = info; @@ -1238,50 +1238,50 @@ void DatabaseHelper::loadStartupPropertyCache() { } bool DatabaseHelper::deleteGroupPermissions(const std::shared_ptr &server, ts::GroupId group_id) { - auto command = sql::command(this->sql, "DELETE FROM `permissions` WHERE `serverId` = :serverId AND `type` = :type AND `id` = :id", - variable{":serverId", server ? server->getServerId() : 0}, - variable{":type", permission::SQL_PERM_GROUP}, - variable{":id", group_id}).execute(); - LOG_SQL_CMD(command); - return !!command; + auto command = sql::command(this->sql, "DELETE FROM `permissions` WHERE `serverId` = :serverId AND `type` = :type AND `id` = :id", + variable{":serverId", server ? server->getServerId() : 0}, + variable{":type", permission::SQL_PERM_GROUP}, + variable{":id", group_id}).execute(); + LOG_SQL_CMD(command); + return !!command; } bool DatabaseHelper::deleteChannelPermissions(const std::shared_ptr &server, ts::ChannelId channel_id) { - auto command = sql::command(sql, "DELETE FROM `permissions` WHERE `serverId` = :serverId AND `channelId` = :chid", - variable{":serverId", server ? server->getServerId() : 0}, - variable{":chid", channel_id}).execute(); - LOG_SQL_CMD(command); - return !!command; + auto command = sql::command(sql, "DELETE FROM `permissions` WHERE `serverId` = :serverId AND `channelId` = :chid", + variable{":serverId", server ? server->getServerId() : 0}, + variable{":chid", channel_id}).execute(); + LOG_SQL_CMD(command); + return !!command; } std::deque> DatabaseHelper::query_properties(ts::ServerId server_id, ts::property::PropertyType type, uint64_t id) { - deque> result; + deque> result; - auto command = sql::command(this->sql, "SELECT `key`, `value`, `type` FROM properties WHERE `serverId` = :serverId AND `type` = :type AND `id` = :id", variable{":serverId", server_id}, variable{":type", type}, variable{":id", id}); - LOG_SQL_CMD(load_properties(server_id, result, command)); + auto command = sql::command(this->sql, "SELECT `key`, `value`, `type` FROM properties WHERE `serverId` = :serverId AND `type` = :type AND `id` = :id", variable{":serverId", server_id}, variable{":type", type}, variable{":id", id}); + LOG_SQL_CMD(load_properties(server_id, result, command)); - return result; + return result; } bool DatabaseHelper::deletePlaylist(const std::shared_ptr &server, ts::PlaylistId playlist_id) { - auto server_id = server ? server->getServerId() : (ServerId) 0; + auto server_id = server ? server->getServerId() : (ServerId) 0; - sql::command(this->sql, "DELETE FROM `playlists` WHERE `serverId` = :server_id AND `playlist_id` = :playlist_id", - variable{":server_id", server_id}, - variable{":playlist_id", playlist_id} - ).executeLater().waitAndGetLater(LOG_SQL_CMD, {-1, "failed to delete playlist " + to_string(playlist_id) + " from table `playlists`"}); + sql::command(this->sql, "DELETE FROM `playlists` WHERE `serverId` = :server_id AND `playlist_id` = :playlist_id", + variable{":server_id", server_id}, + variable{":playlist_id", playlist_id} + ).executeLater().waitAndGetLater(LOG_SQL_CMD, {-1, "failed to delete playlist " + to_string(playlist_id) + " from table `playlists`"}); - sql::command(this->sql, "DELETE FROM `permissions` WHERE `serverId` = :serverId AND `type` = :type AND `id` = :id", - variable{":serverId", server ? server->getServerId() : 0}, - variable{":type", permission::SQL_PERM_PLAYLIST}, - variable{":id", playlist_id} - ).executeLater().waitAndGetLater(LOG_SQL_CMD, {-1, "failed to delete playlist permissions for playlist " + to_string(playlist_id)}); + sql::command(this->sql, "DELETE FROM `permissions` WHERE `serverId` = :serverId AND `type` = :type AND `id` = :id", + variable{":serverId", server ? server->getServerId() : 0}, + variable{":type", permission::SQL_PERM_PLAYLIST}, + variable{":id", playlist_id} + ).executeLater().waitAndGetLater(LOG_SQL_CMD, {-1, "failed to delete playlist permissions for playlist " + to_string(playlist_id)}); - sql::command(this->sql, "DELETE FROM `properties` WHERE `serverId` = :serverId AND `type` = :type AND `id` = :id", - variable{":serverId", server ? server->getServerId() : 0}, - variable{":type", property::PROP_TYPE_PLAYLIST}, - variable{":id", playlist_id} - ).executeLater().waitAndGetLater(LOG_SQL_CMD, {-1, "failed to delete playlist properties for playlist " + to_string(playlist_id)}); + sql::command(this->sql, "DELETE FROM `properties` WHERE `serverId` = :serverId AND `type` = :type AND `id` = :id", + variable{":serverId", server ? server->getServerId() : 0}, + variable{":type", property::PROP_TYPE_PLAYLIST}, + variable{":id", playlist_id} + ).executeLater().waitAndGetLater(LOG_SQL_CMD, {-1, "failed to delete playlist properties for playlist " + to_string(playlist_id)}); - return true; + return true; } \ No newline at end of file diff --git a/server/src/DatabaseHelper.h b/server/src/DatabaseHelper.h index 1f6eb2d..eefaf4c 100644 --- a/server/src/DatabaseHelper.h +++ b/server/src/DatabaseHelper.h @@ -41,7 +41,7 @@ namespace ts { /* CREATE_TABLE("properties", "`serverId` INTEGER DEFAULT -1, `type` INTEGER, `id` INTEGER, `key` VARCHAR(" UNKNOWN_KEY_LENGTH "), `value` TEXT", command_append_utf8); - CREATE_TABLE("permissions", "`serverId` INT NOT NULL, `type` INT, `id` INT, `channelId` INT, `permId` VARCHAR(" UNKNOWN_KEY_LENGTH "), `value` INT, `grant` INT", command_append_utf8); + CREATE_TABLE("permissions", "`serverId` INT NOT NULL, `type` INT, `id` INT, `channelId` INT, `permId` VARCHAR(" UNKNOWN_KEY_LENGTH "), `value` INT, `grant` INT", command_append_utf8); */ struct StartupPermissionEntry { permission::PermissionSqlType type = permission::SQL_PERM_CHANNEL; @@ -70,8 +70,8 @@ namespace ts { }; struct FastPropertyEntry { - std::shared_ptr type; - std::string value; + std::shared_ptr type; + std::string value; }; class DatabaseHelper { @@ -92,25 +92,25 @@ namespace ts { std::deque> queryDatabaseInfoByUid(const std::shared_ptr &, std::deque); std::shared_ptr loadClientPermissionManager(const std::shared_ptr&, ClientDbId); //Just and write - void saveClientPermissions(const std::shared_ptr&, ClientDbId , const std::shared_ptr& /* permission manager */); + void saveClientPermissions(const std::shared_ptr&, ClientDbId , const std::shared_ptr& /* permission manager */); std::shared_ptr loadChannelPermissions(const std::shared_ptr&, ChannelId); //Just read - void saveChannelPermissions(const std::shared_ptr&, ChannelId, const std::shared_ptr& /* permission manager */); + void saveChannelPermissions(const std::shared_ptr&, ChannelId, const std::shared_ptr& /* permission manager */); - std::shared_ptr loadGroupPermissions(const std::shared_ptr&, GroupId); //Just read - void saveGroupPermissions(const std::shared_ptr&, GroupId, const std::shared_ptr& /* permission manager */); + std::shared_ptr loadGroupPermissions(const std::shared_ptr&, GroupId); //Just read + void saveGroupPermissions(const std::shared_ptr&, GroupId, const std::shared_ptr& /* permission manager */); - std::shared_ptr loadPlaylistPermissions(const std::shared_ptr&, PlaylistId /* playlist id */); //Read and write + std::shared_ptr loadPlaylistPermissions(const std::shared_ptr&, PlaylistId /* playlist id */); //Read and write std::shared_ptr loadServerProperties(const std::shared_ptr&); //Read and write - std::shared_ptr loadPlaylistProperties(const std::shared_ptr&, PlaylistId); //Read and write + std::shared_ptr loadPlaylistProperties(const std::shared_ptr&, PlaylistId); //Read and write std::shared_ptr loadChannelProperties(const std::shared_ptr&, ChannelId); //Read and write std::shared_ptr loadClientProperties(const std::shared_ptr&, ClientDbId, ClientType); bool deleteGroupPermissions(const std::shared_ptr&, GroupId); - bool deleteChannelPermissions(const std::shared_ptr&, ChannelId); - bool deletePlaylist(const std::shared_ptr&, PlaylistId /* playlist id */); - std::deque> query_properties(ServerId /* server */, property::PropertyType /* type */, uint64_t /* id */); /* required for server snapshots */ + bool deleteChannelPermissions(const std::shared_ptr&, ChannelId); + bool deletePlaylist(const std::shared_ptr&, PlaylistId /* playlist id */); + std::deque> query_properties(ServerId /* server */, property::PropertyType /* type */, uint64_t /* id */); /* required for server snapshots */ void tick(); private: diff --git a/server/src/Group.cpp b/server/src/Group.cpp index 478a6b5..00506f6 100644 --- a/server/src/Group.cpp +++ b/server/src/Group.cpp @@ -20,12 +20,12 @@ Group::Group(GroupManager* handle, GroupTarget target, GroupType type, GroupId g this->handle = handle; this->_properties = new Properties(); - this->_properties->register_property_type(); - this->setPermissionManager(make_shared()); + this->_properties->register_property_type(); + this->setPermissionManager(make_shared()); - this->properties()[property::GROUP_ID] = groupId; - this->properties()[property::GROUP_TYPE] = type; - /* + this->properties()[property::GROUP_ID] = groupId; + this->properties()[property::GROUP_TYPE] = type; + /* this->_properties->registerProperty("sgid", groupId, PROP_GROUP_INIT_SERVER); this->_properties->registerProperty("cgid", groupId, PROP_GROUP_INIT_CHANNEL); this->_properties->registerProperty("gid", groupId, PROP_PRIVATE_TEMP); @@ -60,27 +60,27 @@ Group::Group(GroupManager* handle, GroupTarget target, GroupType type, GroupId g } void Group::setPermissionManager(const std::shared_ptr &manager) { - this->_permissions = manager; - this->apply_properties_from_permissions(); + this->_permissions = manager; + this->apply_properties_from_permissions(); } void Group::apply_properties_from_permissions() { - { - auto permission = this->_permissions->permission_value_flagged(permission::i_icon_id); - this->properties()[property::GROUP_ICONID] = permission.has_value ? permission.value : 0; - } - { - auto permission = this->_permissions->permission_value_flagged(permission::i_group_show_name_in_tree); - this->properties()[property::GROUP_NAMEMODE] = permission.has_value ? permission.value : 0; - } - { - auto permission = this->_permissions->permission_value_flagged(permission::i_group_sort_id); - this->properties()[property::GROUP_SORTID] = permission.has_value ? permission.value : 0; - } - { - auto permission = this->_permissions->permission_value_flagged(permission::b_group_is_permanent); - this->properties()[property::GROUP_SAVEDB] = permission.has_value ? permission.value : 0; - } + { + auto permission = this->_permissions->permission_value_flagged(permission::i_icon_id); + this->properties()[property::GROUP_ICONID] = permission.has_value ? permission.value : 0; + } + { + auto permission = this->_permissions->permission_value_flagged(permission::i_group_show_name_in_tree); + this->properties()[property::GROUP_NAMEMODE] = permission.has_value ? permission.value : 0; + } + { + auto permission = this->_permissions->permission_value_flagged(permission::i_group_sort_id); + this->properties()[property::GROUP_SORTID] = permission.has_value ? permission.value : 0; + } + { + auto permission = this->_permissions->permission_value_flagged(permission::b_group_is_permanent); + this->properties()[property::GROUP_SAVEDB] = permission.has_value ? permission.value : 0; + } } Group::~Group() { @@ -115,41 +115,41 @@ bool GroupManager::loadGroupFormDatabase(GroupId id) { } std::vector> GroupManager::availableGroups(bool root) { - std::vector> response; - for(const auto& group : this->groups) - response.push_back(group); - if(root && this->root){ - auto elm = this->root->availableGroups(); - for(const auto& e : elm) - response.push_back(e); - } - return response; + std::vector> response; + for(const auto& group : this->groups) + response.push_back(group); + if(root && this->root){ + auto elm = this->root->availableGroups(); + for(const auto& e : elm) + response.push_back(e); + } + return response; } std::vector> GroupManager::availableServerGroups(bool root){ - std::vector> response; - for(const auto& group : this->groups) - if(group->target() == GroupTarget::GROUPTARGET_SERVER) - response.push_back(group); - if(root && this->root){ - auto elm = this->root->availableServerGroups(); - for(const auto& e : elm) - response.push_back(e); - } - return response; + std::vector> response; + for(const auto& group : this->groups) + if(group->target() == GroupTarget::GROUPTARGET_SERVER) + response.push_back(group); + if(root && this->root){ + auto elm = this->root->availableServerGroups(); + for(const auto& e : elm) + response.push_back(e); + } + return response; } std::vector> GroupManager::availableChannelGroups(bool root) { - std::vector> response; - for(const auto& group : this->groups) - if(group->target() == GroupTarget::GROUPTARGET_CHANNEL) - response.push_back(group); - if(root && this->root){ - auto elm = this->root->availableChannelGroups(true); - for(const auto& e : elm) - response.push_back(e); - } - return response; + std::vector> response; + for(const auto& group : this->groups) + if(group->target() == GroupTarget::GROUPTARGET_CHANNEL) + response.push_back(group); + if(root && this->root){ + auto elm = this->root->availableChannelGroups(true); + for(const auto& e : elm) + response.push_back(e); + } + return response; } int GroupManager::insertGroupFromDb(int count, char **values, char **column) { @@ -194,11 +194,11 @@ int GroupManager::insertGroupFromDb(int count, char **values, char **column) { else if(strcmp(columns[index], "value") == 0) value = values[index]; - auto info = property::info(key); - if(info == property::GROUP_UNDEFINED) { - logError(this->getServerId(), "Invalid property for group: " + key); - return 0; - } + auto info = property::info(key); + if(info == property::GROUP_UNDEFINED) { + logError(this->getServerId(), "Invalid property for group: " + key); + return 0; + } auto prop = g->properties()[info.property]; prop.setDbReference(true); @@ -226,14 +226,14 @@ int GroupManager::insertGroupFromDb(int count, char **values, char **column) { } void GroupManager::handleChannelDeleted(const ChannelId& channel_id) { - unique_lock cache_lock(this->cacheLock); - auto cached_clients = std::vector>{this->cachedClients.begin(), this->cachedClients.end()}; - cache_lock.unlock(); + unique_lock cache_lock(this->cacheLock); + auto cached_clients = std::vector>{this->cachedClients.begin(), this->cachedClients.end()}; + cache_lock.unlock(); - for(auto& entry : cached_clients) { - lock_guard entry_lock(entry->lock); - entry->channel_groups.erase(channel_id); - } + for(auto& entry : cached_clients) { + lock_guard entry_lock(entry->lock); + entry->channel_groups.erase(channel_id); + } } bool GroupManager::isLocalGroup(std::shared_ptr gr) { @@ -241,16 +241,16 @@ bool GroupManager::isLocalGroup(std::shared_ptr gr) { } std::shared_ptr GroupManager::defaultGroup(GroupTarget type, bool enforce_property) { - threads::MutexLock lock(this->cacheLock); + threads::MutexLock lock(this->cacheLock); if(this->groups.empty()) return nullptr; - auto server = this->server.lock(); - auto id = - server ? - server->properties()[type == GroupTarget::GROUPTARGET_SERVER ? property::VIRTUALSERVER_DEFAULT_SERVER_GROUP : property::VIRTUALSERVER_DEFAULT_CHANNEL_GROUP].as_save() : - serverInstance->properties()[property::SERVERINSTANCE_GUEST_SERVERQUERY_GROUP].as_save(); - auto group = this->findGroupLocal(id); - if(group || enforce_property) return group; + auto server = this->server.lock(); + auto id = + server ? + server->properties()[type == GroupTarget::GROUPTARGET_SERVER ? property::VIRTUALSERVER_DEFAULT_SERVER_GROUP : property::VIRTUALSERVER_DEFAULT_CHANNEL_GROUP].as_save() : + serverInstance->properties()[property::SERVERINSTANCE_GUEST_SERVERQUERY_GROUP].as_save(); + auto group = this->findGroupLocal(id); + if(group || enforce_property) return group; for(auto elm : this->groups) if(elm->target() == type) @@ -261,14 +261,14 @@ std::shared_ptr GroupManager::defaultGroup(GroupTarget type, bool enforce std::shared_ptr GroupManager::findGroup(GroupId groupId) { auto result = this->findGroupLocal(groupId); - if(!result && this->root) result = this->root->findGroup(groupId); + if(!result && this->root) result = this->root->findGroup(groupId); return result; } std::shared_ptr GroupManager::findGroupLocal(GroupId groupId) { - for(const auto& elm : this->groups) - if(elm->groupId() == groupId) return elm; - return nullptr; + for(const auto& elm : this->groups) + if(elm->groupId() == groupId) return elm; + return nullptr; } std::vector> GroupManager::findGroup(GroupTarget target, std::string name) { @@ -283,18 +283,18 @@ std::vector> GroupManager::findGroup(GroupTarget target, } ServerId GroupManager::getServerId() { - auto l = this->server.lock(); - return l ? l->getServerId() : 0; + auto l = this->server.lock(); + return l ? l->getServerId() : 0; } std::shared_ptr GroupManager::createGroup(GroupTarget target, GroupType type, std::string name) { if(type != GROUP_TYPE_NORMAL && this->root) return root->createGroup(target, type, name); - auto rawId = generateGroupId(this->sql); - if(rawId <= 0) { - logError(this->getServerId(), "Could not create a new group! ({})", "Could not generate group id"); - return nullptr; - } + auto rawId = generateGroupId(this->sql); + if(rawId <= 0) { + logError(this->getServerId(), "Could not create a new group! ({})", "Could not generate group id"); + return nullptr; + } auto groupId = (GroupId) rawId; auto res = sql::command(this->sql, "INSERT INTO `groups` (`serverId`, `groupId`, `target`, `type`, `displayName`) VALUES (:sid, :gid, :target, :type, :name)", variable{":sid", this->getServerId()}, variable{":gid", groupId}, variable{":target", target}, variable{":type", type}, variable{":name", name}).execute(); @@ -310,7 +310,7 @@ std::shared_ptr GroupManager::createGroup(GroupTarget target, GroupType t } GroupId GroupManager::copyGroup(std::shared_ptr group, GroupType type, std::string name, ServerId targetServerId) { - auto group_server = group->handle->getServerId(); + auto group_server = group->handle->getServerId(); auto groupId = generateGroupId(this->sql); auto print = LOG_SQL_CMD; @@ -327,26 +327,26 @@ GroupId GroupManager::copyGroup(std::shared_ptr group, GroupType type, st variable{":sid", targetServerId}, variable{":gid", groupId}, variable{":target", group->target()}, variable{":type", type}, variable{":name", name}).execute(); print(res); - if(targetServerId == (this->getServerId())) + if(targetServerId == (this->getServerId())) this->loadGroupFormDatabase(groupId); - else if(this->root) - this->root->loadGroupFormDatabase(groupId); + else if(this->root) + this->root->loadGroupFormDatabase(groupId); return groupId; } bool GroupManager::copyGroupPermissions(const shared_ptr &source, const shared_ptr &target) { - auto targetServer = target->handle->getServerId(); - auto sourceServer = source->handle->getServerId(); + auto targetServer = target->handle->getServerId(); + auto sourceServer = source->handle->getServerId(); - auto res = sql::command(this->sql, "DELETE FROM `permissions` WHERE `serverId` = :sid AND `type` = :type AND `id` = :id", variable{":sid", targetServer}, variable{":type", SQL_PERM_GROUP}, variable{":id", target->groupId()}).execute(); - LOG_SQL_CMD(res); + auto res = sql::command(this->sql, "DELETE FROM `permissions` WHERE `serverId` = :sid AND `type` = :type AND `id` = :id", variable{":sid", targetServer}, variable{":type", SQL_PERM_GROUP}, variable{":id", target->groupId()}).execute(); + LOG_SQL_CMD(res); res = sql::command(this->sql, "INSERT INTO `permissions` (`serverId`, `type`, `id`, `channelId`, `permId`, `value`, `grant`) SELECT :tsid AS `serverId`, `type`, :target AS `id`, 0 AS `channelId`, `permId`, `value`,`grant` FROM `permissions` WHERE `serverId` = :ssid AND `type` = :type AND `id` = :source", variable{":ssid", sourceServer}, variable{":tsid", targetServer}, variable{":type", SQL_PERM_GROUP}, variable{":source", source->groupId()}, variable{":target", target->groupId()}).execute(); - target->setPermissionManager(serverInstance->databaseHelper()->loadGroupPermissions(target->handle->server.lock(), target->groupId())); + target->setPermissionManager(serverInstance->databaseHelper()->loadGroupPermissions(target->handle->server.lock(), target->groupId())); LOG_SQL_CMD(res); - return true; + return true; } bool GroupManager::reloadGroupPermissions(std::shared_ptr group) { @@ -373,19 +373,19 @@ bool GroupManager::renameGroup(std::shared_ptr group, std::string name) { } bool GroupManager::deleteAllGroups() { - LOG_SQL_CMD(sql::command(this->sql, "DELETE FROM `groups` 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()); - { - lock_guard cache_lock(this->cacheLock); - for(const auto& entry : this->cachedClients) { - lock_guard entry_lock(entry->lock); - entry->server_groups.clear(); - entry->channel_groups.clear(); - } - } - this->groups.clear(); - return true; + LOG_SQL_CMD(sql::command(this->sql, "DELETE FROM `groups` 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()); + { + lock_guard cache_lock(this->cacheLock); + for(const auto& entry : this->cachedClients) { + lock_guard entry_lock(entry->lock); + entry->server_groups.clear(); + entry->channel_groups.clear(); + } + } + this->groups.clear(); + return true; } bool GroupManager::deleteGroup(std::shared_ptr group) { @@ -397,42 +397,42 @@ bool GroupManager::deleteGroup(std::shared_ptr 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); + { + 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& group_assignment) { - return group_assignment->group == group; - }), entry->server_groups.end()); + entry->server_groups.erase(std::remove_if(entry->server_groups.begin(), entry->server_groups.end(), [&](const std::shared_ptr& 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++; - } - } - } + 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; 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); - flag_sql |= !res; + LOG_SQL_CMD(res); + flag_sql |= !res; res = sql::command(this->sql, "DELETE FROM `assignedGroups` WHERE `serverId` = :sid AND `groupId` = :gid", variable{":sid", this->getServerId()}, variable{":gid", group->groupId()}).execute(); - LOG_SQL_CMD(res); - flag_sql |= !res; + LOG_SQL_CMD(res); + flag_sql |= !res; - flag_sql &= serverInstance->databaseHelper()->deleteGroupPermissions(this->server.lock(), group->groupId()); - if(flag_sql) - logError(this->getServerId(), "Could not delete group {} ({}) from database. May leader to invalid data", group->name(), group->groupId()); + flag_sql &= serverInstance->databaseHelper()->deleteGroupPermissions(this->server.lock(), group->groupId()); + if(flag_sql) + logError(this->getServerId(), "Could not delete group {} ({}) from database. May leader to invalid data", group->name(), group->groupId()); return true; } int64_t GroupManager::generateGroupId(sql::SqlManager* sql) { - int64_t hightestGroupId = 0; + int64_t hightestGroupId = 0; sql::command(sql, "SELECT `groupId` FROM `groups` ORDER BY `groupId` DESC LIMIT 1").query([](int64_t* ptr, int, char** values, char**){ *ptr = stoul(values[0]); return 0; @@ -441,125 +441,125 @@ int64_t GroupManager::generateGroupId(sql::SqlManager* sql) { } std::deque GroupManager::update_server_group_property(const shared_ptr &client, bool channel_lock, const std::shared_ptr& channel) { - std::deque changed; + std::deque changed; - //Server groups - { - auto groups = this->getServerGroups(client->getClientDatabaseId(), client->getType()); - string group_string; - for(const auto& group : groups) - if(group_string.empty()) group_string += to_string(group->group->groupId()); - else group_string += "," + to_string(group->group->groupId()); - if(client->properties()[property::CLIENT_SERVERGROUPS] != group_string) { - client->properties()[property::CLIENT_SERVERGROUPS] = group_string; - changed.push_back(property::CLIENT_SERVERGROUPS); + //Server groups + { + auto groups = this->getServerGroups(client->getClientDatabaseId(), client->getType()); + string group_string; + for(const auto& group : groups) + if(group_string.empty()) group_string += to_string(group->group->groupId()); + else group_string += "," + to_string(group->group->groupId()); + if(client->properties()[property::CLIENT_SERVERGROUPS] != group_string) { + client->properties()[property::CLIENT_SERVERGROUPS] = group_string; + changed.push_back(property::CLIENT_SERVERGROUPS); - unique_lock chan_lock(client->channel_lock, defer_lock); - if(channel_lock) - chan_lock.lock(); + unique_lock chan_lock(client->channel_lock, defer_lock); + if(channel_lock) + chan_lock.lock(); - client->cached_server_groups.clear(); - client->cached_server_groups.reserve(groups.size()); - for(const auto& group : groups) - client->cached_server_groups.push_back(group->group->groupId()); - } + client->cached_server_groups.clear(); + client->cached_server_groups.reserve(groups.size()); + for(const auto& group : groups) + client->cached_server_groups.push_back(group->group->groupId()); + } - } + } - //Channel groups - if(channel){ - shared_ptr group = this->getChannelGroup(client->getClientDatabaseId(), channel, true); - if(client->properties()[property::CLIENT_CHANNEL_GROUP_INHERITED_CHANNEL_ID] != group->channelId) { - client->properties()[property::CLIENT_CHANNEL_GROUP_INHERITED_CHANNEL_ID] = group->channelId; - changed.push_back(property::CLIENT_CHANNEL_GROUP_INHERITED_CHANNEL_ID); - } - if(client->properties()[property::CLIENT_CHANNEL_GROUP_ID] != group->group->groupId()) { - client->properties()[property::CLIENT_CHANNEL_GROUP_ID] = group->group->groupId(); - changed.push_back(property::CLIENT_CHANNEL_GROUP_ID); + //Channel groups + if(channel){ + shared_ptr group = this->getChannelGroup(client->getClientDatabaseId(), channel, true); + if(client->properties()[property::CLIENT_CHANNEL_GROUP_INHERITED_CHANNEL_ID] != group->channelId) { + client->properties()[property::CLIENT_CHANNEL_GROUP_INHERITED_CHANNEL_ID] = group->channelId; + changed.push_back(property::CLIENT_CHANNEL_GROUP_INHERITED_CHANNEL_ID); + } + if(client->properties()[property::CLIENT_CHANNEL_GROUP_ID] != group->group->groupId()) { + client->properties()[property::CLIENT_CHANNEL_GROUP_ID] = group->group->groupId(); + changed.push_back(property::CLIENT_CHANNEL_GROUP_ID); - unique_lock chan_lock(client->channel_lock, defer_lock); - if(channel_lock) - chan_lock.lock(); + unique_lock chan_lock(client->channel_lock, defer_lock); + if(channel_lock) + chan_lock.lock(); - client->cached_channel_group = group->group->groupId(); - } - } + client->cached_channel_group = group->group->groupId(); + } + } - if(!changed.empty()) - client->join_state_id++; /* groups have changed :) */ + if(!changed.empty()) + client->join_state_id++; /* groups have changed :) */ - return changed; + return changed; } void GroupManager::cleanupAssignments(ClientDbId client) { - if(this->root) - this->root->cleanupAssignments(client); + if(this->root) + this->root->cleanupAssignments(client); for(const auto& assignment : this->getAssignedServerGroups(client)) { - if(!assignment->group->is_permanent()) - this->removeServerGroup(client, assignment->group); + if(!assignment->group->is_permanent()) + this->removeServerGroup(client, assignment->group); } } void GroupManager::enableCache(const ClientDbId& client_database_id) { if(this->root) - this->root->enableCache(client_database_id); + this->root->enableCache(client_database_id); unique_lock cache_lock(this->cacheLock); /* 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 */ - } + if(entry->client_database_id == client_database_id) { + entry->use_count++; + return; /* client already cached, no need to cache client */ + } auto entry = std::make_shared(); entry->client_database_id = client_database_id; - entry->use_count++; + entry->use_count++; this->cachedClients.push_back(entry); - lock_guard client_cache_lock(entry->lock); /* lock the client because we're currently loading the cache. */ - cache_lock.unlock(); + 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) { + .query([&](int length, std::string* value, std::string* column) { shared_ptr group = nullptr; time_point until; ChannelId channelId = 0; for(int index = 0; index < length; index++){ try { - if(column[index] == "groupId"){ - group = this->findGroup(stoll(value[index])); - } else if(column[index] == "until"){ - until = time_point() + 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; - } + if(column[index] == "groupId"){ + group = this->findGroup(stoll(value[index])); + } else if(column[index] == "until"){ + until = time_point() + 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; + } } catch(std::exception& ex) { - logError(this->getServerId(), "Failed to load group assignment from database for client {}. Column {} contains an invalid value: {}", client_database_id, column[index], value[index]); - return 0; + logError(this->getServerId(), "Failed to load group assignment from database for client {}. Column {} contains an invalid value: {}", client_database_id, column[index], value[index]); + return 0; } } if(!group) { - return 0; + return 0; } auto assignment = std::make_shared(); assignment->group = group; assignment->until = until; assignment->parent = &*entry; - assignment->channelId = channelId; - assignment->server = this->getServerId(); + assignment->channelId = channelId; + assignment->server = this->getServerId(); if(channelId == 0) - entry->server_groups.push_back(assignment); + entry->server_groups.push_back(assignment); else - entry->channel_groups[channelId] = assignment; + entry->channel_groups[channelId] = assignment; return 0; }); } @@ -567,17 +567,17 @@ void GroupManager::enableCache(const ClientDbId& client_database_id) { //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 instance void GroupManager::disableCache(const ClientDbId& client_database_id) { if(this->root) { - this->root->disableCache(client_database_id); + this->root->disableCache(client_database_id); } - lock_guard cache_lock(this->cacheLock); - this->cachedClients.erase(std::remove_if(this->cachedClients.begin(), this->cachedClients.end(), [&](const std::shared_ptr& client) { - if(client->client_database_id != client_database_id) - return false; + lock_guard cache_lock(this->cacheLock); + this->cachedClients.erase(std::remove_if(this->cachedClients.begin(), this->cachedClients.end(), [&](const std::shared_ptr& client) { + if(client->client_database_id != client_database_id) + return false; - lock_guard client_lock{client->lock}; - return (--client->use_count) == 0; - }), this->cachedClients.end()); + lock_guard client_lock{client->lock}; + return (--client->use_count) == 0; + }), this->cachedClients.end()); } void GroupManager::clearCache() { @@ -588,7 +588,7 @@ void GroupManager::clearCache() { } bool GroupManager::isClientCached(const ClientDbId& client_database_id) { - return this->resolve_cached_client(client_database_id) == nullptr; + return this->resolve_cached_client(client_database_id) == nullptr; } typedef std::vector> ResList; @@ -635,13 +635,13 @@ vector> GroupManager::listGroupAssignments(ClientDbI auto cached = resolve_cached_client(cldbId); if(cached) { - { - lock_guard lock{cached->lock}; - for(const auto &serverGroup : cached->server_groups) - result.push_back(serverGroup); - for(auto& channelGroup : cached->channel_groups) - result.push_back(channelGroup.second); - } + { + lock_guard lock{cached->lock}; + for(const auto &serverGroup : cached->server_groups) + result.push_back(serverGroup); + for(auto& channelGroup : cached->channel_groups) + result.push_back(channelGroup.second); + } if(this->root){ auto append = this->root->listGroupAssignments(cldbId); @@ -654,7 +654,7 @@ vector> 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([&](int length, char** value, char** column){ + .query([&](int length, char** value, char** column){ shared_ptr group = nullptr; time_point until; uint64_t channelId = 0; @@ -696,152 +696,152 @@ vector> GroupManager::listGroupAssignments(ClientDbI } std::shared_ptr GroupManager::resolve_cached_client(ClientDbId client_database_id) { - { - lock_guard lock(this->cacheLock); - for(auto& entry : this->cachedClients) - if(entry->client_database_id == client_database_id) - return entry; - } + { + lock_guard lock(this->cacheLock); + for(auto& entry : this->cachedClients) + if(entry->client_database_id == client_database_id) + return entry; + } - return nullptr; + return nullptr; } std::vector> GroupManager::getAssignedServerGroups(ClientDbId cldbid) { - auto cached = this->resolve_cached_client(cldbid); - sql::result res; - std::vector> result; - if(this->root) { - auto root = this->root->getAssignedServerGroups(cldbid); - result.insert(result.begin(), root.begin(), root.end()); - } + auto cached = this->resolve_cached_client(cldbid); + sql::result res; + std::vector> result; + if(this->root) { + auto root = this->root->getAssignedServerGroups(cldbid); + result.insert(result.begin(), root.begin(), root.end()); + } - if(cached) { - lock_guard cache_lock{cached->lock}; - result.insert(result.end(), cached->server_groups.begin(), cached->server_groups.end()); - return result; - } + if(cached) { + lock_guard cache_lock{cached->lock}; + result.insert(result.end(), cached->server_groups.begin(), cached->server_groups.end()); + return result; + } - debugMessage(this->getServerId(), "Query client groups for client {} on server {}.", cldbid, 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([&](int length, char** value, char** column){ - shared_ptr group = nullptr; - time_point until; + debugMessage(this->getServerId(), "Query client groups for client {} on server {}.", cldbid, 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([&](int length, char** value, char** column){ + shared_ptr group = nullptr; + time_point until; - for(int index = 0; index < length; index++){ - if(value[index] == nullptr) { - logError(this->getServerId(), string() + "Invalid value at " + column[index]); - continue; - } - if(strcmp(column[index], "groupId") == 0 && value[index] != nullptr){ - group = this->findGroup(stoll(value[index])); - } else if(strcmp(column[index], "until") == 0){ - until = time_point() + milliseconds(stoll(value[index] == nullptr ? "0" : value[index])); - } else cerr << "Invalid column " << column[index] << endl; - } - if(!group) - return 0; + for(int index = 0; index < length; index++){ + if(value[index] == nullptr) { + logError(this->getServerId(), string() + "Invalid value at " + column[index]); + continue; + } + if(strcmp(column[index], "groupId") == 0 && value[index] != nullptr){ + group = this->findGroup(stoll(value[index])); + } else if(strcmp(column[index], "until") == 0){ + until = time_point() + milliseconds(stoll(value[index] == nullptr ? "0" : value[index])); + } else cerr << "Invalid column " << column[index] << endl; + } + if(!group) + return 0; - shared_ptr assignment = std::make_shared(); - assignment->parent = nullptr; - assignment->group = group; - assignment->until = until; - assignment->server = this->getServerId(); - result.push_back(assignment); - return 0; - }); - LOG_SQL_CMD(res); - return result; + shared_ptr assignment = std::make_shared(); + assignment->parent = nullptr; + assignment->group = group; + assignment->until = until; + assignment->server = this->getServerId(); + result.push_back(assignment); + return 0; + }); + LOG_SQL_CMD(res); + return result; } std::vector> GroupManager::getServerGroups(ClientDbId cldbid, server::ClientType type) { auto result = this->getAssignedServerGroups(cldbid); - if(result.empty()) return this->defaultServerGroupGroupAssignments(cldbid, type); - return result; + if(result.empty()) return this->defaultServerGroupGroupAssignments(cldbid, type); + return result; } std::vector> GroupManager::defaultServerGroupGroupAssignments(ClientDbId client, ClientType type) { - std::vector> result; - if(type == ClientType::CLIENT_QUERY && this->root) { - auto root = this->root->defaultServerGroupGroupAssignments(client, type); - result.insert(result.begin(), root.begin(), root.end()); - } else if(type == ClientType::CLIENT_MUSIC) { - threads::MutexLock lock(this->cacheLock); + std::vector> result; + if(type == ClientType::CLIENT_QUERY && this->root) { + auto root = this->root->defaultServerGroupGroupAssignments(client, type); + result.insert(result.begin(), root.begin(), root.end()); + } else if(type == ClientType::CLIENT_MUSIC) { + threads::MutexLock lock(this->cacheLock); - auto server = this->server.lock(); - auto id = - server ? - server->properties()[property::VIRTUALSERVER_DEFAULT_MUSIC_GROUP].as_save() : - serverInstance->properties()[property::SERVERINSTANCE_TEMPLATE_MUSICDEFAULT_GROUP].as_save(); - auto group = this->findGroupLocal(id); - if(group) { - result.push_back(std::make_shared(nullptr, this->getServerId(), 0, group, time_point())); - return result; - } - } - result.push_back(std::make_shared(nullptr, this->getServerId(), 0, this->defaultGroup(GroupTarget::GROUPTARGET_SERVER), time_point())); - return result; + auto server = this->server.lock(); + auto id = + server ? + server->properties()[property::VIRTUALSERVER_DEFAULT_MUSIC_GROUP].as_save() : + serverInstance->properties()[property::SERVERINSTANCE_TEMPLATE_MUSICDEFAULT_GROUP].as_save(); + auto group = this->findGroupLocal(id); + if(group) { + result.push_back(std::make_shared(nullptr, this->getServerId(), 0, group, time_point())); + return result; + } + } + result.push_back(std::make_shared(nullptr, this->getServerId(), 0, this->defaultGroup(GroupTarget::GROUPTARGET_SERVER), time_point())); + return result; } std::shared_ptr GroupManager::getChannelGroupExact(ClientDbId cldbId, const std::shared_ptr& channel, bool assign_default) { - auto cached = resolve_cached_client(cldbId); - if(cached) { - lock_guard cache_lock(cached->lock); - if(cached->channel_groups.count(channel->channelId()) > 0) { - return cached->channel_groups[channel->channelId()]; - } else - return assign_default ? this->defaultChannelGroupAssignment(cldbId, channel) : nullptr; - } + auto cached = resolve_cached_client(cldbId); + if(cached) { + lock_guard cache_lock(cached->lock); + if(cached->channel_groups.count(channel->channelId()) > 0) { + return cached->channel_groups[channel->channelId()]; + } else + return assign_default ? this->defaultChannelGroupAssignment(cldbId, channel) : nullptr; + } - std::shared_ptr 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){ - shared_ptr group = nullptr; - time_point until; + std::shared_ptr 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){ + shared_ptr group = nullptr; + time_point until; - for(int index = 0; index < length; index++){ - if(value[index] == nullptr) { - logError(this->getServerId(), string() + "Invalid value at " + column[index]); - continue; - } - if(strcmp(column[index], "groupId") == 0){ - group = this->findGroup(stoll(value[index])); - } else if(strcmp(column[index], "until") == 0){ - until = time_point() + milliseconds(stoll(value[index])); - } else cerr << "Invalid column " << column[index] << endl; - } - if(!group) - return 0; + for(int index = 0; index < length; index++){ + if(value[index] == nullptr) { + logError(this->getServerId(), string() + "Invalid value at " + column[index]); + continue; + } + if(strcmp(column[index], "groupId") == 0){ + group = this->findGroup(stoll(value[index])); + } else if(strcmp(column[index], "until") == 0){ + until = time_point() + milliseconds(stoll(value[index])); + } else cerr << "Invalid column " << column[index] << endl; + } + if(!group) + return 0; - shared_ptr assignment = std::make_shared(); - assignment->parent = nullptr; - assignment->group = group; - assignment->until = until; - assignment->server = this->getServerId(); - assignment->channelId = channel->channelId(); - result = std::move(assignment); - return 0; - }); - (LOG_SQL_CMD)(res); + shared_ptr assignment = std::make_shared(); + assignment->parent = nullptr; + assignment->group = group; + assignment->until = until; + assignment->server = this->getServerId(); + assignment->channelId = channel->channelId(); + result = std::move(assignment); + return 0; + }); + (LOG_SQL_CMD)(res); - return !result && assign_default ? this->defaultChannelGroupAssignment(cldbId, channel) : result; + return !result && assign_default ? this->defaultChannelGroupAssignment(cldbId, channel) : result; } std::shared_ptr GroupManager::getChannelGroup(ClientDbId cldbId, const shared_ptr &channel, bool assign_default) { - shared_ptr group; - std::shared_ptr inheritance_channel = channel; - while(inheritance_channel && !group) { - group = this->getChannelGroupExact(cldbId, inheritance_channel, false); - if(!group) { - auto inheritance = inheritance_channel->permissions()->permission_value_flagged(permission::b_channel_group_inheritance_end); - if(inheritance.has_value && inheritance.value == 1) - break; + shared_ptr group; + std::shared_ptr inheritance_channel = channel; + while(inheritance_channel && !group) { + group = this->getChannelGroupExact(cldbId, inheritance_channel, false); + if(!group) { + auto inheritance = inheritance_channel->permissions()->permission_value_flagged(permission::b_channel_group_inheritance_end); + if(inheritance.has_value && inheritance.value == 1) + break; - inheritance_channel = inheritance_channel->parent(); - } - } - return !group && assign_default ? this->defaultChannelGroupAssignment(cldbId, channel) : group; + inheritance_channel = inheritance_channel->parent(); + } + } + return !group && assign_default ? this->defaultChannelGroupAssignment(cldbId, channel) : group; } std::shared_ptr GroupManager::defaultChannelGroupAssignment(ClientDbId cldbId, const std::shared_ptr &channel) { - return std::make_shared(nullptr, this->getServerId(), channel->channelId(), this->defaultGroup(GroupTarget::GROUPTARGET_CHANNEL), time_point()); + return std::make_shared(nullptr, this->getServerId(), channel->channelId(), this->defaultGroup(GroupTarget::GROUPTARGET_CHANNEL), time_point()); } void GroupManager::addServerGroup(ClientDbId cldbId, std::shared_ptr group, time_point until) { @@ -855,8 +855,8 @@ void GroupManager::addServerGroup(ClientDbId cldbId, std::shared_ptr grou auto cached = resolve_cached_client(cldbId); if(cached) { - lock_guard cache_lock(cached->lock); - cached->server_groups.push_back(std::make_shared(cached.get(), this->getServerId(), 0, group, until)); + lock_guard cache_lock(cached->lock); + cached->server_groups.push_back(std::make_shared(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)", @@ -872,10 +872,10 @@ void GroupManager::removeServerGroup(ClientDbId cldbId, std::shared_ptr g if(!this->hasServerGroupAssigned(cldbId, group)) return; auto cached = resolve_cached_client(cldbId); if(cached) { - lock_guard cache_lock(cached->lock); - cached->server_groups.erase(std::remove_if(cached->server_groups.begin(), cached->server_groups.end(), [&](const std::shared_ptr& group_assignment) { - return group_assignment->group == group; - }), cached->server_groups.end()); + lock_guard cache_lock(cached->lock); + cached->server_groups.erase(std::remove_if(cached->server_groups.begin(), cached->server_groups.end(), [&](const std::shared_ptr& group_assignment) { + return group_assignment->group == group; + }), cached->server_groups.end()); } sql::command(this->sql, "DELETE FROM `assignedGroups` WHERE `serverId` = :sid AND `cldbid` = :cldbid AND `groupId` = :gid AND `channelId` = :chid", @@ -887,19 +887,19 @@ void GroupManager::removeServerGroup(ClientDbId cldbId, std::shared_ptr g } void GroupManager::setChannelGroup(ClientDbId cldbId, std::shared_ptr group, std::shared_ptr channel, time_point until) { - auto old_group = getChannelGroupExact(cldbId, channel, false); - if(old_group) { - if(old_group->group == group) return; - } else if(!group) return; + auto old_group = getChannelGroupExact(cldbId, channel, false); + if(old_group) { + if(old_group->group == 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 = resolve_cached_client(cldbId); if(cached) { - lock_guard cache_lock(cached->lock); - if(default_group) - cached->channel_groups.erase(channel->channelId()); - else - cached->channel_groups[channel->channelId()] = std::make_shared(cached.get(), this->getServerId(), channel->channelId(), group, until); + lock_guard cache_lock(cached->lock); + if(default_group) + cached->channel_groups.erase(channel->channelId()); + else + cached->channel_groups[channel->channelId()] = std::make_shared(cached.get(), this->getServerId(), channel->channelId(), group, until); } sql::command(this->sql, "DELETE FROM `assignedGroups` WHERE `serverId` = :sid AND `cldbid` = :cldbid AND `channelId` = :chid", @@ -908,13 +908,13 @@ void GroupManager::setChannelGroup(ClientDbId cldbId, std::shared_ptr gro variable{":chid", channel->channelId()}) .executeLater().waitAndGetLater(LOG_SQL_CMD, {1, "future failed"}); - if(!default_group) { - sql::command(this->sql, "INSERT INTO `assignedGroups` (`serverId`, `cldbid`, `groupId`, `channelId`, `until`) VALUES (:sid, :cldbid, :gid, :chid, :until)", - variable{":sid", this->getServerId()}, - variable{":cldbid", cldbId}, - variable{":gid", group->groupId()}, - variable{":chid", channel->channelId()}, - variable{":until", time_point_cast(until).time_since_epoch().count()}) - .executeLater().waitAndGetLater(LOG_SQL_CMD, {1, "future failed"}); - } + if(!default_group) { + sql::command(this->sql, "INSERT INTO `assignedGroups` (`serverId`, `cldbid`, `groupId`, `channelId`, `until`) VALUES (:sid, :cldbid, :gid, :chid, :until)", + variable{":sid", this->getServerId()}, + variable{":cldbid", cldbId}, + variable{":gid", group->groupId()}, + variable{":chid", channel->channelId()}, + variable{":until", time_point_cast(until).time_since_epoch().count()}) + .executeLater().waitAndGetLater(LOG_SQL_CMD, {1, "future failed"}); + } } \ No newline at end of file diff --git a/server/src/Group.h b/server/src/Group.h index 07f9f03..c3ce431 100644 --- a/server/src/Group.h +++ b/server/src/Group.h @@ -17,7 +17,7 @@ namespace ts { class ConnectedClient; } - struct CachedClient; + struct CachedClient; class GroupManager; class Group; @@ -82,7 +82,7 @@ namespace ts { Group(GroupManager* handle, GroupTarget target, GroupType type, GroupId groupId); ~Group(); - std::shared_ptr permissions(){ return this->_permissions; } + std::shared_ptr permissions(){ return this->_permissions; } Properties& properties(){ return *this->_properties; } GroupNameMode nameMode(){ return (GroupNameMode) (uint8_t) properties()[property::GROUP_NAMEMODE]; } @@ -97,43 +97,43 @@ namespace ts { void apply_properties_from_permissions(); inline permission::PermissionValue updateType() { - auto permission_manager = this->permissions(); /* copy the manager */ - assert(permission_manager); - const auto data = permission_manager->permission_value_flagged(permission::i_group_auto_update_type); - return data.has_value ? data.value : 0; - } - - inline bool permission_granted(const permission::PermissionType& permission, const permission::v2::PermissionFlaggedValue& granted_value, bool require_granted_value) { - auto permission_manager = this->permissions(); /* copy the manager */ - assert(permission_manager); - const auto data = permission_manager->permission_value_flagged(permission); - if(!data.has_value) { - return !require_granted_value || granted_value.has_value; - } - if(!granted_value.has_value) { - return false; - } - if(data.value == -1) { - return granted_value.value == -1; - } - return granted_value.value >= data.value; - } - - inline bool is_permanent() { - auto permission_manager = this->permissions(); /* copy the manager */ - assert(permission_manager); - const auto data = permission_manager->permission_value_flagged(permission::b_group_is_permanent); - return data.has_value ? data.value == 1 : false; + auto permission_manager = this->permissions(); /* copy the manager */ + assert(permission_manager); + const auto data = permission_manager->permission_value_flagged(permission::i_group_auto_update_type); + return data.has_value ? data.value : 0; } - inline IconId icon_id() { - auto permission_manager = this->permissions(); /* copy the manager */ - assert(permission_manager); - const auto data = permission_manager->permission_value_flagged(permission::i_icon_id); - return data.has_value ? data.value : 0; - } + inline bool permission_granted(const permission::PermissionType& permission, const permission::v2::PermissionFlaggedValue& granted_value, bool require_granted_value) { + auto permission_manager = this->permissions(); /* copy the manager */ + assert(permission_manager); + const auto data = permission_manager->permission_value_flagged(permission); + if(!data.has_value) { + return !require_granted_value || granted_value.has_value; + } + if(!granted_value.has_value) { + return false; + } + if(data.value == -1) { + return granted_value.value == -1; + } + return granted_value.value >= data.value; + } + + inline bool is_permanent() { + auto permission_manager = this->permissions(); /* copy the manager */ + assert(permission_manager); + const auto data = permission_manager->permission_value_flagged(permission::b_group_is_permanent); + return data.has_value ? data.value == 1 : false; + } + + inline IconId icon_id() { + auto permission_manager = this->permissions(); /* copy the manager */ + assert(permission_manager); + const auto data = permission_manager->permission_value_flagged(permission::i_icon_id); + return data.has_value ? data.value : 0; + } private: - void setPermissionManager(const std::shared_ptr& manager); + void setPermissionManager(const std::shared_ptr& manager); GroupManager* handle; std::shared_ptr _permissions; @@ -154,7 +154,7 @@ namespace ts { bool loadGroupFormDatabase(GroupId id = 0); - std::vector> availableGroups(bool root = true); + std::vector> availableGroups(bool root = true); std::vector> availableServerGroups(bool root = true); std::vector> availableChannelGroups(bool root = true); @@ -165,9 +165,9 @@ namespace ts { } std::shared_ptr get_group_assignment(const ClientDbId& client_database_id, const std::shared_ptr& group) { - for(const auto& assign : this->getAssignedServerGroups(client_database_id)) - if(assign->group == group) return assign; - return nullptr; + for(const auto& assign : this->getAssignedServerGroups(client_database_id)) + if(assign->group == group) return assign; + return nullptr; } std::vector> getServerGroups(ClientDbId cldbid, server::ClientType type); @@ -177,13 +177,13 @@ namespace ts { } - //Gets the channel group (may inherited) + //Gets the channel group (may inherited) std::shared_ptr getChannelGroup(ClientDbId cldbId, const std::shared_ptr &, bool assign_default); - //Gets the channel group within the channel - std::shared_ptr getChannelGroupExact(ClientDbId cldbId, const std::shared_ptr&, bool assign_default); + //Gets the channel group within the channel + std::shared_ptr getChannelGroupExact(ClientDbId cldbId, const std::shared_ptr&, bool assign_default); - std::vector> defaultServerGroupGroupAssignments(ClientDbId, server::ClientType); - std::shared_ptr defaultChannelGroupAssignment(ClientDbId cldbId, const std::shared_ptr &); + std::vector> defaultServerGroupGroupAssignments(ClientDbId, server::ClientType); + std::shared_ptr defaultChannelGroupAssignment(ClientDbId cldbId, const std::shared_ptr &); void addServerGroup(ClientDbId cldbId, std::shared_ptr, std::chrono::time_point until = std::chrono::time_point()); void removeServerGroup(ClientDbId cldbId, std::shared_ptr); @@ -204,7 +204,7 @@ namespace ts { void cleanupAssignments(ClientDbId); std::shared_ptr findGroup(GroupId); - std::shared_ptr findGroupLocal(GroupId); + std::shared_ptr findGroupLocal(GroupId); std::vector> findGroup(GroupTarget target, std::string); std::shared_ptr defaultGroup(GroupTarget type, bool enforce_property = false); @@ -221,8 +221,8 @@ namespace ts { void handleChannelDeleted(const ChannelId& /* channel id */); private: std::shared_ptr root = nullptr; - std::weak_ptr server; - ServerId getServerId(); + std::weak_ptr server; + ServerId getServerId(); sql::SqlManager* sql; std::vector> groups; diff --git a/server/src/InstanceHandler.cpp b/server/src/InstanceHandler.cpp index e37ed8f..1d71bac 100644 --- a/server/src/InstanceHandler.cpp +++ b/server/src/InstanceHandler.cpp @@ -25,7 +25,7 @@ #include #ifndef _POSIX_SOURCE - #define _POSIX_SOURCE + #define _POSIX_SOURCE #endif #include #undef _POSIX_SOURCE @@ -39,20 +39,20 @@ using namespace ts::server; extern bool mainThreadActive; InstanceHandler::InstanceHandler(SqlDataManager *sql) : sql(sql) { - serverInstance = this; - this->tick_manager = make_shared(config::threads::ticking, "tick task "); - this->statistics = make_shared(nullptr, true); - this->statistics->measure_bandwidths(true); + serverInstance = this; + this->tick_manager = make_shared(config::threads::ticking, "tick task "); + this->statistics = make_shared(nullptr, true); + this->statistics->measure_bandwidths(true); this->licenseHelper = make_shared(); - this->dbHelper = new DatabaseHelper(this->getSql()); + this->dbHelper = new DatabaseHelper(this->getSql()); this->_properties = new Properties(); - this->_properties->register_property_type(); - this->properties()[property::SERVERINSTANCE_FILETRANSFER_PORT] = ts::config::binding::DefaultFilePort; - this->properties()[property::SERVERINSTANCE_FILETRANSFER_HOST] = ts::config::binding::DefaultFileHost; - this->properties()[property::SERVERINSTANCE_QUERY_PORT] = ts::config::binding::DefaultQueryPort; - this->properties()[property::SERVERINSTANCE_QUERY_HOST] = ts::config::binding::DefaultQueryHost; + this->_properties->register_property_type(); + this->properties()[property::SERVERINSTANCE_FILETRANSFER_PORT] = ts::config::binding::DefaultFilePort; + this->properties()[property::SERVERINSTANCE_FILETRANSFER_HOST] = ts::config::binding::DefaultFileHost; + this->properties()[property::SERVERINSTANCE_QUERY_PORT] = ts::config::binding::DefaultQueryPort; + this->properties()[property::SERVERINSTANCE_QUERY_HOST] = ts::config::binding::DefaultQueryHost; auto result = sql::command(this->getSql(), "SELECT * FROM `properties` WHERE `id` = :id AND `type` = :type AND `serverId` = :serverId", variable{":id", 0}, variable{":serverId", 0}, variable{":type", property::PropertyType::PROP_TYPE_INSTANCE}) @@ -66,11 +66,11 @@ InstanceHandler::InstanceHandler(SqlDataManager *sql) : sql(sql) { } } - const auto &info = property::impl::info(key); - if(*info == property::SERVERINSTANCE_UNDEFINED) { - logError(0, "Got an unknown instance property " + key); - return 0; - } + const auto &info = property::impl::info(key); + if(*info == property::SERVERINSTANCE_UNDEFINED) { + logError(0, "Got an unknown instance property " + key); + return 0; + } auto prop = instance->properties()[info]; prop = value; prop.setDbReference(true); @@ -95,86 +95,86 @@ InstanceHandler::InstanceHandler(SqlDataManager *sql) : sql(sql) { sql::command(this->getSql(), sqlQuery, variable{":sid", 0}, variable{":type", property::PropertyType::PROP_TYPE_INSTANCE}, variable{":id", 0}, variable{":key", prop.type().name}, variable{":value", prop.value()}) .executeLater().waitAndGetLater(LOG_SQL_CMD, sql::result{1, "future failed"}); }); - this->properties()[property::SERVERINSTANCE_DATABASE_VERSION] = this->sql->get_database_version(); - this->properties()[property::SERVERINSTANCE_PERMISSIONS_VERSION] = this->sql->get_permissions_version(); + this->properties()[property::SERVERINSTANCE_DATABASE_VERSION] = this->sql->get_database_version(); + this->properties()[property::SERVERINSTANCE_PERMISSIONS_VERSION] = this->sql->get_permissions_version(); globalServerAdmin = std::make_shared(this->getSql(), nullptr, "serveradmin", true); static_pointer_cast(globalServerAdmin)->setSharedLock(globalServerAdmin); ts::server::DatabaseHelper::assignDatabaseId(this->getSql(), 0, globalServerAdmin); - this->_musicRoot = std::make_shared(this->getSql(), nullptr, "Music Manager", false); - static_pointer_cast(this->_musicRoot)->setSharedLock(this->_musicRoot); + this->_musicRoot = std::make_shared(this->getSql(), nullptr, "Music Manager", false); + static_pointer_cast(this->_musicRoot)->setSharedLock(this->_musicRoot); - { - this->groupManager = std::make_shared(nullptr, this->getSql()); - this->groupManager->loadGroupFormDatabase(); + { + this->groupManager = std::make_shared(nullptr, this->getSql()); + this->groupManager->loadGroupFormDatabase(); - if (this->groupManager->availableServerGroups(false).empty()) { - if(!this->setupDefaultGroups()){ - logCritical(LOG_INSTANCE, "Could not setup server instance! Stopping..."); - mainThreadActive = false; - return; - } - } + if (this->groupManager->availableServerGroups(false).empty()) { + if(!this->setupDefaultGroups()){ + logCritical(LOG_INSTANCE, "Could not setup server instance! Stopping..."); + mainThreadActive = false; + return; + } + } - debugMessage(LOG_INSTANCE, "Instance admin group id " + to_string(this->properties()[property::SERVERINSTANCE_ADMIN_SERVERQUERY_GROUP].as())); - auto instance_server_admin = this->groupManager->findGroup(this->properties()[property::SERVERINSTANCE_ADMIN_SERVERQUERY_GROUP].as()); - if (!instance_server_admin) { - instance_server_admin = this->groupManager->availableServerGroups(false).front(); - logCritical(LOG_INSTANCE, "Missing instance server admin group! Using first available (" + (instance_server_admin ? instance_server_admin->name() : "nil") + ")"); - } - if(this->groupManager->listGroupAssignments(this->globalServerAdmin->getClientDatabaseId()).empty()) - this->groupManager->addServerGroup(this->globalServerAdmin->getClientDatabaseId(), instance_server_admin); + debugMessage(LOG_INSTANCE, "Instance admin group id " + to_string(this->properties()[property::SERVERINSTANCE_ADMIN_SERVERQUERY_GROUP].as())); + auto instance_server_admin = this->groupManager->findGroup(this->properties()[property::SERVERINSTANCE_ADMIN_SERVERQUERY_GROUP].as()); + if (!instance_server_admin) { + instance_server_admin = this->groupManager->availableServerGroups(false).front(); + logCritical(LOG_INSTANCE, "Missing instance server admin group! Using first available (" + (instance_server_admin ? instance_server_admin->name() : "nil") + ")"); + } + if(this->groupManager->listGroupAssignments(this->globalServerAdmin->getClientDatabaseId()).empty()) + this->groupManager->addServerGroup(this->globalServerAdmin->getClientDatabaseId(), instance_server_admin); - debugMessage(LOG_INSTANCE, "Server guest group id " + to_string(this->properties()[property::SERVERINSTANCE_GUEST_SERVERQUERY_GROUP].as())); - auto instance_server_guest = this->groupManager->findGroup(this->properties()[property::SERVERINSTANCE_GUEST_SERVERQUERY_GROUP].as_save()); - if (!instance_server_guest) { - instance_server_guest = this->groupManager->availableServerGroups(false).front(); - logCritical(LOG_INSTANCE, "Missing instance server guest group! Using first available (" + (instance_server_guest ? instance_server_guest->name() : "nil") + ")"); - } - this->properties()[property::SERVERINSTANCE_GUEST_SERVERQUERY_GROUP] = instance_server_guest->groupId(); + debugMessage(LOG_INSTANCE, "Server guest group id " + to_string(this->properties()[property::SERVERINSTANCE_GUEST_SERVERQUERY_GROUP].as())); + auto instance_server_guest = this->groupManager->findGroup(this->properties()[property::SERVERINSTANCE_GUEST_SERVERQUERY_GROUP].as_save()); + if (!instance_server_guest) { + instance_server_guest = this->groupManager->availableServerGroups(false).front(); + logCritical(LOG_INSTANCE, "Missing instance server guest group! Using first available (" + (instance_server_guest ? instance_server_guest->name() : "nil") + ")"); + } + this->properties()[property::SERVERINSTANCE_GUEST_SERVERQUERY_GROUP] = instance_server_guest->groupId(); - debugMessage(LOG_INSTANCE, "Server music group id " + to_string(this->properties()[property::SERVERINSTANCE_TEMPLATE_MUSICDEFAULT_GROUP].as())); - auto instance_server_music = this->groupManager->findGroup(this->properties()[property::SERVERINSTANCE_TEMPLATE_MUSICDEFAULT_GROUP].as_save()); - if (!instance_server_music) { - instance_server_music = instance_server_guest; - logCritical(LOG_INSTANCE, "Missing instance server music group! Using serverguest (" + (instance_server_music ? instance_server_music->name() : "nil") + ")"); - } - this->properties()[property::SERVERINSTANCE_TEMPLATE_MUSICDEFAULT_GROUP] = instance_server_music->groupId(); - } + debugMessage(LOG_INSTANCE, "Server music group id " + to_string(this->properties()[property::SERVERINSTANCE_TEMPLATE_MUSICDEFAULT_GROUP].as())); + auto instance_server_music = this->groupManager->findGroup(this->properties()[property::SERVERINSTANCE_TEMPLATE_MUSICDEFAULT_GROUP].as_save()); + if (!instance_server_music) { + instance_server_music = instance_server_guest; + logCritical(LOG_INSTANCE, "Missing instance server music group! Using serverguest (" + (instance_server_music ? instance_server_music->name() : "nil") + ")"); + } + this->properties()[property::SERVERINSTANCE_TEMPLATE_MUSICDEFAULT_GROUP] = instance_server_music->groupId(); + } - { - this->default_tree = make_shared(nullptr, this->getSql()); - this->default_tree->loadChannelsFromDatabase(); + { + this->default_tree = make_shared(nullptr, this->getSql()); + this->default_tree->loadChannelsFromDatabase(); - this->default_tree->deleteSemiPermanentChannels(); - if(this->default_tree->channel_count() == 0){ - logMessage(LOG_GENERAL, "Generating default tree"); + this->default_tree->deleteSemiPermanentChannels(); + if(this->default_tree->channel_count() == 0){ + logMessage(LOG_GENERAL, "Generating default tree"); - std::shared_ptr ch; - ch = this->default_tree->createChannel(0, 0, "[cspacer01]┏╋━━━━━━◥◣◆◢◤━━━━━━╋┓"); - ch = this->default_tree->createChannel(0, ch->channelId(), "[cspacer02] TeaSpeak Server"); - ch = this->default_tree->createChannel(0, ch->channelId(), "[cspacer03]┗╋━━━━━━◥◣◆◢◤━━━━━━╋┛"); - ch = this->default_tree->createChannel(0, ch->channelId(), "[cspacer04]Default Channel"); - this->default_tree->setDefaultChannel(ch); + std::shared_ptr ch; + ch = this->default_tree->createChannel(0, 0, "[cspacer01]┏╋━━━━━━◥◣◆◢◤━━━━━━╋┓"); + ch = this->default_tree->createChannel(0, ch->channelId(), "[cspacer02] TeaSpeak Server"); + ch = this->default_tree->createChannel(0, ch->channelId(), "[cspacer03]┗╋━━━━━━◥◣◆◢◤━━━━━━╋┛"); + ch = this->default_tree->createChannel(0, ch->channelId(), "[cspacer04]Default Channel"); + this->default_tree->setDefaultChannel(ch); - this->properties()[property::SERVERINSTANCE_UNIQUE_ID] = ""; /* we def got a new instance */ - } - if(!this->default_tree->getDefaultChannel()) this->default_tree->setDefaultChannel(this->default_tree->findChannel("[cspacer04]Default Channel", nullptr)); - if(!this->default_tree->getDefaultChannel()) this->default_tree->setDefaultChannel(*this->default_tree->channels().begin()); - assert(this->default_tree->getDefaultChannel()); - } + this->properties()[property::SERVERINSTANCE_UNIQUE_ID] = ""; /* we def got a new instance */ + } + if(!this->default_tree->getDefaultChannel()) this->default_tree->setDefaultChannel(this->default_tree->findChannel("[cspacer04]Default Channel", nullptr)); + if(!this->default_tree->getDefaultChannel()) this->default_tree->setDefaultChannel(*this->default_tree->channels().begin()); + assert(this->default_tree->getDefaultChannel()); + } - { - this->default_server_properties = serverInstance->databaseHelper()->loadServerProperties(nullptr); - } + { + this->default_server_properties = serverInstance->databaseHelper()->loadServerProperties(nullptr); + } - if(this->properties()[property::SERVERINSTANCE_MONTHLY_TIMESTAMP].as() == 0) { - debugMessage(LOG_INSTANCE, "Setting up monthly reset timestamp!"); - this->properties()[property::SERVERINSTANCE_MONTHLY_TIMESTAMP] = duration_cast(system_clock::now().time_since_epoch()).count(); - } + if(this->properties()[property::SERVERINSTANCE_MONTHLY_TIMESTAMP].as() == 0) { + debugMessage(LOG_INSTANCE, "Setting up monthly reset timestamp!"); + this->properties()[property::SERVERINSTANCE_MONTHLY_TIMESTAMP] = duration_cast(system_clock::now().time_since_epoch()).count(); + } this->banMgr = new BanManager(this->getSql()); this->banMgr->loadBans(); @@ -199,159 +199,159 @@ void InstanceHandler::cancelExecute(TSServer* server) { InstanceHandler::~InstanceHandler() { delete this->_properties; - delete this->banMgr; + delete this->banMgr; delete this->dbHelper; - groupManager = nullptr; - globalServerAdmin = nullptr; - _musicRoot = nullptr; + groupManager = nullptr; + globalServerAdmin = nullptr; + _musicRoot = nullptr; - licenseHelper = nullptr; - statistics = nullptr; - tick_manager = nullptr; + licenseHelper = nullptr; + statistics = nullptr; + tick_manager = nullptr; } 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; + 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; + 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 InstanceHandler::startInstance() { if (this->active) - return false; + return false; active = true; - string errorMessage; + string errorMessage; this->web_list->enabled = ts::config::server::enable_teamspeak_weblist; this->permission_mapper = make_shared(); - if(!this->permission_mapper->initialize(config::permission_mapping_file, errorMessage)) { - logCritical(LOG_INSTANCE, "Failed to initialize permission name mapping from file {}: {}", config::permission_mapping_file, errorMessage); - return false; - } - this->sslMgr = new ssl::SSLManager(); - if(!this->sslMgr->initialize()) { - logCritical(LOG_GENERAL, "Failed to initialize ssl manager."); - return false; - } + if(!this->permission_mapper->initialize(config::permission_mapping_file, errorMessage)) { + logCritical(LOG_INSTANCE, "Failed to initialize permission name mapping from file {}: {}", config::permission_mapping_file, errorMessage); + return false; + } + this->sslMgr = new ssl::SSLManager(); + if(!this->sslMgr->initialize()) { + logCritical(LOG_GENERAL, "Failed to initialize ssl manager."); + return false; + } - this->conversation_io = make_shared("conv io #"); - if(!this->conversation_io->initialize(1)) { //TODO: Make the conversation IO loop thread size configurable - logCritical(LOG_GENERAL, "Failed to initialize conversation io write loop"); - return false; - } + this->conversation_io = make_shared("conv io #"); + if(!this->conversation_io->initialize(1)) { //TODO: Make the conversation IO loop thread size configurable + logCritical(LOG_GENERAL, "Failed to initialize conversation io write loop"); + return false; + } - { - vector errors; - if(!this->reloadConfig(errors, false)) { - logCritical(LOG_GENERAL, "Failed to initialize config:"); - for(auto& error : errors) - logCritical(LOG_GENERAL, "{}", error); - return false; - } - for(auto& error : errors) - logError(LOG_GENERAL, "{}", error); - } + { + vector errors; + if(!this->reloadConfig(errors, false)) { + logCritical(LOG_GENERAL, "Failed to initialize config:"); + for(auto& error : errors) + logCritical(LOG_GENERAL, "{}", error); + return false; + } + for(auto& error : errors) + logError(LOG_GENERAL, "{}", error); + } - this->loadWebCertificate(); - fileServer = new ts::server::FileServer(); - { - auto bindings_string = this->properties()[property::SERVERINSTANCE_FILETRANSFER_HOST].as(); - auto port = this->properties()[property::SERVERINSTANCE_FILETRANSFER_PORT].as(); - auto ft_bindings = net::resolve_bindings(bindings_string, port); - deque> bindings; + this->loadWebCertificate(); + fileServer = new ts::server::FileServer(); + { + auto bindings_string = this->properties()[property::SERVERINSTANCE_FILETRANSFER_HOST].as(); + auto port = this->properties()[property::SERVERINSTANCE_FILETRANSFER_PORT].as(); + auto ft_bindings = net::resolve_bindings(bindings_string, port); + deque> bindings; - for(auto& binding : ft_bindings) { - if(!get<2>(binding).empty()) { - logError(LOG_FT, "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)); + for(auto& binding : ft_bindings) { + if(!get<2>(binding).empty()) { + logError(LOG_FT, "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); - } + entry->file_descriptor = -1; + entry->event_accept = nullptr; + bindings.push_back(entry); + } - logMessage(LOG_FT, "Starting server on {}:{}", bindings_string, port); - if(!fileServer->start(bindings, errorMessage)) { - logCritical(LOG_FT, "Failed to start server: {}", errorMessage); - return false; - } - } + logMessage(LOG_FT, "Starting server on {}:{}", bindings_string, port); + if(!fileServer->start(bindings, errorMessage)) { + logCritical(LOG_FT, "Failed to start server: {}", errorMessage); + return false; + } + } - if(config::query::sslMode > 0) { - if(!this->sslMgr->getContext("query")) { - logCritical(LOG_QUERY, "Missing query SSL certificate."); - return false; - } - } + if(config::query::sslMode > 0) { + if(!this->sslMgr->getContext("query")) { + logCritical(LOG_QUERY, "Missing query SSL certificate."); + return false; + } + } - queryServer = new ts::server::QueryServer(this->getSql()); - { - auto server_query = queryServer->find_query_account_by_name("serveradmin"); - if(!server_query) { - string queryPassword = rnd_string(12); - if((server_query = queryServer->create_query_account("serveradmin", 0, "serveradmin", queryPassword))) { - logMessageFmt(true, LOG_GENERAL, "------------------ [Server Query] ------------------"); - logMessageFmt(true, LOG_GENERAL, " New Admin Server Query login credentials generated"); - logMessageFmt(true, LOG_GENERAL, " Username: serveradmin"); - logMessageFmt(true, LOG_GENERAL, " Password: " + queryPassword); - logMessageFmt(true, LOG_GENERAL, "------------------ [Server Query] ------------------"); - } else { - logCriticalFmt(true,LOG_GENERAL,"Failed to create a new server admin query account!"); - } - } - } + queryServer = new ts::server::QueryServer(this->getSql()); + { + auto server_query = queryServer->find_query_account_by_name("serveradmin"); + if(!server_query) { + string queryPassword = rnd_string(12); + if((server_query = queryServer->create_query_account("serveradmin", 0, "serveradmin", queryPassword))) { + logMessageFmt(true, LOG_GENERAL, "------------------ [Server Query] ------------------"); + logMessageFmt(true, LOG_GENERAL, " New Admin Server Query login credentials generated"); + logMessageFmt(true, LOG_GENERAL, " Username: serveradmin"); + logMessageFmt(true, LOG_GENERAL, " Password: " + queryPassword); + logMessageFmt(true, LOG_GENERAL, "------------------ [Server Query] ------------------"); + } else { + logCriticalFmt(true,LOG_GENERAL,"Failed to create a new server admin query account!"); + } + } + } - { - auto query_bindings_string = this->properties()[property::SERVERINSTANCE_QUERY_HOST].as(); - auto query_port = this->properties()[property::SERVERINSTANCE_QUERY_PORT].as(); - auto query_bindings = net::resolve_bindings(query_bindings_string, query_port); - deque> bindings; + { + auto query_bindings_string = this->properties()[property::SERVERINSTANCE_QUERY_HOST].as(); + auto query_port = this->properties()[property::SERVERINSTANCE_QUERY_PORT].as(); + auto query_bindings = net::resolve_bindings(query_bindings_string, query_port); + deque> bindings; - for(auto& binding : query_bindings) { - if(!get<2>(binding).empty()) { - logError(LOG_QUERY, "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)); + for(auto& binding : query_bindings) { + if(!get<2>(binding).empty()) { + logError(LOG_QUERY, "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); - } + entry->file_descriptor = -1; + entry->event_accept = nullptr; + bindings.push_back(entry); + } - logMessage(LOG_QUERY, "Starting server on {}:{}", query_bindings_string, query_port); - if(!queryServer->start(bindings, errorMessage)) { - logCritical(LOG_QUERY, "Failed to start query server: {}", errorMessage); - return false; - } - } + logMessage(LOG_QUERY, "Starting server on {}:{}", query_bindings_string, query_port); + if(!queryServer->start(bindings, errorMessage)) { + logCritical(LOG_QUERY, "Failed to start query server: {}", errorMessage); + return false; + } + } #ifdef COMPILE_WEB_CLIENT - if(config::web::activated) { - string error; - auto rsa = this->sslMgr->initializeSSLKey("teaforo_sign", R"( + if(config::web::activated) { + string error; + auto rsa = this->sslMgr->initializeSSLKey("teaforo_sign", R"( -----BEGIN PUBLIC KEY----- MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAsfsTByPTE0aIqi6pJl4f Xr4UqsIZkU5wYtktKIFpoDGHCHspCTMXF0fOXJkSGaTBtvTUEraRZz0+zshU+aiy @@ -362,21 +362,21 @@ vf11A3V+xDFIrSbS9pix1jWgztrQbUcHDczQozArcyflE5+rUMuPPRp3IyRuSq/6 FwIDAQAB -----END PUBLIC KEY----- )", error, true); - if(!rsa) { //TODO just disable the forum verification - logCritical(LOG_GENERAL, "Failed to initialize WebClient TeaForum key! ({})", error); - return false; - } - this->web_event_loop = make_shared(); - } + if(!rsa) { //TODO just disable the forum verification + logCritical(LOG_GENERAL, "Failed to initialize WebClient TeaForum key! ({})", error); + return false; + } + this->web_event_loop = make_shared(); + } #endif - if(config::experimental_31) { - this->teamspeak_license.reset(new TeamSpeakLicense("protocol_key.txt")); - if(!this->teamspeak_license->load(errorMessage)) { - logCritical(LOG_INSTANCE, "§cFailed to load the protocol key chain! ({})", errorMessage); - return false; - } - } + if(config::experimental_31) { + this->teamspeak_license.reset(new TeamSpeakLicense("protocol_key.txt")); + if(!this->teamspeak_license->load(errorMessage)) { + logCritical(LOG_INSTANCE, "§cFailed to load the protocol key chain! ({})", errorMessage); + return false; + } + } this->voiceServerManager = new ServerManager(this); if (!this->voiceServerManager->initialize(true)) { @@ -413,183 +413,183 @@ void InstanceHandler::stopInstance() { this->active = false; this->activeCon.notify_all(); } - this->web_list->enabled = false; + this->web_list->enabled = false; - threads::MutexLock lock_tick(this->lock_tick); - this->scheduler()->cancelTask(INSTANCE_TICK_NAME); + threads::MutexLock lock_tick(this->lock_tick); + this->scheduler()->cancelTask(INSTANCE_TICK_NAME); - debugMessage(LOG_INSTANCE, "Stopping all virtual servers"); + debugMessage(LOG_INSTANCE, "Stopping all virtual servers"); if (this->voiceServerManager) - this->voiceServerManager->shutdownAll(ts::config::messages::applicationStopped); + this->voiceServerManager->shutdownAll(ts::config::messages::applicationStopped); delete this->voiceServerManager; this->voiceServerManager = nullptr; - debugMessage(LOG_INSTANCE, "All virtual server stopped"); + debugMessage(LOG_INSTANCE, "All virtual server stopped"); - debugMessage(LOG_QUERY, "Stopping query server"); + debugMessage(LOG_QUERY, "Stopping query server"); if (this->queryServer) this->queryServer->stop(); delete this->queryServer; this->queryServer = nullptr; - debugMessage(LOG_QUERY, "Query server stopped"); + debugMessage(LOG_QUERY, "Query server stopped"); - debugMessage(LOG_FT, "Stopping file server"); + debugMessage(LOG_FT, "Stopping file server"); if (this->fileServer) this->fileServer->stop(); delete this->fileServer; this->fileServer = nullptr; - debugMessage(LOG_FT, "File server stopped"); + debugMessage(LOG_FT, "File server stopped"); - this->save_channel_permissions(); - this->save_group_permissions(); + this->save_channel_permissions(); + this->save_group_permissions(); - delete this->sslMgr; - this->sslMgr = nullptr; + delete this->sslMgr; + this->sslMgr = nullptr; - this->web_event_loop = nullptr; + this->web_event_loop = nullptr; } void InstanceHandler::tickInstance() { - threads::MutexLock lock(this->lock_tick); - if(!this->active) return; + threads::MutexLock lock(this->lock_tick); + if(!this->active) return; - auto now = system_clock::now(); + auto now = system_clock::now(); - if(generalUpdateTimestamp + seconds(5) < now) { - generalUpdateTimestamp = now; + if(generalUpdateTimestamp + seconds(5) < now) { + generalUpdateTimestamp = now; - { - ALARM_TIMER(t, "InstanceHandler::tickInstance -> db helper tick", milliseconds(5)); - this->dbHelper->tick(); - } - { - ALARM_TIMER(t, strobf("InstanceHandler::tickInstance -> license tick").string(), milliseconds(5)); - this->licenseHelper->tick(); - } - } - { - ALARM_TIMER(t, "InstanceHandler::tickInstance -> flush", milliseconds(5)); - //logger::flush(); - } - if(statisticsUpdateTimestamp + seconds(5) < now) { - statisticsUpdateTimestamp = now; - { - ALARM_TIMER(t, "InstanceHandler::tickInstance -> statistics tick", milliseconds(5)); - this->statistics->tick(); - } + { + ALARM_TIMER(t, "InstanceHandler::tickInstance -> db helper tick", milliseconds(5)); + this->dbHelper->tick(); + } + { + ALARM_TIMER(t, strobf("InstanceHandler::tickInstance -> license tick").string(), milliseconds(5)); + this->licenseHelper->tick(); + } + } + { + ALARM_TIMER(t, "InstanceHandler::tickInstance -> flush", milliseconds(5)); + //logger::flush(); + } + if(statisticsUpdateTimestamp + seconds(5) < now) { + statisticsUpdateTimestamp = now; + { + ALARM_TIMER(t, "InstanceHandler::tickInstance -> statistics tick", milliseconds(5)); + this->statistics->tick(); + } - { - ALARM_TIMER(t, "InstanceHandler::tickInstance -> statistics tick [monthly]", milliseconds(2)); - auto month_timestamp = system_clock::time_point() + seconds(this->properties()[property::SERVERINSTANCE_MONTHLY_TIMESTAMP].as()); - auto time_t_old = system_clock::to_time_t(month_timestamp); - auto time_t_new = system_clock::to_time_t(system_clock::now()); + { + ALARM_TIMER(t, "InstanceHandler::tickInstance -> statistics tick [monthly]", milliseconds(2)); + auto month_timestamp = system_clock::time_point() + seconds(this->properties()[property::SERVERINSTANCE_MONTHLY_TIMESTAMP].as()); + auto time_t_old = system_clock::to_time_t(month_timestamp); + auto time_t_new = system_clock::to_time_t(system_clock::now()); - tm tm_old{}, tm_new{}; - gmtime_r(&time_t_old, &tm_old); - gmtime_r(&time_t_new, &tm_new); + tm tm_old{}, tm_new{}; + gmtime_r(&time_t_old, &tm_old); + gmtime_r(&time_t_new, &tm_new); - if(tm_old.tm_mon != tm_new.tm_mon) { - logMessage(LOG_INSTANCE, "We entered a new month! Resetting monthly stats!"); - if(!this->resetMonthlyStats()) logError(LOG_INSTANCE, "Monthly stats reset failed!"); - else { - logMessage(LOG_INSTANCE, "Monthly stats reset done!"); - this->properties()[property::SERVERINSTANCE_MONTHLY_TIMESTAMP] = duration_cast(system_clock::now().time_since_epoch()).count(); - } - } - } - } - if(memcleanTimestamp + minutes(10) < now) { - memcleanTimestamp = now; - { - { - ALARM_TIMER(t, "InstanceHandler::tickInstance -> mem cleanup -> buffer cleanup", milliseconds(5)); - auto info = buffer::cleanup_buffers(buffer::cleanmode::CHUNKS_BLOCKS); - if(info.bytes_freed_buffer != 0 || info.bytes_freed_internal != 0) - logMessage(LOG_INSTANCE, "Cleanupped buffers. (Buffer: {}, Internal: {})", info.bytes_freed_buffer, info.bytes_freed_internal); - } - } - } - { - ALARM_TIMER(t, "InstanceHandler::tickInstance -> fileserver tick", milliseconds(5)); - if(this->fileServer) this->fileServer->instanceTick(); - } - { - ALARM_TIMER(t, "InstanceHandler::tickInstance -> sql_test tick", milliseconds(5)); - if(this->sql && this->active) { - if(sqlTestTimestamp + seconds(10) < now) { - sqlTestTimestamp = now; - threads::Thread(THREAD_SAVE_OPERATIONS | THREAD_DETACHED, [&](){ - auto command = this->sql->sql()->getType() == sql::TYPE_SQLITE ? "SELECT * FROM `sqlite_master`" : "SHOW TABLES"; - auto result = sql::command(this->getSql(), command).query([command](int, string*, string*){ return 0; }); - if(!result) { - logCritical(LOG_INSTANCE, "Dummy sql connection test faild! (Failed to execute command \"{}\". Error message: {})", command, result.fmtStr()); - logCritical(LOG_INSTANCE, "Stopping instance!"); - ts::server::shutdownInstance("invalid sql connection!"); - } - //debugMessage(0, "SQL connection still alive!"); - }); - } - } - } - if(groupSaveTimestamp + minutes(1) < now) { - speachUpdateTimestamp = now; - this->save_group_permissions(); - } - if(channelSaveTimestamp + minutes(1) < now) { - speachUpdateTimestamp = now; - this->save_channel_permissions(); - } - if(speachUpdateTimestamp + seconds(5) < now) { - speachUpdateTimestamp = now; + if(tm_old.tm_mon != tm_new.tm_mon) { + logMessage(LOG_INSTANCE, "We entered a new month! Resetting monthly stats!"); + if(!this->resetMonthlyStats()) logError(LOG_INSTANCE, "Monthly stats reset failed!"); + else { + logMessage(LOG_INSTANCE, "Monthly stats reset done!"); + this->properties()[property::SERVERINSTANCE_MONTHLY_TIMESTAMP] = duration_cast(system_clock::now().time_since_epoch()).count(); + } + } + } + } + if(memcleanTimestamp + minutes(10) < now) { + memcleanTimestamp = now; + { + { + ALARM_TIMER(t, "InstanceHandler::tickInstance -> mem cleanup -> buffer cleanup", milliseconds(5)); + auto info = buffer::cleanup_buffers(buffer::cleanmode::CHUNKS_BLOCKS); + if(info.bytes_freed_buffer != 0 || info.bytes_freed_internal != 0) + logMessage(LOG_INSTANCE, "Cleanupped buffers. (Buffer: {}, Internal: {})", info.bytes_freed_buffer, info.bytes_freed_internal); + } + } + } + { + ALARM_TIMER(t, "InstanceHandler::tickInstance -> fileserver tick", milliseconds(5)); + if(this->fileServer) this->fileServer->instanceTick(); + } + { + ALARM_TIMER(t, "InstanceHandler::tickInstance -> sql_test tick", milliseconds(5)); + if(this->sql && this->active) { + if(sqlTestTimestamp + seconds(10) < now) { + sqlTestTimestamp = now; + threads::Thread(THREAD_SAVE_OPERATIONS | THREAD_DETACHED, [&](){ + auto command = this->sql->sql()->getType() == sql::TYPE_SQLITE ? "SELECT * FROM `sqlite_master`" : "SHOW TABLES"; + auto result = sql::command(this->getSql(), command).query([command](int, string*, string*){ return 0; }); + if(!result) { + logCritical(LOG_INSTANCE, "Dummy sql connection test faild! (Failed to execute command \"{}\". Error message: {})", command, result.fmtStr()); + logCritical(LOG_INSTANCE, "Stopping instance!"); + ts::server::shutdownInstance("invalid sql connection!"); + } + //debugMessage(0, "SQL connection still alive!"); + }); + } + } + } + if(groupSaveTimestamp + minutes(1) < now) { + speachUpdateTimestamp = now; + this->save_group_permissions(); + } + if(channelSaveTimestamp + minutes(1) < now) { + speachUpdateTimestamp = now; + this->save_channel_permissions(); + } + if(speachUpdateTimestamp + seconds(5) < now) { + speachUpdateTimestamp = now; - this->properties()[property::SERVERINSTANCE_SPOKEN_TIME_ALIVE] = this->calculateSpokenTime().count(); - this->properties()[property::SERVERINSTANCE_SPOKEN_TIME_TOTAL] = - this->properties()[property::SERVERINSTANCE_SPOKEN_TIME_ALIVE].as() + - this->properties()[property::SERVERINSTANCE_SPOKEN_TIME_DELETED].as() + - this->properties()[property::SERVERINSTANCE_SPOKEN_TIME_VARIANZ].as(); - } + this->properties()[property::SERVERINSTANCE_SPOKEN_TIME_ALIVE] = this->calculateSpokenTime().count(); + this->properties()[property::SERVERINSTANCE_SPOKEN_TIME_TOTAL] = + this->properties()[property::SERVERINSTANCE_SPOKEN_TIME_ALIVE].as() + + this->properties()[property::SERVERINSTANCE_SPOKEN_TIME_DELETED].as() + + this->properties()[property::SERVERINSTANCE_SPOKEN_TIME_VARIANZ].as(); + } - this->web_list->tick(); + this->web_list->tick(); } void InstanceHandler::save_group_permissions() { - auto groups = this->getGroupManager()->availableGroups(false); - for(auto& group : groups) { - auto permissions = group->permissions(); - if(permissions->require_db_updates()) { - auto begin = system_clock::now(); - serverInstance->databaseHelper()->saveGroupPermissions(nullptr, group->groupId(), permissions); - auto end = system_clock::now(); - debugMessage(0, "Saved instance group permissions for group {} ({}) in {}ms", group->groupId(), group->name(), duration_cast(end - begin).count()); - } - } + auto groups = this->getGroupManager()->availableGroups(false); + for(auto& group : groups) { + auto permissions = group->permissions(); + if(permissions->require_db_updates()) { + auto begin = system_clock::now(); + serverInstance->databaseHelper()->saveGroupPermissions(nullptr, group->groupId(), permissions); + auto end = system_clock::now(); + debugMessage(0, "Saved instance group permissions for group {} ({}) in {}ms", group->groupId(), group->name(), duration_cast(end - begin).count()); + } + } } void InstanceHandler::save_channel_permissions() { - shared_lock tree_lock(this->getChannelTreeLock()); - auto channels = this->getChannelTree()->channels(); - tree_lock.unlock(); + shared_lock tree_lock(this->getChannelTreeLock()); + auto channels = this->getChannelTree()->channels(); + tree_lock.unlock(); - for(auto& channel : channels) { - auto permissions = channel->permissions(); - if(permissions->require_db_updates()) { - auto begin = system_clock::now(); - serverInstance->databaseHelper()->saveChannelPermissions(nullptr, channel->channelId(), permissions); - auto end = system_clock::now(); - debugMessage(0, "Saved instance channel permissions for channel {} ({}) in {}ms", channel->channelId(), channel->name(), duration_cast(end - begin).count()); - } - } + for(auto& channel : channels) { + auto permissions = channel->permissions(); + if(permissions->require_db_updates()) { + auto begin = system_clock::now(); + serverInstance->databaseHelper()->saveChannelPermissions(nullptr, channel->channelId(), permissions); + auto end = system_clock::now(); + debugMessage(0, "Saved instance channel permissions for channel {} ({}) in {}ms", channel->channelId(), channel->name(), duration_cast(end - begin).count()); + } + } } std::chrono::milliseconds InstanceHandler::calculateSpokenTime() { - std::chrono::milliseconds result{}; - for(const auto& server : this->voiceServerManager->serverInstances()) - result += server->spoken_time; - return result; + std::chrono::milliseconds result{}; + for(const auto& server : this->voiceServerManager->serverInstances()) + result += server->spoken_time; + return result; } void InstanceHandler::resetSpeechTime() { - this->properties()[property::SERVERINSTANCE_SPOKEN_TIME_DELETED] = 0; - this->properties()[property::SERVERINSTANCE_SPOKEN_TIME_VARIANZ] = 0; - for(const auto& server : this->voiceServerManager->serverInstances()) - server->properties()[property::VIRTUALSERVER_SPOKEN_TIME] = 0; + this->properties()[property::SERVERINSTANCE_SPOKEN_TIME_DELETED] = 0; + this->properties()[property::SERVERINSTANCE_SPOKEN_TIME_VARIANZ] = 0; + for(const auto& server : this->voiceServerManager->serverInstances()) + server->properties()[property::VIRTUALSERVER_SPOKEN_TIME] = 0; } #include @@ -597,288 +597,288 @@ void InstanceHandler::resetSpeechTime() { #include string get_mac_address() { - struct ifreq ifr{}; - struct ifconf ifc{}; - char buf[1024]; - int success = 0; + struct ifreq ifr{}; + struct ifconf ifc{}; + char buf[1024]; + int success = 0; - int sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_IP); - if (sock == -1) { return "undefined"; }; + int sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_IP); + if (sock == -1) { return "undefined"; }; - ifc.ifc_len = sizeof(buf); - ifc.ifc_buf = buf; - if (ioctl(sock, SIOCGIFCONF, &ifc) == -1) { /* handle error */ } + ifc.ifc_len = sizeof(buf); + ifc.ifc_buf = buf; + if (ioctl(sock, SIOCGIFCONF, &ifc) == -1) { /* handle error */ } - struct ifreq* it = ifc.ifc_req; - const struct ifreq* const end = it + (ifc.ifc_len / sizeof(struct ifreq)); + struct ifreq* it = ifc.ifc_req; + const struct ifreq* const end = it + (ifc.ifc_len / sizeof(struct ifreq)); - for (; it != end; ++it) { - strcpy(ifr.ifr_name, it->ifr_name); - if (ioctl(sock, SIOCGIFFLAGS, &ifr) == 0) { - if (!(ifr.ifr_flags & IFF_LOOPBACK)) { // don't count loopback - if (ioctl(sock, SIOCGIFHWADDR, &ifr) == 0) { - success = 1; - break; - } - } - } else { return "undefined"; } - } + for (; it != end; ++it) { + strcpy(ifr.ifr_name, it->ifr_name); + if (ioctl(sock, SIOCGIFFLAGS, &ifr) == 0) { + if (!(ifr.ifr_flags & IFF_LOOPBACK)) { // don't count loopback + if (ioctl(sock, SIOCGIFHWADDR, &ifr) == 0) { + success = 1; + break; + } + } + } else { return "undefined"; } + } - return success ? base64::encode(ifr.ifr_hwaddr.sa_data, 6) : "undefined"; + return success ? base64::encode(ifr.ifr_hwaddr.sa_data, 6) : "undefined"; } #define SN_BUFFER 1024 std::shared_ptr InstanceHandler::generateLicenseData() { - auto request = make_shared(); - request->license = config::license; - request->servers_online = this->voiceServerManager->runningServers(); - auto report = this->voiceServerManager->clientReport(); - request->client_online = report.clients_ts; - request->web_clients_online = report.clients_web; - request->bots_online = report.bots; - request->queries_online = report.queries; - request->speach_total = this->properties()[property::SERVERINSTANCE_SPOKEN_TIME_TOTAL].as(); - request->speach_varianz = this->properties()[property::SERVERINSTANCE_SPOKEN_TIME_VARIANZ].as(); - request->speach_online = this->properties()[property::SERVERINSTANCE_SPOKEN_TIME_ALIVE].as(); - request->speach_dead = this->properties()[property::SERVERINSTANCE_SPOKEN_TIME_DELETED].as(); + auto request = make_shared(); + request->license = config::license; + request->servers_online = this->voiceServerManager->runningServers(); + auto report = this->voiceServerManager->clientReport(); + request->client_online = report.clients_ts; + request->web_clients_online = report.clients_web; + request->bots_online = report.bots; + request->queries_online = report.queries; + request->speach_total = this->properties()[property::SERVERINSTANCE_SPOKEN_TIME_TOTAL].as(); + request->speach_varianz = this->properties()[property::SERVERINSTANCE_SPOKEN_TIME_VARIANZ].as(); + request->speach_online = this->properties()[property::SERVERINSTANCE_SPOKEN_TIME_ALIVE].as(); + request->speach_dead = this->properties()[property::SERVERINSTANCE_SPOKEN_TIME_DELETED].as(); - static std::string null_str{"\0\0\0\0\0\0\0\0", 8}; /* we need at least some characters */ - request->web_certificate_revision = this->web_cert_revision.empty() ? null_str : this->web_cert_revision; + static std::string null_str{"\0\0\0\0\0\0\0\0", 8}; /* we need at least some characters */ + request->web_certificate_revision = this->web_cert_revision.empty() ? null_str : this->web_cert_revision; { auto info = make_shared(); info->timestamp = duration_cast(system_clock::now().time_since_epoch()).count(); info->version = build::version()->string(true); - { /* uname */ - utsname retval{}; - if(uname(&retval) < 0) { - info->uname = "unknown (" + string(strerror(errno)) + ")"; - } else { - char buffer[SN_BUFFER]; - snprintf(buffer, SN_BUFFER, "sys:%s version:%s release:%s", retval.sysname, retval.version, retval.release); - info->uname = string(buffer); - } + { /* uname */ + utsname retval{}; + if(uname(&retval) < 0) { + info->uname = "unknown (" + string(strerror(errno)) + ")"; + } else { + char buffer[SN_BUFFER]; + snprintf(buffer, SN_BUFFER, "sys:%s version:%s release:%s", retval.sysname, retval.version, retval.release); + info->uname = string(buffer); + } - } + } - { /* unique id */ - auto property_unique_id = this->properties()[property::SERVERINSTANCE_UNIQUE_ID]; - if(property_unique_id.as().empty()) - property_unique_id = rnd_string(64); + { /* unique id */ + auto property_unique_id = this->properties()[property::SERVERINSTANCE_UNIQUE_ID]; + if(property_unique_id.as().empty()) + property_unique_id = rnd_string(64); - auto hash = digest::sha256(info->uname); - hash = digest::sha256(hash + property_unique_id.as() + get_mac_address()); - info->unique_identifier = base64::encode(hash); - } + auto hash = digest::sha256(info->uname); + hash = digest::sha256(hash + property_unique_id.as() + get_mac_address()); + info->unique_identifier = base64::encode(hash); + } - request->info = info; + request->info = info; } - return request; + return request; } bool InstanceHandler::resetMonthlyStats() { - //serverId` INTEGER DEFAULT -1, `type` INTEGER, `id` INTEGER, `key` VARCHAR(" UNKNOWN_KEY_LENGTH "), `value` TEXT - auto result = sql::command(this->getSql(), "UPDATE `properties` SET `value` = 0 WHERE " - "`key` = 'serverinstance_monthly_timestamp' OR " - "`key` = 'virtualserver_month_bytes_downloaded' OR " - "`key` = 'virtualserver_month_bytes_uploaded' OR " - "`key` = 'client_month_bytes_downloaded' OR " - "`key` = 'client_month_bytes_uploaded' OR " - "`key` = 'client_month_online_time'").execute(); - if(!result) { - logError(LOG_INSTANCE, "Failed to reset monthly stats ({})", result.fmtStr()); - return false; - } + //serverId` INTEGER DEFAULT -1, `type` INTEGER, `id` INTEGER, `key` VARCHAR(" UNKNOWN_KEY_LENGTH "), `value` TEXT + auto result = sql::command(this->getSql(), "UPDATE `properties` SET `value` = 0 WHERE " + "`key` = 'serverinstance_monthly_timestamp' OR " + "`key` = 'virtualserver_month_bytes_downloaded' OR " + "`key` = 'virtualserver_month_bytes_uploaded' OR " + "`key` = 'client_month_bytes_downloaded' OR " + "`key` = 'client_month_bytes_uploaded' OR " + "`key` = 'client_month_online_time'").execute(); + if(!result) { + logError(LOG_INSTANCE, "Failed to reset monthly stats ({})", result.fmtStr()); + return false; + } - for(const auto& server : this->getVoiceServerManager()->serverInstances()) { - server->properties()[property::VIRTUALSERVER_MONTH_BYTES_UPLOADED] = 0; - server->properties()[property::VIRTUALSERVER_MONTH_BYTES_DOWNLOADED] = 0; + for(const auto& server : this->getVoiceServerManager()->serverInstances()) { + server->properties()[property::VIRTUALSERVER_MONTH_BYTES_UPLOADED] = 0; + server->properties()[property::VIRTUALSERVER_MONTH_BYTES_DOWNLOADED] = 0; - for(const auto& client : server->getClients()) { - client->properties()[property::CLIENT_MONTH_ONLINE_TIME] = 0; - client->properties()[property::CLIENT_MONTH_BYTES_UPLOADED] = 0; - client->properties()[property::CLIENT_MONTH_BYTES_DOWNLOADED] = 0; - } - } - return true; + for(const auto& client : server->getClients()) { + client->properties()[property::CLIENT_MONTH_ONLINE_TIME] = 0; + client->properties()[property::CLIENT_MONTH_BYTES_UPLOADED] = 0; + client->properties()[property::CLIENT_MONTH_BYTES_DOWNLOADED] = 0; + } + } + return true; } bool InstanceHandler::reloadConfig(std::vector& errors, bool reload_file) { - if(reload_file) { - auto cfg_errors = config::reload(); - if(!cfg_errors.empty()) { - errors.emplace_back("Failed to load config:"); - errors.insert(errors.begin(), cfg_errors.begin(), cfg_errors.end()); - return false; - } - } + if(reload_file) { + auto cfg_errors = config::reload(); + if(!cfg_errors.empty()) { + errors.emplace_back("Failed to load config:"); + errors.insert(errors.begin(), cfg_errors.begin(), cfg_errors.end()); + return false; + } + } - string error; + string error; #ifdef COMPILE_WEB_CLIENT - if(config::web::activated) { - this->sslMgr->unregister_web_contexts(false); + if(config::web::activated) { + this->sslMgr->unregister_web_contexts(false); - string error; - for (auto &certificate : config::web::ssl::certificates) { - if(get<0>(certificate) == "default") { - logWarning(LOG_GENERAL, "Default Web certificate will be ignored. Using internal one!"); - continue; - } + string error; + for (auto &certificate : config::web::ssl::certificates) { + if(get<0>(certificate) == "default") { + logWarning(LOG_GENERAL, "Default Web certificate will be ignored. Using internal one!"); + continue; + } - auto result = this->sslMgr->initializeContext( - "web_" + get<0>(certificate), get<1>(certificate), get<2>(certificate), error, false, make_shared( - ts::ssl::SSLGenerator{ - .subjects = {}, - .issues = {{"O", "TeaSpeak"}, - {"OU", "Web server"}, - {"creator", "WolverinDEV"}} - } - )); - if (!result) { - errors.push_back("Failed to initialize web certificate for servername " + get<0>(certificate) + "! (Key: " + get<1>(certificate) + ", Certificate: " + get<2>(certificate) + ")"); - continue; - } - } - } + auto result = this->sslMgr->initializeContext( + "web_" + get<0>(certificate), get<1>(certificate), get<2>(certificate), error, false, make_shared( + ts::ssl::SSLGenerator{ + .subjects = {}, + .issues = {{"O", "TeaSpeak"}, + {"OU", "Web server"}, + {"creator", "WolverinDEV"}} + } + )); + if (!result) { + errors.push_back("Failed to initialize web certificate for servername " + get<0>(certificate) + "! (Key: " + get<1>(certificate) + ", Certificate: " + get<2>(certificate) + ")"); + continue; + } + } + } #endif - auto result = this->sslMgr->initializeContext("query_new", config::query::ssl::keyFile, config::query::ssl::certFile, error, false, make_shared(ssl::SSLGenerator{ - .subjects = {}, - .issues = {{"O", "TeaSpeak"}, {"OU", "Query server"}, {"creator", "WolverinDEV"}} - })); - if(!result) - errors.push_back("Failed to initialize query certificate! (" + error + ")"); - this->sslMgr->rename_context("query_new", "query"); //Will not succeed if the query_new context failed + auto result = this->sslMgr->initializeContext("query_new", config::query::ssl::keyFile, config::query::ssl::certFile, error, false, make_shared(ssl::SSLGenerator{ + .subjects = {}, + .issues = {{"O", "TeaSpeak"}, {"OU", "Query server"}, {"creator", "WolverinDEV"}} + })); + if(!result) + errors.push_back("Failed to initialize query certificate! (" + error + ")"); + this->sslMgr->rename_context("query_new", "query"); //Will not succeed if the query_new context failed - return true; + return true; } void InstanceHandler::setWebCertRoot(const std::string &key, const std::string &certificate, const std::string &revision) { - std::string error{}; + std::string error{}; - logMessage(LOG_INSTANCE, strobf("Received new web default certificate. Revision {}").string(), hex::hex(revision)); + logMessage(LOG_INSTANCE, strobf("Received new web default certificate. Revision {}").string(), hex::hex(revision)); - std::string _key{key}, _cert{certificate}, _revision{revision}; - auto result = this->sslMgr->initializeContext(strobf("web_default_new").string(), _key, _cert, error, true); - if(!result) { - logError(LOG_INSTANCE, strobf("Failed to use web default certificate: {}").string(), error); - return; - } + std::string _key{key}, _cert{certificate}, _revision{revision}; + auto result = this->sslMgr->initializeContext(strobf("web_default_new").string(), _key, _cert, error, true); + if(!result) { + logError(LOG_INSTANCE, strobf("Failed to use web default certificate: {}").string(), error); + return; + } - this->sslMgr->rename_context(strobf("web_default_new").string(), strobf("web_default").string()); + this->sslMgr->rename_context(strobf("web_default_new").string(), strobf("web_default").string()); - //https://127-0-0-1.con-gate.work:9987 - { /* "Crypt" */ - auto& xor_short = _key.length() < _cert.length() ? _key : _cert; - auto& xor_long = _key.length() < _cert.length() ? _cert : _key; - for(size_t index = 0; index < xor_short.length(); index++) - xor_short[index] ^= xor_long[index]; - for(size_t index = 0; index < xor_long.length(); index++) - xor_long[index] ^= ((index << 4) & 0xFF) | ((index >> 4) & 0xFF); - } + //https://127-0-0-1.con-gate.work:9987 + { /* "Crypt" */ + auto& xor_short = _key.length() < _cert.length() ? _key : _cert; + auto& xor_long = _key.length() < _cert.length() ? _cert : _key; + for(size_t index = 0; index < xor_short.length(); index++) + xor_short[index] ^= xor_long[index]; + for(size_t index = 0; index < xor_long.length(); index++) + xor_long[index] ^= ((index << 4) & 0xFF) | ((index >> 4) & 0xFF); + } - for(auto& e : _cert) - e ^= 0x8A; - for(auto& e : _key) - e ^= 0x8A; + for(auto& e : _cert) + e ^= 0x8A; + for(auto& e : _key) + e ^= 0x8A; - _key = base64::encode(_key); - _cert = base64::encode(_cert); - _revision = base64::encode(_revision); + _key = base64::encode(_key); + _cert = base64::encode(_cert); + _revision = base64::encode(_revision); - auto response = sql::command(this->sql->sql(), - strobf("DELETE FROM `general` WHERE `key` = 'webcert-revision' or `key` = 'webcert-cert' or `key` = 'webcert-key'").string()).execute(); - if(!response) { - logError(LOG_INSTANCE, strobf("Failed to delete old default web certificate in database: {}").string(), response.fmtStr()); - return; - } + auto response = sql::command(this->sql->sql(), + strobf("DELETE FROM `general` WHERE `key` = 'webcert-revision' or `key` = 'webcert-cert' or `key` = 'webcert-key'").string()).execute(); + if(!response) { + logError(LOG_INSTANCE, strobf("Failed to delete old default web certificate in database: {}").string(), response.fmtStr()); + return; + } - response = sql::command(this->sql->sql(), strobf("INSERT INTO `general` (`key`, `value`) VALUES ('webcert-revision', :rev), ('webcert-cert', :cert), ('webcert-key', :key)").string(), - variable{":rev", _revision}, - variable{":cert", _cert}, - variable{":key", _key} - ).execute(); - if(!response) { - logError(LOG_INSTANCE, strobf("Failed to insert new default web certificate in database: {}").string(), response.fmtStr()); - return; - } + response = sql::command(this->sql->sql(), strobf("INSERT INTO `general` (`key`, `value`) VALUES ('webcert-revision', :rev), ('webcert-cert', :cert), ('webcert-key', :key)").string(), + variable{":rev", _revision}, + variable{":cert", _cert}, + variable{":key", _key} + ).execute(); + if(!response) { + logError(LOG_INSTANCE, strobf("Failed to insert new default web certificate in database: {}").string(), response.fmtStr()); + return; + } } void InstanceHandler::loadWebCertificate() { - std::string error{}; + std::string error{}; - /* TMP */ - { - std::string key{}, cert{}; - auto result = this->sslMgr->initializeContext("web_default", key, cert, error, true, make_shared( - ts::ssl::SSLGenerator{ - .subjects = {}, - .issues = {{"O", "TeaSpeak"}, - {"OU", "Web server"}, - {"creator", "WolverinDEV"}} - } - )); - if(!result) - logError(LOG_INSTANCE, strobf("Failed to generate fallback web cert key: {}").string(), error); - } + /* TMP */ + { + std::string key{}, cert{}; + auto result = this->sslMgr->initializeContext("web_default", key, cert, error, true, make_shared( + ts::ssl::SSLGenerator{ + .subjects = {}, + .issues = {{"O", "TeaSpeak"}, + {"OU", "Web server"}, + {"creator", "WolverinDEV"}} + } + )); + if(!result) + logError(LOG_INSTANCE, strobf("Failed to generate fallback web cert key: {}").string(), error); + } - std::string revision{}, cert{}, _key{}; + std::string revision{}, cert{}, _key{}; - sql::command(this->sql->sql(), strobf("SELECT * FROM `general` WHERE `key` = 'webcert-revision' or `key` = 'webcert-cert' or `key` = 'webcert-key'").string()) - .query([&](int count, std::string* values, std::string* names) { - std::string key{}, value{}; - for(int index = 0; index < count; index++) { - if(names[index] == "key") - key = values[index]; - else if(names[index] == "value") - value = values[index]; - } + sql::command(this->sql->sql(), strobf("SELECT * FROM `general` WHERE `key` = 'webcert-revision' or `key` = 'webcert-cert' or `key` = 'webcert-key'").string()) + .query([&](int count, std::string* values, std::string* names) { + std::string key{}, value{}; + for(int index = 0; index < count; index++) { + if(names[index] == "key") + key = values[index]; + else if(names[index] == "value") + value = values[index]; + } - if(!value.empty() && !key.empty()) { - if(key == strobf("webcert-revision").string()) - revision = value; - else if(key == strobf("webcert-cert").string()) - cert = value; - else if(key == strobf("webcert-key").string()) - _key = value; - } - }); + if(!value.empty() && !key.empty()) { + if(key == strobf("webcert-revision").string()) + revision = value; + else if(key == strobf("webcert-cert").string()) + cert = value; + else if(key == strobf("webcert-key").string()) + _key = value; + } + }); - _key = base64::decode(_key); - cert = base64::decode(cert); - revision = base64::decode(revision); + _key = base64::decode(_key); + cert = base64::decode(cert); + revision = base64::decode(revision); - if(revision.empty() || cert.empty() || _key.empty()) { - if(!revision.empty() || !cert.empty() || !_key.empty()) - logWarning(LOG_INSTANCE, strobf("Failed to load default web certificate from database.").string()); - return; - } + if(revision.empty() || cert.empty() || _key.empty()) { + if(!revision.empty() || !cert.empty() || !_key.empty()) + logWarning(LOG_INSTANCE, strobf("Failed to load default web certificate from database.").string()); + return; + } - for(auto& e : cert) - e ^= 0x8A; - for(auto& e : _key) - e ^= 0x8A; + for(auto& e : cert) + e ^= 0x8A; + for(auto& e : _key) + e ^= 0x8A; - { /* "Crypt" */ - auto& xor_short = _key.length() < cert.length() ? _key : cert; - auto& xor_long = _key.length() < cert.length() ? cert : _key; + { /* "Crypt" */ + auto& xor_short = _key.length() < cert.length() ? _key : cert; + auto& xor_long = _key.length() < cert.length() ? cert : _key; - for(size_t index = 0; index < xor_long.length(); index++) - xor_long[index] ^= ((index << 4) & 0xFF) | ((index >> 4) & 0xFF); + for(size_t index = 0; index < xor_long.length(); index++) + xor_long[index] ^= ((index << 4) & 0xFF) | ((index >> 4) & 0xFF); - for(size_t index = 0; index < xor_short.length(); index++) - xor_short[index] ^= xor_long[index]; - } + for(size_t index = 0; index < xor_short.length(); index++) + xor_short[index] ^= xor_long[index]; + } - auto result = this->sslMgr->initializeContext(strobf("web_default_new").string(), _key, cert, error, true); - if(!result) { - logError(LOG_INSTANCE, strobf("Failed to use web default certificate from db: {}").string(), error); - return; - } + auto result = this->sslMgr->initializeContext(strobf("web_default_new").string(), _key, cert, error, true); + if(!result) { + logError(LOG_INSTANCE, strobf("Failed to use web default certificate from db: {}").string(), error); + return; + } - this->sslMgr->rename_context(strobf("web_default_new").string(), strobf("web_default").string()); - this->web_cert_revision = revision; + this->sslMgr->rename_context(strobf("web_default_new").string(), strobf("web_default").string()); + this->web_cert_revision = revision; } \ No newline at end of file diff --git a/server/src/InstanceHandler.h b/server/src/InstanceHandler.h index 8b0842d..cc90543 100644 --- a/server/src/InstanceHandler.h +++ b/server/src/InstanceHandler.h @@ -11,13 +11,13 @@ #include "server/WebIoManager.h" namespace ts { - namespace weblist { - class WebListManager; - } + namespace weblist { + class WebListManager; + } - namespace permission { - class PermissionNameMapper; - } + namespace permission { + class PermissionNameMapper; + } namespace server { class InstanceHandler { @@ -29,22 +29,22 @@ namespace ts { void stopInstance(); ts::Properties& properties(){ - return *_properties; + return *_properties; } std::shared_ptr getInitialServerAdmin(){ return globalServerAdmin; } std::shared_ptr getGroupManager(){ return groupManager; } - std::shared_ptr getChannelTree() { return this->default_tree; } - std::shared_mutex& getChannelTreeLock() { return this->default_tree_lock; } + std::shared_ptr getChannelTree() { return this->default_tree; } + std::shared_mutex& getChannelTreeLock() { return this->default_tree_lock; } ServerManager* getVoiceServerManager(){ return this->voiceServerManager; } FileServer* getFileServer(){ return fileServer; } QueryServer* getQueryServer(){ return queryServer; } DatabaseHelper* databaseHelper(){ return this->dbHelper; } BanManager* banManager(){ return this->banMgr; } - ssl::SSLManager* sslManager(){ return this->sslMgr; } - sql::SqlManager* getSql(){ return sql->sql(); } + ssl::SSLManager* sslManager(){ return this->sslMgr; } + sql::SqlManager* getSql(){ return sql->sql(); } std::chrono::time_point getStartTimestamp(){ return startTimestamp; } @@ -54,51 +54,51 @@ namespace ts { bool reloadConfig(std::vector& /* errors */, bool /* reload file */); void setWebCertRoot(const std::string& /* key */, const std::string& /* certificate */, const std::string& /* revision */); - const std::shared_ptr& musicRoot() { return this->_musicRoot; } + const std::shared_ptr& musicRoot() { return this->_musicRoot; } - std::chrono::milliseconds calculateSpokenTime(); - void resetSpeechTime(); + std::chrono::milliseconds calculateSpokenTime(); + void resetSpeechTime(); - bool resetMonthlyStats(); + bool resetMonthlyStats(); - std::shared_ptr getStatistics(){ return statistics; } - std::shared_ptr scheduler(){ return this->tick_manager; } - std::shared_ptr generateLicenseData(); + std::shared_ptr getStatistics(){ return statistics; } + std::shared_ptr scheduler(){ return this->tick_manager; } + std::shared_ptr generateLicenseData(); - std::shared_ptr getTeamSpeakLicense() { return this->teamspeak_license; } - std::shared_ptr getDefaultServerProperties() { return this->default_server_properties; } - std::shared_ptr getWebIoLoop() { return this->web_event_loop; } - std::shared_ptr getWebList() { return this->web_list; } + std::shared_ptr getTeamSpeakLicense() { return this->teamspeak_license; } + std::shared_ptr getDefaultServerProperties() { return this->default_server_properties; } + std::shared_ptr getWebIoLoop() { return this->web_event_loop; } + std::shared_ptr getWebList() { return this->web_list; } - std::shared_ptr getPermissionMapper() { return this->permission_mapper; } - std::shared_ptr getConversationIo() { return this->conversation_io; } + std::shared_ptr getPermissionMapper() { return this->permission_mapper; } + std::shared_ptr getConversationIo() { return this->conversation_io; } private: std::mutex activeLock; std::condition_variable activeCon; bool active = false; std::chrono::system_clock::time_point startTimestamp; - std::chrono::system_clock::time_point sqlTestTimestamp; - std::chrono::system_clock::time_point speachUpdateTimestamp; - std::chrono::system_clock::time_point groupSaveTimestamp; - std::chrono::system_clock::time_point channelSaveTimestamp; - std::chrono::system_clock::time_point generalUpdateTimestamp; - std::chrono::system_clock::time_point statisticsUpdateTimestamp; - std::chrono::system_clock::time_point memcleanTimestamp; - SqlDataManager* sql; + std::chrono::system_clock::time_point sqlTestTimestamp; + std::chrono::system_clock::time_point speachUpdateTimestamp; + std::chrono::system_clock::time_point groupSaveTimestamp; + std::chrono::system_clock::time_point channelSaveTimestamp; + std::chrono::system_clock::time_point generalUpdateTimestamp; + std::chrono::system_clock::time_point statisticsUpdateTimestamp; + std::chrono::system_clock::time_point memcleanTimestamp; + SqlDataManager* sql; FileServer* fileServer = nullptr; QueryServer* queryServer = nullptr; ServerManager* voiceServerManager = nullptr; DatabaseHelper* dbHelper = nullptr; - BanManager* banMgr = nullptr; - ssl::SSLManager* sslMgr = nullptr; + BanManager* banMgr = nullptr; + ssl::SSLManager* sslMgr = nullptr; ts::Properties* _properties = nullptr; std::shared_ptr conversation_io = nullptr; std::shared_ptr web_event_loop = nullptr; - std::shared_ptr web_list = nullptr; + std::shared_ptr web_list = nullptr; std::shared_ptr default_server_properties = nullptr; std::shared_ptr default_tree = nullptr; @@ -106,26 +106,26 @@ namespace ts { std::shared_ptr groupManager = nullptr; std::shared_ptr globalServerAdmin = nullptr; - std::shared_ptr _musicRoot = nullptr; + std::shared_ptr _musicRoot = nullptr; - std::shared_ptr licenseHelper = nullptr; - std::shared_ptr statistics = nullptr; + std::shared_ptr licenseHelper = nullptr; + std::shared_ptr statistics = nullptr; std::shared_ptr tick_manager = nullptr; - std::shared_ptr permission_mapper = nullptr; + std::shared_ptr permission_mapper = nullptr; std::shared_ptr teamspeak_license = nullptr; std::string web_cert_revision{}; - threads::Mutex lock_tick; + threads::Mutex lock_tick; private: bool setupDefaultGroups(); void tickInstance(); void save_group_permissions(); - void save_channel_permissions(); + void save_channel_permissions(); - void loadWebCertificate(); + void loadWebCertificate(); }; } } diff --git a/server/src/InstanceHandlerSetup.cpp b/server/src/InstanceHandlerSetup.cpp index da6408a..08313b3 100644 --- a/server/src/InstanceHandlerSetup.cpp +++ b/server/src/InstanceHandlerSetup.cpp @@ -17,132 +17,132 @@ using namespace ts::server; #define PERMISSION_TEMPLATE_FILE "resources/permissions.template" struct GroupInfo { - /** - * 0 = Query - * 1 = Server - * 2 = Channel - */ - int target; - std::deque properties; - string name; - /* permission type, value, granted, skip, negate */ - deque> permissions; + /** + * 0 = Query + * 1 = Server + * 2 = Channel + */ + int target; + std::deque properties; + string name; + /* permission type, value, granted, skip, negate */ + deque> permissions; }; /* TODO may use a transaction here? */ bool InstanceHandler::setupDefaultGroups() { - debugMessage(LOG_INSTANCE, "Creating new instance groups"); - deque> groups; + debugMessage(LOG_INSTANCE, "Creating new instance groups"); + deque> groups; - ifstream in(PERMISSION_TEMPLATE_FILE); - if(!in) { - logCritical(LOG_INSTANCE, "Could not open default permissions file {}", PERMISSION_TEMPLATE_FILE); - return false; - } - string line; - while(getline(in, line)){ - TEST_COMMENT; + ifstream in(PERMISSION_TEMPLATE_FILE); + if(!in) { + logCritical(LOG_INSTANCE, "Could not open default permissions file {}", PERMISSION_TEMPLATE_FILE); + return false; + } + string line; + while(getline(in, line)){ + TEST_COMMENT; - if(line != "--start") { - logCritical(LOG_INSTANCE, R"(Permission template file contains invalid start line ("{}", expected "{}")!)", line, "--start"); - return false; - } - auto group = make_shared(); - while(true){ - getline(in, line); - TEST_COMMENT; + if(line != "--start") { + logCritical(LOG_INSTANCE, R"(Permission template file contains invalid start line ("{}", expected "{}")!)", line, "--start"); + return false; + } + auto group = make_shared(); + while(true){ + getline(in, line); + TEST_COMMENT; if(line == "--end") break; if(line.find("name:") == 0) { - group->name = line.substr(5); - continue; + group->name = line.substr(5); + continue; + } + if(line.find("target:") == 0) { + group->target = stoi(line.substr(7)); + continue; + } + if(line.find("property:") == 0) { + group->properties.push_back(line.substr(9)); + continue; } - if(line.find("target:") == 0) { - group->target = stoi(line.substr(7)); - continue; - } - if(line.find("property:") == 0) { - group->properties.push_back(line.substr(9)); - continue; - } - if(line.find("permission:") == 0) { - line = line.substr(11); - auto assign_index = line.find('='); + if(line.find("permission:") == 0) { + line = line.substr(11); + auto assign_index = line.find('='); - string permission_name = line.substr(0, assign_index); - string string_value = line.substr(assign_index + 1); - string string_granted, string_skip, string_negate; + string permission_name = line.substr(0, assign_index); + string string_value = line.substr(assign_index + 1); + string string_granted, string_skip, string_negate; - if(string_value.find(',') != -1) { - string_granted = string_value.substr(string_value.find(',') + 1); - string_value = string_value.substr(0, string_value.find(',')); - } - if(string_granted.find(',') != -1) { - string_skip = string_granted.substr(string_granted.find(',') + 1); - string_granted = string_granted.substr(0, string_granted.find(',')); - } - if(string_skip.find(',') != -1) { - string_negate = string_skip.substr(string_skip.find(',') + 1); - string_skip = string_skip.substr(0, string_skip.find(',')); - } + if(string_value.find(',') != -1) { + string_granted = string_value.substr(string_value.find(',') + 1); + string_value = string_value.substr(0, string_value.find(',')); + } + if(string_granted.find(',') != -1) { + string_skip = string_granted.substr(string_granted.find(',') + 1); + string_granted = string_granted.substr(0, string_granted.find(',')); + } + if(string_skip.find(',') != -1) { + string_negate = string_skip.substr(string_skip.find(',') + 1); + string_skip = string_skip.substr(0, string_skip.find(',')); + } - auto permInfo = permission::resolvePermissionData(permission_name); - if(permInfo->type == permission::unknown){ - logError(LOG_INSTANCE, "Default permission file contains unknown permission. Key: {}", permission_name); - continue; - } + auto permInfo = permission::resolvePermissionData(permission_name); + if(permInfo->type == permission::unknown){ + logError(LOG_INSTANCE, "Default permission file contains unknown permission. Key: {}", permission_name); + continue; + } - permission::PermissionValue permission_value; - try { - permission_value = stoi(string_value); - } catch(std::exception& ex) { - logError(LOG_INSTANCE, "Failed to parse value for key {}. Value: {}", permission_name, string_value); - continue; - } + permission::PermissionValue permission_value; + try { + permission_value = stoi(string_value); + } catch(std::exception& ex) { + logError(LOG_INSTANCE, "Failed to parse value for key {}. Value: {}", permission_name, string_value); + continue; + } - permission::PermissionValue permission_granted = permNotGranted; - if(!string_granted.empty()) { - try { - permission_granted = stoi(string_granted); - } catch(std::exception& ex) { - logError(LOG_INSTANCE, "Failed to parse granted value for key {}. Value: {}", permission_name, string_granted); - continue; - } - } + permission::PermissionValue permission_granted = permNotGranted; + if(!string_granted.empty()) { + try { + permission_granted = stoi(string_granted); + } catch(std::exception& ex) { + logError(LOG_INSTANCE, "Failed to parse granted value for key {}. Value: {}", permission_name, string_granted); + continue; + } + } - bool flag_skip = string_skip == "true" || string_skip == "1"; - bool flag_negate = string_negate == "true" || string_negate == "1"; + bool flag_skip = string_skip == "true" || string_skip == "1"; + bool flag_negate = string_negate == "true" || string_negate == "1"; - group->permissions.emplace_back(make_tuple(permInfo->type, permission_value, permission_granted, flag_skip, flag_negate)); - } - } - groups.push_back(group); - } + group->permissions.emplace_back(make_tuple(permInfo->type, permission_value, permission_granted, flag_skip, flag_negate)); + } + } + groups.push_back(group); + } - debugMessage(LOG_INSTANCE, "Read " + to_string(groups.size()) + " default groups"); - for(const auto& info : groups) { - debugMessage(LOG_INSTANCE, "Creating default group {} with type {}", info->name, to_string(info->target)); - //Query groups - auto group = this->groupManager->createGroup( - info->target != 2 ? GroupTarget::GROUPTARGET_SERVER : GroupTarget::GROUPTARGET_CHANNEL, - info->target == 0 ? GroupType::GROUP_TYPE_QUERY : GroupType::GROUP_TYPE_TEMPLATE, - info->name - ); - for(auto perm : info->permissions) { - group->permissions()->set_permission(get<0>(perm), {get<1>(perm), get<2>(perm)}, permission::v2::set_value, permission::v2::set_value, get<3>(perm), get<4>(perm)); - } + debugMessage(LOG_INSTANCE, "Read " + to_string(groups.size()) + " default groups"); + for(const auto& info : groups) { + debugMessage(LOG_INSTANCE, "Creating default group {} with type {}", info->name, to_string(info->target)); + //Query groups + auto group = this->groupManager->createGroup( + info->target != 2 ? GroupTarget::GROUPTARGET_SERVER : GroupTarget::GROUPTARGET_CHANNEL, + info->target == 0 ? GroupType::GROUP_TYPE_QUERY : GroupType::GROUP_TYPE_TEMPLATE, + info->name + ); + for(auto perm : info->permissions) { + group->permissions()->set_permission(get<0>(perm), {get<1>(perm), get<2>(perm)}, permission::v2::set_value, permission::v2::set_value, get<3>(perm), get<4>(perm)); + } - for(const auto& property : info->properties) { - const auto& prop = property::impl::info(property); - if(*prop == property::SERVERINSTANCE_UNDEFINED) { - logCritical(LOG_INSTANCE, "Invalid template property name: " + property); - } else { - this->properties()[prop] = group->groupId(); - } - } - } - this->save_group_permissions(); - this->getSql()->pool->threads()->wait_for(); //Wait for all permissions to flush - return true; + for(const auto& property : info->properties) { + const auto& prop = property::impl::info(property); + if(*prop == property::SERVERINSTANCE_UNDEFINED) { + logCritical(LOG_INSTANCE, "Invalid template property name: " + property); + } else { + this->properties()[prop] = group->groupId(); + } + } + } + this->save_group_permissions(); + this->getSql()->pool->threads()->wait_for(); //Wait for all permissions to flush + return true; } diff --git a/server/src/ServerManager.cpp b/server/src/ServerManager.cpp index 43c2279..9f98703 100644 --- a/server/src/ServerManager.cpp +++ b/server/src/ServerManager.cpp @@ -12,27 +12,27 @@ using namespace ts::server; ServerManager::ServerManager(InstanceHandler* handle) : handle(handle) { this->puzzles = new protocol::PuzzleManager(); - this->handshakeTickers = new threads::Scheduler(1, "handshake ticker"); + this->handshakeTickers = new threads::Scheduler(1, "handshake ticker"); this->execute_loop = new event::EventExecutor("executor #"); - //this->join_loop = new event::EventExecutor("joiner #"); + //this->join_loop = new event::EventExecutor("joiner #"); this->_ioManager = new io::VoiceIOManager(); - this->handshakeTickers->schedule("ticker", [&](){ this->tickHandshakeClients(); }, seconds(1)); + this->handshakeTickers->schedule("ticker", [&](){ this->tickHandshakeClients(); }, seconds(1)); } ServerManager::~ServerManager() { - this->state = State::STOPPED; + this->state = State::STOPPED; { threads::MutexLock lock(this->instanceLock); this->instances.clear(); } - { - this->acknowledge.condition.notify_all(); - if(this->acknowledge.thread) - this->acknowledge.thread->join(); - delete this->acknowledge.thread; - } + { + this->acknowledge.condition.notify_all(); + if(this->acknowledge.thread) + this->acknowledge.thread->join(); + delete this->acknowledge.thread; + } delete this->puzzles; this->puzzles = nullptr; @@ -43,15 +43,15 @@ ServerManager::~ServerManager() { this->execute_loop = nullptr; if(this->join_loop) - this->join_loop->shutdown(); + this->join_loop->shutdown(); delete this->join_loop; this->join_loop = nullptr; if(this->handshakeTickers) { this->handshakeTickers->shutdown(); } - delete this->handshakeTickers; - this->handshakeTickers = nullptr; + delete this->handshakeTickers; + this->handshakeTickers = nullptr; if(this->_ioManager) this->_ioManager->shutdownGlobally(); delete this->_ioManager; @@ -89,33 +89,33 @@ bool ServerManager::initialize(bool autostart) { uint16_t port = 0; for(int index = 0; index < length; index++) { - try { - if(columns[index] == "serverId") - id = static_cast(stoll(values[index])); - else if(columns[index] == "host") - host = values[index]; - else if(columns[index] == "port") - port = static_cast(stoul(values[index])); - } catch(std::exception& ex) { - logError(LOG_INSTANCE, "Failed to parse virtual server from database. Failed to parse field {} with value {}: {}", columns[index], values[index], ex.what()); - return 0; - } + try { + if(columns[index] == "serverId") + id = static_cast(stoll(values[index])); + else if(columns[index] == "host") + host = values[index]; + else if(columns[index] == "port") + port = static_cast(stoul(values[index])); + } catch(std::exception& ex) { + logError(LOG_INSTANCE, "Failed to parse virtual server from database. Failed to parse field {} with value {}: {}", columns[index], values[index], ex.what()); + return 0; + } } if(id == 0) { - logError(LOG_INSTANCE, "Failed to load virtual server from database. Server id is zero!"); - return 0; + logError(LOG_INSTANCE, "Failed to load virtual server from database. Server id is zero!"); + return 0; } if(host.empty()) { - logWarning(id, "The loaded host is empty. Using default one (from the config.yml)"); - host = config::binding::DefaultVoiceHost; - } + logWarning(id, "The loaded host is empty. Using default one (from the config.yml)"); + host = config::binding::DefaultVoiceHost; + } if(port == 0) { - logError(LOG_INSTANCE, "Failed to load virtual server from database. Server port is zero!"); - return 0; + logError(LOG_INSTANCE, "Failed to load virtual server from database. Server port is zero!"); + return 0; } @@ -159,18 +159,18 @@ bool ServerManager::initialize(bool autostart) { { this->acknowledge.thread = new threads::Thread(THREAD_SAVE_OPERATIONS, [&] { - system_clock::time_point next_execute = system_clock::now() + milliseconds(500); + system_clock::time_point next_execute = system_clock::now() + milliseconds(500); while(this->state == State::STARTED || this->state == State::STARTING) { - unique_lock lock(this->acknowledge.lock); - this->acknowledge.condition.wait_until(lock, next_execute, [&](){ return this->state != State::STARTED && this->state != State::STARTING; }); + unique_lock lock(this->acknowledge.lock); + this->acknowledge.condition.wait_until(lock, next_execute, [&](){ return this->state != State::STARTED && this->state != State::STARTING; }); - auto now = system_clock::now(); - next_execute = now + milliseconds(500); - for(const auto& server : this->serverInstances()) { - auto vserver = server->getVoiceServer(); //Read this only once - if(vserver) + auto now = system_clock::now(); + next_execute = now + milliseconds(500); + for(const auto& server : this->serverInstances()) { + auto vserver = server->getVoiceServer(); //Read this only once + if(vserver) vserver->execute_resend(now, next_execute); - } + } } return 0; }); @@ -205,9 +205,9 @@ uint16_t ServerManager::next_available_port() { auto vserver = instance->getVoiceServer(); if(instance->running() && vserver) { - for(const auto& binding : vserver->activeBindings()) { - unallowed_ports.push_back(binding->address_port()); - } + for(const auto& binding : vserver->activeBindings()) { + unallowed_ports.push_back(binding->address_port()); + } } } @@ -323,9 +323,9 @@ shared_ptr ServerManager::createServer(std::string hosts, uint16_t por auto server = make_shared(serverId, this->handle->getSql()); server->self = server; - if(!server->initialize(true)) { - //FIXME error handling - } + if(!server->initialize(true)) { + //FIXME error handling + } server->properties()[property::VIRTUALSERVER_HOST] = hosts; server->properties()[property::VIRTUALSERVER_PORT] = port; @@ -340,16 +340,16 @@ shared_ptr ServerManager::createServer(std::string hosts, uint16_t por bool ServerManager::deleteServer(shared_ptr server) { { threads::MutexLock l(this->instanceLock); - bool found = false; - for(const auto& s : this->instances) - if(s == server) { - found = true; - break; - } - if(!found) return false; - this->instances.erase(std::remove_if(this->instances.begin(), this->instances.end(), [&](const shared_ptr& s) { - return s == server; - }), this->instances.end()); + bool found = false; + for(const auto& s : this->instances) + if(s == server) { + found = true; + break; + } + if(!found) return false; + this->instances.erase(std::remove_if(this->instances.begin(), this->instances.end(), [&](const shared_ptr& s) { + return s == server; + }), this->instances.end()); } this->adjust_executor_threads(); @@ -365,10 +365,10 @@ bool ServerManager::deleteServer(shared_ptr server) { } } } - { - threads::MutexLock locK(server->stateLock); - server->state = ServerState::DELETING; - } + { + threads::MutexLock locK(server->stateLock); + server->state = ServerState::DELETING; + } this->handle->properties()[property::SERVERINSTANCE_SPOKEN_TIME_DELETED] += server->properties()[property::VIRTUALSERVER_SPOKEN_TIME].as(); sql::command(this->handle->getSql(), "DELETE FROM `tokens` WHERE `serverId` = :sid", variable{":sid", server->getServerId()}).executeLater().waitAndGetLater(LOG_SQL_CMD, {1, "future failed"}); @@ -419,9 +419,9 @@ void ServerManager::shutdownAll(const std::string& msg) { } void ServerManager::tickHandshakeClients() { - for(const auto& server : this->serverInstances()) { + for(const auto& server : this->serverInstances()) { auto vserver = server->getVoiceServer(); if(vserver) vserver->tickHandshakingClients(); - } + } } \ No newline at end of file diff --git a/server/src/ServerManager.h b/server/src/ServerManager.h index 10ea016..891e66a 100644 --- a/server/src/ServerManager.h +++ b/server/src/ServerManager.h @@ -61,7 +61,7 @@ namespace ts { protocol::PuzzleManager* rsaPuzzles() { return this->puzzles; } - event::EventExecutor* get_join_loop() { return this->join_loop; } + event::EventExecutor* get_join_loop() { return this->join_loop; } event::EventExecutor* get_executor_loop() { return this->execute_loop; } inline void adjust_executor_threads() { @@ -85,8 +85,8 @@ namespace ts { protocol::PuzzleManager* puzzles = nullptr; event::EventExecutor* execute_loop = nullptr; - event::EventExecutor* join_loop = nullptr; - threads::Scheduler* handshakeTickers = nullptr; + event::EventExecutor* join_loop = nullptr; + threads::Scheduler* handshakeTickers = nullptr; io::VoiceIOManager* _ioManager = nullptr; struct { diff --git a/server/src/ServerManagerSnapshotDeploy.cpp b/server/src/ServerManagerSnapshotDeploy.cpp index aca85ad..e14059a 100644 --- a/server/src/ServerManagerSnapshotDeploy.cpp +++ b/server/src/ServerManagerSnapshotDeploy.cpp @@ -15,1209 +15,1209 @@ using namespace ts::server; #define PREFIX string("[SNAPSHOT] ") inline std::string avArguments(const ts::Command& cmd, int index){ - stringstream res; - for(const auto &key : cmd[index].keys()) - res << key << "=" << cmd[index][key].as() << " "; - return res.str(); + stringstream res; + for(const auto &key : cmd[index].keys()) + res << key << "=" << cmd[index][key].as() << " "; + return res.str(); } //TeamSpeak: permid=i_channel_needed_permission_modify_power permvalue=75 permskip=0 permnegated=0 //TeaSpeak: perm=i_channel_needed_permission_modify_power value=75 granted=75 flag_skip=0 flag_negated=0 struct SnapshotPermissionEntry { - std::shared_ptr type = permission::PermissionTypeEntry::unknown; - permission::PermissionValue value = permNotGranted; - permission::PermissionValue grant = permNotGranted; - bool negated = false; - bool skipped = false; + std::shared_ptr type = permission::PermissionTypeEntry::unknown; + permission::PermissionValue value = permNotGranted; + permission::PermissionValue grant = permNotGranted; + bool negated = false; + bool skipped = false; - SnapshotPermissionEntry(const std::shared_ptr& type, permission::PermissionValue value, permission::PermissionValue grant) : type(std::move(type)), value(value), grant(grant) {} - SnapshotPermissionEntry(const std::shared_ptr& type, permission::PermissionValue value, permission::PermissionValue grant, bool negated, bool skipped) : type(type), value(value), grant(grant), negated(negated), skipped(skipped) {} - SnapshotPermissionEntry() = default; + SnapshotPermissionEntry(const std::shared_ptr& type, permission::PermissionValue value, permission::PermissionValue grant) : type(std::move(type)), value(value), grant(grant) {} + SnapshotPermissionEntry(const std::shared_ptr& type, permission::PermissionValue value, permission::PermissionValue grant, bool negated, bool skipped) : type(type), value(value), grant(grant), negated(negated), skipped(skipped) {} + SnapshotPermissionEntry() = default; - static deque> parse(const Command& cmd, int& index, permission::teamspeak::GroupType type, int version, const std::vector& ends, bool ignore_first = false) { - deque> result{}; + static deque> parse(const Command& cmd, int& index, permission::teamspeak::GroupType type, int version, const std::vector& ends, bool ignore_first = false) { + deque> result{}; - while(true) { - bool end_reached = false; - for(const auto& e : ends) - if(cmd[index].has(e)) { end_reached = true; break; } - if(end_reached || cmd.bulkCount() < index) { - if(ignore_first) ignore_first = false; - else break; - } + while(true) { + bool end_reached = false; + for(const auto& e : ends) + if(cmd[index].has(e)) { end_reached = true; break; } + if(end_reached || cmd.bulkCount() < index) { + if(ignore_first) ignore_first = false; + else break; + } - if(version == 0) { - auto permission_name = cmd[index]["permid"].string(); - for(const auto& mapped : permission::teamspeak::map_key(permission_name, type)) { - auto permission = permission::resolvePermissionData(mapped); - if(permission->type == permission::unknown) { - logError(0, "Failed to parse permission {}. Original: {}", mapped, permission_name); - index++; - continue; - } + if(version == 0) { + auto permission_name = cmd[index]["permid"].string(); + for(const auto& mapped : permission::teamspeak::map_key(permission_name, type)) { + auto permission = permission::resolvePermissionData(mapped); + if(permission->type == permission::unknown) { + logError(0, "Failed to parse permission {}. Original: {}", mapped, permission_name); + index++; + continue; + } - bool found = false; - for(auto& e : result) - if(e->type->type == permission->type) { - found = true; - if(permission->grantName() == mapped) - e->grant = cmd[index]["permvalue"]; - else { - e->value = cmd[index]["permvalue"]; - e->negated = cmd[index]["permnegated"]; - e->skipped = cmd[index]["permskip"]; - } - } - if(!found) { - auto entry = make_unique(); - entry->type = permission; - if(permission->grantName() == mapped) - entry->grant = cmd[index]["permvalue"]; - else { - entry->value = cmd[index]["permvalue"]; - entry->negated = cmd[index]["permnegated"]; - entry->skipped = cmd[index]["permskip"]; - } - result.push_back(std::move(entry)); - } - } - } else if(version >= 1) { - auto permission_name = cmd[index]["perm"].string(); + bool found = false; + for(auto& e : result) + if(e->type->type == permission->type) { + found = true; + if(permission->grantName() == mapped) + e->grant = cmd[index]["permvalue"]; + else { + e->value = cmd[index]["permvalue"]; + e->negated = cmd[index]["permnegated"]; + e->skipped = cmd[index]["permskip"]; + } + } + if(!found) { + auto entry = make_unique(); + entry->type = permission; + if(permission->grantName() == mapped) + entry->grant = cmd[index]["permvalue"]; + else { + entry->value = cmd[index]["permvalue"]; + entry->negated = cmd[index]["permnegated"]; + entry->skipped = cmd[index]["permskip"]; + } + result.push_back(std::move(entry)); + } + } + } else if(version >= 1) { + auto permission_name = cmd[index]["perm"].string(); - auto permission = permission::resolvePermissionData(permission_name); - if(permission->type == permission::unknown) { - logError(0, "Failed to parse permission {}", permission_name); - index++; - continue; - } + auto permission = permission::resolvePermissionData(permission_name); + if(permission->type == permission::unknown) { + logError(0, "Failed to parse permission {}", permission_name); + index++; + continue; + } - auto entry = make_unique(); - entry->type = permission; - entry->value = cmd[index]["value"]; - entry->grant = cmd[index]["grant"]; - entry->skipped = cmd[index]["flag_skip"]; - entry->negated = cmd[index]["flag_negated"]; - result.push_back(std::move(entry)); - } else { - logError(0, "Failed to parse snapshot permission entry! Invalid version!"); - index++; - continue; - } + auto entry = make_unique(); + entry->type = permission; + entry->value = cmd[index]["value"]; + entry->grant = cmd[index]["grant"]; + entry->skipped = cmd[index]["flag_skip"]; + entry->negated = cmd[index]["flag_negated"]; + result.push_back(std::move(entry)); + } else { + logError(0, "Failed to parse snapshot permission entry! Invalid version!"); + index++; + continue; + } - index++; - } + index++; + } - deque> addings{}; - for(const auto& perm : result) { - if(perm->type->type == permission::i_group_auto_update_type) { //Migrate this type - for(const auto& e : permission::update::migrate) - if(e.type == perm->value) { - auto _type = permission::resolvePermissionData(e.permission.name); - if(_type->type == permission::unknown) { - logError(0, "Invalid update permission for update group {}. Permission name: {}", e.type, e.permission.name); - } else { - addings.push_back(make_unique(_type, e.permission.value, e.permission.granted, e.permission.negated, e.permission.skipped)); - } - } - } - } - for(auto& a : addings) - result.push_back(move(a)); + deque> addings{}; + for(const auto& perm : result) { + if(perm->type->type == permission::i_group_auto_update_type) { //Migrate this type + for(const auto& e : permission::update::migrate) + if(e.type == perm->value) { + auto _type = permission::resolvePermissionData(e.permission.name); + if(_type->type == permission::unknown) { + logError(0, "Invalid update permission for update group {}. Permission name: {}", e.type, e.permission.name); + } else { + addings.push_back(make_unique(_type, e.permission.value, e.permission.granted, e.permission.negated, e.permission.skipped)); + } + } + } + } + for(auto& a : addings) + result.push_back(move(a)); - return result; - } + return result; + } - void write(Command& cmd, int& index, permission::teamspeak::GroupType type, int version) { - if(version == 0) { - if(this->value != permNotGranted) { - for(const auto& name : permission::teamspeak::unmap_key(this->type->name, type)) { - cmd[index]["permid"] = name; - cmd[index]["permvalue"] = this->value; - cmd[index]["permskip"] = this->skipped; - cmd[index]["permnegated"] = this->negated; - index++; - } - } - if(this->grant != permNotGranted) { - for(const auto& name : permission::teamspeak::unmap_key(this->type->grantName(), type)) { - cmd[index]["permid"] = name; - cmd[index]["permvalue"] = this->grant; - cmd[index]["permskip"] = false; - cmd[index]["permnegated"] = false; - index++; - } - } - } else if(version >= 1) { - cmd[index]["perm"] = this->type->name; - cmd[index]["value"] = this->value; - cmd[index]["grant"] = this->grant; - cmd[index]["flag_skip"] = this->skipped; - cmd[index]["flag_negated"] = this->negated; - index++; - } else { - logError(0, "Could not write snapshot permission! Invalid version. ({})", version); - } - } + void write(Command& cmd, int& index, permission::teamspeak::GroupType type, int version) { + if(version == 0) { + if(this->value != permNotGranted) { + for(const auto& name : permission::teamspeak::unmap_key(this->type->name, type)) { + cmd[index]["permid"] = name; + cmd[index]["permvalue"] = this->value; + cmd[index]["permskip"] = this->skipped; + cmd[index]["permnegated"] = this->negated; + index++; + } + } + if(this->grant != permNotGranted) { + for(const auto& name : permission::teamspeak::unmap_key(this->type->grantName(), type)) { + cmd[index]["permid"] = name; + cmd[index]["permvalue"] = this->grant; + cmd[index]["permskip"] = false; + cmd[index]["permnegated"] = false; + index++; + } + } + } else if(version >= 1) { + cmd[index]["perm"] = this->type->name; + cmd[index]["value"] = this->value; + cmd[index]["grant"] = this->grant; + cmd[index]["flag_skip"] = this->skipped; + cmd[index]["flag_negated"] = this->negated; + index++; + } else { + logError(0, "Could not write snapshot permission! Invalid version. ({})", version); + } + } }; std::shared_ptr ServerManager::createServerFromSnapshot(shared_ptr old, std::string host, uint16_t port, const ts::Command &arguments, std::string &error) { - ServerId serverId = 0; - map> channelGroupRelations; //cid is the new cgid - map channelGroupMapping; - map> serverGroupRelations; //gid is the new gid - map serverGroupMapping; - map db_mapping_client_id; + ServerId serverId = 0; + map> channelGroupRelations; //cid is the new cgid + map channelGroupMapping; + map> serverGroupRelations; //gid is the new gid + map serverGroupMapping; + map db_mapping_client_id; - deque>> futures; + deque>> futures; - bool sid_success = false; - serverId = this->next_available_server_id(sid_success); - if(!sid_success) { - error = "failed to generate new sid"; - return nullptr; - } - ServerId log_server_id = old ? old->getServerId() : serverId; + bool sid_success = false; + serverId = this->next_available_server_id(sid_success); + if(!sid_success) { + error = "failed to generate new sid"; + return nullptr; + } + ServerId log_server_id = old ? old->getServerId() : serverId; - futures.push_back({ - "server id register", - sql::command(this->handle->getSql(), "INSERT INTO `servers` (`serverId`, `host`, `port`) VALUES (:sid, :host, :port)", variable{":sid", serverId}, variable{":host", host}, variable{":port", port}).executeLater() - }); - sql::model sql_insert_channel(this->handle->getSql(), "INSERT INTO `channels` (`serverId`, `channelId`, `type`, `parentId`) VALUES (:sid, :id, :type, :pid)", variable{":sid", serverId}); - sql::model sql_insert_group_assignment(this->handle->getSql(), "INSERT INTO `assignedGroups` (`serverId`, `cldbid`, `groupId`, `channelId`, `until`) VALUES (:sid, :cldbid, :gid, :chid, :until)", variable{":sid", serverId}, variable{":until", 0}); - sql::model sql_insert_permission(this->handle->getSql(), "INSERT INTO `permissions` (`serverId`, `type`, `id`, `channelId`, `permId`, `value`, `grant`, `flag_skip`, `flag_negate`) VALUES (:serverId, :type, :id, :chId, :permId, :value, :grant, :flag_skip, :flag_negate)", - variable{":serverId", serverId}); - sql::model sql_update_permission_grant(this->handle->getSql(), "UPDATE `permissions` SET `grant` = :grant WHERE `serverId` = :sid AND `type` = :type AND `id` = :id AND `channelId` = :chid AND `permId` = :key", variable{":sid", serverId}); - sql::model sql_insert_property(this->handle->getSql(), "INSERT INTO `properties` (`serverId`, `type`, `id`, `key`, `value`) VALUES (:sid, :type, :id, :key, :value)", variable{":sid", serverId}); - sql::model sql_insert_bot(this->handle->getSql(), "INSERT INTO `musicbots` (`serverId`, `botId`, `uniqueId`, `owner`) VALUES (:server_id, :bot_id, :unique_id, :owner)", variable{":server_id", serverId}); - sql::model sql_insert_playlist(this->handle->getSql(), "INSERT INTO `playlists` (`serverId`, `playlist_id`) VALUES (:server_id, :playlist_id)", variable{":server_id", serverId}); - sql::model sql_insert_playlist_song(this->handle->getSql(), "INSERT INTO `playlist_songs` (`serverId`, `playlist_id`, `song_id`, `order_id`, `invoker_dbid`, `url`, `url_loader`, `loaded`, `metadata`) VALUES (:server_id, :playlist_id, :song_id, :order_id, :invoker_dbid, :url, :url_loader, :loaded, :metadata)", variable{":server_id", serverId}); + futures.push_back({ + "server id register", + sql::command(this->handle->getSql(), "INSERT INTO `servers` (`serverId`, `host`, `port`) VALUES (:sid, :host, :port)", variable{":sid", serverId}, variable{":host", host}, variable{":port", port}).executeLater() + }); + sql::model sql_insert_channel(this->handle->getSql(), "INSERT INTO `channels` (`serverId`, `channelId`, `type`, `parentId`) VALUES (:sid, :id, :type, :pid)", variable{":sid", serverId}); + sql::model sql_insert_group_assignment(this->handle->getSql(), "INSERT INTO `assignedGroups` (`serverId`, `cldbid`, `groupId`, `channelId`, `until`) VALUES (:sid, :cldbid, :gid, :chid, :until)", variable{":sid", serverId}, variable{":until", 0}); + sql::model sql_insert_permission(this->handle->getSql(), "INSERT INTO `permissions` (`serverId`, `type`, `id`, `channelId`, `permId`, `value`, `grant`, `flag_skip`, `flag_negate`) VALUES (:serverId, :type, :id, :chId, :permId, :value, :grant, :flag_skip, :flag_negate)", + variable{":serverId", serverId}); + sql::model sql_update_permission_grant(this->handle->getSql(), "UPDATE `permissions` SET `grant` = :grant WHERE `serverId` = :sid AND `type` = :type AND `id` = :id AND `channelId` = :chid AND `permId` = :key", variable{":sid", serverId}); + sql::model sql_insert_property(this->handle->getSql(), "INSERT INTO `properties` (`serverId`, `type`, `id`, `key`, `value`) VALUES (:sid, :type, :id, :key, :value)", variable{":sid", serverId}); + sql::model sql_insert_bot(this->handle->getSql(), "INSERT INTO `musicbots` (`serverId`, `botId`, `uniqueId`, `owner`) VALUES (:server_id, :bot_id, :unique_id, :owner)", variable{":server_id", serverId}); + sql::model sql_insert_playlist(this->handle->getSql(), "INSERT INTO `playlists` (`serverId`, `playlist_id`) VALUES (:server_id, :playlist_id)", variable{":server_id", serverId}); + sql::model sql_insert_playlist_song(this->handle->getSql(), "INSERT INTO `playlist_songs` (`serverId`, `playlist_id`, `song_id`, `order_id`, `invoker_dbid`, `url`, `url_loader`, `loaded`, `metadata`) VALUES (:server_id, :playlist_id, :song_id, :order_id, :invoker_dbid, :url, :url_loader, :loaded, :metadata)", variable{":server_id", serverId}); - int index = 0; - auto snapshot_version = arguments[index].has("snapshot_version") ? arguments[index++]["snapshot_version"] : 0; - debugMessage(0, "Got server snapshot with version {}", snapshot_version); - while(true){ - for(auto &key : arguments[index].keys()){ - if(key == "end_virtualserver") continue; - if(key == "begin_virtualserver") continue; - if(snapshot_version == 0) { - if(key == "virtualserver_download_quota" || key == "virtualserver_upload_quota" || key == "virtualserver_max_download_total_bandwidth" || key == "virtualserver_max_upload_total_bandwidth") { - auto value = arguments[index][key].string(); - try { - arguments[index][key] = to_string((int64_t) std::stoull(value)); - } catch(const std::exception& ex) { - continue; - } - } - } - debugMessage(LOG_GENERAL, PREFIX + " Having server key {} = {}", key, arguments[index][key].as()); - futures.push_back({ - "server property import proprty=" + key, - sql_insert_property.command().values( - variable{":type", property::PropertyType::PROP_TYPE_SERVER}, - variable{":id", 0}, - variable{":key", key}, - variable{":value", arguments[index][key].string()} - ).executeLater() - }); - } + int index = 0; + auto snapshot_version = arguments[index].has("snapshot_version") ? arguments[index++]["snapshot_version"] : 0; + debugMessage(0, "Got server snapshot with version {}", snapshot_version); + while(true){ + for(auto &key : arguments[index].keys()){ + if(key == "end_virtualserver") continue; + if(key == "begin_virtualserver") continue; + if(snapshot_version == 0) { + if(key == "virtualserver_download_quota" || key == "virtualserver_upload_quota" || key == "virtualserver_max_download_total_bandwidth" || key == "virtualserver_max_upload_total_bandwidth") { + auto value = arguments[index][key].string(); + try { + arguments[index][key] = to_string((int64_t) std::stoull(value)); + } catch(const std::exception& ex) { + continue; + } + } + } + debugMessage(LOG_GENERAL, PREFIX + " Having server key {} = {}", key, arguments[index][key].as()); + futures.push_back({ + "server property import proprty=" + key, + sql_insert_property.command().values( + variable{":type", property::PropertyType::PROP_TYPE_SERVER}, + variable{":id", 0}, + variable{":key", key}, + variable{":value", arguments[index][key].string()} + ).executeLater() + }); + } - if(arguments[index].has("end_virtualserver")) break; - index++; - } + if(arguments[index].has("end_virtualserver")) break; + index++; + } - //`serverId` INT NOT NULL, `playlist_id` INT, `song_id` INT, `order_id` INT, `invoker_dbid` INT, `url` TEXT, `url_loader` TEXT - /* - * sql::command(this->handle->getSql(), "INSERT INTO `playlists` (`serverId`, `playlist_id`) VALUES (:server_id, :playlist_id)", - variable{":server_id", this->ref_server()->getServerId()}, - variable{":playlist_id", playlist_id} - ) - */ - index++; - for(;index < arguments.bulkCount(); index++) { - if (arguments[index].has("begin_channels")) { - while (true) { - if (arguments[index].has("end_channels")) break; + //`serverId` INT NOT NULL, `playlist_id` INT, `song_id` INT, `order_id` INT, `invoker_dbid` INT, `url` TEXT, `url_loader` TEXT + /* + * sql::command(this->handle->getSql(), "INSERT INTO `playlists` (`serverId`, `playlist_id`) VALUES (:server_id, :playlist_id)", + variable{":server_id", this->ref_server()->getServerId()}, + variable{":playlist_id", playlist_id} + ) + */ + index++; + for(;index < arguments.bulkCount(); index++) { + if (arguments[index].has("begin_channels")) { + while (true) { + if (arguments[index].has("end_channels")) break; - auto channel_id = arguments[index]["channel_id"].string(); - futures.push_back({ - "channel register cid=" + channel_id, - sql_insert_channel.command().values(variable{":id", channel_id}, variable{":type", -1}, variable{":pid", arguments[index]["channel_pid"].string()}).executeLater() - }); //TODO findout type! - for (const auto &key : arguments[index].keys()) { - if(key == "channel_id") continue; - if(key == "channel_pid") continue; - if(key == "channel_security_salt") continue; - if(key == "channel_filepath") continue; //TODO implement channel_filepath? - if(key == "begin_channels") continue; + auto channel_id = arguments[index]["channel_id"].string(); + futures.push_back({ + "channel register cid=" + channel_id, + sql_insert_channel.command().values(variable{":id", channel_id}, variable{":type", -1}, variable{":pid", arguments[index]["channel_pid"].string()}).executeLater() + }); //TODO findout type! + for (const auto &key : arguments[index].keys()) { + if(key == "channel_id") continue; + if(key == "channel_pid") continue; + if(key == "channel_security_salt") continue; + if(key == "channel_filepath") continue; //TODO implement channel_filepath? + if(key == "begin_channels") continue; - debugMessage(log_server_id, PREFIX + "Having channel key " + key + "=" + arguments[index][key].as() + " for channel " + arguments[index]["channel_id"].as()); + debugMessage(log_server_id, PREFIX + "Having channel key " + key + "=" + arguments[index][key].as() + " for channel " + arguments[index]["channel_id"].as()); - futures.push_back({ - "channel prop register cid=" + channel_id + " property=" + key, - sql_insert_property.command().values(variable{":sid", serverId}, - variable{":type", property::PropertyType::PROP_TYPE_CHANNEL}, - variable{":id", channel_id}, - variable{":key", key}, - variable{":value", arguments[index][key].as()} - ).executeLater() + futures.push_back({ + "channel prop register cid=" + channel_id + " property=" + key, + sql_insert_property.command().values(variable{":sid", serverId}, + variable{":type", property::PropertyType::PROP_TYPE_CHANNEL}, + variable{":id", channel_id}, + variable{":key", key}, + variable{":value", arguments[index][key].as()} + ).executeLater() }); - } - index++; - } - } else if (arguments[index].has("begin_clients")) { - while (true) { - if (arguments[index].has("end_clients")) break; - debugMessage(log_server_id, PREFIX + "Create client {}/{} -> end: {}",arguments[index]["client_id"].as(), arguments[index]["client_nickname"].as(), to_string(arguments[index].has("end_clients"))); + } + index++; + } + } else if (arguments[index].has("begin_clients")) { + while (true) { + if (arguments[index].has("end_clients")) break; + debugMessage(log_server_id, PREFIX + "Create client {}/{} -> end: {}",arguments[index]["client_id"].as(), arguments[index]["client_nickname"].as(), to_string(arguments[index].has("end_clients"))); - ClientDbId oldId = 0; - auto res = sql::command(this->handle->getSql(), "SELECT `cldbid` FROM `clients` WHERE `clientUid` = :uid AND `serverId` = 0", variable{":uid", arguments[index]["client_unique_id"].as()}).query([](ClientDbId* ptr, int, char** v, char**){ *ptr = stoll(v[0]); return 0;}, &oldId); - LOG_SQL_CMD(res); - if(oldId == 0){ - res = sql::command(this->handle->getSql(), "SELECT `cldbid` FROM `clients` WHERE `serverId` = 0 ORDER BY `cldbid` DESC LIMIT 1").query([](ClientDbId* ptr, int length, char** values, char** names){ - *ptr = static_cast(stoll(values[0])); - return 0; - }, &oldId); - LOG_SQL_CMD(res); + ClientDbId oldId = 0; + auto res = sql::command(this->handle->getSql(), "SELECT `cldbid` FROM `clients` WHERE `clientUid` = :uid AND `serverId` = 0", variable{":uid", arguments[index]["client_unique_id"].as()}).query([](ClientDbId* ptr, int, char** v, char**){ *ptr = stoll(v[0]); return 0;}, &oldId); + LOG_SQL_CMD(res); + if(oldId == 0){ + res = sql::command(this->handle->getSql(), "SELECT `cldbid` FROM `clients` WHERE `serverId` = 0 ORDER BY `cldbid` DESC LIMIT 1").query([](ClientDbId* ptr, int length, char** values, char** names){ + *ptr = static_cast(stoll(values[0])); + return 0; + }, &oldId); + LOG_SQL_CMD(res); - oldId += 1; - //Create a new client - sql::command(this->handle->getSql(), "INSERT INTO `clients` (`serverId`, `cldbid`, `clientUid`, `firstConnect`, `lastConnect`, `connections`, `lastName`) VALUES (:sid, :cldbid, :cluid, :firstConnect, :lastConnect, :connections, :name)", - variable{":sid", 0}, variable{":cldbid", oldId}, variable{":cluid", arguments[index]["client_unique_id"].as()}, - variable{":firstConnect", arguments[index]["client_created"].as()}, - variable{":lastConnect", "0"}, variable{":connections", "0"}, variable{":name", arguments[index]["client_nickname"].as()}).execute(); //TODO log error - } - db_mapping_client_id[arguments[index]["client_id"].as()] = oldId; - sql::command(this->handle->getSql(), "INSERT INTO `clients` (`serverId`, `cldbid`, `clientUid`, `firstConnect`, `lastConnect`, `connections`, `lastName`) VALUES (:sid, :cldbid, :cluid, :firstConnect, :lastConnect, :connections, :name)", - variable{":sid", serverId}, variable{":cldbid", oldId}, variable{":cluid", arguments[index]["client_unique_id"].as()}, - variable{":firstConnect", arguments[index]["client_created"].as()}, - variable{":lastConnect", "0"}, variable{":connections", "0"}, variable{":name", arguments[index]["client_nickname"].as()}).execute(); //TODO log error - //TODO here - index++; - } - } else if (arguments[index].has("begin_permissions")) { - while (true) { - index++; - if (arguments[index].has("end_permissions")) break; + oldId += 1; + //Create a new client + sql::command(this->handle->getSql(), "INSERT INTO `clients` (`serverId`, `cldbid`, `clientUid`, `firstConnect`, `lastConnect`, `connections`, `lastName`) VALUES (:sid, :cldbid, :cluid, :firstConnect, :lastConnect, :connections, :name)", + variable{":sid", 0}, variable{":cldbid", oldId}, variable{":cluid", arguments[index]["client_unique_id"].as()}, + variable{":firstConnect", arguments[index]["client_created"].as()}, + variable{":lastConnect", "0"}, variable{":connections", "0"}, variable{":name", arguments[index]["client_nickname"].as()}).execute(); //TODO log error + } + db_mapping_client_id[arguments[index]["client_id"].as()] = oldId; + sql::command(this->handle->getSql(), "INSERT INTO `clients` (`serverId`, `cldbid`, `clientUid`, `firstConnect`, `lastConnect`, `connections`, `lastName`) VALUES (:sid, :cldbid, :cluid, :firstConnect, :lastConnect, :connections, :name)", + variable{":sid", serverId}, variable{":cldbid", oldId}, variable{":cluid", arguments[index]["client_unique_id"].as()}, + variable{":firstConnect", arguments[index]["client_created"].as()}, + variable{":lastConnect", "0"}, variable{":connections", "0"}, variable{":name", arguments[index]["client_nickname"].as()}).execute(); //TODO log error + //TODO here + index++; + } + } else if (arguments[index].has("begin_permissions")) { + while (true) { + index++; + if (arguments[index].has("end_permissions")) break; - else if (arguments[index].has("server_groups")) { - GroupId currentGroupId = 1; - while (true) { - if (arguments[index].has("end_groups")) break; + else if (arguments[index].has("server_groups")) { + GroupId currentGroupId = 1; + while (true) { + if (arguments[index].has("end_groups")) break; - if(!arguments[index].has("id")){ //new group - logError(0, "Invalid server group permission entry! Missing group id!"); - index++; - continue; - } + if(!arguments[index].has("id")){ //new group + logError(0, "Invalid server group permission entry! Missing group id!"); + index++; + continue; + } - currentGroupId = static_cast(GroupManager::generateGroupId(this->handle->getSql())); - debugMessage(log_server_id, PREFIX + "Insert group: " + to_string(currentGroupId) + " - " + arguments[index]["name"].as()); - serverGroupMapping[arguments[index]["id"].as()] = currentGroupId; + currentGroupId = static_cast(GroupManager::generateGroupId(this->handle->getSql())); + debugMessage(log_server_id, PREFIX + "Insert group: " + to_string(currentGroupId) + " - " + arguments[index]["name"].as()); + serverGroupMapping[arguments[index]["id"].as()] = currentGroupId; - LOG_SQL_CMD(sql::command(this->handle->getSql(), "INSERT INTO `groups` (`serverId`, `groupId`, `target`, `type`, `displayName`) VALUES (:sid, :gid, :target, :type, :name)", - variable{":sid", serverId}, - variable{":gid", currentGroupId}, - variable{":target", GROUPTARGET_SERVER}, - variable{":type", GroupType::GROUP_TYPE_NORMAL}, - variable{":name", arguments[index]["name"].as()}) - .execute()); + LOG_SQL_CMD(sql::command(this->handle->getSql(), "INSERT INTO `groups` (`serverId`, `groupId`, `target`, `type`, `displayName`) VALUES (:sid, :gid, :target, :type, :name)", + variable{":sid", serverId}, + variable{":gid", currentGroupId}, + variable{":target", GROUPTARGET_SERVER}, + variable{":type", GroupType::GROUP_TYPE_NORMAL}, + variable{":name", arguments[index]["name"].as()}) + .execute()); - auto permissions = SnapshotPermissionEntry::parse(arguments, index, permission::teamspeak::SERVER, snapshot_version, {"end_group"}); - for(const auto& entry : permissions) { - futures.push_back({ - "server group permission sgid=" + to_string(currentGroupId) + " permId=" + entry->type->name, - sql_insert_permission.command().values( - variable{":id", currentGroupId}, - variable{":type", permission::SQL_PERM_GROUP}, - variable{":chId", 0}, + auto permissions = SnapshotPermissionEntry::parse(arguments, index, permission::teamspeak::SERVER, snapshot_version, {"end_group"}); + for(const auto& entry : permissions) { + futures.push_back({ + "server group permission sgid=" + to_string(currentGroupId) + " permId=" + entry->type->name, + sql_insert_permission.command().values( + variable{":id", currentGroupId}, + variable{":type", permission::SQL_PERM_GROUP}, + variable{":chId", 0}, - variable{":permId", entry->type->name}, - variable{":value", entry->value}, - variable{":grant", entry->grant}, - variable{":flag_skip", entry->skipped}, - variable{":flag_negate", entry->negated} - ).executeLater() - }); - } - index++; - } + variable{":permId", entry->type->name}, + variable{":value", entry->value}, + variable{":grant", entry->grant}, + variable{":flag_skip", entry->skipped}, + variable{":flag_negate", entry->negated} + ).executeLater() + }); + } + index++; + } - //Relations for sgroups after list - index++; - while (true) { - if (arguments[index].has("end_relations")) break; - serverGroupRelations[db_mapping_client_id[arguments[index]["cldbid"].as()]].push_back(serverGroupMapping[arguments[index]["gid"].as()]); - futures.push_back({ - "server group relation", - sql_insert_group_assignment.command().values(variable{":cldbid", db_mapping_client_id[arguments[index]["cldbid"].as()]}, variable{":gid", serverGroupMapping[arguments[index]["gid"].as()]}, variable{":chid", "0"}).executeLater() - }); - index++; - } - } else if(arguments[index].has("channel_groups")){ - GroupId currentGroupId = 1; - while (true) { - if (arguments[index].has("end_groups")) break; + //Relations for sgroups after list + index++; + while (true) { + if (arguments[index].has("end_relations")) break; + serverGroupRelations[db_mapping_client_id[arguments[index]["cldbid"].as()]].push_back(serverGroupMapping[arguments[index]["gid"].as()]); + futures.push_back({ + "server group relation", + sql_insert_group_assignment.command().values(variable{":cldbid", db_mapping_client_id[arguments[index]["cldbid"].as()]}, variable{":gid", serverGroupMapping[arguments[index]["gid"].as()]}, variable{":chid", "0"}).executeLater() + }); + index++; + } + } else if(arguments[index].has("channel_groups")){ + GroupId currentGroupId = 1; + while (true) { + if (arguments[index].has("end_groups")) break; - if(!arguments[index].has("id")){ //new group - logError(0, "Invalid server group permission entry! Missing group id!"); - index++; - continue; - } + if(!arguments[index].has("id")){ //new group + logError(0, "Invalid server group permission entry! Missing group id!"); + index++; + continue; + } - currentGroupId = static_cast(GroupManager::generateGroupId(this->handle->getSql())); - debugMessage(log_server_id, PREFIX + "Insert channel group: " + to_string(currentGroupId) + " - " + arguments[index]["name"].as()); - channelGroupMapping[arguments[index]["id"].as()] = currentGroupId; + currentGroupId = static_cast(GroupManager::generateGroupId(this->handle->getSql())); + debugMessage(log_server_id, PREFIX + "Insert channel group: " + to_string(currentGroupId) + " - " + arguments[index]["name"].as()); + channelGroupMapping[arguments[index]["id"].as()] = currentGroupId; - LOG_SQL_CMD(sql::command(this->handle->getSql(), "INSERT INTO `groups` (`serverId`, `groupId`, `target`, `type`, `displayName`) VALUES (:sid, :gid, :target, :type, :name)", - variable{":sid", serverId}, - variable{":gid", currentGroupId}, - variable{":target", GROUPTARGET_CHANNEL}, - variable{":type", GroupType::GROUP_TYPE_NORMAL}, - variable{":name", arguments[index]["name"].as()}) - .execute()); + LOG_SQL_CMD(sql::command(this->handle->getSql(), "INSERT INTO `groups` (`serverId`, `groupId`, `target`, `type`, `displayName`) VALUES (:sid, :gid, :target, :type, :name)", + variable{":sid", serverId}, + variable{":gid", currentGroupId}, + variable{":target", GROUPTARGET_CHANNEL}, + variable{":type", GroupType::GROUP_TYPE_NORMAL}, + variable{":name", arguments[index]["name"].as()}) + .execute()); - auto permissions = SnapshotPermissionEntry::parse(arguments, index, permission::teamspeak::SERVER, snapshot_version, {"end_group"}); - for(const auto& entry : permissions) { - futures.push_back({ - "channel group property insert cgid=" + to_string(currentGroupId) + " property=" + entry->type->name, - sql_insert_permission.command().values( - variable{":id", currentGroupId}, - variable{":type", permission::SQL_PERM_GROUP}, - variable{":chId", 0}, + auto permissions = SnapshotPermissionEntry::parse(arguments, index, permission::teamspeak::SERVER, snapshot_version, {"end_group"}); + for(const auto& entry : permissions) { + futures.push_back({ + "channel group property insert cgid=" + to_string(currentGroupId) + " property=" + entry->type->name, + sql_insert_permission.command().values( + variable{":id", currentGroupId}, + variable{":type", permission::SQL_PERM_GROUP}, + variable{":chId", 0}, - variable{":permId", entry->type->name}, - variable{":value", entry->value}, - variable{":grant", entry->grant}, - variable{":flag_skip", entry->skipped}, - variable{":flag_negate", entry->negated} - ).executeLater() - }); - } - index++; - } + variable{":permId", entry->type->name}, + variable{":value", entry->value}, + variable{":grant", entry->grant}, + variable{":flag_skip", entry->skipped}, + variable{":flag_negate", entry->negated} + ).executeLater() + }); + } + index++; + } - //Relations for sgroups after list - ChannelId chId = 0; - index++; - while (true) { - if (arguments[index].has("end_relations")) break; - if(arguments[index].has("iid")) chId = arguments[index]["iid"]; - channelGroupRelations[db_mapping_client_id[arguments[index]["cldbid"].as()]][chId] = channelGroupMapping[arguments[index]["gid"].as()]; - futures.push_back({ - "channel group relation", - sql_insert_group_assignment.command().values(variable{":cldbid", db_mapping_client_id[arguments[index]["cldbid"].as()]}, variable{":gid", channelGroupMapping[arguments[index]["gid"].as()]}, variable{":chid", chId}).executeLater() - }); - index++; - } - } else if (arguments[index].has("client_flat")) { - ClientDbId currentId = 0; - while (true) { - if (arguments[index].has("end_flat")) break; - if(arguments[index].has("id1")) - currentId = arguments[index]["id1"]; + //Relations for sgroups after list + ChannelId chId = 0; + index++; + while (true) { + if (arguments[index].has("end_relations")) break; + if(arguments[index].has("iid")) chId = arguments[index]["iid"]; + channelGroupRelations[db_mapping_client_id[arguments[index]["cldbid"].as()]][chId] = channelGroupMapping[arguments[index]["gid"].as()]; + futures.push_back({ + "channel group relation", + sql_insert_group_assignment.command().values(variable{":cldbid", db_mapping_client_id[arguments[index]["cldbid"].as()]}, variable{":gid", channelGroupMapping[arguments[index]["gid"].as()]}, variable{":chid", chId}).executeLater() + }); + index++; + } + } else if (arguments[index].has("client_flat")) { + ClientDbId currentId = 0; + while (true) { + if (arguments[index].has("end_flat")) break; + if(arguments[index].has("id1")) + currentId = arguments[index]["id1"]; - auto permissions = SnapshotPermissionEntry::parse(arguments, index, permission::teamspeak::CLIENT, snapshot_version, {"id1", "end_flat"}, true); - for(const auto& entry : permissions) { - futures.push_back({ - "client permission insert clientId=" + to_string(currentId) + " permId=" + entry->type->name, - sql_insert_permission.command().values( - variable{":id", currentId}, - variable{":type", permission::SQL_PERM_USER}, - variable{":chId", 0}, + auto permissions = SnapshotPermissionEntry::parse(arguments, index, permission::teamspeak::CLIENT, snapshot_version, {"id1", "end_flat"}, true); + for(const auto& entry : permissions) { + futures.push_back({ + "client permission insert clientId=" + to_string(currentId) + " permId=" + entry->type->name, + sql_insert_permission.command().values( + variable{":id", currentId}, + variable{":type", permission::SQL_PERM_USER}, + variable{":chId", 0}, - variable{":permId", entry->type->name}, - variable{":value", entry->value}, - variable{":grant", entry->grant}, - variable{":flag_skip", entry->skipped}, - variable{":flag_negate", entry->negated} - ).executeLater() - }); - } - } - } else if (arguments[index].has("channel_flat")) { - ChannelId currentChannelId = 0; - while (true) { - if (arguments[index].has("end_flat")) break; + variable{":permId", entry->type->name}, + variable{":value", entry->value}, + variable{":grant", entry->grant}, + variable{":flag_skip", entry->skipped}, + variable{":flag_negate", entry->negated} + ).executeLater() + }); + } + } + } else if (arguments[index].has("channel_flat")) { + ChannelId currentChannelId = 0; + while (true) { + if (arguments[index].has("end_flat")) break; - if (arguments[index].has("id1")) - currentChannelId = arguments[index]["id1"]; //We also have id2 unknown for what this is. Maybe parent? + if (arguments[index].has("id1")) + currentChannelId = arguments[index]["id1"]; //We also have id2 unknown for what this is. Maybe parent? - auto permissions = SnapshotPermissionEntry::parse(arguments, index, permission::teamspeak::CHANNEL, snapshot_version, {"id1", "end_flat"}, true); - for(const auto& entry : permissions) { - futures.push_back({ - "channel permission insert channelId=" + to_string(currentChannelId) + " permId=" + entry->type->name, - sql_insert_permission.command().values( - variable{":id", 0}, - variable{":type", permission::SQL_PERM_CHANNEL}, - variable{":chId", currentChannelId}, + auto permissions = SnapshotPermissionEntry::parse(arguments, index, permission::teamspeak::CHANNEL, snapshot_version, {"id1", "end_flat"}, true); + for(const auto& entry : permissions) { + futures.push_back({ + "channel permission insert channelId=" + to_string(currentChannelId) + " permId=" + entry->type->name, + sql_insert_permission.command().values( + variable{":id", 0}, + variable{":type", permission::SQL_PERM_CHANNEL}, + variable{":chId", currentChannelId}, - variable{":permId", entry->type->name}, - variable{":value", entry->value}, - variable{":grant", entry->grant}, - variable{":flag_skip", entry->skipped}, - variable{":flag_negate", entry->negated} - ).executeLater() - }); - } - } - } else if (arguments[index].has("channel_client_flat")) { - ClientDbId currentClientId = 0; - ChannelId currentChannelId = 0; - while (true) { - if (arguments[index].has("end_flat")) break; - //id1 = channel id - //id2 = client id - if(arguments[index].has("id1")) - currentChannelId = arguments[index]["id1"]; - if(arguments[index].has("id2")) - currentClientId = db_mapping_client_id[arguments[index]["id2"]]; - if(currentChannelId > 0 && currentClientId > 0){ - auto permissions = SnapshotPermissionEntry::parse(arguments, index, permission::teamspeak::CLIENT, snapshot_version, {"id1", "id2", "end_flat"}, true); - for(const auto& entry : permissions) { - futures.push_back({ - "client channel permission insert", - sql_insert_permission.command().values( - variable{":id", currentClientId}, - variable{":type", permission::SQL_PERM_CHANNEL}, - variable{":chId", currentChannelId}, + variable{":permId", entry->type->name}, + variable{":value", entry->value}, + variable{":grant", entry->grant}, + variable{":flag_skip", entry->skipped}, + variable{":flag_negate", entry->negated} + ).executeLater() + }); + } + } + } else if (arguments[index].has("channel_client_flat")) { + ClientDbId currentClientId = 0; + ChannelId currentChannelId = 0; + while (true) { + if (arguments[index].has("end_flat")) break; + //id1 = channel id + //id2 = client id + if(arguments[index].has("id1")) + currentChannelId = arguments[index]["id1"]; + if(arguments[index].has("id2")) + currentClientId = db_mapping_client_id[arguments[index]["id2"]]; + if(currentChannelId > 0 && currentClientId > 0){ + auto permissions = SnapshotPermissionEntry::parse(arguments, index, permission::teamspeak::CLIENT, snapshot_version, {"id1", "id2", "end_flat"}, true); + for(const auto& entry : permissions) { + futures.push_back({ + "client channel permission insert", + sql_insert_permission.command().values( + variable{":id", currentClientId}, + variable{":type", permission::SQL_PERM_CHANNEL}, + variable{":chId", currentChannelId}, - variable{":permId", entry->type->name}, - variable{":value", entry->value}, - variable{":grant", entry->grant}, - variable{":flag_skip", entry->skipped}, - variable{":flag_negate", entry->negated} - ).executeLater() - }); - } - } else index++; - } - } else { - error = "Invalid tag in permissions. Available: " + avArguments(arguments, index); - return nullptr; - } - } - } else if(arguments[index].has("begin_music")) { - PlaylistId playlist_index = 0; - PlaylistId playlist_error = 0xFFFFFFFF; - map db_mapping_playlist; + variable{":permId", entry->type->name}, + variable{":value", entry->value}, + variable{":grant", entry->grant}, + variable{":flag_skip", entry->skipped}, + variable{":flag_negate", entry->negated} + ).executeLater() + }); + } + } else index++; + } + } else { + error = "Invalid tag in permissions. Available: " + avArguments(arguments, index); + return nullptr; + } + } + } else if(arguments[index].has("begin_music")) { + PlaylistId playlist_index = 0; + PlaylistId playlist_error = 0xFFFFFFFF; + map db_mapping_playlist; - /* - //gather static info (not needed because its a new server) - { - auto sql_result = sql::command(this->handle->getSql(), "SELECT `playlist_id` FROM `playlists` WHERE `serverId` = :server_id ORDER BY `playlist_id` DESC", variable{":server_id", serverId}).query([&](int length, string* values, string* names){ - if(length != 1) return; + /* + //gather static info (not needed because its a new server) + { + auto sql_result = sql::command(this->handle->getSql(), "SELECT `playlist_id` FROM `playlists` WHERE `serverId` = :server_id ORDER BY `playlist_id` DESC", variable{":server_id", serverId}).query([&](int length, string* values, string* names){ + if(length != 1) return; - try { - playlist_index = (PlaylistId) stoll(values[0]); - } catch(const std::exception& ex) { - error = "Failed to parse playlist id from database. ID: " + values[0]; - return; - } - }); - LOG_SQL_CMD(sql_result); + try { + playlist_index = (PlaylistId) stoll(values[0]); + } catch(const std::exception& ex) { + error = "Failed to parse playlist id from database. ID: " + values[0]; + return; + } + }); + LOG_SQL_CMD(sql_result); - if(playlist_index == 0) { - error = "failed to gather info"; - return nullptr; - } - } - */ + if(playlist_index == 0) { + error = "failed to gather info"; + return nullptr; + } + } + */ - while(!arguments[index].has("end_music")) { - if(arguments[index].has("begin_bots")) { - while(!arguments[index].has("end_bots")) { - ClientDbId bot_id = arguments[index]["bot_id"]; - if(db_mapping_client_id.find(bot_id) == db_mapping_client_id.end()) { - logError(log_server_id, PREFIX + "Failed to insert music bot within id {}. (Mapping not found)", bot_id); - index++; - continue; - } - ClientDbId new_bot_id = db_mapping_client_id[bot_id]; + while(!arguments[index].has("end_music")) { + if(arguments[index].has("begin_bots")) { + while(!arguments[index].has("end_bots")) { + ClientDbId bot_id = arguments[index]["bot_id"]; + if(db_mapping_client_id.find(bot_id) == db_mapping_client_id.end()) { + logError(log_server_id, PREFIX + "Failed to insert music bot within id {}. (Mapping not found)", bot_id); + index++; + continue; + } + ClientDbId new_bot_id = db_mapping_client_id[bot_id]; - //begin_bots - auto sql_result = sql_insert_bot.command().values( - variable{":bot_id", new_bot_id}, - variable{":unique_id", arguments[index]["bot_unique_id"].string()}, - variable{":owner", arguments[index]["bot_owner_id"].string()} - ).execute(); - if(!sql_result) { - logError(log_server_id, PREFIX + "Failed to insert music bot with id {} (old: {}). ({})", new_bot_id, bot_id, sql_result.fmtStr()); - index++; - continue; - } + //begin_bots + auto sql_result = sql_insert_bot.command().values( + variable{":bot_id", new_bot_id}, + variable{":unique_id", arguments[index]["bot_unique_id"].string()}, + variable{":owner", arguments[index]["bot_owner_id"].string()} + ).execute(); + if(!sql_result) { + logError(log_server_id, PREFIX + "Failed to insert music bot with id {} (old: {}). ({})", new_bot_id, bot_id, sql_result.fmtStr()); + index++; + continue; + } - for(const auto& key : arguments[index].keys()) { - if(key == "begin_music") continue; - if(key == "begin_bots") continue; - if(key == "bot_unique_id") continue; - if(key == "bot_owner_id") continue; - if(key == "bot_id") continue; + for(const auto& key : arguments[index].keys()) { + if(key == "begin_music") continue; + if(key == "begin_bots") continue; + if(key == "bot_unique_id") continue; + if(key == "bot_owner_id") continue; + if(key == "bot_id") continue; - const auto& property = property::info(key); - if(property->property_index == property::CLIENT_UNDEFINED) { - debugMessage(log_server_id, PREFIX + "Failed to parse give music bot property {} for bot {} (old: {}). Value: {}", key, new_bot_id, bot_id, arguments[index][key].string()); - continue; - } + const auto& property = property::info(key); + if(property->property_index == property::CLIENT_UNDEFINED) { + debugMessage(log_server_id, PREFIX + "Failed to parse give music bot property {} for bot {} (old: {}). Value: {}", key, new_bot_id, bot_id, arguments[index][key].string()); + continue; + } - futures.push_back({ - "music bot insert", - sql_insert_property.command().values( - variable{":type", property::PropertyType::PROP_TYPE_CLIENT}, - variable{":id", new_bot_id}, - variable{":key", key}, - variable{":value", arguments[index][key].string()} - ).executeLater() - }); - } + futures.push_back({ + "music bot insert", + sql_insert_property.command().values( + variable{":type", property::PropertyType::PROP_TYPE_CLIENT}, + variable{":id", new_bot_id}, + variable{":key", key}, + variable{":value", arguments[index][key].string()} + ).executeLater() + }); + } - index++; - } - } else if(arguments[index].has("begin_playlist")) { - while(!arguments[index].has("end_playlist")) { - PlaylistId playlist_id = arguments[index]["playlist_id"]; - db_mapping_playlist[++playlist_index] = playlist_id; + index++; + } + } else if(arguments[index].has("begin_playlist")) { + while(!arguments[index].has("end_playlist")) { + PlaylistId playlist_id = arguments[index]["playlist_id"]; + db_mapping_playlist[++playlist_index] = playlist_id; - auto sql_result = sql_insert_playlist.command().values(variable{":playlist_id", playlist_index}).execute(); - if(!sql_result) { - db_mapping_playlist[playlist_index] = playlist_error; - logError(log_server_id, PREFIX + "Failed to insert playlist with id {} (old: {}). ({})", playlist_index, playlist_id, sql_result.fmtStr()); - index++; - continue; - } + auto sql_result = sql_insert_playlist.command().values(variable{":playlist_id", playlist_index}).execute(); + if(!sql_result) { + db_mapping_playlist[playlist_index] = playlist_error; + logError(log_server_id, PREFIX + "Failed to insert playlist with id {} (old: {}). ({})", playlist_index, playlist_id, sql_result.fmtStr()); + index++; + continue; + } - for(const auto& key : arguments[index].keys()) { - if(key == "begin_music") continue; - if(key == "begin_playlist") continue; - if(key == "playlist_id") continue; + for(const auto& key : arguments[index].keys()) { + if(key == "begin_music") continue; + if(key == "begin_playlist") continue; + if(key == "playlist_id") continue; - const auto& property = property::info(key); - if(property->property_index == property::CLIENT_UNDEFINED) { - debugMessage(log_server_id, PREFIX + "Failed to parse given playlist property {} for playlist {} (old: {}). Value: {}", key, playlist_index, playlist_id, arguments[index][key].string()); - continue; - } + const auto& property = property::info(key); + if(property->property_index == property::CLIENT_UNDEFINED) { + debugMessage(log_server_id, PREFIX + "Failed to parse given playlist property {} for playlist {} (old: {}). Value: {}", key, playlist_index, playlist_id, arguments[index][key].string()); + continue; + } - futures.push_back({ - "playlist insert", - sql_insert_property.command().values( - variable{":type", property::PropertyType::PROP_TYPE_PLAYLIST}, - variable{":id", playlist_index}, - variable{":key", key}, - variable{":value", arguments[index][key].string()} - ).executeLater() - }); - } + futures.push_back({ + "playlist insert", + sql_insert_property.command().values( + variable{":type", property::PropertyType::PROP_TYPE_PLAYLIST}, + variable{":id", playlist_index}, + variable{":key", key}, + variable{":value", arguments[index][key].string()} + ).executeLater() + }); + } - index++; - } - } else if(arguments[index].has("begin_playlist_songs")) { - PlaylistId current_playlist = 0; - PlaylistId current_playlist_mapped = 0; - while(!arguments[index].has("end_playlist_songs")) { - if(arguments[index].has("song_playlist_id")) { - current_playlist = arguments[index]["song_playlist_id"]; - current_playlist_mapped = (PlaylistId) db_mapping_client_id[current_playlist]; - if(current_playlist_mapped == 0) { - logError(log_server_id, PREFIX + "Failed to insert playlist song entry {}. Playlist id {} hasn't been mapped", arguments[index]["song_id"].value(), current_playlist); - current_playlist_mapped = playlist_error; - } - } - if(current_playlist_mapped == playlist_error) { /* current list is an error */ - index++; - continue; - } + index++; + } + } else if(arguments[index].has("begin_playlist_songs")) { + PlaylistId current_playlist = 0; + PlaylistId current_playlist_mapped = 0; + while(!arguments[index].has("end_playlist_songs")) { + if(arguments[index].has("song_playlist_id")) { + current_playlist = arguments[index]["song_playlist_id"]; + current_playlist_mapped = (PlaylistId) db_mapping_client_id[current_playlist]; + if(current_playlist_mapped == 0) { + logError(log_server_id, PREFIX + "Failed to insert playlist song entry {}. Playlist id {} hasn't been mapped", arguments[index]["song_id"].value(), current_playlist); + current_playlist_mapped = playlist_error; + } + } + if(current_playlist_mapped == playlist_error) { /* current list is an error */ + index++; + continue; + } - auto sql_future = sql_insert_playlist_song.command().values( - variable{":playlist_id", current_playlist_mapped}, - variable{":song_id", arguments[index]["song_id"].value()}, - variable{":order_id", arguments[index]["song_order"].value()}, - variable{":invoker_dbid", arguments[index]["song_invoker"].value()}, - variable{":url", arguments[index]["song_url"].value()}, - variable{":url_loader", arguments[index]["song_url_loader"].value()}, - variable{":loaded", arguments[index]["song_loaded"].value()}, - variable{":metadata", arguments[index]["song_metadata"].value()} - ).executeLater(); - futures.push_back({ - "song insert", - sql_future - }); + auto sql_future = sql_insert_playlist_song.command().values( + variable{":playlist_id", current_playlist_mapped}, + variable{":song_id", arguments[index]["song_id"].value()}, + variable{":order_id", arguments[index]["song_order"].value()}, + variable{":invoker_dbid", arguments[index]["song_invoker"].value()}, + variable{":url", arguments[index]["song_url"].value()}, + variable{":url_loader", arguments[index]["song_url_loader"].value()}, + variable{":loaded", arguments[index]["song_loaded"].value()}, + variable{":metadata", arguments[index]["song_metadata"].value()} + ).executeLater(); + futures.push_back({ + "song insert", + sql_future + }); - index++; - } - } - index++; - } - } else { - error = "Invalid root tag. Available: " + avArguments(arguments, index); - return nullptr; - } - } + index++; + } + } + index++; + } + } else { + error = "Invalid root tag. Available: " + avArguments(arguments, index); + return nullptr; + } + } - debugMessage(serverId, "Wait for success!"); + debugMessage(serverId, "Wait for success!"); - for(const auto& future : futures) { - auto result = future.second.waitAndGet({-1, "timeout"}, chrono::system_clock::now() + chrono::seconds(5)); - if(!result) { - logError(serverId, "Failed to execute snapshotdeploy command {}: {}", future.first, result.fmtStr()); - } - } + for(const auto& future : futures) { + auto result = future.second.waitAndGet({-1, "timeout"}, chrono::system_clock::now() + chrono::seconds(5)); + if(!result) { + logError(serverId, "Failed to execute snapshotdeploy command {}: {}", future.first, result.fmtStr()); + } + } - if(old) - this->deleteServer(old); - //Now we load the server - auto server = make_shared(serverId, this->handle->getSql()); - server->self = server; - if(!server->initialize(false)) { - //FIXME Error handling! - } - server->properties()[property::VIRTUALSERVER_HOST] = host; - server->properties()[property::VIRTUALSERVER_PORT] = port; - server->properties()[property::VIRTUALSERVER_ASK_FOR_PRIVILEGEKEY] = false; + if(old) + this->deleteServer(old); + //Now we load the server + auto server = make_shared(serverId, this->handle->getSql()); + server->self = server; + if(!server->initialize(false)) { + //FIXME Error handling! + } + server->properties()[property::VIRTUALSERVER_HOST] = host; + server->properties()[property::VIRTUALSERVER_PORT] = port; + server->properties()[property::VIRTUALSERVER_ASK_FOR_PRIVILEGEKEY] = false; - { - threads::MutexLock l(this->instanceLock); - this->instances.push_back(server); - } - this->adjust_executor_threads(); + { + threads::MutexLock l(this->instanceLock); + this->instances.push_back(server); + } + this->adjust_executor_threads(); - server->properties()[property::VIRTUALSERVER_DEFAULT_SERVER_GROUP] = serverGroupMapping[server->properties()[property::VIRTUALSERVER_DEFAULT_SERVER_GROUP].as()]; - server->properties()[property::VIRTUALSERVER_DEFAULT_CHANNEL_GROUP] = channelGroupMapping[server->properties()[property::VIRTUALSERVER_DEFAULT_CHANNEL_GROUP].as()]; - server->properties()[property::VIRTUALSERVER_DEFAULT_CHANNEL_ADMIN_GROUP] = channelGroupMapping[server->properties()[property::VIRTUALSERVER_DEFAULT_CHANNEL_ADMIN_GROUP].as()]; - server->ensureValidDefaultGroups(); - return server; + server->properties()[property::VIRTUALSERVER_DEFAULT_SERVER_GROUP] = serverGroupMapping[server->properties()[property::VIRTUALSERVER_DEFAULT_SERVER_GROUP].as()]; + server->properties()[property::VIRTUALSERVER_DEFAULT_CHANNEL_GROUP] = channelGroupMapping[server->properties()[property::VIRTUALSERVER_DEFAULT_CHANNEL_GROUP].as()]; + server->properties()[property::VIRTUALSERVER_DEFAULT_CHANNEL_ADMIN_GROUP] = channelGroupMapping[server->properties()[property::VIRTUALSERVER_DEFAULT_CHANNEL_ADMIN_GROUP].as()]; + server->ensureValidDefaultGroups(); + return server; } struct CommandTuple { - Command& cmd; - int& index; - int version; + Command& cmd; + int& index; + int version; }; struct PermissionCommandTuple { - Command& cmd; - int& index; - int version; - ClientDbId client; - ChannelId channel; + Command& cmd; + int& index; + int version; + ClientDbId client; + ChannelId channel; }; inline bool writePermissions(const shared_ptr& manager, Command& cmd, int& index, int version, permission::teamspeak::GroupType type, std::string& error) { - for(const auto& permission_container : manager->permissions()) { - auto permission = get<1>(permission_container); - SnapshotPermissionEntry{ - permission::resolvePermissionData(get<0>(permission_container)), - permission.flags.value_set ? permission.values.value : permNotGranted, - permission.flags.grant_set ? permission.values.grant : permNotGranted, - permission.flags.negate, - permission.flags.skip - }.write(cmd, index, type, version); - } - return true; + for(const auto& permission_container : manager->permissions()) { + auto permission = get<1>(permission_container); + SnapshotPermissionEntry{ + permission::resolvePermissionData(get<0>(permission_container)), + permission.flags.value_set ? permission.values.value : permNotGranted, + permission.flags.grant_set ? permission.values.grant : permNotGranted, + permission.flags.negate, + permission.flags.skip + }.write(cmd, index, type, version); + } + return true; } inline void writeRelations(const shared_ptr& server, GroupTarget type, Command& cmd, int& index, int version) { - PermissionCommandTuple parm{cmd, index, version, 0, 0}; - auto res = sql::command(server->getSql(), "SELECT `cldbid`, `groups`.`groupId`, `channelId`, `until` FROM `assignedGroups` INNER JOIN `groups` ON `groups`.`serverId` = `assignedGroups`.`serverId` AND `groups`.`groupId` = `assignedGroups`.`groupId` WHERE `groups`.`serverId` = :sid AND `groups`.target = :type", - variable{":sid", server->getServerId()}, - variable{":type", type} - ).query([](PermissionCommandTuple* commandIndex, int length, char** value, char** name) { - ClientDbId cldbid = 0; - ChannelId channelId = 0; - GroupId gid = 0; - int64_t until = 0; + PermissionCommandTuple parm{cmd, index, version, 0, 0}; + auto res = sql::command(server->getSql(), "SELECT `cldbid`, `groups`.`groupId`, `channelId`, `until` FROM `assignedGroups` INNER JOIN `groups` ON `groups`.`serverId` = `assignedGroups`.`serverId` AND `groups`.`groupId` = `assignedGroups`.`groupId` WHERE `groups`.`serverId` = :sid AND `groups`.target = :type", + variable{":sid", server->getServerId()}, + variable{":type", type} + ).query([](PermissionCommandTuple* commandIndex, int length, char** value, char** name) { + ClientDbId cldbid = 0; + ChannelId channelId = 0; + GroupId gid = 0; + int64_t until = 0; - for(int idx = 0; idx < length; idx++) { - try { - if(strcmp(name[idx], "cldbid") == 0) - cldbid = stoul(value[idx]); - else if(strcmp(name[idx], "groupId") == 0) - gid = stoul(value[idx]); - else if(strcmp(name[idx], "channelId") == 0) - channelId = stoul(value[idx]); - else if(strcmp(name[idx], "until") == 0) - until = stoll(value[idx]); - } catch (std::exception& ex) { - logError(0, "Failed to write snapshot group relation (Skipping it)! Message: {} @ {} => {}", ex.what(), name[idx], value[idx]); - return 0; - } - } + for(int idx = 0; idx < length; idx++) { + try { + if(strcmp(name[idx], "cldbid") == 0) + cldbid = stoul(value[idx]); + else if(strcmp(name[idx], "groupId") == 0) + gid = stoul(value[idx]); + else if(strcmp(name[idx], "channelId") == 0) + channelId = stoul(value[idx]); + else if(strcmp(name[idx], "until") == 0) + until = stoll(value[idx]); + } catch (std::exception& ex) { + logError(0, "Failed to write snapshot group relation (Skipping it)! Message: {} @ {} => {}", ex.what(), name[idx], value[idx]); + return 0; + } + } - if(commandIndex->channel != channelId) { - commandIndex->channel = channelId; - commandIndex->cmd[commandIndex->index]["iid"] = channelId; - } - commandIndex->cmd[commandIndex->index]["gid"] = gid; - commandIndex->cmd[commandIndex->index]["until"] = until; - commandIndex->cmd[commandIndex->index++]["cldbid"] = cldbid; + if(commandIndex->channel != channelId) { + commandIndex->channel = channelId; + commandIndex->cmd[commandIndex->index]["iid"] = channelId; + } + commandIndex->cmd[commandIndex->index]["gid"] = gid; + commandIndex->cmd[commandIndex->index]["until"] = until; + commandIndex->cmd[commandIndex->index++]["cldbid"] = cldbid; - return 0; - }, &parm); - LOG_SQL_CMD(res); + return 0; + }, &parm); + LOG_SQL_CMD(res); } struct DatabaseMusicbot { - ClientDbId bot_id; - ClientDbId bot_owner_id; - std::string bot_unique_id; + ClientDbId bot_id; + ClientDbId bot_owner_id; + std::string bot_unique_id; }; bool ServerManager::createServerSnapshot(Command &cmd, shared_ptr server, int version, std::string &error) { - int index = 0; + int index = 0; - if(version == -1) version = 2; //Auto versioned - if(version < 0 || version > 2) { - error = "Invalid snapshot version!"; - return false; - } - if(version > 0) cmd[index++]["snapshot_version"] = version; - //Server - { - cmd[index]["begin_virtualserver"] = ""; - for(const auto& serverProperty : server->properties().list_properties(property::FLAG_SNAPSHOT)) { - if(version == 0) { - switch (serverProperty.type().property_index) { - case property::VIRTUALSERVER_DOWNLOAD_QUOTA: - case property::VIRTUALSERVER_UPLOAD_QUOTA: - case property::VIRTUALSERVER_MAX_DOWNLOAD_TOTAL_BANDWIDTH: - case property::VIRTUALSERVER_MAX_UPLOAD_TOTAL_BANDWIDTH: - cmd[index][serverProperty.type().name] = (uint64_t) serverProperty.as_save(); - default: - break; - } - } - cmd[index][serverProperty.type().name] = serverProperty.value(); - } - cmd[index++]["end_virtualserver"] = ""; - } + if(version == -1) version = 2; //Auto versioned + if(version < 0 || version > 2) { + error = "Invalid snapshot version!"; + return false; + } + if(version > 0) cmd[index++]["snapshot_version"] = version; + //Server + { + cmd[index]["begin_virtualserver"] = ""; + for(const auto& serverProperty : server->properties().list_properties(property::FLAG_SNAPSHOT)) { + if(version == 0) { + switch (serverProperty.type().property_index) { + case property::VIRTUALSERVER_DOWNLOAD_QUOTA: + case property::VIRTUALSERVER_UPLOAD_QUOTA: + case property::VIRTUALSERVER_MAX_DOWNLOAD_TOTAL_BANDWIDTH: + case property::VIRTUALSERVER_MAX_UPLOAD_TOTAL_BANDWIDTH: + cmd[index][serverProperty.type().name] = (uint64_t) serverProperty.as_save(); + default: + break; + } + } + cmd[index][serverProperty.type().name] = serverProperty.value(); + } + cmd[index++]["end_virtualserver"] = ""; + } - //Channels - { - cmd[index]["begin_channels"] = ""; - for(const auto& channel : server->getChannelTree()->channels()) { - for(const auto& channelProperty : channel->properties().list_properties(property::FLAG_SNAPSHOT)) { - if(channelProperty.type() == property::CHANNEL_ID) - cmd[index]["channel_id"] = channelProperty.as(); - else if(channelProperty.type() == property::CHANNEL_PID) - cmd[index]["channel_pid"] = channelProperty.as(); - else - cmd[index][channelProperty.type().name] = channelProperty.as(); - } - index++; - } - cmd[index++]["end_channels"] = ""; - } + //Channels + { + cmd[index]["begin_channels"] = ""; + for(const auto& channel : server->getChannelTree()->channels()) { + for(const auto& channelProperty : channel->properties().list_properties(property::FLAG_SNAPSHOT)) { + if(channelProperty.type() == property::CHANNEL_ID) + cmd[index]["channel_id"] = channelProperty.as(); + else if(channelProperty.type() == property::CHANNEL_PID) + cmd[index]["channel_pid"] = channelProperty.as(); + else + cmd[index][channelProperty.type().name] = channelProperty.as(); + } + index++; + } + cmd[index++]["end_channels"] = ""; + } - //Clients - { - cmd[index]["begin_clients"] = ""; - CommandTuple parm{cmd, index, version}; - auto res = sql::command(server->getSql(), "SELECT `cldbid`, `clientUid`, `firstConnect`, `lastName` FROM `clients` WHERE `serverId` = :sid", - variable{":sid", server->getServerId()} - ).query([&](CommandTuple* commandIndex, int length, char** value, char** name) { - ClientDbId cldbid = 0; - int64_t clientCreated = 0; - string clientUid; - string lastName; - string description; //TODO description + //Clients + { + cmd[index]["begin_clients"] = ""; + CommandTuple parm{cmd, index, version}; + auto res = sql::command(server->getSql(), "SELECT `cldbid`, `clientUid`, `firstConnect`, `lastName` FROM `clients` WHERE `serverId` = :sid", + variable{":sid", server->getServerId()} + ).query([&](CommandTuple* commandIndex, int length, char** value, char** name) { + ClientDbId cldbid = 0; + int64_t clientCreated = 0; + string clientUid; + string lastName; + string description; //TODO description - for(int idx = 0; idx < length; idx++) { - try { - if(strcmp(name[idx], "cldbid") == 0) - cldbid = value[idx] && strlen(value[idx]) > 0 ? stoul(value[idx]) : 0; - else if(strcmp(name[idx], "clientUid") == 0) - clientUid = value[idx]; - else if(strcmp(name[idx], "firstConnect") == 0) - clientCreated = value[idx] && strlen(value[idx]) > 0 ? stoll(value[idx]) : 0L; - else if(strcmp(name[idx], "lastName") == 0) - lastName = value[idx]; - } catch (std::exception& ex) { - logError(0, "Failed to write snapshot client (Skipping it)! Message: {} @ {} => {}", ex.what(), name[idx], value[idx]); - return 0; - } - } + for(int idx = 0; idx < length; idx++) { + try { + if(strcmp(name[idx], "cldbid") == 0) + cldbid = value[idx] && strlen(value[idx]) > 0 ? stoul(value[idx]) : 0; + else if(strcmp(name[idx], "clientUid") == 0) + clientUid = value[idx]; + else if(strcmp(name[idx], "firstConnect") == 0) + clientCreated = value[idx] && strlen(value[idx]) > 0 ? stoll(value[idx]) : 0L; + else if(strcmp(name[idx], "lastName") == 0) + lastName = value[idx]; + } catch (std::exception& ex) { + logError(0, "Failed to write snapshot client (Skipping it)! Message: {} @ {} => {}", ex.what(), name[idx], value[idx]); + return 0; + } + } - commandIndex->cmd[commandIndex->index]["client_id"] = cldbid; - commandIndex->cmd[commandIndex->index]["client_unique_id"] = clientUid; - commandIndex->cmd[commandIndex->index]["client_nickname"] = lastName; - commandIndex->cmd[commandIndex->index]["client_created"] = clientCreated; - commandIndex->cmd[commandIndex->index]["client_description"] = description; - if(commandIndex->version == 0) - commandIndex->cmd[commandIndex->index]["client_unread_messages"] = 0; - commandIndex->index++; - return 0; - }, &parm); - LOG_SQL_CMD(res); - cmd[index++]["end_clients"] = ""; - } + commandIndex->cmd[commandIndex->index]["client_id"] = cldbid; + commandIndex->cmd[commandIndex->index]["client_unique_id"] = clientUid; + commandIndex->cmd[commandIndex->index]["client_nickname"] = lastName; + commandIndex->cmd[commandIndex->index]["client_created"] = clientCreated; + commandIndex->cmd[commandIndex->index]["client_description"] = description; + if(commandIndex->version == 0) + commandIndex->cmd[commandIndex->index]["client_unread_messages"] = 0; + commandIndex->index++; + return 0; + }, &parm); + LOG_SQL_CMD(res); + cmd[index++]["end_clients"] = ""; + } - //music and music bots - if(version >= 2) { - cmd[index]["begin_music"] = ""; + //music and music bots + if(version >= 2) { + cmd[index]["begin_music"] = ""; - /* music bots */ - { - cmd[index]["begin_bots"] = ""; + /* music bots */ + { + cmd[index]["begin_bots"] = ""; - deque music_bots; + deque music_bots; - auto sql_result = sql::command(server->getSql(), "SELECT `botId`, `uniqueId`, `owner` FROM `musicbots` WHERE `serverId` = :sid", variable{":sid", server->getServerId()}).query([&](int length, string* values, string* names) { - DatabaseMusicbot data; - for(int column = 0; column < length; column++) { - try { - if(names[column] == "botId") - data.bot_id = stoll(values[column]); - else if(names[column] == "uniqueId") - data.bot_unique_id = values[column]; - else if(names[column] == "owner") - data.bot_owner_id = stoll(values[column]); - } catch(std::exception& ex) { - return; - } - } + auto sql_result = sql::command(server->getSql(), "SELECT `botId`, `uniqueId`, `owner` FROM `musicbots` WHERE `serverId` = :sid", variable{":sid", server->getServerId()}).query([&](int length, string* values, string* names) { + DatabaseMusicbot data; + for(int column = 0; column < length; column++) { + try { + if(names[column] == "botId") + data.bot_id = stoll(values[column]); + else if(names[column] == "uniqueId") + data.bot_unique_id = values[column]; + else if(names[column] == "owner") + data.bot_owner_id = stoll(values[column]); + } catch(std::exception& ex) { + return; + } + } - music_bots.emplace_back(data); - }); - if(!sql_result) - logError(server->getServerId(), PREFIX + "Failed to write music bots to snapshot. {}", sql_result.fmtStr()); + music_bots.emplace_back(data); + }); + if(!sql_result) + logError(server->getServerId(), PREFIX + "Failed to write music bots to snapshot. {}", sql_result.fmtStr()); - for(const auto& music_bot : music_bots) { - auto properties = serverInstance->databaseHelper()->query_properties(server->getServerId(), property::PROP_TYPE_CLIENT, music_bot.bot_id); + for(const auto& music_bot : music_bots) { + auto properties = serverInstance->databaseHelper()->query_properties(server->getServerId(), property::PROP_TYPE_CLIENT, music_bot.bot_id); - cmd[index]["bot_unique_id"] = music_bot.bot_unique_id; - cmd[index]["bot_owner_id"] = music_bot.bot_owner_id; - cmd[index]["bot_id"] = music_bot.bot_id; + cmd[index]["bot_unique_id"] = music_bot.bot_unique_id; + cmd[index]["bot_owner_id"] = music_bot.bot_owner_id; + cmd[index]["bot_id"] = music_bot.bot_id; - for(const auto& property : properties) { - if((property->type->flags & (property::FLAG_SAVE_MUSIC | property::FLAG_SAVE)) == 0) continue; - if(property->value == property->type->default_value) continue; + for(const auto& property : properties) { + if((property->type->flags & (property::FLAG_SAVE_MUSIC | property::FLAG_SAVE)) == 0) continue; + if(property->value == property->type->default_value) continue; - cmd[index][property->type->name] = property->value; - } + cmd[index][property->type->name] = property->value; + } - index++; - } - cmd[index++]["end_bots"] = ""; - } + index++; + } + cmd[index++]["end_bots"] = ""; + } - /* playlists */ - { - cmd[index]["begin_playlist"] = ""; - deque playlist_ids; + /* playlists */ + { + cmd[index]["begin_playlist"] = ""; + deque playlist_ids; - auto sql_result = sql::command(server->getSql(), "SELECT `playlist_id` FROM `playlists` WHERE `serverId` = :sid", variable{":sid", server->getServerId()}).query([&](int length, string* values, string* names) { - try { - playlist_ids.push_back(stoll(values[0])); - } catch(std::exception& ex) { - return; - } - }); - if(!sql_result) - logError(server->getServerId(), PREFIX + "Failed to write playlists to snapshot. {}", sql_result.fmtStr()); + auto sql_result = sql::command(server->getSql(), "SELECT `playlist_id` FROM `playlists` WHERE `serverId` = :sid", variable{":sid", server->getServerId()}).query([&](int length, string* values, string* names) { + try { + playlist_ids.push_back(stoll(values[0])); + } catch(std::exception& ex) { + return; + } + }); + if(!sql_result) + logError(server->getServerId(), PREFIX + "Failed to write playlists to snapshot. {}", sql_result.fmtStr()); - for(const auto& playlist : playlist_ids) { - auto properties = serverInstance->databaseHelper()->query_properties(server->getServerId(), property::PROP_TYPE_PLAYLIST, playlist); + for(const auto& playlist : playlist_ids) { + auto properties = serverInstance->databaseHelper()->query_properties(server->getServerId(), property::PROP_TYPE_PLAYLIST, playlist); - cmd[index]["playlist_id"] = playlist; + cmd[index]["playlist_id"] = playlist; - for(const auto& property : properties) { - if((property->type->flags & (property::FLAG_SAVE_MUSIC | property::FLAG_SAVE)) == 0) continue; - if(property->value == property->type->default_value) continue; + for(const auto& property : properties) { + if((property->type->flags & (property::FLAG_SAVE_MUSIC | property::FLAG_SAVE)) == 0) continue; + if(property->value == property->type->default_value) continue; - cmd[index][property->type->name] = property->value; - } + cmd[index][property->type->name] = property->value; + } - index++; - } + index++; + } - cmd[index++]["end_playlist"] = ""; - } + cmd[index++]["end_playlist"] = ""; + } - /* playlist info */ - { - cmd[index]["begin_playlist_songs"] = ""; - //playlist_songs => `serverId` INT NOT NULL, `playlist_id` INT, `song_id` INT, `order_id` INT, `invoker_dbid` INT, `url` TEXT, `url_loader` TEXT, `loaded` BOOL, `metadata` TEXT - PlaylistId current_playlist = 0; + /* playlist info */ + { + cmd[index]["begin_playlist_songs"] = ""; + //playlist_songs => `serverId` INT NOT NULL, `playlist_id` INT, `song_id` INT, `order_id` INT, `invoker_dbid` INT, `url` TEXT, `url_loader` TEXT, `loaded` BOOL, `metadata` TEXT + PlaylistId current_playlist = 0; - auto sql_result = sql::command(server->getSql(), - "SELECT `playlist_id`, `song_id`, `order_id`, `invoker_dbid`, `url`, `url_loader`, `loaded`, `metadata` FROM `playlist_songs` WHERE `serverId` = :sid ORDER BY `playlist_id`", variable{":sid", server->getServerId()} - ).query([&](int length, string* values, string* names) { - for(int column = 0; column < length; column++) { - if(names[column] == "song_id") - cmd[index]["song_id"] = values[column]; - else if(names[column] == "order_id") - cmd[index]["song_order"] = values[column]; - else if(names[column] == "invoker_dbid") - cmd[index]["song_invoker"] = values[column]; - else if(names[column] == "url") - cmd[index]["song_url"] = values[column]; - else if(names[column] == "url_loader") - cmd[index]["song_url_loader"] = values[column]; - else if(names[column] == "loaded") - cmd[index]["song_loaded"] = values[column]; - else if(names[column] == "metadata") - cmd[index]["song_metadata"] = values[column]; - else if(names[column] == "playlist_id") { - try { - auto playlist_id = stoll(values[column]); - if(current_playlist != playlist_id) { - cmd[index]["song_playlist_id"] = values[column]; /* song_playlist_id will be only set if the playlist id had changed */ - current_playlist = playlist_id; - } - } catch(std::exception& ex) { - logError(server->getServerId(), PREFIX + "Failed to parse playlist id. value: {}, message: {}", values[column], ex.what()); - } - } - } - index++; - }); - if(!sql_result) - logError(server->getServerId(), PREFIX + "Failed to write playlist songs to snapshot. {}", sql_result.fmtStr()); - cmd[index++]["end_playlist_songs"] = ""; - } + auto sql_result = sql::command(server->getSql(), + "SELECT `playlist_id`, `song_id`, `order_id`, `invoker_dbid`, `url`, `url_loader`, `loaded`, `metadata` FROM `playlist_songs` WHERE `serverId` = :sid ORDER BY `playlist_id`", variable{":sid", server->getServerId()} + ).query([&](int length, string* values, string* names) { + for(int column = 0; column < length; column++) { + if(names[column] == "song_id") + cmd[index]["song_id"] = values[column]; + else if(names[column] == "order_id") + cmd[index]["song_order"] = values[column]; + else if(names[column] == "invoker_dbid") + cmd[index]["song_invoker"] = values[column]; + else if(names[column] == "url") + cmd[index]["song_url"] = values[column]; + else if(names[column] == "url_loader") + cmd[index]["song_url_loader"] = values[column]; + else if(names[column] == "loaded") + cmd[index]["song_loaded"] = values[column]; + else if(names[column] == "metadata") + cmd[index]["song_metadata"] = values[column]; + else if(names[column] == "playlist_id") { + try { + auto playlist_id = stoll(values[column]); + if(current_playlist != playlist_id) { + cmd[index]["song_playlist_id"] = values[column]; /* song_playlist_id will be only set if the playlist id had changed */ + current_playlist = playlist_id; + } + } catch(std::exception& ex) { + logError(server->getServerId(), PREFIX + "Failed to parse playlist id. value: {}, message: {}", values[column], ex.what()); + } + } + } + index++; + }); + if(!sql_result) + logError(server->getServerId(), PREFIX + "Failed to write playlist songs to snapshot. {}", sql_result.fmtStr()); + cmd[index++]["end_playlist_songs"] = ""; + } - cmd[index++]["end_music"] = ""; - } + cmd[index++]["end_music"] = ""; + } - //Permissions - { - cmd[index++]["begin_permissions"] = ""; - //Server groups - { - //List groups - { - cmd[index]["server_groups"] = ""; - for(const auto& group : server->getGroupManager()->availableServerGroups(false)) { - cmd[index]["id"] = group->groupId(); - cmd[index]["name"] = group->name(); - if(!writePermissions(group->permissions(), cmd, index, version, permission::teamspeak::SERVER, error)) break; - cmd[index++]["end_group"] = ""; - } - cmd[index++]["end_groups"] = ""; - } + //Permissions + { + cmd[index++]["begin_permissions"] = ""; + //Server groups + { + //List groups + { + cmd[index]["server_groups"] = ""; + for(const auto& group : server->getGroupManager()->availableServerGroups(false)) { + cmd[index]["id"] = group->groupId(); + cmd[index]["name"] = group->name(); + if(!writePermissions(group->permissions(), cmd, index, version, permission::teamspeak::SERVER, error)) break; + cmd[index++]["end_group"] = ""; + } + cmd[index++]["end_groups"] = ""; + } - //List relations - { - cmd[index]["begin_relations"] = ""; - writeRelations(server, GROUPTARGET_SERVER, cmd, index, version); - cmd[index++]["end_relations"] = ""; - } - } + //List relations + { + cmd[index]["begin_relations"] = ""; + writeRelations(server, GROUPTARGET_SERVER, cmd, index, version); + cmd[index++]["end_relations"] = ""; + } + } - //Channel groups - { - //List groups - { - cmd[index]["channel_groups"] = ""; - for(const auto& group : server->getGroupManager()->availableChannelGroups(false)) { - cmd[index]["id"] = group->groupId(); - cmd[index]["name"] = group->name(); - if(!writePermissions(group->permissions(), cmd, index, version, permission::teamspeak::SERVER, error)) break; - cmd[index++]["end_group"] = ""; - } - cmd[index++]["end_groups"] = ""; - } + //Channel groups + { + //List groups + { + cmd[index]["channel_groups"] = ""; + for(const auto& group : server->getGroupManager()->availableChannelGroups(false)) { + cmd[index]["id"] = group->groupId(); + cmd[index]["name"] = group->name(); + if(!writePermissions(group->permissions(), cmd, index, version, permission::teamspeak::SERVER, error)) break; + cmd[index++]["end_group"] = ""; + } + cmd[index++]["end_groups"] = ""; + } - //List relations - { - cmd[index]["begin_relations"] = ""; - writeRelations(server, GROUPTARGET_CHANNEL, cmd, index, version); - cmd[index++]["end_relations"] = ""; - } - } + //List relations + { + cmd[index]["begin_relations"] = ""; + writeRelations(server, GROUPTARGET_CHANNEL, cmd, index, version); + cmd[index++]["end_relations"] = ""; + } + } - //Client rights - { - cmd[index]["client_flat"] = ""; - PermissionCommandTuple parm{cmd, index, version, 0, 0}; - auto res = sql::command(server->getSql(), "SELECT `id`, `permId`, `value`, `grant`, `flag_skip`, `flag_negate` FROM `permissions` WHERE `serverId` = :sid AND `type` = :type AND `channelId` = 0 ORDER BY `id`", - variable{":sid", server->getServerId()}, - variable{":type", permission::SQL_PERM_USER} - ).query([&](PermissionCommandTuple* commandIndex, int length, char** values, char**names){ - auto type = permission::resolvePermissionData(permission::unknown); - permission::PermissionValue value = 0, grant = 0; - bool skipped = false, negated = false; - ClientDbId cldbid = 0; + //Client rights + { + cmd[index]["client_flat"] = ""; + PermissionCommandTuple parm{cmd, index, version, 0, 0}; + auto res = sql::command(server->getSql(), "SELECT `id`, `permId`, `value`, `grant`, `flag_skip`, `flag_negate` FROM `permissions` WHERE `serverId` = :sid AND `type` = :type AND `channelId` = 0 ORDER BY `id`", + variable{":sid", server->getServerId()}, + variable{":type", permission::SQL_PERM_USER} + ).query([&](PermissionCommandTuple* commandIndex, int length, char** values, char**names){ + auto type = permission::resolvePermissionData(permission::unknown); + permission::PermissionValue value = 0, grant = 0; + bool skipped = false, negated = false; + ClientDbId cldbid = 0; - for(int idx = 0; idx < length; idx++) { - try { - if(strcmp(names[idx], "id") == 0) - cldbid = stoul(values[idx] && strlen(values[idx]) > 0 ? values[idx] : "0"); - else if(strcmp(names[idx], "value") == 0) - value = values[idx] && strlen(values[idx]) > 0 ? stoi(values[idx]) : permNotGranted; - else if(strcmp(names[idx], "grant") == 0) - grant = values[idx] && strlen(values[idx]) > 0 ? stoi(values[idx]) : permNotGranted; - else if(strcmp(names[idx], "permId") == 0) { - type = permission::resolvePermissionData(values[idx]); - if(type->type == permission::unknown) { - logError(0, "Could not parse client permission for snapshot (Invalid type {})! Skipping it!", values[idx]); - return 0; - } - } - else if(strcmp(names[idx], "flag_skip") == 0) - skipped = values[idx] ? strcmp(values[idx], "1") == 0 : false; - else if(strcmp(names[idx], "flag_negate") == 0) - negated = values[idx] ? strcmp(values[idx], "1") == 0 : false; - } catch (std::exception& ex) { - logError(0, "Failed to write snapshot client permission (Skipping it)! Message: {} @ {} => {}", ex.what(), names[idx], values[idx]); - return 0; - } - } + for(int idx = 0; idx < length; idx++) { + try { + if(strcmp(names[idx], "id") == 0) + cldbid = stoul(values[idx] && strlen(values[idx]) > 0 ? values[idx] : "0"); + else if(strcmp(names[idx], "value") == 0) + value = values[idx] && strlen(values[idx]) > 0 ? stoi(values[idx]) : permNotGranted; + else if(strcmp(names[idx], "grant") == 0) + grant = values[idx] && strlen(values[idx]) > 0 ? stoi(values[idx]) : permNotGranted; + else if(strcmp(names[idx], "permId") == 0) { + type = permission::resolvePermissionData(values[idx]); + if(type->type == permission::unknown) { + logError(0, "Could not parse client permission for snapshot (Invalid type {})! Skipping it!", values[idx]); + return 0; + } + } + else if(strcmp(names[idx], "flag_skip") == 0) + skipped = values[idx] ? strcmp(values[idx], "1") == 0 : false; + else if(strcmp(names[idx], "flag_negate") == 0) + negated = values[idx] ? strcmp(values[idx], "1") == 0 : false; + } catch (std::exception& ex) { + logError(0, "Failed to write snapshot client permission (Skipping it)! Message: {} @ {} => {}", ex.what(), names[idx], values[idx]); + return 0; + } + } - if(type->type == permission::unknown) { - logError(0, "Could not parse client permission for snapshot (Missing type)! Skipping it!"); - return 0; - } - if(cldbid == 0) { - logError(0, "Could not parse client permission for snapshot (Missing cldbid)! Skipping it!"); - return 0; - } - if(value == permNotGranted && grant == permNotGranted && !skipped && !negated) return 0; + if(type->type == permission::unknown) { + logError(0, "Could not parse client permission for snapshot (Missing type)! Skipping it!"); + return 0; + } + if(cldbid == 0) { + logError(0, "Could not parse client permission for snapshot (Missing cldbid)! Skipping it!"); + return 0; + } + if(value == permNotGranted && grant == permNotGranted && !skipped && !negated) return 0; - if(commandIndex->client != cldbid) { - commandIndex->client = cldbid; - commandIndex->cmd[commandIndex->index]["id1"] = cldbid; - commandIndex->cmd[commandIndex->index]["id2"] = 0; - } - SnapshotPermissionEntry{type, value, grant, negated, skipped}.write(commandIndex->cmd, commandIndex->index, permission::teamspeak::CLIENT, commandIndex->version); - return 0; - }, &parm); - LOG_SQL_CMD(res); - cmd[index++]["end_flat"] = ""; - } + if(commandIndex->client != cldbid) { + commandIndex->client = cldbid; + commandIndex->cmd[commandIndex->index]["id1"] = cldbid; + commandIndex->cmd[commandIndex->index]["id2"] = 0; + } + SnapshotPermissionEntry{type, value, grant, negated, skipped}.write(commandIndex->cmd, commandIndex->index, permission::teamspeak::CLIENT, commandIndex->version); + return 0; + }, &parm); + LOG_SQL_CMD(res); + cmd[index++]["end_flat"] = ""; + } - //Channel rights - { - cmd[index]["channel_flat"] = ""; - PermissionCommandTuple parm{cmd, index, version, 0, 0}; - auto res = sql::command(server->getSql(), "SELECT `channelId`, `permId`, `value`, `grant`, `flag_skip`, `flag_negate` FROM `permissions` WHERE `serverId` = :sid AND `type` = :type", - variable{":sid", server->getServerId()}, - variable{":type", permission::SQL_PERM_CHANNEL}).query( - [&](PermissionCommandTuple* commandIndex, int length, char** values, char**names){ - auto type = permission::resolvePermissionData(permission::unknown); - permission::PermissionValue value = 0, grant = 0; - bool skipped = false, negated = false; - ChannelId chid = 0; + //Channel rights + { + cmd[index]["channel_flat"] = ""; + PermissionCommandTuple parm{cmd, index, version, 0, 0}; + auto res = sql::command(server->getSql(), "SELECT `channelId`, `permId`, `value`, `grant`, `flag_skip`, `flag_negate` FROM `permissions` WHERE `serverId` = :sid AND `type` = :type", + variable{":sid", server->getServerId()}, + variable{":type", permission::SQL_PERM_CHANNEL}).query( + [&](PermissionCommandTuple* commandIndex, int length, char** values, char**names){ + auto type = permission::resolvePermissionData(permission::unknown); + permission::PermissionValue value = 0, grant = 0; + bool skipped = false, negated = false; + ChannelId chid = 0; - for(int idx = 0; idx < length; idx++) { - try { - if(strcmp(names[idx], "channelId") == 0) - chid = stoul(values[idx] && strlen(values[idx]) > 0 ? values[idx] : "0"); - else if(strcmp(names[idx], "value") == 0) - value = values[idx] && strlen(values[idx]) > 0 ? stoi(values[idx]) : permNotGranted; - else if(strcmp(names[idx], "grant") == 0) - grant = values[idx] && strlen(values[idx]) > 0 ? stoi(values[idx]) : permNotGranted; - else if(strcmp(names[idx], "permId") == 0) { - type = permission::resolvePermissionData(values[idx]); - if(type->type == permission::unknown) { - logError(0, "Could not parse channel permission for snapshot (Invalid type {})! Skipping it!", values[idx]); - return 0; - } - } - else if(strcmp(names[idx], "flag_skip") == 0) - skipped = values[idx] ? strcmp(values[idx], "1") == 0 : false; - else if(strcmp(names[idx], "flag_negate") == 0) - negated = values[idx] ? strcmp(values[idx], "1") == 0 : false; - } catch (std::exception& ex) { - logError(0, "Failed to write snapshot channel permission (Skipping it)! Message: {} @ {} => {}", ex.what(), names[idx], values[idx]); - return 0; - } - } + for(int idx = 0; idx < length; idx++) { + try { + if(strcmp(names[idx], "channelId") == 0) + chid = stoul(values[idx] && strlen(values[idx]) > 0 ? values[idx] : "0"); + else if(strcmp(names[idx], "value") == 0) + value = values[idx] && strlen(values[idx]) > 0 ? stoi(values[idx]) : permNotGranted; + else if(strcmp(names[idx], "grant") == 0) + grant = values[idx] && strlen(values[idx]) > 0 ? stoi(values[idx]) : permNotGranted; + else if(strcmp(names[idx], "permId") == 0) { + type = permission::resolvePermissionData(values[idx]); + if(type->type == permission::unknown) { + logError(0, "Could not parse channel permission for snapshot (Invalid type {})! Skipping it!", values[idx]); + return 0; + } + } + else if(strcmp(names[idx], "flag_skip") == 0) + skipped = values[idx] ? strcmp(values[idx], "1") == 0 : false; + else if(strcmp(names[idx], "flag_negate") == 0) + negated = values[idx] ? strcmp(values[idx], "1") == 0 : false; + } catch (std::exception& ex) { + logError(0, "Failed to write snapshot channel permission (Skipping it)! Message: {} @ {} => {}", ex.what(), names[idx], values[idx]); + return 0; + } + } - if(type->type == permission::unknown) { - logError(0, "Could not parse channel permission for snapshot (Missing type)! Skipping it!"); - return 0; - } - if(chid == 0) { - logError(0, "Could not parse channel permission for snapshot (Missing channel id)! Skipping it!"); - return 0; - } - if(value == permNotGranted && grant == permNotGranted && !skipped && !negated) return 0; + if(type->type == permission::unknown) { + logError(0, "Could not parse channel permission for snapshot (Missing type)! Skipping it!"); + return 0; + } + if(chid == 0) { + logError(0, "Could not parse channel permission for snapshot (Missing channel id)! Skipping it!"); + return 0; + } + if(value == permNotGranted && grant == permNotGranted && !skipped && !negated) return 0; - if(commandIndex->channel != chid) { - commandIndex->channel = chid; - commandIndex->cmd[commandIndex->index]["id1"] = chid; - commandIndex->cmd[commandIndex->index]["id2"] = 0; - } - SnapshotPermissionEntry{type, value, grant, negated, skipped}.write(commandIndex->cmd, commandIndex->index, permission::teamspeak::CHANNEL, commandIndex->version); - return 0; - }, &parm); - LOG_SQL_CMD(res); - cmd[index++]["end_flat"] = ""; - } + if(commandIndex->channel != chid) { + commandIndex->channel = chid; + commandIndex->cmd[commandIndex->index]["id1"] = chid; + commandIndex->cmd[commandIndex->index]["id2"] = 0; + } + SnapshotPermissionEntry{type, value, grant, negated, skipped}.write(commandIndex->cmd, commandIndex->index, permission::teamspeak::CHANNEL, commandIndex->version); + return 0; + }, &parm); + LOG_SQL_CMD(res); + cmd[index++]["end_flat"] = ""; + } - //Client channel rights - { - cmd[index]["channel_client_flat"] = ""; - PermissionCommandTuple parm{cmd, index, version, 0, 0}; - auto res = sql::command(server->getSql(), "SELECT `id`, `channelId`, `permId`, `value`, `grant`, `flag_skip`, `flag_negate` FROM `permissions` WHERE `serverId` = :sid AND `type` = :type AND `channelId` != 0", - variable{":sid", server->getServerId()}, - variable{":type", permission::SQL_PERM_USER}).query( - [&](PermissionCommandTuple* commandIndex, int length, char** values, char**names){ - auto type = permission::resolvePermissionData(permission::unknown); - permission::PermissionValue value = 0, grant = 0; - bool skipped = false, negated = false; - ChannelId chid = 0; - ClientDbId cldbid = 0; - for(int idx = 0; idx < length; idx++) { - try { - if(strcmp(names[idx], "channelId") == 0) - chid = stoul(values[idx] && strlen(values[idx]) > 0 ? values[idx] : "0"); - if(strcmp(names[idx], "id") == 0) - cldbid = stoul(values[idx] && strlen(values[idx]) > 0 ? values[idx] : "0"); - else if(strcmp(names[idx], "value") == 0) - value = values[idx] && strlen(values[idx]) > 0 ? stoi(values[idx]) : permNotGranted; - else if(strcmp(names[idx], "grant") == 0) - grant = values[idx] && strlen(values[idx]) > 0 ? stoi(values[idx]) : permNotGranted; - else if(strcmp(names[idx], "permId") == 0) { - type = permission::resolvePermissionData(values[idx]); - if(type->type == permission::unknown) { - logError(0, "Could not parse channel client permission for snapshot (Invalid type {})! Skipping it!", values[idx]); - return 0; - } - } - else if(strcmp(names[idx], "flag_skip") == 0) - skipped = values[idx] ? strcmp(values[idx], "1") == 0 : false; - else if(strcmp(names[idx], "flag_negate") == 0) - negated = values[idx] ? strcmp(values[idx], "1") == 0 : false; - } catch (std::exception& ex) { - logError(0, "Failed to write snapshot channel client permission (Skipping it)! Message: {} @ {} => {}", ex.what(), names[idx], values[idx]); - return 0; - } - } + //Client channel rights + { + cmd[index]["channel_client_flat"] = ""; + PermissionCommandTuple parm{cmd, index, version, 0, 0}; + auto res = sql::command(server->getSql(), "SELECT `id`, `channelId`, `permId`, `value`, `grant`, `flag_skip`, `flag_negate` FROM `permissions` WHERE `serverId` = :sid AND `type` = :type AND `channelId` != 0", + variable{":sid", server->getServerId()}, + variable{":type", permission::SQL_PERM_USER}).query( + [&](PermissionCommandTuple* commandIndex, int length, char** values, char**names){ + auto type = permission::resolvePermissionData(permission::unknown); + permission::PermissionValue value = 0, grant = 0; + bool skipped = false, negated = false; + ChannelId chid = 0; + ClientDbId cldbid = 0; + for(int idx = 0; idx < length; idx++) { + try { + if(strcmp(names[idx], "channelId") == 0) + chid = stoul(values[idx] && strlen(values[idx]) > 0 ? values[idx] : "0"); + if(strcmp(names[idx], "id") == 0) + cldbid = stoul(values[idx] && strlen(values[idx]) > 0 ? values[idx] : "0"); + else if(strcmp(names[idx], "value") == 0) + value = values[idx] && strlen(values[idx]) > 0 ? stoi(values[idx]) : permNotGranted; + else if(strcmp(names[idx], "grant") == 0) + grant = values[idx] && strlen(values[idx]) > 0 ? stoi(values[idx]) : permNotGranted; + else if(strcmp(names[idx], "permId") == 0) { + type = permission::resolvePermissionData(values[idx]); + if(type->type == permission::unknown) { + logError(0, "Could not parse channel client permission for snapshot (Invalid type {})! Skipping it!", values[idx]); + return 0; + } + } + else if(strcmp(names[idx], "flag_skip") == 0) + skipped = values[idx] ? strcmp(values[idx], "1") == 0 : false; + else if(strcmp(names[idx], "flag_negate") == 0) + negated = values[idx] ? strcmp(values[idx], "1") == 0 : false; + } catch (std::exception& ex) { + logError(0, "Failed to write snapshot channel client permission (Skipping it)! Message: {} @ {} => {}", ex.what(), names[idx], values[idx]); + return 0; + } + } - if(type->type == permission::unknown) { - logError(0, "Could not parse channel client permission for snapshot (Missing type)! Skipping it!"); - return 0; - } - if(chid == 0) { - logError(0, "Could not parse channel client permission for snapshot (Missing channel id)! Skipping it!"); - return 0; - } - if(cldbid == 0) { - logError(0, "Could not parse channel client permission for snapshot (Missing cldbid)! Skipping it!"); - return 0; - } - if(value == permNotGranted && grant == permNotGranted && !skipped && !negated) return 0; + if(type->type == permission::unknown) { + logError(0, "Could not parse channel client permission for snapshot (Missing type)! Skipping it!"); + return 0; + } + if(chid == 0) { + logError(0, "Could not parse channel client permission for snapshot (Missing channel id)! Skipping it!"); + return 0; + } + if(cldbid == 0) { + logError(0, "Could not parse channel client permission for snapshot (Missing cldbid)! Skipping it!"); + return 0; + } + if(value == permNotGranted && grant == permNotGranted && !skipped && !negated) return 0; - if(commandIndex->channel != chid || commandIndex->client != cldbid) { - commandIndex->channel = chid; - commandIndex->client = cldbid; - commandIndex->cmd[commandIndex->index]["id1"] = chid; - commandIndex->cmd[commandIndex->index]["id2"] = cldbid; - } - SnapshotPermissionEntry{type, value, grant, negated, skipped}.write(commandIndex->cmd, commandIndex->index, permission::teamspeak::CLIENT, commandIndex->version); - return 0; - }, &parm); - LOG_SQL_CMD(res); - cmd[index++]["end_flat"] = ""; - } - cmd[index++]["end_permissions"] = ""; - } - return true; + if(commandIndex->channel != chid || commandIndex->client != cldbid) { + commandIndex->channel = chid; + commandIndex->client = cldbid; + commandIndex->cmd[commandIndex->index]["id1"] = chid; + commandIndex->cmd[commandIndex->index]["id2"] = cldbid; + } + SnapshotPermissionEntry{type, value, grant, negated, skipped}.write(commandIndex->cmd, commandIndex->index, permission::teamspeak::CLIENT, commandIndex->version); + return 0; + }, &parm); + LOG_SQL_CMD(res); + cmd[index++]["end_flat"] = ""; + } + cmd[index++]["end_permissions"] = ""; + } + return true; } \ No newline at end of file diff --git a/server/src/ShutdownHelper.cpp b/server/src/ShutdownHelper.cpp index b8f87d1..4fb2776 100644 --- a/server/src/ShutdownHelper.cpp +++ b/server/src/ShutdownHelper.cpp @@ -13,121 +13,121 @@ extern bool mainThreadActive; bool shuttingDown = false; void ts::server::shutdownInstance(const std::string& message) { - if(shuttingDown) return; - shuttingDown = true; + if(shuttingDown) return; + shuttingDown = true; - auto hangup_controller = std::thread([]{ - threads::self::sleep_for(chrono::seconds(30)); - logCriticalFmt(true, 0, "Could not shutdown server within 30 seconds! (Hangup!)"); - logCriticalFmt(true, 0, "Killing server!"); + auto hangup_controller = std::thread([]{ + threads::self::sleep_for(chrono::seconds(30)); + logCriticalFmt(true, 0, "Could not shutdown server within 30 seconds! (Hangup!)"); + logCriticalFmt(true, 0, "Killing server!"); - auto force_kill = std::thread([]{ - threads::self::sleep_for(chrono::seconds(5)); - logCriticalFmt(true, 0, "Failed to exit normally!"); - logCriticalFmt(true, 0, "executing raise(SIGKILL);"); - raise(SIGKILL); - }); - threads::name(force_kill, "force stopper"); - force_kill.detach(); + auto force_kill = std::thread([]{ + threads::self::sleep_for(chrono::seconds(5)); + logCriticalFmt(true, 0, "Failed to exit normally!"); + logCriticalFmt(true, 0, "executing raise(SIGKILL);"); + raise(SIGKILL); + }); + threads::name(force_kill, "force stopper"); + force_kill.detach(); - exit(2); - }); - threads::name(hangup_controller, "stop controller"); - hangup_controller.detach(); + exit(2); + }); + threads::name(hangup_controller, "stop controller"); + hangup_controller.detach(); - logMessage(LOG_GENERAL, "Stopping all server instances!"); - if(serverInstance && serverInstance->getVoiceServerManager()) - serverInstance->getVoiceServerManager()->shutdownAll(message); + logMessage(LOG_GENERAL, "Stopping all server instances!"); + if(serverInstance && serverInstance->getVoiceServerManager()) + serverInstance->getVoiceServerManager()->shutdownAll(message); - mainThreadActive = false; + mainThreadActive = false; } std::shared_ptr currentShutdown = nullptr; std::shared_ptr server::scheduledShutdown() { return currentShutdown; } inline void broadcastMessage(const std::string& message) { - if(!serverInstance || !serverInstance->getVoiceServerManager()) - return; + if(!serverInstance || !serverInstance->getVoiceServerManager()) + return; - for(const auto &server : serverInstance->getVoiceServerManager()->serverInstances()) { - if(server->running()) { - server->broadcastMessage(server->getServerRoot(), message); - } - } + for(const auto &server : serverInstance->getVoiceServerManager()->serverInstances()) { + if(server->running()) { + server->broadcastMessage(server->getServerRoot(), message); + } + } } void executeScheduledShutdown(const std::shared_ptr& data); bool server::scheduleShutdown(const std::chrono::system_clock::time_point& time, const std::string& reason) { - server::cancelShutdown(false); //Cancel old shutdown + server::cancelShutdown(false); //Cancel old shutdown - auto data = std::make_shared(); - data->active = true; - data->time_point = time; - data->reason = reason; + auto data = std::make_shared(); + data->active = true; + data->time_point = time; + data->reason = reason; - data->shutdown_thread = std::thread{[data]{ - executeScheduledShutdown(data); - }}; - threads::name(data->shutdown_thread, "Shutdown executor"); - currentShutdown = data; - return true; + data->shutdown_thread = std::thread{[data]{ + executeScheduledShutdown(data); + }}; + threads::name(data->shutdown_thread, "Shutdown executor"); + currentShutdown = data; + return true; } void server::cancelShutdown(bool notify) { - if(!currentShutdown) return; - if(notify && !config::messages::shutdown::canceled.empty()) { - broadcastMessage(config::messages::shutdown::canceled); - } + if(!currentShutdown) return; + if(notify && !config::messages::shutdown::canceled.empty()) { + broadcastMessage(config::messages::shutdown::canceled); + } - auto current = server::scheduledShutdown(); - current->active = false; - current->shutdownNotify.notify_all(); - if(!threads::save_join(current->shutdown_thread)) { - logCritical(LOG_GENERAL, "Could not terminate shutdown thread!"); - current->shutdown_thread.detach(); - } - currentShutdown = nullptr; + auto current = server::scheduledShutdown(); + current->active = false; + current->shutdownNotify.notify_all(); + if(!threads::save_join(current->shutdown_thread)) { + logCritical(LOG_GENERAL, "Could not terminate shutdown thread!"); + current->shutdown_thread.detach(); + } + currentShutdown = nullptr; } void executeScheduledShutdown(const shared_ptr& data) { - std::time_t time_point = system_clock::to_time_t(data->time_point); - { - auto message = strvar::transform(config::messages::shutdown::scheduled, strvar::FunctionValue("time", (strvar::FunctionValue::FValueFNEasy) [&](std::deque value) { - auto pattern = !value.empty() ? value[0] : "%Y-%m-%d_%H:%M:%S"; + std::time_t time_point = system_clock::to_time_t(data->time_point); + { + auto message = strvar::transform(config::messages::shutdown::scheduled, strvar::FunctionValue("time", (strvar::FunctionValue::FValueFNEasy) [&](std::deque value) { + auto pattern = !value.empty() ? value[0] : "%Y-%m-%d_%H:%M:%S"; - tm* tm_info = localtime(&time_point); + tm* tm_info = localtime(&time_point); - char timeBuffer[1024]; - if(strftime(timeBuffer, 1024, pattern.c_str(), tm_info) == 0) { - return string("string is longer than the buffer"); - } + char timeBuffer[1024]; + if(strftime(timeBuffer, 1024, pattern.c_str(), tm_info) == 0) { + return string("string is longer than the buffer"); + } - return string(timeBuffer); - })); - broadcastMessage(message); - } + return string(timeBuffer); + })); + broadcastMessage(message); + } - while(data->time_point > system_clock::now()) { - auto per = data->time_point - system_clock::now(); - pair best_period = {seconds(0), ""}; + while(data->time_point > system_clock::now()) { + auto per = data->time_point - system_clock::now(); + pair best_period = {seconds(0), ""}; - for(const auto& period : config::messages::shutdown::intervals) { - if(period.first > per) continue; - if(period.first > best_period.first) best_period = period; - } + for(const auto& period : config::messages::shutdown::intervals) { + if(period.first > per) continue; + if(period.first > best_period.first) best_period = period; + } - { - std::unique_lock lock(data->shutdownMutex); - data->shutdownNotify.wait_until(lock, data->time_point - best_period.first, [data](){ return !data->active; }); - if(!data->active) return; - } + { + std::unique_lock lock(data->shutdownMutex); + data->shutdownNotify.wait_until(lock, data->time_point - best_period.first, [data](){ return !data->active; }); + if(!data->active) return; + } - if(best_period.first.count() == 0) - broadcastMessage(config::messages::shutdown::now); - else - broadcastMessage(strvar::transform(config::messages::shutdown::interval, strvar::StringValue{"interval", best_period.second})); - } + if(best_period.first.count() == 0) + broadcastMessage(config::messages::shutdown::now); + else + broadcastMessage(strvar::transform(config::messages::shutdown::interval, strvar::StringValue{"interval", best_period.second})); + } - ts::server::shutdownInstance(data->reason); - //No need to delete own task + ts::server::shutdownInstance(data->reason); + //No need to delete own task } \ No newline at end of file diff --git a/server/src/ShutdownHelper.h b/server/src/ShutdownHelper.h index 85654de..04b609d 100644 --- a/server/src/ShutdownHelper.h +++ b/server/src/ShutdownHelper.h @@ -3,22 +3,22 @@ #include "Configuration.h" namespace ts { - namespace server { - extern void shutdownInstance(const std::string& reason = ts::config::messages::applicationStopped); + namespace server { + extern void shutdownInstance(const std::string& reason = ts::config::messages::applicationStopped); - struct ShutdownData { - std::string reason; - std::chrono::system_clock::time_point time_point; - bool active; + struct ShutdownData { + std::string reason; + std::chrono::system_clock::time_point time_point; + bool active; - std::thread shutdown_thread{}; - std::mutex shutdownMutex; - std::condition_variable shutdownNotify; - }; + std::thread shutdown_thread{}; + std::mutex shutdownMutex; + std::condition_variable shutdownNotify; + }; - extern bool scheduleShutdown(const std::chrono::system_clock::time_point&, const std::string& = ts::config::messages::applicationStopped); - extern std::shared_ptr scheduledShutdown(); - extern void cancelShutdown(bool notify = true); - } + extern bool scheduleShutdown(const std::chrono::system_clock::time_point&, const std::string& = ts::config::messages::applicationStopped); + extern std::shared_ptr scheduledShutdown(); + extern void cancelShutdown(bool notify = true); + } } \ No newline at end of file diff --git a/server/src/SignalHandler.cpp b/server/src/SignalHandler.cpp index ebe7140..7bc5549 100644 --- a/server/src/SignalHandler.cpp +++ b/server/src/SignalHandler.cpp @@ -19,40 +19,40 @@ google_breakpad::ExceptionHandler* globalExceptionHandler = nullptr; extern bool mainThreadDone; static bool dumpCallback(const google_breakpad::MinidumpDescriptor& descriptor, void* context, bool succeeded) { - logCritical(LOG_GENERAL, "The server crashed!"); - try { - if(!fs::exists(fs::u8path(ts::config::crash_path))) - fs::create_directories(fs::u8path(ts::config::crash_path)); + logCritical(LOG_GENERAL, "The server crashed!"); + try { + if(!fs::exists(fs::u8path(ts::config::crash_path))) + fs::create_directories(fs::u8path(ts::config::crash_path)); - auto path = fs::u8path(descriptor.path()); - path = fs::u8path(ts::config::crash_path + "crash_dump_" + path.filename().string()); - fs::rename(fs::u8path(descriptor.path()), path); - logCritical(LOG_GENERAL, "Wrote crash dump to " + path.relative_path().string()); - } catch (...) { - logCritical(LOG_GENERAL, "Failed to write/move crash dump!"); - } - if(std::current_exception()) { - logCritical(LOG_GENERAL, "Exception reached stack root and cause the server to crash!"); - logCritical(LOG_GENERAL, " Type: {}", std::current_exception().__cxa_exception_type()->name()); - try { - std::rethrow_exception(std::current_exception()); - } catch(std::exception& ex) { - logCritical(LOG_GENERAL, " Message: {}", ex.what()); - } catch(...) {} - } - logCritical(LOG_GENERAL, "Please report this crash to the TeaSpeak maintainer WolverinDEV"); - logCritical(LOG_GENERAL, "Official issue and bug tracker url: https://github.com/TeaSpeak/TeaSpeak/issues"); - logCritical(LOG_GENERAL, "Any reports of crashes are useless if you not provide the above generated crashlog!"); - logCritical(LOG_GENERAL, "Stopping server"); - ts::server::shutdownInstance(ts::config::messages::applicationCrashed); - while(!mainThreadDone) threads::self::sleep_for(chrono::seconds(1)); - return succeeded; + auto path = fs::u8path(descriptor.path()); + path = fs::u8path(ts::config::crash_path + "crash_dump_" + path.filename().string()); + fs::rename(fs::u8path(descriptor.path()), path); + logCritical(LOG_GENERAL, "Wrote crash dump to " + path.relative_path().string()); + } catch (...) { + logCritical(LOG_GENERAL, "Failed to write/move crash dump!"); + } + if(std::current_exception()) { + logCritical(LOG_GENERAL, "Exception reached stack root and cause the server to crash!"); + logCritical(LOG_GENERAL, " Type: {}", std::current_exception().__cxa_exception_type()->name()); + try { + std::rethrow_exception(std::current_exception()); + } catch(std::exception& ex) { + logCritical(LOG_GENERAL, " Message: {}", ex.what()); + } catch(...) {} + } + logCritical(LOG_GENERAL, "Please report this crash to the TeaSpeak maintainer WolverinDEV"); + logCritical(LOG_GENERAL, "Official issue and bug tracker url: https://github.com/TeaSpeak/TeaSpeak/issues"); + logCritical(LOG_GENERAL, "Any reports of crashes are useless if you not provide the above generated crashlog!"); + logCritical(LOG_GENERAL, "Stopping server"); + ts::server::shutdownInstance(ts::config::messages::applicationCrashed); + while(!mainThreadDone) threads::self::sleep_for(chrono::seconds(1)); + return succeeded; } std::atomic spawn_failed_count = 0; bool ts::syssignal::setup() { - logMessage(LOG_GENERAL, "Setting up exception handler"); - globalExceptionHandler = new google_breakpad::ExceptionHandler(google_breakpad::MinidumpDescriptor("."), nullptr, dumpCallback, nullptr, true, -1); + logMessage(LOG_GENERAL, "Setting up exception handler"); + globalExceptionHandler = new google_breakpad::ExceptionHandler(google_breakpad::MinidumpDescriptor("."), nullptr, dumpCallback, nullptr, true, -1); SIG(SIGTERM, &ts::syssignal::handleStopSignal); if(isatty(fileno(stdin))) //We cant listen for this signal if stdin ist a atty @@ -62,40 +62,40 @@ bool ts::syssignal::setup() { } bool ts::syssignal::setup_threads() { - threads::set_global_error_handler([](auto error) { - if(error == threads::ThreadError::HANDLE_DELETE_UNDETACHED) { - logCritical(LOG_GENERAL, "Missed out thread detachment! This could lead to memory leaks!"); - return threads::ThreadErrorAction::IGNORE; - } else if(error == threads::ThreadError::SPAWN_FAILED) { - logCritical(LOG_GENERAL, "Spawning a new thread failed!"); - if(spawn_failed_count++ == 0) { - logCritical(LOG_GENERAL, "Stopping process!"); - try { - std::thread([]{ - ts::server::shutdownInstance("Failed to spawn new threads! Safety shutdown"); - }).detach(); - } catch(...) { - logCritical(LOG_GENERAL, "Failed to spawn shutdown thread (Of cause...). Stopping application directly!"); - logCritical(LOG_GENERAL, "If this happens frequently dont forget to checkout std stderr channel for more information"); - raise(SIGKILL); - } - } - return threads::ThreadErrorAction::RAISE; - } + threads::set_global_error_handler([](auto error) { + if(error == threads::ThreadError::HANDLE_DELETE_UNDETACHED) { + logCritical(LOG_GENERAL, "Missed out thread detachment! This could lead to memory leaks!"); + return threads::ThreadErrorAction::IGNORE; + } else if(error == threads::ThreadError::SPAWN_FAILED) { + logCritical(LOG_GENERAL, "Spawning a new thread failed!"); + if(spawn_failed_count++ == 0) { + logCritical(LOG_GENERAL, "Stopping process!"); + try { + std::thread([]{ + ts::server::shutdownInstance("Failed to spawn new threads! Safety shutdown"); + }).detach(); + } catch(...) { + logCritical(LOG_GENERAL, "Failed to spawn shutdown thread (Of cause...). Stopping application directly!"); + logCritical(LOG_GENERAL, "If this happens frequently dont forget to checkout std stderr channel for more information"); + raise(SIGKILL); + } + } + return threads::ThreadErrorAction::RAISE; + } - return threads::ThreadErrorAction::RAISE; - }); - return true; + return threads::ThreadErrorAction::RAISE; + }); + return true; } atomic_int signal_count = 0; void ts::syssignal::handleStopSignal(int signal) { - logMessageFmt(true, LOG_INSTANCE, "Got stop signal ({}). Stopping instance.", signal == SIGTERM ? "SIGTERM" : - signal == SIGINT ? "SIGINT" : - "UNKNOWN (" + to_string(signal) + ")"); - if(signal_count++ >= 3) { - logMessageFmt(true, LOG_INSTANCE, "Got stop signal more that tree times. Force exiting instance."); - raise(SIGKILL); - } - ts::server::shutdownInstance(); + logMessageFmt(true, LOG_INSTANCE, "Got stop signal ({}). Stopping instance.", signal == SIGTERM ? "SIGTERM" : + signal == SIGINT ? "SIGINT" : + "UNKNOWN (" + to_string(signal) + ")"); + if(signal_count++ >= 3) { + logMessageFmt(true, LOG_INSTANCE, "Got stop signal more that tree times. Force exiting instance."); + raise(SIGKILL); + } + ts::server::shutdownInstance(); } \ No newline at end of file diff --git a/server/src/TS3ServerClientManager.cpp b/server/src/TS3ServerClientManager.cpp index 9e29f85..4e3d390 100644 --- a/server/src/TS3ServerClientManager.cpp +++ b/server/src/TS3ServerClientManager.cpp @@ -16,164 +16,164 @@ using namespace ts::permission; using namespace std::chrono; bool TSServer::registerClient(shared_ptr client) { - sassert(client); + sassert(client); - { - lock_guard lock(this->clients.lock); - if(client->getClientId() > 0) { - logCritical(this->getServerId(), "Client {} ({}|{}) has been already registered!", client->getDisplayName(), client->getClientId(), client->getUid()); - return false; - } + { + lock_guard lock(this->clients.lock); + if(client->getClientId() > 0) { + logCritical(this->getServerId(), "Client {} ({}|{}) has been already registered!", client->getDisplayName(), client->getClientId(), client->getUid()); + return false; + } - ClientId client_id = 0; - ClientId max_client_id = this->clients.clients.size(); + ClientId client_id = 0; + ClientId max_client_id = this->clients.clients.size(); - while(client_id < max_client_id && this->clients.clients[client_id]) - client_id++; - if(client_id == max_client_id) - this->clients.clients.push_back(client); - else - this->clients.clients[client_id] = client; - this->clients.count++; - client->setClientId(client_id); - } + while(client_id < max_client_id && this->clients.clients[client_id]) + client_id++; + if(client_id == max_client_id) + this->clients.clients.push_back(client); + else + this->clients.clients[client_id] = client; + this->clients.count++; + client->setClientId(client_id); + } - { - lock_guard lock(this->client_nickname_lock); + { + lock_guard lock(this->client_nickname_lock); - auto login_name = client->getDisplayName(); - while(login_name.length() < 3) - login_name += "."; + auto login_name = client->getDisplayName(); + while(login_name.length() < 3) + login_name += "."; - if(client->getExternalType() == ClientType::CLIENT_TEAMSPEAK) - client->properties()[property::CLIENT_LOGIN_NAME] = login_name; + if(client->getExternalType() == ClientType::CLIENT_TEAMSPEAK) + client->properties()[property::CLIENT_LOGIN_NAME] = login_name; - std::shared_ptr found_client = nullptr; + std::shared_ptr found_client = nullptr; - auto client_name = login_name; - size_t counter = 0; + auto client_name = login_name; + size_t counter = 0; - { - lock_guard clients_lock(this->clients.lock); - while(true) { - for(auto& _client : this->clients.clients) { - if(!_client) continue; + { + lock_guard clients_lock(this->clients.lock); + while(true) { + for(auto& _client : this->clients.clients) { + if(!_client) continue; - if(_client->getDisplayName() == client_name && _client != client) - goto increase_name; - } - goto nickname_valid; + if(_client->getDisplayName() == client_name && _client != client) + goto increase_name; + } + goto nickname_valid; - increase_name: - client_name = login_name + to_string(++counter); - } - } - nickname_valid: - client->setDisplayName(client_name); - } + increase_name: + client_name = login_name + to_string(++counter); + } + } + nickname_valid: + client->setDisplayName(client_name); + } - if(client->getType() == ClientType::CLIENT_TEAMSPEAK || client->getType() == ClientType::CLIENT_WEB) { - this->properties()[property::VIRTUALSERVER_CLIENT_CONNECTIONS] ++; //increase manager connections - this->properties()[property::VIRTUALSERVER_LAST_CLIENT_CONNECT] = duration_cast(system_clock::now().time_since_epoch()).count(); - } - else if(client->getType() == ClientType::CLIENT_QUERY) { - this->properties()[property::VIRTUALSERVER_LAST_QUERY_CONNECT] = duration_cast(system_clock::now().time_since_epoch()).count(); - this->properties()[property::VIRTUALSERVER_QUERY_CLIENT_CONNECTIONS] ++; //increase manager connections - } + if(client->getType() == ClientType::CLIENT_TEAMSPEAK || client->getType() == ClientType::CLIENT_WEB) { + this->properties()[property::VIRTUALSERVER_CLIENT_CONNECTIONS] ++; //increase manager connections + this->properties()[property::VIRTUALSERVER_LAST_CLIENT_CONNECT] = duration_cast(system_clock::now().time_since_epoch()).count(); + } + else if(client->getType() == ClientType::CLIENT_QUERY) { + this->properties()[property::VIRTUALSERVER_LAST_QUERY_CONNECT] = duration_cast(system_clock::now().time_since_epoch()).count(); + this->properties()[property::VIRTUALSERVER_QUERY_CLIENT_CONNECTIONS] ++; //increase manager connections + } return true; } bool TSServer::unregisterClient(shared_ptr cl, std::string reason, std::unique_lock& chan_tree_lock) { - if(cl->getType() == ClientType::CLIENT_TEAMSPEAK && cl->getType() == ClientType::CLIENT_WEB) { - sassert(cl->state == ConnectionState::DISCONNECTED); - } + if(cl->getType() == ClientType::CLIENT_TEAMSPEAK && cl->getType() == ClientType::CLIENT_WEB) { + sassert(cl->state == ConnectionState::DISCONNECTED); + } - auto client_id = cl->getClientId(); - if(client_id == 0) return false; /* not registered */ - { + auto client_id = cl->getClientId(); + if(client_id == 0) return false; /* not registered */ + { - lock_guard lock(this->clients.lock); - if(client_id >= this->clients.clients.size()) { - logCritical(this->getServerId(), "Client {} ({}|{}) has been registered, but client id exceed client id! Failed to unregister client.", cl->getDisplayName(), client_id, cl->getUid()); - } else { - auto& client_container = this->clients.clients[client_id]; - if(client_container != cl) { - logCritical(this->getServerId(), "Client {} ({}|{}) has been registered, but container hasn't client set! Failed to unregister client.", cl->getDisplayName(), client_id, cl->getUid()); - } else { - client_container.reset(); - this->clients.count--; - } - } - } + lock_guard lock(this->clients.lock); + if(client_id >= this->clients.clients.size()) { + logCritical(this->getServerId(), "Client {} ({}|{}) has been registered, but client id exceed client id! Failed to unregister client.", cl->getDisplayName(), client_id, cl->getUid()); + } else { + auto& client_container = this->clients.clients[client_id]; + if(client_container != cl) { + logCritical(this->getServerId(), "Client {} ({}|{}) has been registered, but container hasn't client set! Failed to unregister client.", cl->getDisplayName(), client_id, cl->getUid()); + } else { + client_container.reset(); + this->clients.count--; + } + } + } - if(cl->getType() == ClientType::CLIENT_TEAMSPEAK || cl->getType() == ClientType::CLIENT_WEB) - this->properties()[property::VIRTUALSERVER_LAST_CLIENT_DISCONNECT] = duration_cast(system_clock::now().time_since_epoch()).count(); - else if(cl->getType() == ClientType::CLIENT_QUERY) - this->properties()[property::VIRTUALSERVER_LAST_QUERY_DISCONNECT] = duration_cast(system_clock::now().time_since_epoch()).count(); + if(cl->getType() == ClientType::CLIENT_TEAMSPEAK || cl->getType() == ClientType::CLIENT_WEB) + this->properties()[property::VIRTUALSERVER_LAST_CLIENT_DISCONNECT] = duration_cast(system_clock::now().time_since_epoch()).count(); + else if(cl->getType() == ClientType::CLIENT_QUERY) + this->properties()[property::VIRTUALSERVER_LAST_QUERY_DISCONNECT] = duration_cast(system_clock::now().time_since_epoch()).count(); - { - if(!chan_tree_lock.owns_lock()) - chan_tree_lock.lock(); + { + if(!chan_tree_lock.owns_lock()) + chan_tree_lock.lock(); - if(cl->currentChannel) //We dont have to make him invisible if he hasnt even a channel - this->client_move(cl, nullptr, nullptr, reason, ViewReasonId::VREASON_SERVER_LEFT, false, chan_tree_lock); - } + if(cl->currentChannel) //We dont have to make him invisible if he hasnt even a channel + this->client_move(cl, nullptr, nullptr, reason, ViewReasonId::VREASON_SERVER_LEFT, false, chan_tree_lock); + } - serverInstance->databaseHelper()->saveClientPermissions(this->ref(), cl->getClientDatabaseId(), cl->clientPermissions); - cl->setClientId(0); - return true; + serverInstance->databaseHelper()->saveClientPermissions(this->ref(), cl->getClientDatabaseId(), cl->clientPermissions); + cl->setClientId(0); + return true; } void TSServer::registerInternalClient(std::shared_ptr client) { client->state = ConnectionState::CONNECTED; - { - lock_guard lock(this->clients.lock); - if(client->getClientId() > 0) { - logCritical(this->getServerId(), "Internal client {} ({}|{}) has been already registered!", client->getDisplayName(), client->getClientId(), client->getUid()); - return; - } + { + lock_guard lock(this->clients.lock); + if(client->getClientId() > 0) { + logCritical(this->getServerId(), "Internal client {} ({}|{}) has been already registered!", client->getDisplayName(), client->getClientId(), client->getUid()); + return; + } - ClientId client_id = 0; - ClientId max_client_id = this->clients.clients.size(); - while(client_id < max_client_id && this->clients.clients[client_id]) - client_id++; - if(client_id == max_client_id) - this->clients.clients.push_back(client); - else - this->clients.clients[client_id] = client; + ClientId client_id = 0; + ClientId max_client_id = this->clients.clients.size(); + while(client_id < max_client_id && this->clients.clients[client_id]) + client_id++; + if(client_id == max_client_id) + this->clients.clients.push_back(client); + else + this->clients.clients[client_id] = client; - this->clients.clients[client_id] = client; - this->clients.count++; - client->setClientId(client_id); - } + this->clients.clients[client_id] = client; + this->clients.count++; + client->setClientId(client_id); + } } void TSServer::unregisterInternalClient(std::shared_ptr client) { client->state = ConnectionState::DISCONNECTED; - { - auto client_id = client->getClientId(); + { + auto client_id = client->getClientId(); - lock_guard lock(this->clients.lock); - if(client_id >= this->clients.clients.size()) { - logCritical(this->getServerId(), "Client {} ({}|{}) has been registered, but client id exceed client id! Failed to unregister internal client.", client->getDisplayName(), client_id, client->getUid()); - } else { - auto& client_container = this->clients.clients[client_id]; - if(client_container != client) { - logCritical(this->getServerId(), "Client {} ({}|{}) has been registered, but container hasn't client set! Failed to unregister internal client.", client->getDisplayName(), client_id, client->getUid()); - } else { - this->clients.count--; - client_container.reset(); - } - } - } + lock_guard lock(this->clients.lock); + if(client_id >= this->clients.clients.size()) { + logCritical(this->getServerId(), "Client {} ({}|{}) has been registered, but client id exceed client id! Failed to unregister internal client.", client->getDisplayName(), client_id, client->getUid()); + } else { + auto& client_container = this->clients.clients[client_id]; + if(client_container != client) { + logCritical(this->getServerId(), "Client {} ({}|{}) has been registered, but container hasn't client set! Failed to unregister internal client.", client->getDisplayName(), client_id, client->getUid()); + } else { + this->clients.count--; + client_container.reset(); + } + } + } } bool TSServer::assignDefaultChannel(const shared_ptr& client, bool join) { - shared_lock server_channel_lock(this->channel_tree_lock); + shared_lock server_channel_lock(this->channel_tree_lock); std::shared_ptr channel = nullptr; if(client->properties()->hasProperty(property::CLIENT_DEFAULT_CHANNEL) && !client->properties()[property::CLIENT_DEFAULT_CHANNEL].as().empty()) { auto str = client->properties()[property::CLIENT_DEFAULT_CHANNEL].as(); @@ -182,69 +182,69 @@ bool TSServer::assignDefaultChannel(const shared_ptr& client, b else channel = this->channelTree->findChannelByPath(str); if (channel) { - if(!channel->permission_granted(permission::i_channel_needed_join_power, client->calculate_permission_value(permission::i_channel_join_power, channel->channelId()), false)) { - logMessage(this->serverId, "{} Client tried to connect to a channel which he hasn't permission for. Channel: {} ({})", CLIENT_STR_LOG_PREFIX_(client), channel->channelId(), channel->name()); - channel = nullptr; + if(!channel->permission_granted(permission::i_channel_needed_join_power, client->calculate_permission_value(permission::i_channel_join_power, channel->channelId()), false)) { + logMessage(this->serverId, "{} Client tried to connect to a channel which he hasn't permission for. Channel: {} ({})", CLIENT_STR_LOG_PREFIX_(client), channel->channelId(), channel->name()); + channel = nullptr; } else if (!channel->passwordMatch(client->properties()[property::CLIENT_DEFAULT_CHANNEL_PASSWORD], true) && client->permissionValue(permission::PERMTEST_ORDERED, permission::b_channel_join_ignore_password, channel) < 1) { - logMessage(this->serverId, "{} Client tried to connect to a channel which is password protected and he hasn't the right password. Channel: {} ({})", CLIENT_STR_LOG_PREFIX_(client), channel->channelId(), channel->name()); - channel = nullptr; + logMessage(this->serverId, "{} Client tried to connect to a channel which is password protected and he hasn't the right password. Channel: {} ({})", CLIENT_STR_LOG_PREFIX_(client), channel->channelId(), channel->name()); + channel = nullptr; } } else logMessage(this->serverId, "{} Client {}/{} tried to join on a not existing channel. Name: {}", - CLIENT_STR_LOG_PREFIX_(client), - client->getDisplayName(), client->getUid(), - client->properties()[property::CLIENT_DEFAULT_CHANNEL].as()); + CLIENT_STR_LOG_PREFIX_(client), + client->getDisplayName(), client->getUid(), + client->properties()[property::CLIENT_DEFAULT_CHANNEL].as()); } if(!channel) channel = this->channelTree->getDefaultChannel(); - if(!channel) return false; + if(!channel) return false; - if(join) { - server_channel_lock.unlock(); - unique_lock server_channel_w_lock(this->channel_tree_lock); - this->client_move(client, channel, nullptr, "", ViewReasonId::VREASON_USER_ACTION, false, server_channel_w_lock); - } else { - client->currentChannel = channel; - } + if(join) { + server_channel_lock.unlock(); + unique_lock server_channel_w_lock(this->channel_tree_lock); + this->client_move(client, channel, nullptr, "", ViewReasonId::VREASON_USER_ACTION, false, server_channel_w_lock); + } else { + client->currentChannel = channel; + } return true; } void TSServer::testBanStateChange(const std::shared_ptr& invoker) { - this->forEachClient([&](shared_ptr client) { - auto ban = client->resolveActiveBan(client->getPeerIp()); - if(ban) { - logMessage(this->getServerId(), "Client {} was online, but had an ban whcih effect him has been registered. Disconnecting client.", CLIENT_STR_LOG_PREFIX_(client)); - auto entryTime = ban->until.time_since_epoch().count() > 0 ? (uint64_t) chrono::ceil(ban->until - system_clock::now()).count() : 0UL; - this->notify_client_ban(client, invoker, ban->reason, entryTime); - client->closeConnection(system_clock::now() + seconds(1)); - } - }); + this->forEachClient([&](shared_ptr client) { + auto ban = client->resolveActiveBan(client->getPeerIp()); + if(ban) { + logMessage(this->getServerId(), "Client {} was online, but had an ban whcih effect him has been registered. Disconnecting client.", CLIENT_STR_LOG_PREFIX_(client)); + auto entryTime = ban->until.time_since_epoch().count() > 0 ? (uint64_t) chrono::ceil(ban->until - system_clock::now()).count() : 0UL; + this->notify_client_ban(client, invoker, ban->reason, entryTime); + client->closeConnection(system_clock::now() + seconds(1)); + } + }); } bool TSServer::could_default_create_channel() { - { + { - auto default_group = this->getGroupManager()->defaultGroup(GroupTarget::GROUPTARGET_SERVER); - if(default_group) { - auto flag = default_group->permissions()->permission_value_flagged(permission::b_channel_create_temporary).value == 1; - flag = flag ? flag : default_group->permissions()->permission_value_flagged(permission::b_channel_create_semi_permanent).value == 1; - flag = flag ? flag : default_group->permissions()->permission_value_flagged(permission::b_channel_create_permanent).value == 1; - if(flag) - return true; - } - } - { + auto default_group = this->getGroupManager()->defaultGroup(GroupTarget::GROUPTARGET_SERVER); + if(default_group) { + auto flag = default_group->permissions()->permission_value_flagged(permission::b_channel_create_temporary).value == 1; + flag = flag ? flag : default_group->permissions()->permission_value_flagged(permission::b_channel_create_semi_permanent).value == 1; + flag = flag ? flag : default_group->permissions()->permission_value_flagged(permission::b_channel_create_permanent).value == 1; + if(flag) + return true; + } + } + { - auto default_group = this->getGroupManager()->defaultGroup(GroupTarget::GROUPTARGET_CHANNEL); - if(default_group) { - auto flag = default_group->permissions()->permission_value_flagged(permission::b_channel_create_temporary).value == 1; - flag = flag ? flag : default_group->permissions()->permission_value_flagged(permission::b_channel_create_semi_permanent).value == 1; - flag = flag ? flag : default_group->permissions()->permission_value_flagged(permission::b_channel_create_permanent).value == 1; - if(flag) - return true; - } - } + auto default_group = this->getGroupManager()->defaultGroup(GroupTarget::GROUPTARGET_CHANNEL); + if(default_group) { + auto flag = default_group->permissions()->permission_value_flagged(permission::b_channel_create_temporary).value == 1; + flag = flag ? flag : default_group->permissions()->permission_value_flagged(permission::b_channel_create_semi_permanent).value == 1; + flag = flag ? flag : default_group->permissions()->permission_value_flagged(permission::b_channel_create_permanent).value == 1; + if(flag) + return true; + } + } - return false; + return false; } /* @@ -255,64 +255,64 @@ bool TSServer::could_default_create_channel() { */ void TSServer::notify_client_ban(const shared_ptr &target, const std::shared_ptr &invoker, const std::string &reason, size_t time) { - /* the target is not allowed to execute anything; Must before channel tree lock because the target may waits for us to finish the channel stuff */ - lock_guard command_lock(target->command_lock); - unique_lock server_channel_lock(this->channel_tree_lock); /* we're "moving" a client! */ + /* the target is not allowed to execute anything; Must before channel tree lock because the target may waits for us to finish the channel stuff */ + lock_guard command_lock(target->command_lock); + unique_lock server_channel_lock(this->channel_tree_lock); /* we're "moving" a client! */ - if(target->currentChannel) { - for(const auto& client : this->getClients()) { - if(!client || client == target) - continue; + if(target->currentChannel) { + for(const auto& client : this->getClients()) { + if(!client || client == target) + continue; - unique_lock client_channel_lock(client->channel_lock); - if(client->isClientVisible(target, false)) - client->notifyClientLeftViewBanned(target, reason, invoker, time, false); - } + unique_lock client_channel_lock(client->channel_lock); + if(client->isClientVisible(target, false)) + client->notifyClientLeftViewBanned(target, reason, invoker, time, false); + } - auto s_channel = dynamic_pointer_cast(target->currentChannel); - s_channel->unregister_client(target); - } + auto s_channel = dynamic_pointer_cast(target->currentChannel); + s_channel->unregister_client(target); + } - /* now disconnect the target itself */ - unique_lock client_channel_lock(target->channel_lock); - target->notifyClientLeftViewBanned(target, reason, invoker, time, false); - target->currentChannel = nullptr; + /* now disconnect the target itself */ + unique_lock client_channel_lock(target->channel_lock); + target->notifyClientLeftViewBanned(target, reason, invoker, time, false); + target->currentChannel = nullptr; } void TSServer::notify_client_kick( - const std::shared_ptr &target, + const std::shared_ptr &target, const std::shared_ptr &invoker, const std::string &reason, const std::shared_ptr &target_channel) { - if(target_channel) { - /* use the move! */ - unique_lock server_channel_lock(this->channel_tree_lock, defer_lock); - this->client_move(target, target_channel, invoker, reason, ViewReasonId::VREASON_CHANNEL_KICK, true, server_channel_lock); - } else { - /* the target is not allowed to execute anything; Must before channel tree lock because the target may waits for us to finish the channel stuff */ - lock_guard command_lock(target->command_lock); - unique_lock server_channel_lock(this->channel_tree_lock); /* we're "moving" a client! */ + if(target_channel) { + /* use the move! */ + unique_lock server_channel_lock(this->channel_tree_lock, defer_lock); + this->client_move(target, target_channel, invoker, reason, ViewReasonId::VREASON_CHANNEL_KICK, true, server_channel_lock); + } else { + /* the target is not allowed to execute anything; Must before channel tree lock because the target may waits for us to finish the channel stuff */ + lock_guard command_lock(target->command_lock); + unique_lock server_channel_lock(this->channel_tree_lock); /* we're "moving" a client! */ - if(target->currentChannel) { - for(const auto& client : this->getClients()) { - if(!client || client == target) - continue; + if(target->currentChannel) { + for(const auto& client : this->getClients()) { + if(!client || client == target) + continue; - unique_lock client_channel_lock(client->channel_lock); - if(client->isClientVisible(target, false)) - client->notifyClientLeftViewKicked(target, nullptr, reason, invoker, false); - } + unique_lock client_channel_lock(client->channel_lock); + if(client->isClientVisible(target, false)) + client->notifyClientLeftViewKicked(target, nullptr, reason, invoker, false); + } - auto s_channel = dynamic_pointer_cast(target->currentChannel); - s_channel->unregister_client(target); - } + auto s_channel = dynamic_pointer_cast(target->currentChannel); + s_channel->unregister_client(target); + } - /* now disconnect the target itself */ - unique_lock client_channel_lock(target->channel_lock); - target->notifyClientLeftViewKicked(target, nullptr, reason, invoker, false); - target->currentChannel = nullptr; - } + /* now disconnect the target itself */ + unique_lock client_channel_lock(target->channel_lock); + target->notifyClientLeftViewKicked(target, nullptr, reason, invoker, false); + target->currentChannel = nullptr; + } } /* @@ -324,235 +324,235 @@ void TSServer::notify_client_kick( * Note: channel cant be a ref because the channel itself gets deleted! */ void TSServer::delete_channel(shared_ptr channel, const shared_ptr &invoker, const std::string& kick_message, unique_lock &tree_lock) { - if(!tree_lock.owns_lock()) - tree_lock.lock(); - if(channel->deleted) - return; + if(!tree_lock.owns_lock()) + tree_lock.lock(); + if(channel->deleted) + return; - deque> clients; - { - for(const auto& sub_channel : this->channelTree->channels(channel)) { - auto s_channel = dynamic_pointer_cast(sub_channel); - assert(s_channel); + deque> clients; + { + for(const auto& sub_channel : this->channelTree->channels(channel)) { + auto s_channel = dynamic_pointer_cast(sub_channel); + assert(s_channel); - auto chan_clients = this->getClientsByChannel(sub_channel); - clients.insert(clients.end(), chan_clients.begin(), chan_clients.end()); - s_channel->deleted = true; - } - auto chan_clients = this->getClientsByChannel(channel); - clients.insert(clients.end(), chan_clients.begin(), chan_clients.end()); - channel->deleted = true; - } - auto default_channel = this->channelTree->getDefaultChannel(); - tree_lock.unlock(); + auto chan_clients = this->getClientsByChannel(sub_channel); + clients.insert(clients.end(), chan_clients.begin(), chan_clients.end()); + s_channel->deleted = true; + } + auto chan_clients = this->getClientsByChannel(channel); + clients.insert(clients.end(), chan_clients.begin(), chan_clients.end()); + channel->deleted = true; + } + auto default_channel = this->channelTree->getDefaultChannel(); + tree_lock.unlock(); - deque> command_locks; - for(const auto& client : clients) { - command_locks.push_back(move(unique_lock(client->command_lock))); - } + deque> command_locks; + for(const auto& client : clients) { + command_locks.push_back(move(unique_lock(client->command_lock))); + } - for(const auto& client : clients) - this->client_move(client, default_channel, invoker, kick_message, ViewReasonId::VREASON_CHANNEL_KICK, true, tree_lock); + for(const auto& client : clients) + this->client_move(client, default_channel, invoker, kick_message, ViewReasonId::VREASON_CHANNEL_KICK, true, tree_lock); - if(!tree_lock.owns_lock()) - tree_lock.lock(); /* no clients left within that tree */ - command_locks.clear(); + if(!tree_lock.owns_lock()) + tree_lock.lock(); /* no clients left within that tree */ + command_locks.clear(); - auto channel_ids = this->channelTree->delete_channel_root(channel); - this->forEachClient([&](const shared_ptr& client) { - unique_lock client_channel_lock(client->channel_lock); - client->notifyChannelDeleted(client->channels->delete_channel_root(channel), invoker); - }); + auto channel_ids = this->channelTree->delete_channel_root(channel); + this->forEachClient([&](const shared_ptr& client) { + unique_lock client_channel_lock(client->channel_lock); + client->notifyChannelDeleted(client->channels->delete_channel_root(channel), invoker); + }); } void TSServer::client_move( - const shared_ptr &target, - shared_ptr target_channel, - const std::shared_ptr &invoker, - const std::string &reason_message, - ts::ViewReasonId reason_id, + const shared_ptr &target, + shared_ptr target_channel, + const std::shared_ptr &invoker, + const std::string &reason_message, + ts::ViewReasonId reason_id, bool notify_client, std::unique_lock &server_channel_write_lock) { - TIMING_START(timings); - if(server_channel_write_lock.owns_lock()) - server_channel_write_lock.unlock(); + TIMING_START(timings); + if(server_channel_write_lock.owns_lock()) + server_channel_write_lock.unlock(); - lock_guard client_command_lock(target->command_lock); - server_channel_write_lock.lock(); - TIMING_STEP(timings, "chan tree l"); - if(target->currentChannel == target_channel) - return; + lock_guard client_command_lock(target->command_lock); + server_channel_write_lock.lock(); + TIMING_STEP(timings, "chan tree l"); + if(target->currentChannel == target_channel) + return; - /* first step: resolve the target channel / or fix missing */ - auto s_target_channel = dynamic_pointer_cast(target_channel); - auto s_source_channel = dynamic_pointer_cast(target->currentChannel); - assert(!target->currentChannel || s_source_channel != nullptr); + /* first step: resolve the target channel / or fix missing */ + auto s_target_channel = dynamic_pointer_cast(target_channel); + auto s_source_channel = dynamic_pointer_cast(target->currentChannel); + assert(!target->currentChannel || s_source_channel != nullptr); - deque client_updates; - std::deque changed_groups{}; - if(target_channel) { - assert(s_target_channel); - if(s_target_channel->deleted) { - target_channel = this->channelTree->getDefaultChannel(); - s_target_channel = dynamic_pointer_cast(target_channel); - assert(s_target_channel); - } + deque client_updates; + std::deque changed_groups{}; + if(target_channel) { + assert(s_target_channel); + if(s_target_channel->deleted) { + target_channel = this->channelTree->getDefaultChannel(); + s_target_channel = dynamic_pointer_cast(target_channel); + assert(s_target_channel); + } - /* update the group properties here already, so for all enter views we could just send the new props directly */ - changed_groups = this->groups->update_server_group_property(target, true, s_target_channel); - client_updates.insert(client_updates.end(), changed_groups.begin(), changed_groups.end()); //TODO: Only update for clients which have no enter? - } - auto l_target_channel = s_target_channel ? this->channelTree->findLinkedChannel(s_target_channel->channelId()) : nullptr; - auto l_source_channel = s_source_channel ? this->channelTree->findLinkedChannel(s_source_channel->channelId()) : nullptr; - TIMING_STEP(timings, "channel res"); + /* update the group properties here already, so for all enter views we could just send the new props directly */ + changed_groups = this->groups->update_server_group_property(target, true, s_target_channel); + client_updates.insert(client_updates.end(), changed_groups.begin(), changed_groups.end()); //TODO: Only update for clients which have no enter? + } + auto l_target_channel = s_target_channel ? this->channelTree->findLinkedChannel(s_target_channel->channelId()) : nullptr; + auto l_source_channel = s_source_channel ? this->channelTree->findLinkedChannel(s_source_channel->channelId()) : nullptr; + TIMING_STEP(timings, "channel res"); - /* second step: show the target channel to the client if its not shown and let him subscibe to the channel */ - if(target_channel && notify_client) { - unique_lock client_channel_lock(target->channel_lock); + /* second step: show the target channel to the client if its not shown and let him subscibe to the channel */ + if(target_channel && notify_client) { + unique_lock client_channel_lock(target->channel_lock); - bool success = false; - /* TODO: Use a bunk here and not a notify for every single */ - for(const auto& channel : target->channels->show_channel(l_target_channel, success)) - target->notifyChannelShow(channel->channel(), channel->previous_channel); - sassert(success); - if(!success) - return; + bool success = false; + /* TODO: Use a bunk here and not a notify for every single */ + for(const auto& channel : target->channels->show_channel(l_target_channel, success)) + target->notifyChannelShow(channel->channel(), channel->previous_channel); + sassert(success); + if(!success) + return; - target->subscribeChannel({target_channel}, false, true); - } - TIMING_STEP(timings, "target show"); + target->subscribeChannel({target_channel}, false, true); + } + TIMING_STEP(timings, "target show"); - if(target_channel) { - this->forEachClient([&](const shared_ptr& client) { - if (!notify_client && client == target) return; + if(target_channel) { + this->forEachClient([&](const shared_ptr& client) { + if (!notify_client && client == target) return; - unique_lock client_channel_lock(client->channel_lock); - auto chan_target = client->channels->find_channel(target_channel); + unique_lock client_channel_lock(client->channel_lock); + auto chan_target = client->channels->find_channel(target_channel); - if(chan_target) { - auto chan_source = client->channels->find_channel(s_source_channel); - if(chan_source) { - if (chan_target->subscribed || client == target) { - if (client == target || client->isClientVisible(target, false)) { - client->notifyClientMoved(target, s_target_channel, reason_id, reason_message, invoker, false); - } else { - client->notifyClientEnterView(target, invoker, reason_message, s_target_channel, reason_id, s_source_channel, false); - } - } else if(client->isClientVisible(target, false)){ - //Client got out of view - client->notifyClientLeftView(target, s_target_channel, reason_id, reason_message.empty() ? string("view left") : reason_message, invoker, false); - } - } else { - if(client == target && client->getType() != ClientType::CLIENT_INTERNAL && client->getType() != ClientType::CLIENT_MUSIC) - logCritical(this->getServerId(), "{} Client enters visibility twice!", CLIENT_STR_LOG_PREFIX_(client)); + if(chan_target) { + auto chan_source = client->channels->find_channel(s_source_channel); + if(chan_source) { + if (chan_target->subscribed || client == target) { + if (client == target || client->isClientVisible(target, false)) { + client->notifyClientMoved(target, s_target_channel, reason_id, reason_message, invoker, false); + } else { + client->notifyClientEnterView(target, invoker, reason_message, s_target_channel, reason_id, s_source_channel, false); + } + } else if(client->isClientVisible(target, false)){ + //Client got out of view + client->notifyClientLeftView(target, s_target_channel, reason_id, reason_message.empty() ? string("view left") : reason_message, invoker, false); + } + } else { + if(client == target && client->getType() != ClientType::CLIENT_INTERNAL && client->getType() != ClientType::CLIENT_MUSIC) + logCritical(this->getServerId(), "{} Client enters visibility twice!", CLIENT_STR_LOG_PREFIX_(client)); - //Client entered view - if(chan_target->subscribed) - client->notifyClientEnterView(target, invoker, reason_message, s_target_channel, ViewReasonId::VREASON_USER_ACTION, nullptr, false); - } - } else { - /* target channel isn't visible => so client gone out of view */ - if(client == target && client->getType() != ClientType::CLIENT_INTERNAL && client->getType() != ClientType::CLIENT_MUSIC) - logCritical(this->getServerId(), "{} Moving own client into a not visible channel! This shall not happen!", CLIENT_STR_LOG_PREFIX_(client)); - //Test for in view? (Notify already does but nvm) + //Client entered view + if(chan_target->subscribed) + client->notifyClientEnterView(target, invoker, reason_message, s_target_channel, ViewReasonId::VREASON_USER_ACTION, nullptr, false); + } + } else { + /* target channel isn't visible => so client gone out of view */ + if(client == target && client->getType() != ClientType::CLIENT_INTERNAL && client->getType() != ClientType::CLIENT_MUSIC) + logCritical(this->getServerId(), "{} Moving own client into a not visible channel! This shall not happen!", CLIENT_STR_LOG_PREFIX_(client)); + //Test for in view? (Notify already does but nvm) - if(client->isClientVisible(target, false)){ - //Client got out of view - if(reason_id == ViewReasonId::VREASON_USER_ACTION) - client->notifyClientLeftView(target, nullptr, ViewReasonId::VREASON_SERVER_LEFT, reason_message.empty() ? "joined a hidden channel" : reason_message, invoker, false); - else - client->notifyClientLeftView(target, nullptr, ViewReasonId::VREASON_SERVER_LEFT, reason_message.empty() ? "moved to a hidden channel" : reason_message, invoker, false); - } - } - }); + if(client->isClientVisible(target, false)){ + //Client got out of view + if(reason_id == ViewReasonId::VREASON_USER_ACTION) + client->notifyClientLeftView(target, nullptr, ViewReasonId::VREASON_SERVER_LEFT, reason_message.empty() ? "joined a hidden channel" : reason_message, invoker, false); + else + client->notifyClientLeftView(target, nullptr, ViewReasonId::VREASON_SERVER_LEFT, reason_message.empty() ? "moved to a hidden channel" : reason_message, invoker, false); + } + } + }); - if(s_source_channel) - s_source_channel->unregister_client(target); - s_target_channel->register_client(target); - } else { - /* client left the server */ - if(target->currentChannel) { - for(const auto& client : this->getClients()) { - if(!client || client == target) - continue; + if(s_source_channel) + s_source_channel->unregister_client(target); + s_target_channel->register_client(target); + } else { + /* client left the server */ + if(target->currentChannel) { + for(const auto& client : this->getClients()) { + if(!client || client == target) + continue; - unique_lock client_channel_lock(client->channel_lock); - if(client->isClientVisible(target, false)) - client->notifyClientLeftView(target, nullptr, reason_id, reason_message, invoker, false); - } + unique_lock client_channel_lock(client->channel_lock); + if(client->isClientVisible(target, false)) + client->notifyClientLeftView(target, nullptr, reason_id, reason_message, invoker, false); + } - s_source_channel->unregister_client(target); - } - } - TIMING_STEP(timings, "notify view"); + s_source_channel->unregister_client(target); + } + } + TIMING_STEP(timings, "notify view"); - target->currentChannel = target_channel; - server_channel_write_lock.unlock(); + target->currentChannel = target_channel; + server_channel_write_lock.unlock(); - /* third step: update stuff for the client (remember: the client cant execute anything at the moment!) */ - shared_lock server_channel_read_lock(this->channel_tree_lock); - unique_lock client_channel_lock(target->channel_lock); - TIMING_STEP(timings, "lock own tr"); + /* third step: update stuff for the client (remember: the client cant execute anything at the moment!) */ + shared_lock server_channel_read_lock(this->channel_tree_lock); + unique_lock client_channel_lock(target->channel_lock); + TIMING_STEP(timings, "lock own tr"); - if (s_source_channel) { - s_source_channel->properties()[property::CHANNEL_LAST_LEFT] = chrono::duration_cast(chrono::system_clock::now().time_since_epoch()).count(); - auto source_channel_group = this->groups->getChannelGroupExact(target->getClientDatabaseId(), s_source_channel, false); - if(source_channel_group) { - auto default_data = source_channel_group->group->permissions()->permission_value_flagged(permission::b_group_is_permanent); - if(!default_data.has_value || default_data.value != 1) - this->groups->setChannelGroup(target->getClientDatabaseId(), nullptr, s_source_channel); - } + if (s_source_channel) { + s_source_channel->properties()[property::CHANNEL_LAST_LEFT] = chrono::duration_cast(chrono::system_clock::now().time_since_epoch()).count(); + auto source_channel_group = this->groups->getChannelGroupExact(target->getClientDatabaseId(), s_source_channel, false); + if(source_channel_group) { + auto default_data = source_channel_group->group->permissions()->permission_value_flagged(permission::b_group_is_permanent); + if(!default_data.has_value || default_data.value != 1) + this->groups->setChannelGroup(target->getClientDatabaseId(), nullptr, s_source_channel); + } - auto update = target->properties()[property::CLIENT_IS_TALKER].as() || target->properties()[property::CLIENT_TALK_REQUEST].as() > 0; - if(update) { - target->properties()[property::CLIENT_IS_TALKER] = 0; - target->properties()[property::CLIENT_TALK_REQUEST] = 0; - target->properties()[property::CLIENT_TALK_REQUEST_MSG] = ""; - client_updates.push_back(property::CLIENT_IS_TALKER); - client_updates.push_back(property::CLIENT_TALK_REQUEST); - client_updates.push_back(property::CLIENT_TALK_REQUEST_MSG); - } - TIMING_STEP(timings, "src chan up"); - } + auto update = target->properties()[property::CLIENT_IS_TALKER].as() || target->properties()[property::CLIENT_TALK_REQUEST].as() > 0; + if(update) { + target->properties()[property::CLIENT_IS_TALKER] = 0; + target->properties()[property::CLIENT_TALK_REQUEST] = 0; + target->properties()[property::CLIENT_TALK_REQUEST_MSG] = ""; + client_updates.push_back(property::CLIENT_IS_TALKER); + client_updates.push_back(property::CLIENT_TALK_REQUEST); + client_updates.push_back(property::CLIENT_TALK_REQUEST_MSG); + } + TIMING_STEP(timings, "src chan up"); + } - if (s_target_channel) { - if(target->update_cached_permissions()) /* update cached calculated permissions */ - target->sendNeededPermissions(false); - TIMING_STEP(timings, "perm gr upd"); + if (s_target_channel) { + if(target->update_cached_permissions()) /* update cached calculated permissions */ + target->sendNeededPermissions(false); + TIMING_STEP(timings, "perm gr upd"); - if(s_source_channel) { - deque deleted; - for(const auto& channel : target->channels->test_channel(l_source_channel, l_target_channel)) - deleted.push_back(channel->channelId()); + if(s_source_channel) { + deque deleted; + for(const auto& channel : target->channels->test_channel(l_source_channel, l_target_channel)) + deleted.push_back(channel->channelId()); - if(!deleted.empty()) - target->notifyChannelHide(deleted, false); + if(!deleted.empty()) + target->notifyChannelHide(deleted, false); - auto i_source_channel = s_source_channel->channelId(); - if(std::find(deleted.begin(), deleted.end(), i_source_channel) == deleted.end()) { - auto source_channel_sub_power = target->calculate_permission_value(permission::i_channel_subscribe_power, i_source_channel); - if(!s_source_channel->permission_granted(permission::i_channel_needed_subscribe_power, source_channel_sub_power, false)) { - auto source_channel_sub_power_ignore = target->calculate_permission_value(permission::b_channel_ignore_subscribe_power, i_source_channel); - if(!DataClient::permission_granted(source_channel_sub_power_ignore, 1, true)) { - logTrace(this->serverId, "Force unsubscribing of client {} for channel {}/{}. (Channel switch and no permissions)", - CLIENT_STR_LOG_PREFIX_(target), s_source_channel->name(), - i_source_channel - ); - target->unsubscribeChannel({s_source_channel}, false); //Unsubscribe last channel (hasn't permissions) - } - } - } - TIMING_STEP(timings, "src hide ts"); - } - } - client_channel_lock.unlock(); - /* both methods lock if they require stuff */ - this->notifyClientPropertyUpdates(target, client_updates, s_source_channel ? true : false); - TIMING_STEP(timings, "notify cpro"); - if(s_target_channel) { - target->updateChannelClientProperties(false, s_source_channel ? true : false); - TIMING_STEP(timings, "notify_t_pr"); - } - debugMessage(this->getServerId(), "{} Client move timings: {}", CLIENT_STR_LOG_PREFIX_(target), TIMING_FINISH(timings)); + auto i_source_channel = s_source_channel->channelId(); + if(std::find(deleted.begin(), deleted.end(), i_source_channel) == deleted.end()) { + auto source_channel_sub_power = target->calculate_permission_value(permission::i_channel_subscribe_power, i_source_channel); + if(!s_source_channel->permission_granted(permission::i_channel_needed_subscribe_power, source_channel_sub_power, false)) { + auto source_channel_sub_power_ignore = target->calculate_permission_value(permission::b_channel_ignore_subscribe_power, i_source_channel); + if(!DataClient::permission_granted(source_channel_sub_power_ignore, 1, true)) { + logTrace(this->serverId, "Force unsubscribing of client {} for channel {}/{}. (Channel switch and no permissions)", + CLIENT_STR_LOG_PREFIX_(target), s_source_channel->name(), + i_source_channel + ); + target->unsubscribeChannel({s_source_channel}, false); //Unsubscribe last channel (hasn't permissions) + } + } + } + TIMING_STEP(timings, "src hide ts"); + } + } + client_channel_lock.unlock(); + /* both methods lock if they require stuff */ + this->notifyClientPropertyUpdates(target, client_updates, s_source_channel ? true : false); + TIMING_STEP(timings, "notify cpro"); + if(s_target_channel) { + target->updateChannelClientProperties(false, s_source_channel ? true : false); + TIMING_STEP(timings, "notify_t_pr"); + } + debugMessage(this->getServerId(), "{} Client move timings: {}", CLIENT_STR_LOG_PREFIX_(target), TIMING_FINISH(timings)); } \ No newline at end of file diff --git a/server/src/TS3ServerHeartbeat.cpp b/server/src/TS3ServerHeartbeat.cpp index 1c83503..6ca125f 100644 --- a/server/src/TS3ServerHeartbeat.cpp +++ b/server/src/TS3ServerHeartbeat.cpp @@ -20,8 +20,8 @@ inline void banClientFlood(TSServer* server, const shared_ptr& serverInstance->banManager()->registerBan(server->getServerId(), cl->getClientDatabaseId(), reason, cl->getUid(), cl->getLoggingPeerIp(), "", "", until); for(const auto &client : server->findClientsByUid(cl->getUid())) { - server->notify_client_ban(client, server->getServerRoot(), reason, time); - client->closeConnection(system_clock::now() + seconds(1)); + server->notify_client_ban(client, server->getServerRoot(), reason, time); + client->closeConnection(system_clock::now() + seconds(1)); } } @@ -32,256 +32,256 @@ variable = duration_cast(timing_end - timing_begin); void TSServer::executeServerTick() { threads::MutexTryLock l(this->stateLock); //Should not attempt to shutdown or start the server while ticking - if(!l) { - if(this->running()) + if(!l) { + if(this->running()) logError(this->getServerId(), "Failed to lock tick mutex!"); - return; - } + return; + } if(!this->running()) return; - try { - if(lastTick.time_since_epoch().count() != 0) { - auto delay = system_clock::now() - lastTick; - auto delay_ms = duration_cast(delay).count(); - if(delay_ms > 510) { - if(delay_ms < 750) { - logWarning(this->getServerId(), - "Found varianzes within the server tick! (Supposed: 500ms Hold: {}ms)", delay_ms); - } else { - logError(this->getServerId(), - "Found varianzes within the server tick! This long delay could be an issue. (Supposed: 500ms Hold: {}ms)", delay_ms); - } - } - } - lastTick = system_clock::now(); + try { + if(lastTick.time_since_epoch().count() != 0) { + auto delay = system_clock::now() - lastTick; + auto delay_ms = duration_cast(delay).count(); + if(delay_ms > 510) { + if(delay_ms < 750) { + logWarning(this->getServerId(), + "Found varianzes within the server tick! (Supposed: 500ms Hold: {}ms)", delay_ms); + } else { + logError(this->getServerId(), + "Found varianzes within the server tick! This long delay could be an issue. (Supposed: 500ms Hold: {}ms)", delay_ms); + } + } + } + lastTick = system_clock::now(); - system_clock::time_point timing_begin, timing_end; - milliseconds timing_update_states, timing_client_tick, timing_channel, timing_statistic, timing_groups, timing_ccache; + system_clock::time_point timing_begin, timing_end; + milliseconds timing_update_states, timing_client_tick, timing_channel, timing_statistic, timing_groups, timing_ccache; - auto client_list = this->getClients(); + auto client_list = this->getClients(); - { - BEGIN_TIMINGS(); + { + BEGIN_TIMINGS(); - size_t clientOnline = 0; - size_t queryOnline = 0; - for(const auto& conn : client_list){ - switch (conn->getType()){ - case ClientType::CLIENT_TEAMSPEAK: - case ClientType::CLIENT_TEASPEAK: - case ClientType::CLIENT_WEB: - clientOnline++; - break; - case ClientType::CLIENT_QUERY: - case ClientType::CLIENT_MUSIC: - queryOnline++; - break; - default: - break; - } - } + size_t clientOnline = 0; + size_t queryOnline = 0; + for(const auto& conn : client_list){ + switch (conn->getType()){ + case ClientType::CLIENT_TEAMSPEAK: + case ClientType::CLIENT_TEASPEAK: + case ClientType::CLIENT_WEB: + clientOnline++; + break; + case ClientType::CLIENT_QUERY: + case ClientType::CLIENT_MUSIC: + queryOnline++; + break; + default: + break; + } + } - properties()[property::VIRTUALSERVER_UPTIME] = std::chrono::duration_cast(std::chrono::system_clock::now() - this->startTimestamp).count(); - properties()[property::VIRTUALSERVER_CLIENTS_ONLINE] = clientOnline + queryOnline; - properties()[property::VIRTUALSERVER_QUERYCLIENTS_ONLINE] = queryOnline; - if(clientOnline + queryOnline == 0) //We don't need to tick, when server is empty! - return; - properties()[property::VIRTUALSERVER_CHANNELS_ONLINE] = this->channelTree->channel_count(); - properties()[property::VIRTUALSERVER_TOTAL_PING] = this->averagePing(); + properties()[property::VIRTUALSERVER_UPTIME] = std::chrono::duration_cast(std::chrono::system_clock::now() - this->startTimestamp).count(); + properties()[property::VIRTUALSERVER_CLIENTS_ONLINE] = clientOnline + queryOnline; + properties()[property::VIRTUALSERVER_QUERYCLIENTS_ONLINE] = queryOnline; + if(clientOnline + queryOnline == 0) //We don't need to tick, when server is empty! + return; + properties()[property::VIRTUALSERVER_CHANNELS_ONLINE] = this->channelTree->channel_count(); + properties()[property::VIRTUALSERVER_TOTAL_PING] = this->averagePing(); - END_TIMINGS(timing_update_states); - } - - { - BEGIN_TIMINGS(); + END_TIMINGS(timing_update_states); + } + + { + BEGIN_TIMINGS(); - auto flood_decrease = this->properties()[property::VIRTUALSERVER_ANTIFLOOD_POINTS_TICK_REDUCE].as(); - auto flood_block = this->properties()[property::VIRTUALSERVER_ANTIFLOOD_POINTS_NEEDED_IP_BLOCK].as(); + auto flood_decrease = this->properties()[property::VIRTUALSERVER_ANTIFLOOD_POINTS_TICK_REDUCE].as(); + auto flood_block = this->properties()[property::VIRTUALSERVER_ANTIFLOOD_POINTS_NEEDED_IP_BLOCK].as(); - bool flag_update_spoken = this->spoken_time_timestamp + seconds(30) < system_clock::now(); + bool flag_update_spoken = this->spoken_time_timestamp + seconds(30) < system_clock::now(); - system_clock::time_point tick_client_begin, tick_client_end = system_clock::now(); - for(const auto& cl : client_list) { - tick_client_begin = tick_client_end; - if(cl->server != this) { - logError(this->getServerId(), "Got registered client, but client does not think hes bound to this server!"); + system_clock::time_point tick_client_begin, tick_client_end = system_clock::now(); + for(const auto& cl : client_list) { + tick_client_begin = tick_client_end; + if(cl->server != this) { + logError(this->getServerId(), "Got registered client, but client does not think hes bound to this server!"); - { - lock_guard lock(this->clients.lock); - for(auto& client : this->clients.clients) { - if(client != cl) continue; + { + lock_guard lock(this->clients.lock); + for(auto& client : this->clients.clients) { + if(client != cl) continue; - client.reset(); - this->clients.count--; - break; - } - } - continue; //Fully ha? - } - if(cl->floodPoints > flood_block){ - if(!cl->ignoresFlood()) { - banClientFlood(this, cl, system_clock::now() + minutes(10)); - continue; - } - } - if(cl->floodPoints > flood_decrease) - cl->floodPoints -= flood_decrease; - else cl->floodPoints = 0; - cl->tick(tick_client_end); - auto voice = dynamic_pointer_cast(cl); - if(flag_update_spoken && voice) - this->spoken_time += voice->takeSpokenTime(); - tick_client_end = system_clock::now(); + client.reset(); + this->clients.count--; + break; + } + } + continue; //Fully ha? + } + if(cl->floodPoints > flood_block){ + if(!cl->ignoresFlood()) { + banClientFlood(this, cl, system_clock::now() + minutes(10)); + continue; + } + } + if(cl->floodPoints > flood_decrease) + cl->floodPoints -= flood_decrease; + else cl->floodPoints = 0; + cl->tick(tick_client_end); + auto voice = dynamic_pointer_cast(cl); + if(flag_update_spoken && voice) + this->spoken_time += voice->takeSpokenTime(); + tick_client_end = system_clock::now(); - auto passed_time = tick_client_end - tick_client_begin; - if(passed_time > microseconds(2500)) { - if(passed_time > milliseconds(10)) { - logError(this->serverId, "Ticking of client {:1} ({:2}) needs more that 2500 microseconds! ({:3} microseconds)", - cl->getLoggingPeerIp() + ":" + to_string(cl->getPeerPort()), - cl->getDisplayName(), - duration_cast(tick_client_end - tick_client_begin).count() - ); - } else { - logWarning(this->serverId, "Ticking of client {:1} ({:2}) needs more that 2500 microseconds! ({:3} microseconds)", - cl->getLoggingPeerIp() + ":" + to_string(cl->getPeerPort()), - cl->getDisplayName(), - duration_cast(tick_client_end - tick_client_begin).count() - ); - } - } + auto passed_time = tick_client_end - tick_client_begin; + if(passed_time > microseconds(2500)) { + if(passed_time > milliseconds(10)) { + logError(this->serverId, "Ticking of client {:1} ({:2}) needs more that 2500 microseconds! ({:3} microseconds)", + cl->getLoggingPeerIp() + ":" + to_string(cl->getPeerPort()), + cl->getDisplayName(), + duration_cast(tick_client_end - tick_client_begin).count() + ); + } else { + logWarning(this->serverId, "Ticking of client {:1} ({:2}) needs more that 2500 microseconds! ({:3} microseconds)", + cl->getLoggingPeerIp() + ":" + to_string(cl->getPeerPort()), + cl->getDisplayName(), + duration_cast(tick_client_end - tick_client_begin).count() + ); + } + } - if(cl->clientPermissions->require_db_updates()) { - auto begin = system_clock::now(); - serverInstance->databaseHelper()->saveClientPermissions(this->ref(), cl->getClientDatabaseId(), cl->clientPermissions); - auto end = system_clock::now(); - debugMessage(this->serverId, "Saved client permissions for client {} ({}) in {}ms", cl->getClientDatabaseId(), cl->getDisplayName(), duration_cast(end - begin).count()); - } - } - if(flag_update_spoken) - this->spoken_time_timestamp = system_clock::now(); + if(cl->clientPermissions->require_db_updates()) { + auto begin = system_clock::now(); + serverInstance->databaseHelper()->saveClientPermissions(this->ref(), cl->getClientDatabaseId(), cl->clientPermissions); + auto end = system_clock::now(); + debugMessage(this->serverId, "Saved client permissions for client {} ({}) in {}ms", cl->getClientDatabaseId(), cl->getDisplayName(), duration_cast(end - begin).count()); + } + } + if(flag_update_spoken) + this->spoken_time_timestamp = system_clock::now(); - END_TIMINGS(timing_client_tick); - } + END_TIMINGS(timing_client_tick); + } - { - BEGIN_TIMINGS(); + { + BEGIN_TIMINGS(); - unique_lock channel_lock(this->channel_tree_lock); - auto channels = this->channelTree->channels(); - channel_lock.unlock(); + unique_lock channel_lock(this->channel_tree_lock); + auto channels = this->channelTree->channels(); + channel_lock.unlock(); - for(const auto& channel : this->channelTree->channels()){ - if(channel->channelType() == ChannelType::temporary) { - auto server_channel = dynamic_pointer_cast(channel); - assert(server_channel); - if(server_channel->client_count() > 0 || !this->getClientsByChannelRoot(channel, true).empty()) - continue; + for(const auto& channel : this->channelTree->channels()){ + if(channel->channelType() == ChannelType::temporary) { + auto server_channel = dynamic_pointer_cast(channel); + assert(server_channel); + if(server_channel->client_count() > 0 || !this->getClientsByChannelRoot(channel, true).empty()) + continue; - seconds deleteTimeout(0); - if(channel->properties().hasProperty(property::CHANNEL_DELETE_DELAY)) - deleteTimeout = seconds(channel->properties()[property::CHANNEL_DELETE_DELAY].as()); + seconds deleteTimeout(0); + if(channel->properties().hasProperty(property::CHANNEL_DELETE_DELAY)) + deleteTimeout = seconds(channel->properties()[property::CHANNEL_DELETE_DELAY].as()); - auto last_left = time_point() + milliseconds(channel->properties()[property::CHANNEL_LAST_LEFT].as()); - auto channel_created = channel->createdTimestamp(); + auto last_left = time_point() + milliseconds(channel->properties()[property::CHANNEL_LAST_LEFT].as()); + auto channel_created = channel->createdTimestamp(); - if(system_clock::now() - last_left < deleteTimeout) continue; //One second stay - if(system_clock::now() - channel_created < deleteTimeout + seconds(1)) continue; //One second stay + if(system_clock::now() - last_left < deleteTimeout) continue; //One second stay + if(system_clock::now() - channel_created < deleteTimeout + seconds(1)) continue; //One second stay - this->delete_channel(server_channel, this->serverRoot, "temporary autodelete", channel_lock); - if(channel_lock.owns_lock()) - channel_lock.unlock(); - } - { - auto permission_manager = channel->permissions(); - if(permission_manager->require_db_updates()) { - auto begin = system_clock::now(); - serverInstance->databaseHelper()->saveChannelPermissions(this->ref(), channel->channelId(), permission_manager); - auto end = system_clock::now(); - debugMessage(this->serverId, "Saved channel permissions for channel {} ({}) in {}ms", channel->channelId(), channel->name(), duration_cast(end - begin).count()); - } - } - } + this->delete_channel(server_channel, this->serverRoot, "temporary autodelete", channel_lock); + if(channel_lock.owns_lock()) + channel_lock.unlock(); + } + { + auto permission_manager = channel->permissions(); + if(permission_manager->require_db_updates()) { + auto begin = system_clock::now(); + serverInstance->databaseHelper()->saveChannelPermissions(this->ref(), channel->channelId(), permission_manager); + auto end = system_clock::now(); + debugMessage(this->serverId, "Saved channel permissions for channel {} ({}) in {}ms", channel->channelId(), channel->name(), duration_cast(end - begin).count()); + } + } + } - END_TIMINGS(timing_channel); - } + END_TIMINGS(timing_channel); + } - { - BEGIN_TIMINGS(); + { + BEGIN_TIMINGS(); - this->serverStatistics->tick(); + this->serverStatistics->tick(); - if(fileStatisticsTimestamp + seconds(5) < system_clock::now()) { - fileStatisticsTimestamp = system_clock::now(); - auto update = this->serverStatistics->mark_file_bytes(); - if(update.first > 0) { - this->properties()[property::VIRTUALSERVER_MONTH_BYTES_DOWNLOADED] += update.first; - this->properties()[property::VIRTUALSERVER_TOTAL_BYTES_DOWNLOADED] += update.first; - } - if(update.second > 0) { - this->properties()[property::VIRTUALSERVER_MONTH_BYTES_UPLOADED] += update.second; - this->properties()[property::VIRTUALSERVER_TOTAL_BYTES_UPLOADED] += update.second; - } - } + if(fileStatisticsTimestamp + seconds(5) < system_clock::now()) { + fileStatisticsTimestamp = system_clock::now(); + auto update = this->serverStatistics->mark_file_bytes(); + if(update.first > 0) { + this->properties()[property::VIRTUALSERVER_MONTH_BYTES_DOWNLOADED] += update.first; + this->properties()[property::VIRTUALSERVER_TOTAL_BYTES_DOWNLOADED] += update.first; + } + if(update.second > 0) { + this->properties()[property::VIRTUALSERVER_MONTH_BYTES_UPLOADED] += update.second; + this->properties()[property::VIRTUALSERVER_TOTAL_BYTES_UPLOADED] += update.second; + } + } - { - lock_guard lock(this->join_attempts_lock); - if(system_clock::now() > this->join_last_decrease + seconds(5)) { - for(auto& elm : this->join_attempts) - if(elm.second > 0) elm.second--; + { + lock_guard lock(this->join_attempts_lock); + if(system_clock::now() > this->join_last_decrease + seconds(5)) { + for(auto& elm : this->join_attempts) + if(elm.second > 0) elm.second--; - auto copy = this->join_attempts; - for(const auto& elm : copy) - if(elm.second == 0){ - auto found = find(this->join_attempts.begin(), this->join_attempts.end(), elm); - if(found != this->join_attempts.end()) this->join_attempts.erase(found); - } - this->join_last_decrease = system_clock::now(); - } - } - END_TIMINGS(timing_statistic); - } + auto copy = this->join_attempts; + for(const auto& elm : copy) + if(elm.second == 0){ + auto found = find(this->join_attempts.begin(), this->join_attempts.end(), elm); + if(found != this->join_attempts.end()) this->join_attempts.erase(found); + } + this->join_last_decrease = system_clock::now(); + } + } + END_TIMINGS(timing_statistic); + } - { - BEGIN_TIMINGS(); + { + BEGIN_TIMINGS(); - auto groups = this->getGroupManager()->availableGroups(false); - for(auto& group : groups) { - auto permissions = group->permissions(); - if(permissions->require_db_updates()) { - auto begin = system_clock::now(); - serverInstance->databaseHelper()->saveGroupPermissions(this->ref(), group->groupId(), permissions); - auto end = system_clock::now(); - debugMessage(this->serverId, "Saved group permissions for group {} ({}) in {}ms", group->groupId(), group->name(), duration_cast(end - begin).count()); - } - } + auto groups = this->getGroupManager()->availableGroups(false); + for(auto& group : groups) { + auto permissions = group->permissions(); + if(permissions->require_db_updates()) { + auto begin = system_clock::now(); + serverInstance->databaseHelper()->saveGroupPermissions(this->ref(), group->groupId(), permissions); + auto end = system_clock::now(); + debugMessage(this->serverId, "Saved group permissions for group {} ({}) in {}ms", group->groupId(), group->name(), duration_cast(end - begin).count()); + } + } - END_TIMINGS(timing_groups); - } + END_TIMINGS(timing_groups); + } - { - BEGIN_TIMINGS(); - if(this->conversation_cache_cleanup_timestamp + minutes(15) < system_clock::now()) { - debugMessage(this->serverId, "Cleaning up conversation cache."); - this->_conversation_manager->cleanup_cache(); - conversation_cache_cleanup_timestamp = system_clock::now(); - } - END_TIMINGS(timing_ccache); - } + { + BEGIN_TIMINGS(); + if(this->conversation_cache_cleanup_timestamp + minutes(15) < system_clock::now()) { + debugMessage(this->serverId, "Cleaning up conversation cache."); + this->_conversation_manager->cleanup_cache(); + conversation_cache_cleanup_timestamp = system_clock::now(); + } + END_TIMINGS(timing_ccache); + } - if(system_clock::now() - lastTick > milliseconds(100)) { - //milliseconds timing_update_states, timing_client_tick, timing_channel, timing_statistic; - logError(this->serverId, "Server tick tooks to long ({}ms => Status updates: {}ms Client tick: {}ms, Channel tick: {}ms, Statistic tick: {}ms, Groups: {}ms, Conversation cache: {}ms)", - duration_cast(system_clock::now() - lastTick).count(), - timing_update_states.count(), - timing_client_tick.count(), - timing_channel.count(), - timing_statistic.count(), - timing_groups.count(), - timing_ccache.count() - ); - } - } catch (std::exception& ex) { - logCritical(this->serverId, "Failed to tick server! Got exception message: {}", ex.what()); - } + if(system_clock::now() - lastTick > milliseconds(100)) { + //milliseconds timing_update_states, timing_client_tick, timing_channel, timing_statistic; + logError(this->serverId, "Server tick tooks to long ({}ms => Status updates: {}ms Client tick: {}ms, Channel tick: {}ms, Statistic tick: {}ms, Groups: {}ms, Conversation cache: {}ms)", + duration_cast(system_clock::now() - lastTick).count(), + timing_update_states.count(), + timing_client_tick.count(), + timing_channel.count(), + timing_statistic.count(), + timing_groups.count(), + timing_ccache.count() + ); + } + } catch (std::exception& ex) { + logCritical(this->serverId, "Failed to tick server! Got exception message: {}", ex.what()); + } } \ No newline at end of file diff --git a/server/src/TSServer.cpp b/server/src/TSServer.cpp index 5177f47..95e9200 100644 --- a/server/src/TSServer.cpp +++ b/server/src/TSServer.cpp @@ -40,200 +40,200 @@ using namespace ts::buffer; extern ts::server::InstanceHandler* serverInstance; TSServer::TSServer(uint16_t serverId, sql::SqlManager* database) : serverId(serverId), sql(database) { - memtrack::allocated(this); + memtrack::allocated(this); } bool TSServer::initialize(bool test_properties) { - assert(self.lock()); + 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 = 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]; + 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] = ""; - } - } + 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; - } + 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))); - } + 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); + this->_conversation_manager = make_shared(this->ref()); + this->_conversation_manager->initialize(this->_conversation_manager); - channelTree = new ServerChannelTree(self.lock(), this->sql); - channelTree->loadChannelsFromDatabase(); + 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; - } + 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()); + 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; + 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->tokenManager = new token::TokenManager(this); + this->tokenManager->loadTokens(); - this->complains = new ComplainManager(this); - if(!this->complains->loadComplains()) logError(this->serverId, "Could not load complains"); + 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(); + //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); + letters = new letter::LetterManager(this); - serverStatistics = make_shared(serverInstance->getStatistics(), true); + 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->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 */ + 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()); + 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(); + 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; - } + 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."); - } - } + 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()); + 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; + /* lets cleanup the conversations for not existent channels */ + this->_conversation_manager->synchronize_channels(); + return true; } TSServer::~TSServer() { - memtrack::freed(this); + memtrack::freed(this); delete this->tokenManager; delete this->groups; delete this->channelTree; @@ -246,47 +246,47 @@ TSServer::~TSServer() { } 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; + 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; + 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; + 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; + 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 TSServer::start(std::string& error) { @@ -296,25 +296,25 @@ bool TSServer::start(std::string& error) { error = "Server isn't offline"; return false; } - this->state = ServerState::BOOTING; + this->state = ServerState::BOOTING; } - this->serverRoot->server = self.lock(); - this->serverAdmin->server = self.lock(); + this->serverRoot->server = self.lock(); + this->serverAdmin->server = self.lock(); { //Client delete after server stop/start - lock_guard lock(this->clients.lock); + 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(); - } - } + 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(config::binding::enforce_default_voice_host) + host = config::binding::DefaultVoiceHost; if(host.empty()){ error = "invalid host (\"" + host + "\")"; @@ -327,42 +327,42 @@ bool TSServer::start(std::string& error) { 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; - } + 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 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); + 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; - } + error = "failed to resole any host!"; + this->stop("failed to start"); + return false; + } //Setup voice server udpVoiceServer = make_shared(self.lock()); @@ -375,34 +375,34 @@ bool TSServer::start(std::string& error) { 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(); + 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(); + 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; + 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)); + 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); - } + entry->file_descriptor = -1; + entry->event_accept = nullptr; + bindings.push_back(entry); + } - logMessage(this->serverId, "[Web] Starting server on {}:{}", web_host_string, web_port); + 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"); @@ -412,13 +412,13 @@ bool TSServer::start(std::string& error) { } //Startup ticking - serverInstance->executeTick(this); + 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_QUERYCLIENTS_ONLINE] = 0; properties()[property::VIRTUALSERVER_CHANNELS_ONLINE] = 0; properties()[property::VIRTUALSERVER_UPTIME] = 0; this->startTimestamp = system_clock::now(); @@ -426,18 +426,18 @@ bool TSServer::start(std::string& error) { this->musicManager->cleanup_semi_bots(); this->musicManager->connectBots(); - { - threads::MutexLock lock(this->stateLock); - this->state = ServerState::ONLINE; - } + { + threads::MutexLock lock(this->stateLock); + this->state = ServerState::ONLINE; + } return true; } std::string TSServer::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)); + 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 TSServer::running() { @@ -445,51 +445,51 @@ bool TSServer::running() { } void TSServer::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 TSServer::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->state = ServerState::SUSPENDING; } - this->preStop(reason); + 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 TSServer::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->closeConnection(chrono::system_clock::now() + chrono::seconds(1)); + cl->closeConnection(chrono::system_clock::now() + chrono::seconds(1)); } else if(cl->getType() == CLIENT_QUERY){ - threads::MutexLock lock(cl->command_lock); - cl->currentChannel = nullptr; + threads::MutexLock lock(cl->command_lock); + cl->currentChannel = nullptr; - continue; //We dont need to disconnect the query + continue; //We dont need to disconnect the query cl->server = nullptr; cl->loadDataForCurrentServer(); } else if(cl->getType() == CLIENT_MUSIC) { - cl->disconnect(""); - cl->currentChannel = nullptr; + cl->disconnect(""); + cl->currentChannel = nullptr; } else if(cl->getType() == CLIENT_INTERNAL) { } else { @@ -509,38 +509,38 @@ void TSServer::stop(const std::string& reason) { this->webControlServer = nullptr; #endif - { - auto list = serverInstance->getWebList(); - if(list) - list->disable_report(self_lock); - } + { + auto list = serverInstance->getWebList(); + if(list) + list->disable_report(self_lock); + } if(this->groups) { - this->groups->clearCache(); + 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; + 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; + { + threads::MutexLock lock(this->stateLock); + this->state = ServerState::OFFLINE; + } + this->serverRoot->server = nullptr; + this->serverAdmin->server = nullptr; } size_t TSServer::onlineClients() { - size_t result = 0; + size_t result = 0; - lock_guard lock(this->clients.lock); + 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++; + if(!cl) + continue; + if(cl->getType() == CLIENT_TEAMSPEAK || cl->getType() == CLIENT_QUERY) + result++; } return result; } @@ -549,26 +549,26 @@ OnlineClientReport TSServer::onlineStats() { OnlineClientReport response{}; { - lock_guard lock(this->clients.lock); + lock_guard lock(this->clients.lock); for(const auto &cl : this->clients.clients) { - if(!cl) continue; + 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; - } + 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; + } } } @@ -576,43 +576,43 @@ OnlineClientReport TSServer::onlineStats() { } std::shared_ptr TSServer::findClient(sockaddr_in *addr) { - lock_guard lock(this->clients.lock); + lock_guard lock(this->clients.lock); for(const auto& elm : this->clients.clients) { - if(elm && elm->isAddressV4()) - if(elm->getAddressV4()->sin_addr.s_addr == addr->sin_addr.s_addr) - if(elm->getAddressV4()->sin_port == addr->sin_port) - return elm; + if(elm && elm->isAddressV4()) + if(elm->getAddressV4()->sin_addr.s_addr == addr->sin_addr.s_addr) + if(elm->getAddressV4()->sin_port == addr->sin_port) + return elm; } return nullptr; } std::shared_ptr TSServer::findClient(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; + lock_guard lock(this->clients.lock); + if(this->clients.clients.size() > client_id) + return this->clients.clients[client_id]; + else + return nullptr; } deque> TSServer::findClientsByCldbId(uint64_t cldbId) { - deque> result; + deque> result; - lock_guard lock(this->clients.lock); - for(const auto &client : this->clients.clients) { - if(!client) continue; + 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; + if(client->getClientDatabaseId() == cldbId) + result.push_back(client); + } + return result; } deque> TSServer::findClientsByUid(std::string uid) { - lock_guard lock(this->clients.lock); + lock_guard lock(this->clients.lock); - deque> result; + deque> result; for(const auto &client : this->clients.clients) { - if(!client) continue; + if(!client) continue; if(client->getUid() == uid) { result.push_back(client); @@ -623,53 +623,53 @@ deque> TSServer::findClientsByUid(std::string uid) { std::shared_ptr TSServer::findClient(std::string name, bool ignoreCase) { if(ignoreCase) { - std::transform(name.begin(), name.end(), name.begin(), ::tolower); + std::transform(name.begin(), name.end(), name.begin(), ::tolower); } - { + { - lock_guard lock(this->clients.lock); + lock_guard lock(this->clients.lock); - for(const auto& client : this->clients.clients) { - if(!client) continue; + 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); - } + string clName = client->getDisplayName(); + if(ignoreCase) { + std::transform(clName.begin(), clName.end(), clName.begin(), ::tolower); + } - if(clName == name) - return client; - } - } + if(clName == name) + return client; + } + } return nullptr; } bool TSServer::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); - } + 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> TSServer::getClients() { - vector> clients; + vector> clients; - { - lock_guard lock(this->clients.lock); - clients.reserve(this->clients.count); + { + lock_guard lock(this->clients.lock); + clients.reserve(this->clients.count); - for(auto& client : this->clients.clients) { - if(!client) continue; - clients.push_back(client); - } - } + for(auto& client : this->clients.clients) { + if(!client) continue; + clients.push_back(client); + } + } - return clients; + return clients; } deque> TSServer::getClientsByChannel(std::shared_ptr channel) { @@ -680,32 +680,32 @@ deque> TSServer::getClientsByChannel(std::shared_ptr shared_lock client_lock(s_channel->client_lock); auto weak_clients = s_channel->clients; - client_lock.unlock(); + 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 */ + 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)); + result.push_back(move(client)); } return result; } deque> TSServer::getClientsByChannelRoot(const std::shared_ptr &root, bool lock) { - assert(this); + assert(this); - shared_lock channel_lock(this->channel_tree_lock, defer_lock); - if(lock) - channel_lock.lock(); + 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()); - } + 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; } @@ -720,12 +720,12 @@ bool TSServer::notifyServerEdited(std::shared_ptr invoker, dequ 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(); + 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); @@ -736,7 +736,7 @@ bool TSServer::notifyServerEdited(std::shared_ptr invoker, dequ bool TSServer::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); + shared_lock client_channel_lock(client->channel_lock); if(cl->isClientVisible(client, false) || (cl == client && selfNotify)) cl->notifyClientUpdated(client, keys, false); }); @@ -772,254 +772,254 @@ std::shared_ptr CalculateCache::getChannelAssignment(TSServer* } std::shared_ptr CalculateCache::getServerChannel(ts::server::TSServer *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; + 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; + return permission::i_icon_id == type; } vector> TSServer::calculatePermissions2( - ClientDbId client_dbid, - const std::deque& permissions, - ClientType client_type, - ChannelId channel_id, - bool calculate_granted, - std::shared_ptr cache) { - if(permissions.empty()) return {}; + ClientDbId client_dbid, + const std::deque& permissions, + ClientType client_type, + ChannelId channel_id, + bool calculate_granted, + std::shared_ptr cache) { + if(permissions.empty()) return {}; - vector> result; - result.reserve(permissions.size()); + vector> result; + result.reserve(permissions.size()); - if(!cache) { - cache = make_shared(); - } + if(!cache) { + cache = make_shared(); + } - if(!cache->client_permissions) { - cache->client_permissions = serverInstance->databaseHelper()->loadClientPermissionManager(self.lock(), client_dbid); - } + 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_permission = false; + int skip_permission_type = -1; /* -1 := unset | 0 := skip, not explicit | 1 := skip, explicit */ - bool have_skip; + 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; + /* + * 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; - } - } - } - } - }; + /* 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 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 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; + 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 */ + //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 */ + 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; - } - } + 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; - } - } - } - }; + 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; - } + 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; - } - } + 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); - } - } + 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); + 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; - } - } - } + 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; - } - } - } - } + /* 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(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; - } + 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}}); - } + 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; + return result; } deque> TSServer::calculatePermissions( - permission::PermissionTestType test_type, - ClientDbId client_dbid, - const std::deque& permissions, - ClientType client_type, + permission::PermissionTestType test_type, + ClientDbId client_dbid, + const std::deque& permissions, + ClientType client_type, const std::shared_ptr &channel, std::shared_ptr cache) { - if(permissions.empty()) return {}; + if(permissions.empty()) return {}; - auto data = calculatePermissions2(client_dbid, permissions, client_type, channel ? channel->channelId() : 0, false, cache); - deque> result; - for(auto& entry : data) - result.emplace_back(entry.first, entry.second.has_value ? entry.second.value : permNotGranted); - return result; + auto data = calculatePermissions2(client_dbid, permissions, client_type, channel ? channel->channelId() : 0, false, cache); + deque> result; + for(auto& entry : data) + result.emplace_back(entry.first, entry.second.has_value ? entry.second.value : permNotGranted); + return result; } permission::v2::PermissionFlaggedValue TSServer::calculatePermission2(ts::permission::PermissionType permission, ts::ClientDbId cldbid, ts::server::ClientType type, ts::ChannelId channel, std::shared_ptr cache) { - auto result = this->calculatePermissions2(cldbid, {permission}, type, channel, false, cache); - if(result.empty()) return {permNotGranted, false}; - return result.front().second; + auto result = this->calculatePermissions2(cldbid, {permission}, type, channel, false, cache); + if(result.empty()) return {permNotGranted, false}; + return result.front().second; } ts::permission::PermissionValue TSServer::calculatePermission(permission::PermissionTestType test, ClientDbId cldbid, permission::PermissionType permission, ClientType client_type, const std::shared_ptr& channel, std::shared_ptr cache) { @@ -1029,9 +1029,9 @@ ts::permission::PermissionValue TSServer::calculatePermission(permission::Permis } ts::permission::PermissionValue TSServer::calculatePermissionGrant(permission::PermissionTestType test, ClientDbId cldbid, permission::PermissionType permission, ClientType client_type, const std::shared_ptr& channel) { - auto result = this->calculatePermissions2(cldbid, {permission}, client_type, channel ? channel->channelId() : 0, true, nullptr); - assert(!result.empty()); - return result[0].second.has_value ? result[0].second.value : permNotGranted; + auto result = this->calculatePermissions2(cldbid, {permission}, client_type, channel ? channel->channelId() : 0, true, nullptr); + assert(!result.empty()); + return result[0].second.has_value ? result[0].second.value : permNotGranted; } bool TSServer::verifyServerPassword(std::string password, bool hashed) { @@ -1057,150 +1057,150 @@ float TSServer::averagePing() { 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(); - } else if(type == ClientType::CLIENT_WEB) { - count++; - sum += duration_cast(dynamic_pointer_cast(client)->client_ping()).count(); - } + auto type = client->getType(); + if(type == ClientType::CLIENT_TEAMSPEAK || type == ClientType::CLIENT_TEASPEAK) { + count++; + sum += duration_cast(dynamic_pointer_cast(client)->calculatePing()).count(); + } else if(type == ClientType::CLIENT_WEB) { + count++; + sum += duration_cast(dynamic_pointer_cast(client)->client_ping()).count(); + } }); - if(count == 0) return 0; + if(count == 0) return 0; return sum / count; } bool TSServer::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()); + 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; + { + 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); - } - } + 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()); + //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()); + 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()); + 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_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_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); - } + 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_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; - } + 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(); + 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(); + 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) { + 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; + } + 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 TSServer::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()); + 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(); - } + 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()); + 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(); - } + 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()); + 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(); - } + 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()); + 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(); - } + 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(); + } } \ No newline at end of file diff --git a/server/src/TSServer.h b/server/src/TSServer.h index 5b99a6c..530987e 100644 --- a/server/src/TSServer.h +++ b/server/src/TSServer.h @@ -65,39 +65,39 @@ namespace ts { namespace conversation { - class ConversationManager; + class ConversationManager; } struct ServerState { - enum value { - OFFLINE, - BOOTING, - ONLINE, - SUSPENDING, - DELETING - }; + enum value { + OFFLINE, + BOOTING, + ONLINE, + SUSPENDING, + DELETING + }; - inline static std::string string(value state) { - switch (state) { - case ServerState::OFFLINE: - return "offline"; - case ServerState::BOOTING: - return "booting"; - case ServerState::ONLINE: - return "online"; - case ServerState::SUSPENDING: - return "suspending"; - case ServerState::DELETING: - return "deleting"; - default: - return "unknown"; - } - } + inline static std::string string(value state) { + switch (state) { + case ServerState::OFFLINE: + return "offline"; + case ServerState::BOOTING: + return "booting"; + case ServerState::ONLINE: + return "online"; + case ServerState::SUSPENDING: + return "suspending"; + case ServerState::DELETING: + return "deleting"; + default: + return "unknown"; + } + } }; struct OnlineClientReport { uint16_t clients_ts = 0; - uint16_t clients_web = 0; + uint16_t clients_web = 0; uint16_t queries = 0; uint16_t bots = 0; }; @@ -110,12 +110,12 @@ namespace ts { std::vector> assignment_server_groups; bool assignment_server_groups_set = false; - ChannelId assignment_channel_group_channel; + ChannelId assignment_channel_group_channel; std::shared_ptr assignment_channel_group; bool assignment_channel_group_set = false; std::shared_ptr server_channel; - ChannelId last_server_channel = 0; + ChannelId last_server_channel = 0; inline std::vector> getGroupAssignments(TSServer* server, ClientDbId cldbid, ClientType type); inline std::shared_ptr getChannelAssignment(TSServer* server, ClientDbId client_dbid, ChannelId channel); @@ -126,7 +126,7 @@ namespace ts { friend class WebClient; friend class DataClient; friend class VoiceClient; - friend class MusicClient; + friend class MusicClient; friend class ConnectedClient; friend class InternalClient; friend class QueryServer; @@ -134,12 +134,12 @@ namespace ts { friend class SpeakingClient; friend class music::MusicBotManager; friend class InstanceHandler; - friend class ServerManager; + friend class ServerManager; public: TSServer(ServerId serverId, sql::SqlManager*); ~TSServer(); - bool initialize(bool test_properties); + bool initialize(bool test_properties); bool start(std::string& error); bool running(); @@ -147,7 +147,7 @@ namespace ts { void stop(const std::string& reason = ts::config::messages::serverStopped); size_t onlineClients(); - OnlineClientReport onlineStats(); + OnlineClientReport onlineStats(); size_t onlineChannels(){ return this->channelTree->channel_count(); } std::shared_ptr findClient(sockaddr_in* addr); std::shared_ptr findClient(ClientId clientId); @@ -157,7 +157,7 @@ namespace ts { bool forEachClient(std::function)>); //bool forEachClient(std::function>, bool executeLaterIfLocked = true); - std::vector> getClients(); + std::vector> getClients(); std::deque> getClientsByChannel(std::shared_ptr); std::deque> getClientsByChannelRoot(const std::shared_ptr &, bool lock_channel_tree); @@ -185,7 +185,7 @@ namespace ts { bool notifyServerEdited(std::shared_ptr, std::deque keys); bool notifyClientPropertyUpdates(std::shared_ptr, const std::deque>& keys, bool selfNotify = true); /* execute only with at least channel tree read lock! */ inline bool notifyClientPropertyUpdates(const std::shared_ptr& client, const std::deque& keys, bool selfNotify = true) { - if(keys.empty()) return false; + if(keys.empty()) return false; std::deque> _keys; for(const auto& key : keys) _keys.push_back(property::impl::info(key)); return this->notifyClientPropertyUpdates(client, _keys, selfNotify); @@ -208,25 +208,25 @@ namespace ts { std::shared_ptr getVoiceServer(){ return this->udpVoiceServer; } WebControlServer* getWebServer(){ return this->webControlServer; } - std::deque> calculatePermissions( - permission::PermissionTestType, - ClientDbId, - const std::deque&, - ClientType type, - const std::shared_ptr& channel, - std::shared_ptr cache = nullptr); + std::deque> calculatePermissions( + permission::PermissionTestType, + ClientDbId, + const std::deque&, + ClientType type, + const std::shared_ptr& channel, + std::shared_ptr cache = nullptr); - std::vector> calculatePermissions2( - ClientDbId /* client db id */, - const std::deque& /* permissions to calculate */, - ClientType type /* client type for default permissions */, - ChannelId /* target channel id */, - bool /* calculate granted */, - std::shared_ptr cache = nullptr /* calculate cache */); + std::vector> calculatePermissions2( + ClientDbId /* client db id */, + const std::deque& /* permissions to calculate */, + ClientType type /* client type for default permissions */, + ChannelId /* target channel id */, + bool /* calculate granted */, + std::shared_ptr cache = nullptr /* calculate cache */); permission::PermissionValue calculatePermission(permission::PermissionTestType, ClientDbId, permission::PermissionType, ClientType type, const std::shared_ptr& channel, std::shared_ptr cache = nullptr); - permission::v2::PermissionFlaggedValue calculatePermission2(permission::PermissionType, ClientDbId, ClientType type, ChannelId channel, std::shared_ptr cache = nullptr); + permission::v2::PermissionFlaggedValue calculatePermission2(permission::PermissionType, ClientDbId, ClientType type, ChannelId channel, std::shared_ptr cache = nullptr); permission::PermissionValue calculatePermissionGrant(permission::PermissionTestType, ClientDbId, permission::PermissionType, ClientType type, const std::shared_ptr& channel); @@ -237,60 +237,60 @@ namespace ts { float averagePing(); float averagePacketLoss(); - bool resetPermissions(std::string&); - void ensureValidDefaultGroups(); + bool resetPermissions(std::string&); + void ensureValidDefaultGroups(); - ServerState::value getState() { return this->state; } + ServerState::value getState() { return this->state; } - bool could_default_create_channel(); + bool could_default_create_channel(); - inline std::shared_ptr ref() { return this->self.lock(); } - inline bool disable_ip_saving() { return this->_disable_ip_saving; } - inline std::chrono::system_clock::time_point start_timestamp() { return this->startTimestamp; }; + inline std::shared_ptr ref() { return this->self.lock(); } + inline bool disable_ip_saving() { return this->_disable_ip_saving; } + inline std::chrono::system_clock::time_point start_timestamp() { return this->startTimestamp; }; - /* Note: Use only this method to disconnect the client and notify everybody else that he has been banned! */ - void notify_client_ban(const std::shared_ptr& /* client */, const std::shared_ptr& /* invoker */, const std::string& /* reason */, size_t /* length */); - void notify_client_kick( - const std::shared_ptr& /* client */, - const std::shared_ptr& /* invoker */, + /* Note: Use only this method to disconnect the client and notify everybody else that he has been banned! */ + void notify_client_ban(const std::shared_ptr& /* client */, const std::shared_ptr& /* invoker */, const std::string& /* reason */, size_t /* length */); + void notify_client_kick( + const std::shared_ptr& /* client */, + const std::shared_ptr& /* invoker */, const std::string& /* reason */, const std::shared_ptr& /* target channel */ ); - void client_move( - const std::shared_ptr& /* client */, - std::shared_ptr /* target channel */, - const std::shared_ptr& /* invoker */, - const std::string& /* reason */, - ViewReasonId /* reason id */, - bool /* notify the client */, - std::unique_lock& /* tree lock */ - ); + void client_move( + const std::shared_ptr& /* client */, + std::shared_ptr /* target channel */, + const std::shared_ptr& /* invoker */, + const std::string& /* reason */, + ViewReasonId /* reason id */, + bool /* notify the client */, + std::unique_lock& /* tree lock */ + ); - void delete_channel( - std::shared_ptr /* target channel */, - const std::shared_ptr& /* invoker */, + void delete_channel( + std::shared_ptr /* target channel */, + const std::shared_ptr& /* invoker */, const std::string& /* kick message */, std::unique_lock& /* tree lock */ ); - inline int voice_encryption_mode() { return this->_voice_encryption_mode; } - inline std::shared_ptr conversation_manager() { return this->_conversation_manager; } + inline int voice_encryption_mode() { return this->_voice_encryption_mode; } + inline std::shared_ptr conversation_manager() { return this->_conversation_manager; } protected: bool registerClient(std::shared_ptr); bool unregisterClient(std::shared_ptr, std::string, std::unique_lock& channel_tree_lock); bool assignDefaultChannel(const std::shared_ptr&, bool join); private: - std::weak_ptr self; + std::weak_ptr self; - //Locks by tick, start and stop - threads::Mutex stateLock; + //Locks by tick, start and stop + threads::Mutex stateLock; ServerState::value state = ServerState::OFFLINE; std::chrono::system_clock::time_point lastTick; void executeServerTick(); - std::shared_ptr udpVoiceServer = nullptr; + std::shared_ptr udpVoiceServer = nullptr; WebControlServer* webControlServer = nullptr; token::TokenManager* tokenManager = nullptr; ComplainManager* complains = nullptr; @@ -303,18 +303,18 @@ namespace ts { uint16_t serverId = 1; - std::chrono::system_clock::time_point startTimestamp; - std::chrono::system_clock::time_point fileStatisticsTimestamp; - std::chrono::system_clock::time_point conversation_cache_cleanup_timestamp; + std::chrono::system_clock::time_point startTimestamp; + std::chrono::system_clock::time_point fileStatisticsTimestamp; + std::chrono::system_clock::time_point conversation_cache_cleanup_timestamp; //The client list struct { - size_t count = 0; - std::mutex lock; - std::vector> clients; + size_t count = 0; + std::mutex lock; + std::vector> clients; } clients; - std::recursive_mutex client_nickname_lock; + std::recursive_mutex client_nickname_lock; //General server properties ecc_key* _serverKey = nullptr; @@ -332,12 +332,12 @@ namespace ts { threads::Mutex join_attempts_lock; std::map join_attempts; threads::Mutex join_lock; - std::chrono::system_clock::time_point join_last_decrease; + std::chrono::system_clock::time_point join_last_decrease; std::chrono::milliseconds spoken_time{0}; - std::chrono::system_clock::time_point spoken_time_timestamp; + std::chrono::system_clock::time_point spoken_time_timestamp; - bool _disable_ip_saving = false; + bool _disable_ip_saving = false; }; } } \ No newline at end of file diff --git a/server/src/build.cpp b/server/src/build.cpp index 133b429..0e43944 100644 --- a/server/src/build.cpp +++ b/server/src/build.cpp @@ -34,31 +34,31 @@ using namespace std; using namespace std::chrono; unique_ptr local_version{[]() -> Version * { - const char *build_timestamp = __TIME__; //23:59:01 - const char *build_date = __DATE__; //Feb 12 1996 + const char *build_timestamp = __TIME__; //23:59:01 + const char *build_date = __DATE__; //Feb 12 1996 - cout << "Time " << build_timestamp << " date " << build_date << endl; - tm timestamp{}, date{}; - if (!strptime(build_timestamp, "%H:%M:%S", ×tamp)) cerr << "Could not parse build timestamp!" << endl; - if (!strptime(build_date, "%b %d %Y", &date)) cerr << "Could not parse build date!" << endl; + cout << "Time " << build_timestamp << " date " << build_date << endl; + tm timestamp{}, date{}; + if (!strptime(build_timestamp, "%H:%M:%S", ×tamp)) cerr << "Could not parse build timestamp!" << endl; + if (!strptime(build_date, "%b %d %Y", &date)) cerr << "Could not parse build date!" << endl; - system_clock::time_point time = - system_clock::time_point() + seconds(mktime(&date)) + hours(timestamp.tm_hour) + minutes(timestamp.tm_min) + - seconds(timestamp.tm_sec); - return new Version{BUILD_MAJOR, BUILD_MINOR, BUILD_PATCH, STR2(BUILD_DATA), time}; + system_clock::time_point time = + system_clock::time_point() + seconds(mktime(&date)) + hours(timestamp.tm_hour) + minutes(timestamp.tm_min) + + seconds(timestamp.tm_sec); + return new Version{BUILD_MAJOR, BUILD_MINOR, BUILD_PATCH, STR2(BUILD_DATA), time}; }()}; namespace build { - const std::unique_ptr& version() { - return local_version; - } + const std::unique_ptr& version() { + return local_version; + } - std::string Version::string(bool timestamp) { - stringstream ss; - ss << this->major << "." << this->minor << "." << this->patch << this->additional; - if(timestamp) ss << " [Build: " << duration_cast(this->timestamp.time_since_epoch()).count() << "]"; - return ss.str(); - } + std::string Version::string(bool timestamp) { + stringstream ss; + ss << this->major << "." << this->minor << "." << this->patch << this->additional; + if(timestamp) ss << " [Build: " << duration_cast(this->timestamp.time_since_epoch()).count() << "]"; + return ss.str(); + } BuildType type() { return static_cast(BUILD_TYPE); } diff --git a/server/src/build.h b/server/src/build.h index f63e832..f1b7a79 100644 --- a/server/src/build.h +++ b/server/src/build.h @@ -52,10 +52,10 @@ namespace build { this->minor = minor; } - std::string string(bool timestamp = true); + std::string string(bool timestamp = true); }; - extern const std::unique_ptr& version(); + extern const std::unique_ptr& version(); extern BuildType type(); extern int buildCount(); diff --git a/server/src/channel/ClientChannelView.cpp b/server/src/channel/ClientChannelView.cpp index 707f3dc..06d1bbe 100644 --- a/server/src/channel/ClientChannelView.cpp +++ b/server/src/channel/ClientChannelView.cpp @@ -9,7 +9,7 @@ using namespace ts; using namespace ts::server; ViewEntry::ViewEntry(const std::shared_ptr &handle, bool editable) : handle(handle), editable(editable) { - memtrack::allocated(this); + memtrack::allocated(this); assert(handle); this->view_timestamp = chrono::system_clock::now(); @@ -19,13 +19,13 @@ ViewEntry::ViewEntry(const std::shared_ptr &handle, bool edita } ViewEntry::~ViewEntry() { - memtrack::freed(this); + memtrack::freed(this); } ChannelId ts::ViewEntry::channelId() const { - if(this->cached_channel_id != 0) return this->cached_channel_id; //We've already got a channel id + if(this->cached_channel_id != 0) return this->cached_channel_id; //We've already got a channel id - //TODO cached_channel_id should be > 0 every time? + //TODO cached_channel_id should be > 0 every time? auto channel = this->handle.lock(); if(!channel) { logCritical(LOG_GENERAL, "ViewEntry::channelId() called without a valid handle and cached_channel_id == 0!"); @@ -35,7 +35,7 @@ ChannelId ts::ViewEntry::channelId() const { } ChannelId ViewEntry::parentId() const { - if(this->cached_parent_id != 0) return this->cached_parent_id; + if(this->cached_parent_id != 0) return this->cached_parent_id; auto channel = this->handle.lock(); return channel && channel->parent() ? channel->parent()->channelId() : 0; @@ -52,14 +52,14 @@ void ts::ViewEntry::setPreviousChannelId(ts::ChannelId id) { void ts::ViewEntry::setParentChannelId(ts::ChannelId id) { assert(this->editable); - this->cached_parent_id = id; + this->cached_parent_id = id; } ClientChannelView::ClientChannelView(server::ConnectedClient* handle) : owner(handle) { - memtrack::allocated(this); + memtrack::allocated(this); } ClientChannelView::~ClientChannelView() { - memtrack::freed(this); + memtrack::freed(this); } ServerId ClientChannelView::getServerId() { @@ -70,9 +70,9 @@ std::deque> ClientChannelView::channels(const std: int deep) { std::deque> result; for(const auto& entry : this->entries(head ? make_shared(head) : nullptr, deep)) { - auto channel = dynamic_pointer_cast(entry)->handle.lock(); - if(channel) - result.push_back(channel); + auto channel = dynamic_pointer_cast(entry)->handle.lock(); + if(channel) + result.push_back(channel); } return result; @@ -90,37 +90,37 @@ std::shared_ptr ClientChannelView::find_channel(ts::ChannelId id) { } std::shared_ptr ClientChannelView::find_channel(const std::shared_ptr &channel) { - if(!channel) return nullptr; + if(!channel) return nullptr; - deque> heads{channel}; - shared_ptr head = nullptr; - bool deep_search = false; + deque> heads{channel}; + shared_ptr head = nullptr; + bool deep_search = false; - while(heads.front()) { - auto parent = heads.front()->parent(); - if(!parent && heads.front()->properties()[property::CHANNEL_PID].as() != 0) { - head = this->find_linked_entry(channel->channelId(), nullptr);//We're searching for a deleted head! So lets iterate over everything - deep_search = true; - break; - } - heads.push_front(parent); - } + while(heads.front()) { + auto parent = heads.front()->parent(); + if(!parent && heads.front()->properties()[property::CHANNEL_PID].as() != 0) { + head = this->find_linked_entry(channel->channelId(), nullptr);//We're searching for a deleted head! So lets iterate over everything + deep_search = true; + break; + } + heads.push_front(parent); + } - if(!deep_search) { - heads.pop_front(); + if(!deep_search) { + heads.pop_front(); - while(heads.size() > 1) { - auto front = move(heads.front()); - heads.pop_front(); + while(heads.size() > 1) { + auto front = move(heads.front()); + heads.pop_front(); - head = this->find_linked_entry(front->channelId(), head, 1); - if(!head) return nullptr; //Channel tree not visible! - } + head = this->find_linked_entry(front->channelId(), head, 1); + if(!head) return nullptr; //Channel tree not visible! + } - head = this->find_linked_entry(channel->channelId(), head, 1); - } + head = this->find_linked_entry(channel->channelId(), head, 1); + } - return head ? static_pointer_cast(head->entry) : nullptr; + return head ? static_pointer_cast(head->entry) : nullptr; } std::deque> ClientChannelView::insert_channels(shared_ptr head, bool test_permissions, bool first_only, std::shared_ptr cache) { @@ -130,26 +130,26 @@ std::deque> ClientChannelView::insert_channels(shared bool has_perm = !test_permissions || owner->permissionGranted(permission::PERMTEST_ORDERED, permission::b_channel_ignore_view_power, 1, nullptr, true, cache); bool first = true; while(head) { - if(!first && first_only) break; - first = false; + if(!first && first_only) break; + first = false; auto channel = dynamic_pointer_cast(head->entry); if(this->channel_visible(channel)) { - if(head->child_head) { - for(const auto& sub : this->insert_channels(head->child_head, test_permissions, false, cache)) - result.push_back(sub); - } + if(head->child_head) { + for(const auto& sub : this->insert_channels(head->child_head, test_permissions, false, cache)) + result.push_back(sub); + } head = head->next; continue; } if(!has_perm) { - if(!channel->permission_granted(permission::i_channel_needed_view_power, this->owner->calculate_permission_value(permission::i_channel_view_power, channel->channelId()), false)) { - head = head->next; - debugMessage(this->getServerId(), "{}[CHANNEL] Dropping channel {} ({}) (No permissions)", CLIENT_STR_LOG_PREFIX_(this->owner), channel->channelId(), channel->name()); - continue; - } + if(!channel->permission_granted(permission::i_channel_needed_view_power, this->owner->calculate_permission_value(permission::i_channel_view_power, channel->channelId()), false)) { + head = head->next; + debugMessage(this->getServerId(), "{}[CHANNEL] Dropping channel {} ({}) (No permissions)", CLIENT_STR_LOG_PREFIX_(this->owner), channel->channelId(), channel->name()); + continue; + } } auto entry = make_shared(dynamic_pointer_cast(head->entry), true); std::shared_ptr parent, previous; @@ -171,11 +171,11 @@ std::deque> ClientChannelView::insert_channels(shared head = head->next; continue; }; - debugMessage(this->getServerId(), "{}[CHANNELS] Insert channel {} ({} => order {}) after {} ({})", - CLIENT_STR_LOG_PREFIX_(this->owner), - channel->channelId(), channel->name(), entry->previousChannelId(), - previous ? previous->channelId() : 0, previous_channel ? previous_channel->name() : "" - ); + debugMessage(this->getServerId(), "{}[CHANNELS] Insert channel {} ({} => order {}) after {} ({})", + CLIENT_STR_LOG_PREFIX_(this->owner), + channel->channelId(), channel->name(), entry->previousChannelId(), + previous ? previous->channelId() : 0, previous_channel ? previous_channel->name() : "" + ); result.push_back(entry); @@ -221,7 +221,7 @@ std::deque> ClientChannelView::show_channel(std::shar while(remote_previous && !(previous = dynamic_pointer_cast(this->find_entry(remote_previous->entry->channelId())))) { remote_previous = remote_previous->previous; } - auto previous_channel = previous ? previous->channel() : nullptr; //weak could be may nullptr + auto previous_channel = previous ? previous->channel() : nullptr; //weak could be may nullptr debugMessage(this->getServerId(), "{}[CHANNELS] Insert channel {} ({}) after {} ({})", CLIENT_STR_LOG_PREFIX_(this->owner), channel->channelId(), channel->name(), @@ -286,56 +286,56 @@ std::deque>> ClientChannelView::updat } std::deque>> ClientChannelView::update_channel_path(std::shared_ptr l_channel, std::shared_ptr l_own, shared_ptr cache, ssize_t length) { - if(!cache) cache = make_shared(); - std::deque>> result; - bool has_perm = owner->permissionGranted(permission::PERMTEST_ORDERED, permission::b_channel_ignore_view_power, 1, nullptr, true, cache); + if(!cache) cache = make_shared(); + std::deque>> result; + bool has_perm = owner->permissionGranted(permission::PERMTEST_ORDERED, permission::b_channel_ignore_view_power, 1, nullptr, true, cache); - while(l_channel && length-- != 0) { - auto b_channel = dynamic_pointer_cast(l_channel->entry); - sassert(b_channel); - auto visible = this->channel_visible(b_channel); - if(!visible) { - if(l_channel->parent.lock() && !this->channel_visible(dynamic_pointer_cast(l_channel->parent.lock()->entry))) { - l_channel = l_channel->next; - continue; /* all subchannels had been checked, because parent isnt visible */ - } - //Test if channel comes visible again! - visible = true; + while(l_channel && length-- != 0) { + auto b_channel = dynamic_pointer_cast(l_channel->entry); + sassert(b_channel); + auto visible = this->channel_visible(b_channel); + if(!visible) { + if(l_channel->parent.lock() && !this->channel_visible(dynamic_pointer_cast(l_channel->parent.lock()->entry))) { + l_channel = l_channel->next; + continue; /* all subchannels had been checked, because parent isnt visible */ + } + //Test if channel comes visible again! + visible = true; - if(!has_perm) { - if(!b_channel->permission_granted(permission::i_channel_needed_view_power, this->owner->calculate_permission_value(permission::i_channel_view_power, b_channel->channelId()), false)) { - visible = false; - } - } - if(visible) { - for(const auto& entry : this->show_channel(l_channel, visible)) - result.emplace_back(true, entry); - for(const auto& entry : this->insert_channels(l_channel->child_head, true, false, cache)) - result.emplace_back(true, entry); - } + if(!has_perm) { + if(!b_channel->permission_granted(permission::i_channel_needed_view_power, this->owner->calculate_permission_value(permission::i_channel_view_power, b_channel->channelId()), false)) { + visible = false; + } + } + if(visible) { + for(const auto& entry : this->show_channel(l_channel, visible)) + result.emplace_back(true, entry); + for(const auto& entry : this->insert_channels(l_channel->child_head, true, false, cache)) + result.emplace_back(true, entry); + } - l_channel = l_channel->next; - continue; /* all subchannels had been checked */ - } else if(visible && !has_perm) { - for(const auto& entry : this->test_channel(l_channel, l_own, cache)) - result.emplace_back(false, entry); - } + l_channel = l_channel->next; + continue; /* all subchannels had been checked */ + } else if(visible && !has_perm) { + for(const auto& entry : this->test_channel(l_channel, l_own, cache)) + result.emplace_back(false, entry); + } - //Root node is okey, test children - if(l_channel->child_head) { - auto entries = this->update_channel_path(l_channel->child_head, l_own, cache, -1); - result.insert(result.end(), entries.begin(), entries.end()); - } + //Root node is okey, test children + if(l_channel->child_head) { + auto entries = this->update_channel_path(l_channel->child_head, l_own, cache, -1); + result.insert(result.end(), entries.begin(), entries.end()); + } - l_channel = l_channel->next; - } + l_channel = l_channel->next; + } - return result; + return result; } std::deque>> ClientChannelView::change_order(const shared_ptr &channel, const std::shared_ptr &parent, const shared_ptr &head) { - std::deque>> result; + std::deque>> result; auto l_entry = this->find_linked_entry(channel->entry->channelId()); auto l_parent = parent ? this->find_linked_entry(parent->entry->channelId()) : nullptr; if(!l_entry) { //Channel not visible yet @@ -343,7 +343,7 @@ std::dequepermissionGranted(permission::PERMTEST_ORDERED, permission::b_channel_ignore_view_power, 1, nullptr); if(!has_perm) { - has_perm = dynamic_pointer_cast(channel->entry)->permission_granted(permission::i_channel_needed_view_power, this->owner->calculate_permission_value(permission::i_channel_view_power, dynamic_pointer_cast(channel->entry)->channelId()), false); + has_perm = dynamic_pointer_cast(channel->entry)->permission_granted(permission::i_channel_needed_view_power, this->owner->calculate_permission_value(permission::i_channel_view_power, dynamic_pointer_cast(channel->entry)->channelId()), false); } if(!has_perm) return {}; //Channel wasn't visible and he still has no permission for that :) @@ -354,7 +354,7 @@ std::dequeinsert_channels(channel, true, true)) - result.push_back({ClientChannelView::ENTER_VIEW, shown}); + result.push_back({ClientChannelView::ENTER_VIEW, shown}); return result; //An invisible channel became visible } //Channel visible! @@ -410,17 +410,17 @@ bool ClientChannelView::remove_channel(ts::ChannelId channelId) { } std::deque ClientChannelView::delete_channel_root(const std::shared_ptr &channel){ - auto linked = this->find_channel(channel); - if(!linked) return {}; + auto linked = this->find_channel(channel); + if(!linked) return {}; - std::deque result; - for(const auto& channel : this->delete_entry(linked)) - result.push_back(channel->channelId()); - return result; + std::deque result; + for(const auto& channel : this->delete_entry(linked)) + result.push_back(channel->channelId()); + return result; } void ClientChannelView::reset() { - while(this->head) + while(this->head) this->delete_entry(this->head->entry); } diff --git a/server/src/channel/ClientChannelView.h b/server/src/channel/ClientChannelView.h index 80a0d27..7bf5b0a 100644 --- a/server/src/channel/ClientChannelView.h +++ b/server/src/channel/ClientChannelView.h @@ -6,7 +6,7 @@ namespace ts { namespace server { class ConnectedClient; - struct CalculateCache; + struct CalculateCache; } struct ViewEntry : public TreeEntry { @@ -21,27 +21,27 @@ namespace ts { void setPreviousChannelId(ChannelId id) override; void setParentChannelId(ChannelId id) override; - bool subscribed = false; + bool subscribed = false; bool editable = false; bool joinable = false; /* used within notify text message */ ChannelId previous_channel = 0; uint16_t join_state_id = 0; /* the calculation id for the flag joinable. If this does not match with the join_state_id within the client the flag needs to be recalculated */ std::weak_ptr handle; - std::chrono::system_clock::time_point view_timestamp; + std::chrono::system_clock::time_point view_timestamp; private: ChannelId cached_channel_id = 0; - ChannelId cached_parent_id = 0; + ChannelId cached_parent_id = 0; }; class ClientChannelView : private TreeView { public: - enum ChannelAction { - NOTHING, - ENTER_VIEW, - DELETE_VIEW, - REORDER, - MOVE - }; + enum ChannelAction { + NOTHING, + ENTER_VIEW, + DELETE_VIEW, + REORDER, + MOVE + }; explicit ClientChannelView(server::ConnectedClient*); ~ClientChannelView() override; @@ -49,12 +49,12 @@ namespace ts { inline size_t count_channels() const { return this->entry_count(); } std::deque> channels(const std::shared_ptr& /* head */ = nullptr, int deep = -1); bool channel_visible(const std::shared_ptr& /* entry */, const std::shared_ptr& /* head */ = nullptr, int deep = -1); - std::shared_ptr find_channel(ChannelId /* channel id */); - std::shared_ptr find_channel(const std::shared_ptr& /* channel */); + std::shared_ptr find_channel(ChannelId /* channel id */); + std::shared_ptr find_channel(const std::shared_ptr& /* channel */); /* add channel tree with siblings */ std::deque> insert_channels( - std::shared_ptr /* head */, + std::shared_ptr /* head */, bool test_permissions, bool first_only, std::shared_ptr cache = nullptr @@ -62,27 +62,27 @@ namespace ts { /* shows the specific channel and their parents */ std::deque> show_channel( - std::shared_ptr /* channel */, - bool& /* success */ + std::shared_ptr /* channel */, + bool& /* success */ ); /* remove invalid channel */ std::deque> test_channel( - std::shared_ptr /* old channel */, + std::shared_ptr /* old channel */, std::shared_ptr /* new channel */, std::shared_ptr cache = nullptr ); /* [{ add := true | delete := false, channel}] */ std::deque>> update_channel( - std::shared_ptr /* channel */, + std::shared_ptr /* channel */, std::shared_ptr /* own channel */, std::shared_ptr cache = nullptr ); - /* [{ add := true | delete := false, channel}] */ - std::deque>> update_channel_path( - std::shared_ptr /* channel */, + /* [{ add := true | delete := false, channel}] */ + std::deque>> update_channel_path( + std::shared_ptr /* channel */, std::shared_ptr /* own channel */, std::shared_ptr cache = nullptr, ssize_t length = -1 @@ -95,7 +95,7 @@ namespace ts { std::deque>> change_order( - const std::shared_ptr &/* channel */, + const std::shared_ptr &/* channel */, const std::shared_ptr /* parent */&, const std::shared_ptr /* previous */& ); diff --git a/server/src/channel/ServerChannel.cpp b/server/src/channel/ServerChannel.cpp index 8282f53..9225d52 100644 --- a/server/src/channel/ServerChannel.cpp +++ b/server/src/channel/ServerChannel.cpp @@ -16,48 +16,48 @@ using namespace ts::server; extern InstanceHandler* serverInstance; ServerChannel::ServerChannel(ChannelId parentId, ChannelId channelId) : BasicChannel(parentId, channelId) { - memtrack::allocated(this); + memtrack::allocated(this); } ServerChannel::~ServerChannel() { - memtrack::freed(this); + memtrack::freed(this); } void ServerChannel::register_client(const std::shared_ptr &client) { - unique_lock lock(this->client_lock); - for(const auto& _client : this->clients) { - if(_client.lock() == client) { - return; - } - } + unique_lock lock(this->client_lock); + for(const auto& _client : this->clients) { + if(_client.lock() == client) { + return; + } + } - this->clients.push_back(client); + this->clients.push_back(client); } void ServerChannel::unregister_client(const std::shared_ptr &client) { - unique_lock lock(this->client_lock); - this->clients.erase(remove_if(this->clients.begin(), this->clients.end(), [client](const auto& weak) { - auto locked = weak.lock(); - if(!locked || client == locked) return true; - return false; - }), this->clients.end()); + unique_lock lock(this->client_lock); + this->clients.erase(remove_if(this->clients.begin(), this->clients.end(), [client](const auto& weak) { + auto locked = weak.lock(); + if(!locked || client == locked) return true; + return false; + }), this->clients.end()); } size_t ServerChannel::client_count() { - shared_lock lock(this->client_lock); - size_t result = 0; - for(const auto& weak_entry : this->clients) { - if(auto entry = weak_entry.lock()) { - auto current_channel = entry->getChannel(); - if(current_channel && current_channel.get() == this) - result++; - } - } - return result; + shared_lock lock(this->client_lock); + size_t result = 0; + for(const auto& weak_entry : this->clients) { + if(auto entry = weak_entry.lock()) { + auto current_channel = entry->getChannel(); + if(current_channel && current_channel.get() == this) + result++; + } + } + return result; } void ServerChannel::setProperties(const std::shared_ptr &ptr) { - BasicChannel::setProperties(ptr); + BasicChannel::setProperties(ptr); } ServerChannelTree::ServerChannelTree(const std::shared_ptr& server, sql::SqlManager* sql) : sql(sql), server(server) { } @@ -65,54 +65,54 @@ ServerChannelTree::ServerChannelTree(const std::shared_ptr& se ServerChannelTree::~ServerChannelTree() { } void ServerChannelTree::deleteSemiPermanentChannels() { - loop: + loop: - for(const auto& ch : this->channels()) - if(ch->channelType() == ChannelType::semipermanent || ch->channelType() == ChannelType::temporary){ //We also delete private channels - this->delete_channel_root(ch); - goto loop; - } + for(const auto& ch : this->channels()) + if(ch->channelType() == ChannelType::semipermanent || ch->channelType() == ChannelType::temporary){ //We also delete private channels + this->delete_channel_root(ch); + goto loop; + } } ChannelId ServerChannelTree::generateChannelId() { - ChannelId channelId = 0; + ChannelId channelId = 0; - auto res = sql::command(this->sql, "SELECT `channelId` FROM `channels` WHERE `serverId` = :sid ORDER BY `channelId` DESC LIMIT 1", variable{":sid", this->getServerId()}).query([](ChannelId* num, int, char** value, char**){ - *num = (ChannelId) stoll(value[0]); - return 0; - }, &channelId); - auto pf = LOG_SQL_CMD; - pf(res); - if(!res) return 0; - return channelId + 1; + auto res = sql::command(this->sql, "SELECT `channelId` FROM `channels` WHERE `serverId` = :sid ORDER BY `channelId` DESC LIMIT 1", variable{":sid", this->getServerId()}).query([](ChannelId* num, int, char** value, char**){ + *num = (ChannelId) stoll(value[0]); + return 0; + }, &channelId); + auto pf = LOG_SQL_CMD; + pf(res); + if(!res) return 0; + return channelId + 1; } std::shared_ptr ServerChannelTree::createChannel(ChannelId parentId, ChannelId orderId, const string &name) { std::shared_ptr channel = BasicChannelTree::createChannel(parentId, orderId, name); - if(!channel) return channel; + if(!channel) return channel; - auto properties = serverInstance->databaseHelper()->loadChannelProperties(this->server.lock(), channel->channelId()); - for(const auto& prop : channel->properties().list_properties()) - if(prop.isModified()) //Copy the already set properties - (*properties)[prop.type()] = prop.value(); + auto properties = serverInstance->databaseHelper()->loadChannelProperties(this->server.lock(), channel->channelId()); + for(const auto& prop : channel->properties().list_properties()) + if(prop.isModified()) //Copy the already set properties + (*properties)[prop.type()] = prop.value(); static_pointer_cast(channel)->setProperties(properties); static_pointer_cast(channel)->setPermissionManager(serverInstance->databaseHelper()->loadChannelPermissions(this->server.lock(), channel->channelId())); - channel->properties()[property::CHANNEL_CREATED_AT] = chrono::duration_cast(chrono::system_clock::now().time_since_epoch()).count(); - channel->properties()[property::CHANNEL_LAST_LEFT] = chrono::duration_cast(chrono::system_clock::now().time_since_epoch()).count(); + channel->properties()[property::CHANNEL_CREATED_AT] = chrono::duration_cast(chrono::system_clock::now().time_since_epoch()).count(); + channel->properties()[property::CHANNEL_LAST_LEFT] = chrono::duration_cast(chrono::system_clock::now().time_since_epoch()).count(); - auto result = sql::command(this->sql, "INSERT INTO `channels` (`serverId`, `channelId`, `type`, `parentId`) VALUES(:sid, :chid, :type, :parent);", variable{":sid", this->getServerId()}, variable{":chid", channel->channelId()}, variable{":type", channel->channelType()}, variable{":parent", channel->parent() ? channel->parent()->channelId() : 0}).execute(); - auto pf = LOG_SQL_CMD; - pf(result); + auto result = sql::command(this->sql, "INSERT INTO `channels` (`serverId`, `channelId`, `type`, `parentId`) VALUES(:sid, :chid, :type, :parent);", variable{":sid", this->getServerId()}, variable{":chid", channel->channelId()}, variable{":type", channel->channelType()}, variable{":parent", channel->parent() ? channel->parent()->channelId() : 0}).execute(); + auto pf = LOG_SQL_CMD; + pf(result); - return channel; + return channel; } inline std::shared_ptr findChannelByPool(const std::deque>& pool, size_t chId){ - for(const auto& elm : pool) - if(elm->entry->channelId() == chId) return dynamic_pointer_cast(elm->entry); - return nullptr; + for(const auto& elm : pool) + if(elm->entry->channelId() == chId) return dynamic_pointer_cast(elm->entry); + return nullptr; } inline std::shared_ptr findLinkedChannelByPool(const std::deque>& pool, size_t chId){ @@ -122,38 +122,38 @@ inline std::shared_ptr findLinkedChannelByPool(const } ServerId ServerChannelTree::getServerId() { - auto s = this->server.lock(); - return s ? s->getServerId() : 0UL; + auto s = this->server.lock(); + return s ? s->getServerId() : 0UL; } bool ServerChannelTree::initializeTempParents() { - auto channelList = this->tmpChannelList; - for(const auto& linked_channel : channelList) { - auto channel = dynamic_pointer_cast(linked_channel->entry); - assert(channel); + auto channelList = this->tmpChannelList; + for(const auto& linked_channel : channelList) { + auto channel = dynamic_pointer_cast(linked_channel->entry); + assert(channel); - if(channel->properties().hasProperty(property::CHANNEL_PID) && channel->properties()[property::CHANNEL_PID].as() != 0){ - if(!channel->parent()) - linked_channel->parent = findLinkedChannelByPool(this->tmpChannelList, channel->properties()[property::CHANNEL_PID]); - if(!channel->parent()){ - logError(this->getServerId(), "Invalid channel parent (Channel does not exists). Channel id: {} ({}) Missing parent id: {}", channel->channelId(), channel->name(), channel->properties()[property::CHANNEL_PID].as()); - logError(this->getServerId(), "Resetting parent"); - channel->properties()[property::CHANNEL_PID] = 0; - } - } - } - return true; + if(channel->properties().hasProperty(property::CHANNEL_PID) && channel->properties()[property::CHANNEL_PID].as() != 0){ + if(!channel->parent()) + linked_channel->parent = findLinkedChannelByPool(this->tmpChannelList, channel->properties()[property::CHANNEL_PID]); + if(!channel->parent()){ + logError(this->getServerId(), "Invalid channel parent (Channel does not exists). Channel id: {} ({}) Missing parent id: {}", channel->channelId(), channel->name(), channel->properties()[property::CHANNEL_PID].as()); + logError(this->getServerId(), "Resetting parent"); + channel->properties()[property::CHANNEL_PID] = 0; + } + } + } + return true; } typedef std::deque> ChannelPool; inline ChannelPool remoteTopChannels(const std::shared_ptr& parent, ChannelPool& pool){ ChannelPool result; - for(const auto& elm : pool) - if(elm->parent.lock() == parent) - result.push_back(elm); - for(const auto& elm : result) - pool.erase(std::find(pool.begin(), pool.end(), elm)); - return result; + for(const auto& elm : pool) + if(elm->parent.lock() == parent) + result.push_back(elm); + for(const auto& elm : result) + pool.erase(std::find(pool.begin(), pool.end(), elm)); + return result; } inline ChannelPool resolveChannelHeads(ServerId serverId, ChannelPool pool) { @@ -163,22 +163,22 @@ inline ChannelPool resolveChannelHeads(ServerId serverId, ChannelPool pool) { auto element = pool.front(); if(!element) continue; - ChannelPool tmp_pool = pool; - auto f = find(tmp_pool.begin(), tmp_pool.end(), element); - if(f != tmp_pool.end()) - tmp_pool.erase(f); + ChannelPool tmp_pool = pool; + auto f = find(tmp_pool.begin(), tmp_pool.end(), element); + if(f != tmp_pool.end()) + tmp_pool.erase(f); while(element->previous) { - auto found = find(tmp_pool.begin(), tmp_pool.end(), element->previous); + auto found = find(tmp_pool.begin(), tmp_pool.end(), element->previous); if(found == tmp_pool.end()) { logError(serverId, "Found recursive channel heads! Cutting"); if(element->previous && element->previous->next == element) - element->previous->next = nullptr; - element->previous = nullptr; - break; + element->previous->next = nullptr; + element->previous = nullptr; + break; } - tmp_pool.erase(found); - element = element->previous; + tmp_pool.erase(found); + element = element->previous; }; //Find the head result.push_back(element); @@ -186,19 +186,19 @@ inline ChannelPool resolveChannelHeads(ServerId serverId, ChannelPool pool) { while(element) { auto it = find(pool.begin(), pool.end(), element); if(it == pool.end()) { - logError(serverId, "Circular tail. Cutting previus."); - if(last != element) { - if(element->previous == last) - element->previous = nullptr; + logError(serverId, "Circular tail. Cutting previus."); + if(last != element) { + if(element->previous == last) + element->previous = nullptr; - if(last->next == element) - last->next = nullptr; - } - break; + if(last->next == element) + last->next = nullptr; + } + break; } else - pool.erase(it); + pool.erase(it); - last = element; + last = element; element = element->next; } } @@ -207,56 +207,56 @@ inline ChannelPool resolveChannelHeads(ServerId serverId, ChannelPool pool) { } inline std::shared_ptr buildChannelTree(ServerId serverId, const std::shared_ptr& parent, ChannelPool& channel_pool){ - auto top_channels = remoteTopChannels(parent, channel_pool); - if(top_channels.empty()) return nullptr; + auto top_channels = remoteTopChannels(parent, channel_pool); + if(top_channels.empty()) return nullptr; - bool brokenTree = false; - for(const auto& linked_channel : top_channels){ - auto channel = dynamic_pointer_cast(linked_channel->entry); - assert(channel); + bool brokenTree = false; + for(const auto& linked_channel : top_channels){ + auto channel = dynamic_pointer_cast(linked_channel->entry); + assert(channel); if(channel->channelOrder() != 0) { - if(channel->channelOrder() == channel->channelId()) { + if(channel->channelOrder() == channel->channelId()) { brokenTree = true; - logError(serverId, "Channel order refers to itself! (Resetting)"); - channel->properties()[property::CHANNEL_ORDER] = 0; - continue; - } - - auto previous_linked = findLinkedChannelByPool(top_channels, channel->channelOrder()); - if(!previous_linked) { - brokenTree = true; - logError(serverId, "Failed to resolve previous channel for channel {} ({}). Previous channel id: {} (Resetting order)", channel->channelId(), channel->name(), channel->channelOrder()); + logError(serverId, "Channel order refers to itself! (Resetting)"); channel->properties()[property::CHANNEL_ORDER] = 0; continue; - } + } - if(previous_linked->next) { - if(previous_linked->next != linked_channel) { + auto previous_linked = findLinkedChannelByPool(top_channels, channel->channelOrder()); + if(!previous_linked) { + brokenTree = true; + logError(serverId, "Failed to resolve previous channel for channel {} ({}). Previous channel id: {} (Resetting order)", channel->channelId(), channel->name(), channel->channelOrder()); + channel->properties()[property::CHANNEL_ORDER] = 0; + continue; + } + + if(previous_linked->next) { + if(previous_linked->next != linked_channel) { brokenTree = true; - logError(serverId, "Previous channel {} ({}) of channel {} ({}) is already linked with another channel {} ({}).", + logError(serverId, "Previous channel {} ({}) of channel {} ({}) is already linked with another channel {} ({}).", previous_linked->entry->channelId(), dynamic_pointer_cast(previous_linked->entry)->name(), channel->channelId(), channel->name(), previous_linked->next->entry->channelId(), dynamic_pointer_cast(previous_linked->next->entry)->name() ); - logError(serverId, "Inserting channel anyway!"); - previous_linked->next->previous = linked_channel; - linked_channel->next = previous_linked->next; - } - } + logError(serverId, "Inserting channel anyway!"); + previous_linked->next->previous = linked_channel; + linked_channel->next = previous_linked->next; + } + } previous_linked->next = linked_channel; linked_channel->previous = previous_linked; - } - } + } + } - for(const auto& elm : top_channels){ - if(elm->next) { - if(elm->next->previous != elm) { + for(const auto& elm : top_channels){ + if(elm->next) { + if(elm->next->previous != elm) { brokenTree = true; - logError(serverId, "Test 'elm->next->previous != elm' failed! Assigning elm->next->previous to elm!"); + logError(serverId, "Test 'elm->next->previous != elm' failed! Assigning elm->next->previous to elm!"); elm->next->previous = elm; - } - } + } + } if(elm->previous) { if(elm->previous->next != elm) { brokenTree = true; @@ -264,15 +264,15 @@ inline std::shared_ptr buildChannelTree(ServerId serv elm->previous->next = elm; } } - } + } - /* Get the heads and merge them */ - auto heads = resolveChannelHeads(serverId, top_channels); - if(heads.size() > 1){ - brokenTree = true; - logError(serverId, "Got multiple channel heads! (Count {})", heads.size()); + /* Get the heads and merge them */ + auto heads = resolveChannelHeads(serverId, top_channels); + if(heads.size() > 1){ + brokenTree = true; + logError(serverId, "Got multiple channel heads! (Count {})", heads.size()); logError(serverId, "Try to appending them", heads.size()); debugMessage(serverId, "Got head:"); @@ -285,9 +285,9 @@ inline std::shared_ptr buildChannelTree(ServerId serv tail->next = heads[index]; tail->next->previous = tail; } - } + } - /* Testing tree */ + /* Testing tree */ if(brokenTree) { auto new_heads = resolveChannelHeads(serverId, top_channels); @@ -297,18 +297,18 @@ inline std::shared_ptr buildChannelTree(ServerId serv } } - brokenTree = true; - if(brokenTree){ + brokenTree = true; + if(brokenTree){ auto entry = heads[0]; - while(entry) { - auto channel = dynamic_pointer_cast(entry->entry); - assert(channel); + while(entry) { + auto channel = dynamic_pointer_cast(entry->entry); + assert(channel); - auto evaluated_order_id = entry->previous ? entry->previous->entry->channelId() : 0; - if(evaluated_order_id != channel->previousChannelId()) { + auto evaluated_order_id = entry->previous ? entry->previous->entry->channelId() : 0; + if(evaluated_order_id != channel->previousChannelId()) { channel->setPreviousChannelId(evaluated_order_id); - debugMessage(serverId, "Fixed order id for channel {} ({}). New previous channel {}", entry->entry->channelId(), channel->name(), channel->channelOrder()); - } + debugMessage(serverId, "Fixed order id for channel {} ({}). New previous channel {}", entry->entry->channelId(), channel->name(), channel->channelOrder()); + } auto evaluated_parent_id = channel->parent() ? channel->parent()->channelId() : 0; if(evaluated_parent_id != channel->properties()[property::CHANNEL_PID].as()) { @@ -316,8 +316,8 @@ inline std::shared_ptr buildChannelTree(ServerId serv channel->properties()[property::CHANNEL_PID] = evaluated_parent_id; } entry = entry->next; - } - } + } + } { /* building sub trees */ auto entry = heads[0]; @@ -327,228 +327,228 @@ inline std::shared_ptr buildChannelTree(ServerId serv } } - return heads[0]; + return heads[0]; } bool ServerChannelTree::buildChannelTreeFromTemp() { - if(this->tmpChannelList.empty()) return true; + if(this->tmpChannelList.empty()) return true; - this->head = buildChannelTree(this->getServerId(), nullptr, this->tmpChannelList); - assert(tmpChannelList.empty()); - return true; + this->head = buildChannelTree(this->getServerId(), nullptr, this->tmpChannelList); + assert(tmpChannelList.empty()); + return true; } inline ssize_t count_characters(const std::string& in) { - size_t index = 0; - size_t count = 0; - while(index < in.length()){ - count++; + size_t index = 0; + size_t count = 0; + while(index < in.length()){ + count++; - auto current = (uint8_t) in[index]; - if(current >= 128) { //UTF8 check - if(current >= 192 && (current <= 193 || current >= 245)) { - return -1; - } else if(current >= 194 && current <= 223) { - if(in.length() - index <= 1) return -1; - else if((uint8_t) in[index + 1] >= 128 && (uint8_t) in[index + 1] <= 191) index += 1; //Valid - else return -1; - } else if(current >= 224 && current <= 239) { - if(in.length() - index <= 2) return -1; - else if((uint8_t) in[index + 1] >= 128 && (uint8_t) in[index + 1] <= 191 && - (uint8_t) in[index + 2] >= 128 && (uint8_t) in[index + 2] <= 191) index += 2; //Valid - else return -1; - } else if(current >= 240 && current <= 244) { - if(in.length() - index <= 3) return -1; - else if((uint8_t) in[index + 1] >= 128 && (uint8_t) in[index + 1] <= 191 && - (uint8_t) in[index + 2] >= 128 && (uint8_t) in[index + 2] <= 191 && - (uint8_t) in[index + 3] >= 128 && (uint8_t) in[index + 3] <= 191) index += 3; //Valid - else return -1; - } else { - return -1; - } - } - index++; - } - return count; + auto current = (uint8_t) in[index]; + if(current >= 128) { //UTF8 check + if(current >= 192 && (current <= 193 || current >= 245)) { + return -1; + } else if(current >= 194 && current <= 223) { + if(in.length() - index <= 1) return -1; + else if((uint8_t) in[index + 1] >= 128 && (uint8_t) in[index + 1] <= 191) index += 1; //Valid + else return -1; + } else if(current >= 224 && current <= 239) { + if(in.length() - index <= 2) return -1; + else if((uint8_t) in[index + 1] >= 128 && (uint8_t) in[index + 1] <= 191 && + (uint8_t) in[index + 2] >= 128 && (uint8_t) in[index + 2] <= 191) index += 2; //Valid + else return -1; + } else if(current >= 240 && current <= 244) { + if(in.length() - index <= 3) return -1; + else if((uint8_t) in[index + 1] >= 128 && (uint8_t) in[index + 1] <= 191 && + (uint8_t) in[index + 2] >= 128 && (uint8_t) in[index + 2] <= 191 && + (uint8_t) in[index + 3] >= 128 && (uint8_t) in[index + 3] <= 191) index += 3; //Valid + else return -1; + } else { + return -1; + } + } + index++; + } + return count; } bool ServerChannelTree::validateChannelNames() { - /* + /* if (count_characters(cmd["channel_name"]) < 1) return {findError("channel_name_inuse"), "Invalid channel name (too short)"}; if (count_characters(cmd["channel_name"]) > 40) return {findError("channel_name_inuse"), "Invalid channel name (too long)"}; - */ - /* - for(const auto &channel : this->channels()){ - mainSearch: - for(const auto& ref : this->channels(channel->parent(), 1)) - if(ref->name() == channel->name() && ref != channel) { - logError(lstream << "Duplicated channel name '" << channel->name() << "'. Fixing it by appending '1'" << endl); - channel->properties()["channel_name"] = channel->name() + "1"; - goto mainSearch; - } - if(channel->name().length() > 40) { - channel->properties()["channel_name"] = channel->name().substr(0, 35) + rnd_string(5); - goto mainSearch; - } - } - */ + */ + /* + for(const auto &channel : this->channels()){ + mainSearch: + for(const auto& ref : this->channels(channel->parent(), 1)) + if(ref->name() == channel->name() && ref != channel) { + logError(lstream << "Duplicated channel name '" << channel->name() << "'. Fixing it by appending '1'" << endl); + channel->properties()["channel_name"] = channel->name() + "1"; + goto mainSearch; + } + if(channel->name().length() > 40) { + channel->properties()["channel_name"] = channel->name().substr(0, 35) + rnd_string(5); + goto mainSearch; + } + } + */ - for(const auto &channel : this->channels()){ - auto name_length = count_characters(channel->name()); - if(name_length > 40) { - logError(this->getServerId(), "Channel {} loaded an invalid name from the database (name to long). Cutting channel name"); - channel->properties()[property::CHANNEL_NAME] = channel->name().substr(0, 40); //FIXME count UTF8 - } else if(name_length < 1) { - logError(this->getServerId(), "Channel {} loaded an invalid name from the database (empty name). Resetting channel name"); - channel->properties()[property::CHANNEL_NAME] = "undefined"; - } - } + for(const auto &channel : this->channels()){ + auto name_length = count_characters(channel->name()); + if(name_length > 40) { + logError(this->getServerId(), "Channel {} loaded an invalid name from the database (name to long). Cutting channel name"); + channel->properties()[property::CHANNEL_NAME] = channel->name().substr(0, 40); //FIXME count UTF8 + } else if(name_length < 1) { + logError(this->getServerId(), "Channel {} loaded an invalid name from the database (empty name). Resetting channel name"); + channel->properties()[property::CHANNEL_NAME] = "undefined"; + } + } - function &)> test_level; + function &)> test_level; - test_level = [&](const std::shared_ptr &head) { - map> used_names; - auto it = head; - while(it) { - auto channel = dynamic_pointer_cast(it->entry); - auto name = channel->name(); + test_level = [&](const std::shared_ptr &head) { + map> used_names; + auto it = head; + while(it) { + auto channel = dynamic_pointer_cast(it->entry); + auto name = channel->name(); - if(used_names.count(name) > 0) { - auto taken_channel = used_names[name]; - assert(taken_channel); + if(used_names.count(name) > 0) { + auto taken_channel = used_names[name]; + assert(taken_channel); - size_t index = 1; - while(true) { - auto _name = name + to_string(index); - if(_name.length() > 40) //FIXME count UTF8 - _name = _name.substr(_name.length() - 40); - if(used_names[_name]) - index++; - else { - name = _name; - break; - } - } - channel->properties()[property::CHANNEL_NAME] = name; + size_t index = 1; + while(true) { + auto _name = name + to_string(index); + if(_name.length() > 40) //FIXME count UTF8 + _name = _name.substr(_name.length() - 40); + if(used_names[_name]) + index++; + else { + name = _name; + break; + } + } + channel->properties()[property::CHANNEL_NAME] = name; - logError(this->getServerId(), "Channel {} has the same name as channel {}. Name: {}. Changing name to {} by appending an index.", - channel->channelId(), - taken_channel->channelId(), - taken_channel->name(), - name - ); - } - used_names[name] = channel; + logError(this->getServerId(), "Channel {} has the same name as channel {}. Name: {}. Changing name to {} by appending an index.", + channel->channelId(), + taken_channel->channelId(), + taken_channel->name(), + name + ); + } + used_names[name] = channel; - if(it->child_head) - test_level(it->child_head); - it = it->next; - } - }; + if(it->child_head) + test_level(it->child_head); + it = it->next; + } + }; - test_level(this->tree_head()); - return true; + test_level(this->tree_head()); + return true; } bool ServerChannelTree::validateChannelIcons() { - for(const auto &channel : this->channels()) { - auto iconId = (IconId) channel->properties()[property::CHANNEL_ICON_ID]; - if(iconId != 0 && !serverInstance->getFileServer()->iconExists(this->server.lock(), iconId)) { - logMessage(this->getServerId(), "[FILE] Missing channel icon (" + to_string(iconId) + ")."); - if(config::server::delete_missing_icon_permissions) { - channel->properties()[property::CHANNEL_ICON_ID] = 0; - channel->permissions()->set_permission(permission::i_icon_id, {0, 0}, permission::v2::PermissionUpdateType::set_value, permission::v2::PermissionUpdateType::do_nothing); - } - } - } - return true; + for(const auto &channel : this->channels()) { + auto iconId = (IconId) channel->properties()[property::CHANNEL_ICON_ID]; + if(iconId != 0 && !serverInstance->getFileServer()->iconExists(this->server.lock(), iconId)) { + logMessage(this->getServerId(), "[FILE] Missing channel icon (" + to_string(iconId) + ")."); + if(config::server::delete_missing_icon_permissions) { + channel->properties()[property::CHANNEL_ICON_ID] = 0; + channel->permissions()->set_permission(permission::i_icon_id, {0, 0}, permission::v2::PermissionUpdateType::set_value, permission::v2::PermissionUpdateType::do_nothing); + } + } + } + return true; } void ServerChannelTree::loadChannelsFromDatabase() { - auto res = sql::command(this->sql, "SELECT * FROM `channels` WHERE `serverId` = :sid", variable{":sid", this->getServerId()}).query(&ServerChannelTree::loadChannelFromData, this); - (LOG_SQL_CMD)(res); - if(!res){ - logError(this->getServerId(), "Could not load channel tree from database"); - return; - } + auto res = sql::command(this->sql, "SELECT * FROM `channels` WHERE `serverId` = :sid", variable{":sid", this->getServerId()}).query(&ServerChannelTree::loadChannelFromData, this); + (LOG_SQL_CMD)(res); + if(!res){ + logError(this->getServerId(), "Could not load channel tree from database"); + return; + } - logMessage(this->getServerId(), "Loaded {} saved channels. Assembling...", this->tmpChannelList.size()); - this->initializeTempParents(); - this->buildChannelTreeFromTemp(); - this->validateChannelNames(); - this->validateChannelIcons(); - //this->printChannelTree(); + logMessage(this->getServerId(), "Loaded {} saved channels. Assembling...", this->tmpChannelList.size()); + this->initializeTempParents(); + this->buildChannelTreeFromTemp(); + this->validateChannelNames(); + this->validateChannelIcons(); + //this->printChannelTree(); } int ServerChannelTree::loadChannelFromData(int argc, char **data, char **column) { - ChannelId channelId = 0; - ChannelId parentId = 0; - auto type = static_cast(0xFF); + ChannelId channelId = 0; + ChannelId parentId = 0; + auto type = static_cast(0xFF); - int index = 0; - try { - for(index = 0; index < argc; index++){ - if(strcmp(column[index], "channelId") == 0) channelId = static_cast(stoll(data[index])); - else if(strcmp(column[index], "parentId") == 0) parentId = static_cast(stoll(data[index])); - else if(strcmp(column[index], "type") == 0) type = static_cast(stoll(data[index])); - else if(strcmp(column[index], "serverId") == 0) {} - else logError(this->getServerId(), "ServerChannelTree::loadChannelFromData called with invalid column from sql \"{}\"", column[index]); - } - } catch (std::exception& ex) { - logError(this->getServerId(), "Failed to load channel. Got exception {}. Exception was thrown at parsing row {} with data \"{}\"", ex.what(), column[index], data[index]); - return 0; - } + int index = 0; + try { + for(index = 0; index < argc; index++){ + if(strcmp(column[index], "channelId") == 0) channelId = static_cast(stoll(data[index])); + else if(strcmp(column[index], "parentId") == 0) parentId = static_cast(stoll(data[index])); + else if(strcmp(column[index], "type") == 0) type = static_cast(stoll(data[index])); + else if(strcmp(column[index], "serverId") == 0) {} + else logError(this->getServerId(), "ServerChannelTree::loadChannelFromData called with invalid column from sql \"{}\"", column[index]); + } + } catch (std::exception& ex) { + logError(this->getServerId(), "Failed to load channel. Got exception {}. Exception was thrown at parsing row {} with data \"{}\"", ex.what(), column[index], data[index]); + return 0; + } - //assert(type != 0xFF); - assert(channelId != 0); + //assert(type != 0xFF); + assert(channelId != 0); - auto channel = std::make_shared(parentId, channelId); - auto server = this->server.lock(); - static_pointer_cast(channel)->setProperties(serverInstance->databaseHelper()->loadChannelProperties(server, channelId)); - static_pointer_cast(channel)->setPermissionManager(serverInstance->databaseHelper()->loadChannelPermissions(server, channel->channelId())); + auto channel = std::make_shared(parentId, channelId); + auto server = this->server.lock(); + static_pointer_cast(channel)->setProperties(serverInstance->databaseHelper()->loadChannelProperties(server, channelId)); + static_pointer_cast(channel)->setPermissionManager(serverInstance->databaseHelper()->loadChannelPermissions(server, channel->channelId())); - auto entry = make_shared(channel); - channel->setLinkedHandle(entry); - this->tmpChannelList.push_back(entry); - return 0; + auto entry = make_shared(channel); + channel->setLinkedHandle(entry); + this->tmpChannelList.push_back(entry); + return 0; } deque ServerChannelTree::deleteChannelRoot(const std::shared_ptr &channel) { - auto server = this->server.lock(); + auto server = this->server.lock(); - auto channels = this->delete_channel_root(channel); - deque channel_ids; - for(const auto& channel : channels) - channel_ids.push_back(channel->channelId()); - return channel_ids; + auto channels = this->delete_channel_root(channel); + deque channel_ids; + for(const auto& channel : channels) + channel_ids.push_back(channel->channelId()); + return channel_ids; } void ServerChannelTree::on_channel_entry_deleted(const shared_ptr &channel) { - BasicChannelTree::on_channel_entry_deleted(channel); + BasicChannelTree::on_channel_entry_deleted(channel); - auto server = this->server.lock(); - if(server) { - server->getGroupManager()->handleChannelDeleted(channel->channelId()); - server->conversation_manager()->delete_conversation(channel->channelId()); - } else - serverInstance->getGroupManager()->handleChannelDeleted(channel->channelId()); + auto server = this->server.lock(); + if(server) { + server->getGroupManager()->handleChannelDeleted(channel->channelId()); + server->conversation_manager()->delete_conversation(channel->channelId()); + } else + 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(); - LOG_SQL_CMD(sql_result); + auto sql_result = sql::command(this->sql, "DELETE FROM `channels` WHERE `serverId` = '" + to_string(this->getServerId()) + "' AND `channelId` = '" + to_string(channel->channelId()) + "'").execute(); + LOG_SQL_CMD(sql_result); - sql_result = sql::command(this->sql, "DELETE FROM `properties` WHERE `serverId` = '" + to_string(this->getServerId()) + "' AND `id` = '" + to_string(channel->channelId()) + "' AND `type` = " + to_string(property::PropertyType::PROP_TYPE_CHANNEL)).execute(); - LOG_SQL_CMD(sql_result); + sql_result = sql::command(this->sql, "DELETE FROM `properties` WHERE `serverId` = '" + to_string(this->getServerId()) + "' AND `id` = '" + to_string(channel->channelId()) + "' AND `type` = " + to_string(property::PropertyType::PROP_TYPE_CHANNEL)).execute(); + LOG_SQL_CMD(sql_result); - serverInstance->databaseHelper()->deleteChannelPermissions(this->server.lock(), channel->channelId()); - sql_result = sql::command(this->sql, "DELETE FROM `assignedGroups` WHERE `serverId` = '" + to_string(this->getServerId()) + "' AND `channelId` = '" + to_string(channel->channelId()) + "'").execute(); - LOG_SQL_CMD(sql_result); + serverInstance->databaseHelper()->deleteChannelPermissions(this->server.lock(), channel->channelId()); + sql_result = sql::command(this->sql, "DELETE FROM `assignedGroups` WHERE `serverId` = '" + to_string(this->getServerId()) + "' AND `channelId` = '" + to_string(channel->channelId()) + "'").execute(); + LOG_SQL_CMD(sql_result); } std::shared_ptr ServerChannelTree::allocateChannel(const shared_ptr &parent, ChannelId channelId) { - return std::make_shared(parent ? parent->channelId() : 0, channelId); + return std::make_shared(parent ? parent->channelId() : 0, channelId); } diff --git a/server/src/client/ConnectedClient.cpp b/server/src/client/ConnectedClient.cpp index 5eb20c6..14a043f 100644 --- a/server/src/client/ConnectedClient.cpp +++ b/server/src/client/ConnectedClient.cpp @@ -24,8 +24,8 @@ using namespace ts::token; extern ts::server::InstanceHandler* serverInstance; ConnectedClient::ConnectedClient(sql::SqlManager* db, const std::shared_ptr&server) : DataClient(db, server) { - memtrack::allocated(this); - memset(&this->remote_address, 0, sizeof(this->remote_address)); + memtrack::allocated(this); + memset(&this->remote_address, 0, sizeof(this->remote_address)); connectionStatistics = make_shared(server ? server->getServerStatistics() : nullptr, false); this->connectionStatistics->measure_bandwidths(false); /* done by the client and we trust this */ @@ -34,43 +34,43 @@ ConnectedClient::ConnectedClient(sql::SqlManager* db, const std::shared_ptr(this); + memtrack::freed(this); } std::shared_ptr ConnectedClient::request_connection_info(const std::shared_ptr &receiver, bool& send_temp) { - auto& info = this->connection_info; + auto& info = this->connection_info; - lock_guard info_lock(info.lock); + lock_guard info_lock(info.lock); if(info.data){ if(chrono::system_clock::now() - info.data_age < chrono::seconds(1)) return info.data; if(chrono::system_clock::now() - info.data_age > chrono::seconds(5)) //Data timeout - info.data = nullptr; + info.data = nullptr; } if(receiver) { - info.receiver.erase(std::remove_if(info.receiver.begin(), info.receiver.end(), [&](const weak_ptr& weak) { - auto locked = weak.lock(); - if(locked == receiver) { - send_temp = true; - return true; - } - return !locked; - }), info.receiver.end()); - info.receiver.push_back(receiver); + info.receiver.erase(std::remove_if(info.receiver.begin(), info.receiver.end(), [&](const weak_ptr& weak) { + auto locked = weak.lock(); + if(locked == receiver) { + send_temp = true; + return true; + } + return !locked; + }), info.receiver.end()); + info.receiver.push_back(receiver); } if(chrono::system_clock::now() - info.last_requested >= chrono::seconds(1)) { - info.last_requested = chrono::system_clock::now(); + info.last_requested = chrono::system_clock::now(); Command cmd("notifyconnectioninforequest"); string invoker; for(const auto& weak_request : info.receiver) { - auto request = weak_request.lock(); - if(!request) continue; - invoker += (invoker.empty() ? "" : ",") + to_string(request->getClientId()); + auto request = weak_request.lock(); + if(!request) continue; + invoker += (invoker.empty() ? "" : ",") + to_string(request->getClientId()); } cmd["invokerids"] = invoker; @@ -82,82 +82,82 @@ std::shared_ptr ConnectedClient::request_connection_info(con //Attention the client should be only read only locked! void ConnectedClient::updateChannelClientProperties(bool lock_channel_tree, bool notify_self) { - /* this->server may be null! */ - shared_ptr server_ref = this->server; + /* this->server may be null! */ + shared_ptr server_ref = this->server; - auto permissions = this->permissionValues(permission::PERMTEST_ORDERED, { - permission::i_client_talk_power, - permission::b_client_is_priority_speaker, - permission::b_client_ignore_antiflood, - permission::i_channel_view_power, - permission::b_channel_ignore_view_power - }, this->currentChannel); + auto permissions = this->permissionValues(permission::PERMTEST_ORDERED, { + permission::i_client_talk_power, + permission::b_client_is_priority_speaker, + permission::b_client_ignore_antiflood, + permission::i_channel_view_power, + permission::b_channel_ignore_view_power + }, this->currentChannel); - permission::PermissionValue - permission_talk_power = permNotGranted, - permission_priority_speaker = permNotGranted, - permission_ignore_antiflood = permNotGranted, - permission_channel_view_power = permNotGranted, - permission_channel_ignore_view_power = permNotGranted; - for(const auto& perm : permissions) { - if(perm.first == permission::i_client_talk_power) - permission_talk_power = perm.second; - else if(perm.first == permission::b_client_is_priority_speaker) - permission_priority_speaker = perm.second; - else if(perm.first == permission::b_client_ignore_antiflood) - permission_ignore_antiflood = perm.second; - else if(perm.first == permission::i_channel_view_power) - permission_channel_view_power = perm.second; - else if(perm.first == permission::b_channel_ignore_view_power) - permission_channel_ignore_view_power = perm.second; - else sassert(false); - } + permission::PermissionValue + permission_talk_power = permNotGranted, + permission_priority_speaker = permNotGranted, + permission_ignore_antiflood = permNotGranted, + permission_channel_view_power = permNotGranted, + permission_channel_ignore_view_power = permNotGranted; + for(const auto& perm : permissions) { + if(perm.first == permission::i_client_talk_power) + permission_talk_power = perm.second; + else if(perm.first == permission::b_client_is_priority_speaker) + permission_priority_speaker = perm.second; + else if(perm.first == permission::b_client_ignore_antiflood) + permission_ignore_antiflood = perm.second; + else if(perm.first == permission::i_channel_view_power) + permission_channel_view_power = perm.second; + else if(perm.first == permission::b_channel_ignore_view_power) + permission_channel_ignore_view_power = perm.second; + else sassert(false); + } if(permission_talk_power < -2) permission_talk_power = -2; else if(permission_talk_power == -2) permission_talk_power = 0; deque notifyList; - debugMessage(this->getServerId(), "{} Got a channel talk power of {} Talk power set is {}", CLIENT_STR_LOG_PREFIX, permission_talk_power, this->properties()[property::CLIENT_TALK_POWER].as()); + debugMessage(this->getServerId(), "{} Got a channel talk power of {} Talk power set is {}", CLIENT_STR_LOG_PREFIX, permission_talk_power, this->properties()[property::CLIENT_TALK_POWER].as()); if(permission_talk_power != this->properties()[property::CLIENT_TALK_POWER].as()) { //We do not have to update tp if there's no channel this->properties()[property::CLIENT_TALK_POWER] = permission_talk_power; notifyList.emplace_back(property::CLIENT_TALK_POWER); - auto update = this->properties()[property::CLIENT_IS_TALKER].as() || this->properties()[property::CLIENT_TALK_REQUEST].as() > 0; - if(update && this->currentChannel) { - if(this->currentChannel->talk_power_granted({permission_talk_power, permission_talk_power != permNotGranted})) { - this->properties()[property::CLIENT_IS_TALKER] = 0; - this->properties()[property::CLIENT_TALK_REQUEST] = 0; - this->properties()[property::CLIENT_TALK_REQUEST_MSG] = ""; - notifyList.emplace_back(property::CLIENT_IS_TALKER); - notifyList.emplace_back(property::CLIENT_TALK_REQUEST); - notifyList.emplace_back(property::CLIENT_TALK_REQUEST_MSG); - } - } + auto update = this->properties()[property::CLIENT_IS_TALKER].as() || this->properties()[property::CLIENT_TALK_REQUEST].as() > 0; + if(update && this->currentChannel) { + if(this->currentChannel->talk_power_granted({permission_talk_power, permission_talk_power != permNotGranted})) { + this->properties()[property::CLIENT_IS_TALKER] = 0; + this->properties()[property::CLIENT_TALK_REQUEST] = 0; + this->properties()[property::CLIENT_TALK_REQUEST_MSG] = ""; + notifyList.emplace_back(property::CLIENT_IS_TALKER); + notifyList.emplace_back(property::CLIENT_TALK_REQUEST); + notifyList.emplace_back(property::CLIENT_TALK_REQUEST_MSG); + } + } } - IconId iconId = 0; + IconId iconId = 0; auto local_permissions = this->clientPermissions; - if(local_permissions) { - permission::v2::PermissionFlaggedValue value{0, false}; - auto permission_flags = local_permissions->permission_flags(permission::i_icon_id); - if(permission_flags.channel_specific && this->currentChannel) { - auto val = local_permissions->channel_permission(permission::i_icon_id, this->currentChannel->channelId()); - value = {val.values.value, val.flags.value_set}; - } - if(!value.has_value) - value = local_permissions->permission_value_flagged(permission::i_icon_id); - if(value.has_value) - iconId = value.value; - } - logTrace(this->getServerId(), "[CLIENT] Updating client icon from " + to_string(this->properties()[property::CLIENT_ICON_ID].as()) + " to " + to_string(iconId)); + if(local_permissions) { + permission::v2::PermissionFlaggedValue value{0, false}; + auto permission_flags = local_permissions->permission_flags(permission::i_icon_id); + if(permission_flags.channel_specific && this->currentChannel) { + auto val = local_permissions->channel_permission(permission::i_icon_id, this->currentChannel->channelId()); + value = {val.values.value, val.flags.value_set}; + } + if(!value.has_value) + value = local_permissions->permission_value_flagged(permission::i_icon_id); + if(value.has_value) + iconId = value.value; + } + logTrace(this->getServerId(), "[CLIENT] Updating client icon from " + to_string(this->properties()[property::CLIENT_ICON_ID].as()) + " to " + to_string(iconId)); if(this->properties()[property::CLIENT_ICON_ID].as() != iconId){ - if(server_ref && iconId != 0) { - auto dir = serverInstance->getFileServer()->iconDirectory(server_ref); - if(!serverInstance->getFileServer()->iconExists(server_ref, iconId)) { - logMessage(this->getServerId(), "[FILE] Missing client icon (" + to_string(iconId) + ")."); - iconId = 0; - } - } + if(server_ref && iconId != 0) { + auto dir = serverInstance->getFileServer()->iconDirectory(server_ref); + if(!serverInstance->getFileServer()->iconExists(server_ref, iconId)) { + logMessage(this->getServerId(), "[FILE] Missing client icon (" + to_string(iconId) + ")."); + iconId = 0; + } + } if(this->properties()[property::CLIENT_ICON_ID].as() != iconId) { this->properties()[property::CLIENT_ICON_ID] = (IconId) iconId; notifyList.emplace_back(property::CLIENT_ICON_ID); @@ -172,40 +172,40 @@ void ConnectedClient::updateChannelClientProperties(bool lock_channel_tree, bool block_flood = permission_ignore_antiflood <= 0 || permission_ignore_antiflood == permNotGranted; if(server_ref) - server_ref->notifyClientPropertyUpdates(_this.lock(), notifyList, notify_self); - this->updateTalkRights(permission_talk_power); + server_ref->notifyClientPropertyUpdates(_this.lock(), notifyList, notify_self); + this->updateTalkRights(permission_talk_power); - if((this->channels_view_power != permission_channel_view_power || this->channels_ignore_view != permission_channel_ignore_view_power) && notify_self && this->currentChannel && server_ref) { - this->channels_view_power = permission_channel_view_power; - this->channels_ignore_view = permission_channel_ignore_view_power; + if((this->channels_view_power != permission_channel_view_power || this->channels_ignore_view != permission_channel_ignore_view_power) && notify_self && this->currentChannel && server_ref) { + this->channels_view_power = permission_channel_view_power; + this->channels_ignore_view = permission_channel_ignore_view_power; - shared_lock server_channel_lock(server_ref->channel_tree_lock, defer_lock); - unique_lock client_channel_lock(this->channel_lock, defer_lock); + shared_lock server_channel_lock(server_ref->channel_tree_lock, defer_lock); + unique_lock client_channel_lock(this->channel_lock, defer_lock); - if(lock_channel_tree) { - /* first read lock server channel tree */ - server_channel_lock.lock(); - client_channel_lock.lock(); - } + if(lock_channel_tree) { + /* first read lock server channel tree */ + server_channel_lock.lock(); + client_channel_lock.lock(); + } - deque deleted; - for(const auto& update_entry : this->channels->update_channel_path(server_ref->channelTree->tree_head(), server_ref->channelTree->find_linked_entry(this->currentChannel->channelId()))) { - if(update_entry.first) - this->notifyChannelShow(update_entry.second->channel(), update_entry.second->previous_channel); - else deleted.push_back(update_entry.second->channelId()); - } - if(!deleted.empty()) - this->notifyChannelHide(deleted, false); /* we've locked the tree before */ - } + deque deleted; + for(const auto& update_entry : this->channels->update_channel_path(server_ref->channelTree->tree_head(), server_ref->channelTree->find_linked_entry(this->currentChannel->channelId()))) { + if(update_entry.first) + this->notifyChannelShow(update_entry.second->channel(), update_entry.second->previous_channel); + else deleted.push_back(update_entry.second->channelId()); + } + if(!deleted.empty()) + this->notifyChannelHide(deleted, false); /* we've locked the tree before */ + } } void ConnectedClient::updateTalkRights(permission::PermissionValue talk_power) { - bool flag = false; - flag |= this->properties()[property::CLIENT_IS_TALKER].as(); - if(!flag && this->currentChannel) { - flag = this->currentChannel->talk_power_granted({talk_power, talk_power != permNotGranted}); - } - this->allowedToTalk = flag; + bool flag = false; + flag |= this->properties()[property::CLIENT_IS_TALKER].as(); + if(!flag && this->currentChannel) { + flag = this->currentChannel->talk_power_granted({talk_power, talk_power != permNotGranted}); + } + this->allowedToTalk = flag; } void ConnectedClient::resetIdleTime() { @@ -227,232 +227,232 @@ std::deque> ConnectedClient::subscribeChannel(cons auto ref_server = this->server; if(!ref_server) - return {}; + return {}; auto general_granted = enforce || this->permission_granted(this->permissionValue(permission::b_channel_ignore_subscribe_power, nullptr), 1, true); - { - shared_lock server_channel_lock(ref_server->channel_tree_lock, defer_lock); - unique_lock client_channel_lock(this->channel_lock, defer_lock); - if(lock_channel) { - server_channel_lock.lock(); - client_channel_lock.lock(); - } + { + shared_lock server_channel_lock(ref_server->channel_tree_lock, defer_lock); + unique_lock client_channel_lock(this->channel_lock, defer_lock); + if(lock_channel) { + server_channel_lock.lock(); + client_channel_lock.lock(); + } - auto cache = make_shared(); - for (const auto& channel : targets) { - auto local_channel = this->channels->find_channel(channel); - if(!local_channel) continue; //Not visible - if(local_channel->subscribed) continue; //Already subscribed + auto cache = make_shared(); + for (const auto& channel : targets) { + auto local_channel = this->channels->find_channel(channel); + if(!local_channel) continue; //Not visible + if(local_channel->subscribed) continue; //Already subscribed - if(!general_granted && channel != this->currentChannel) { - auto granted_permission = this->calculate_permission_value(permission::i_channel_subscribe_power, channel->channelId()); + if(!general_granted && channel != this->currentChannel) { + auto granted_permission = this->calculate_permission_value(permission::i_channel_subscribe_power, channel->channelId()); - if(!channel->permission_granted(permission::i_channel_needed_subscribe_power, granted_permission, false)) { - auto ignore_power = this->calculate_permission_value(permission::b_channel_ignore_subscribe_power, channel->channelId()); - if(!ignore_power.has_value || ignore_power.value < 1) - continue; - } - } + if(!channel->permission_granted(permission::i_channel_needed_subscribe_power, granted_permission, false)) { + auto ignore_power = this->calculate_permission_value(permission::b_channel_ignore_subscribe_power, channel->channelId()); + if(!ignore_power.has_value || ignore_power.value < 1) + continue; + } + } - local_channel->subscribed = true; - subscribed_channels.push_back(channel); - } + local_channel->subscribed = true; + subscribed_channels.push_back(channel); + } - deque> visible_clients; - for(const auto& target_channel : subscribed_channels) { - /* ref_server->getClientsByChannel only acquires channel client lock */ - for(const auto& client : ref_server->getClientsByChannel(target_channel)) { - visible_clients.push_back(client); - } - } + deque> visible_clients; + for(const auto& target_channel : subscribed_channels) { + /* ref_server->getClientsByChannel only acquires channel client lock */ + for(const auto& client : ref_server->getClientsByChannel(target_channel)) { + visible_clients.push_back(client); + } + } - this->notifyClientEnterView(visible_clients, ViewReasonSystem); + this->notifyClientEnterView(visible_clients, ViewReasonSystem); - if (!subscribed_channels.empty()) - this->notifyChannelSubscribed(subscribed_channels); - } + if (!subscribed_channels.empty()) + this->notifyChannelSubscribed(subscribed_channels); + } return subscribed_channels; } std::deque> ConnectedClient::unsubscribeChannel(const std::deque>& targets, bool lock_channel) { - auto ref_server = this->server; - if(!ref_server) - return {}; + auto ref_server = this->server; + if(!ref_server) + return {}; deque > unsubscribed_channels; - { - shared_lock server_channel_lock(ref_server->channel_tree_lock, defer_lock); - unique_lock client_channel_lock(this->channel_lock, defer_lock); - if(lock_channel) { - server_channel_lock.lock(); - client_channel_lock.lock(); - } + { + shared_lock server_channel_lock(ref_server->channel_tree_lock, defer_lock); + unique_lock client_channel_lock(this->channel_lock, defer_lock); + if(lock_channel) { + server_channel_lock.lock(); + client_channel_lock.lock(); + } - for (const auto& channel : targets) { - if(this->currentChannel == channel) continue; + for (const auto& channel : targets) { + if(this->currentChannel == channel) continue; - auto chan = this->channels->find_channel(channel); - if(!chan || !chan->subscribed) continue; - chan->subscribed = false; + auto chan = this->channels->find_channel(channel); + if(!chan || !chan->subscribed) continue; + chan->subscribed = false; - /* ref_server->getClientsByChannel only acquires channel client lock */ - auto clients = this->server->getClientsByChannel(channel); - this->visibleClients.erase(std::remove_if(this->visibleClients.begin(), this->visibleClients.end(), [&, clients](const weak_ptr& weak) { - auto c = weak.lock(); - if(!c) { - logError(this->getServerId(), "{} Got \"dead\" client in visible client list! This can cause a remote client disconnect within the future!", CLIENT_STR_LOG_PREFIX); - return true; - } + /* ref_server->getClientsByChannel only acquires channel client lock */ + auto clients = this->server->getClientsByChannel(channel); + this->visibleClients.erase(std::remove_if(this->visibleClients.begin(), this->visibleClients.end(), [&, clients](const weak_ptr& weak) { + auto c = weak.lock(); + if(!c) { + logError(this->getServerId(), "{} Got \"dead\" client in visible client list! This can cause a remote client disconnect within the future!", CLIENT_STR_LOG_PREFIX); + return true; + } - return std::find(clients.begin(), clients.end(), c) != clients.end(); - }), this->visibleClients.end()); + return std::find(clients.begin(), clients.end(), c) != clients.end(); + }), this->visibleClients.end()); - unsubscribed_channels.push_back(channel); - } + unsubscribed_channels.push_back(channel); + } - if (!unsubscribed_channels.empty()) - this->notifyChannelUnsubscribed(unsubscribed_channels); - } + if (!unsubscribed_channels.empty()) + this->notifyChannelUnsubscribed(unsubscribed_channels); + } return unsubscribed_channels; } bool ConnectedClient::isClientVisible(const std::shared_ptr& client, bool lock) { - for(const auto& entry : this->getVisibleClients(lock)) - if(entry.lock() == client) - return true; - return false; + for(const auto& entry : this->getVisibleClients(lock)) + if(entry.lock() == client) + return true; + return false; } bool ConnectedClient::notifyClientLeftView(const std::deque> &clients, const std::string &reason, bool lock, const ts::ViewReasonServerLeftT &_vrsl) { - if(clients.empty()) - return true; + if(clients.empty()) + return true; - if(lock) { - /* TODO: add locking of server channel tree in read mode and client tree in write mode! */ - assert(false); - } + if(lock) { + /* TODO: add locking of server channel tree in read mode and client tree in write mode! */ + assert(false); + } - Command cmd("notifyclientleftview"); - cmd["reasonmsg"] = reason; - cmd["reasonid"] = ViewReasonId::VREASON_SERVER_LEFT; - cmd["ctid"] = 0; + Command cmd("notifyclientleftview"); + cmd["reasonmsg"] = reason; + cmd["reasonid"] = ViewReasonId::VREASON_SERVER_LEFT; + cmd["ctid"] = 0; - ChannelId current_channel_id = 0; + ChannelId current_channel_id = 0; - size_t index = 0; - auto it = clients.begin(); - while(it != clients.end()) { - auto client = *it; - assert(client->getClientId() > 0); - assert(client->currentChannel || &*client == this); - if(!client->currentChannel) - continue; + size_t index = 0; + auto it = clients.begin(); + while(it != clients.end()) { + auto client = *it; + assert(client->getClientId() > 0); + assert(client->currentChannel || &*client == this); + if(!client->currentChannel) + continue; - if(current_channel_id != (client->currentChannel ? client->currentChannel->channelId() : 0)) { - if(current_channel_id != 0) - break; - else - cmd[index]["cfid"] = (current_channel_id = client->currentChannel->channelId()); - } - cmd[index]["clid"] = client->getClientId(); - it++; - index++; - } + if(current_channel_id != (client->currentChannel ? client->currentChannel->channelId() : 0)) { + if(current_channel_id != 0) + break; + else + cmd[index]["cfid"] = (current_channel_id = client->currentChannel->channelId()); + } + cmd[index]["clid"] = client->getClientId(); + it++; + index++; + } - this->visibleClients.erase(std::remove_if(this->visibleClients.begin(), this->visibleClients.end(), [&](const weak_ptr& weak) { - auto c = weak.lock(); - if(!c) { - logError(this->getServerId(), "{} Got \"dead\" client in visible client list! This can cause a remote client disconnect within the future!", CLIENT_STR_LOG_PREFIX); - return true; - } - return std::find(clients.begin(), it, c) != it; - }), this->visibleClients.end()); + this->visibleClients.erase(std::remove_if(this->visibleClients.begin(), this->visibleClients.end(), [&](const weak_ptr& weak) { + auto c = weak.lock(); + if(!c) { + logError(this->getServerId(), "{} Got \"dead\" client in visible client list! This can cause a remote client disconnect within the future!", CLIENT_STR_LOG_PREFIX); + return true; + } + return std::find(clients.begin(), it, c) != it; + }), this->visibleClients.end()); - this->sendCommand(cmd); - if(it != clients.end()) - return this->notifyClientLeftView({it, clients.end()}, reason, false, _vrsl); - return true; + this->sendCommand(cmd); + if(it != clients.end()) + return this->notifyClientLeftView({it, clients.end()}, reason, false, _vrsl); + return true; } bool ConnectedClient::notifyClientLeftView( - const shared_ptr &client, - const std::shared_ptr &target_channel, - ViewReasonId reason_id, - const std::string& reason_message, - std::shared_ptr invoker, - bool lock_channel_tree) { - assert(!lock_channel_tree); /* not supported yet! */ - assert(client && client->getClientId() != 0); - assert(client->currentChannel || &*client == this); + const shared_ptr &client, + const std::shared_ptr &target_channel, + ViewReasonId reason_id, + const std::string& reason_message, + std::shared_ptr invoker, + bool lock_channel_tree) { + assert(!lock_channel_tree); /* not supported yet! */ + assert(client && client->getClientId() != 0); + assert(client->currentChannel || &*client == this); - if(client != this) { - if(reason_id == VREASON_SERVER_STOPPED || reason_id == VREASON_SERVER_SHUTDOWN) { - debugMessage(this->getServerId(), "Replacing notify left reason " + to_string(reason_id) + " with " + to_string(VREASON_SERVER_LEFT)); - reason_id = VREASON_SERVER_LEFT; - } - } + if(client != this) { + if(reason_id == VREASON_SERVER_STOPPED || reason_id == VREASON_SERVER_SHUTDOWN) { + debugMessage(this->getServerId(), "Replacing notify left reason " + to_string(reason_id) + " with " + to_string(VREASON_SERVER_LEFT)); + reason_id = VREASON_SERVER_LEFT; + } + } - /* - switch (reasonId) { - case ViewReasonId::VREASON_MOVED: - case ViewReasonId::VREASON_BAN: - case ViewReasonId::VREASON_CHANNEL_KICK: - case ViewReasonId::VREASON_SERVER_KICK: - case ViewReasonId::VREASON_SERVER_SHUTDOWN: - case ViewReasonId::VREASON_SERVER_STOPPED: - if(reasonMessage.empty()) { - logCritical(this->getServerId(), "{} ConnectedClient::notifyClientLeftView() => missing reason message for reason id {}", CLIENT_STR_LOG_PREFIX, reasonId); - reasonMessage = ""; - } - break; - default: - break; - } - */ + /* + switch (reasonId) { + case ViewReasonId::VREASON_MOVED: + case ViewReasonId::VREASON_BAN: + case ViewReasonId::VREASON_CHANNEL_KICK: + case ViewReasonId::VREASON_SERVER_KICK: + case ViewReasonId::VREASON_SERVER_SHUTDOWN: + case ViewReasonId::VREASON_SERVER_STOPPED: + if(reasonMessage.empty()) { + logCritical(this->getServerId(), "{} ConnectedClient::notifyClientLeftView() => missing reason message for reason id {}", CLIENT_STR_LOG_PREFIX, reasonId); + reasonMessage = ""; + } + break; + default: + break; + } + */ - switch (reason_id) { - case ViewReasonId::VREASON_MOVED: - case ViewReasonId::VREASON_BAN: - case ViewReasonId::VREASON_CHANNEL_KICK: - case ViewReasonId::VREASON_SERVER_KICK: - if(!invoker) { - logCritical(this->getServerId(), "{} ConnectedClient::notifyClientLeftView() => missing invoker for reason id {}", CLIENT_STR_LOG_PREFIX, reason_id); - if(this->server) - invoker = this->server->serverRoot; - } - break; - default: - break; - } + switch (reason_id) { + case ViewReasonId::VREASON_MOVED: + case ViewReasonId::VREASON_BAN: + case ViewReasonId::VREASON_CHANNEL_KICK: + case ViewReasonId::VREASON_SERVER_KICK: + if(!invoker) { + logCritical(this->getServerId(), "{} ConnectedClient::notifyClientLeftView() => missing invoker for reason id {}", CLIENT_STR_LOG_PREFIX, reason_id); + if(this->server) + invoker = this->server->serverRoot; + } + break; + default: + break; + } - Command cmd("notifyclientleftview"); - cmd["reasonmsg"] = reason_message; - cmd["reasonid"] = reason_id; - cmd["clid"] = client->getClientId(); - cmd["cfid"] = client->currentChannel ? client->currentChannel->channelId() : 0; - cmd["ctid"] = target_channel ? target_channel->channelId() : 0; + Command cmd("notifyclientleftview"); + cmd["reasonmsg"] = reason_message; + cmd["reasonid"] = reason_id; + cmd["clid"] = client->getClientId(); + cmd["cfid"] = client->currentChannel ? client->currentChannel->channelId() : 0; + cmd["ctid"] = target_channel ? target_channel->channelId() : 0; - if (invoker) { - cmd["invokerid"] = invoker->getClientId(); - cmd["invokername"] = invoker->getDisplayName(); - cmd["invokeruid"] = invoker->getUid(); - } + if (invoker) { + cmd["invokerid"] = invoker->getClientId(); + cmd["invokername"] = invoker->getDisplayName(); + cmd["invokeruid"] = invoker->getUid(); + } - /* TODO: Critical warn if the client hasn't been found? */ - this->visibleClients.erase(std::remove_if(this->visibleClients.begin(), this->visibleClients.end(), [&, client](const weak_ptr& weak) { - auto c = weak.lock(); - if(!c) { - logError(this->getServerId(), "{} Got \"dead\" client in visible client list! This can cause a remote client disconnect within the future!", CLIENT_STR_LOG_PREFIX); - return true; - } - return c == client; - }), this->visibleClients.end()); - this->sendCommand(cmd); - return true; + /* TODO: Critical warn if the client hasn't been found? */ + this->visibleClients.erase(std::remove_if(this->visibleClients.begin(), this->visibleClients.end(), [&, client](const weak_ptr& weak) { + auto c = weak.lock(); + if(!c) { + logError(this->getServerId(), "{} Got \"dead\" client in visible client list! This can cause a remote client disconnect within the future!", CLIENT_STR_LOG_PREFIX); + return true; + } + return c == client; + }), this->visibleClients.end()); + this->sendCommand(cmd); + return true; } bool ConnectedClient::notifyClientLeftViewKicked(const std::shared_ptr &client, @@ -460,25 +460,25 @@ bool ConnectedClient::notifyClientLeftViewKicked(const std::shared_ptr invoker, bool lock_channel_tree) { - assert(!lock_channel_tree); /* not supported yet! */ - assert(client && client->getClientId() != 0); - assert(client->currentChannel || &*client == this); + assert(!lock_channel_tree); /* not supported yet! */ + assert(client && client->getClientId() != 0); + assert(client->currentChannel || &*client == this); - /* TODO: Critical warn if the client hasn't been found? */ - this->visibleClients.erase(std::remove_if(this->visibleClients.begin(), this->visibleClients.end(), [&, client](const weak_ptr& weak) { - auto c = weak.lock(); - if(!c) { - logError(this->getServerId(), "{} Got \"dead\" client in visible client list! This can cause a remote client disconnect within the future!", CLIENT_STR_LOG_PREFIX); - return true; - } - return c == client; - }), this->visibleClients.end()); + /* TODO: Critical warn if the client hasn't been found? */ + this->visibleClients.erase(std::remove_if(this->visibleClients.begin(), this->visibleClients.end(), [&, client](const weak_ptr& weak) { + auto c = weak.lock(); + if(!c) { + logError(this->getServerId(), "{} Got \"dead\" client in visible client list! This can cause a remote client disconnect within the future!", CLIENT_STR_LOG_PREFIX); + return true; + } + return c == client; + }), this->visibleClients.end()); - if(!invoker) { - logCritical(this->getServerId(), "{} ConnectedClient::notifyClientLeftViewKicked() => missing invoker for reason id {}", CLIENT_STR_LOG_PREFIX, target_channel ? ViewReasonId::VREASON_CHANNEL_KICK : ViewReasonId::VREASON_SERVER_KICK); - if(this->server) - invoker = this->server->serverRoot; - } + if(!invoker) { + logCritical(this->getServerId(), "{} ConnectedClient::notifyClientLeftViewKicked() => missing invoker for reason id {}", CLIENT_STR_LOG_PREFIX, target_channel ? ViewReasonId::VREASON_CHANNEL_KICK : ViewReasonId::VREASON_SERVER_KICK); + if(this->server) + invoker = this->server->serverRoot; + } Command cmd("notifyclientleftview"); @@ -499,15 +499,15 @@ bool ConnectedClient::notifyClientLeftViewKicked(const std::shared_ptr &client, - const std::string& message, - std::shared_ptr invoker, - size_t length, - bool lock_channel_tree) { + const shared_ptr &client, + const std::string& message, + std::shared_ptr invoker, + size_t length, + bool lock_channel_tree) { - assert(!lock_channel_tree); /* not supported yet! */ - assert(client && client->getClientId() != 0); - assert(client->currentChannel || &*client == this); + assert(!lock_channel_tree); /* not supported yet! */ + assert(client && client->getClientId() != 0); + assert(client->currentChannel || &*client == this); Command cmd("notifyclientleftview"); @@ -524,55 +524,55 @@ bool ConnectedClient::notifyClientLeftViewBanned( } if (length > 0) { - cmd["bantime"] = length; + cmd["bantime"] = length; } - /* TODO: Critical warn if the client hasn't been found? */ - this->visibleClients.erase(std::remove_if(this->visibleClients.begin(), this->visibleClients.end(), [&, client](const weak_ptr& weak) { - auto c = weak.lock(); - if(!c) { - logError(this->getServerId(), "{} Got \"dead\" client in visible client list! This can cause a remote client disconnect within the future!", CLIENT_STR_LOG_PREFIX); - return true; - } - return c == client; - }), this->visibleClients.end()); + /* TODO: Critical warn if the client hasn't been found? */ + this->visibleClients.erase(std::remove_if(this->visibleClients.begin(), this->visibleClients.end(), [&, client](const weak_ptr& weak) { + auto c = weak.lock(); + if(!c) { + logError(this->getServerId(), "{} Got \"dead\" client in visible client list! This can cause a remote client disconnect within the future!", CLIENT_STR_LOG_PREFIX); + return true; + } + return c == client; + }), this->visibleClients.end()); this->sendCommand(cmd); return true; } bool ConnectedClient::sendNeededPermissions(bool enforce) { - if(!enforce && this->state != ConnectionState::CONNECTED) return false; + if(!enforce && this->state != ConnectionState::CONNECTED) return false; - if(!enforce && chrono::system_clock::now() - this->lastNeededNotify < chrono::seconds(5) && this->lastNeededPermissionNotifyChannel == this->currentChannel) { //Dont spam these (hang up ui) - this->requireNeededPermissionResend = true; - return true; - } - this->lastNeededNotify = chrono::system_clock::now(); - this->lastNeededPermissionNotifyChannel = this->currentChannel; - this->requireNeededPermissionResend = false; + if(!enforce && chrono::system_clock::now() - this->lastNeededNotify < chrono::seconds(5) && this->lastNeededPermissionNotifyChannel == this->currentChannel) { //Dont spam these (hang up ui) + this->requireNeededPermissionResend = true; + return true; + } + this->lastNeededNotify = chrono::system_clock::now(); + this->lastNeededPermissionNotifyChannel = this->currentChannel; + this->requireNeededPermissionResend = false; - return this->notifyClientNeededPermissions(); + return this->notifyClientNeededPermissions(); } bool ConnectedClient::notifyClientNeededPermissions() { Command cmd("notifyclientneededpermissions"); int index = 0; - unique_lock cache_lock(this->cached_permissions_lock); - auto permissions = this->cached_permissions; - cache_lock.unlock(); + unique_lock cache_lock(this->cached_permissions_lock); + auto permissions = this->cached_permissions; + cache_lock.unlock(); - for(const auto& value : permissions) { - if(value.second != permNotGranted || value.first == permission::b_client_force_push_to_talk) { - cmd[index]["permid"] = value.first; - cmd[index++]["permvalue"] = value.second == permNotGranted ? 0 : value.second; - } - } - if(permissions.empty()) { - cmd[0]["permid"] = permission::b_client_force_push_to_talk; - cmd[0]["permvalue"] = false; - } + for(const auto& value : permissions) { + if(value.second != permNotGranted || value.first == permission::b_client_force_push_to_talk) { + cmd[index]["permid"] = value.first; + cmd[index++]["permvalue"] = value.second == permNotGranted ? 0 : value.second; + } + } + if(permissions.empty()) { + cmd[0]["permid"] = permission::b_client_force_push_to_talk; + cmd[0]["permvalue"] = false; + } this->sendCommand(cmd); return true; @@ -607,104 +607,104 @@ inline std::shared_ptr pop_view_entry(std::deque>::iterator; inline void send_channels(ConnectedClient* client, ChannelIT begin, const ChannelIT& end, bool override_orderid){ - if(begin == end) - return; + if(begin == end) + return; - Command channellist("channellist"); - size_t index = 0; + Command channellist("channellist"); + size_t index = 0; - while(begin != end) { - auto channel = (*begin)->channel(); - if(!channel) continue; + while(begin != end) { + auto channel = (*begin)->channel(); + if(!channel) continue; - for (const auto &elm : channel->properties().list_properties(property::FLAG_CHANNEL_VIEW, client->getType() == CLIENT_TEAMSPEAK ? property::FLAG_NEW : (uint16_t) 0)) { - if(elm.type() == property::CHANNEL_ORDER) - channellist[index][elm.type().name] = override_orderid ? 0 : (*begin)->previous_channel; - else - channellist[index][elm.type().name] = elm.as(); - } + for (const auto &elm : channel->properties().list_properties(property::FLAG_CHANNEL_VIEW, client->getType() == CLIENT_TEAMSPEAK ? property::FLAG_NEW : (uint16_t) 0)) { + if(elm.type() == property::CHANNEL_ORDER) + channellist[index][elm.type().name] = override_orderid ? 0 : (*begin)->previous_channel; + else + channellist[index][elm.type().name] = elm.as(); + } - begin++; - if(++index > 6) - break; - } - if(dynamic_cast(client)) { - auto vc = dynamic_cast(client); - vc->sendCommand0(channellist, false, true); /* we need to process this command directly so it will be processed before the channellistfinished stuff */ - } else { - client->sendCommand(channellist); - } - if(begin != end) - send_channels(client, begin, end, override_orderid); + begin++; + if(++index > 6) + break; + } + if(dynamic_cast(client)) { + auto vc = dynamic_cast(client); + vc->sendCommand0(channellist, false, true); /* we need to process this command directly so it will be processed before the channellistfinished stuff */ + } else { + client->sendCommand(channellist); + } + if(begin != end) + send_channels(client, begin, end, override_orderid); } void ConnectedClient::sendChannelList(bool lock_channel_tree) { - shared_lock server_channel_lock(this->server->channel_tree_lock, defer_lock); - unique_lock client_channel_lock(this->channel_lock, defer_lock); - if(lock_channel_tree) { - server_channel_lock.lock(); - client_channel_lock.lock(); - } + shared_lock server_channel_lock(this->server->channel_tree_lock, defer_lock); + unique_lock client_channel_lock(this->channel_lock, defer_lock); + if(lock_channel_tree) { + server_channel_lock.lock(); + client_channel_lock.lock(); + } auto channels = this->channels->insert_channels(this->server->channelTree->tree_head(), true, false); if(this->currentChannel) { - bool send_success; - for(const auto& channel : this->channels->show_channel(this->server->channelTree->find_linked_entry(this->currentChannel->channelId()), send_success)) - channels.push_back(channel); - if(!send_success) - logCritical(this->getServerId(), "ConnectedClient::sendChannelList => failed to insert default channel!"); + bool send_success; + for(const auto& channel : this->channels->show_channel(this->server->channelTree->find_linked_entry(this->currentChannel->channelId()), send_success)) + channels.push_back(channel); + if(!send_success) + logCritical(this->getServerId(), "ConnectedClient::sendChannelList => failed to insert default channel!"); } /* - this->channels->print(); - auto channels_left = channels; - for(const auto& channel : channels) { - if(channel->previous_channel == 0) continue; + this->channels->print(); + auto channels_left = channels; + for(const auto& channel : channels) { + if(channel->previous_channel == 0) continue; - bool erased = false; - bool own = true; - for(const auto& entry : channels_left) { - if(entry->channelId() == channel->channelId()) - own = true; - if(entry->channelId() == channel->previous_channel) { - channels_left.erase(find(channels_left.begin(), channels_left.end(), entry)); - erased = true; - break; - } - } - if(!erased || !own) { - logCritical(this->getServerId(), "Client {} would get an invalid channel order disconnect! Channel {} is not send before his channel! (Flags: erased := {} | own := {})", CLIENT_STR_LOG_PREFIX_(this), channel->previous_channel, erased, own); - } - } + bool erased = false; + bool own = true; + for(const auto& entry : channels_left) { + if(entry->channelId() == channel->channelId()) + own = true; + if(entry->channelId() == channel->previous_channel) { + channels_left.erase(find(channels_left.begin(), channels_left.end(), entry)); + erased = true; + break; + } + } + if(!erased || !own) { + logCritical(this->getServerId(), "Client {} would get an invalid channel order disconnect! Channel {} is not send before his channel! (Flags: erased := {} | own := {})", CLIENT_STR_LOG_PREFIX_(this), channel->previous_channel, erased, own); + } + } */ std::deque> entry_channels{pop_view_entry(channels, this->currentChannel->channelId())}; while(entry_channels.front()) entry_channels.push_front(pop_view_entry(channels, entry_channels.front()->parentId())); entry_channels.pop_front(); - if(entry_channels.empty()) - logCritical(this->getServerId(), "ConnectedClient::sendChannelList => invalid (empty) own channel path!"); + if(entry_channels.empty()) + logCritical(this->getServerId(), "ConnectedClient::sendChannelList => invalid (empty) own channel path!"); send_channels(this, entry_channels.begin(), entry_channels.end(), true); - this->notifyClientEnterView(_this.lock(), nullptr, "", this->currentChannel, ViewReasonId::VREASON_SYSTEM, nullptr, false); //Notify self after path is send + this->notifyClientEnterView(_this.lock(), nullptr, "", this->currentChannel, ViewReasonId::VREASON_SYSTEM, nullptr, false); //Notify self after path is send - send_channels(this, channels.begin(), channels.end(), false); + send_channels(this, channels.begin(), channels.end(), false); this->sendCommand(Command("channellistfinished")); } void ConnectedClient::sendChannelDescription(const std::shared_ptr& channel, bool lock) { - shared_lock tree_lock(this->channel_lock, defer_lock); - if(lock) - tree_lock.lock(); + shared_lock tree_lock(this->channel_lock, defer_lock); + if(lock) + tree_lock.lock(); - if(!this->channels->channel_visible(channel)) return; + if(!this->channels->channel_visible(channel)) return; - auto limit = this->getType() == CLIENT_TEAMSPEAK ? 8192 : 131130; + auto limit = this->getType() == CLIENT_TEAMSPEAK ? 8192 : 131130; - auto description = channel->properties()[property::CHANNEL_DESCRIPTION].as(); - Command cmd("notifychanneledited"); + auto description = channel->properties()[property::CHANNEL_DESCRIPTION].as(); + Command cmd("notifychanneledited"); cmd["cid"] = channel->channelId(); cmd["reasonid"] = 9; cmd["channel_description"] = description.size() > limit ? description.substr(0, limit) : description; @@ -712,51 +712,51 @@ void ConnectedClient::sendChannelDescription(const std::shared_ptr } void ConnectedClient::tick(const std::chrono::system_clock::time_point &time) { - ALARM_TIMER(A1, "ConnectedClient::tick", milliseconds(2)); - if(this->state == ConnectionState::CONNECTED) { - if(this->requireNeededPermissionResend) - this->sendNeededPermissions(false); - if(this->lastOnlineTimestamp.time_since_epoch().count() == 0) { - this->lastOnlineTimestamp = time; - } else if(time - this->lastOnlineTimestamp > seconds(120)) { - this->properties()[property::CLIENT_MONTH_ONLINE_TIME] += duration_cast(time - lastOnlineTimestamp).count(); + ALARM_TIMER(A1, "ConnectedClient::tick", milliseconds(2)); + if(this->state == ConnectionState::CONNECTED) { + if(this->requireNeededPermissionResend) + this->sendNeededPermissions(false); + if(this->lastOnlineTimestamp.time_since_epoch().count() == 0) { + this->lastOnlineTimestamp = time; + } else if(time - this->lastOnlineTimestamp > seconds(120)) { + this->properties()[property::CLIENT_MONTH_ONLINE_TIME] += duration_cast(time - lastOnlineTimestamp).count(); this->properties()[property::CLIENT_TOTAL_ONLINE_TIME] += duration_cast(time - lastOnlineTimestamp).count(); lastOnlineTimestamp = time; - } - if(time - this->lastTransfareTimestamp > seconds(5)) { - lastTransfareTimestamp = time; - auto update = this->connectionStatistics->mark_file_bytes(); - if(update.first > 0) { - this->properties()[property::CLIENT_MONTH_BYTES_DOWNLOADED] += update.first; - this->properties()[property::CLIENT_TOTAL_BYTES_DOWNLOADED] += update.first; - } - if(update.second > 0) { - this->properties()[property::CLIENT_MONTH_BYTES_UPLOADED] += update.second; - this->properties()[property::CLIENT_TOTAL_BYTES_UPLOADED] += update.second; - } - } - } + } + if(time - this->lastTransfareTimestamp > seconds(5)) { + lastTransfareTimestamp = time; + auto update = this->connectionStatistics->mark_file_bytes(); + if(update.first > 0) { + this->properties()[property::CLIENT_MONTH_BYTES_DOWNLOADED] += update.first; + this->properties()[property::CLIENT_TOTAL_BYTES_DOWNLOADED] += update.first; + } + if(update.second > 0) { + this->properties()[property::CLIENT_MONTH_BYTES_UPLOADED] += update.second; + this->properties()[property::CLIENT_TOTAL_BYTES_UPLOADED] += update.second; + } + } + } - if(this->last_statistics_tick + seconds(5) < time) { - this->last_statistics_tick = time; - this->connectionStatistics->tick(); - } + if(this->last_statistics_tick + seconds(5) < time) { + this->last_statistics_tick = time; + this->connectionStatistics->tick(); + } } void ConnectedClient::sendServerInit() { Command command("initserver"); for(const auto& prop : this->server->properties().list_properties(property::FLAG_SERVER_VIEW, this->getType() == CLIENT_TEAMSPEAK ? property::FLAG_NEW : (uint16_t) 0)) { - command[prop.type().name] = prop.value(); + command[prop.type().name] = prop.value(); } - command["virtualserver_maxclients"] = 32; + command["virtualserver_maxclients"] = 32; //Server stuff command["client_talk_power"] = this->properties()[property::CLIENT_TALK_POWER].as(); command["client_needed_serverquery_view_power"] = this->properties()[property::CLIENT_NEEDED_SERVERQUERY_VIEW_POWER].as(); - command["client_myteamspeak_id"] = this->properties()[property::CLIENT_MYTEAMSPEAK_ID].as(); - command["client_integrations"] = this->properties()[property::CLIENT_INTEGRATIONS].as(); + command["client_myteamspeak_id"] = this->properties()[property::CLIENT_MYTEAMSPEAK_ID].as(); + command["client_integrations"] = this->properties()[property::CLIENT_INTEGRATIONS].as(); if(ts::config::server::DefaultServerLicense == LicenseType::LICENSE_AUTOMATIC_INSTANCE){ if(serverInstance->getVoiceServerManager()->usedSlots() <= 32) @@ -773,21 +773,21 @@ void ConnectedClient::sendServerInit() { else command["lt"] = LicenseType::LICENSE_HOSTING; } else { - command["lt"] = ts::config::server::DefaultServerLicense; + command["lt"] = ts::config::server::DefaultServerLicense; } - command["pv"] = 6; //Protocol version + command["pv"] = 6; //Protocol version command["acn"] = this->getDisplayName(); command["aclid"] = this->getClientId(); if(dynamic_cast(this)) { - dynamic_cast(this)->sendCommand0(command, false, true); /* process it directly so the order for the channellist entries is ensured. (First serverinit then everything else) */ + dynamic_cast(this)->sendCommand0(command, false, true); /* process it directly so the order for the channellist entries is ensured. (First serverinit then everything else) */ } else { - this->sendCommand(command); + this->sendCommand(command); } } bool ConnectedClient::handleCommandFull(Command& cmd, bool disconnectOnFail) { - system_clock::time_point start, end; - start = system_clock::now(); + system_clock::time_point start, end; + start = system_clock::now(); #ifdef PKT_LOG_CMD logTrace(this->getServerId() == 0 ? LOG_QUERY : this->getServerId(), "{}[Command][Client -> Server] Processing command: {}", CLIENT_STR_LOG_PREFIX, cmd.build(false)); #endif @@ -798,7 +798,7 @@ bool ConnectedClient::handleCommandFull(Command& cmd, bool disconnectOnFail) { } catch(invalid_argument& ex){ debugMessage(this->getServerId(), "{}[Command] Execution throws invalid_argument exception ({}).", CLIENT_STR_LOG_PREFIX, ex.what()); if(disconnectOnFail) { - this->disconnect("Invalid argument (" + string(ex.what()) + ")"); + this->disconnect("Invalid argument (" + string(ex.what()) + ")"); return false; } else { result = {findError("parameter_convert"), "Invalid argument (" + string(ex.what()) + ")"}; @@ -811,8 +811,8 @@ bool ConnectedClient::handleCommandFull(Command& cmd, bool disconnectOnFail) { result = {findError("vs_critical"), "error while command handling (" + string(ex.what()) + ")"}; } } catch (...) { - this->disconnect("Error while command handling! (unknown)"); - return false; + this->disconnect("Error while command handling! (unknown)"); + return false; } bool generateReturnStatus = false; if(result.type() == PERM_ERROR){ @@ -834,14 +834,14 @@ bool ConnectedClient::handleCommandFull(Command& cmd, bool disconnectOnFail) { ss << ", "; } ss << "}" << endl; - logTrace(this->getServerId(), "{}[Command] Command {} with return code {} fails and returns error code {:#06x}. Properties: {}", CLIENT_STR_LOG_PREFIX, cmd.command(), cmd["return_code"].size() ? "\"" + cmd["return_code"].first().as() + "\"" : "", result.error.errorId, ss.str()); + logTrace(this->getServerId(), "{}[Command] Command {} with return code {} fails and returns error code {:#06x}. Properties: {}", CLIENT_STR_LOG_PREFIX, cmd.command(), cmd["return_code"].size() ? "\"" + cmd["return_code"].first().as() + "\"" : "", result.error.errorId, ss.str()); } if(generateReturnStatus) this->notifyError(result, cmd["return_code"].size() > 0 ? cmd["return_code"].first().as() : ""); if(!result && this->state == ConnectionState::INIT_HIGH) { - this->closeConnection(system_clock::now()); //Disconnect now + this->closeConnection(system_clock::now()); //Disconnect now } for (const auto& handler : postCommandHandler) @@ -849,184 +849,184 @@ bool ConnectedClient::handleCommandFull(Command& cmd, bool disconnectOnFail) { postCommandHandler.clear(); end = system_clock::now(); if(end - start > milliseconds(10)) { - if(end - start > milliseconds(100)) - logError(this->getServerId(), "Command handling of command {} needs {}ms. This could be an issue!", cmd.command(), duration_cast(end - start).count()); - else - logWarning(this->getServerId(), "Command handling of command {} needs {}ms.", cmd.command(), duration_cast(end - start).count()); + if(end - start > milliseconds(100)) + logError(this->getServerId(), "Command handling of command {} needs {}ms. This could be an issue!", cmd.command(), duration_cast(end - start).count()); + else + logWarning(this->getServerId(), "Command handling of command {} needs {}ms.", cmd.command(), duration_cast(end - start).count()); } return true; } std::shared_ptr ConnectedClient::resolveActiveBan(const std::string& ip_address) { - if(this->permissionGranted(permission::PERMTEST_ORDERED, permission::b_client_ignore_bans, 1)) return nullptr; + if(this->permissionGranted(permission::PERMTEST_ORDERED, permission::b_client_ignore_bans, 1)) return nullptr; - //Check if manager banned - auto banManager = serverInstance->banManager(); - shared_ptr banEntry = nullptr; - deque> entries; + //Check if manager banned + auto banManager = serverInstance->banManager(); + shared_ptr banEntry = nullptr; + deque> entries; - if (!banEntry) { - banEntry = banManager->findBanByName(this->server->getServerId(), this->getDisplayName()); - if(banEntry) - debugMessage(this->getServerId(), "{} Resolved name ban ({}). Record id {}, server id {}", - CLIENT_STR_LOG_PREFIX, - this->getDisplayName(), - banEntry->banId, - banEntry->serverId); - } - if (!banEntry) { - banEntry = banManager->findBanByUid(this->server->getServerId(), this->getUid()); - if(banEntry) - debugMessage(this->getServerId(), "{} Resolved uuid ban ({}). Record id {}, server id {}", - CLIENT_STR_LOG_PREFIX, - this->getUid(), - banEntry->banId, - banEntry->serverId); - } - if (!banEntry && !ip_address.empty()) { - banEntry = banManager->findBanByIp(this->server->getServerId(), ip_address); - if(banEntry) - debugMessage(this->getServerId(), "{} Resolved ip ban ({}). Record id {}, server id {}", - CLIENT_STR_LOG_PREFIX, - ip_address, - banEntry->banId, - banEntry->serverId); - } - auto vclient = dynamic_cast(this); - if(vclient) - if (!banEntry) { - banEntry = banManager->findBanByHwid(this->server->getServerId(), vclient->getHardwareId()); - if(banEntry) - debugMessage(this->getServerId(), "{} Resolved hwid ban ({}). Record id {}, server id {}", - CLIENT_STR_LOG_PREFIX, - vclient->getHardwareId(), - banEntry->banId, - banEntry->serverId); - } + if (!banEntry) { + banEntry = banManager->findBanByName(this->server->getServerId(), this->getDisplayName()); + if(banEntry) + debugMessage(this->getServerId(), "{} Resolved name ban ({}). Record id {}, server id {}", + CLIENT_STR_LOG_PREFIX, + this->getDisplayName(), + banEntry->banId, + banEntry->serverId); + } + if (!banEntry) { + banEntry = banManager->findBanByUid(this->server->getServerId(), this->getUid()); + if(banEntry) + debugMessage(this->getServerId(), "{} Resolved uuid ban ({}). Record id {}, server id {}", + CLIENT_STR_LOG_PREFIX, + this->getUid(), + banEntry->banId, + banEntry->serverId); + } + if (!banEntry && !ip_address.empty()) { + banEntry = banManager->findBanByIp(this->server->getServerId(), ip_address); + if(banEntry) + debugMessage(this->getServerId(), "{} Resolved ip ban ({}). Record id {}, server id {}", + CLIENT_STR_LOG_PREFIX, + ip_address, + banEntry->banId, + banEntry->serverId); + } + auto vclient = dynamic_cast(this); + if(vclient) + if (!banEntry) { + banEntry = banManager->findBanByHwid(this->server->getServerId(), vclient->getHardwareId()); + if(banEntry) + debugMessage(this->getServerId(), "{} Resolved hwid ban ({}). Record id {}, server id {}", + CLIENT_STR_LOG_PREFIX, + vclient->getHardwareId(), + banEntry->banId, + banEntry->serverId); + } - return banEntry; + return banEntry; } bool ConnectedClient::update_cached_permissions() { - auto values = this->permissionValues(permission::PERMTEST_ORDERED, permission::neededPermissions, shared_ptr(this->currentChannel)); /* copy the channel here so it does not change */ - auto updated = false; + auto values = this->permissionValues(permission::PERMTEST_ORDERED, permission::neededPermissions, shared_ptr(this->currentChannel)); /* copy the channel here so it does not change */ + auto updated = false; - { - lock_guard cached_lock(this->cached_permissions_lock); + { + lock_guard cached_lock(this->cached_permissions_lock); - vector old_permissions; - old_permissions.reserve(this->cached_permissions.size()); + vector old_permissions; + old_permissions.reserve(this->cached_permissions.size()); - for(const auto& value : this->cached_permissions) - old_permissions.push_back(value.first); + for(const auto& value : this->cached_permissions) + old_permissions.push_back(value.first); - for(const auto& value : values) { - auto value_it = cached_permissions.find(value.first); - if(value_it == cached_permissions.end()) { /* new entry */ - updated = true; - this->cached_permissions[value.first] = value.second; - continue; /* no need to remove that from old_permissions because it isn't there */ - } else if(value_it->second != value.second) { /* entry changed */ - updated = true; - value_it->second = value.second; - } + for(const auto& value : values) { + auto value_it = cached_permissions.find(value.first); + if(value_it == cached_permissions.end()) { /* new entry */ + updated = true; + this->cached_permissions[value.first] = value.second; + continue; /* no need to remove that from old_permissions because it isn't there */ + } else if(value_it->second != value.second) { /* entry changed */ + updated = true; + value_it->second = value.second; + } - { /* we've updated the value or verified it */ - auto old_it = find(old_permissions.begin(), old_permissions.end(), value.first); - if(old_it != old_permissions.end()) - old_permissions.erase(old_it); - } - } + { /* we've updated the value or verified it */ + auto old_it = find(old_permissions.begin(), old_permissions.end(), value.first); + if(old_it != old_permissions.end()) + old_permissions.erase(old_it); + } + } - for(const auto& left : old_permissions) { - auto value_it = cached_permissions.find(left); - if(value_it != cached_permissions.end()) { - cached_permissions.erase(value_it); - updated = true; - } - } - } + for(const auto& left : old_permissions) { + auto value_it = cached_permissions.find(left); + if(value_it != cached_permissions.end()) { + cached_permissions.erase(value_it); + updated = true; + } + } + } - return updated; + return updated; } void ConnectedClient::sendTSPermEditorWarning() { - if(config::voice::warn_on_permission_editor) { - if(system_clock::now() - this->command_times.servergrouplist > milliseconds(1000)) return; - if(system_clock::now() - this->command_times.channelgrouplist > milliseconds(1000)) return; + if(config::voice::warn_on_permission_editor) { + if(system_clock::now() - this->command_times.servergrouplist > milliseconds(1000)) return; + if(system_clock::now() - this->command_times.channelgrouplist > milliseconds(1000)) return; - this->command_times.last_notify = system_clock::now(); - this->notifyClientPoke(this->server->serverRoot, config::messages::teamspeak_permission_editor); - } + this->command_times.last_notify = system_clock::now(); + this->notifyClientPoke(this->server->serverRoot, config::messages::teamspeak_permission_editor); + } } permission::v2::PermissionFlaggedValue ConnectedClient::calculate_permission_value(const ts::permission::PermissionType &permission, ts::ChannelId channel_id) { - if(channel_id == (this->currentChannel ? this->currentChannel->channelId() : 0) || channel_id == -1) { - std::lock_guard lock(this->cached_permissions_lock); - auto index = this->cached_permissions.find(permission); - if(index != this->cached_permissions.end()) - return {index->second, index->second != permNotGranted}; - } + if(channel_id == (this->currentChannel ? this->currentChannel->channelId() : 0) || channel_id == -1) { + std::lock_guard lock(this->cached_permissions_lock); + auto index = this->cached_permissions.find(permission); + if(index != this->cached_permissions.end()) + return {index->second, index->second != permNotGranted}; + } - auto ref_server = this->server; - if(ref_server) { - auto result = this->server->calculatePermissions2(this->getClientDatabaseId(), {permission}, this->getType(), channel_id, false); - if(!result.empty()) /* it should never be empty! */ - return result.back().second; - } + auto ref_server = this->server; + if(ref_server) { + auto result = this->server->calculatePermissions2(this->getClientDatabaseId(), {permission}, this->getType(), channel_id, false); + if(!result.empty()) /* it should never be empty! */ + return result.back().second; + } - auto value = this->permissionValue(permission::PERMTEST_ORDERED, permission, nullptr); - return {value, value != permNotGranted}; + auto value = this->permissionValue(permission::PERMTEST_ORDERED, permission, nullptr); + return {value, value != permNotGranted}; } #define RESULT(flag) \ do { \ - ventry->join_state_id = this->join_state_id; \ - ventry->joinable = (flag); \ - return flag; \ + ventry->join_state_id = this->join_state_id; \ + ventry->joinable = (flag); \ + return flag; \ } while(0) bool ConnectedClient::calculate_and_get_join_state(const std::shared_ptr& channel) { - shared_ptr ventry; - { - shared_lock view_lock(this->channel_lock); - ventry = this->channel_view()->find_channel(channel); - if(!ventry) - return false; - } - if(ventry->join_state_id == this->join_state_id) - return ventry->joinable; + shared_ptr ventry; + { + shared_lock view_lock(this->channel_lock); + ventry = this->channel_view()->find_channel(channel); + if(!ventry) + return false; + } + if(ventry->join_state_id == this->join_state_id) + return ventry->joinable; - auto permission_cache = make_shared(); - switch(channel->channelType()) { - case ChannelType::permanent: - if(!this->permissionGranted(permission::PERMTEST_ORDERED, permission::b_channel_join_permanent, 1, channel, true, permission_cache)) - RESULT(false); - break; - case ChannelType::semipermanent: - if(!this->permissionGranted(permission::PERMTEST_ORDERED, permission::b_channel_join_semi_permanent, 1, channel, true, permission_cache)) - RESULT(false); - break; - case ChannelType::temporary: - if(!this->permissionGranted(permission::PERMTEST_ORDERED, permission::b_channel_join_temporary, 1, channel, true, permission_cache)) - RESULT(false); - break; - } - if(!this->permissionGranted(permission::PERMTEST_ORDERED, permission::b_channel_ignore_join_power, 1, channel, true, permission_cache)) { - auto result = this->server->calculatePermissions2(this->getClientDatabaseId(), {permission::i_channel_join_power}, this->getType(), channel->channelId(), false, permission_cache); - if(result.empty()) - RESULT(false); + auto permission_cache = make_shared(); + switch(channel->channelType()) { + case ChannelType::permanent: + if(!this->permissionGranted(permission::PERMTEST_ORDERED, permission::b_channel_join_permanent, 1, channel, true, permission_cache)) + RESULT(false); + break; + case ChannelType::semipermanent: + if(!this->permissionGranted(permission::PERMTEST_ORDERED, permission::b_channel_join_semi_permanent, 1, channel, true, permission_cache)) + RESULT(false); + break; + case ChannelType::temporary: + if(!this->permissionGranted(permission::PERMTEST_ORDERED, permission::b_channel_join_temporary, 1, channel, true, permission_cache)) + RESULT(false); + break; + } + if(!this->permissionGranted(permission::PERMTEST_ORDERED, permission::b_channel_ignore_join_power, 1, channel, true, permission_cache)) { + auto result = this->server->calculatePermissions2(this->getClientDatabaseId(), {permission::i_channel_join_power}, this->getType(), channel->channelId(), false, permission_cache); + if(result.empty()) + RESULT(false); - if(!channel->permission_granted(permission::i_channel_needed_join_power, result.back().second, false)) - RESULT(false); - } + if(!channel->permission_granted(permission::i_channel_needed_join_power, result.back().second, false)) + RESULT(false); + } - auto val = this->permissionValue(permission::PERMTEST_ORDERED, permission::b_client_is_sticky, this->currentChannel, permission_cache); - if (val != permNotGranted && val > 0) { - auto st = this->permissionValue(permission::PERMTEST_ORDERED, permission::b_client_ignore_sticky, this->currentChannel, permission_cache); - if (st != 1) - RESULT(false); - } - RESULT(true); + auto val = this->permissionValue(permission::PERMTEST_ORDERED, permission::b_client_is_sticky, this->currentChannel, permission_cache); + if (val != permNotGranted && val > 0) { + auto st = this->permissionValue(permission::PERMTEST_ORDERED, permission::b_client_ignore_sticky, this->currentChannel, permission_cache); + if (st != 1) + RESULT(false); + } + RESULT(true); } \ No newline at end of file diff --git a/server/src/client/ConnectedClient.h b/server/src/client/ConnectedClient.h index 6f298b1..004078b 100644 --- a/server/src/client/ConnectedClient.h +++ b/server/src/client/ConnectedClient.h @@ -26,7 +26,7 @@ if(!this->currentChannel) return {findError("channel_invalid_id"), "invalid boun #define CMD_RESET_IDLE \ do { \ - this->resetIdleTime(); \ + this->resetIdleTime(); \ } while(false) #define CMD_REQ_PARM(parm) \ @@ -61,8 +61,8 @@ do { \ /* optional parameter required. By default true */ #define CACHED_PERM_CHECK(permission_type, required, ...) \ do { \ - if(!this->permission_granted(this->cached_permission_value(permission_type), required, #__VA_ARGS__)) \ - return CommandResultPermissionError{permission_type}; \ + if(!this->permission_granted(this->cached_permission_value(permission_type), required, #__VA_ARGS__)) \ + return CommandResultPermissionError{permission_type}; \ } while(0) //p = permission | target_permission = channel permission | channel = target channel | requires a power @@ -75,7 +75,7 @@ this->permissionGranted(permission::PERMTEST_ORDERED, p, (channel)->permissions( #define PERM_CHECK_CHANNEL_NEEDED(p, target_permission, channel) PERM_CHECK_CHANNEL_NEEDEDR(p, target_permission, channel, true) namespace ts { - class GroupManager; + class GroupManager; namespace connection { class VoiceClientConnection; } @@ -84,7 +84,7 @@ namespace ts { class TSServer; class MusicClient; class WebClient; - class MusicClient; + class MusicClient; struct ConnectionInfoData { std::chrono::time_point timestamp; @@ -106,7 +106,7 @@ namespace ts { friend class ts::GroupManager; friend class ServerManager; public: - explicit ConnectedClient(sql::SqlManager*, const std::shared_ptr& server); + explicit ConnectedClient(sql::SqlManager*, const std::shared_ptr& server); ~ConnectedClient() override; ConnectionState connectionState(){ return this->state; } @@ -116,12 +116,12 @@ namespace ts { std::string getHardwareId(){ return properties()[property::CLIENT_HARDWARE_ID]; } //General connection stuff - bool isAddressV4() { return this->remote_address.ss_family == AF_INET; } + bool isAddressV4() { return this->remote_address.ss_family == AF_INET; } const sockaddr_in* getAddressV4(){ return (sockaddr_in*) &this->remote_address; } - bool isAddressV6() { return this->remote_address.ss_family == AF_INET6; } - const sockaddr_in6* getAddressV6(){ return (sockaddr_in6*) &this->remote_address; } + bool isAddressV6() { return this->remote_address.ss_family == AF_INET6; } + const sockaddr_in6* getAddressV6(){ return (sockaddr_in6*) &this->remote_address; } - /* Note: Order is not guaranteed here! */ + /* Note: Order is not guaranteed here! */ virtual void sendCommand(const ts::Command& command, bool low = false) = 0; //General manager stuff @@ -129,22 +129,22 @@ namespace ts { virtual uint16_t getClientId() { return this->properties()[property::CLIENT_ID]; } virtual void setClientId(uint16_t clId) { properties()[property::CLIENT_ID] = clId; } - inline std::shared_ptr getChannel(){ return this->currentChannel; } - inline std::shared_ptr getServer(){ return this->server; } - inline ServerId getServerId(){ return this->server ? this->server->getServerId() : (ServerId) 0; } + inline std::shared_ptr getChannel(){ return this->currentChannel; } + inline std::shared_ptr getServer(){ return this->server; } + inline ServerId getServerId(){ return this->server ? this->server->getServerId() : (ServerId) 0; } - //bool channelSubscribed(const std::shared_ptr&); - /* if lock_channel == false then channel_lock must be write locked! */ + //bool channelSubscribed(const std::shared_ptr&); + /* if lock_channel == false then channel_lock must be write locked! */ std::deque> subscribeChannel(const std::deque>& target, bool lock_channel, bool /* enforce */); - /* if lock_channel == false then channel_lock must be write locked! */ + /* if lock_channel == false then channel_lock must be write locked! */ std::deque> unsubscribeChannel(const std::deque>& target, bool lock_channel); bool isClientVisible(const std::shared_ptr&, bool /* lock channel lock */); inline std::deque> getVisibleClients(bool lock_channel){ - std::shared_lock lock(this->channel_lock, std::defer_lock); - if(lock_channel) - lock.lock(); - return this->visibleClients; + std::shared_lock lock(this->channel_lock, std::defer_lock); + if(lock_channel) + lock.lock(); + return this->visibleClients; } /** Notifies general stuff **/ @@ -167,7 +167,7 @@ namespace ts { //Group manager virtual bool notifyClientPoke(std::shared_ptr invoker, std::string msg); virtual bool notifyClientUpdated( - const std::shared_ptr &, + const std::shared_ptr &, const std::deque> &, bool lock_channel_tree ); /* invalid client id causes error: invalid clientID */ @@ -178,18 +178,18 @@ namespace ts { virtual bool notifyClientChatClosed(const std::shared_ptr &); virtual bool notifyTextMessage(ChatMessageMode mode, const std::shared_ptr &sender, uint64_t targetId, ChannelId channel_id, const std::chrono::system_clock::time_point& /* timestamp */, const std::string &textMessage); inline void sendChannelMessage(const std::shared_ptr& sender, const std::string& textMessage){ - this->notifyTextMessage(ChatMessageMode::TEXTMODE_CHANNEL, sender, this->currentChannel ? this->currentChannel->channelId() : 0, 0, std::chrono::system_clock::now(), textMessage); + this->notifyTextMessage(ChatMessageMode::TEXTMODE_CHANNEL, sender, this->currentChannel ? this->currentChannel->channelId() : 0, 0, std::chrono::system_clock::now(), textMessage); } //Group Client Groups virtual bool notifyServerGroupClientAdd(const std::shared_ptr &invoker, const std::shared_ptr &client, const std::shared_ptr &group); virtual bool notifyServerGroupClientRemove(std::shared_ptr invoker, std::shared_ptr client, std::shared_ptr group); - /* invalid client id causes error: invalid clientID */ - /* invalid channel id causes error: invalid channelID */ - /* an invalid channel or not a client's channel causes: invalid channelID */ + /* invalid client id causes error: invalid clientID */ + /* invalid channel id causes error: invalid channelID */ + /* an invalid channel or not a client's channel causes: invalid channelID */ virtual bool notifyClientChannelGroupChanged( - const std::shared_ptr& invoker, - const std::shared_ptr& client, - const std::shared_ptr& /*channel*/, + const std::shared_ptr& invoker, + const std::shared_ptr& client, + const std::shared_ptr& /*channel*/, const std::shared_ptr& /*inherited channel*/, const std::shared_ptr& group, bool lock_channel_tree /* server channel tree AND client tree must be at least read locked! */ @@ -199,7 +199,7 @@ namespace ts { virtual bool notifyChannelDescriptionChanged(std::shared_ptr channel); virtual bool notifyChannelPasswordChanged(std::shared_ptr); virtual bool notifyChannelEdited( - const std::shared_ptr& /* channel */, + const std::shared_ptr& /* channel */, const std::vector& /* properties */, const std::shared_ptr& /* invoker */, bool /* send channel description */ @@ -211,60 +211,60 @@ namespace ts { virtual bool notifyChannelDeleted(const std::deque& /* channel_ids */, const std::shared_ptr& /* invoker */); //Client view virtual bool notifyClientEnterView( - const std::shared_ptr &client, - const std::shared_ptr &invoker, - const std::string& /* reason */, - const std::shared_ptr &to, - ViewReasonId reasonId, - const std::shared_ptr &from, - bool lock_channel_tree + const std::shared_ptr &client, + const std::shared_ptr &invoker, + const std::string& /* reason */, + const std::shared_ptr &to, + ViewReasonId reasonId, + const std::shared_ptr &from, + bool lock_channel_tree ); - virtual bool notifyClientEnterView(const std::deque>& /* clients */, const ViewReasonSystemT& /* mode */); /* channel lock must be write locked */ - virtual bool notifyClientMoved( - const std::shared_ptr &client, - const std::shared_ptr &target_channel, - ViewReasonId reason, - std::string msg, + virtual bool notifyClientEnterView(const std::deque>& /* clients */, const ViewReasonSystemT& /* mode */); /* channel lock must be write locked */ + virtual bool notifyClientMoved( + const std::shared_ptr &client, + const std::shared_ptr &target_channel, + ViewReasonId reason, + std::string msg, std::shared_ptr invoker, - bool lock_channel_tree + bool lock_channel_tree ); virtual bool notifyClientLeftView( - const std::shared_ptr &client, - const std::shared_ptr &target_channel, - ViewReasonId reasonId, - const std::string& reasonMessage, - std::shared_ptr invoker, - bool lock_channel_tree + const std::shared_ptr &client, + const std::shared_ptr &target_channel, + ViewReasonId reasonId, + const std::string& reasonMessage, + std::shared_ptr invoker, + bool lock_channel_tree + ); + virtual bool notifyClientLeftView( + const std::deque>& /* clients */, + const std::string& /* reason */, + bool /* lock channel view */, + const ViewReasonServerLeftT& /* mode */ ); - virtual bool notifyClientLeftView( - const std::deque>& /* clients */, - const std::string& /* reason */, - bool /* lock channel view */, - const ViewReasonServerLeftT& /* mode */ - ); virtual bool notifyClientLeftViewKicked( - const std::shared_ptr &client, - const std::shared_ptr &target_channel, - const std::string& message, - std::shared_ptr invoker, - bool lock_channel_tree + const std::shared_ptr &client, + const std::shared_ptr &target_channel, + const std::string& message, + std::shared_ptr invoker, + bool lock_channel_tree ); virtual bool notifyClientLeftViewBanned( - const std::shared_ptr &client, - const std::string& message, - std::shared_ptr invoker, - size_t length, - bool lock_channel_tree + const std::shared_ptr &client, + const std::string& message, + std::shared_ptr invoker, + size_t length, + bool lock_channel_tree ); - virtual bool notifyMusicPlayerSongChange(const std::shared_ptr& bot, const std::shared_ptr& newEntry); - virtual bool notifyMusicQueueAdd(const std::shared_ptr& bot, const std::shared_ptr& entry, int index, const std::shared_ptr& invoker); - virtual bool notifyMusicQueueRemove(const std::shared_ptr& bot, const std::deque>& entry, const std::shared_ptr& invoker); - virtual bool notifyMusicQueueOrderChange(const std::shared_ptr& bot, const std::shared_ptr& entry, int order, const std::shared_ptr& invoker); - virtual bool notifyMusicPlayerStatusUpdate(const std::shared_ptr&); + virtual bool notifyMusicPlayerSongChange(const std::shared_ptr& bot, const std::shared_ptr& newEntry); + virtual bool notifyMusicQueueAdd(const std::shared_ptr& bot, const std::shared_ptr& entry, int index, const std::shared_ptr& invoker); + virtual bool notifyMusicQueueRemove(const std::shared_ptr& bot, const std::deque>& entry, const std::shared_ptr& invoker); + virtual bool notifyMusicQueueOrderChange(const std::shared_ptr& bot, const std::shared_ptr& entry, int order, const std::shared_ptr& invoker); + virtual bool notifyMusicPlayerStatusUpdate(const std::shared_ptr&); - virtual bool notifyConversationMessageDelete(const ChannelId /* conversation id */, const std::chrono::system_clock::time_point& /* begin timestamp */, const std::chrono::system_clock::time_point& /* begin end */, ClientDbId /* client id */, size_t /* messages */); + virtual bool notifyConversationMessageDelete(const ChannelId /* conversation id */, const std::chrono::system_clock::time_point& /* begin timestamp */, const std::chrono::system_clock::time_point& /* begin end */, ClientDbId /* client id */, size_t /* messages */); virtual bool closeConnection(const std::chrono::system_clock::time_point &timeout = std::chrono::system_clock::time_point()) = 0; virtual bool disconnect(const std::string& reason) = 0; @@ -273,88 +273,88 @@ namespace ts { void increaseFloodPoints(uint16_t); bool shouldFloodBlock(); - virtual bool ignoresFlood() { return !this->block_flood; } + virtual bool ignoresFlood() { return !this->block_flood; } std::shared_ptr request_connection_info(const std::shared_ptr & /* receiver */, bool& /* send temporary (no client response yet) */); virtual void updateChannelClientProperties(bool /* lock channel tree */, bool /* notify our self */); void updateTalkRights(permission::PermissionValue talk_power); - virtual std::shared_ptr resolveActiveBan(const std::string& ip_address); + virtual std::shared_ptr resolveActiveBan(const std::string& ip_address); - inline std::shared_ptr getConnectionStatistics() { - return this->connectionStatistics; - } + inline std::shared_ptr getConnectionStatistics() { + return this->connectionStatistics; + } - inline std::shared_ptr channel_view() { return this->channels; } + inline std::shared_ptr channel_view() { return this->channels; } - inline std::shared_ptr ref() { return _this.lock(); } + inline std::shared_ptr ref() { return _this.lock(); } - /* - * permission stuff - */ - inline permission::PermissionValue cached_permission_value(permission::PermissionType type) const { - std::lock_guard lock(this->cached_permissions_lock); - auto index = this->cached_permissions.find(type); - if(index != this->cached_permissions.end()) - return index->second; + /* + * permission stuff + */ + inline permission::PermissionValue cached_permission_value(permission::PermissionType type) const { + std::lock_guard lock(this->cached_permissions_lock); + auto index = this->cached_permissions.find(type); + if(index != this->cached_permissions.end()) + return index->second; - /* We're only caching permissions which are granted to reduce memory */ - //logError(this->getServerId(), "{} Looked up cached permission, which hasn't been cached!", CLIENT_STR_LOG_PREFIX); - return permNotGranted; - } - bool update_cached_permissions(); - permission::v2::PermissionFlaggedValue calculate_permission_value(const permission::PermissionType& /* permission type */, ChannelId /* target channel */); + /* We're only caching permissions which are granted to reduce memory */ + //logError(this->getServerId(), "{} Looked up cached permission, which hasn't been cached!", CLIENT_STR_LOG_PREFIX); + return permNotGranted; + } + bool update_cached_permissions(); + permission::v2::PermissionFlaggedValue calculate_permission_value(const permission::PermissionType& /* permission type */, ChannelId /* target channel */); protected: std::weak_ptr _this; //General states std::mutex state_lock; ConnectionState state = ConnectionState::UNKNWON; - sockaddr_storage remote_address; + sockaddr_storage remote_address; std::shared_ptr currentChannel = nullptr; bool allowedToTalk = false; - threads::Mutex disconnectLock; - std::shared_mutex finalDisconnectLock; + threads::Mutex disconnectLock; + std::shared_mutex finalDisconnectLock; std::vector cached_server_groups{}; /* variable locked with channel_lock */ - GroupId cached_channel_group = 0; /* variable locked with channel_lock */ + GroupId cached_channel_group = 0; /* variable locked with channel_lock */ std::deque> visibleClients{}; /* variable locked with channel_lock */ std::deque> mutedClients{}; /* variable locked with channel_lock */ - std::deque> openChats{}; /* variable locked with channel_lock */ + std::deque> openChats{}; /* variable locked with channel_lock */ - std::chrono::system_clock::time_point lastNeededNotify; + std::chrono::system_clock::time_point lastNeededNotify; std::shared_ptr lastNeededPermissionNotifyChannel = nullptr; bool requireNeededPermissionResend = false; - std::chrono::system_clock::time_point connectTimestamp; + std::chrono::system_clock::time_point connectTimestamp; std::chrono::system_clock::time_point lastOnlineTimestamp; - std::chrono::system_clock::time_point lastTransfareTimestamp; - std::chrono::system_clock::time_point idleTimestamp; - std::chrono::system_clock::time_point last_statistics_tick; + std::chrono::system_clock::time_point lastTransfareTimestamp; + std::chrono::system_clock::time_point idleTimestamp; + std::chrono::system_clock::time_point last_statistics_tick; - struct { - std::mutex lock; - std::shared_ptr data; - std::chrono::system_clock::time_point data_age; + struct { + std::mutex lock; + std::shared_ptr data; + std::chrono::system_clock::time_point data_age; - std::deque> receiver; - std::chrono::system_clock::time_point last_requested; - } connection_info; + std::deque> receiver; + std::chrono::system_clock::time_point last_requested; + } connection_info; - struct { - std::chrono::system_clock::time_point servergrouplist; - std::chrono::system_clock::time_point channelgrouplist; + struct { + std::chrono::system_clock::time_point servergrouplist; + std::chrono::system_clock::time_point channelgrouplist; - std::chrono::system_clock::time_point last_notify; - std::chrono::milliseconds notify_timeout = std::chrono::seconds(60); - } command_times; + std::chrono::system_clock::time_point last_notify; + std::chrono::milliseconds notify_timeout = std::chrono::seconds(60); + } command_times; std::shared_ptr connectionStatistics = nullptr; - bool block_flood = true; + bool block_flood = true; FloodPoints floodPoints = 0; std::shared_ptr channels; @@ -362,27 +362,27 @@ namespace ts { std::mutex cached_permissions_lock; std::map cached_permissions; /* contains all needed permissions which are set */ - #pragma pack(push, 1) + #pragma pack(push, 1) struct CachedPermission { - bool flag_skip : 1; /* could be enabled by server / channel or client group. If this flag is set we need no lookup for channel permissions */ + bool flag_skip : 1; /* could be enabled by server / channel or client group. If this flag is set we need no lookup for channel permissions */ bool flag_value : 1; /* says if we have a value or not */ permission::PermissionValue value; }; - static_assert(sizeof(CachedPermission) == 5); - #pragma pack(pop) + static_assert(sizeof(CachedPermission) == 5); + #pragma pack(pop) - permission::PermissionValue channels_view_power = permNotGranted; - permission::PermissionValue channels_ignore_view = permNotGranted; + permission::PermissionValue channels_view_power = permNotGranted; + permission::PermissionValue channels_ignore_view = permNotGranted; bool subscribeToAll = false; - uint16_t join_state_id = 1; /* default channel value is 0 and by default we need to calculate at least once, so we use 1 */ - bool calculate_and_get_join_state(const std::shared_ptr&); + uint16_t join_state_id = 1; /* default channel value is 0 and by default we need to calculate at least once, so we use 1 */ + bool calculate_and_get_join_state(const std::shared_ptr&); std::weak_ptr selectedBot; - std::weak_ptr subscribed_bot; + std::weak_ptr subscribed_bot; - virtual void tick(const std::chrono::system_clock::time_point &time); + virtual void tick(const std::chrono::system_clock::time_point &time); //Locked by everything who has something todo with command handling threads::Mutex command_lock; /* Note: This mutex must be recursive! */ std::vector> postCommandHandler; @@ -397,15 +397,15 @@ namespace ts { CommandResult handleCommandServerRequestConnectionInfo(Command&); CommandResult handleCommandConnectionInfoAutoUpdate(Command&); CommandResult handleCommandPermissionList(Command&); - CommandResult handleCommandPropertyList(Command&); + CommandResult handleCommandPropertyList(Command&); CommandResult handleCommandServerGroupList(Command&); CommandResult handleCommandClientGetIds(Command&); CommandResult handleCommandClientUpdate(Command&); - CommandResult handleCommandClientEdit(Command&); - CommandResult handleCommandClientEdit(Command&, const std::shared_ptr& /* target */); - CommandResult handleCommandClientMove(Command&); + CommandResult handleCommandClientEdit(Command&); + CommandResult handleCommandClientEdit(Command&, const std::shared_ptr& /* target */); + CommandResult handleCommandClientMove(Command&); CommandResult handleCommandClientGetVariables(Command&); CommandResult handleCommandClientKick(Command&); CommandResult handleCommandClientPoke(Command&); @@ -435,8 +435,8 @@ namespace ts { CommandResult handleCommandServerGroupAddPerm(Command&); CommandResult handleCommandServerGroupDelPerm(Command&); - CommandResult handleCommandServerGroupAutoAddPerm(Command&); - CommandResult handleCommandServerGroupAutoDelPerm(Command&); + CommandResult handleCommandServerGroupAutoAddPerm(Command&); + CommandResult handleCommandServerGroupAutoDelPerm(Command&); CommandResult handleCommandClientAddPerm(Command&); //TODO: Use cached permission values CommandResult handleCommandClientDelPerm(Command&); //TODO: Use cached permission values @@ -468,7 +468,7 @@ namespace ts { CommandResult handleCommandFTInitUpload(Command&); //TODO: Use cached permission values CommandResult handleCommandFTInitDownload(Command&); //TODO: Use cached permission values CommandResult handleCommandFTGetFileInfo(Command&); //TODO: Use cached permission values - //CMD_TODO handleCommandFTGetFileInfo -> 5 points + //CMD_TODO handleCommandFTGetFileInfo -> 5 points //CMD_TODO handleCommandFTStop -> 5 points //CMD_TODO handleCommandFTRenameFile -> 5 points //CMD_TODO handleCommandFTList -> 5 points @@ -479,7 +479,7 @@ namespace ts { CommandResult handleCommandBanClient(Command&); CommandResult handleCommandBanDel(Command&); CommandResult handleCommandBanDelAll(Command&); - CommandResult handleCommandBanTriggerList(Command&); + CommandResult handleCommandBanTriggerList(Command&); CommandResult handleCommandTokenList(Command&); CommandResult handleCommandTokenAdd(Command&); @@ -533,43 +533,43 @@ namespace ts { CommandResult handleCommandChannelFind(Command&); //TODO: Use cached permission values CommandResult handleCommandChannelInfo(Command&); //TODO: Use cached permission values - CommandResult handleCommandMusicBotCreate(Command&); //TODO: Use cached permission values - CommandResult handleCommandMusicBotDelete(Command&); //TODO: Use cached permission values - CommandResult handleCommandMusicBotSetSubscription(Command&); //TODO: Use cached permission values + CommandResult handleCommandMusicBotCreate(Command&); //TODO: Use cached permission values + CommandResult handleCommandMusicBotDelete(Command&); //TODO: Use cached permission values + CommandResult handleCommandMusicBotSetSubscription(Command&); //TODO: Use cached permission values - CommandResult handleCommandMusicBotPlayerInfo(Command&); //TODO: Use cached permission values - CommandResult handleCommandMusicBotPlayerAction(Command&); //TODO: Use cached permission values + CommandResult handleCommandMusicBotPlayerInfo(Command&); //TODO: Use cached permission values + CommandResult handleCommandMusicBotPlayerAction(Command&); //TODO: Use cached permission values - CommandResult handleCommandMusicBotQueueList(Command&); //TODO: Use cached permission values - CommandResult handleCommandMusicBotQueueAdd(Command&); //TODO: Use cached permission values - CommandResult handleCommandMusicBotQueueRemove(Command&); //TODO: Use cached permission values - CommandResult handleCommandMusicBotQueueReorder(Command&); //TODO: Use cached permission values + CommandResult handleCommandMusicBotQueueList(Command&); //TODO: Use cached permission values + CommandResult handleCommandMusicBotQueueAdd(Command&); //TODO: Use cached permission values + CommandResult handleCommandMusicBotQueueRemove(Command&); //TODO: Use cached permission values + CommandResult handleCommandMusicBotQueueReorder(Command&); //TODO: Use cached permission values - CommandResult handleCommandMusicBotPlaylistAssign(Command&); + CommandResult handleCommandMusicBotPlaylistAssign(Command&); - /* playlist management */ - CommandResult handleCommandPlaylistList(Command&); - CommandResult handleCommandPlaylistCreate(Command&); - CommandResult handleCommandPlaylistDelete(Command&); - CommandResult handleCommandPlaylistPermList(Command&); - CommandResult handleCommandPlaylistAddPerm(Command&); - CommandResult handleCommandPlaylistDelPerm(Command&); + /* playlist management */ + CommandResult handleCommandPlaylistList(Command&); + CommandResult handleCommandPlaylistCreate(Command&); + CommandResult handleCommandPlaylistDelete(Command&); + CommandResult handleCommandPlaylistPermList(Command&); + CommandResult handleCommandPlaylistAddPerm(Command&); + CommandResult handleCommandPlaylistDelPerm(Command&); - /* playlist properties */ - CommandResult handleCommandPlaylistInfo(Command&); - CommandResult handleCommandPlaylistEdit(Command&); + /* playlist properties */ + CommandResult handleCommandPlaylistInfo(Command&); + CommandResult handleCommandPlaylistEdit(Command&); - CommandResult handleCommandPlaylistSongList(Command&); - CommandResult handleCommandPlaylistSongAdd(Command&); - CommandResult handleCommandPlaylistSongReorder(Command&); - CommandResult handleCommandPlaylistSongRemove(Command&); + CommandResult handleCommandPlaylistSongList(Command&); + CommandResult handleCommandPlaylistSongAdd(Command&); + CommandResult handleCommandPlaylistSongReorder(Command&); + CommandResult handleCommandPlaylistSongRemove(Command&); - CommandResult handleCommandPermReset(Command&); //TODO: Use cached permission values + CommandResult handleCommandPermReset(Command&); //TODO: Use cached permission values - CommandResult handleCommandHelp(Command&); //TODO: Use cached permission values + CommandResult handleCommandHelp(Command&); //TODO: Use cached permission values - CommandResult handleCommandUpdateMyTsId(Command&); - CommandResult handleCommandUpdateMyTsData(Command&); + CommandResult handleCommandUpdateMyTsId(Command&); + CommandResult handleCommandUpdateMyTsData(Command&); /// /// With a whisper list set a client can talk to the specified clients and channels bypassing the standard rule that voice is only transmitted to the current channel. Whisper lists can be defined for individual clients. /// @@ -587,15 +587,15 @@ namespace ts { //Legacy, for TeamSpeak 3 CommandResult handleCommandClientSetServerQueryLogin(Command&); - CommandResult handleCommandQueryList(Command&); - CommandResult handleCommandQueryRename(Command&); - CommandResult handleCommandQueryCreate(Command&); - CommandResult handleCommandQueryDelete(Command&); - CommandResult handleCommandQueryChangePassword(Command&); + CommandResult handleCommandQueryList(Command&); + CommandResult handleCommandQueryRename(Command&); + CommandResult handleCommandQueryCreate(Command&); + CommandResult handleCommandQueryDelete(Command&); + CommandResult handleCommandQueryChangePassword(Command&); - CommandResult handleCommandConversationHistory(Command&); - CommandResult handleCommandConversationFetch(Command&); - CommandResult handleCommandConversationMessageDelete(Command&); + CommandResult handleCommandConversationHistory(Command&); + CommandResult handleCommandConversationFetch(Command&); + CommandResult handleCommandConversationMessageDelete(Command&); CommandResult handleCommandLogView(Command&); //CMD_TODO handleCommandLogAdd @@ -604,7 +604,7 @@ namespace ts { //handleCommandChannelCreatePrivate() -> return findError(0x02) //handleCommandCustome_Unknown_Command() -> return findError(0x100) - CommandResult handleCommandDummy_IpChange(Command&); + CommandResult handleCommandDummy_IpChange(Command&); //handleCommandDummy_NewIp //handleCommandDummy_ConnectFailed //handleCommandDummy_ConnectionLost @@ -623,17 +623,17 @@ namespace ts { typedef std::function& /* sender */, const std::string& /* message */)> handle_text_command_fn_t; bool handle_text_command( - ChatMessageMode, - const std::string& /* key */, - const std::deque& /* arguments */, + ChatMessageMode, + const std::string& /* key */, + const std::deque& /* arguments */, const handle_text_command_fn_t& /* send function */, const std::shared_ptr& /* sender target */ ); inline std::string notify_response_command(const std::string_view& notify) { - if(this->getExternalType() == ClientType::CLIENT_TEAMSPEAK) - return std::string(notify); - return ""; + if(this->getExternalType() == ClientType::CLIENT_TEAMSPEAK) + return std::string(notify); + return ""; } }; } diff --git a/server/src/client/ConnectedClientCommandHandler.cpp b/server/src/client/ConnectedClientCommandHandler.cpp index 767501d..9ebf422 100644 --- a/server/src/client/ConnectedClientCommandHandler.cpp +++ b/server/src/client/ConnectedClientCommandHandler.cpp @@ -51,19 +51,19 @@ using namespace ts::token; permission::PermissionType permType = permission::PermissionType::unknown; \ bool grant = false; \ if (cmd[index].has("permid")) { \ - permType = cmd[index]["permid"].as(); \ - if ((permType & PERM_ID_GRANT) != 0) { \ - grant = true; \ - permType &= ~PERM_ID_GRANT; \ - } \ + permType = cmd[index]["permid"].as(); \ + if ((permType & PERM_ID_GRANT) != 0) { \ + grant = true; \ + permType &= ~PERM_ID_GRANT; \ + } \ } else if (cmd[index].has("permsid")) { \ - auto resv = permission::resolvePermissionData(cmd[index]["permsid"].as()); \ - permType = resv->type; \ - if (resv->grantName() == cmd[index]["permsid"].as()) grant = true; \ + auto resv = permission::resolvePermissionData(cmd[index]["permsid"].as()); \ + permType = resv->type; \ + if (resv->grantName() == cmd[index]["permsid"].as()) grant = true; \ } \ if (permission::resolvePermissionData(permType)->type == permission::PermissionType::unknown) { \ - if (conOnError) continue; \ - return {findError("parameter_invalid"), "could not resolve permission " + (cmd[index].has("permid") ? cmd[index]["permid"].as() : cmd[index]["permsid"].as())}; \ + if (conOnError) continue; \ + return {findError("parameter_invalid"), "could not resolve permission " + (cmd[index].has("permid") ? cmd[index]["permid"].as() : cmd[index]["permsid"].as())}; \ } #define CHANNEL_PERM_TEST_INIT \ @@ -71,10 +71,10 @@ auto current_channel = channel == this->getChannel() #define CHANNEL_PERM_TEST(permission_type, required, enfore_required) \ do { \ - if(current_channel) \ - PERM_CHECK_CHANNELR(permission_type, required, channel, enfore_required); \ - else \ - PERM_CHECK_CHANNELR(permission_type, required, channel, enfore_required); \ + if(current_channel) \ + PERM_CHECK_CHANNELR(permission_type, required, channel, enfore_required); \ + else \ + PERM_CHECK_CHANNELR(permission_type, required, channel, enfore_required); \ } while(0) #define RESOLVE_CHANNEL_R(command, force) \ @@ -94,17 +94,17 @@ if (!l_channel && (channel_id != 0 || force)) return {findError("channel_invalid /* the "newest" channel permission access test */ #define CHANNEL_PERMISSION_TEST(permission_type, permission_needed_type, _channel, permission_required) \ do { \ - auto permission_granted = this->calculate_permission_value(permission_type, _channel->channelId()); \ - if(!channel->permission_granted(permission_needed_type, permission_granted, permission_required)) \ - return CommandResultPermissionError{permission_type}; \ + auto permission_granted = this->calculate_permission_value(permission_type, _channel->channelId()); \ + if(!channel->permission_granted(permission_needed_type, permission_granted, permission_required)) \ + return CommandResultPermissionError{permission_type}; \ } while(0) /* the "newest" group permission access test */ #define GROUP_PERMISSION_TEST(permission_type, permission_needed_type, _group, permission_required) \ do { \ - auto permission_granted = this->calculate_permission_value(permission_type, 0); \ - if(!_group->permission_granted(permission_needed_type, permission_granted, permission_required)) \ - return CommandResultPermissionError{permission_type}; \ + auto permission_granted = this->calculate_permission_value(permission_type, 0); \ + if(!_group->permission_granted(permission_needed_type, permission_granted, permission_required)) \ + return CommandResultPermissionError{permission_type}; \ } while(0) inline bool permission_require_granted_value(permission::PermissionType type) { @@ -152,33 +152,33 @@ inline bool permission_require_granted_value(permission::PermissionType type) { } inline bool permission_is_group_property(permission::PermissionType type) { - switch (type) { - case permission::i_icon_id: - case permission::i_group_show_name_in_tree: - case permission::i_group_sort_id: - case permission::b_group_is_permanent: + switch (type) { + case permission::i_icon_id: + case permission::i_group_show_name_in_tree: + case permission::i_group_sort_id: + case permission::b_group_is_permanent: case permission::i_displayed_group_needed_modify_power: case permission::i_displayed_group_needed_member_add_power: case permission::i_displayed_group_needed_member_remove_power: - return true; - default: - return false; - } + return true; + default: + return false; + } } inline bool permission_is_client_property(permission::PermissionType type) { - switch (type) { - case permission::i_icon_id: - case permission::i_client_talk_power: - case permission::i_client_max_idletime: - case permission::i_group_sort_id: - case permission::i_channel_view_power: - case permission::b_channel_ignore_view_power: - case permission::b_client_is_priority_speaker: - return true; - default: - return false; - } + switch (type) { + case permission::i_icon_id: + case permission::i_client_talk_power: + case permission::i_client_max_idletime: + case permission::i_group_sort_id: + case permission::i_channel_view_power: + case permission::b_channel_ignore_view_power: + case permission::b_client_is_priority_speaker: + return true; + default: + return false; + } } CommandResult ConnectedClient::handleCommand(Command &cmd) { @@ -264,7 +264,7 @@ CommandResult ConnectedClient::handleCommand(Command &cmd) { else if (command == "ftdeletefile") return this->handleCommandFTDeleteFile(cmd); else if (command == "ftinitupload") return this->handleCommandFTInitUpload(cmd); else if (command == "ftinitdownload") return this->handleCommandFTInitDownload(cmd); - else if (command == "ftgetfileinfo") return this->handleCommandFTGetFileInfo(cmd); + else if (command == "ftgetfileinfo") return this->handleCommandFTGetFileInfo(cmd); //Banlist else if (command == "banlist") return this->handleCommandBanList(cmd); else if (command == "banadd") return this->handleCommandBanAdd(cmd); @@ -272,7 +272,7 @@ CommandResult ConnectedClient::handleCommand(Command &cmd) { else if (command == "banclient") return this->handleCommandBanClient(cmd); else if (command == "bandel") return this->handleCommandBanDel(cmd); else if (command == "bandelall") return this->handleCommandBanDelAll(cmd); - else if (command == "bantriggerlist") return this->handleCommandBanTriggerList(cmd); + else if (command == "bantriggerlist") return this->handleCommandBanTriggerList(cmd); //Tokens else if (command == "tokenlist" || command == "privilegekeylist") return this->handleCommandTokenList(cmd); else if (command == "tokenadd" || command == "privilegekeyadd") return this->handleCommandTokenAdd(cmd); @@ -324,8 +324,8 @@ CommandResult ConnectedClient::handleCommand(Command &cmd) { else if (command == "clientsetserverquerylogin") return this->handleCommandClientSetServerQueryLogin(cmd); - //Music stuff - else if (command == "musicbotcreate") return this->handleCommandMusicBotCreate(cmd); + //Music stuff + else if (command == "musicbotcreate") return this->handleCommandMusicBotCreate(cmd); else if (command == "musicbotdelete") return this->handleCommandMusicBotDelete(cmd); else if (command == "musicbotsetsubscription") return this->handleCommandMusicBotSetSubscription(cmd); else if (command == "musicbotplayerinfo") return this->handleCommandMusicBotPlayerInfo(cmd); @@ -334,18 +334,18 @@ CommandResult ConnectedClient::handleCommand(Command &cmd) { else if (command == "musicbotqueueadd") return this->handleCommandMusicBotQueueAdd(cmd); else if (command == "musicbotqueueremove") return this->handleCommandMusicBotQueueRemove(cmd); else if (command == "musicbotqueuereorder") return this->handleCommandMusicBotQueueReorder(cmd); - else if (command == "musicbotplaylistassign") return this->handleCommandMusicBotPlaylistAssign(cmd); + else if (command == "musicbotplaylistassign") return this->handleCommandMusicBotPlaylistAssign(cmd); - else if (command == "help") return this->handleCommandHelp(cmd); + else if (command == "help") return this->handleCommandHelp(cmd); - else if (command == "logview") return this->handleCommandLogView(cmd); + else if (command == "logview") return this->handleCommandLogView(cmd); else if (command == "servergroupautoaddperm") return this->handleCommandServerGroupAutoAddPerm(cmd); - else if (command == "servergroupautodelperm") return this->handleCommandServerGroupAutoDelPerm(cmd); + else if (command == "servergroupautodelperm") return this->handleCommandServerGroupAutoDelPerm(cmd); - else if (command == "updatemytsid") return this->handleCommandUpdateMyTsId(cmd); + else if (command == "updatemytsid") return this->handleCommandUpdateMyTsId(cmd); else if (command == "updatemytsdata") return this->handleCommandUpdateMyTsData(cmd); - else if (command == "querycreate") return this->handleCommandQueryCreate(cmd); + else if (command == "querycreate") return this->handleCommandQueryCreate(cmd); else if (command == "querydelete") return this->handleCommandQueryDelete(cmd); else if (command == "querylist") return this->handleCommandQueryList(cmd); else if (command == "queryrename") return this->handleCommandQueryRename(cmd); @@ -373,8 +373,8 @@ CommandResult ConnectedClient::handleCommand(Command &cmd) { if (this->getType() == ClientType::CLIENT_QUERY) return CommandResult::NotImplemented; //Dont log query invalid commands if (this->getType() == ClientType::CLIENT_TEAMSPEAK) if (command.empty() || command.find_first_not_of(' ') == -1) { - if (!this->permissionGranted(permission::PERMTEST_ORDERED, permission::b_client_allow_invalid_packet, 1, this->currentChannel)) - ((VoiceClient *) this)->disconnect(VREASON_SERVER_KICK, config::messages::kick_invalid_command, this->server ? this->server->serverAdmin : static_pointer_cast(serverInstance->getInitialServerAdmin()), true); + if (!this->permissionGranted(permission::PERMTEST_ORDERED, permission::b_client_allow_invalid_packet, 1, this->currentChannel)) + ((VoiceClient *) this)->disconnect(VREASON_SERVER_KICK, config::messages::kick_invalid_command, this->server ? this->server->serverAdmin : static_pointer_cast(serverInstance->getInitialServerAdmin()), true); } logError(this->getServerId(), "Missing command '{}'", command); @@ -409,11 +409,11 @@ CommandResult ConnectedClient::handleCommandServerEdit(Command &cmd) { CMD_CHK_AND_INC_FLOOD_POINTS(5); if (cmd[0].has("sid") && this->getServerId() != cmd["sid"].as()) - return {findError("server_invalid_id"), "Invalid server id! You can just edit the server where your're bound on"}; + return {findError("server_invalid_id"), "Invalid server id! You can just edit the server where your're bound on"}; auto target_server = this->server; if(cmd[0].has("sid")) { - target_server = serverInstance->getVoiceServerManager()->findServerById(cmd["sid"]); + target_server = serverInstance->getVoiceServerManager()->findServerById(cmd["sid"]); if(!target_server && cmd["sid"].as() != 0) return {findError("server_invalid_id")}; } @@ -431,25 +431,25 @@ CommandResult ConnectedClient::handleCommandServerEdit(Command &cmd) { SERVEREDIT_CHK_PROP_CACHED("virtualserver_channel_temp_delete_delay_default", permission::b_virtualserver_modify_channel_temp_delete_delay_default, ChannelId) } SERVEREDIT_CHK_PROP_CACHED("virtualserver_codec_encryption_mode", permission::b_virtualserver_modify_codec_encryption_mode, int) } SERVEREDIT_CHK_PROP_CACHED("virtualserver_default_server_group", permission::b_virtualserver_modify_default_servergroup, GroupId) - if(target_server) { - auto group = target_server->groups->findGroup(cmd["virtualserver_default_server_group"].as()); - if (!group || group->target() != GROUPTARGET_SERVER) return {findError("parameter_invalid"), "Invalid default server group!"}; - } + if(target_server) { + auto group = target_server->groups->findGroup(cmd["virtualserver_default_server_group"].as()); + if (!group || group->target() != GROUPTARGET_SERVER) return {findError("parameter_invalid"), "Invalid default server group!"}; + } } SERVEREDIT_CHK_PROP_CACHED("virtualserver_default_channel_group", permission::b_virtualserver_modify_default_channelgroup, GroupId) - if(target_server) { - auto group = target_server->groups->findGroup(cmd["virtualserver_default_channel_group"].as()); - if (!group || group->target() != GROUPTARGET_CHANNEL) return {findError("parameter_invalid"), "Invalid default channel group!"}; - } + if(target_server) { + auto group = target_server->groups->findGroup(cmd["virtualserver_default_channel_group"].as()); + if (!group || group->target() != GROUPTARGET_CHANNEL) return {findError("parameter_invalid"), "Invalid default channel group!"}; + } } SERVEREDIT_CHK_PROP_CACHED("virtualserver_default_channel_admin_group", permission::b_virtualserver_modify_default_channeladmingroup, GroupId) - if(target_server) { - auto group = target_server->groups->findGroup(cmd["virtualserver_default_channel_admin_group"].as()); - if (!group || group->target() != GROUPTARGET_CHANNEL) return {findError("parameter_invalid"), "Invalid default channel admin group!"}; - } + if(target_server) { + auto group = target_server->groups->findGroup(cmd["virtualserver_default_channel_admin_group"].as()); + if (!group || group->target() != GROUPTARGET_CHANNEL) return {findError("parameter_invalid"), "Invalid default channel admin group!"}; + } } SERVEREDIT_CHK_PROP_CACHED("virtualserver_default_music_group", permission::b_virtualserver_modify_default_musicgroup, GroupId) - if(target_server) { - auto group = target_server->groups->findGroup(cmd["virtualserver_default_music_group"].as()); - if (!group || group->target() != GROUPTARGET_SERVER) return {findError("parameter_invalid"), "Invalid default music group!"}; - } + if(target_server) { + auto group = target_server->groups->findGroup(cmd["virtualserver_default_music_group"].as()); + if (!group || group->target() != GROUPTARGET_SERVER) return {findError("parameter_invalid"), "Invalid default music group!"}; + } } SERVEREDIT_CHK_PROP_CACHED("virtualserver_priority_speaker_dimm_modificator", permission::b_virtualserver_modify_priority_speaker_dimm_modificator, float) } @@ -474,11 +474,11 @@ CommandResult ConnectedClient::handleCommandServerEdit(Command &cmd) { SERVEREDIT_CHK_PROP_CACHED("virtualserver_weblist_enabled", permission::b_virtualserver_modify_weblist, bool) if (target_server && target_server->running()) { if (cmd["virtualserver_weblist_enabled"].as()) - serverInstance->getWebList()->enable_report(target_server); + serverInstance->getWebList()->enable_report(target_server); else - serverInstance->getWebList()->disable_report(target_server); + serverInstance->getWebList()->disable_report(target_server); debugMessage(target_server->getServerId(), "Changed weblist state to -> {}", - cmd["virtualserver_weblist_enabled"].as() ? "activated" : "disabled"); + cmd["virtualserver_weblist_enabled"].as() ? "activated" : "disabled"); } } SERVEREDIT_CHK_PROP_CACHED("virtualserver_needed_identity_security_level", permission::b_virtualserver_modify_needed_identity_security_level, int) } @@ -510,42 +510,42 @@ CommandResult ConnectedClient::handleCommandServerEdit(Command &cmd) { if (cmd["virtualserver_flag_password"].as() && !cmd[0].has("virtualserver_password")) return {findError("parameter_invalid"), "Invalid password flag"}; } SERVEREDIT_CHK_PROP_CACHED("virtualserver_password", permission::b_virtualserver_modify_password, string) - if(cmd["virtualserver_password"].string().empty()) { - toApplay["virtualserver_flag_password"] = "0"; - } else { - toApplay["virtualserver_flag_password"] = "1"; - if(this->getType() == CLIENT_QUERY) - toApplay["virtualserver_password"] = base64::encode(digest::sha1(cmd["virtualserver_password"].string())); - } + if(cmd["virtualserver_password"].string().empty()) { + toApplay["virtualserver_flag_password"] = "0"; + } else { + toApplay["virtualserver_flag_password"] = "1"; + if(this->getType() == CLIENT_QUERY) + toApplay["virtualserver_password"] = base64::encode(digest::sha1(cmd["virtualserver_password"].string())); + } } SERVEREDIT_CHK_PROP_CACHED("virtualserver_default_client_description", permission::b_virtualserver_modify_default_messages, string) } SERVEREDIT_CHK_PROP_CACHED("virtualserver_default_channel_description", permission::b_virtualserver_modify_default_messages, string) } SERVEREDIT_CHK_PROP_CACHED("virtualserver_default_channel_topic", permission::b_virtualserver_modify_default_messages, string) } SERVEREDIT_CHK_PROP_CACHED("virtualserver_country_code", permission::b_virtualserver_modify_country_code, string) } - SERVEREDIT_CHK_PROP2("virtualserver_max_download_total_bandwidth", permission::b_virtualserver_modify_ft_settings, uint64_t, int64_t) - if(cmd["virtualserver_max_download_total_bandwidth"].string().find('-') == string::npos) - toApplay["virtualserver_max_download_total_bandwidth"] = to_string((int64_t) cmd["virtualserver_max_download_total_bandwidth"].as()); - else - toApplay["virtualserver_max_download_total_bandwidth"] = to_string(cmd["virtualserver_max_download_total_bandwidth"].as()); - } - SERVEREDIT_CHK_PROP2("virtualserver_max_upload_total_bandwidth", permission::b_virtualserver_modify_ft_settings, uint64_t, int64_t) - if(cmd["virtualserver_max_upload_total_bandwidth"].string().find('-') == string::npos) - toApplay["virtualserver_max_upload_total_bandwidth"] = to_string((int64_t) cmd["virtualserver_max_upload_total_bandwidth"].as()); - else - toApplay["virtualserver_max_upload_total_bandwidth"] = to_string(cmd["virtualserver_max_upload_total_bandwidth"].as()); - } - SERVEREDIT_CHK_PROP2("virtualserver_download_quota", permission::b_virtualserver_modify_ft_quotas, uint64_t, int64_t) - if(cmd["virtualserver_download_quota"].string().find('-') == string::npos) - toApplay["virtualserver_download_quota"] = to_string((int64_t) cmd["virtualserver_download_quota"].as()); - else - toApplay["virtualserver_download_quota"] = to_string(cmd["virtualserver_download_quota"].as()); - } - SERVEREDIT_CHK_PROP2("virtualserver_upload_quota", permission::b_virtualserver_modify_ft_quotas, uint64_t, int64_t) - if(cmd["virtualserver_upload_quota"].string().find('-') == string::npos) - toApplay["virtualserver_upload_quota"] = to_string((int64_t) cmd["virtualserver_upload_quota"].as()); - else - toApplay["virtualserver_upload_quota"] = to_string(cmd["virtualserver_upload_quota"].as()); - } + SERVEREDIT_CHK_PROP2("virtualserver_max_download_total_bandwidth", permission::b_virtualserver_modify_ft_settings, uint64_t, int64_t) + if(cmd["virtualserver_max_download_total_bandwidth"].string().find('-') == string::npos) + toApplay["virtualserver_max_download_total_bandwidth"] = to_string((int64_t) cmd["virtualserver_max_download_total_bandwidth"].as()); + else + toApplay["virtualserver_max_download_total_bandwidth"] = to_string(cmd["virtualserver_max_download_total_bandwidth"].as()); + } + SERVEREDIT_CHK_PROP2("virtualserver_max_upload_total_bandwidth", permission::b_virtualserver_modify_ft_settings, uint64_t, int64_t) + if(cmd["virtualserver_max_upload_total_bandwidth"].string().find('-') == string::npos) + toApplay["virtualserver_max_upload_total_bandwidth"] = to_string((int64_t) cmd["virtualserver_max_upload_total_bandwidth"].as()); + else + toApplay["virtualserver_max_upload_total_bandwidth"] = to_string(cmd["virtualserver_max_upload_total_bandwidth"].as()); + } + SERVEREDIT_CHK_PROP2("virtualserver_download_quota", permission::b_virtualserver_modify_ft_quotas, uint64_t, int64_t) + if(cmd["virtualserver_download_quota"].string().find('-') == string::npos) + toApplay["virtualserver_download_quota"] = to_string((int64_t) cmd["virtualserver_download_quota"].as()); + else + toApplay["virtualserver_download_quota"] = to_string(cmd["virtualserver_download_quota"].as()); + } + SERVEREDIT_CHK_PROP2("virtualserver_upload_quota", permission::b_virtualserver_modify_ft_quotas, uint64_t, int64_t) + if(cmd["virtualserver_upload_quota"].string().find('-') == string::npos) + toApplay["virtualserver_upload_quota"] = to_string((int64_t) cmd["virtualserver_upload_quota"].as()); + else + toApplay["virtualserver_upload_quota"] = to_string(cmd["virtualserver_upload_quota"].as()); + } else { logError(target_server ? target_server->getServerId() : 0, "Client " + this->getDisplayName() + " tried to change a not existing server properties. (" + key + ")"); //return CommandResult::NotImplemented; @@ -553,38 +553,38 @@ CommandResult ConnectedClient::handleCommandServerEdit(Command &cmd) { } std::deque keys; - bool group_update = false; + bool group_update = false; for (const auto& elm : toApplay) { auto info = property::impl::info(elm.first); - if(*info == property::VIRTUALSERVER_UNDEFINED) { - logCritical(target_server ? target_server->getServerId() : 0, "Missing server property " + elm.first); - continue; - } + if(*info == property::VIRTUALSERVER_UNDEFINED) { + logCritical(target_server ? target_server->getServerId() : 0, "Missing server property " + elm.first); + continue; + } - if(!info->validate_input(elm.second)) { - logError(target_server ? target_server->getServerId() : 0, "Client " + this->getDisplayName() + " tried to change a property to an invalid value. (Value: '" + elm.second + "', Property: '" + info->name + "')"); - continue; - } - if(target_server) - target_server->properties()[info] = elm.second; - else - (*serverInstance->getDefaultServerProperties())[info] = elm.second; + if(!info->validate_input(elm.second)) { + logError(target_server ? target_server->getServerId() : 0, "Client " + this->getDisplayName() + " tried to change a property to an invalid value. (Value: '" + elm.second + "', Property: '" + info->name + "')"); + continue; + } + if(target_server) + target_server->properties()[info] = elm.second; + else + (*serverInstance->getDefaultServerProperties())[info] = elm.second; keys.push_back(elm.first); - group_update |= *info == property::VIRTUALSERVER_DEFAULT_SERVER_GROUP || *info == property::VIRTUALSERVER_DEFAULT_CHANNEL_GROUP || *info == property::VIRTUALSERVER_DEFAULT_MUSIC_GROUP; + group_update |= *info == property::VIRTUALSERVER_DEFAULT_SERVER_GROUP || *info == property::VIRTUALSERVER_DEFAULT_CHANNEL_GROUP || *info == property::VIRTUALSERVER_DEFAULT_MUSIC_GROUP; } if(target_server) { - if (group_update) - target_server->forEachClient([&](const shared_ptr& client) { - if(target_server->notifyClientPropertyUpdates(client, target_server->groups->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 */ - } - }); + if (group_update) + target_server->forEachClient([&](const shared_ptr& client) { + if(target_server->notifyClientPropertyUpdates(client, target_server->groups->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 */ + } + }); - if (!keys.empty()) - target_server->notifyServerEdited(_this.lock(), keys); + if (!keys.empty()) + target_server->notifyServerEdited(_this.lock(), keys); } return CommandResult::Success; } @@ -592,7 +592,7 @@ CommandResult ConnectedClient::handleCommandServerEdit(Command &cmd) { CommandResult ConnectedClient::handleCommandClientGetVariables(Command &cmd) { CMD_REQ_SERVER; auto client = this->server->findClient(cmd["clid"].as()); - shared_lock tree_lock(this->channel_lock); + shared_lock tree_lock(this->channel_lock); if (!client || (client != this && !this->isClientVisible(client, false))) return {findError("client_invalid_id"), ""}; @@ -617,19 +617,19 @@ CommandResult ConnectedClient::handleCommandClientKick(Command &cmd) { auto type = cmd["reasonid"].as(); if (type == ViewReasonId::VREASON_CHANNEL_KICK) { auto channel = client->currentChannel; - PERM_CHECK_CHANNELR(permission::i_client_kick_from_channel_power, client->permissionValue(permission::PERMTEST_ORDERED, permission::i_client_needed_kick_from_channel_power, channel), channel, true); + PERM_CHECK_CHANNELR(permission::i_client_kick_from_channel_power, client->permissionValue(permission::PERMTEST_ORDERED, permission::i_client_needed_kick_from_channel_power, channel), channel, true); targetChannel = this->server->channelTree->getDefaultChannel(); } else if (type == ViewReasonId::VREASON_SERVER_KICK) { auto channel = client->currentChannel; - PERM_CHECK_CHANNELR(permission::i_client_kick_from_server_power, client->permissionValue(permission::PERMTEST_ORDERED, permission::i_client_needed_kick_from_server_power, channel), channel, true); + PERM_CHECK_CHANNELR(permission::i_client_kick_from_server_power, client->permissionValue(permission::PERMTEST_ORDERED, permission::i_client_needed_kick_from_server_power, channel), channel, true); targetChannel = nullptr; } else return CommandResult::NotImplemented; if (targetChannel) { - this->server->notify_client_kick(client, this->ref(), cmd["reasonmsg"].as(), targetChannel); + this->server->notify_client_kick(client, this->ref(), cmd["reasonmsg"].as(), targetChannel); } else { - this->server->notify_client_kick(client, this->ref(), cmd["reasonmsg"].as(), nullptr); - client->closeConnection(system_clock::now() + seconds(1)); + this->server->notify_client_kick(client, this->ref(), cmd["reasonmsg"].as(), nullptr); + client->closeConnection(system_clock::now() + seconds(1)); } return CommandResult::Success; @@ -637,13 +637,13 @@ CommandResult ConnectedClient::handleCommandClientKick(Command &cmd) { CommandResult ConnectedClient::handleCommandChannelGetDescription(Command &cmd) { CMD_CHK_AND_INC_FLOOD_POINTS(0); - RESOLVE_CHANNEL_R(cmd["cid"], true); - auto channel = dynamic_pointer_cast(l_channel->entry); - assert(channel); + RESOLVE_CHANNEL_R(cmd["cid"], true); + auto channel = dynamic_pointer_cast(l_channel->entry); + assert(channel); - if(!this->permission_granted(this->permissionValue(permission::b_channel_ignore_description_view_power, channel), 1, true)) { - CHANNEL_PERMISSION_TEST(permission::i_channel_description_view_power, permission::i_channel_needed_description_view_power, channel, false); - } + if(!this->permission_granted(this->permissionValue(permission::b_channel_ignore_description_view_power, channel), 1, true)) { + CHANNEL_PERMISSION_TEST(permission::i_channel_description_view_power, permission::i_channel_needed_description_view_power, channel, false); + } this->sendChannelDescription(channel, true); return CommandResult::Success; @@ -659,9 +659,9 @@ CommandResult ConnectedClient::handleCommandGetConnectionInfo(Command &cmd) { bool send_temp; auto info = client->request_connection_info(_this.lock(), send_temp); if (info) { - this->notifyConnectionInfo(client, info); + this->notifyConnectionInfo(client, info); } else if(send_temp) { - this->notifyConnectionInfo(client, nullptr); + this->notifyConnectionInfo(client, nullptr); } return CommandResult::Success; @@ -675,37 +675,37 @@ CommandResult ConnectedClient::handleCommandSetConnectionInfo(Command &cmd) { /* CONNECTION_FILETRANSFER_BANDWIDTH_SENT, //how many bytes per second are currently being sent by file transfers - CONNECTION_FILETRANSFER_BANDWIDTH_RECEIVED, //how many bytes per second are currently being received by file transfers - CONNECTION_FILETRANSFER_BYTES_RECEIVED_TOTAL, //how many bytes we received in total through file transfers - CONNECTION_FILETRANSFER_BYTES_SENT_TOTAL, //how many bytes we sent in total through file transfers + CONNECTION_FILETRANSFER_BANDWIDTH_RECEIVED, //how many bytes per second are currently being received by file transfers + CONNECTION_FILETRANSFER_BYTES_RECEIVED_TOTAL, //how many bytes we received in total through file transfers + CONNECTION_FILETRANSFER_BYTES_SENT_TOTAL, //how many bytes we sent in total through file transfers */ deque> receivers; - { - lock_guard info_lock(this->connection_info.lock); - for(const auto& weak_receiver : this->connection_info.receiver) { - auto receiver = weak_receiver.lock(); - if(!receiver) continue; + { + lock_guard info_lock(this->connection_info.lock); + for(const auto& weak_receiver : this->connection_info.receiver) { + auto receiver = weak_receiver.lock(); + if(!receiver) continue; - receivers.push_back(receiver); - } - this->connection_info.receiver.clear(); - this->connection_info.data = info; - this->connection_info.data_age = system_clock::now(); - } - for(const auto& receiver : receivers) - receiver->notifyConnectionInfo(_this.lock(), info); + receivers.push_back(receiver); + } + this->connection_info.receiver.clear(); + this->connection_info.data = info; + this->connection_info.data_age = system_clock::now(); + } + for(const auto& receiver : receivers) + receiver->notifyConnectionInfo(_this.lock(), info); return CommandResult::Success; } CommandResult ConnectedClient::handleCommandServerRequestConnectionInfo(Command &) { CMD_REQ_SERVER; - CACHED_PERM_CHECK(permission::b_virtualserver_connectioninfo_view, 1, true); + CACHED_PERM_CHECK(permission::b_virtualserver_connectioninfo_view, 1, true); Command notify("notifyserverconnectioninfo"); auto statistics = this->server->getServerStatistics()->statistics(); - auto report = this->server->getServerStatistics()->dataReport(); + auto report = this->server->getServerStatistics()->dataReport(); notify[0]["connection_filetransfer_bandwidth_sent"] = report.file_send; notify[0]["connection_filetransfer_bandwidth_received"] = report.file_recv; @@ -713,8 +713,8 @@ CommandResult ConnectedClient::handleCommandServerRequestConnectionInfo(Command notify[0]["connection_filetransfer_bytes_sent_total"] = (*statistics)[property::CONNECTION_FILETRANSFER_BYTES_SENT_TOTAL].as(); notify[0]["connection_filetransfer_bytes_received_total"] = (*statistics)[property::CONNECTION_FILETRANSFER_BYTES_RECEIVED_TOTAL].as(); - notify[0]["connection_filetransfer_bytes_sent_month"] = this->server->properties()[property::VIRTUALSERVER_MONTH_BYTES_DOWNLOADED].as(); - notify[0]["connection_filetransfer_bytes_received_month"] = this->server->properties()[property::VIRTUALSERVER_MONTH_BYTES_UPLOADED].as(); + notify[0]["connection_filetransfer_bytes_sent_month"] = this->server->properties()[property::VIRTUALSERVER_MONTH_BYTES_DOWNLOADED].as(); + notify[0]["connection_filetransfer_bytes_received_month"] = this->server->properties()[property::VIRTUALSERVER_MONTH_BYTES_UPLOADED].as(); notify[0]["connection_packets_sent_total"] = (*statistics)[property::CONNECTION_PACKETS_SENT_TOTAL].as(); notify[0]["connection_bytes_sent_total"] = (*statistics)[property::CONNECTION_BYTES_SENT_TOTAL].as(); @@ -747,70 +747,70 @@ CommandResult ConnectedClient::handleCommandClientGetIds(Command &cmd) { CMD_REQ_SERVER; bool error = false; - bool found = false; + bool found = false; auto client_list = this->server->getClients(); - Command notify(this->getExternalType() == CLIENT_TEAMSPEAK ? "notifyclientids" : ""); - int result_index = 0; + Command notify(this->getExternalType() == CLIENT_TEAMSPEAK ? "notifyclientids" : ""); + int result_index = 0; for(int index = 0; index < cmd.bulkCount(); index++) { - auto unique_id = cmd[index]["cluid"].as(); - for(const auto& entry : client_list) { - if(entry->getUid() == unique_id) { - if(!config::server::show_invisible_clients_as_online && !this->channels->channel_visible(entry->currentChannel, nullptr)) - continue; + auto unique_id = cmd[index]["cluid"].as(); + for(const auto& entry : client_list) { + if(entry->getUid() == unique_id) { + if(!config::server::show_invisible_clients_as_online && !this->channels->channel_visible(entry->currentChannel, nullptr)) + continue; - notify[result_index]["name"] = entry->getDisplayName(); - notify[result_index]["clid"] = entry->getClientId(); - notify[result_index]["cluid"] = entry->getUid(); - result_index++; - found = true; - } - } - if(found) found = false; + notify[result_index]["name"] = entry->getDisplayName(); + notify[result_index]["clid"] = entry->getClientId(); + notify[result_index]["cluid"] = entry->getUid(); + result_index++; + found = true; + } + } + if(found) found = false; else error = false; } string uid = cmd["cluid"]; if(result_index > 0) { - this->sendCommand(notify); + this->sendCommand(notify); + } + if(error) { + return {findError("database_empty_result"), "empty!"}; } - if(error) { - return {findError("database_empty_result"), "empty!"}; - } return CommandResult::Success; } CommandResult ConnectedClient::handleCommandChannelSubscribe(Command &cmd) { - CMD_REF_SERVER(ref_server); + CMD_REF_SERVER(ref_server); CMD_RESET_IDLE; - bool flood_points = false; - deque> channels; + bool flood_points = false; + deque> channels; - { - shared_lock server_channel_lock(this->server->channel_tree_lock); - unique_lock client_channel_lock(this->channel_lock); + { + shared_lock server_channel_lock(this->server->channel_tree_lock); + unique_lock client_channel_lock(this->channel_lock); - for (int index = 0; index < cmd.bulkCount(); index++) { - auto local_channel = this->channel_view()->find_channel(cmd[index]["cid"].as()); - if(!local_channel) - return {findError("channel_invalid_id"), "Cant resolve channel"}; + for (int index = 0; index < cmd.bulkCount(); index++) { + auto local_channel = this->channel_view()->find_channel(cmd[index]["cid"].as()); + if(!local_channel) + return {findError("channel_invalid_id"), "Cant resolve channel"}; - auto channel = this->server->channelTree->findChannel(cmd[index]["cid"].as()); - if (!channel) - return {findError("channel_invalid_id"), "Cant resolve channel"}; + auto channel = this->server->channelTree->findChannel(cmd[index]["cid"].as()); + if (!channel) + return {findError("channel_invalid_id"), "Cant resolve channel"}; - channels.push_back(channel); - if(!flood_points && system_clock::now() - local_channel->view_timestamp > seconds(5)) { - flood_points = true; - CMD_CHK_AND_INC_FLOOD_POINTS(15); - } - } + channels.push_back(channel); + if(!flood_points && system_clock::now() - local_channel->view_timestamp > seconds(5)) { + flood_points = true; + CMD_CHK_AND_INC_FLOOD_POINTS(15); + } + } - if(!channels.empty()) - this->subscribeChannel(channels, false, false); - } + if(!channels.empty()) + this->subscribeChannel(channels, false, false); + } return CommandResult::Success; } @@ -819,12 +819,12 @@ CommandResult ConnectedClient::handleCommandChannelSubscribeAll(Command &cmd) { CMD_REQ_SERVER; CMD_CHK_AND_INC_FLOOD_POINTS(20); - { - shared_lock server_channel_lock(this->server->channel_tree_lock); - unique_lock client_channel_lock(this->channel_lock); - this->subscribeChannel(this->server->channelTree->channels(), false, false); - this->subscribeToAll = true; - } + { + shared_lock server_channel_lock(this->server->channel_tree_lock); + unique_lock client_channel_lock(this->channel_lock); + this->subscribeChannel(this->server->channelTree->channels(), false, false); + this->subscribeToAll = true; + } return CommandResult::Success; } @@ -832,19 +832,19 @@ CommandResult ConnectedClient::handleCommandChannelUnsubscribe(Command &cmd) { CMD_REQ_SERVER; CMD_CHK_AND_INC_FLOOD_POINTS(5); - { - shared_lock server_channel_lock(this->server->channel_tree_lock); - unique_lock client_channel_lock(this->channel_lock); + { + shared_lock server_channel_lock(this->server->channel_tree_lock); + unique_lock client_channel_lock(this->channel_lock); - deque> channels; - for(int index = 0; index < cmd.bulkCount(); index++) { - auto channel = this->server->channelTree->findChannel(cmd["cid"].as()); - if(!channel) continue; - channels.push_front(channel); - } + deque> channels; + for(int index = 0; index < cmd.bulkCount(); index++) { + auto channel = this->server->channelTree->findChannel(cmd["cid"].as()); + if(!channel) continue; + channels.push_front(channel); + } - this->unsubscribeChannel(channels, false); - } + this->unsubscribeChannel(channels, false); + } return CommandResult::Success; } @@ -852,12 +852,12 @@ CommandResult ConnectedClient::handleCommandChannelUnsubscribeAll(Command &cmd) CMD_REQ_SERVER; CMD_CHK_AND_INC_FLOOD_POINTS(25); - { - shared_lock server_channel_lock(this->server->channel_tree_lock); - unique_lock client_channel_lock(this->channel_lock); - this->unsubscribeChannel(this->server->channelTree->channels(), false); - this->subscribeToAll = false; - } + { + shared_lock server_channel_lock(this->server->channel_tree_lock); + unique_lock client_channel_lock(this->channel_lock); + this->unsubscribeChannel(this->server->channelTree->channels(), false); + this->subscribeToAll = false; + } return CommandResult::Success; } @@ -865,46 +865,46 @@ CommandResult ConnectedClient::handleCommandPermissionList(Command &cmd) { CMD_CHK_AND_INC_FLOOD_POINTS(5); static std::string permission_list_string[ClientType::MAX]; - static std::mutex permission_list_string_lock; + static std::mutex permission_list_string_lock; - static auto build_permission_list = [](const std::string& command, const ClientType& type) { - Command list_builder(command); - int index = 0; - for (auto group : permission::availableGroups) - list_builder[index++]["group_id_end"] = group; + static auto build_permission_list = [](const std::string& command, const ClientType& type) { + Command list_builder(command); + int index = 0; + for (auto group : permission::availableGroups) + list_builder[index++]["group_id_end"] = group; - auto avPerms = permission::availablePermissions; - std::sort(avPerms.begin(), avPerms.end(), [](const std::shared_ptr &a, const std::shared_ptr &b) { - return a->type < b->type; - }); + auto avPerms = permission::availablePermissions; + std::sort(avPerms.begin(), avPerms.end(), [](const std::shared_ptr &a, const std::shared_ptr &b) { + return a->type < b->type; + }); - auto mapper = serverInstance->getPermissionMapper(); - for (const auto& permission : avPerms) { - if (!permission->clientSupported) continue; + auto mapper = serverInstance->getPermissionMapper(); + for (const auto& permission : avPerms) { + if (!permission->clientSupported) continue; - auto &blk = list_builder[index++]; + auto &blk = list_builder[index++]; - blk["permname"] = permission->name; - blk["permname"] = mapper->permission_name(type, permission->type); - blk["permdesc"] = permission->description; - blk["permid"] = permission->type; - } - return list_builder; - }; + blk["permname"] = permission->name; + blk["permname"] = mapper->permission_name(type, permission->type); + blk["permdesc"] = permission->description; + blk["permid"] = permission->type; + } + return list_builder; + }; - auto type = this->getType(); - if(type == CLIENT_TEASPEAK || type == CLIENT_TEAMSPEAK || type == CLIENT_QUERY) { - Command response(this->getExternalType() == CLIENT_TEAMSPEAK ? "notifypermissionlist" : ""); - { - lock_guard lock(permission_list_string_lock); - if(permission_list_string[type].empty()) - permission_list_string[type] = build_permission_list("", type).build(); - response[0][""] = permission_list_string[type]; - } - this->sendCommand(response); - } else { - this->sendCommand(build_permission_list(this->getExternalType() == CLIENT_TEAMSPEAK ? "notifypermissionlist" : "", type)); - } + auto type = this->getType(); + if(type == CLIENT_TEASPEAK || type == CLIENT_TEAMSPEAK || type == CLIENT_QUERY) { + Command response(this->getExternalType() == CLIENT_TEAMSPEAK ? "notifypermissionlist" : ""); + { + lock_guard lock(permission_list_string_lock); + if(permission_list_string[type].empty()) + permission_list_string[type] = build_permission_list("", type).build(); + response[0][""] = permission_list_string[type]; + } + this->sendCommand(response); + } else { + this->sendCommand(build_permission_list(this->getExternalType() == CLIENT_TEAMSPEAK ? "notifypermissionlist" : "", type)); + } return CommandResult::Success; } @@ -950,9 +950,9 @@ CommandResult ConnectedClient::handleCommandPropertyList(ts::Command& cmd) { } CommandResult ConnectedClient::handleCommandServerGroupList(Command &) { - CMD_RESET_IDLE; - CMD_CHK_AND_INC_FLOOD_POINTS(5); - CACHED_PERM_CHECK(permission::b_virtualserver_servergroup_list, 1, true); + CMD_RESET_IDLE; + CMD_CHK_AND_INC_FLOOD_POINTS(5); + CACHED_PERM_CHECK(permission::b_virtualserver_servergroup_list, 1, true); this->notifyServerGroupList(); this->command_times.servergrouplist = system_clock::now(); @@ -971,125 +971,125 @@ CommandResult ConnectedClient::handleCommandChannelGroupAdd(Command &cmd) { if(gr->name() == cmd["name"].string() && gr->target() == GroupTarget::GROUPTARGET_CHANNEL) return {findError("parameter_invalid"), "Group already exists"}; auto group = group_manager->createGroup(GroupTarget::GROUPTARGET_CHANNEL, cmd["type"].as(), cmd["name"].string()); if (group) { - group->permissions()->set_permission(permission::b_group_is_permanent, {1, 0}, permission::v2::set_value, permission::v2::do_nothing); - if(this->server) - this->server->forEachClient([](shared_ptr cl) { - cl->notifyChannelGroupList(); - }); + group->permissions()->set_permission(permission::b_group_is_permanent, {1, 0}, permission::v2::set_value, permission::v2::do_nothing); + if(this->server) + this->server->forEachClient([](shared_ptr cl) { + cl->notifyChannelGroupList(); + }); } else return {findError("parameter_invalid"), "invalid server group id"}; return CommandResult::Success; } //name=Channel\sAdmin scgid=5 tcgid=4 type=1 CommandResult ConnectedClient::handleCommandChannelGroupCopy(Command &cmd) { - CMD_RESET_IDLE; - CMD_CHK_AND_INC_FLOOD_POINTS(5); + CMD_RESET_IDLE; + CMD_CHK_AND_INC_FLOOD_POINTS(5); - auto ref_server = this->server; - CACHED_PERM_CHECK(permission::b_virtualserver_channelgroup_create, 1, true); + auto ref_server = this->server; + CACHED_PERM_CHECK(permission::b_virtualserver_channelgroup_create, 1, true); - auto group_manager = this->server ? this->server->groups : serverInstance->getGroupManager().get(); + auto group_manager = this->server ? this->server->groups : serverInstance->getGroupManager().get(); - auto source_group_id = cmd["scgid"].as(); - auto source_group = group_manager->findGroup(source_group_id); + auto source_group_id = cmd["scgid"].as(); + auto source_group = group_manager->findGroup(source_group_id); - if(!source_group || source_group->target() != GROUPTARGET_CHANNEL) - return {findError("group_invalid_id"), "invalid source group"}; + if(!source_group || source_group->target() != GROUPTARGET_CHANNEL) + return {findError("group_invalid_id"), "invalid source group"}; - const auto group_type_modificable = [&](GroupType type) { - switch(type) { - case GroupType::GROUP_TYPE_TEMPLATE: - if(!this->permission_granted(this->permissionValue(permission::b_serverinstance_modify_templates, 0), 1, true)) - return permission::b_serverinstance_modify_templates; - break; - case GroupType::GROUP_TYPE_QUERY: - if(!this->permission_granted(this->permissionValue(permission::b_serverinstance_modify_querygroup, 0), 1, true)) - return permission::b_serverinstance_modify_querygroup; - break; + const auto group_type_modificable = [&](GroupType type) { + switch(type) { + case GroupType::GROUP_TYPE_TEMPLATE: + if(!this->permission_granted(this->permissionValue(permission::b_serverinstance_modify_templates, 0), 1, true)) + return permission::b_serverinstance_modify_templates; + break; + case GroupType::GROUP_TYPE_QUERY: + if(!this->permission_granted(this->permissionValue(permission::b_serverinstance_modify_querygroup, 0), 1, true)) + return permission::b_serverinstance_modify_querygroup; + break; - default: - break; - } - return permission::undefined; - }; + default: + break; + } + return permission::undefined; + }; - { - auto result = group_type_modificable(source_group->type()); - if(result != permission::undefined) - return CommandResultPermissionError{result}; - } + { + auto result = group_type_modificable(source_group->type()); + if(result != permission::undefined) + return CommandResultPermissionError{result}; + } - auto global_update = false; - if(cmd[0].has("tcgid") && cmd["tcgid"].as() != 0) { - //Copy an existing group - auto target_group = group_manager->findGroup(cmd["tcgid"]); - if(!target_group || target_group->target() != GROUPTARGET_CHANNEL) - return {findError("group_invalid_id"), "invalid target group"}; + auto global_update = false; + if(cmd[0].has("tcgid") && cmd["tcgid"].as() != 0) { + //Copy an existing group + auto target_group = group_manager->findGroup(cmd["tcgid"]); + if(!target_group || target_group->target() != GROUPTARGET_CHANNEL) + return {findError("group_invalid_id"), "invalid target group"}; - { - auto result = group_type_modificable(target_group->type()); - if(result != permission::undefined) - return CommandResultPermissionError{result}; - } + { + auto result = group_type_modificable(target_group->type()); + if(result != permission::undefined) + return CommandResultPermissionError{result}; + } - if(!target_group->permission_granted(permission::i_channel_group_needed_modify_power, this->calculate_permission_value(permission::i_channel_group_modify_power, 0), true)) - return CommandResultPermissionError{permission::i_channel_group_modify_power}; + if(!target_group->permission_granted(permission::i_channel_group_needed_modify_power, this->calculate_permission_value(permission::i_channel_group_modify_power, 0), true)) + return CommandResultPermissionError{permission::i_channel_group_modify_power}; - if(!group_manager->copyGroupPermissions(source_group, target_group)) - return {findError("vs_critical"), "failed to copy group permissions"}; + if(!group_manager->copyGroupPermissions(source_group, target_group)) + return {findError("vs_critical"), "failed to copy group permissions"}; - global_update = !this->server || !group_manager->isLocalGroup(target_group); - } else { - //Copy a new group - auto target_type = cmd["type"].as(); + global_update = !this->server || !group_manager->isLocalGroup(target_group); + } else { + //Copy a new group + auto target_type = cmd["type"].as(); - { - auto result = group_type_modificable(target_type); - if(result != permission::undefined) - return CommandResultPermissionError{result}; - } + { + auto result = group_type_modificable(target_type); + if(result != permission::undefined) + return CommandResultPermissionError{result}; + } - if(!ref_server && target_type == GroupType::GROUP_TYPE_NORMAL) - return {findError("parameter_invalid"), "You cant create normal groups on the template server!"}; + if(!ref_server && target_type == GroupType::GROUP_TYPE_NORMAL) + return {findError("parameter_invalid"), "You cant create normal groups on the template server!"}; - if(!group_manager->findGroup(GroupTarget::GROUPTARGET_CHANNEL, cmd["name"].string()).empty()) - return {findError("group_name_used"), "You cant create normal groups on the template server!"}; + if(!group_manager->findGroup(GroupTarget::GROUPTARGET_CHANNEL, cmd["name"].string()).empty()) + return {findError("group_name_used"), "You cant create normal groups on the template server!"}; - auto target_group_id = group_manager->copyGroup(source_group, target_type, cmd["name"], target_type != GroupType::GROUP_TYPE_NORMAL ? 0 : this->getServerId()); - if(target_group_id == 0) - return {findError("vs_critical"), "failed to copy group"}; + auto target_group_id = group_manager->copyGroup(source_group, target_type, cmd["name"], target_type != GroupType::GROUP_TYPE_NORMAL ? 0 : this->getServerId()); + if(target_group_id == 0) + return {findError("vs_critical"), "failed to copy group"}; - if(this->getType() == ClientType::CLIENT_QUERY) { - Command notify(""); - notify["cgid"] = target_group_id; - this->sendCommand(notify); - } + if(this->getType() == ClientType::CLIENT_QUERY) { + Command notify(""); + notify["cgid"] = target_group_id; + this->sendCommand(notify); + } - global_update = !this->server || !group_manager->isLocalGroup(group_manager->findGroup(target_group_id)); - } + global_update = !this->server || !group_manager->isLocalGroup(group_manager->findGroup(target_group_id)); + } - for(const auto& server : (global_update ? serverInstance->getVoiceServerManager()->serverInstances() : deque>{this->server})) - if(server) - server->forEachClient([](shared_ptr cl) { - cl->notifyChannelGroupList(); - }); - return CommandResult::Success; + for(const auto& server : (global_update ? serverInstance->getVoiceServerManager()->serverInstances() : deque>{this->server})) + if(server) + server->forEachClient([](shared_ptr cl) { + cl->notifyChannelGroupList(); + }); + return CommandResult::Success; } CommandResult ConnectedClient::handleCommandChannelGroupRename(Command &cmd) { CMD_RESET_IDLE; CMD_CHK_AND_INC_FLOOD_POINTS(5); - auto group_manager = this->server ? this->server->getGroupManager() : serverInstance->getGroupManager().get(); + auto group_manager = this->server ? this->server->getGroupManager() : serverInstance->getGroupManager().get(); auto serverGroup = group_manager->findGroup(cmd["cgid"].as()); if (!serverGroup || serverGroup->target() != GROUPTARGET_CHANNEL) return {findError("parameter_invalid"), "invalid channel group id"}; - GROUP_PERMISSION_TEST(permission::i_channel_group_modify_power, permission::i_channel_group_needed_modify_power, serverGroup, true); + GROUP_PERMISSION_TEST(permission::i_channel_group_modify_power, permission::i_channel_group_needed_modify_power, serverGroup, true); - group_manager->renameGroup(serverGroup, cmd["name"].string()); - if(this->server) + group_manager->renameGroup(serverGroup, cmd["name"].string()); + if(this->server) this->server->forEachClient([](shared_ptr cl) { - cl->notifyChannelGroupList(); - }); + cl->notifyChannelGroupList(); + }); return CommandResult::Success; } @@ -1098,21 +1098,21 @@ CommandResult ConnectedClient::handleCommandChannelGroupDel(Command &cmd) { CMD_RESET_IDLE; CMD_CHK_AND_INC_FLOOD_POINTS(5); - auto group_manager = this->server ? this->server->getGroupManager() : serverInstance->getGroupManager().get(); + auto group_manager = this->server ? this->server->getGroupManager() : serverInstance->getGroupManager().get(); auto channel_group = group_manager->findGroup(cmd["cgid"].as()); if (!channel_group || channel_group->target() != GROUPTARGET_CHANNEL) return {findError("parameter_invalid"), "invalid channel group id"}; - CACHED_PERM_CHECK(permission::b_virtualserver_channelgroup_delete, 1, true); + CACHED_PERM_CHECK(permission::b_virtualserver_channelgroup_delete, 1, true); if(this->server) { - if(this->server->properties()[property::VIRTUALSERVER_DEFAULT_CHANNEL_GROUP] == channel_group->groupId()) - return {findError("parameter_invalid"), "Could not delete default channel group!"}; - if(this->server->properties()[property::VIRTUALSERVER_DEFAULT_CHANNEL_ADMIN_GROUP] == channel_group->groupId()) - return {findError("parameter_invalid"), "Could not delete default channel admin group!"}; + if(this->server->properties()[property::VIRTUALSERVER_DEFAULT_CHANNEL_GROUP] == channel_group->groupId()) + return {findError("parameter_invalid"), "Could not delete default channel group!"}; + if(this->server->properties()[property::VIRTUALSERVER_DEFAULT_CHANNEL_ADMIN_GROUP] == channel_group->groupId()) + return {findError("parameter_invalid"), "Could not delete default channel admin group!"}; } - if(serverInstance->properties()[property::SERVERINSTANCE_TEMPLATE_CHANNELDEFAULT_GROUP] == channel_group->groupId()) - return {findError("parameter_invalid"), "Could not delete instance default channel group!"}; - if(serverInstance->properties()[property::SERVERINSTANCE_TEMPLATE_CHANNELADMIN_GROUP] == channel_group->groupId()) - return {findError("parameter_invalid"), "Could not delete instance default channel admin group!"}; + if(serverInstance->properties()[property::SERVERINSTANCE_TEMPLATE_CHANNELDEFAULT_GROUP] == channel_group->groupId()) + return {findError("parameter_invalid"), "Could not delete instance default channel group!"}; + if(serverInstance->properties()[property::SERVERINSTANCE_TEMPLATE_CHANNELADMIN_GROUP] == channel_group->groupId()) + return {findError("parameter_invalid"), "Could not delete instance default channel admin group!"}; if (!cmd["force"].as()) if (!group_manager->listGroupMembers(channel_group, false).empty()) @@ -1121,8 +1121,8 @@ CommandResult ConnectedClient::handleCommandChannelGroupDel(Command &cmd) { if (group_manager->deleteGroup(channel_group) && this->server) { this->server->forEachClient([&](shared_ptr cl) { if(this->server->notifyClientPropertyUpdates(cl, this->server->groups->update_server_group_property(cl, true, cl->getChannel()))) { - if(cl->update_cached_permissions()) /* update cached calculated permissions */ - cl->sendNeededPermissions(false); /* cached permissions had changed, notify the client */ + if(cl->update_cached_permissions()) /* update cached calculated permissions */ + cl->sendNeededPermissions(false); /* cached permissions had changed, notify the client */ } cl->notifyChannelGroupList(); }); @@ -1134,10 +1134,10 @@ CommandResult ConnectedClient::handleCommandChannelGroupDel(Command &cmd) { CommandResult ConnectedClient::handleCommandChannelGroupList(Command &) { CMD_RESET_IDLE; CMD_CHK_AND_INC_FLOOD_POINTS(5); - CACHED_PERM_CHECK(permission::b_virtualserver_channelgroup_list, 1, true); + CACHED_PERM_CHECK(permission::b_virtualserver_channelgroup_list, 1, true); this->notifyChannelGroupList(); - this->command_times.servergrouplist = system_clock::now(); + this->command_times.servergrouplist = system_clock::now(); return CommandResult::Success; } @@ -1145,60 +1145,60 @@ CommandResult ConnectedClient::handleCommandChannelGroupClientList(Command &cmd) CMD_REQ_SERVER; CMD_RESET_IDLE; CMD_CHK_AND_INC_FLOOD_POINTS(5); - CACHED_PERM_CHECK(permission::b_virtualserver_channelgroup_client_list, 1, true); + CACHED_PERM_CHECK(permission::b_virtualserver_channelgroup_client_list, 1, true); Command result(this->getExternalType() == ClientType::CLIENT_TEAMSPEAK ? "notifychannelgroupclientlist" : ""); deque variables{variable{":sid", this->getServerId()}}; string query = "SELECT `groupId`, `cldbid`, `until`, `channelId` FROM `assignedGroups` WHERE `serverId` = :sid"; if(cmd[0].has("cgid") && cmd["cgid"].as() > 0) { - auto group = this->server->getGroupManager()->findGroup(cmd["cgid"]); - if(!group || group->target() != GroupTarget::GROUPTARGET_CHANNEL) - return {findError("parameter_invalid"), "invalid channel group id"}; + auto group = this->server->getGroupManager()->findGroup(cmd["cgid"]); + if(!group || group->target() != GroupTarget::GROUPTARGET_CHANNEL) + return {findError("parameter_invalid"), "invalid channel group id"}; query += " AND `groupId` = :groupId"; variables.push_back({":groupId", cmd["cgid"].as()}); } else { - query += " AND `groupId` IN (SELECT `groupId` FROM `groups` WHERE `serverId` = :sid AND `target` = :target)"; - variables.push_back({":target", GroupTarget::GROUPTARGET_CHANNEL}); + query += " AND `groupId` IN (SELECT `groupId` FROM `groups` WHERE `serverId` = :sid AND `target` = :target)"; + variables.push_back({":target", GroupTarget::GROUPTARGET_CHANNEL}); } if(cmd[0].has("cldbid") && cmd["cldbid"].as() > 0) { - query += " AND `cldbid` = :cldbid"; - variables.push_back({":cldbid", cmd["cldbid"].as()}); + query += " AND `cldbid` = :cldbid"; + variables.push_back({":cldbid", cmd["cldbid"].as()}); } if(cmd[0].has("cid") && cmd["cid"].as() > 0) { - auto channel = this->server->getChannelTree()->findChannel(cmd["cid"]); - if(!channel) - return {findError("parameter_invalid"), "invalid channel id"}; - query += " AND `channelId` = :cid"; - variables.push_back({":cid", cmd["cid"].as()}); + auto channel = this->server->getChannelTree()->findChannel(cmd["cid"]); + if(!channel) + return {findError("parameter_invalid"), "invalid channel id"}; + query += " AND `channelId` = :cid"; + variables.push_back({":cid", cmd["cid"].as()}); } debugMessage(this->getServerId(), "Command channelgroupclientlist sql: {}", query); auto command = sql::command(this->sql, query); for(const auto& variable : variables) - command.value(variable); + command.value(variable); - int index = 0; + int index = 0; command.query([&](Command& command, int& index, int length, string* values, string* names) { - GroupId group_id = 0; - ChannelId channel_id = 0; - ClientDbId cldbid = 0; - for(int i = 0; i < length; i++) { - try { - if(names[i] == "groupId") - group_id = stoll(values[i]); - else if(names[i] == "cldbid") - cldbid = stoll(values[i]); - else if(names[i] == "channelId") - channel_id = stoll(values[i]); - } catch(std::exception& ex) { - logError(this->getServerId(), "Failed to parse db field {}", names[i]); - } - } - result[index]["cid"] = channel_id; - result[index]["cgid"] = group_id; - result[index]["cldbid"] = cldbid; - index++; + GroupId group_id = 0; + ChannelId channel_id = 0; + ClientDbId cldbid = 0; + for(int i = 0; i < length; i++) { + try { + if(names[i] == "groupId") + group_id = stoll(values[i]); + else if(names[i] == "cldbid") + cldbid = stoll(values[i]); + else if(names[i] == "channelId") + channel_id = stoll(values[i]); + } catch(std::exception& ex) { + logError(this->getServerId(), "Failed to parse db field {}", names[i]); + } + } + result[index]["cid"] = channel_id; + result[index]["cgid"] = group_id; + result[index]["cldbid"] = cldbid; + index++; }, result, index); if (index == 0) return {findError("database_empty_result"), "empty"}; this->sendCommand(result); @@ -1207,15 +1207,15 @@ CommandResult ConnectedClient::handleCommandChannelGroupClientList(Command &cmd) CommandResult ConnectedClient::handleCommandChannelGroupPermList(Command &cmd) { CMD_CHK_AND_INC_FLOOD_POINTS(5); - CACHED_PERM_CHECK(permission::b_virtualserver_channelgroup_permission_list, 1, true); + CACHED_PERM_CHECK(permission::b_virtualserver_channelgroup_permission_list, 1, true); auto channelGroup = (this->server ? this->server->groups : serverInstance->getGroupManager().get())->findGroup(cmd["cgid"].as()); if (!channelGroup || channelGroup->target() != GROUPTARGET_CHANNEL) return {findError("parameter_invalid"), "invalid channel group id"}; if (!this->notifyGroupPermList(channelGroup, cmd.hasParm("permsid"))) return {findError("database_empty_result"), "empty"}; - if(this->getType() == ClientType::CLIENT_TEAMSPEAK && this->command_times.last_notify + this->command_times.notify_timeout < system_clock::now()) { - this->sendTSPermEditorWarning(); - } + if(this->getType() == ClientType::CLIENT_TEAMSPEAK && this->command_times.last_notify + this->command_times.notify_timeout < system_clock::now()) { + this->sendTSPermEditorWarning(); + } return CommandResult::Success; } @@ -1223,112 +1223,112 @@ CommandResult ConnectedClient::handleCommandChannelGroupPermList(Command &cmd) { CommandResult ConnectedClient::handleCommandChannelGroupAddPerm(Command &cmd) { CMD_CHK_AND_INC_FLOOD_POINTS(5); - auto group_manager = this->server ? this->server->getGroupManager() : serverInstance->getGroupManager().get(); + auto group_manager = this->server ? this->server->getGroupManager() : serverInstance->getGroupManager().get(); auto channelGroup = group_manager->findGroup(cmd["cgid"].as()); if (!channelGroup || channelGroup->target() != GROUPTARGET_CHANNEL) return {findError("parameter_invalid"), "invalid channel group id"}; - GROUP_PERMISSION_TEST(permission::i_channel_group_modify_power, permission::i_channel_group_needed_modify_power, channelGroup, true); + GROUP_PERMISSION_TEST(permission::i_channel_group_modify_power, permission::i_channel_group_needed_modify_power, channelGroup, true); - auto maxValue = this->getPermissionGrantValue(permission::PERMTEST_ORDERED, permission::i_permission_modify_power, this->currentChannel); - bool ignoreGrant = this->permission_granted(this->cached_permission_value(permission::b_permission_modify_power_ignore), 1); - bool updateList = false; + auto maxValue = this->getPermissionGrantValue(permission::PERMTEST_ORDERED, permission::i_permission_modify_power, this->currentChannel); + bool ignoreGrant = this->permission_granted(this->cached_permission_value(permission::b_permission_modify_power_ignore), 1); + bool updateList = false; bool conOnError = cmd[0].has("continueonerror"); auto permission_manager = channelGroup->permissions(); for (int index = 0; index < cmd.bulkCount(); index++) { - PARSE_PERMISSION(cmd); + PARSE_PERMISSION(cmd); - auto val = cmd[index]["permvalue"].as(); - if(permission_require_granted_value(permType) && val > maxValue) - return CommandResultPermissionError{permission::i_permission_modify_power}; + auto val = cmd[index]["permvalue"].as(); + if(permission_require_granted_value(permType) && val > maxValue) + return CommandResultPermissionError{permission::i_permission_modify_power}; - if(!ignoreGrant && !this->permissionGrantGranted(permission::PERMTEST_ORDERED, permType, 1, this->currentChannel)) - return CommandResultPermissionError{permission::i_permission_modify_power}; + if(!ignoreGrant && !this->permissionGrantGranted(permission::PERMTEST_ORDERED, permType, 1, this->currentChannel)) + return CommandResultPermissionError{permission::i_permission_modify_power}; - if (grant) { - permission_manager->set_permission(permType, {0, cmd[index]["permvalue"]}, permission::v2::PermissionUpdateType::do_nothing, permission::v2::PermissionUpdateType::set_value); - } else { - permission_manager->set_permission( - permType, - {cmd[index]["permvalue"], 0}, - permission::v2::PermissionUpdateType::set_value, - permission::v2::PermissionUpdateType::do_nothing, + if (grant) { + permission_manager->set_permission(permType, {0, cmd[index]["permvalue"]}, permission::v2::PermissionUpdateType::do_nothing, permission::v2::PermissionUpdateType::set_value); + } else { + permission_manager->set_permission( + permType, + {cmd[index]["permvalue"], 0}, + permission::v2::PermissionUpdateType::set_value, + permission::v2::PermissionUpdateType::do_nothing, - cmd[index]["permskip"].as() ? 1 : 0, - cmd[index]["permnegated"].as() ? 1 : 0 - ); - updateList |= permission_is_group_property(permType); - } + cmd[index]["permskip"].as() ? 1 : 0, + cmd[index]["permnegated"].as() ? 1 : 0 + ); + updateList |= permission_is_group_property(permType); + } } - if(updateList) - channelGroup->apply_properties_from_permissions(); + if(updateList) + channelGroup->apply_properties_from_permissions(); if(this->server) { - if(updateList) - this->server->forEachClient([](shared_ptr cl) { - cl->notifyChannelGroupList(); - }); - this->server->forEachClient([channelGroup](shared_ptr cl) { - unique_lock client_channel_lock(cl->channel_lock); /* while we're updating groups we dont want to change anything! */ - if (cl->channelGroupAssigned(channelGroup, cl->getChannel())) { - if(cl->update_cached_permissions()) - cl->sendNeededPermissions(false); /* update the needed permissions */ - cl->updateChannelClientProperties(false, true); - cl->join_state_id++; /* join permission may changed, all channels need to be recalculate dif needed */ - } - }); + if(updateList) + this->server->forEachClient([](shared_ptr cl) { + cl->notifyChannelGroupList(); + }); + this->server->forEachClient([channelGroup](shared_ptr cl) { + unique_lock client_channel_lock(cl->channel_lock); /* while we're updating groups we dont want to change anything! */ + if (cl->channelGroupAssigned(channelGroup, cl->getChannel())) { + if(cl->update_cached_permissions()) + cl->sendNeededPermissions(false); /* update the needed permissions */ + cl->updateChannelClientProperties(false, true); + cl->join_state_id++; /* join permission may changed, all channels need to be recalculate dif needed */ + } + }); } return CommandResult::Success; } CommandResult ConnectedClient::handleCommandChannelGroupDelPerm(Command &cmd) { CMD_CHK_AND_INC_FLOOD_POINTS(5); - auto group_manager = this->server ? this->server->getGroupManager() : serverInstance->getGroupManager().get(); + auto group_manager = this->server ? this->server->getGroupManager() : serverInstance->getGroupManager().get(); auto channelGroup = group_manager->findGroup(cmd["cgid"].as()); if (!channelGroup || channelGroup->target() != GROUPTARGET_CHANNEL) return {findError("parameter_invalid"), "invalid channel group id"}; - GROUP_PERMISSION_TEST(permission::i_channel_group_modify_power, permission::i_channel_group_needed_modify_power, channelGroup, true); + GROUP_PERMISSION_TEST(permission::i_channel_group_modify_power, permission::i_channel_group_needed_modify_power, channelGroup, true); - bool ignoreGrant = this->permission_granted(this->cached_permission_value(permission::b_permission_modify_power_ignore), 1); - bool updateList = false; - bool conOnError = cmd[0].has("continueonerror"); + bool ignoreGrant = this->permission_granted(this->cached_permission_value(permission::b_permission_modify_power_ignore), 1); + bool updateList = false; + bool conOnError = cmd[0].has("continueonerror"); - auto permission_manager = channelGroup->permissions(); + auto permission_manager = channelGroup->permissions(); for (int index = 0; index < cmd.bulkCount(); index++) { PARSE_PERMISSION(cmd) - if(!ignoreGrant && !this->permissionGrantGranted(permission::PERMTEST_ORDERED, permType, 1, this->currentChannel)) - return CommandResultPermissionError{permission::i_permission_modify_power}; + if(!ignoreGrant && !this->permissionGrantGranted(permission::PERMTEST_ORDERED, permType, 1, this->currentChannel)) + return CommandResultPermissionError{permission::i_permission_modify_power}; if (grant) { - permission_manager->set_permission(permType, permission::v2::empty_permission_values, permission::v2::PermissionUpdateType::do_nothing, permission::v2::PermissionUpdateType::delete_value); - } else { - permission_manager->set_permission( - permType, - permission::v2::empty_permission_values, - permission::v2::PermissionUpdateType::delete_value, - permission::v2::PermissionUpdateType::do_nothing - ); - updateList |= permission_is_group_property(permType); - } + permission_manager->set_permission(permType, permission::v2::empty_permission_values, permission::v2::PermissionUpdateType::do_nothing, permission::v2::PermissionUpdateType::delete_value); + } else { + permission_manager->set_permission( + permType, + permission::v2::empty_permission_values, + permission::v2::PermissionUpdateType::delete_value, + permission::v2::PermissionUpdateType::do_nothing + ); + updateList |= permission_is_group_property(permType); + } } - if(updateList) - channelGroup->apply_properties_from_permissions(); + if(updateList) + channelGroup->apply_properties_from_permissions(); if(this->server) { - if(updateList) - this->server->forEachClient([](shared_ptr cl) { - cl->notifyChannelGroupList(); - }); - this->server->forEachClient([channelGroup](shared_ptr cl) { - unique_lock client_channel_lock(cl->channel_lock); /* while we're updating groups we dont want to change anything! */ - if (cl->channelGroupAssigned(channelGroup, cl->getChannel())) { - if(cl->update_cached_permissions()) /* update cached calculated permissions */ - cl->sendNeededPermissions(false); /* cached permissions had changed, notify the client */ - cl->updateChannelClientProperties(false, false); - cl->join_state_id++; /* join permission may changed, all channels need to be recalculate dif needed */ - } - }); + if(updateList) + this->server->forEachClient([](shared_ptr cl) { + cl->notifyChannelGroupList(); + }); + this->server->forEachClient([channelGroup](shared_ptr cl) { + unique_lock client_channel_lock(cl->channel_lock); /* while we're updating groups we dont want to change anything! */ + if (cl->channelGroupAssigned(channelGroup, cl->getChannel())) { + if(cl->update_cached_permissions()) /* update cached calculated permissions */ + cl->sendNeededPermissions(false); /* cached permissions had changed, notify the client */ + cl->updateChannelClientProperties(false, false); + cl->join_state_id++; /* join permission may changed, all channels need to be recalculate dif needed */ + } + }); } return CommandResult::Success; } @@ -1343,103 +1343,103 @@ CommandResult ConnectedClient::handleCommandClientMove(Command &cmd) { shared_lock server_channel_r_lock(this->server->channel_tree_lock); auto target_client_id = cmd["clid"].as(); auto target_client = target_client_id == 0 ? this->ref() : this->server->findClient(target_client_id); - if(!target_client) { - return {findError("client_invalid_id"), "Invalid target clid"}; - } + if(!target_client) { + return {findError("client_invalid_id"), "Invalid target clid"}; + } if(!target_client->currentChannel) { - if(target_client != this) - return {findError("client_invalid_id"), "Invalid target clid"}; + if(target_client != this) + return {findError("client_invalid_id"), "Invalid target clid"}; } auto channel = this->server->channelTree->findChannel(cmd["cid"].as()); if (!channel) { - return {findError("channel_invalid_id")}; + return {findError("channel_invalid_id")}; } - auto permission_cache = make_shared(); + auto permission_cache = make_shared(); if(!cmd[0].has("cpw")) - cmd["cpw"] = ""; + cmd["cpw"] = ""; if (!channel->passwordMatch(cmd["cpw"], true)) if (!this->permissionGranted(permission::PERMTEST_ORDERED, permission::b_channel_join_ignore_password, 1, channel, true, permission_cache)) return {findError("channel_invalid_password"), "invalid password"}; switch(channel->channelType()) { - case ChannelType::permanent: - PERM_CHECK_CHANNEL_CR(permission::b_channel_join_permanent, 1, channel, true, permission_cache); - break; - case ChannelType::semipermanent: - PERM_CHECK_CHANNEL_CR(permission::b_channel_join_semi_permanent, 1, channel, true, permission_cache); - break; - case ChannelType::temporary: - PERM_CHECK_CHANNEL_CR(permission::b_channel_join_temporary, 1, channel, true, permission_cache); - break; + case ChannelType::permanent: + PERM_CHECK_CHANNEL_CR(permission::b_channel_join_permanent, 1, channel, true, permission_cache); + break; + case ChannelType::semipermanent: + PERM_CHECK_CHANNEL_CR(permission::b_channel_join_semi_permanent, 1, channel, true, permission_cache); + break; + case ChannelType::temporary: + PERM_CHECK_CHANNEL_CR(permission::b_channel_join_temporary, 1, channel, true, permission_cache); + break; } if (!channel->properties()[property::CHANNEL_FLAG_MAXCLIENTS_UNLIMITED].as() || !channel->properties()[property::CHANNEL_FLAG_MAXFAMILYCLIENTS_UNLIMITED].as()) { - if(!this->permissionGranted(permission::PERMTEST_ORDERED, permission::b_channel_join_ignore_maxclients, 1, channel, true, permission_cache)) { - if(!channel->properties()[property::CHANNEL_FLAG_MAXCLIENTS_UNLIMITED].as()) { - auto maxClients = channel->properties()[property::CHANNEL_MAXCLIENTS].as(); - if (maxClients >= 0 && maxClients <= this->server->getClientsByChannel(channel).size()) - return {findError("channel_maxclients_reached"), ""}; - } - if(!channel->properties()[property::CHANNEL_FLAG_MAXFAMILYCLIENTS_UNLIMITED].as()) { - shared_ptr family_root; + if(!this->permissionGranted(permission::PERMTEST_ORDERED, permission::b_channel_join_ignore_maxclients, 1, channel, true, permission_cache)) { + if(!channel->properties()[property::CHANNEL_FLAG_MAXCLIENTS_UNLIMITED].as()) { + auto maxClients = channel->properties()[property::CHANNEL_MAXCLIENTS].as(); + if (maxClients >= 0 && maxClients <= this->server->getClientsByChannel(channel).size()) + return {findError("channel_maxclients_reached"), ""}; + } + if(!channel->properties()[property::CHANNEL_FLAG_MAXFAMILYCLIENTS_UNLIMITED].as()) { + shared_ptr family_root; - if(channel->properties()[property::CHANNEL_FLAG_MAXFAMILYCLIENTS_INHERITED].as()) { - family_root = channel; - while(family_root && family_root->properties()[property::CHANNEL_FLAG_MAXFAMILYCLIENTS_INHERITED].as()) family_root = family_root->parent(); - } - if(family_root && !family_root->properties()[property::CHANNEL_FLAG_MAXFAMILYCLIENTS_UNLIMITED]) { //Could not be CHANNEL_FLAG_MAXFAMILYCLIENTS_INHERITED - auto maxClients = family_root->properties()[property::CHANNEL_MAXFAMILYCLIENTS].as(); - auto clients = 0; - for(const auto& entry : this->server->getClientsByChannelRoot(channel, false)) if(entry.get() != this) clients++; //Dont count the client itself - if (maxClients >= 0 && maxClients <= clients) - return {findError("channel_maxfamily_reached"), ""}; - } - } - } + if(channel->properties()[property::CHANNEL_FLAG_MAXFAMILYCLIENTS_INHERITED].as()) { + family_root = channel; + while(family_root && family_root->properties()[property::CHANNEL_FLAG_MAXFAMILYCLIENTS_INHERITED].as()) family_root = family_root->parent(); + } + if(family_root && !family_root->properties()[property::CHANNEL_FLAG_MAXFAMILYCLIENTS_UNLIMITED]) { //Could not be CHANNEL_FLAG_MAXFAMILYCLIENTS_INHERITED + auto maxClients = family_root->properties()[property::CHANNEL_MAXFAMILYCLIENTS].as(); + auto clients = 0; + for(const auto& entry : this->server->getClientsByChannelRoot(channel, false)) if(entry.get() != this) clients++; //Dont count the client itself + if (maxClients >= 0 && maxClients <= clients) + return {findError("channel_maxfamily_reached"), ""}; + } + } + } } if(!this->permissionGranted(permission::PERMTEST_ORDERED, permission::b_channel_ignore_join_power, 1, channel, true, permission_cache)) { - CHANNEL_PERMISSION_TEST(permission::i_channel_join_power, permission::i_channel_needed_join_power, channel, false); + CHANNEL_PERMISSION_TEST(permission::i_channel_join_power, permission::i_channel_needed_join_power, channel, false); } - if (target_client == this) { - auto permission_cache_current = make_shared(); - auto val = this->permissionValue(permission::PERMTEST_ORDERED, permission::b_client_is_sticky, this->currentChannel, permission_cache_current); - if (val != permNotGranted && val > 0) { - auto st = this->permissionValue(permission::PERMTEST_ORDERED, permission::b_client_ignore_sticky, this->currentChannel, permission_cache_current); - if (st != 1) - return CommandResultPermissionError{permission::b_client_is_sticky}; - } - } + if (target_client == this) { + auto permission_cache_current = make_shared(); + auto val = this->permissionValue(permission::PERMTEST_ORDERED, permission::b_client_is_sticky, this->currentChannel, permission_cache_current); + if (val != permNotGranted && val > 0) { + auto st = this->permissionValue(permission::PERMTEST_ORDERED, permission::b_client_ignore_sticky, this->currentChannel, permission_cache_current); + if (st != 1) + return CommandResultPermissionError{permission::b_client_is_sticky}; + } + } - if (target_client != this) { - PERM_CHECK_CHANNELR(permission::i_client_move_power, target_client->permissionValue(permission::PERMTEST_ORDERED, permission::i_client_needed_move_power, target_client->getChannel()), target_client->getChannel(), true); - PERM_CHECK_CHANNEL_CR(permission::i_client_move_power, target_client->permissionValue(permission::PERMTEST_ORDERED, permission::i_client_needed_move_power, channel), channel, true, permission_cache); - } + if (target_client != this) { + PERM_CHECK_CHANNELR(permission::i_client_move_power, target_client->permissionValue(permission::PERMTEST_ORDERED, permission::i_client_needed_move_power, target_client->getChannel()), target_client->getChannel(), true); + PERM_CHECK_CHANNEL_CR(permission::i_client_move_power, target_client->permissionValue(permission::PERMTEST_ORDERED, permission::i_client_needed_move_power, channel), channel, true, permission_cache); + } - server_channel_r_lock.unlock(); - unique_lock server_channel_w_lock(this->server->channel_tree_lock); - auto oldChannel = target_client->getChannel(); - this->server->client_move( - target_client, - channel, - target_client == this ? nullptr : _this.lock(), - "", - target_client == this ? ViewReasonId::VREASON_USER_ACTION : ViewReasonId::VREASON_MOVED, - true, - server_channel_w_lock - ); + server_channel_r_lock.unlock(); + unique_lock server_channel_w_lock(this->server->channel_tree_lock); + auto oldChannel = target_client->getChannel(); + this->server->client_move( + target_client, + channel, + target_client == this ? nullptr : _this.lock(), + "", + target_client == this ? ViewReasonId::VREASON_USER_ACTION : ViewReasonId::VREASON_MOVED, + true, + server_channel_w_lock + ); - if(oldChannel) { - if(!server_channel_w_lock.owns_lock()) - server_channel_w_lock.lock(); - if(oldChannel->channelType() == ChannelType::temporary && oldChannel->properties()[property::CHANNEL_DELETE_DELAY].as() == 0) - if(this->server->getClientsByChannelRoot(oldChannel, false).empty()) - this->server->delete_channel(dynamic_pointer_cast(oldChannel), this->ref(), "temporary auto delete", server_channel_w_lock); - if(server_channel_w_lock.owns_lock()) - server_channel_w_lock.unlock(); - } + if(oldChannel) { + if(!server_channel_w_lock.owns_lock()) + server_channel_w_lock.lock(); + if(oldChannel->channelType() == ChannelType::temporary && oldChannel->properties()[property::CHANNEL_DELETE_DELAY].as() == 0) + if(this->server->getClientsByChannelRoot(oldChannel, false).empty()) + this->server->delete_channel(dynamic_pointer_cast(oldChannel), this->ref(), "temporary auto delete", server_channel_w_lock); + if(server_channel_w_lock.owns_lock()) + server_channel_w_lock.unlock(); + } return CommandResult::Success; } @@ -1449,14 +1449,14 @@ CommandResult ConnectedClient::handleCommandChannelCreate(Command &cmd) { CMD_CHK_AND_INC_FLOOD_POINTS(25); CMD_CHK_PARM_COUNT(1); - auto permission_cache = make_shared(); + auto permission_cache = make_shared(); if (cmd[0].has("cpid") && cmd["cpid"].as() != 0) CACHED_PERM_CHECK(permission::b_channel_create_child, 1); if (cmd[0].has("channel_order")) CACHED_PERM_CHECK(permission::b_channel_create_with_sortorder, 1); - if(!cmd[0].has("channel_flag_permanent")) cmd[0]["channel_flag_permanent"] = false; - if(!cmd[0].has("channel_flag_semi_permanent")) cmd[0]["channel_flag_semi_permanent"] = false; - if(!cmd[0].has("channel_flag_default")) cmd[0]["channel_flag_default"] = false; - if(!cmd[0].has("channel_flag_password")) cmd[0]["channel_flag_password"] = false; + if(!cmd[0].has("channel_flag_permanent")) cmd[0]["channel_flag_permanent"] = false; + if(!cmd[0].has("channel_flag_semi_permanent")) cmd[0]["channel_flag_semi_permanent"] = false; + if(!cmd[0].has("channel_flag_default")) cmd[0]["channel_flag_default"] = false; + if(!cmd[0].has("channel_flag_password")) cmd[0]["channel_flag_password"] = false; if (cmd[0]["channel_flag_permanent"].as()) CACHED_PERM_CHECK(permission::b_channel_create_permanent, 1); else if (cmd[0]["channel_flag_semi_permanent"].as()) CACHED_PERM_CHECK(permission::b_channel_create_semi_permanent, 1); @@ -1469,14 +1469,14 @@ CommandResult ConnectedClient::handleCommandChannelCreate(Command &cmd) { else if(this->permission_granted(this->cached_permission_value(permission::b_channel_create_modify_with_force_password), 1)) return CommandResultPermissionError{permission::b_channel_create_modify_with_force_password}; if(cmd[0].has("channel_password") && this->getType() == ClientType::CLIENT_QUERY) - cmd["channel_password"] = base64::decode(digest::sha1(cmd["channel_password"].string())); + cmd["channel_password"] = base64::decode(digest::sha1(cmd["channel_password"].string())); if (cmd[0].has("channel_description")) CACHED_PERM_CHECK(permission::b_channel_create_with_description, 1); if (cmd[0].has("channel_maxclients") || (cmd[0].has("channel_flag_maxclients_unlimited") && !cmd["channel_flag_maxclients_unlimited"].as())) { - CACHED_PERM_CHECK(permission::b_channel_create_with_maxclients, 1); - if(!cmd[0]["channel_flag_permanent"].as() && !cmd[0]["channel_flag_semi_permanent"].as()) { - cmd["channel_maxclients"] = -1; - cmd["channel_flag_maxclients_unlimited"] = 1; - } + CACHED_PERM_CHECK(permission::b_channel_create_with_maxclients, 1); + if(!cmd[0]["channel_flag_permanent"].as() && !cmd[0]["channel_flag_semi_permanent"].as()) { + cmd["channel_maxclients"] = -1; + cmd["channel_flag_maxclients_unlimited"] = 1; + } } if (cmd[0].has("channel_maxfamilyclients")) CACHED_PERM_CHECK(permission::b_channel_create_with_maxfamilyclients, 1); if (cmd[0].has("channel_needed_talk_power")) CACHED_PERM_CHECK(permission::b_channel_create_with_needed_talk_power, 1); @@ -1484,218 +1484,218 @@ CommandResult ConnectedClient::handleCommandChannelCreate(Command &cmd) { auto target_tree = this->server ? this->server->channelTree : serverInstance->getChannelTree().get(); auto& tree_lock = this->server ? this->server->channel_tree_lock : serverInstance->getChannelTreeLock(); - unique_lock tree_channel_lock(tree_lock); + unique_lock tree_channel_lock(tree_lock); - if(cmd[0].has("channel_conversation_history_length")) { - auto value = cmd["channel_conversation_history_length"].as(); - if(value == 0) { - CACHED_PERM_CHECK(permission::b_channel_create_modify_conversation_history_unlimited, 1); - } else { - CACHED_PERM_CHECK(permission::i_channel_create_modify_conversation_history_length, 1); - } - } + if(cmd[0].has("channel_conversation_history_length")) { + auto value = cmd["channel_conversation_history_length"].as(); + if(value == 0) { + CACHED_PERM_CHECK(permission::b_channel_create_modify_conversation_history_unlimited, 1); + } else { + CACHED_PERM_CHECK(permission::i_channel_create_modify_conversation_history_length, 1); + } + } - { - auto delete_delay = cmd[0].has("channel_delete_delay") ? cmd["channel_delete_delay"].as() : 0UL; - if(delete_delay == 0) { - if(this->server) - cmd["channel_delete_delay"] = this->server->properties()[property::VIRTUALSERVER_CHANNEL_TEMP_DELETE_DELAY_DEFAULT].as(); - else - cmd["channel_delete_delay"] = 0; - } else { - CACHED_PERM_CHECK(permission::i_channel_create_modify_with_temp_delete_delay, cmd["channel_delete_delay"].as()); - } - } + { + auto delete_delay = cmd[0].has("channel_delete_delay") ? cmd["channel_delete_delay"].as() : 0UL; + if(delete_delay == 0) { + if(this->server) + cmd["channel_delete_delay"] = this->server->properties()[property::VIRTUALSERVER_CHANNEL_TEMP_DELETE_DELAY_DEFAULT].as(); + else + cmd["channel_delete_delay"] = 0; + } else { + CACHED_PERM_CHECK(permission::i_channel_create_modify_with_temp_delete_delay, cmd["channel_delete_delay"].as()); + } + } - { - size_t created_tmp = 0, created_semi = 0, created_perm = 0; - auto own_cldbid = this->getClientDatabaseId(); - for(const auto& channel : target_tree->channels()) { - if(channel->properties()[property::CHANNEL_CREATED_BY] == own_cldbid) { - if(channel->properties()[property::CHANNEL_FLAG_PERMANENT].as()) - created_perm++; - else if(channel->properties()[property::CHANNEL_FLAG_SEMI_PERMANENT].as()) - created_semi++; - else - created_tmp++; - } - } + { + size_t created_tmp = 0, created_semi = 0, created_perm = 0; + auto own_cldbid = this->getClientDatabaseId(); + for(const auto& channel : target_tree->channels()) { + if(channel->properties()[property::CHANNEL_CREATED_BY] == own_cldbid) { + if(channel->properties()[property::CHANNEL_FLAG_PERMANENT].as()) + created_perm++; + else if(channel->properties()[property::CHANNEL_FLAG_SEMI_PERMANENT].as()) + created_semi++; + else + created_tmp++; + } + } - auto max_channels = this->permissionValue(permission::PERMTEST_ORDERED, permission::i_client_max_channels, nullptr, permission_cache); + auto max_channels = this->permissionValue(permission::PERMTEST_ORDERED, permission::i_client_max_channels, nullptr, permission_cache); - if(max_channels >= 0) { - if(max_channels <= created_perm + created_semi + created_tmp) - return CommandResultPermissionError{permission::i_client_max_channels}; - } + if(max_channels >= 0) { + if(max_channels <= created_perm + created_semi + created_tmp) + return CommandResultPermissionError{permission::i_client_max_channels}; + } - if (cmd[0]["channel_flag_permanent"].as()) { - max_channels = this->permissionValue(permission::PERMTEST_ORDERED, permission::i_client_max_permanent_channels, nullptr, permission_cache); - if(max_channels >= 0) { - if(max_channels <= created_perm) - return CommandResultPermissionError{permission::i_client_max_permanent_channels}; - } - } - else if (cmd[0]["channel_flag_semi_permanent"].as()) { - max_channels = this->permissionValue(permission::PERMTEST_ORDERED, permission::i_client_max_semi_channels, nullptr, permission_cache); - if(max_channels >= 0) { - if(max_channels <= created_semi) - return CommandResultPermissionError{permission::i_client_max_semi_channels}; - } - } - else { - max_channels = this->permissionValue(permission::PERMTEST_ORDERED, permission::i_client_max_temporary_channels, nullptr, permission_cache); - if(max_channels >= 0) { - if(max_channels <= created_tmp) - return CommandResultPermissionError{permission::i_client_max_temporary_channels}; - } - } - } + if (cmd[0]["channel_flag_permanent"].as()) { + max_channels = this->permissionValue(permission::PERMTEST_ORDERED, permission::i_client_max_permanent_channels, nullptr, permission_cache); + if(max_channels >= 0) { + if(max_channels <= created_perm) + return CommandResultPermissionError{permission::i_client_max_permanent_channels}; + } + } + else if (cmd[0]["channel_flag_semi_permanent"].as()) { + max_channels = this->permissionValue(permission::PERMTEST_ORDERED, permission::i_client_max_semi_channels, nullptr, permission_cache); + if(max_channels >= 0) { + if(max_channels <= created_semi) + return CommandResultPermissionError{permission::i_client_max_semi_channels}; + } + } + else { + max_channels = this->permissionValue(permission::PERMTEST_ORDERED, permission::i_client_max_temporary_channels, nullptr, permission_cache); + if(max_channels >= 0) { + if(max_channels <= created_tmp) + return CommandResultPermissionError{permission::i_client_max_temporary_channels}; + } + } + } //TODO check voice (opus etc) std::shared_ptr parent = nullptr; - std::shared_ptr created_channel = nullptr, old_default_channel; + std::shared_ptr created_channel = nullptr, old_default_channel; - //bool enforce_permanent_parent = cmd[0]["channel_flag_default"].as(); //TODO check parents here - { //Checkout the parent(s) - if (cmd[0].has("cpid") && cmd["cpid"].as() != 0 && cmd["cpid"].as() != -1) { - parent = target_tree->findLinkedChannel(cmd["cpid"].as()); - if (!parent) return {findError("channel_invalid_id"), "Cant resolve parent channel"}; - } + //bool enforce_permanent_parent = cmd[0]["channel_flag_default"].as(); //TODO check parents here + { //Checkout the parent(s) + if (cmd[0].has("cpid") && cmd["cpid"].as() != 0 && cmd["cpid"].as() != -1) { + parent = target_tree->findLinkedChannel(cmd["cpid"].as()); + if (!parent) return {findError("channel_invalid_id"), "Cant resolve parent channel"}; + } - { + { - auto min_channel_deep = this->permissionValue(permission::PERMTEST_ORDERED, permission::i_channel_min_depth, nullptr, permission_cache); - auto max_channel_deep = this->permissionValue(permission::PERMTEST_ORDERED, permission::i_channel_max_depth, nullptr, permission_cache); + auto min_channel_deep = this->permissionValue(permission::PERMTEST_ORDERED, permission::i_channel_min_depth, nullptr, permission_cache); + auto max_channel_deep = this->permissionValue(permission::PERMTEST_ORDERED, permission::i_channel_max_depth, nullptr, permission_cache); - if(min_channel_deep >= 0 || max_channel_deep >= 0) { - auto channel_deep = 0; - auto local_parent = parent; - while(local_parent) { - channel_deep++; - { - const auto typed_parent = dynamic_pointer_cast(local_parent->entry); - if(typed_parent->deleted) return {findError("channel_is_deleted"), "Oneof parent is deleted"}; - } - local_parent = local_parent->parent.lock(); - } + if(min_channel_deep >= 0 || max_channel_deep >= 0) { + auto channel_deep = 0; + auto local_parent = parent; + while(local_parent) { + channel_deep++; + { + const auto typed_parent = dynamic_pointer_cast(local_parent->entry); + if(typed_parent->deleted) return {findError("channel_is_deleted"), "Oneof parent is deleted"}; + } + local_parent = local_parent->parent.lock(); + } - if(min_channel_deep >= 0 && channel_deep < min_channel_deep) return CommandResultPermissionError{permission::i_channel_min_depth}; - if(max_channel_deep >= 0 && channel_deep > max_channel_deep) return CommandResultPermissionError{permission::i_channel_max_depth}; - } - } - } + if(min_channel_deep >= 0 && channel_deep < min_channel_deep) return CommandResultPermissionError{permission::i_channel_min_depth}; + if(max_channel_deep >= 0 && channel_deep > max_channel_deep) return CommandResultPermissionError{permission::i_channel_max_depth}; + } + } + } if(!cmd[0].has("channel_order")) { auto last = parent ? parent->child_head : target_tree->tree_head(); while(last && last->next) last = last->next; if(last) - cmd["channel_order"] = last->entry->channelId(); + cmd["channel_order"] = last->entry->channelId(); } else { } - if (cmd["channel_name"].string().length() < 1) return {findError("channel_name_inuse"), "Invalid channel name"}; + if (cmd["channel_name"].string().length() < 1) return {findError("channel_name_inuse"), "Invalid channel name"}; - { - if (target_tree->findChannel(cmd["channel_name"].as(), parent ? dynamic_pointer_cast(parent->entry) : nullptr)) return {findError("channel_name_inuse"), "Name already in use"}; + { + if (target_tree->findChannel(cmd["channel_name"].as(), parent ? dynamic_pointer_cast(parent->entry) : nullptr)) return {findError("channel_name_inuse"), "Name already in use"}; - created_channel = target_tree->createChannel(parent ? parent->entry->channelId() : (ChannelId) 0, cmd[0].has("channel_order") ? cmd["channel_order"].as() : (ChannelId) 0, cmd["channel_name"].as()); - } + created_channel = target_tree->createChannel(parent ? parent->entry->channelId() : (ChannelId) 0, cmd[0].has("channel_order") ? cmd["channel_order"].as() : (ChannelId) 0, cmd["channel_name"].as()); + } if (!created_channel) return {findError("channel_invalid_flags"), "Could not create channel"}; - auto created_linked_channel = target_tree->findLinkedChannel(created_channel->channelId()); + auto created_linked_channel = target_tree->findLinkedChannel(created_channel->channelId()); sassert(created_linked_channel); if (cmd[0].has("channel_flag_default") && cmd["channel_flag_default"].as()) { - old_default_channel = target_tree->getDefaultChannel(); - target_tree->setDefaultChannel(created_channel); + old_default_channel = target_tree->getDefaultChannel(); + target_tree->setDefaultChannel(created_channel); } created_channel->properties()[property::CHANNEL_CREATED_BY] = this->getClientDatabaseId(); - { - auto permission_manager = created_channel->permissions(); - permission_manager->set_permission( - permission::i_channel_needed_modify_power, + { + auto permission_manager = created_channel->permissions(); + permission_manager->set_permission( + permission::i_channel_needed_modify_power, {this->permissionValue(permission::PERMTEST_ORDERED, permission::i_channel_modify_power, this->currentChannel, permission_cache), 0}, permission::v2::PermissionUpdateType::set_value, permission::v2::PermissionUpdateType::do_nothing ); - permission_manager->set_permission( - permission::i_channel_needed_delete_power, - {this->permissionValue(permission::PERMTEST_ORDERED, permission::i_channel_delete_power, this->currentChannel, permission_cache), 0}, - permission::v2::PermissionUpdateType::set_value, - permission::v2::PermissionUpdateType::do_nothing - ); - } + permission_manager->set_permission( + permission::i_channel_needed_delete_power, + {this->permissionValue(permission::PERMTEST_ORDERED, permission::i_channel_delete_power, this->currentChannel, permission_cache), 0}, + permission::v2::PermissionUpdateType::set_value, + permission::v2::PermissionUpdateType::do_nothing + ); + } for (auto &prop : cmd[0].keys()) { if (prop == "channel_flag_default") continue; - if (prop == "channel_order") continue; - if (prop == "channel_name") continue; - if (prop == "cpid") continue; - if (prop == "cid") continue; + if (prop == "channel_order") continue; + if (prop == "channel_name") continue; + if (prop == "cpid") continue; + if (prop == "cid") continue; - const auto &property = property::info(prop); - if(*property == property::CHANNEL_UNDEFINED) { - logError(this->getServerId(), "Client " + this->getDisplayName() + " tried to change a not existing channel property " + prop); - continue; - } + const auto &property = property::info(prop); + if(*property == property::CHANNEL_UNDEFINED) { + logError(this->getServerId(), "Client " + this->getDisplayName() + " tried to change a not existing channel property " + prop); + continue; + } - if(!property->validate_input(cmd[prop].as())) { - logError(this->getServerId(), "Client " + this->getDisplayName() + " tried to change a property to an invalid value. (Value: '" + cmd[prop].as() + "', Property: '" + property->name + "')"); - continue; - } + if(!property->validate_input(cmd[prop].as())) { + logError(this->getServerId(), "Client " + this->getDisplayName() + " tried to change a property to an invalid value. (Value: '" + cmd[prop].as() + "', Property: '" + property->name + "')"); + continue; + } created_channel->properties()[property] = cmd[prop].as(); } - if(created_channel->parent()) { - if(created_channel->parent()->channelType() > created_channel->channelType()) - created_channel->setChannelType(created_channel->parent()->channelType()); - } + if(created_channel->parent()) { + if(created_channel->parent()->channelType() > created_channel->channelType()) + created_channel->setChannelType(created_channel->parent()->channelType()); + } - if(this->server) { - const auto self_lock = _this.lock(); - this->server->forEachClient([&, created_channel](const shared_ptr& client) { - unique_lock client_channel_lock(client->channel_lock); - auto result = client->channels->add_channel(created_linked_channel); - if(!result) return; + if(this->server) { + const auto self_lock = _this.lock(); + this->server->forEachClient([&, created_channel](const shared_ptr& client) { + unique_lock client_channel_lock(client->channel_lock); + auto result = client->channels->add_channel(created_linked_channel); + if(!result) return; - client->notifyChannelCreate(created_channel, result->previous_channel, self_lock); - if(client == self_lock && this->getType() == ClientType::CLIENT_QUERY) { - Command notify(""); - notify["cid"] = created_channel->channelId(); - this->sendCommand(notify); - } - client->notifyChannelDescriptionChanged(created_channel); - if (client->subscribeToAll) - client->subscribeChannel({created_channel}, false, true); + client->notifyChannelCreate(created_channel, result->previous_channel, self_lock); + if(client == self_lock && this->getType() == ClientType::CLIENT_QUERY) { + Command notify(""); + notify["cid"] = created_channel->channelId(); + this->sendCommand(notify); + } + client->notifyChannelDescriptionChanged(created_channel); + if (client->subscribeToAll) + client->subscribeChannel({created_channel}, false, true); - if(old_default_channel) { - //TODO: Reminder: client channel tree must be at least read locked here! - client->notifyChannelEdited(old_default_channel, {property::CHANNEL_FLAG_DEFAULT}, self_lock, false); - } - }); + if(old_default_channel) { + //TODO: Reminder: client channel tree must be at least read locked here! + client->notifyChannelEdited(old_default_channel, {property::CHANNEL_FLAG_DEFAULT}, self_lock, false); + } + }); - GroupId adminGroup = this->server->properties()[property::VIRTUALSERVER_DEFAULT_CHANNEL_ADMIN_GROUP]; - auto channel_admin_group = this->server->groups->findGroup(adminGroup); - if (!channel_admin_group) { - logError(this->getServerId(), "Missing server's default channel admin group! Using default channel group!"); - channel_admin_group = this->server->groups->defaultGroup(GroupTarget::GROUPTARGET_CHANNEL); - } - this->server->groups->setChannelGroup(this->getClientDatabaseId(), channel_admin_group, created_channel); + GroupId adminGroup = this->server->properties()[property::VIRTUALSERVER_DEFAULT_CHANNEL_ADMIN_GROUP]; + auto channel_admin_group = this->server->groups->findGroup(adminGroup); + if (!channel_admin_group) { + logError(this->getServerId(), "Missing server's default channel admin group! Using default channel group!"); + channel_admin_group = this->server->groups->defaultGroup(GroupTarget::GROUPTARGET_CHANNEL); + } + this->server->groups->setChannelGroup(this->getClientDatabaseId(), channel_admin_group, created_channel); - if (created_channel->channelType() == ChannelType::temporary && (this->getType() == ClientType::CLIENT_TEAMSPEAK || this->getType() == ClientType::CLIENT_WEB)) - this->server->client_move( - this->ref(), - created_channel, - nullptr, - "channel created", - ViewReasonId::VREASON_USER_ACTION, - true, - tree_channel_lock + if (created_channel->channelType() == ChannelType::temporary && (this->getType() == ClientType::CLIENT_TEAMSPEAK || this->getType() == ClientType::CLIENT_WEB)) + this->server->client_move( + this->ref(), + created_channel, + nullptr, + "channel created", + ViewReasonId::VREASON_USER_ACTION, + true, + tree_channel_lock ); } @@ -1706,63 +1706,63 @@ CommandResult ConnectedClient::handleCommandChannelDelete(Command &cmd) { CMD_RESET_IDLE; CMD_CHK_AND_INC_FLOOD_POINTS(25); - RESOLVE_CHANNEL_W(cmd["cid"], true); - auto channel = dynamic_pointer_cast(l_channel->entry); - assert(channel); - if(channel->deleted) /* channel gets already removed */ - return CommandResult::Success; + RESOLVE_CHANNEL_W(cmd["cid"], true); + auto channel = dynamic_pointer_cast(l_channel->entry); + assert(channel); + if(channel->deleted) /* channel gets already removed */ + return CommandResult::Success; - CHANNEL_PERMISSION_TEST(permission::i_channel_delete_power, permission::i_channel_needed_delete_power, channel, true); - for(const auto& ch : channel_tree->channels(channel)) { - if(ch->defaultChannel()) - return {findError("channel_can_not_delete_default"), "The channel tree you're going to delete contains the default channel!"}; - } + CHANNEL_PERMISSION_TEST(permission::i_channel_delete_power, permission::i_channel_needed_delete_power, channel, true); + for(const auto& ch : channel_tree->channels(channel)) { + if(ch->defaultChannel()) + return {findError("channel_can_not_delete_default"), "The channel tree you're going to delete contains the default channel!"}; + } - if(this->server) { - auto clients = this->server->getClientsByChannelRoot(channel, false); - if(!clients.empty()) - PERM_CHECK_CHANNELR(permission::b_channel_delete_flag_force, 1, channel, true); + if(this->server) { + auto clients = this->server->getClientsByChannelRoot(channel, false); + if(!clients.empty()) + PERM_CHECK_CHANNELR(permission::b_channel_delete_flag_force, 1, channel, true); - this->server->delete_channel(channel, this->ref(), "channel deleted", channel_tree_write_lock); - } else { - auto channel_ids = channel_tree->deleteChannelRoot(channel); - this->notifyChannelDeleted(channel_ids, this->ref()); - } + this->server->delete_channel(channel, this->ref(), "channel deleted", channel_tree_write_lock); + } else { + auto channel_ids = channel_tree->deleteChannelRoot(channel); + this->notifyChannelDeleted(channel_ids, this->ref()); + } return CommandResult::Success; } inline ssize_t count_characters(const std::string& in) { - size_t index = 0; - size_t count = 0; - while(index < in.length()){ - count++; + size_t index = 0; + size_t count = 0; + while(index < in.length()){ + count++; - auto current = (uint8_t) in[index]; - if(current >= 128) { //UTF8 check - if(current >= 192 && (current <= 193 || current >= 245)) { - return -1; - } else if(current >= 194 && current <= 223) { - if(in.length() - index <= 1) return -1; - else if((uint8_t) in[index + 1] >= 128 && (uint8_t) in[index + 1] <= 191) index += 1; //Valid - else return -1; - } else if(current >= 224 && current <= 239) { - if(in.length() - index <= 2) return -1; - else if((uint8_t) in[index + 1] >= 128 && (uint8_t) in[index + 1] <= 191 && - (uint8_t) in[index + 2] >= 128 && (uint8_t) in[index + 2] <= 191) index += 2; //Valid - else return -1; - } else if(current >= 240 && current <= 244) { - if(in.length() - index <= 3) return -1; - else if((uint8_t) in[index + 1] >= 128 && (uint8_t) in[index + 1] <= 191 && - (uint8_t) in[index + 2] >= 128 && (uint8_t) in[index + 2] <= 191 && - (uint8_t) in[index + 3] >= 128 && (uint8_t) in[index + 3] <= 191) index += 3; //Valid - else return -1; - } else { - return -1; - } - } - index++; - } - return count; + auto current = (uint8_t) in[index]; + if(current >= 128) { //UTF8 check + if(current >= 192 && (current <= 193 || current >= 245)) { + return -1; + } else if(current >= 194 && current <= 223) { + if(in.length() - index <= 1) return -1; + else if((uint8_t) in[index + 1] >= 128 && (uint8_t) in[index + 1] <= 191) index += 1; //Valid + else return -1; + } else if(current >= 224 && current <= 239) { + if(in.length() - index <= 2) return -1; + else if((uint8_t) in[index + 1] >= 128 && (uint8_t) in[index + 1] <= 191 && + (uint8_t) in[index + 2] >= 128 && (uint8_t) in[index + 2] <= 191) index += 2; //Valid + else return -1; + } else if(current >= 240 && current <= 244) { + if(in.length() - index <= 3) return -1; + else if((uint8_t) in[index + 1] >= 128 && (uint8_t) in[index + 1] <= 191 && + (uint8_t) in[index + 2] >= 128 && (uint8_t) in[index + 2] <= 191 && + (uint8_t) in[index + 3] >= 128 && (uint8_t) in[index + 3] <= 191) index += 3; //Valid + else return -1; + } else { + return -1; + } + } + index++; + } + return count; } /* @@ -1775,572 +1775,572 @@ CommandResult ConnectedClient::handleCommandChannelEdit(Command &cmd) { CMD_RESET_IDLE; CMD_CHK_AND_INC_FLOOD_POINTS(25); - RESOLVE_CHANNEL_R(cmd["cid"], true); - auto channel = dynamic_pointer_cast(l_channel->entry); - assert(channel); - if(channel->deleted) { - /* channel gets already removed */ - return CommandResult::Success; - } + RESOLVE_CHANNEL_R(cmd["cid"], true); + auto channel = dynamic_pointer_cast(l_channel->entry); + assert(channel); + if(channel->deleted) { + /* channel gets already removed */ + return CommandResult::Success; + } - std::deque> keys; - bool require_write_lock = false; - bool update_max_clients = false; - bool update_max_family_clients = false; - bool update_clients_in_channel = false; - bool update_password = false; - bool update_name = false; - /* Step 1 */ + std::deque> keys; + bool require_write_lock = false; + bool update_max_clients = false; + bool update_max_family_clients = false; + bool update_clients_in_channel = false; + bool update_password = false; + bool update_name = false; + /* Step 1 */ - bool target_channel_type_changed = false; - ChannelType::ChannelType target_channel_type = channel->channelType(); - CHANNEL_PERM_TEST_INIT; - CHANNEL_PERMISSION_TEST(permission::i_channel_modify_power, permission::i_channel_needed_modify_power, channel, true); + bool target_channel_type_changed = false; + ChannelType::ChannelType target_channel_type = channel->channelType(); + CHANNEL_PERM_TEST_INIT; + CHANNEL_PERMISSION_TEST(permission::i_channel_modify_power, permission::i_channel_needed_modify_power, channel, true); - for (const auto &key : cmd[0].keys()) { - if(key == "cid") - continue; - if(key == "return_code") - continue; + for (const auto &key : cmd[0].keys()) { + if(key == "cid") + continue; + if(key == "return_code") + continue; - const auto &property = property::info(key); - if(*property == property::CHANNEL_UNDEFINED) { - logError(this->getServerId(), R"({} Tried to edit a not existing channel property "{}" to "{}")", CLIENT_STR_LOG_PREFIX, key, cmd[key].string()); - continue; - } + const auto &property = property::info(key); + if(*property == property::CHANNEL_UNDEFINED) { + logError(this->getServerId(), R"({} Tried to edit a not existing channel property "{}" to "{}")", CLIENT_STR_LOG_PREFIX, key, cmd[key].string()); + continue; + } - if((property->flags & property::FLAG_USER_EDITABLE) == 0) { - logError(this->getServerId(), "{} Tried to change a channel property which is not changeable. (Key: {}, Value: \"{}\")", CLIENT_STR_LOG_PREFIX, key, cmd[key].string()); - continue; - } + if((property->flags & property::FLAG_USER_EDITABLE) == 0) { + logError(this->getServerId(), "{} Tried to change a channel property which is not changeable. (Key: {}, Value: \"{}\")", CLIENT_STR_LOG_PREFIX, key, cmd[key].string()); + continue; + } - if(!property->validate_input(cmd[key].as())) { - logError(this->getServerId(), "{} Tried to change a channel property to an invalid value. (Key: {}, Value: \"{}\")", CLIENT_STR_LOG_PREFIX, key, cmd[key].string()); - continue; - } + if(!property->validate_input(cmd[key].as())) { + logError(this->getServerId(), "{} Tried to change a channel property to an invalid value. (Key: {}, Value: \"{}\")", CLIENT_STR_LOG_PREFIX, key, cmd[key].string()); + continue; + } - if(channel->properties()[*property].as() == cmd[key].as()) - continue; /* we dont need to update stuff which is the same */ + if(channel->properties()[*property].as() == cmd[key].as()) + continue; /* we dont need to update stuff which is the same */ - if(key == "channel_icon_id") { - CHANNEL_PERMISSION_TEST(permission::i_channel_permission_modify_power, permission::i_channel_needed_permission_modify_power, channel, true); - } else if (key == "channel_order") { - CHANNEL_PERM_TEST(permission::b_channel_modify_sortorder, 1, true); - require_write_lock = true; - } else if (key == "channel_flag_default") { - if(!cmd["channel_flag_default"].as()) - return {findError("channel_invalid_flags")}; + if(key == "channel_icon_id") { + CHANNEL_PERMISSION_TEST(permission::i_channel_permission_modify_power, permission::i_channel_needed_permission_modify_power, channel, true); + } else if (key == "channel_order") { + CHANNEL_PERM_TEST(permission::b_channel_modify_sortorder, 1, true); + require_write_lock = true; + } else if (key == "channel_flag_default") { + if(!cmd["channel_flag_default"].as()) + return {findError("channel_invalid_flags")}; - CHANNEL_PERM_TEST(permission::b_channel_modify_make_default, 1, true); - require_write_lock = true; - } else if (key == "channel_name") { - CHANNEL_PERM_TEST(permission::b_channel_modify_name, 1, true); - if (count_characters(cmd["channel_name"]) < 1) - return {findError("channel_name_inuse"), "Invalid channel name (too short)"}; - if (count_characters(cmd["channel_name"]) > 40) - return {findError("channel_name_inuse"), "Invalid channel name (too long)"}; - require_write_lock = true; - update_name = true; - } else if (key == "channel_name_phonetic") { - CHANNEL_PERM_TEST(permission::b_channel_modify_name, 1, true); - } else if (key == "channel_topic") { - CHANNEL_PERM_TEST(permission::b_channel_modify_topic, 1, true); - } else if (key == "channel_description") { - CHANNEL_PERM_TEST(permission::b_channel_modify_description, 1, true); - if(!this->permissionGranted(permission::PERMTEST_ORDERED, permission::b_client_use_bbcode_any, 1, this->currentChannel)) { - auto bbcode_image = bbcode::sloppy::has_image(cmd[key]); - auto bbcode_url = bbcode::sloppy::has_url(cmd[key]); - debugMessage(this->getServerId(), "Channel description contains bb codes: Image: {} URL: {}", bbcode_image, bbcode_url); - if(bbcode_image && !this->permissionGranted(permission::PERMTEST_ORDERED, permission::b_client_use_bbcode_image, 1, this->currentChannel)) - return CommandResultPermissionError{permission::b_client_use_bbcode_image}; - if(bbcode_url && !this->permissionGranted(permission::PERMTEST_ORDERED, permission::b_client_use_bbcode_url, 1, this->currentChannel)) - return CommandResultPermissionError{permission::b_client_use_bbcode_url}; - } - } else if (key == "channel_codec") { - CHANNEL_PERM_TEST(permission::b_channel_modify_codec, 1, true); - } else if (key == "channel_codec_quality") { - CHANNEL_PERM_TEST(permission::b_channel_modify_codec_quality, 1, true); - } else if (key == "channel_codec_is_unencrypted") { - if (cmd["channel_codec_is_unencrypted"].as()) - CHANNEL_PERM_TEST(permission::b_channel_modify_make_codec_encrypted, 1, true); - } else if (key == "channel_needed_talk_power") { - CHANNEL_PERM_TEST(permission::b_channel_modify_needed_talk_power, 1, true); - update_clients_in_channel = true; - } else if (key == "channel_maxclients" || key == "channel_flag_maxclients_unlimited") { - CHANNEL_PERM_TEST(permission::b_channel_modify_maxclients, 1, true); - require_write_lock = true; - update_max_clients = true; - } else if(key == "channel_maxfamilyclients" || key == "channel_flag_maxfamilyclients_unlimited" || key == "channel_flag_maxfamilyclients_inherited") { - CHANNEL_PERM_TEST(permission::b_channel_modify_maxfamilyclients, 1, true); - require_write_lock = true; - update_max_family_clients = true; - } else if (key == "channel_flag_permanent" || key == "channel_flag_semi_permanent") { - if (cmd[0].has("channel_flag_permanent") && cmd["channel_flag_permanent"].as()) { - CHANNEL_PERM_TEST(permission::b_channel_modify_make_permanent, 1, true); - target_channel_type = ChannelType::permanent; - } else if (cmd[0].has("channel_flag_semi_permanent") && cmd["channel_flag_semi_permanent"].as()) { - CHANNEL_PERM_TEST(permission::b_channel_modify_make_semi_permanent, 1, true); - target_channel_type = ChannelType::semipermanent; - } else { - CHANNEL_PERM_TEST(permission::b_channel_modify_make_temporary, 1, true); - target_channel_type = ChannelType::temporary; - } - target_channel_type_changed = true; - require_write_lock = true; - } else if (key == "channel_delete_delay") { - CHANNEL_PERM_TEST(permission::b_channel_modify_temp_delete_delay, cmd["channel_delete_delay"].as(), true); - } else if (key == "channel_password" || key == "channel_flag_password") { - CHANNEL_PERM_TEST(permission::b_channel_modify_password, 1, true); - update_password = true; - } else if (key == "channel_conversation_history_length") { - auto value = cmd["channel_conversation_history_length"].as(); - if(value == 0) { - CHANNEL_PERM_TEST(permission::b_channel_create_modify_conversation_history_unlimited, 1, true); - } else { - CHANNEL_PERM_TEST(permission::i_channel_create_modify_conversation_history_length, 1, true); - } - } else if (key == "channel_flag_conversation_private") { - CHANNEL_PERM_TEST(permission::b_channel_create_modify_conversation_private, 1, true); - } else { - logCritical( - this->getServerId(), - "The client " + this->getDisplayName() + " tried to change a editable channel property but we haven't found a permission. Please report this error. (Channel property: {})", - key - ); - continue; - } - keys.push_back(property); - } + CHANNEL_PERM_TEST(permission::b_channel_modify_make_default, 1, true); + require_write_lock = true; + } else if (key == "channel_name") { + CHANNEL_PERM_TEST(permission::b_channel_modify_name, 1, true); + if (count_characters(cmd["channel_name"]) < 1) + return {findError("channel_name_inuse"), "Invalid channel name (too short)"}; + if (count_characters(cmd["channel_name"]) > 40) + return {findError("channel_name_inuse"), "Invalid channel name (too long)"}; + require_write_lock = true; + update_name = true; + } else if (key == "channel_name_phonetic") { + CHANNEL_PERM_TEST(permission::b_channel_modify_name, 1, true); + } else if (key == "channel_topic") { + CHANNEL_PERM_TEST(permission::b_channel_modify_topic, 1, true); + } else if (key == "channel_description") { + CHANNEL_PERM_TEST(permission::b_channel_modify_description, 1, true); + if(!this->permissionGranted(permission::PERMTEST_ORDERED, permission::b_client_use_bbcode_any, 1, this->currentChannel)) { + auto bbcode_image = bbcode::sloppy::has_image(cmd[key]); + auto bbcode_url = bbcode::sloppy::has_url(cmd[key]); + debugMessage(this->getServerId(), "Channel description contains bb codes: Image: {} URL: {}", bbcode_image, bbcode_url); + if(bbcode_image && !this->permissionGranted(permission::PERMTEST_ORDERED, permission::b_client_use_bbcode_image, 1, this->currentChannel)) + return CommandResultPermissionError{permission::b_client_use_bbcode_image}; + if(bbcode_url && !this->permissionGranted(permission::PERMTEST_ORDERED, permission::b_client_use_bbcode_url, 1, this->currentChannel)) + return CommandResultPermissionError{permission::b_client_use_bbcode_url}; + } + } else if (key == "channel_codec") { + CHANNEL_PERM_TEST(permission::b_channel_modify_codec, 1, true); + } else if (key == "channel_codec_quality") { + CHANNEL_PERM_TEST(permission::b_channel_modify_codec_quality, 1, true); + } else if (key == "channel_codec_is_unencrypted") { + if (cmd["channel_codec_is_unencrypted"].as()) + CHANNEL_PERM_TEST(permission::b_channel_modify_make_codec_encrypted, 1, true); + } else if (key == "channel_needed_talk_power") { + CHANNEL_PERM_TEST(permission::b_channel_modify_needed_talk_power, 1, true); + update_clients_in_channel = true; + } else if (key == "channel_maxclients" || key == "channel_flag_maxclients_unlimited") { + CHANNEL_PERM_TEST(permission::b_channel_modify_maxclients, 1, true); + require_write_lock = true; + update_max_clients = true; + } else if(key == "channel_maxfamilyclients" || key == "channel_flag_maxfamilyclients_unlimited" || key == "channel_flag_maxfamilyclients_inherited") { + CHANNEL_PERM_TEST(permission::b_channel_modify_maxfamilyclients, 1, true); + require_write_lock = true; + update_max_family_clients = true; + } else if (key == "channel_flag_permanent" || key == "channel_flag_semi_permanent") { + if (cmd[0].has("channel_flag_permanent") && cmd["channel_flag_permanent"].as()) { + CHANNEL_PERM_TEST(permission::b_channel_modify_make_permanent, 1, true); + target_channel_type = ChannelType::permanent; + } else if (cmd[0].has("channel_flag_semi_permanent") && cmd["channel_flag_semi_permanent"].as()) { + CHANNEL_PERM_TEST(permission::b_channel_modify_make_semi_permanent, 1, true); + target_channel_type = ChannelType::semipermanent; + } else { + CHANNEL_PERM_TEST(permission::b_channel_modify_make_temporary, 1, true); + target_channel_type = ChannelType::temporary; + } + target_channel_type_changed = true; + require_write_lock = true; + } else if (key == "channel_delete_delay") { + CHANNEL_PERM_TEST(permission::b_channel_modify_temp_delete_delay, cmd["channel_delete_delay"].as(), true); + } else if (key == "channel_password" || key == "channel_flag_password") { + CHANNEL_PERM_TEST(permission::b_channel_modify_password, 1, true); + update_password = true; + } else if (key == "channel_conversation_history_length") { + auto value = cmd["channel_conversation_history_length"].as(); + if(value == 0) { + CHANNEL_PERM_TEST(permission::b_channel_create_modify_conversation_history_unlimited, 1, true); + } else { + CHANNEL_PERM_TEST(permission::i_channel_create_modify_conversation_history_length, 1, true); + } + } else if (key == "channel_flag_conversation_private") { + CHANNEL_PERM_TEST(permission::b_channel_create_modify_conversation_private, 1, true); + } else { + logCritical( + this->getServerId(), + "The client " + this->getDisplayName() + " tried to change a editable channel property but we haven't found a permission. Please report this error. (Channel property: {})", + key + ); + continue; + } + keys.push_back(property); + } - unique_lock server_channel_w_lock(this->server ? this->server->channel_tree_lock : serverInstance->getChannelTreeLock(), defer_lock); - if(require_write_lock) { - channel_tree_read_lock.unlock(); - server_channel_w_lock.lock(); + unique_lock server_channel_w_lock(this->server ? this->server->channel_tree_lock : serverInstance->getChannelTreeLock(), defer_lock); + if(require_write_lock) { + channel_tree_read_lock.unlock(); + server_channel_w_lock.lock(); - /* not that while we're waiting to edit the server the channel got deleted... fuck my english */ - if(channel->deleted) - return CommandResult::Success; - } + /* not that while we're waiting to edit the server the channel got deleted... fuck my english */ + if(channel->deleted) + return CommandResult::Success; + } - /* test the password parameters */ - if(update_password) { - if(!cmd[0].has("channel_password")) { - if(cmd[0].has("channel_flag_password") && cmd["channel_flag_password"].as()) - return {findError("parameter_missing"), ""}; - else - cmd["channel_password"] = ""; /* no password set */ - keys.push_back(property::info(property::CHANNEL_PASSWORD)); - } - if(!cmd[0].has("channel_flag_password")) { - cmd["channel_flag_password"] = !cmd["channel_password"].string().empty(); - keys.push_back(property::info(property::CHANNEL_FLAG_PASSWORD)); - } + /* test the password parameters */ + if(update_password) { + if(!cmd[0].has("channel_password")) { + if(cmd[0].has("channel_flag_password") && cmd["channel_flag_password"].as()) + return {findError("parameter_missing"), ""}; + else + cmd["channel_password"] = ""; /* no password set */ + keys.push_back(property::info(property::CHANNEL_PASSWORD)); + } + if(!cmd[0].has("channel_flag_password")) { + cmd["channel_flag_password"] = !cmd["channel_password"].string().empty(); + keys.push_back(property::info(property::CHANNEL_FLAG_PASSWORD)); + } - if(cmd["channel_flag_password"].as()) { - if(cmd["channel_password"].string().empty()) - return {findError("channel_invalid_flags")}; /* we cant enable a password without a given password */ + if(cmd["channel_flag_password"].as()) { + if(cmd["channel_password"].string().empty()) + return {findError("channel_invalid_flags")}; /* we cant enable a password without a given password */ - /* we've to "encode" the password */ - if(this->getType() == ClientType ::CLIENT_QUERY) - cmd["channel_password"] = base64::encode(digest::sha1(cmd["channel_password"].string())); - } else { - cmd["channel_password"] = ""; /* flag password if false so we set the password to empty */ - } - } + /* we've to "encode" the password */ + if(this->getType() == ClientType ::CLIENT_QUERY) + cmd["channel_password"] = base64::encode(digest::sha1(cmd["channel_password"].string())); + } else { + cmd["channel_password"] = ""; /* flag password if false so we set the password to empty */ + } + } - /* test the default channel update */ - if(cmd[0].has("channel_flag_default") || channel->defaultChannel()) { - if(target_channel_type != ChannelType::permanent) - return {findError("channel_default_require_permanent")}; /* default channel is not allowed to be non permanent */ + /* test the default channel update */ + if(cmd[0].has("channel_flag_default") || channel->defaultChannel()) { + if(target_channel_type != ChannelType::permanent) + return {findError("channel_default_require_permanent")}; /* default channel is not allowed to be non permanent */ - if((cmd[0].has("channel_flag_password") && cmd["channel_flag_password"].as()) || channel->properties()[property::CHANNEL_FLAG_PASSWORD]) { - cmd["channel_flag_password"] = false; - cmd["channel_password"] = ""; - keys.push_back(property::info(property::CHANNEL_FLAG_PASSWORD)); - } + if((cmd[0].has("channel_flag_password") && cmd["channel_flag_password"].as()) || channel->properties()[property::CHANNEL_FLAG_PASSWORD]) { + cmd["channel_flag_password"] = false; + cmd["channel_password"] = ""; + keys.push_back(property::info(property::CHANNEL_FLAG_PASSWORD)); + } - if(cmd[0].has("channel_flag_default")) { - cmd["channel_maxclients"] = -1; - cmd["channel_flag_maxclients_unlimited"] = true; - keys.push_back(property::info(property::CHANNEL_MAXCLIENTS)); - keys.push_back(property::info(property::CHANNEL_FLAG_MAXCLIENTS_UNLIMITED)); - update_max_clients = true; + if(cmd[0].has("channel_flag_default")) { + cmd["channel_maxclients"] = -1; + cmd["channel_flag_maxclients_unlimited"] = true; + keys.push_back(property::info(property::CHANNEL_MAXCLIENTS)); + keys.push_back(property::info(property::CHANNEL_FLAG_MAXCLIENTS_UNLIMITED)); + update_max_clients = true; - cmd["channel_maxfamilyclients"] = -1; - cmd["channel_flag_maxfamilyclients_inherited"] = true; - keys.push_back(property::info(property::CHANNEL_MAXFAMILYCLIENTS)); - keys.push_back(property::info(property::CHANNEL_FLAG_MAXFAMILYCLIENTS_INHERITED)); - update_max_family_clients = true; - } - } + cmd["channel_maxfamilyclients"] = -1; + cmd["channel_flag_maxfamilyclients_inherited"] = true; + keys.push_back(property::info(property::CHANNEL_MAXFAMILYCLIENTS)); + keys.push_back(property::info(property::CHANNEL_FLAG_MAXFAMILYCLIENTS_INHERITED)); + update_max_family_clients = true; + } + } - /* "fix" max client for temporary channels */ - if(target_channel_type_changed) { - if(target_channel_type == ChannelType::temporary) { - if(channel->properties()[property::CHANNEL_MAXCLIENTS].as() != -1) { - cmd["channel_maxclients"] = -1; - cmd["channel_flag_maxclients_unlimited"] = true; - keys.push_back(property::info(property::CHANNEL_MAXCLIENTS)); - keys.push_back(property::info(property::CHANNEL_FLAG_MAXCLIENTS_UNLIMITED)); - update_max_clients = true; - } - if(channel->properties()[property::CHANNEL_MAXFAMILYCLIENTS].as() != -1) { - cmd["channel_maxfamilyclients"] = -1; - cmd["channel_flag_maxfamilyclients_inherited"] = true; - keys.push_back(property::info(property::CHANNEL_MAXFAMILYCLIENTS)); - keys.push_back(property::info(property::CHANNEL_FLAG_MAXFAMILYCLIENTS_INHERITED)); - update_max_family_clients = true; - } - } + /* "fix" max client for temporary channels */ + if(target_channel_type_changed) { + if(target_channel_type == ChannelType::temporary) { + if(channel->properties()[property::CHANNEL_MAXCLIENTS].as() != -1) { + cmd["channel_maxclients"] = -1; + cmd["channel_flag_maxclients_unlimited"] = true; + keys.push_back(property::info(property::CHANNEL_MAXCLIENTS)); + keys.push_back(property::info(property::CHANNEL_FLAG_MAXCLIENTS_UNLIMITED)); + update_max_clients = true; + } + if(channel->properties()[property::CHANNEL_MAXFAMILYCLIENTS].as() != -1) { + cmd["channel_maxfamilyclients"] = -1; + cmd["channel_flag_maxfamilyclients_inherited"] = true; + keys.push_back(property::info(property::CHANNEL_MAXFAMILYCLIENTS)); + keys.push_back(property::info(property::CHANNEL_FLAG_MAXFAMILYCLIENTS_INHERITED)); + update_max_family_clients = true; + } + } - if(target_channel_type != ChannelType::permanent) { - /* test if any child is the default channel */ - for(const auto& child : channel_tree->channels(channel)) - if(child->defaultChannel()) - return {findError("channel_default_require_permanent")}; /* default channel is not allowed to be non permanent */ - } - auto parent = channel->parent(); - if(parent && parent->channelType() > target_channel_type) - return {findError("channel_parent_not_permanent")}; - } + if(target_channel_type != ChannelType::permanent) { + /* test if any child is the default channel */ + for(const auto& child : channel_tree->channels(channel)) + if(child->defaultChannel()) + return {findError("channel_default_require_permanent")}; /* default channel is not allowed to be non permanent */ + } + auto parent = channel->parent(); + if(parent && parent->channelType() > target_channel_type) + return {findError("channel_parent_not_permanent")}; + } - /* test the max clients parameters */ - if(update_max_clients) { - if(!cmd[0].has("channel_maxclients")) { - if(cmd[0].has("channel_flag_maxclients_unlimited") && cmd["channel_flag_maxclients_unlimited"].as()) - cmd["channel_maxclients"] = -1; - else - return {findError("parameter_missing"), "channel_maxclients"}; /* max clients must be specified */ - keys.push_back(property::info(property::CHANNEL_MAXCLIENTS)); - } + /* test the max clients parameters */ + if(update_max_clients) { + if(!cmd[0].has("channel_maxclients")) { + if(cmd[0].has("channel_flag_maxclients_unlimited") && cmd["channel_flag_maxclients_unlimited"].as()) + cmd["channel_maxclients"] = -1; + else + return {findError("parameter_missing"), "channel_maxclients"}; /* max clients must be specified */ + keys.push_back(property::info(property::CHANNEL_MAXCLIENTS)); + } - if(!cmd[0].has("channel_flag_maxclients_unlimited")) { - cmd["channel_flag_maxclients_unlimited"] = cmd["channel_maxclients"].as() < 0; - keys.push_back(property::info(property::CHANNEL_FLAG_MAXCLIENTS_UNLIMITED)); - } + if(!cmd[0].has("channel_flag_maxclients_unlimited")) { + cmd["channel_flag_maxclients_unlimited"] = cmd["channel_maxclients"].as() < 0; + keys.push_back(property::info(property::CHANNEL_FLAG_MAXCLIENTS_UNLIMITED)); + } - if(cmd["channel_flag_maxclients_unlimited"].as() && cmd["channel_maxclients"].as() != -1) - return {findError("channel_invalid_flags")}; /* channel cant have a max client settings AND be unlimited as well */ + if(cmd["channel_flag_maxclients_unlimited"].as() && cmd["channel_maxclients"].as() != -1) + return {findError("channel_invalid_flags")}; /* channel cant have a max client settings AND be unlimited as well */ - if(!cmd["channel_flag_maxclients_unlimited"].as() && target_channel_type == ChannelType::temporary) - return {findError("channel_invalid_flags")}; /* temporary channels cant have a limited user count */ - } + if(!cmd["channel_flag_maxclients_unlimited"].as() && target_channel_type == ChannelType::temporary) + return {findError("channel_invalid_flags")}; /* temporary channels cant have a limited user count */ + } - /* test the max family clients parameters */ - if(update_max_family_clients) { - if(!cmd[0].has("channel_maxfamilyclients")) { - if(cmd[0].has("channel_flag_maxfamilyclients_unlimited")) { - if(cmd["channel_flag_maxfamilyclients_unlimited"].as()) - cmd["channel_flag_maxfamilyclients_inherited"] = false; - else - cmd["channel_flag_maxfamilyclients_inherited"] = true; - keys.push_back(property::info(property::CHANNEL_FLAG_MAXFAMILYCLIENTS_INHERITED)); - } else if(cmd[0].has("channel_flag_maxfamilyclients_inherited")) { - if(cmd["channel_flag_maxfamilyclients_inherited"].as()) - cmd["channel_flag_maxfamilyclients_unlimited"] = false; - else - cmd["channel_flag_maxfamilyclients_unlimited"] = true; - keys.push_back(property::info(property::CHANNEL_FLAG_MAXFAMILYCLIENTS_UNLIMITED)); - } else /* not really possible */ - return {findError("parameter_missing"), "channel_maxfamilyclients"}; /* family max clients must be */ - cmd["channel_maxfamilyclients"] = -1; - keys.push_back(property::info(property::CHANNEL_MAXFAMILYCLIENTS)); - } - //keep this order because this command: "channeledit cid= channel_maxfamilyclients=-1" should set max family clients mode to inherited - if(!cmd[0].has("channel_flag_maxfamilyclients_inherited")) { - auto flag_unlimited = cmd[0].has("channel_flag_maxfamilyclients_unlimited") && cmd["channel_flag_maxfamilyclients_unlimited"].as(); - if(flag_unlimited) - cmd["channel_flag_maxfamilyclients_inherited"] = false; - else - cmd["channel_flag_maxfamilyclients_inherited"] = cmd["channel_maxfamilyclients"].as() < 0; - } - if(!cmd[0].has("channel_flag_maxfamilyclients_unlimited")) { - auto flag_inherited = cmd[0].has("channel_flag_maxfamilyclients_inherited") && cmd["channel_flag_maxfamilyclients_inherited"].as(); - if(flag_inherited) - cmd["channel_flag_maxfamilyclients_unlimited"] = false; - else - cmd["channel_flag_maxfamilyclients_unlimited"] = cmd["channel_maxfamilyclients"].as() < 0; - } + /* test the max family clients parameters */ + if(update_max_family_clients) { + if(!cmd[0].has("channel_maxfamilyclients")) { + if(cmd[0].has("channel_flag_maxfamilyclients_unlimited")) { + if(cmd["channel_flag_maxfamilyclients_unlimited"].as()) + cmd["channel_flag_maxfamilyclients_inherited"] = false; + else + cmd["channel_flag_maxfamilyclients_inherited"] = true; + keys.push_back(property::info(property::CHANNEL_FLAG_MAXFAMILYCLIENTS_INHERITED)); + } else if(cmd[0].has("channel_flag_maxfamilyclients_inherited")) { + if(cmd["channel_flag_maxfamilyclients_inherited"].as()) + cmd["channel_flag_maxfamilyclients_unlimited"] = false; + else + cmd["channel_flag_maxfamilyclients_unlimited"] = true; + keys.push_back(property::info(property::CHANNEL_FLAG_MAXFAMILYCLIENTS_UNLIMITED)); + } else /* not really possible */ + return {findError("parameter_missing"), "channel_maxfamilyclients"}; /* family max clients must be */ + cmd["channel_maxfamilyclients"] = -1; + keys.push_back(property::info(property::CHANNEL_MAXFAMILYCLIENTS)); + } + //keep this order because this command: "channeledit cid= channel_maxfamilyclients=-1" should set max family clients mode to inherited + if(!cmd[0].has("channel_flag_maxfamilyclients_inherited")) { + auto flag_unlimited = cmd[0].has("channel_flag_maxfamilyclients_unlimited") && cmd["channel_flag_maxfamilyclients_unlimited"].as(); + if(flag_unlimited) + cmd["channel_flag_maxfamilyclients_inherited"] = false; + else + cmd["channel_flag_maxfamilyclients_inherited"] = cmd["channel_maxfamilyclients"].as() < 0; + } + if(!cmd[0].has("channel_flag_maxfamilyclients_unlimited")) { + auto flag_inherited = cmd[0].has("channel_flag_maxfamilyclients_inherited") && cmd["channel_flag_maxfamilyclients_inherited"].as(); + if(flag_inherited) + cmd["channel_flag_maxfamilyclients_unlimited"] = false; + else + cmd["channel_flag_maxfamilyclients_unlimited"] = cmd["channel_maxfamilyclients"].as() < 0; + } - if(cmd["channel_flag_maxfamilyclients_inherited"].as() && cmd["channel_flag_maxfamilyclients_unlimited"].as()) - return {findError("channel_invalid_flags")}; /* both at the same time are not possible */ + if(cmd["channel_flag_maxfamilyclients_inherited"].as() && cmd["channel_flag_maxfamilyclients_unlimited"].as()) + return {findError("channel_invalid_flags")}; /* both at the same time are not possible */ - if(cmd["channel_flag_maxfamilyclients_inherited"].as() && cmd["channel_maxfamilyclients"].as() != -1) - return {findError("channel_invalid_flags")}; /* flag inherited required max users to be -1 */ + if(cmd["channel_flag_maxfamilyclients_inherited"].as() && cmd["channel_maxfamilyclients"].as() != -1) + return {findError("channel_invalid_flags")}; /* flag inherited required max users to be -1 */ - if(cmd["channel_flag_maxfamilyclients_unlimited"].as() && cmd["channel_maxfamilyclients"].as() != -1) - return {findError("channel_invalid_flags")}; /* flag unlimited required max users to be -1 */ + if(cmd["channel_flag_maxfamilyclients_unlimited"].as() && cmd["channel_maxfamilyclients"].as() != -1) + return {findError("channel_invalid_flags")}; /* flag unlimited required max users to be -1 */ - if(cmd["channel_maxfamilyclients"].as() != -1 && target_channel_type == ChannelType::temporary) - return {findError("channel_invalid_flags")}; /* temporary channels cant have a limited user count */ - } + if(cmd["channel_maxfamilyclients"].as() != -1 && target_channel_type == ChannelType::temporary) + return {findError("channel_invalid_flags")}; /* temporary channels cant have a limited user count */ + } - /* test the channel name */ - if(update_name) { - auto named_channel = channel_tree->findChannel(cmd["channel_name"].string(), channel->parent()); - if (named_channel) - return {findError("channel_name_inuse"), to_string(named_channel->channelId())}; - } + /* test the channel name */ + if(update_name) { + auto named_channel = channel_tree->findChannel(cmd["channel_name"].string(), channel->parent()); + if (named_channel) + return {findError("channel_name_inuse"), to_string(named_channel->channelId())}; + } - auto self_ref = this->ref(); - shared_ptr old_default_channel; - deque> child_channel_updated; - for(const std::shared_ptr& key : keys) { - if(*key == property::CHANNEL_ORDER) { - /* TODO: May move that up because if it fails may some other props have already be applied */ - if (!channel_tree->change_order(channel, cmd[key->name])) - return {findError("channel_invalid_order"), "Can't change order id"}; + auto self_ref = this->ref(); + shared_ptr old_default_channel; + deque> child_channel_updated; + for(const std::shared_ptr& key : keys) { + if(*key == property::CHANNEL_ORDER) { + /* TODO: May move that up because if it fails may some other props have already be applied */ + if (!channel_tree->change_order(channel, cmd[key->name])) + return {findError("channel_invalid_order"), "Can't change order id"}; - if(this->server) { - auto parent = channel->hasParent() ? channel_tree->findLinkedChannel(channel->parent()->channelId()) : nullptr; - auto previous = channel_tree->findLinkedChannel(channel->previousChannelId()); + if(this->server) { + auto parent = channel->hasParent() ? channel_tree->findLinkedChannel(channel->parent()->channelId()) : nullptr; + auto previous = channel_tree->findLinkedChannel(channel->previousChannelId()); - this->server->forEachClient([&](const shared_ptr& cl) { - unique_lock client_channel_lock(cl->channel_lock); + this->server->forEachClient([&](const shared_ptr& cl) { + unique_lock client_channel_lock(cl->channel_lock); - auto actions = cl->channels->change_order(l_channel, parent, previous); - std::deque deletions; - for(const auto& action : actions) { - switch (action.first) { - case ClientChannelView::NOTHING: - continue; - case ClientChannelView::ENTER_VIEW: - cl->notifyChannelShow(action.second->channel(), action.second->previous_channel); - break; - case ClientChannelView::DELETE_VIEW: - deletions.push_back(action.second->channelId()); - break; - case ClientChannelView::MOVE: - cl->notifyChannelMoved(action.second->channel(), action.second->previous_channel, this->ref()); - break; - case ClientChannelView::REORDER: - cl->notifyChannelEdited(action.second->channel(), {property::CHANNEL_ORDER}, self_ref, false); - break; - } - } - if(!deletions.empty()) { - cl->notifyChannelHide(deletions, false); - return; //Channel got deleted so we dont have to send the updates - } - }); - } - } else if(*key == property::CHANNEL_FLAG_DEFAULT) { - old_default_channel = channel_tree->getDefaultChannel(); - if(old_default_channel == channel) { - old_default_channel = nullptr; - continue; - } + auto actions = cl->channels->change_order(l_channel, parent, previous); + std::deque deletions; + for(const auto& action : actions) { + switch (action.first) { + case ClientChannelView::NOTHING: + continue; + case ClientChannelView::ENTER_VIEW: + cl->notifyChannelShow(action.second->channel(), action.second->previous_channel); + break; + case ClientChannelView::DELETE_VIEW: + deletions.push_back(action.second->channelId()); + break; + case ClientChannelView::MOVE: + cl->notifyChannelMoved(action.second->channel(), action.second->previous_channel, this->ref()); + break; + case ClientChannelView::REORDER: + cl->notifyChannelEdited(action.second->channel(), {property::CHANNEL_ORDER}, self_ref, false); + break; + } + } + if(!deletions.empty()) { + cl->notifyChannelHide(deletions, false); + return; //Channel got deleted so we dont have to send the updates + } + }); + } + } else if(*key == property::CHANNEL_FLAG_DEFAULT) { + old_default_channel = channel_tree->getDefaultChannel(); + if(old_default_channel == channel) { + old_default_channel = nullptr; + continue; + } - if(!cmd[key->name].as()) { - old_default_channel = nullptr; - continue; - } + if(!cmd[key->name].as()) { + old_default_channel = nullptr; + continue; + } - channel_tree->setDefaultChannel(channel); + channel_tree->setDefaultChannel(channel); - deque> updated_channels; - shared_ptr current_channel = channel; - do { - if(current_channel->channelType() == ChannelType::permanent) - break; + deque> updated_channels; + shared_ptr current_channel = channel; + do { + if(current_channel->channelType() == ChannelType::permanent) + break; - current_channel->setChannelType(ChannelType::permanent); - if(current_channel != channel) - updated_channels.push_back(channel); - } while ((current_channel = current_channel->parent())); + current_channel->setChannelType(ChannelType::permanent); + if(current_channel != channel) + updated_channels.push_back(channel); + } while ((current_channel = current_channel->parent())); - if(this->server && !updated_channels.empty()) { - std::reverse(updated_channels.begin(), updated_channels.end()); - auto this_ref = this->ref(); - this->server->forEachClient([&](const shared_ptr& client) { - unique_lock client_channel_lock(client->channel_lock); - for(const auto& channel : updated_channels) { - client->notifyChannelEdited(channel, {property::CHANNEL_FLAG_PERMANENT, property::CHANNEL_FLAG_SEMI_PERMANENT}, this_ref, false); - } - }); - } - } else if(*key == property::CHANNEL_FLAG_PERMANENT || *key == property::CHANNEL_FLAG_SEMI_PERMANENT) { - if(target_channel_type_changed) { /* must be true else the key would not appere here */ - target_channel_type_changed = false; /* we only need to check all subchannels once! */ + if(this->server && !updated_channels.empty()) { + std::reverse(updated_channels.begin(), updated_channels.end()); + auto this_ref = this->ref(); + this->server->forEachClient([&](const shared_ptr& client) { + unique_lock client_channel_lock(client->channel_lock); + for(const auto& channel : updated_channels) { + client->notifyChannelEdited(channel, {property::CHANNEL_FLAG_PERMANENT, property::CHANNEL_FLAG_SEMI_PERMANENT}, this_ref, false); + } + }); + } + } else if(*key == property::CHANNEL_FLAG_PERMANENT || *key == property::CHANNEL_FLAG_SEMI_PERMANENT) { + if(target_channel_type_changed) { /* must be true else the key would not appere here */ + target_channel_type_changed = false; /* we only need to check all subchannels once! */ - { /* check channel children */ - deque> channel_to_test = {channel}; + { /* check channel children */ + deque> channel_to_test = {channel}; - while(!channel_to_test.empty()) { - auto current_channel = channel_to_test.front(); - channel_to_test.pop_front(); + while(!channel_to_test.empty()) { + auto current_channel = channel_to_test.front(); + channel_to_test.pop_front(); - for(const auto& child : channel_tree->channels(current_channel, 1)) { - if(child == current_channel) - continue; + for(const auto& child : channel_tree->channels(current_channel, 1)) { + if(child == current_channel) + continue; - if(child->channelType() < target_channel_type) { - child->setChannelType(target_channel_type); - channel_to_test.push_back(child); - child_channel_updated.push_back(child); - } - } - } - } - } - } else if(*key == property::CHANNEL_CONVERSATION_HISTORY_LENGTH) { - //channel_conversation_history_length - auto conversation_manager = this->server->conversation_manager(); - if(conversation_manager) { - auto conversation = conversation_manager->get(channel->channelId()); - if(conversation) - conversation->set_history_length(cmd[key->name]); - } - } + if(child->channelType() < target_channel_type) { + child->setChannelType(target_channel_type); + channel_to_test.push_back(child); + child_channel_updated.push_back(child); + } + } + } + } + } + } else if(*key == property::CHANNEL_CONVERSATION_HISTORY_LENGTH) { + //channel_conversation_history_length + auto conversation_manager = this->server->conversation_manager(); + if(conversation_manager) { + auto conversation = conversation_manager->get(channel->channelId()); + if(conversation) + conversation->set_history_length(cmd[key->name]); + } + } - channel->properties()[key] = cmd[key->name].string(); - } - if(this->server) { - vector key_vector; - key_vector.reserve(keys.size()); - for(const auto& key : keys) - key_vector.push_back((property::ChannelProperties) key->property_index); + channel->properties()[key] = cmd[key->name].string(); + } + if(this->server) { + vector key_vector; + key_vector.reserve(keys.size()); + for(const auto& key : keys) + key_vector.push_back((property::ChannelProperties) key->property_index); - auto self_rev = this->ref(); - this->server->forEachClient([&](const shared_ptr& client) { - unique_lock client_channel_lock(client->channel_lock); + auto self_rev = this->ref(); + this->server->forEachClient([&](const shared_ptr& client) { + unique_lock client_channel_lock(client->channel_lock); - for(const auto& channel : child_channel_updated) - client->notifyChannelEdited(channel, {property::CHANNEL_FLAG_PERMANENT, property::CHANNEL_FLAG_SEMI_PERMANENT}, self_rev, false); + for(const auto& channel : child_channel_updated) + client->notifyChannelEdited(channel, {property::CHANNEL_FLAG_PERMANENT, property::CHANNEL_FLAG_SEMI_PERMANENT}, self_rev, false); - client->notifyChannelEdited(channel, key_vector, self_rev, false); + client->notifyChannelEdited(channel, key_vector, self_rev, false); - if(old_default_channel) /* clients need to have one or more defualt channels... */ - client->notifyChannelEdited(old_default_channel, {property::CHANNEL_FLAG_DEFAULT}, self_rev, false); - }); - } + if(old_default_channel) /* clients need to have one or more defualt channels... */ + client->notifyChannelEdited(old_default_channel, {property::CHANNEL_FLAG_DEFAULT}, self_rev, false); + }); + } - if(server_channel_w_lock.owns_lock()) - server_channel_w_lock.unlock(); + if(server_channel_w_lock.owns_lock()) + server_channel_w_lock.unlock(); - if(!channel_tree_read_lock.owns_lock()) - channel_tree_read_lock.lock(); + if(!channel_tree_read_lock.owns_lock()) + channel_tree_read_lock.lock(); - if(update_clients_in_channel && this->server) { - for(const auto& client : this->server->getClientsByChannel(channel)) - client->updateChannelClientProperties(true, true); //TODO: May only update the talk power and not all? - } + if(update_clients_in_channel && this->server) { + for(const auto& client : this->server->getClientsByChannel(channel)) + client->updateChannelClientProperties(true, true); //TODO: May only update the talk power and not all? + } return CommandResult::Success; } CommandResult ConnectedClient::handleCommandChannelMove(Command &cmd) { CMD_RESET_IDLE; CMD_CHK_AND_INC_FLOOD_POINTS(25); - RESOLVE_CHANNEL_W(cmd["cid"], true); - auto channel = dynamic_pointer_cast(l_channel->entry); - assert(channel); - if(channel->deleted) - return {findError("channel_is_deleted"), "target channel has been deleted"}; + RESOLVE_CHANNEL_W(cmd["cid"], true); + auto channel = dynamic_pointer_cast(l_channel->entry); + assert(channel); + if(channel->deleted) + return {findError("channel_is_deleted"), "target channel has been deleted"}; - if(!cmd[0].has("order")) - cmd["order"] = 0; + if(!cmd[0].has("order")) + cmd["order"] = 0; auto l_parent = channel_tree->findLinkedChannel(cmd["cpid"]); - shared_ptr l_order; - if(cmd[0].has("order")) { - l_order = channel_tree->findLinkedChannel(cmd["order"]); - if (!l_order && cmd["order"].as() != 0) return {findError("channel_invalid_id"), "Cant resolve order channel"}; - } else { - l_order = l_parent ? l_parent->child_head : (this->server ? this->server->getChannelTree() : serverInstance->getChannelTree().get())->tree_head(); - while(l_order && l_order->next) - l_order = l_order->next; - } + shared_ptr l_order; + if(cmd[0].has("order")) { + l_order = channel_tree->findLinkedChannel(cmd["order"]); + if (!l_order && cmd["order"].as() != 0) return {findError("channel_invalid_id"), "Cant resolve order channel"}; + } else { + l_order = l_parent ? l_parent->child_head : (this->server ? this->server->getChannelTree() : serverInstance->getChannelTree().get())->tree_head(); + while(l_order && l_order->next) + l_order = l_order->next; + } auto parent = l_parent ? dynamic_pointer_cast(l_parent->entry) : nullptr; auto order = l_order ? dynamic_pointer_cast(l_order->entry) : nullptr; - if((parent && parent->deleted) || (order && order->deleted)) - return {findError("channel_is_deleted"), "parent channel order previous channel has been deleted"}; + if((parent && parent->deleted) || (order && order->deleted)) + return {findError("channel_is_deleted"), "parent channel order previous channel has been deleted"}; if(channel->parent() == parent && channel->channelOrder() == (order ? order->channelId() : 0)) - return CommandResult::Success; + return CommandResult::Success; if (channel->parent() != parent) PERM_CHECK_CHANNELR(permission::b_channel_modify_parent, 1, channel, true); - if ((order ? order->channelId() : 0) != channel->channelOrder()) - PERM_CHECK_CHANNELR(permission::b_channel_modify_sortorder, 1, channel, true); + if ((order ? order->channelId() : 0) != channel->channelOrder()) + PERM_CHECK_CHANNELR(permission::b_channel_modify_sortorder, 1, channel, true); - { + { - auto min_channel_deep = this->permissionValue(permission::PERMTEST_ORDERED, permission::i_channel_min_depth, nullptr, nullptr); - auto max_channel_deep = this->permissionValue(permission::PERMTEST_ORDERED, permission::i_channel_max_depth, nullptr, nullptr); + auto min_channel_deep = this->permissionValue(permission::PERMTEST_ORDERED, permission::i_channel_min_depth, nullptr, nullptr); + auto max_channel_deep = this->permissionValue(permission::PERMTEST_ORDERED, permission::i_channel_max_depth, nullptr, nullptr); - if(min_channel_deep >= 0 || max_channel_deep >= 0) { - auto channel_deep = 0; - auto local_parent = l_parent; - while(local_parent) { - channel_deep++; - local_parent = local_parent->parent.lock(); - } + if(min_channel_deep >= 0 || max_channel_deep >= 0) { + auto channel_deep = 0; + auto local_parent = l_parent; + while(local_parent) { + channel_deep++; + local_parent = local_parent->parent.lock(); + } - if(min_channel_deep >= 0 && channel_deep < min_channel_deep) return CommandResultPermissionError{permission::i_channel_min_depth}; - if(max_channel_deep >= 0 && channel_deep > max_channel_deep) return CommandResultPermissionError{permission::i_channel_max_depth}; - } - } - { - auto name = channel_tree->findChannel(channel->name(), parent); - if(name && name != channel) return {findError("channel_invalid_name"), "Channel with this name already exists"}; - } + if(min_channel_deep >= 0 && channel_deep < min_channel_deep) return CommandResultPermissionError{permission::i_channel_min_depth}; + if(max_channel_deep >= 0 && channel_deep > max_channel_deep) return CommandResultPermissionError{permission::i_channel_max_depth}; + } + } + { + auto name = channel_tree->findChannel(channel->name(), parent); + if(name && name != channel) return {findError("channel_invalid_name"), "Channel with this name already exists"}; + } debugMessage(this->getServerId(), "Moving channel {} from old [{} | {}] to [{} | {}]", channel->name(), channel->channelOrder(), channel->parent() ? channel->parent()->channelId() : 0, order ? order->channelId() : 0, parent ? parent->channelId() : 0); if (!channel_tree->move_channel(channel, parent, order)) return {findError("channel_invalid_order"), "Cant change order id"}; - deque> channel_type_updates; - { - auto flag_default = channel->defaultChannel(); - auto current_channel = channel; - do { - if(flag_default) { - if(current_channel->channelType() != ChannelType::permanent) { - current_channel->setChannelType(ChannelType::permanent); - channel_type_updates.push_front(current_channel); - } - } else if(current_channel->hasParent()) { - if(current_channel->channelType() < current_channel->parent()->channelType()) { - current_channel->setChannelType(current_channel->parent()->channelType()); - channel_type_updates.push_front(current_channel); - } - } - } while ((current_channel = dynamic_pointer_cast(current_channel->parent()))); - } + deque> channel_type_updates; + { + auto flag_default = channel->defaultChannel(); + auto current_channel = channel; + do { + if(flag_default) { + if(current_channel->channelType() != ChannelType::permanent) { + current_channel->setChannelType(ChannelType::permanent); + channel_type_updates.push_front(current_channel); + } + } else if(current_channel->hasParent()) { + if(current_channel->channelType() < current_channel->parent()->channelType()) { + current_channel->setChannelType(current_channel->parent()->channelType()); + channel_type_updates.push_front(current_channel); + } + } + } while ((current_channel = dynamic_pointer_cast(current_channel->parent()))); + } - if(this->server) { - auto self_rev = this->ref(); - this->server->forEachClient([&](const shared_ptr& client) { - unique_lock channel_lock(client->channel_lock); - for(const auto& type_update : channel_type_updates) - client->notifyChannelEdited(type_update, {property::CHANNEL_FLAG_PERMANENT, property::CHANNEL_FLAG_SEMI_PERMANENT}, self_rev, false); + if(this->server) { + auto self_rev = this->ref(); + this->server->forEachClient([&](const shared_ptr& client) { + unique_lock channel_lock(client->channel_lock); + for(const auto& type_update : channel_type_updates) + client->notifyChannelEdited(type_update, {property::CHANNEL_FLAG_PERMANENT, property::CHANNEL_FLAG_SEMI_PERMANENT}, self_rev, false); - auto actions = client->channels->change_order(l_channel, l_parent, l_order); - std::deque deletions; - for(const auto& action : actions) { - switch (action.first) { - case ClientChannelView::NOTHING: - continue; - case ClientChannelView::ENTER_VIEW: - client->notifyChannelShow(action.second->channel(), action.second->previous_channel); - break; - case ClientChannelView::DELETE_VIEW: - deletions.push_back(action.second->channelId()); - break; - case ClientChannelView::MOVE: - client->notifyChannelMoved(action.second->channel(), action.second->previous_channel, _this.lock()); - break; - case ClientChannelView::REORDER: - client->notifyChannelEdited(action.second->channel(), {property::CHANNEL_ORDER}, self_rev, false); - break; - } - } - if(!deletions.empty()) - client->notifyChannelHide(deletions, false); - }); - } + auto actions = client->channels->change_order(l_channel, l_parent, l_order); + std::deque deletions; + for(const auto& action : actions) { + switch (action.first) { + case ClientChannelView::NOTHING: + continue; + case ClientChannelView::ENTER_VIEW: + client->notifyChannelShow(action.second->channel(), action.second->previous_channel); + break; + case ClientChannelView::DELETE_VIEW: + deletions.push_back(action.second->channelId()); + break; + case ClientChannelView::MOVE: + client->notifyChannelMoved(action.second->channel(), action.second->previous_channel, _this.lock()); + break; + case ClientChannelView::REORDER: + client->notifyChannelEdited(action.second->channel(), {property::CHANNEL_ORDER}, self_rev, false); + break; + } + } + if(!deletions.empty()) + client->notifyChannelHide(deletions, false); + }); + } return CommandResult::Success; } @@ -2362,18 +2362,18 @@ CommandResult ConnectedClient::handleCommandClientPoke(Command &cmd) { CommandResult ConnectedClient::handleCommandChannelPermList(Command &cmd) { CMD_CHK_AND_INC_FLOOD_POINTS(5); - RESOLVE_CHANNEL_R(cmd["cid"], true); - auto channel = dynamic_pointer_cast(l_channel->entry); - assert(channel); + RESOLVE_CHANNEL_R(cmd["cid"], true); + auto channel = dynamic_pointer_cast(l_channel->entry); + assert(channel); - CHANNEL_PERM_TEST_INIT; - CHANNEL_PERM_TEST(permission::b_virtualserver_channel_permission_list, 1, true); + CHANNEL_PERM_TEST_INIT; + CHANNEL_PERM_TEST(permission::b_virtualserver_channel_permission_list, 1, true); - if(this->getType() == ClientType::CLIENT_TEAMSPEAK && this->command_times.last_notify + this->command_times.notify_timeout < system_clock::now()) { - this->sendTSPermEditorWarning(); - } + if(this->getType() == ClientType::CLIENT_TEAMSPEAK && this->command_times.last_notify + this->command_times.notify_timeout < system_clock::now()) { + this->sendTSPermEditorWarning(); + } - auto sids = cmd.hasParm("permsid"); + auto sids = cmd.hasParm("permsid"); Command result(this->notify_response_command("notifychannelpermlist")); int index = 0; @@ -2383,13 +2383,13 @@ CommandResult ConnectedClient::handleCommandChannelPermList(Command &cmd) { auto type = this->getType(); auto permission_manager = channel->permissions(); for (const auto &permission_data : permission_manager->permissions()) { - auto& permission = std::get<1>(permission_data); + auto& permission = std::get<1>(permission_data); if(permission.flags.value_set) { - if(sids) { - result[index]["permsid"] = permission_mapper->permission_name(type, std::get<0>(permission_data)); - } else { - result[index]["permid"] = std::get<0>(permission_data); - } + if(sids) { + result[index]["permsid"] = permission_mapper->permission_name(type, std::get<0>(permission_data)); + } else { + result[index]["permid"] = std::get<0>(permission_data); + } result[index]["permvalue"] = permission.values.value; result[index]["permnegated"] = permission.flags.negate; @@ -2397,11 +2397,11 @@ CommandResult ConnectedClient::handleCommandChannelPermList(Command &cmd) { index++; } if(permission.flags.grant_set) { - if(sids) { - result[index]["permsid"] = permission_mapper->permission_name_grant(type, std::get<0>(permission_data)); - } else { - result[index]["permid"] = (uint16_t) (std::get<0>(permission_data) | PERM_ID_GRANT); - } + if(sids) { + result[index]["permsid"] = permission_mapper->permission_name_grant(type, std::get<0>(permission_data)); + } else { + result[index]["permid"] = (uint16_t) (std::get<0>(permission_data) | PERM_ID_GRANT); + } result[index]["permvalue"] = permission.values.grant; result[index]["permnegated"] = 0; result[index]["permskip"] = 0; @@ -2409,7 +2409,7 @@ CommandResult ConnectedClient::handleCommandChannelPermList(Command &cmd) { } } if(index == 0) - return {ErrorType::DBEmpty}; + return {ErrorType::DBEmpty}; this->sendCommand(result); return CommandResult::Success; @@ -2424,103 +2424,103 @@ CommandResult ConnectedClient::handleCommandChannelAddPerm(Command &cmd) { CMD_RESET_IDLE; CMD_CHK_AND_INC_FLOOD_POINTS(5); - RESOLVE_CHANNEL_R(cmd["cid"], true); - auto channel = dynamic_pointer_cast(l_channel->entry); - assert(channel); + RESOLVE_CHANNEL_R(cmd["cid"], true); + auto channel = dynamic_pointer_cast(l_channel->entry); + assert(channel); - CHANNEL_PERMISSION_TEST(permission::i_channel_permission_modify_power, permission::i_channel_needed_permission_modify_power, channel, true); + CHANNEL_PERMISSION_TEST(permission::i_channel_permission_modify_power, permission::i_channel_needed_permission_modify_power, channel, true); - auto maxValue = this->getPermissionGrantValue(permission::PERMTEST_ORDERED, permission::i_permission_modify_power, channel); - bool ignoreGrant = this->permissionGranted(permission::PERMTEST_ORDERED, permission::b_permission_modify_power_ignore, 1, channel); - auto updateClients = false, update_view = false, update_channel_properties = false; + auto maxValue = this->getPermissionGrantValue(permission::PERMTEST_ORDERED, permission::i_permission_modify_power, channel); + bool ignoreGrant = this->permissionGranted(permission::PERMTEST_ORDERED, permission::b_permission_modify_power_ignore, 1, channel); + auto updateClients = false, update_view = false, update_channel_properties = false; - CommandResult command_result = CommandResult::Success; + CommandResult command_result = CommandResult::Success; bool conOnError = cmd[0].has("continueonerror"); auto permission_manager = channel->permissions(); for (int index = 0; index < cmd.bulkCount(); index++) { - PARSE_PERMISSION(cmd); + PARSE_PERMISSION(cmd); - auto val = cmd[index]["permvalue"].as(); - if(permission_require_granted_value(permType) && val > maxValue) { - command_result = CommandResultPermissionError{permission::i_permission_modify_power}; - break; - } + auto val = cmd[index]["permvalue"].as(); + if(permission_require_granted_value(permType) && val > maxValue) { + command_result = CommandResultPermissionError{permission::i_permission_modify_power}; + break; + } - if(!ignoreGrant && !this->permissionGrantGranted(permission::PERMTEST_ORDERED, permType, 1, channel)) { - command_result = CommandResultPermissionError{permission::i_permission_modify_power}; - break; - } + if(!ignoreGrant && !this->permissionGrantGranted(permission::PERMTEST_ORDERED, permType, 1, channel)) { + command_result = CommandResultPermissionError{permission::i_permission_modify_power}; + break; + } if (grant) { - permission_manager->set_permission(permType, {0, cmd[index]["permvalue"]}, permission::v2::PermissionUpdateType::do_nothing, permission::v2::PermissionUpdateType::set_value); + permission_manager->set_permission(permType, {0, cmd[index]["permvalue"]}, permission::v2::PermissionUpdateType::do_nothing, permission::v2::PermissionUpdateType::set_value); } else { - permission_manager->set_permission( - permType, - {cmd[index]["permvalue"], 0}, - permission::v2::PermissionUpdateType::set_value, - permission::v2::PermissionUpdateType::do_nothing, + permission_manager->set_permission( + permType, + {cmd[index]["permvalue"], 0}, + permission::v2::PermissionUpdateType::set_value, + permission::v2::PermissionUpdateType::do_nothing, - cmd[index]["permskip"].as() ? 1 : 0, - cmd[index]["permnegated"].as() ? 1 : 0 + cmd[index]["permskip"].as() ? 1 : 0, + cmd[index]["permnegated"].as() ? 1 : 0 ); - updateClients |= permission_is_client_property(permType); + updateClients |= permission_is_client_property(permType); update_view |= permType == permission::i_channel_needed_view_power; - update_channel_properties |= channel->permission_require_property_update(permType); + update_channel_properties |= channel->permission_require_property_update(permType); - if (permType == permission::i_icon_id) { - if(this->server) { - auto self_ref = this->ref(); - this->server->forEachClient([&](std::shared_ptr cl) { - shared_lock client_channel_lock(cl->channel_lock); - cl->notifyChannelEdited(channel, {property::CHANNEL_ICON_ID}, self_ref, false); - }); - } - continue; - } + if (permType == permission::i_icon_id) { + if(this->server) { + auto self_ref = this->ref(); + this->server->forEachClient([&](std::shared_ptr cl) { + shared_lock client_channel_lock(cl->channel_lock); + cl->notifyChannelEdited(channel, {property::CHANNEL_ICON_ID}, self_ref, false); + }); + } + continue; + } } } /* broadcast the updated channel properties */ if(update_channel_properties) { - auto updates = channel->update_properties_from_permissions(); - if(!updates.empty() && this->server){ - auto self_ref = this->ref(); - this->server->forEachClient([&](std::shared_ptr cl) { - shared_lock client_channel_lock(cl->channel_lock); - cl->notifyChannelEdited(channel, updates, self_ref, false); - }); - } + auto updates = channel->update_properties_from_permissions(); + if(!updates.empty() && this->server){ + auto self_ref = this->ref(); + this->server->forEachClient([&](std::shared_ptr cl) { + shared_lock client_channel_lock(cl->channel_lock); + cl->notifyChannelEdited(channel, updates, self_ref, false); + }); + } } - if(updateClients && this->server) - for(const auto& client : this->server->getClientsByChannel(channel)) { - /* let them lock the server channel tree as well (read lock so does not matter) */ - client->updateChannelClientProperties(true, true); - client->join_state_id++; /* join permission may changed, all channels need to be recalculate dif needed */ - } + if(updateClients && this->server) + for(const auto& client : this->server->getClientsByChannel(channel)) { + /* let them lock the server channel tree as well (read lock so does not matter) */ + client->updateChannelClientProperties(true, true); + client->join_state_id++; /* join permission may changed, all channels need to be recalculate dif needed */ + } if(update_view && this->server) { - auto l_source = this->server->channelTree->findLinkedChannel(channel->channelId()); - this->server->forEachClient([&](const shared_ptr& cl) { - /* server tree read lock still active */ - auto l_target = !cl->currentChannel ? nullptr : cl->server->channelTree->findLinkedChannel(cl->currentChannel->channelId()); - sassert(l_source); - if(cl->currentChannel) sassert(l_target); + auto l_source = this->server->channelTree->findLinkedChannel(channel->channelId()); + this->server->forEachClient([&](const shared_ptr& cl) { + /* server tree read lock still active */ + auto l_target = !cl->currentChannel ? nullptr : cl->server->channelTree->findLinkedChannel(cl->currentChannel->channelId()); + sassert(l_source); + if(cl->currentChannel) sassert(l_target); - { - unique_lock client_channel_lock(cl->channel_lock); + { + unique_lock client_channel_lock(cl->channel_lock); - deque deleted; - for(const auto& update_entry : cl->channels->update_channel(l_source, l_target)) { - if(update_entry.first) - cl->notifyChannelShow(update_entry.second->channel(), update_entry.second->previous_channel); - else deleted.push_back(update_entry.second->channelId()); - } - if(!deleted.empty()) - cl->notifyChannelHide(deleted, false); - } - }); + deque deleted; + for(const auto& update_entry : cl->channels->update_channel(l_source, l_target)) { + if(update_entry.first) + cl->notifyChannelShow(update_entry.second->channel(), update_entry.second->previous_channel); + else deleted.push_back(update_entry.second->channelId()); + } + if(!deleted.empty()) + cl->notifyChannelHide(deleted, false); + } + }); } return command_result; } @@ -2528,72 +2528,72 @@ CommandResult ConnectedClient::handleCommandChannelAddPerm(Command &cmd) { CommandResult ConnectedClient::handleCommandChannelDelPerm(Command &cmd) { CMD_RESET_IDLE; - RESOLVE_CHANNEL_R(cmd["cid"], true) - auto channel = dynamic_pointer_cast(l_channel->entry); - assert(channel); - CHANNEL_PERMISSION_TEST(permission::i_channel_permission_modify_power, permission::i_channel_needed_permission_modify_power, channel, true); + RESOLVE_CHANNEL_R(cmd["cid"], true) + auto channel = dynamic_pointer_cast(l_channel->entry); + assert(channel); + CHANNEL_PERMISSION_TEST(permission::i_channel_permission_modify_power, permission::i_channel_needed_permission_modify_power, channel, true); - bool ignoreGrant = this->permissionGranted(permission::PERMTEST_ORDERED, permission::b_permission_modify_power_ignore, 1, channel); + bool ignoreGrant = this->permissionGranted(permission::PERMTEST_ORDERED, permission::b_permission_modify_power_ignore, 1, channel); bool conOnError = cmd[0].has("continueonerror"); - auto updateClients = false, update_view = false, update_channel_properties = false; + auto updateClients = false, update_view = false, update_channel_properties = false; - auto permission_manager = channel->permissions(); + auto permission_manager = channel->permissions(); for (int index = 0; index < cmd.bulkCount(); index++) { - PARSE_PERMISSION(cmd); + PARSE_PERMISSION(cmd); - if(!ignoreGrant && !this->permissionGrantGranted(permission::PERMTEST_ORDERED, permType, 1, channel)) - return CommandResultPermissionError{permission::i_permission_modify_power}; + if(!ignoreGrant && !this->permissionGrantGranted(permission::PERMTEST_ORDERED, permType, 1, channel)) + return CommandResultPermissionError{permission::i_permission_modify_power}; if (grant) { - permission_manager->set_permission(permType, permission::v2::empty_permission_values, permission::v2::PermissionUpdateType::do_nothing, permission::v2::PermissionUpdateType::delete_value); + permission_manager->set_permission(permType, permission::v2::empty_permission_values, permission::v2::PermissionUpdateType::do_nothing, permission::v2::PermissionUpdateType::delete_value); } else { - permission_manager->set_permission(permType, permission::v2::empty_permission_values, permission::v2::PermissionUpdateType::delete_value, permission::v2::PermissionUpdateType::do_nothing); - updateClients |= permission_is_client_property(permType); + permission_manager->set_permission(permType, permission::v2::empty_permission_values, permission::v2::PermissionUpdateType::delete_value, permission::v2::PermissionUpdateType::do_nothing); + updateClients |= permission_is_client_property(permType); update_view |= permType == permission::i_channel_needed_view_power; - update_channel_properties |= channel->permission_require_property_update(permType); + update_channel_properties |= channel->permission_require_property_update(permType); } } - /* broadcast the updated channel properties */ - if(update_channel_properties) { - auto updates = channel->update_properties_from_permissions(); - if(!updates.empty() && this->server){ - auto self_ref = this->ref(); - this->server->forEachClient([&](std::shared_ptr cl) { - shared_lock client_channel_lock(cl->channel_lock); - cl->notifyChannelEdited(channel, updates, self_ref, false); - }); - } - } + /* broadcast the updated channel properties */ + if(update_channel_properties) { + auto updates = channel->update_properties_from_permissions(); + if(!updates.empty() && this->server){ + auto self_ref = this->ref(); + this->server->forEachClient([&](std::shared_ptr cl) { + shared_lock client_channel_lock(cl->channel_lock); + cl->notifyChannelEdited(channel, updates, self_ref, false); + }); + } + } - if(updateClients && this->server) - this->server->forEachClient([&](std::shared_ptr cl) { - if(cl->currentChannel == channel) { - cl->updateChannelClientProperties(true, true); - cl->join_state_id++; /* join permission may changed, all channels need to be recalculate dif needed */ - } - }); + if(updateClients && this->server) + this->server->forEachClient([&](std::shared_ptr cl) { + if(cl->currentChannel == channel) { + cl->updateChannelClientProperties(true, true); + cl->join_state_id++; /* join permission may changed, all channels need to be recalculate dif needed */ + } + }); if(update_view && this->server) { - this->server->forEachClient([&](std::shared_ptr cl) { - /* server tree read lock still active */ - auto l_source = cl->server->channelTree->findLinkedChannel(channel->channelId()); - auto l_target = !cl->currentChannel ? nullptr : cl->server->channelTree->findLinkedChannel(cl->currentChannel->channelId()); - sassert(l_source); - if(cl->currentChannel) sassert(l_target); + this->server->forEachClient([&](std::shared_ptr cl) { + /* server tree read lock still active */ + auto l_source = cl->server->channelTree->findLinkedChannel(channel->channelId()); + auto l_target = !cl->currentChannel ? nullptr : cl->server->channelTree->findLinkedChannel(cl->currentChannel->channelId()); + sassert(l_source); + if(cl->currentChannel) sassert(l_target); - { - unique_lock client_channel_lock(cl->channel_lock); + { + unique_lock client_channel_lock(cl->channel_lock); - deque deleted; - for(const auto& update_entry : cl->channels->update_channel(l_source, l_target)) { - if(update_entry.first) - cl->notifyChannelShow(update_entry.second->channel(), update_entry.second->previous_channel); - else deleted.push_back(update_entry.second->channelId()); - } - if(!deleted.empty()) - cl->notifyChannelHide(deleted, false); - } - }); + deque deleted; + for(const auto& update_entry : cl->channels->update_channel(l_source, l_target)) { + if(update_entry.first) + cl->notifyChannelShow(update_entry.second->channel(), update_entry.second->previous_channel); + else deleted.push_back(update_entry.second->channelId()); + } + if(!deleted.empty()) + cl->notifyChannelHide(deleted, false); + } + }); } return CommandResult::Success; } @@ -2603,26 +2603,26 @@ CommandResult ConnectedClient::handleCommandServerGroupAdd(Command &cmd) { CMD_RESET_IDLE; CMD_CHK_AND_INC_FLOOD_POINTS(5); CACHED_PERM_CHECK(permission::b_virtualserver_servergroup_create, 1, true); - if(cmd["name"].string().empty()) return {findError("parameter_invalid"), "invalid group name"}; + if(cmd["name"].string().empty()) return {findError("parameter_invalid"), "invalid group name"}; - if(cmd["type"].as() == GroupType::GROUP_TYPE_QUERY) { - if(!this->permission_granted(this->cached_permission_value(permission::b_serverinstance_modify_querygroup), 1, true)) - return CommandResultPermissionError{permission::b_serverinstance_modify_querygroup}; - } else if(cmd["type"].as() == GroupType::GROUP_TYPE_TEMPLATE) { - if(!this->permission_granted(this->cached_permission_value(permission::b_serverinstance_modify_templates), 1, true)) - return CommandResultPermissionError{permission::b_serverinstance_modify_templates}; - } else if(!this->server) return {findError("parameter_invalid"), "you cant create normal groups on the template server!"}; + if(cmd["type"].as() == GroupType::GROUP_TYPE_QUERY) { + if(!this->permission_granted(this->cached_permission_value(permission::b_serverinstance_modify_querygroup), 1, true)) + return CommandResultPermissionError{permission::b_serverinstance_modify_querygroup}; + } else if(cmd["type"].as() == GroupType::GROUP_TYPE_TEMPLATE) { + if(!this->permission_granted(this->cached_permission_value(permission::b_serverinstance_modify_templates), 1, true)) + return CommandResultPermissionError{permission::b_serverinstance_modify_templates}; + } else if(!this->server) return {findError("parameter_invalid"), "you cant create normal groups on the template server!"}; auto group_manager = this->server ? this->server->getGroupManager() : serverInstance->getGroupManager().get(); for(const auto& gr : group_manager->availableServerGroups(true)) if(gr->name() == cmd["name"].string() && gr->target() == GroupTarget::GROUPTARGET_SERVER) return {findError("parameter_invalid"), "Group already exists"}; auto group = group_manager->createGroup(GroupTarget::GROUPTARGET_SERVER, cmd["type"].as(), cmd["name"].string()); if (group) { - group->permissions()->set_permission(permission::b_group_is_permanent, {1,0}, permission::v2::set_value, permission::v2::do_nothing); - if(this->server) - this->server->forEachClient([](shared_ptr cl) { - cl->notifyServerGroupList(); - }); + group->permissions()->set_permission(permission::b_group_is_permanent, {1,0}, permission::v2::set_value, permission::v2::do_nothing); + if(this->server) + this->server->forEachClient([](shared_ptr cl) { + cl->notifyServerGroupList(); + }); } else return {ErrorType::VSError, "Could not create group"}; return CommandResult::Success; } @@ -2632,7 +2632,7 @@ CommandResult ConnectedClient::handleCommandServerGroupCopy(Command &cmd) { CMD_CHK_AND_INC_FLOOD_POINTS(5); auto ref_server = this->server; - CACHED_PERM_CHECK(permission::b_virtualserver_servergroup_create, 1, true); + CACHED_PERM_CHECK(permission::b_virtualserver_servergroup_create, 1, true); auto group_manager = this->server ? this->server->groups : serverInstance->getGroupManager().get(); @@ -2640,85 +2640,85 @@ CommandResult ConnectedClient::handleCommandServerGroupCopy(Command &cmd) { auto source_group = group_manager->findGroup(source_group_id); if(!source_group || source_group->target() != GROUPTARGET_SERVER) - return {findError("group_invalid_id"), "invalid source group"}; + return {findError("group_invalid_id"), "invalid source group"}; const auto group_type_modificable = [&](GroupType type) { - switch(type) { - case GroupType::GROUP_TYPE_TEMPLATE: - if(!this->permission_granted(this->permissionValue(permission::b_serverinstance_modify_templates, 0), 1, true)) - return permission::b_serverinstance_modify_templates; - break; - case GroupType::GROUP_TYPE_QUERY: - if(!this->permission_granted(this->permissionValue(permission::b_serverinstance_modify_querygroup, 0), 1, true)) - return permission::b_serverinstance_modify_querygroup; - break; + switch(type) { + case GroupType::GROUP_TYPE_TEMPLATE: + if(!this->permission_granted(this->permissionValue(permission::b_serverinstance_modify_templates, 0), 1, true)) + return permission::b_serverinstance_modify_templates; + break; + case GroupType::GROUP_TYPE_QUERY: + if(!this->permission_granted(this->permissionValue(permission::b_serverinstance_modify_querygroup, 0), 1, true)) + return permission::b_serverinstance_modify_querygroup; + break; - default: - break; - } - return permission::undefined; + default: + break; + } + return permission::undefined; }; - { - auto result = group_type_modificable(source_group->type()); - if(result != permission::undefined) - return CommandResultPermissionError{result}; - } - - auto global_update = false; - if(cmd[0].has("tsgid") && cmd["tsgid"].as() != 0) { - //Copy an existing group - auto target_group = group_manager->findGroup(cmd["tsgid"]); - if(!target_group || target_group->target() != GROUPTARGET_SERVER) - return {findError("group_invalid_id"), "invalid target group"}; - - { - auto result = group_type_modificable(target_group->type()); - if(result != permission::undefined) - return CommandResultPermissionError{result}; - } - - if(!target_group->permission_granted(permission::i_server_group_needed_modify_power, this->calculate_permission_value(permission::i_server_group_modify_power, 0), true)) - return CommandResultPermissionError{permission::i_server_group_modify_power}; - - if(!group_manager->copyGroupPermissions(source_group, target_group)) - return {findError("vs_critical"), "failed to copy group permissions"}; - - global_update = !this->server || !group_manager->isLocalGroup(target_group); - } else { - //Copy a new group - auto target_type = cmd["type"].as(); - - { - auto result = group_type_modificable(target_type); - if(result != permission::undefined) - return CommandResultPermissionError{result}; - } - - if(!ref_server && target_type == GroupType::GROUP_TYPE_NORMAL) - return {findError("parameter_invalid"), "You cant create normal groups on the template server!"}; - - if(!group_manager->findGroup(GroupTarget::GROUPTARGET_SERVER, cmd["name"].string()).empty()) - return {findError("group_name_used"), "You cant create normal groups on the template server!"}; - - auto target_group_id = group_manager->copyGroup(source_group, target_type, cmd["name"], target_type != GroupType::GROUP_TYPE_NORMAL ? 0 : this->getServerId()); - if(target_group_id == 0) - return {findError("vs_critical"), "failed to copy group"}; - - if(this->getType() == ClientType::CLIENT_QUERY) { - Command notify(""); - notify["sgid"] = target_group_id; - this->sendCommand(notify); - } - - global_update = !this->server || !group_manager->isLocalGroup(group_manager->findGroup(target_group_id)); + { + auto result = group_type_modificable(source_group->type()); + if(result != permission::undefined) + return CommandResultPermissionError{result}; } - for(const auto& server : (global_update ? serverInstance->getVoiceServerManager()->serverInstances() : deque>{this->server})) - if(server) - server->forEachClient([](shared_ptr cl) { - cl->notifyServerGroupList(); - }); + auto global_update = false; + if(cmd[0].has("tsgid") && cmd["tsgid"].as() != 0) { + //Copy an existing group + auto target_group = group_manager->findGroup(cmd["tsgid"]); + if(!target_group || target_group->target() != GROUPTARGET_SERVER) + return {findError("group_invalid_id"), "invalid target group"}; + + { + auto result = group_type_modificable(target_group->type()); + if(result != permission::undefined) + return CommandResultPermissionError{result}; + } + + if(!target_group->permission_granted(permission::i_server_group_needed_modify_power, this->calculate_permission_value(permission::i_server_group_modify_power, 0), true)) + return CommandResultPermissionError{permission::i_server_group_modify_power}; + + if(!group_manager->copyGroupPermissions(source_group, target_group)) + return {findError("vs_critical"), "failed to copy group permissions"}; + + global_update = !this->server || !group_manager->isLocalGroup(target_group); + } else { + //Copy a new group + auto target_type = cmd["type"].as(); + + { + auto result = group_type_modificable(target_type); + if(result != permission::undefined) + return CommandResultPermissionError{result}; + } + + if(!ref_server && target_type == GroupType::GROUP_TYPE_NORMAL) + return {findError("parameter_invalid"), "You cant create normal groups on the template server!"}; + + if(!group_manager->findGroup(GroupTarget::GROUPTARGET_SERVER, cmd["name"].string()).empty()) + return {findError("group_name_used"), "You cant create normal groups on the template server!"}; + + auto target_group_id = group_manager->copyGroup(source_group, target_type, cmd["name"], target_type != GroupType::GROUP_TYPE_NORMAL ? 0 : this->getServerId()); + if(target_group_id == 0) + return {findError("vs_critical"), "failed to copy group"}; + + if(this->getType() == ClientType::CLIENT_QUERY) { + Command notify(""); + notify["sgid"] = target_group_id; + this->sendCommand(notify); + } + + global_update = !this->server || !group_manager->isLocalGroup(group_manager->findGroup(target_group_id)); + } + + for(const auto& server : (global_update ? serverInstance->getVoiceServerManager()->serverInstances() : deque>{this->server})) + if(server) + server->forEachClient([](shared_ptr cl) { + cl->notifyServerGroupList(); + }); return CommandResult::Success; } @@ -2730,22 +2730,22 @@ CommandResult ConnectedClient::handleCommandServerGroupRename(Command &cmd) { auto serverGroup = group_manager->findGroup(cmd["sgid"].as()); if (!serverGroup || serverGroup->target() != GROUPTARGET_SERVER) return {findError("parameter_invalid"), "invalid server group id"}; - GROUP_PERMISSION_TEST(permission::i_server_group_modify_power, permission::i_server_group_needed_modify_power, serverGroup, true); + GROUP_PERMISSION_TEST(permission::i_server_group_modify_power, permission::i_server_group_needed_modify_power, serverGroup, true); - auto type = serverGroup->type(); - if(type == GroupType::GROUP_TYPE_QUERY) { - if(!this->permission_granted(this->cached_permission_value(permission::b_serverinstance_modify_querygroup), 1, true)) - return CommandResultPermissionError{permission::b_serverinstance_modify_querygroup}; - } else if(type == GroupType::GROUP_TYPE_TEMPLATE) { - if(!this->permission_granted(this->cached_permission_value(permission::b_serverinstance_modify_templates), 1, true)) - return CommandResultPermissionError{permission::b_serverinstance_modify_templates}; - } + auto type = serverGroup->type(); + if(type == GroupType::GROUP_TYPE_QUERY) { + if(!this->permission_granted(this->cached_permission_value(permission::b_serverinstance_modify_querygroup), 1, true)) + return CommandResultPermissionError{permission::b_serverinstance_modify_querygroup}; + } else if(type == GroupType::GROUP_TYPE_TEMPLATE) { + if(!this->permission_granted(this->cached_permission_value(permission::b_serverinstance_modify_templates), 1, true)) + return CommandResultPermissionError{permission::b_serverinstance_modify_templates}; + } - group_manager->renameGroup(serverGroup, cmd["name"].string()); - if(this->server) - this->server->forEachClient([](shared_ptr cl) { - cl->notifyServerGroupList(); - }); + group_manager->renameGroup(serverGroup, cmd["name"].string()); + if(this->server) + this->server->forEachClient([](shared_ptr cl) { + cl->notifyServerGroupList(); + }); return CommandResult::Success; } @@ -2755,42 +2755,42 @@ CommandResult ConnectedClient::handleCommandServerGroupDel(Command &cmd) { CMD_RESET_IDLE; CMD_CHK_AND_INC_FLOOD_POINTS(5); - CACHED_PERM_CHECK(permission::b_virtualserver_servergroup_delete, 1, true); - auto group_manager = this->server ? this->server->getGroupManager() : serverInstance->getGroupManager().get(); + CACHED_PERM_CHECK(permission::b_virtualserver_servergroup_delete, 1, true); + auto group_manager = this->server ? this->server->getGroupManager() : serverInstance->getGroupManager().get(); auto serverGroup = group_manager->findGroup(cmd["sgid"].as()); if (!serverGroup || serverGroup->target() != GROUPTARGET_SERVER) return {findError("parameter_invalid"), "invalid server group id"}; - if(this->server && this->server->properties()[property::VIRTUALSERVER_DEFAULT_SERVER_GROUP] == serverGroup->groupId()) - return {findError("parameter_invalid"), "Could not delete default server group!"}; - if(serverInstance->properties()[property::SERVERINSTANCE_TEMPLATE_SERVERADMIN_GROUP] == serverGroup->groupId()) - return {findError("parameter_invalid"), "Could not delete instance default server admin group!"}; - if(serverInstance->properties()[property::SERVERINSTANCE_TEMPLATE_SERVERDEFAULT_GROUP] == serverGroup->groupId()) - return {findError("parameter_invalid"), "Could not delete instance default server group!"}; - if(serverInstance->properties()[property::SERVERINSTANCE_GUEST_SERVERQUERY_GROUP] == serverGroup->groupId()) - return {findError("parameter_invalid"), "Could not delete instance default guest server query group!"}; + if(this->server && this->server->properties()[property::VIRTUALSERVER_DEFAULT_SERVER_GROUP] == serverGroup->groupId()) + return {findError("parameter_invalid"), "Could not delete default server group!"}; + if(serverInstance->properties()[property::SERVERINSTANCE_TEMPLATE_SERVERADMIN_GROUP] == serverGroup->groupId()) + return {findError("parameter_invalid"), "Could not delete instance default server admin group!"}; + if(serverInstance->properties()[property::SERVERINSTANCE_TEMPLATE_SERVERDEFAULT_GROUP] == serverGroup->groupId()) + return {findError("parameter_invalid"), "Could not delete instance default server group!"}; + if(serverInstance->properties()[property::SERVERINSTANCE_GUEST_SERVERQUERY_GROUP] == serverGroup->groupId()) + return {findError("parameter_invalid"), "Could not delete instance default guest server query group!"}; - auto type = serverGroup->type(); - if(type == GroupType::GROUP_TYPE_QUERY) { - if(!this->permission_granted(this->cached_permission_value(permission::b_serverinstance_modify_querygroup), 1, true)) - return CommandResultPermissionError{permission::b_serverinstance_modify_querygroup}; - } else if(type == GroupType::GROUP_TYPE_TEMPLATE) { - if(!this->permission_granted(this->cached_permission_value(permission::b_serverinstance_modify_templates), 1, true)) - return CommandResultPermissionError{permission::b_serverinstance_modify_templates}; - } + auto type = serverGroup->type(); + if(type == GroupType::GROUP_TYPE_QUERY) { + if(!this->permission_granted(this->cached_permission_value(permission::b_serverinstance_modify_querygroup), 1, true)) + return CommandResultPermissionError{permission::b_serverinstance_modify_querygroup}; + } else if(type == GroupType::GROUP_TYPE_TEMPLATE) { + if(!this->permission_granted(this->cached_permission_value(permission::b_serverinstance_modify_templates), 1, true)) + return CommandResultPermissionError{permission::b_serverinstance_modify_templates}; + } - if (!cmd["force"].as()) + if (!cmd["force"].as()) if (!group_manager->listGroupMembers(serverGroup, false).empty()) return {findError("database_empty_result"), "group not empty!"}; if (group_manager->deleteGroup(serverGroup)) { - if(this->server) - this->server->forEachClient([&](shared_ptr cl) { - if(this->server->notifyClientPropertyUpdates(cl, this->server->groups->update_server_group_property(cl, true, cl->getChannel()))) { - if(cl->update_cached_permissions()) /* update cached calculated permissions */ - cl->sendNeededPermissions(false); /* cached permissions had changed, notify the client */ - } - cl->notifyServerGroupList(); - }); + if(this->server) + this->server->forEachClient([&](shared_ptr cl) { + if(this->server->notifyClientPropertyUpdates(cl, this->server->groups->update_server_group_property(cl, true, cl->getChannel()))) { + if(cl->update_cached_permissions()) /* update cached calculated permissions */ + cl->sendNeededPermissions(false); /* cached permissions had changed, notify the client */ + } + cl->notifyServerGroupList(); + }); } return CommandResult::Success; @@ -2802,7 +2802,7 @@ CommandResult ConnectedClient::handleCommandServerGroupClientList(Command &cmd) CMD_RESET_IDLE; CMD_CHK_AND_INC_FLOOD_POINTS(5); - CACHED_PERM_CHECK(permission::b_virtualserver_servergroup_client_list, 1, true); + CACHED_PERM_CHECK(permission::b_virtualserver_servergroup_client_list, 1, true); auto server = cmd[0].has("sid") && cmd["sid"] == 0 ? nullptr : this->server; auto groupManager = server ? this->server->groups : serverInstance->getGroupManager().get(); @@ -2830,108 +2830,108 @@ CommandResult ConnectedClient::handleCommandServerGroupAddClient(Command &cmd) { auto target_server = cmd[0].has("sid") && cmd["sid"] == 0 ? nullptr : this->server; auto group_manager = target_server ? this->server->groups : serverInstance->getGroupManager().get(); - auto target_cldbid = cmd["cldbid"].as(); - if (!serverInstance->databaseHelper()->validClientDatabaseId(target_server, cmd["cldbid"])) return {findError("client_invalid_id"), "invalid cldbid"}; + auto target_cldbid = cmd["cldbid"].as(); + if (!serverInstance->databaseHelper()->validClientDatabaseId(target_server, cmd["cldbid"])) return {findError("client_invalid_id"), "invalid cldbid"}; - 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(!this->permission_granted(this->permissionValue(permission::i_client_permission_modify_power), needed_client_permission)) - return CommandResultPermissionError{permission::i_client_needed_permission_modify_power}; - } + 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(!this->permission_granted(this->permissionValue(permission::i_client_permission_modify_power), needed_client_permission)) + return CommandResultPermissionError{permission::i_client_needed_permission_modify_power}; + } vector> target_groups; - vector> applied_groups; - target_groups.reserve(cmd.bulkCount()); + vector> 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); + 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() && continue_on_error) - continue; + for(auto index = 0; index < cmd.bulkCount(); index++) { + auto group_id = cmd[index]["sgid"]; + if(!group_id.castable() && continue_on_error) + continue; - auto gid = group_id.as(); - auto group = group_manager->findGroup(gid); - if(!group) { - if(continue_on_error) - continue; + auto gid = group_id.as(); + 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)}; - } + return {findError("parameter_invalid"), "missing server group for id " + to_string(gid)}; + } - if(!target_server && group->target() != GroupTarget::GROUPTARGET_SERVER && group->type() != GroupType::GROUP_TYPE_QUERY) - return {findError("parameter_invalid"), "invalid server group type for id " + to_string(gid)}; + if(!target_server && group->target() != GroupTarget::GROUPTARGET_SERVER && group->type() != GroupType::GROUP_TYPE_QUERY) + return {findError("parameter_invalid"), "invalid server group type for id " + to_string(gid)}; - if(find(target_groups.begin(), target_groups.end(), group) != target_groups.end()) { - if(continue_on_error) - continue; + if(find(target_groups.begin(), target_groups.end(), group) != target_groups.end()) { + if(continue_on_error) + continue; - return {findError("parameter_invalid"), "duplicate server group for id " + to_string(gid)}; - } + 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; + /* 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}; - } + 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; + 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}; - } - } + return CommandResultPermissionError{permission::i_server_group_self_add_power}; + } + } - target_groups.push_back(std::move(group)); - } - } + 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())}; + 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); - }}; + 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())}; - } + 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); - } - } + group_manager->addServerGroup(target_cldbid, group); + applied_groups.push_back(group); + } + } for(const auto& _server : target_server ? std::deque>{target_server} : serverInstance->getVoiceServerManager()->serverInstances()) { for (const auto &targetClient : _server->findClientsByCldbId(target_cldbid)) { if (_server->notifyClientPropertyUpdates(targetClient, _server->groups->update_server_group_property(targetClient, true, targetClient->getChannel()))) { for (const auto &client : _server->getClients()) { if(client->isClientVisible(targetClient, true) || client == targetClient) - for(const auto& group : applied_groups) - client->notifyServerGroupClientAdd(_this.lock(), targetClient, group); + for(const auto& group : applied_groups) + client->notifyServerGroupClientAdd(_this.lock(), targetClient, group); } - if(targetClient->update_cached_permissions()) /* update cached calculated permissions */ - targetClient->sendNeededPermissions(false); /* cached permissions had changed, notify the client */ - targetClient->updateChannelClientProperties(true, true); + if(targetClient->update_cached_permissions()) /* update cached calculated permissions */ + targetClient->sendNeededPermissions(false); /* cached permissions had changed, notify the client */ + targetClient->updateChannelClientProperties(true, true); } } } @@ -2940,130 +2940,130 @@ CommandResult ConnectedClient::handleCommandServerGroupAddClient(Command &cmd) { } CommandResult ConnectedClient::handleCommandServerGroupDelClient(Command &cmd) { - CMD_RESET_IDLE; - CMD_CHK_AND_INC_FLOOD_POINTS(25); + CMD_RESET_IDLE; + CMD_CHK_AND_INC_FLOOD_POINTS(25); - auto target_server = cmd[0].has("sid") && cmd["sid"] == 0 ? nullptr : this->server; - auto group_manager = target_server ? this->server->groups : serverInstance->getGroupManager().get(); + auto target_server = cmd[0].has("sid") && cmd["sid"] == 0 ? nullptr : this->server; + auto group_manager = target_server ? this->server->groups : serverInstance->getGroupManager().get(); - auto target_cldbid = cmd["cldbid"].as(); - if (!serverInstance->databaseHelper()->validClientDatabaseId(target_server, cmd["cldbid"])) return {findError("client_invalid_id"), "invalid cldbid"}; + auto target_cldbid = cmd["cldbid"].as(); + if (!serverInstance->databaseHelper()->validClientDatabaseId(target_server, cmd["cldbid"])) return {findError("client_invalid_id"), "invalid cldbid"}; - 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(!this->permission_granted(this->permissionValue(permission::i_client_permission_modify_power), needed_client_permission)) - return CommandResultPermissionError{permission::i_client_needed_permission_modify_power}; - } + 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(!this->permission_granted(this->permissionValue(permission::i_client_permission_modify_power), needed_client_permission)) + return CommandResultPermissionError{permission::i_client_needed_permission_modify_power}; + } - vector> target_groups; - vector> applied_groups; - target_groups.reserve(cmd.bulkCount()); + vector> target_groups; + vector> 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); + 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() && continue_on_error) - continue; + for(auto index = 0; index < cmd.bulkCount(); index++) { + auto group_id = cmd[index]["sgid"]; + if(!group_id.castable() && continue_on_error) + continue; - auto gid = group_id.as(); - auto group = group_manager->findGroup(gid); - if(!group) { - if(continue_on_error) - continue; + auto gid = group_id.as(); + 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)}; - } + return {findError("parameter_invalid"), "missing server group for id " + to_string(gid)}; + } - if(!target_server && group->target() != GroupTarget::GROUPTARGET_SERVER && group->type() != GroupType::GROUP_TYPE_QUERY) - return {findError("parameter_invalid"), "invalid server group type for id " + to_string(gid)}; + if(!target_server && group->target() != GroupTarget::GROUPTARGET_SERVER && group->type() != GroupType::GROUP_TYPE_QUERY) + return {findError("parameter_invalid"), "invalid server group type for id " + to_string(gid)}; - if(find(target_groups.begin(), target_groups.end(), group) != target_groups.end()) { - if(continue_on_error) - continue; + if(find(target_groups.begin(), target_groups.end(), group) != target_groups.end()) { + if(continue_on_error) + continue; - return {findError("parameter_invalid"), "duplicate server group for id " + to_string(gid)}; - } + 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; + /* 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}; - } + 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; + 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}; - } - } + return CommandResultPermissionError{permission::i_server_group_self_remove_power}; + } + } - target_groups.push_back(std::move(group)); - } - } + 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)}; - } + 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); - }}; + 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)}; - } + 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); - } - } + applied_groups.push_back(group); + group_manager->removeServerGroup(target_cldbid, group); + } + } - for(const auto& _server : target_server ? std::deque>{target_server} : serverInstance->getVoiceServerManager()->serverInstances()) { - for (const auto &targetClient : _server->findClientsByCldbId(target_cldbid)) { - if (_server->notifyClientPropertyUpdates(targetClient, _server->groups->update_server_group_property(targetClient, true, targetClient->getChannel()))) { - for (const auto &client : _server->getClients()) { - if(client->isClientVisible(targetClient, true) || client == targetClient) - for(const auto& group : applied_groups) - client->notifyServerGroupClientRemove(_this.lock(), targetClient, group); - } - if(targetClient->update_cached_permissions()) /* update cached calculated permissions */ - targetClient->sendNeededPermissions(false); /* cached permissions had changed, notify the client */ - targetClient->updateChannelClientProperties(true, true); - } - } - } + for(const auto& _server : target_server ? std::deque>{target_server} : serverInstance->getVoiceServerManager()->serverInstances()) { + for (const auto &targetClient : _server->findClientsByCldbId(target_cldbid)) { + if (_server->notifyClientPropertyUpdates(targetClient, _server->groups->update_server_group_property(targetClient, true, targetClient->getChannel()))) { + for (const auto &client : _server->getClients()) { + if(client->isClientVisible(targetClient, true) || client == targetClient) + for(const auto& group : applied_groups) + client->notifyServerGroupClientRemove(_this.lock(), targetClient, group); + } + if(targetClient->update_cached_permissions()) /* update cached calculated permissions */ + targetClient->sendNeededPermissions(false); /* cached permissions had changed, notify the client */ + targetClient->updateChannelClientProperties(true, true); + } + } + } - return CommandResult::Success; + return CommandResult::Success; } CommandResult ConnectedClient::handleCommandServerGroupPermList(Command &cmd) { @@ -3076,7 +3076,7 @@ CommandResult ConnectedClient::handleCommandServerGroupPermList(Command &cmd) { if (!serverGroup) return {findError("parameter_invalid"), "invalid server group id"}; if(this->getType() == ClientType::CLIENT_TEAMSPEAK && this->command_times.last_notify + this->command_times.notify_timeout < system_clock::now()) { - this->sendTSPermEditorWarning(); + this->sendTSPermEditorWarning(); } if (!this->notifyGroupPermList(serverGroup, cmd.hasParm("permsid"))) return {findError("database_empty_result"), "empty"}; @@ -3090,78 +3090,78 @@ CommandResult ConnectedClient::handleCommandServerGroupAddPerm(Command &cmd) { auto serverGroup = (this->server ? this->server->groups : serverInstance->getGroupManager().get())->findGroup(cmd["sgid"].as()); if (!serverGroup) return {findError("parameter_invalid"), "invalid server group id"}; if (serverGroup->target() != GROUPTARGET_SERVER) return {findError("parameter_invalid"), "invalid group type"}; - GROUP_PERMISSION_TEST(permission::i_server_group_modify_power, permission::i_server_group_needed_modify_power, serverGroup, true); + GROUP_PERMISSION_TEST(permission::i_server_group_modify_power, permission::i_server_group_needed_modify_power, serverGroup, true); - auto type = serverGroup->type(); - if(type == GroupType::GROUP_TYPE_QUERY) { - if(!this->permission_granted(this->cached_permission_value(permission::b_serverinstance_modify_querygroup), 1, true)) - return CommandResultPermissionError{permission::b_serverinstance_modify_querygroup}; - } else if(type == GroupType::GROUP_TYPE_TEMPLATE) { - if(!this->permission_granted(this->cached_permission_value(permission::b_serverinstance_modify_templates), 1, true)) - return CommandResultPermissionError{permission::b_serverinstance_modify_templates}; - } + auto type = serverGroup->type(); + if(type == GroupType::GROUP_TYPE_QUERY) { + if(!this->permission_granted(this->cached_permission_value(permission::b_serverinstance_modify_querygroup), 1, true)) + return CommandResultPermissionError{permission::b_serverinstance_modify_querygroup}; + } else if(type == GroupType::GROUP_TYPE_TEMPLATE) { + if(!this->permission_granted(this->cached_permission_value(permission::b_serverinstance_modify_templates), 1, true)) + return CommandResultPermissionError{permission::b_serverinstance_modify_templates}; + } - auto maxValue = this->getPermissionGrantValue(permission::PERMTEST_ORDERED, permission::i_permission_modify_power, this->currentChannel); - bool ignoreGrant = this->permission_granted(this->cached_permission_value(permission::b_permission_modify_power_ignore), 1); + auto maxValue = this->getPermissionGrantValue(permission::PERMTEST_ORDERED, permission::i_permission_modify_power, this->currentChannel); + bool ignoreGrant = this->permission_granted(this->cached_permission_value(permission::b_permission_modify_power_ignore), 1); bool conOnError = cmd[0].has("continueonerror"); bool checkTp = false; bool sgroupUpdate = false; auto permissions = serverGroup->permissions(); for (int index = 0; index < cmd.bulkCount(); index++) { - PARSE_PERMISSION(cmd); - //permvalue='1' permnegated='0' permskip='0' + PARSE_PERMISSION(cmd); + //permvalue='1' permnegated='0' permskip='0' auto val = cmd[index]["permvalue"].as(); - if(permission_require_granted_value(permType) && val > maxValue) { + if(permission_require_granted_value(permType) && val > maxValue) { if(conOnError) continue; return CommandResultPermissionError{permission::i_permission_modify_power}; } - if(!ignoreGrant && !this->permissionGrantGranted(permission::PERMTEST_ORDERED, permType, 1, this->currentChannel)) { + if(!ignoreGrant && !this->permissionGrantGranted(permission::PERMTEST_ORDERED, permType, 1, this->currentChannel)) { if(conOnError) continue; return CommandResultPermissionError{permission::i_permission_modify_power}; } - if (grant) { - permissions->set_permission(permType, {0, cmd[index]["permvalue"]}, permission::v2::PermissionUpdateType::do_nothing, permission::v2::PermissionUpdateType::set_value); - } else { - permissions->set_permission( - permType, - {cmd[index]["permvalue"], 0}, - permission::v2::PermissionUpdateType::set_value, - permission::v2::PermissionUpdateType::do_nothing, + if (grant) { + permissions->set_permission(permType, {0, cmd[index]["permvalue"]}, permission::v2::PermissionUpdateType::do_nothing, permission::v2::PermissionUpdateType::set_value); + } else { + permissions->set_permission( + permType, + {cmd[index]["permvalue"], 0}, + permission::v2::PermissionUpdateType::set_value, + permission::v2::PermissionUpdateType::do_nothing, - cmd[index]["permskip"].as() ? 1 : 0, - cmd[index]["permnegated"].as() ? 1 : 0 - ); - sgroupUpdate |= permission_is_group_property(permType); - checkTp |= permission_is_client_property(permType); - } + cmd[index]["permskip"].as() ? 1 : 0, + cmd[index]["permnegated"].as() ? 1 : 0 + ); + sgroupUpdate |= permission_is_group_property(permType); + checkTp |= permission_is_client_property(permType); + } } if(sgroupUpdate) - serverGroup->apply_properties_from_permissions(); + serverGroup->apply_properties_from_permissions(); //TODO may update for every server? if(this->server) { - auto lock = this->_this.lock(); - auto server = this->server; - threads::Thread([checkTp, sgroupUpdate, serverGroup, lock, server]() { - if(sgroupUpdate) - server->forEachClient([](shared_ptr cl) { - cl->notifyServerGroupList(); - }); - server->forEachClient([serverGroup, checkTp](shared_ptr cl) { - if (cl->serverGroupAssigned(serverGroup)) { - if(cl->update_cached_permissions()) /* update cached calculated permissions */ - cl->sendNeededPermissions(false); /* cached permissions had changed, notify the client */ - if (checkTp) - cl->updateChannelClientProperties(true, true); - cl->join_state_id++; /* join permission may changed, all channels need to be recalculate dif needed */ - } - }); - }).detach(); + auto lock = this->_this.lock(); + auto server = this->server; + threads::Thread([checkTp, sgroupUpdate, serverGroup, lock, server]() { + if(sgroupUpdate) + server->forEachClient([](shared_ptr cl) { + cl->notifyServerGroupList(); + }); + server->forEachClient([serverGroup, checkTp](shared_ptr cl) { + if (cl->serverGroupAssigned(serverGroup)) { + if(cl->update_cached_permissions()) /* update cached calculated permissions */ + cl->sendNeededPermissions(false); /* cached permissions had changed, notify the client */ + if (checkTp) + cl->updateChannelClientProperties(true, true); + cl->join_state_id++; /* join permission may changed, all channels need to be recalculate dif needed */ + } + }); + }).detach(); } return CommandResult::Success; } @@ -3173,65 +3173,65 @@ CommandResult ConnectedClient::handleCommandServerGroupDelPerm(Command &cmd) { auto serverGroup = (this->server ? this->server->groups : serverInstance->getGroupManager().get())->findGroup(cmd["sgid"].as()); if (!serverGroup) return {findError("parameter_invalid"), "invalid server group id"}; if (serverGroup->target() != GROUPTARGET_SERVER) return {findError("parameter_invalid"), "invalid group type"}; - GROUP_PERMISSION_TEST(permission::i_server_group_modify_power, permission::i_server_group_needed_modify_power, serverGroup, true); + GROUP_PERMISSION_TEST(permission::i_server_group_modify_power, permission::i_server_group_needed_modify_power, serverGroup, true); - auto type = serverGroup->type(); - if(type == GroupType::GROUP_TYPE_QUERY) { - if(!this->permission_granted(this->cached_permission_value(permission::b_serverinstance_modify_querygroup), 1, true)) - return CommandResultPermissionError{permission::b_serverinstance_modify_querygroup}; - } else if(type == GroupType::GROUP_TYPE_TEMPLATE) { - if(!this->permission_granted(this->cached_permission_value(permission::b_serverinstance_modify_templates), 1, true)) - return CommandResultPermissionError{permission::b_serverinstance_modify_templates}; - } + auto type = serverGroup->type(); + if(type == GroupType::GROUP_TYPE_QUERY) { + if(!this->permission_granted(this->cached_permission_value(permission::b_serverinstance_modify_querygroup), 1, true)) + return CommandResultPermissionError{permission::b_serverinstance_modify_querygroup}; + } else if(type == GroupType::GROUP_TYPE_TEMPLATE) { + if(!this->permission_granted(this->cached_permission_value(permission::b_serverinstance_modify_templates), 1, true)) + return CommandResultPermissionError{permission::b_serverinstance_modify_templates}; + } - bool ignoreGrant = this->permission_granted(this->cached_permission_value(permission::b_permission_modify_power_ignore), 1); + bool ignoreGrant = this->permission_granted(this->cached_permission_value(permission::b_permission_modify_power_ignore), 1); bool conOnError = cmd[0].has("continueonerror"); bool checkTp = false; auto sgroupUpdate = false; for (int index = 0; index < cmd.bulkCount(); index++) { - PARSE_PERMISSION(cmd); + PARSE_PERMISSION(cmd); - if(!ignoreGrant && !this->permissionGrantGranted(permission::PERMTEST_ORDERED, permType, 1, this->currentChannel)) { + if(!ignoreGrant && !this->permissionGrantGranted(permission::PERMTEST_ORDERED, permType, 1, this->currentChannel)) { if(conOnError) continue; return CommandResultPermissionError{permission::i_permission_modify_power}; } - if (grant) { - serverGroup->permissions()->set_permission(permType, permission::v2::empty_permission_values, permission::v2::PermissionUpdateType::do_nothing, permission::v2::PermissionUpdateType::delete_value); - } else { - serverGroup->permissions()->set_permission( - permType, - permission::v2::empty_permission_values, - permission::v2::PermissionUpdateType::delete_value, - permission::v2::PermissionUpdateType::do_nothing - ); - sgroupUpdate |= permission_is_group_property(permType); - checkTp |= permission_is_client_property(permType); - } + if (grant) { + serverGroup->permissions()->set_permission(permType, permission::v2::empty_permission_values, permission::v2::PermissionUpdateType::do_nothing, permission::v2::PermissionUpdateType::delete_value); + } else { + serverGroup->permissions()->set_permission( + permType, + permission::v2::empty_permission_values, + permission::v2::PermissionUpdateType::delete_value, + permission::v2::PermissionUpdateType::do_nothing + ); + sgroupUpdate |= permission_is_group_property(permType); + checkTp |= permission_is_client_property(permType); + } } - if(sgroupUpdate) - serverGroup->apply_properties_from_permissions(); + if(sgroupUpdate) + serverGroup->apply_properties_from_permissions(); if(this->server) { - auto lock = this->_this.lock(); - auto server = this->server; - threads::Thread([checkTp, sgroupUpdate, serverGroup, lock, server]() { - if(sgroupUpdate) - server->forEachClient([](shared_ptr cl) { - cl->notifyServerGroupList(); - }); - server->forEachClient([serverGroup, checkTp](shared_ptr cl) { - if (cl->serverGroupAssigned(serverGroup)) { + auto lock = this->_this.lock(); + auto server = this->server; + threads::Thread([checkTp, sgroupUpdate, serverGroup, lock, server]() { + if(sgroupUpdate) + server->forEachClient([](shared_ptr cl) { + cl->notifyServerGroupList(); + }); + server->forEachClient([serverGroup, checkTp](shared_ptr cl) { + if (cl->serverGroupAssigned(serverGroup)) { - if(cl->update_cached_permissions()) /* update cached calculated permissions */ - cl->sendNeededPermissions(false); /* cached permissions had changed, notify the client */ - if (checkTp) - cl->updateChannelClientProperties(true, true); - cl->join_state_id++; /* join permission may changed, all channels need to be recalculate dif needed */ - } - }); - }).detach(); + if(cl->update_cached_permissions()) /* update cached calculated permissions */ + cl->sendNeededPermissions(false); /* cached permissions had changed, notify the client */ + if (checkTp) + cl->updateChannelClientProperties(true, true); + cl->join_state_id++; /* join permission may changed, all channels need to be recalculate dif needed */ + } + }); + }).detach(); } return CommandResult::Success; @@ -3247,22 +3247,22 @@ CommandResult ConnectedClient::handleCommandServerGroupAutoAddPerm(ts::Command& deque> groups; for(const auto& group : group_manager->availableGroups(false)) { if(group->updateType() == cmd["sgtype"].as() && group->target() == GROUPTARGET_SERVER) { - if(group->permission_granted(permission::i_server_group_needed_modify_power, this->calculate_permission_value(permission::i_server_group_modify_power, 0), true)) { - auto type = group->type(); - if(type == GroupType::GROUP_TYPE_QUERY) { - if(!this->permissionGranted(permission::PERMTEST_ORDERED, permission::b_serverinstance_modify_querygroup, 1)) - continue; - } else if(type == GroupType::GROUP_TYPE_TEMPLATE) { - if(!this->permissionGranted(permission::PERMTEST_ORDERED, permission::b_serverinstance_modify_templates, 1)) - continue; - } - groups.push_back(group);//sgtype - } + if(group->permission_granted(permission::i_server_group_needed_modify_power, this->calculate_permission_value(permission::i_server_group_modify_power, 0), true)) { + auto type = group->type(); + if(type == GroupType::GROUP_TYPE_QUERY) { + if(!this->permissionGranted(permission::PERMTEST_ORDERED, permission::b_serverinstance_modify_querygroup, 1)) + continue; + } else if(type == GroupType::GROUP_TYPE_TEMPLATE) { + if(!this->permissionGranted(permission::PERMTEST_ORDERED, permission::b_serverinstance_modify_templates, 1)) + continue; + } + groups.push_back(group);//sgtype + } } } if(groups.empty()) - return CommandResult::Success; + return CommandResult::Success; auto maxValue = this->getPermissionGrantValue(permission::PERMTEST_ORDERED, permission::i_permission_modify_power, this->currentChannel); @@ -3286,81 +3286,81 @@ CommandResult ConnectedClient::handleCommandServerGroupAutoAddPerm(ts::Command& return CommandResultPermissionError{permission::i_permission_modify_power}; } for(const auto& serverGroup : groups) { - if (grant) { - serverGroup->permissions()->set_permission(permType, {0, cmd[index]["permvalue"]}, permission::v2::PermissionUpdateType::do_nothing, permission::v2::PermissionUpdateType::set_value); - } else { - serverGroup->permissions()->set_permission( - permType, - {cmd[index]["permvalue"], 0}, - permission::v2::PermissionUpdateType::set_value, - permission::v2::PermissionUpdateType::do_nothing, + if (grant) { + serverGroup->permissions()->set_permission(permType, {0, cmd[index]["permvalue"]}, permission::v2::PermissionUpdateType::do_nothing, permission::v2::PermissionUpdateType::set_value); + } else { + serverGroup->permissions()->set_permission( + permType, + {cmd[index]["permvalue"], 0}, + permission::v2::PermissionUpdateType::set_value, + permission::v2::PermissionUpdateType::do_nothing, - cmd[index]["permskip"].as() ? 1 : 0, - cmd[index]["permnegated"].as() ? 1 : 0 - ); - } + cmd[index]["permskip"].as() ? 1 : 0, + cmd[index]["permnegated"].as() ? 1 : 0 + ); + } } - sgroupUpdate |= permission_is_group_property(permType); + sgroupUpdate |= permission_is_group_property(permType); checkTp |= permission_is_client_property(permType); } - if(sgroupUpdate) - for(auto& group : groups) - group->apply_properties_from_permissions(); + if(sgroupUpdate) + for(auto& group : groups) + group->apply_properties_from_permissions(); auto lock = this->_this.lock(); if(ref_server) { - threads::Thread([checkTp, sgroupUpdate, groups, lock, ref_server]() { - if(sgroupUpdate) - ref_server->forEachClient([](shared_ptr cl) { - cl->notifyServerGroupList(); - }); - ref_server->forEachClient([groups, checkTp](shared_ptr cl) { - for(const auto& serverGroup : groups) { - if (cl->serverGroupAssigned(serverGroup)) { - if(cl->update_cached_permissions()) {/* update cached calculated permissions */ - cl->sendNeededPermissions(false); /* cached permissions had changed, notify the client */ - } - if (checkTp) { - cl->updateChannelClientProperties(true, true); - } - cl->join_state_id++; /* join permission may changed, all channels need to be recalculate if needed */ - break; - } - } - }); - }).detach(); + threads::Thread([checkTp, sgroupUpdate, groups, lock, ref_server]() { + if(sgroupUpdate) + ref_server->forEachClient([](shared_ptr cl) { + cl->notifyServerGroupList(); + }); + ref_server->forEachClient([groups, checkTp](shared_ptr cl) { + for(const auto& serverGroup : groups) { + if (cl->serverGroupAssigned(serverGroup)) { + if(cl->update_cached_permissions()) {/* update cached calculated permissions */ + cl->sendNeededPermissions(false); /* cached permissions had changed, notify the client */ + } + if (checkTp) { + cl->updateChannelClientProperties(true, true); + } + cl->join_state_id++; /* join permission may changed, all channels need to be recalculate if needed */ + break; + } + } + }); + }).detach(); } return CommandResult::Success; } CommandResult ConnectedClient::handleCommandServerGroupAutoDelPerm(ts::Command& cmd) { - CMD_RESET_IDLE; - CMD_CHK_AND_INC_FLOOD_POINTS(25); + CMD_RESET_IDLE; + CMD_CHK_AND_INC_FLOOD_POINTS(25); - auto ref_server = this->server; - auto group_manager = ref_server ? this->server->groups : &*serverInstance->getGroupManager(); + auto ref_server = this->server; + auto group_manager = ref_server ? this->server->groups : &*serverInstance->getGroupManager(); deque> groups; - for(const auto& group : group_manager->availableGroups(false)) { - if(group->updateType() == cmd["sgtype"].as() && group->target() == GROUPTARGET_SERVER) { - if(group->permission_granted(permission::i_server_group_needed_modify_power, this->calculate_permission_value(permission::i_server_group_modify_power, 0), true)) { - auto type = group->type(); - if(type == GroupType::GROUP_TYPE_QUERY) { - if(!this->permissionGranted(permission::PERMTEST_ORDERED, permission::b_serverinstance_modify_querygroup, 1)) - continue; - } else if(type == GroupType::GROUP_TYPE_TEMPLATE) { - if(!this->permissionGranted(permission::PERMTEST_ORDERED, permission::b_serverinstance_modify_templates, 1)) - continue; - } - groups.push_back(group);//sgtype - } - } - } + for(const auto& group : group_manager->availableGroups(false)) { + if(group->updateType() == cmd["sgtype"].as() && group->target() == GROUPTARGET_SERVER) { + if(group->permission_granted(permission::i_server_group_needed_modify_power, this->calculate_permission_value(permission::i_server_group_modify_power, 0), true)) { + auto type = group->type(); + if(type == GroupType::GROUP_TYPE_QUERY) { + if(!this->permissionGranted(permission::PERMTEST_ORDERED, permission::b_serverinstance_modify_querygroup, 1)) + continue; + } else if(type == GroupType::GROUP_TYPE_TEMPLATE) { + if(!this->permissionGranted(permission::PERMTEST_ORDERED, permission::b_serverinstance_modify_templates, 1)) + continue; + } + groups.push_back(group);//sgtype + } + } + } if(groups.empty()) return CommandResult::Success; - bool ignoreGrant = this->permissionGranted(permission::PERMTEST_ORDERED, permission::b_permission_modify_power_ignore, 1); + bool ignoreGrant = this->permissionGranted(permission::PERMTEST_ORDERED, permission::b_permission_modify_power_ignore, 1); bool conOnError = cmd[0].has("continueonerror"); bool checkTp = false; auto sgroupUpdate = false; @@ -3373,48 +3373,48 @@ CommandResult ConnectedClient::handleCommandServerGroupAutoDelPerm(ts::Command& } for(const auto& serverGroup : groups) { - if (grant) { - serverGroup->permissions()->set_permission(permType, permission::v2::empty_permission_values, permission::v2::PermissionUpdateType::do_nothing, permission::v2::PermissionUpdateType::delete_value); - } else { - serverGroup->permissions()->set_permission( - permType, - permission::v2::empty_permission_values, - permission::v2::PermissionUpdateType::delete_value, - permission::v2::PermissionUpdateType::do_nothing - ); - sgroupUpdate |= permission_is_group_property(permType); - } + if (grant) { + serverGroup->permissions()->set_permission(permType, permission::v2::empty_permission_values, permission::v2::PermissionUpdateType::do_nothing, permission::v2::PermissionUpdateType::delete_value); + } else { + serverGroup->permissions()->set_permission( + permType, + permission::v2::empty_permission_values, + permission::v2::PermissionUpdateType::delete_value, + permission::v2::PermissionUpdateType::do_nothing + ); + sgroupUpdate |= permission_is_group_property(permType); + } } checkTp |= permission_is_client_property(permType); } - if(sgroupUpdate) { - for(auto& group : groups) - group->apply_properties_from_permissions(); - } + if(sgroupUpdate) { + for(auto& group : groups) + group->apply_properties_from_permissions(); + } - if(ref_server) { - auto lock = this->_this.lock(); - threads::Thread([checkTp, sgroupUpdate, groups, lock, ref_server]() { - if(sgroupUpdate) - ref_server->forEachClient([](shared_ptr cl) { - cl->notifyServerGroupList(); - }); - ref_server->forEachClient([groups, checkTp](shared_ptr cl) { - for(const auto& serverGroup : groups) { - if (cl->serverGroupAssigned(serverGroup)) { - if(cl->update_cached_permissions()) /* update cached calculated permissions */ - cl->sendNeededPermissions(false); /* cached permissions had changed, notify the client */ - if (checkTp) - cl->updateChannelClientProperties(true, true); - cl->join_state_id++; /* join permission may changed, all channels need to be recalculate dif needed */ - break; - } - } - }); - }).detach(); - } + if(ref_server) { + auto lock = this->_this.lock(); + threads::Thread([checkTp, sgroupUpdate, groups, lock, ref_server]() { + if(sgroupUpdate) + ref_server->forEachClient([](shared_ptr cl) { + cl->notifyServerGroupList(); + }); + ref_server->forEachClient([groups, checkTp](shared_ptr cl) { + for(const auto& serverGroup : groups) { + if (cl->serverGroupAssigned(serverGroup)) { + if(cl->update_cached_permissions()) /* update cached calculated permissions */ + cl->sendNeededPermissions(false); /* cached permissions had changed, notify the client */ + if (checkTp) + cl->updateChannelClientProperties(true, true); + cl->join_state_id++; /* join permission may changed, all channels need to be recalculate dif needed */ + break; + } + } + }); + }).detach(); + } return CommandResult::Success; } @@ -3429,74 +3429,74 @@ CommandResult ConnectedClient::handleCommandSetClientChannelGroup(Command &cmd) serverGroup = this->server->groups->defaultGroup(GroupTarget::GROUPTARGET_CHANNEL); if (!serverGroup || serverGroup->target() != GROUPTARGET_CHANNEL) - return {findError("parameter_invalid"), "invalid channel group id"}; + return {findError("parameter_invalid"), "invalid channel group id"}; shared_lock server_channel_lock(this->server->channel_tree_lock); /* ensure we dont get moved or somebody could move us */ - auto channel_id = cmd["cid"].as(); + auto channel_id = cmd["cid"].as(); auto channel = this->server->channelTree->findChannel(channel_id); if (!channel) return {findError("channel_invalid_id"), "Cant resolve channel"}; auto target_cldbid = cmd["cldbid"].as(); { - auto channel_group_member_add_power = this->calculate_permission_value(permission::i_channel_group_member_add_power, channel_id); - if(!serverGroup->permission_granted(permission::i_channel_group_needed_member_add_power, channel_group_member_add_power, true)) { - if(target_cldbid != this->getClientDatabaseId()) - return CommandResultPermissionError{permission::i_channel_group_member_add_power}; + auto channel_group_member_add_power = this->calculate_permission_value(permission::i_channel_group_member_add_power, channel_id); + if(!serverGroup->permission_granted(permission::i_channel_group_needed_member_add_power, channel_group_member_add_power, true)) { + if(target_cldbid != this->getClientDatabaseId()) + return CommandResultPermissionError{permission::i_channel_group_member_add_power}; - auto channel_group_self_add_power = this->calculate_permission_value(permission::i_channel_group_self_add_power, channel_id); - if(!serverGroup->permission_granted(permission::i_channel_group_needed_member_add_power, channel_group_self_add_power, true)) - return CommandResultPermissionError{permission::i_channel_group_self_add_power}; - } + auto channel_group_self_add_power = this->calculate_permission_value(permission::i_channel_group_self_add_power, channel_id); + if(!serverGroup->permission_granted(permission::i_channel_group_needed_member_add_power, channel_group_self_add_power, true)) + return CommandResultPermissionError{permission::i_channel_group_self_add_power}; + } - auto client_permission_modify_power = this->calculate_permission_value(permission::i_client_permission_modify_power, channel_id); - auto client_needed_permission_modify_power = this->server->calculatePermission2( - permission::i_client_needed_permission_modify_power, target_cldbid, ClientType::CLIENT_TEAMSPEAK, channel_id); + auto client_permission_modify_power = this->calculate_permission_value(permission::i_client_permission_modify_power, channel_id); + auto client_needed_permission_modify_power = this->server->calculatePermission2( + permission::i_client_needed_permission_modify_power, target_cldbid, ClientType::CLIENT_TEAMSPEAK, channel_id); - if(client_needed_permission_modify_power.has_value) { - if(!this->permission_granted(client_permission_modify_power, client_needed_permission_modify_power.value, true)) - return CommandResultPermissionError{permission::i_client_permission_modify_power}; - } - } + if(client_needed_permission_modify_power.has_value) { + if(!this->permission_granted(client_permission_modify_power, client_needed_permission_modify_power.value, true)) + return CommandResultPermissionError{permission::i_client_permission_modify_power}; + } + } - { - auto old_group = this->server->groups->getChannelGroupExact(target_cldbid, channel, false); - if(old_group) { - auto channel_group_member_remove_power = this->calculate_permission_value(permission::i_channel_group_member_remove_power, channel_id); - if(!serverGroup->permission_granted(permission::i_channel_group_needed_member_remove_power, channel_group_member_remove_power, true)) { - if(target_cldbid != this->getClientDatabaseId()) - return CommandResultPermissionError{permission::i_channel_group_member_remove_power}; + { + auto old_group = this->server->groups->getChannelGroupExact(target_cldbid, channel, false); + if(old_group) { + auto channel_group_member_remove_power = this->calculate_permission_value(permission::i_channel_group_member_remove_power, channel_id); + if(!serverGroup->permission_granted(permission::i_channel_group_needed_member_remove_power, channel_group_member_remove_power, true)) { + if(target_cldbid != this->getClientDatabaseId()) + return CommandResultPermissionError{permission::i_channel_group_member_remove_power}; - auto channel_group_self_remove_power = this->calculate_permission_value(permission::i_channel_group_self_remove_power, channel_id); - if(!serverGroup->permission_granted(permission::i_channel_group_needed_member_remove_power, channel_group_self_remove_power, true)) - return CommandResultPermissionError{permission::i_channel_group_self_remove_power}; - } - } - } + auto channel_group_self_remove_power = this->calculate_permission_value(permission::i_channel_group_self_remove_power, channel_id); + if(!serverGroup->permission_granted(permission::i_channel_group_needed_member_remove_power, channel_group_self_remove_power, true)) + return CommandResultPermissionError{permission::i_channel_group_self_remove_power}; + } + } + } this->server->groups->setChannelGroup(target_cldbid, serverGroup, channel); for (const auto &targetClient : this->server->findClientsByCldbId(target_cldbid)) { - unique_lock client_channel_lock_w(targetClient->channel_lock); - auto updates = this->server->groups->update_server_group_property(targetClient, false, targetClient->getChannel()); /* needs a write lock */ - client_channel_lock_w.unlock(); - shared_lock client_channel_lock_r(targetClient->channel_lock); - auto result = this->server->notifyClientPropertyUpdates(targetClient, updates); + unique_lock client_channel_lock_w(targetClient->channel_lock); + auto updates = this->server->groups->update_server_group_property(targetClient, false, targetClient->getChannel()); /* needs a write lock */ + client_channel_lock_w.unlock(); + shared_lock client_channel_lock_r(targetClient->channel_lock); + auto result = this->server->notifyClientPropertyUpdates(targetClient, updates); if (result) { - if(targetClient->update_cached_permissions()) /* update cached calculated permissions */ - targetClient->sendNeededPermissions(false); /* cached permissions had changed, notify the client */ + if(targetClient->update_cached_permissions()) /* update cached calculated permissions */ + targetClient->sendNeededPermissions(false); /* cached permissions had changed, notify the client */ if(targetClient->properties()[property::CLIENT_CHANNEL_GROUP_INHERITED_CHANNEL_ID] == channel->channelId()) { //Only if group assigned over the channel - for (const auto &viewer : this->server->getClients()) { - /* if in view will be tested within that method */ - shared_lock viewer_channel_lock(viewer->channel_lock, defer_lock); - if(viewer != targetClient) - viewer_channel_lock.lock(); - viewer->notifyClientChannelGroupChanged(_this.lock(), targetClient, targetClient->getChannel(), channel, serverGroup, false); - } + for (const auto &viewer : this->server->getClients()) { + /* if in view will be tested within that method */ + shared_lock viewer_channel_lock(viewer->channel_lock, defer_lock); + if(viewer != targetClient) + viewer_channel_lock.lock(); + viewer->notifyClientChannelGroupChanged(_this.lock(), targetClient, targetClient->getChannel(), channel, serverGroup, false); + } } } - targetClient->updateChannelClientProperties(false, true); + targetClient->updateChannelClientProperties(false, true); } return CommandResult::Success; @@ -3513,69 +3513,69 @@ CommandResult ConnectedClient::handleCommandSendTextMessage(Command &cmd) { auto target = this->server->findClient(cmd["target"].as()); if (!target) return {findError("client_invalid_id"), "invalid target clid"}; - bool chat_open = false; - { + bool chat_open = false; + { - shared_lock channel_lock(this->channel_lock); - this->openChats.erase(remove_if(this->openChats.begin(), this->openChats.end(), [](const weak_ptr& weak) { return !weak.lock(); }), this->openChats.end()); - for(const auto& entry : this->openChats) { - if(entry.lock() == target) { - chat_open = true; - break; - } - } - } + shared_lock channel_lock(this->channel_lock); + this->openChats.erase(remove_if(this->openChats.begin(), this->openChats.end(), [](const weak_ptr& weak) { return !weak.lock(); }), this->openChats.end()); + for(const auto& entry : this->openChats) { + if(entry.lock() == target) { + chat_open = true; + break; + } + } + } - if(!chat_open) { - if (target == this) { - PERM_CHECK_CHANNELR(permission::b_client_even_textmessage_send, 1, this->currentChannel, true); - } + if(!chat_open) { + if (target == this) { + PERM_CHECK_CHANNELR(permission::b_client_even_textmessage_send, 1, this->currentChannel, true); + } - PERM_CHECK_CHANNELR(permission::i_client_private_textmessage_power, target->permissionValue(permission::PERMTEST_ORDERED, permission::i_client_needed_private_textmessage_power, target->currentChannel), this->currentChannel, false); + PERM_CHECK_CHANNELR(permission::i_client_private_textmessage_power, target->permissionValue(permission::PERMTEST_ORDERED, permission::i_client_needed_private_textmessage_power, target->currentChannel), this->currentChannel, false); - { - unique_lock channel_lock(target->channel_lock); - target->openChats.push_back(_this); - } + { + unique_lock channel_lock(target->channel_lock); + target->openChats.push_back(_this); + } - { - unique_lock channel_lock(this->channel_lock); - this->openChats.push_back(target); - } - } + { + unique_lock channel_lock(this->channel_lock); + this->openChats.push_back(target); + } + } - if(this->handleTextMessage(ChatMessageMode::TEXTMODE_PRIVATE, cmd["msg"], target)) return CommandResult::Success; + if(this->handleTextMessage(ChatMessageMode::TEXTMODE_PRIVATE, cmd["msg"], target)) return CommandResult::Success; target->notifyTextMessage(ChatMessageMode::TEXTMODE_PRIVATE, _this.lock(), target->getClientId(), 0, timestamp, cmd["msg"].string()); this->notifyTextMessage(ChatMessageMode::TEXTMODE_PRIVATE, _this.lock(), target->getClientId(), 0, timestamp, cmd["msg"].string()); } else if (cmd["targetmode"] == ChatMessageMode::TEXTMODE_CHANNEL) { if(!cmd[0].has("cid")) - cmd["cid"] = 0; + cmd["cid"] = 0; RESOLVE_CHANNEL_R(cmd["cid"], false); auto channel = l_channel ? dynamic_pointer_cast(l_channel->entry) : nullptr; if(!channel) { - CMD_REQ_CHANNEL; - channel = this->currentChannel; - channel_id = this->currentChannel->channelId(); + CMD_REQ_CHANNEL; + channel = this->currentChannel; + channel_id = this->currentChannel->channelId(); } if(channel == this->currentChannel) { - channel_tree_read_lock.unlock(); //Method may creates a music bot which modifies the channel tree - if(this->handleTextMessage(ChatMessageMode::TEXTMODE_CHANNEL, cmd["msg"], nullptr)) - return CommandResult::Success; - channel_tree_read_lock.lock(); + channel_tree_read_lock.unlock(); //Method may creates a music bot which modifies the channel tree + if(this->handleTextMessage(ChatMessageMode::TEXTMODE_CHANNEL, cmd["msg"], nullptr)) + return CommandResult::Success; + channel_tree_read_lock.lock(); } - if(!this->permissionGranted(permission::PERMTEST_ORDERED, permission::b_client_channel_textmessage_send, 1, channel, false)) - return CommandResultPermissionError{permission::b_client_channel_textmessage_send}; + if(!this->permissionGranted(permission::PERMTEST_ORDERED, permission::b_client_channel_textmessage_send, 1, channel, false)) + return CommandResultPermissionError{permission::b_client_channel_textmessage_send}; - bool conversation_private = channel->properties()[property::CHANNEL_FLAG_CONVERSATION_PRIVATE].as(); - if(channel != this->currentChannel) { - if(conversation_private) - return {findError("conversation_is_private")}; + bool conversation_private = channel->properties()[property::CHANNEL_FLAG_CONVERSATION_PRIVATE].as(); + if(channel != this->currentChannel) { + if(conversation_private) + return {findError("conversation_is_private")}; - if(!this->calculate_and_get_join_state(channel)) - return CommandResultPermissionError{permission::unknown}; /* You're not allowed to send messages :) */ - } + if(!this->calculate_and_get_join_state(channel)) + return CommandResultPermissionError{permission::unknown}; /* You're not allowed to send messages :) */ + } auto message = cmd["msg"].string(); auto _this = this->_this.lock(); @@ -3583,54 +3583,54 @@ CommandResult ConnectedClient::handleCommandSendTextMessage(Command &cmd) { auto flag_password = channel->properties()[property::CHANNEL_FLAG_PASSWORD].as(); for(const auto& client : this->server->getClients()) { - if(client->connectionState() != ConnectionState::CONNECTED) - continue; + if(client->connectionState() != ConnectionState::CONNECTED) + continue; - auto type = client->getType(); - if(type == ClientType::CLIENT_INTERNAL || type == ClientType::CLIENT_MUSIC) - continue; + auto type = client->getType(); + if(type == ClientType::CLIENT_INTERNAL || type == ClientType::CLIENT_MUSIC) + continue; - auto own_channel = client->currentChannel == this->currentChannel; - if(conversation_private && !own_channel) - continue; + auto own_channel = client->currentChannel == this->currentChannel; + if(conversation_private && !own_channel) + continue; - if(type != ClientType::CLIENT_TEAMSPEAK || own_channel) { - if(!own_channel && &*client != this) { - if(flag_password) - continue; /* TODO: Send notification about new message. The client then could request messages via message history */ + if(type != ClientType::CLIENT_TEAMSPEAK || own_channel) { + if(!own_channel && &*client != this) { + if(flag_password) + continue; /* TODO: Send notification about new message. The client then could request messages via message history */ if(!client->calculate_and_get_join_state(channel)) - continue; - } - client->notifyTextMessage(ChatMessageMode::TEXTMODE_CHANNEL, _this, client_id, channel_id, timestamp, message); - } + continue; + } + client->notifyTextMessage(ChatMessageMode::TEXTMODE_CHANNEL, _this, client_id, channel_id, timestamp, message); + } } if(!conversation_private) { - auto conversations = this->server->conversation_manager(); - auto conversation = conversations->get_or_create(channel->channelId()); - conversation->register_message(this->getClientDatabaseId(), this->getUid(), this->getDisplayName(), timestamp, cmd["msg"].string()); + auto conversations = this->server->conversation_manager(); + auto conversation = conversations->get_or_create(channel->channelId()); + conversation->register_message(this->getClientDatabaseId(), this->getUid(), this->getDisplayName(), timestamp, cmd["msg"].string()); } } else if (cmd["targetmode"] == ChatMessageMode::TEXTMODE_SERVER) { - CACHED_PERM_CHECK(permission::b_client_server_textmessage_send, 1); + CACHED_PERM_CHECK(permission::b_client_server_textmessage_send, 1); - if(this->handleTextMessage(ChatMessageMode::TEXTMODE_SERVER, cmd["msg"], nullptr)) return CommandResult::Success; - for(const auto& client : this->server->getClients()) { - if (client->connectionState() != ConnectionState::CONNECTED) - continue; + if(this->handleTextMessage(ChatMessageMode::TEXTMODE_SERVER, cmd["msg"], nullptr)) return CommandResult::Success; + for(const auto& client : this->server->getClients()) { + if (client->connectionState() != ConnectionState::CONNECTED) + continue; - auto type = client->getType(); - if (type == ClientType::CLIENT_INTERNAL || type == ClientType::CLIENT_MUSIC) - continue; + auto type = client->getType(); + if (type == ClientType::CLIENT_INTERNAL || type == ClientType::CLIENT_MUSIC) + continue; - client->notifyTextMessage(ChatMessageMode::TEXTMODE_SERVER, _this.lock(), this->getClientId(), 0, timestamp, cmd["msg"].string()); - } + client->notifyTextMessage(ChatMessageMode::TEXTMODE_SERVER, _this.lock(), this->getClientId(), 0, timestamp, cmd["msg"].string()); + } - { - auto conversations = this->server->conversation_manager(); - auto conversation = conversations->get_or_create(0); - conversation->register_message(this->getClientDatabaseId(), this->getUid(), this->getDisplayName(), timestamp, cmd["msg"].string()); - } + { + auto conversations = this->server->conversation_manager(); + auto conversation = conversations->get_or_create(0); + conversation->register_message(this->getClientDatabaseId(), this->getUid(), this->getDisplayName(), timestamp, cmd["msg"].string()); + } } else return {findError("parameter_invalid"), "invalid target mode"}; return CommandResult::Success; @@ -3654,17 +3654,17 @@ CommandResult ConnectedClient::handleCommandClientChatClosed(Command &cmd) { auto client = this->server->findClient(cmd["clid"].as()); if (!client) return {findError("client_invalid_id"), "invalid client id"}; { - unique_lock channel_lock(this->channel_lock); + unique_lock channel_lock(this->channel_lock); this->openChats.erase(remove_if(this->openChats.begin(), this->openChats.end(), [client](const weak_ptr& weak) { return weak.lock() == client; }), this->openChats.end()); } - { - unique_lock channel_lock(client->channel_lock); + { + unique_lock channel_lock(client->channel_lock); client->openChats.erase(remove_if(client->openChats.begin(), client->openChats.end(), [&](const weak_ptr& weak) { return weak.lock().get() == this; }), client->openChats.end()); - } + } client->notifyClientChatClosed(_this.lock()); return CommandResult::Success; } @@ -3679,15 +3679,15 @@ inline void cmd_filelist_append_files(ServerId sid, Command &command, vectorname, fileEntry->type == file::FileType::FILE ? "file" : "directory"); + logTrace(sid, " - {} ({})", fileEntry->name, fileEntry->type == file::FileType::FILE ? "file" : "directory"); - command[index]["name"] = fileEntry->name; - command[index]["datetime"] = std::chrono::duration_cast(fileEntry->lastChanged.time_since_epoch()).count(); - command[index]["type"] = fileEntry->type; + command[index]["name"] = fileEntry->name; + command[index]["datetime"] = std::chrono::duration_cast(fileEntry->lastChanged.time_since_epoch()).count(); + command[index]["type"] = fileEntry->type; if (fileEntry->type == file::FileType::FILE) - command[index]["size"] = static_pointer_cast(fileEntry)->fileSize; + command[index]["size"] = static_pointer_cast(fileEntry)->fileSize; else - command[index]["size"] = 0; + command[index]["size"] = 0; index++; } } @@ -3713,20 +3713,20 @@ CommandResult ConnectedClient::handleCommandFTGetFileList(Command &cmd) { auto channel = this->server->channelTree->findChannel(cmd["cid"].as()); if (!channel) return {findError("channel_invalid_id"), "Cant resolve channel"}; if (!channel->passwordMatch(cmd["cpw"]) && !this->permissionGranted(permission::PERMTEST_ORDERED, permission::b_ft_ignore_password, 1, channel, true)) - return cmd["cpw"].string().empty() ? CommandResultPermissionError{permission::b_ft_ignore_password} : CommandResult{findError("channel_invalid_password")}; - CHANNEL_PERMISSION_TEST(permission::i_ft_file_browse_power, permission::i_ft_needed_file_browse_power, channel, true); - cmd_filelist_append_files( - this->getServerId(), - fileList, - serverInstance->getFileServer()->listFiles(serverInstance->getFileServer()->resolveDirectory(this->server, channel, cmd["path"].as())) + return cmd["cpw"].string().empty() ? CommandResultPermissionError{permission::b_ft_ignore_password} : CommandResult{findError("channel_invalid_password")}; + CHANNEL_PERMISSION_TEST(permission::i_ft_file_browse_power, permission::i_ft_needed_file_browse_power, channel, true); + cmd_filelist_append_files( + this->getServerId(), + fileList, + serverInstance->getFileServer()->listFiles(serverInstance->getFileServer()->resolveDirectory(this->server, channel, cmd["path"].as())) ); } else { if (cmd["path"].as() == "/icons" || cmd["path"].as() == "/icons/") { - cmd_filelist_append_files(this->getServerId(), fileList, serverInstance->getFileServer()->listFiles(serverInstance->getFileServer()->iconDirectory(this->server))); - PERM_CHECKR(permission::b_icon_manage, 1, true); + cmd_filelist_append_files(this->getServerId(), fileList, serverInstance->getFileServer()->listFiles(serverInstance->getFileServer()->iconDirectory(this->server))); + PERM_CHECKR(permission::b_icon_manage, 1, true); } else if (cmd["path"].as() == "/") { - cmd_filelist_append_files(this->getServerId(), fileList, serverInstance->getFileServer()->listFiles(serverInstance->getFileServer()->avatarDirectory(this->server))); - PERM_CHECKR(permission::b_icon_manage, 1, true); + cmd_filelist_append_files(this->getServerId(), fileList, serverInstance->getFileServer()->listFiles(serverInstance->getFileServer()->avatarDirectory(this->server))); + PERM_CHECKR(permission::b_icon_manage, 1, true); } else { logMessage(this->getServerId(), "{} Requested file list for unknown path/name: path: {} name: {}", cmd["path"].string(), cmd["name"].string()); return CommandResult::NotImplemented; @@ -3734,14 +3734,14 @@ CommandResult ConnectedClient::handleCommandFTGetFileList(Command &cmd) { } if (fileList[0].has("name")) { - if(dynamic_cast(this)) { - dynamic_cast(this)->sendCommand0(fileList, false, true); /* We need to process this directly else the order could get shuffled up! */ - this->sendCommand(fileListFinished); - } else { - this->sendCommand(fileList); - if(this->getType() != CLIENT_QUERY) - this->sendCommand(fileListFinished); - } + if(dynamic_cast(this)) { + dynamic_cast(this)->sendCommand0(fileList, false, true); /* We need to process this directly else the order could get shuffled up! */ + this->sendCommand(fileListFinished); + } else { + this->sendCommand(fileList); + if(this->getType() != CLIENT_QUERY) + this->sendCommand(fileListFinished); + } return CommandResult::Success; } else { return {findError("database_empty_result"), "empty"}; @@ -3757,8 +3757,8 @@ CommandResult ConnectedClient::handleCommandFTCreateDir(Command &cmd) { auto channel = this->server->channelTree->findChannel(cmd["cid"].as()); if (!channel) return {findError("channel_invalid_id"), "Cant resolve channel"}; if (!channel->passwordMatch(cmd["cpw"]) && !this->permissionGranted(permission::PERMTEST_ORDERED, permission::b_ft_ignore_password, 1, channel, true)) - return cmd["cpw"].string().empty() ? CommandResultPermissionError{permission::b_ft_ignore_password} : CommandResult{findError("channel_invalid_password")}; - CHANNEL_PERMISSION_TEST(permission::i_ft_directory_create_power, permission::i_ft_needed_directory_create_power, channel, true); + return cmd["cpw"].string().empty() ? CommandResultPermissionError{permission::b_ft_ignore_password} : CommandResult{findError("channel_invalid_password")}; + CHANNEL_PERMISSION_TEST(permission::i_ft_directory_create_power, permission::i_ft_needed_directory_create_power, channel, true); auto dir = serverInstance->getFileServer()->createDirectory(cmd["dirname"], serverInstance->getFileServer()->resolveDirectory(this->server, channel, cmd["path"].as())); if (!dir) return {findError("file_invalid_path"), "could not create dir"}; @@ -3778,8 +3778,8 @@ CommandResult ConnectedClient::handleCommandFTDeleteFile(Command &cmd) { auto channel = this->server->channelTree->findChannel(cmd["cid"].as()); if (!channel) return {findError("channel_invalid_id"), "Cant resolve channel"}; if (!channel->passwordMatch(cmd["cpw"]) && !this->permissionGranted(permission::PERMTEST_ORDERED, permission::b_ft_ignore_password, 1, channel, true)) - return cmd["cpw"].string().empty() ? CommandResultPermissionError{permission::b_ft_ignore_password} : CommandResult{findError("channel_invalid_password")}; - CHANNEL_PERMISSION_TEST(permission::i_ft_file_delete_power, permission::i_ft_needed_file_delete_power, channel, true); + return cmd["cpw"].string().empty() ? CommandResultPermissionError{permission::b_ft_ignore_password} : CommandResult{findError("channel_invalid_password")}; + CHANNEL_PERMISSION_TEST(permission::i_ft_file_delete_power, permission::i_ft_needed_file_delete_power, channel, true); for (int index = 0; index < cmd.bulkCount(); index++) files.push_back(serverInstance->getFileServer()->findFile(cmd[index]["name"].as(), serverInstance->getFileServer()->resolveDirectory(this->server, channel))); } else { @@ -3807,7 +3807,7 @@ CommandResult ConnectedClient::handleCommandFTDeleteFile(Command &cmd) { } files.push_back(serverInstance->getFileServer()->findFile(cmd["name"].string(), serverInstance->getFileServer()->avatarDirectory(this->server))); } else { - logError(this->getServerId(), "Unknown requested file to delete: {}", cmd["path"].as()); + logError(this->getServerId(), "Unknown requested file to delete: {}", cmd["path"].as()); return CommandResult::NotImplemented; } } @@ -3815,7 +3815,7 @@ CommandResult ConnectedClient::handleCommandFTDeleteFile(Command &cmd) { for (const auto &file : files) { if (!file) continue; if (!serverInstance->getFileServer()->deleteFile(file)) { - logCritical(this->getServerId(), "Could not delete file {}/{}", file->path, file->name); + logCritical(this->getServerId(), "Could not delete file {}/{}", file->path, file->name); } } @@ -3831,18 +3831,18 @@ CommandResult ConnectedClient::handleCommandFTInitUpload(Command &cmd) { if (cmd[0].has("cid") && cmd["cid"] != 0) { //Channel auto channel = this->server->channelTree->findChannel(cmd["cid"].as()); if (!channel) return {findError("channel_invalid_id"), "Cant resolve channel"}; - if (!channel->passwordMatch(cmd["cpw"]) && !this->permissionGranted(permission::PERMTEST_ORDERED, permission::b_ft_ignore_password, 1, channel, true)) - return cmd["cpw"].string().empty() ? CommandResultPermissionError{permission::b_ft_ignore_password} : CommandResult{findError("channel_invalid_password")}; - CHANNEL_PERMISSION_TEST(permission::i_ft_file_upload_power, permission::i_ft_needed_file_upload_power, channel, true); + if (!channel->passwordMatch(cmd["cpw"]) && !this->permissionGranted(permission::PERMTEST_ORDERED, permission::b_ft_ignore_password, 1, channel, true)) + return cmd["cpw"].string().empty() ? CommandResultPermissionError{permission::b_ft_ignore_password} : CommandResult{findError("channel_invalid_password")}; + CHANNEL_PERMISSION_TEST(permission::i_ft_file_upload_power, permission::i_ft_needed_file_upload_power, channel, true); directory = serverInstance->getFileServer()->resolveDirectory(this->server, channel); } else { if (cmd["path"].as().empty() && cmd["name"].as().find("/icon_") == 0) { - auto max_size = this->permissionValue(permission::PERMTEST_ORDERED, permission::i_max_icon_filesize, this->currentChannel); - if(max_size != -1 && max_size < (ssize_t) cmd["size"].as()) return CommandResultPermissionError{permission::i_max_icon_filesize}; + auto max_size = this->permissionValue(permission::PERMTEST_ORDERED, permission::i_max_icon_filesize, this->currentChannel); + if(max_size != -1 && max_size < (ssize_t) cmd["size"].as()) return CommandResultPermissionError{permission::i_max_icon_filesize}; directory = serverInstance->getFileServer()->iconDirectory(this->server); } else if (cmd["path"].as().empty() && cmd["name"].string() == "/avatar") { - auto max_size = this->permissionValue(permission::PERMTEST_ORDERED, permission::i_client_max_avatar_filesize, this->currentChannel); - if(max_size != -1 && max_size < (ssize_t) cmd["size"].as()) return CommandResultPermissionError{permission::i_client_max_avatar_filesize}; + auto max_size = this->permissionValue(permission::PERMTEST_ORDERED, permission::i_client_max_avatar_filesize, this->currentChannel); + if(max_size != -1 && max_size < (ssize_t) cmd["size"].as()) return CommandResultPermissionError{permission::i_client_max_avatar_filesize}; directory = serverInstance->getFileServer()->avatarDirectory(this->server); cmd["name"] = "/avatar_" + this->getAvatarId(); @@ -3857,26 +3857,26 @@ CommandResult ConnectedClient::handleCommandFTInitUpload(Command &cmd) { return {findError("file_invalid_path"), "could not resolve directory"}; } - { - auto server_quota = this->server->properties()[property::VIRTUALSERVER_UPLOAD_QUOTA].as(); - auto server_used_quota = this->server->properties()[property::VIRTUALSERVER_MONTH_BYTES_UPLOADED].as(); - server_used_quota += cmd["size"].as(); - for(const auto& trans : serverInstance->getFileServer()->pending_file_transfers()) - server_used_quota += trans->size; - for(const auto& trans : serverInstance->getFileServer()->running_file_transfers()) - server_used_quota += trans->remaining_bytes(); - if(server_quota >= 0 && server_quota * 1024 * 1024 < (int64_t) server_used_quota) return {findError("file_transfer_server_quota_exceeded")}; + { + auto server_quota = this->server->properties()[property::VIRTUALSERVER_UPLOAD_QUOTA].as(); + auto server_used_quota = this->server->properties()[property::VIRTUALSERVER_MONTH_BYTES_UPLOADED].as(); + server_used_quota += cmd["size"].as(); + for(const auto& trans : serverInstance->getFileServer()->pending_file_transfers()) + server_used_quota += trans->size; + for(const auto& trans : serverInstance->getFileServer()->running_file_transfers()) + server_used_quota += trans->remaining_bytes(); + if(server_quota >= 0 && server_quota * 1024 * 1024 < (int64_t) server_used_quota) return {findError("file_transfer_server_quota_exceeded")}; - auto client_quota = this->permissionValue(permission::PERMTEST_ORDERED, permission::i_ft_quota_mb_upload_per_client, this->currentChannel); - auto client_used_quota = this->properties()[property::CLIENT_MONTH_BYTES_UPLOADED].as(); - client_used_quota += cmd["size"].as(); - for(const auto& trans : serverInstance->getFileServer()->pending_file_transfers(_this.lock())) - client_used_quota += trans->size; - for(const auto& trans : serverInstance->getFileServer()->running_file_transfers(_this.lock())) - client_used_quota += trans->remaining_bytes(); + auto client_quota = this->permissionValue(permission::PERMTEST_ORDERED, permission::i_ft_quota_mb_upload_per_client, this->currentChannel); + auto client_used_quota = this->properties()[property::CLIENT_MONTH_BYTES_UPLOADED].as(); + client_used_quota += cmd["size"].as(); + for(const auto& trans : serverInstance->getFileServer()->pending_file_transfers(_this.lock())) + client_used_quota += trans->size; + for(const auto& trans : serverInstance->getFileServer()->running_file_transfers(_this.lock())) + client_used_quota += trans->remaining_bytes(); - if(client_quota >= 0 && client_quota * 1024 * 1024 < (int64_t) client_used_quota) return {findError("file_transfer_client_quota_exceeded")}; - } + if(client_quota >= 0 && client_quota * 1024 * 1024 < (int64_t) client_used_quota) return {findError("file_transfer_client_quota_exceeded")}; + } if (cmd["overwrite"].as() && cmd["resume"].as()) return {findError("file_overwrite_excludes_resume"), "funny"}; if (serverInstance->getFileServer()->findFile(cmd["name"].as(), directory) && !(cmd["overwrite"].as() || cmd["resume"].as())) @@ -3892,22 +3892,22 @@ CommandResult ConnectedClient::handleCommandFTInitUpload(Command &cmd) { result["clientftfid"] = cmd["clientftfid"].as(); result["ftkey"] = key->key; - auto bindings = serverInstance->getFileServer()->list_bindings(); - if(!bindings.empty()) { - result["port"] = net::port(bindings[0]->address); - string ip = ""; - for(auto& entry : bindings) { - if(net::is_anybind(entry->address)) { - ip = ""; - break; - } - ip += net::to_string(entry->address, false) + ","; - } - if(!ip.empty()) - result["ip"] = ip; - } else { - return {findError("server_is_not_running"), "file server is not bound to any address"}; - } + auto bindings = serverInstance->getFileServer()->list_bindings(); + if(!bindings.empty()) { + result["port"] = net::port(bindings[0]->address); + string ip = ""; + for(auto& entry : bindings) { + if(net::is_anybind(entry->address)) { + ip = ""; + break; + } + ip += net::to_string(entry->address, false) + ","; + } + if(!ip.empty()) + result["ip"] = ip; + } else { + return {findError("server_is_not_running"), "file server is not bound to any address"}; + } result["seekpos"] = 0; result["proto"] = 1; result["serverftfid"] = key->key_id; //TODO generate! @@ -3928,9 +3928,9 @@ CommandResult ConnectedClient::handleCommandFTInitDownload(Command &cmd) { auto channel = this->server->channelTree->findChannel(cmd["cid"].as()); if (!channel) return {findError("channel_invalid_id"), "Cant resolve channel"}; - CHANNEL_PERMISSION_TEST(permission::i_ft_file_download_power, permission::i_ft_needed_file_download_power, channel, true); + CHANNEL_PERMISSION_TEST(permission::i_ft_file_download_power, permission::i_ft_needed_file_download_power, channel, true); if (!channel->passwordMatch(cmd["cpw"]) && !this->permissionGranted(permission::PERMTEST_ORDERED, permission::b_ft_ignore_password, 1, channel, true)) - return cmd["cpw"].string().empty() ? CommandResultPermissionError{permission::b_ft_ignore_password} : CommandResult{findError("channel_invalid_password")}; + return cmd["cpw"].string().empty() ? CommandResultPermissionError{permission::b_ft_ignore_password} : CommandResult{findError("channel_invalid_password")}; directory = serverInstance->getFileServer()->resolveDirectory(this->server, channel); } else { @@ -3956,27 +3956,27 @@ CommandResult ConnectedClient::handleCommandFTInitDownload(Command &cmd) { auto key = serverInstance->getFileServer()->generateDownloadTransferKey(error, directory->path + "/" + directory->name + cmd["name"].as(), 0, _this.lock()); //TODO implement resume! if (!key) return {findError("vs_critical"), "cant generate key (" + error + ")"}; - { - auto server_quota = this->server->properties()[property::VIRTUALSERVER_DOWNLOAD_QUOTA].as(); - auto server_used_quota = this->server->properties()[property::VIRTUALSERVER_MONTH_BYTES_DOWNLOADED].as(); - server_used_quota += key->size; - for(const auto& trans : serverInstance->getFileServer()->pending_file_transfers()) - server_used_quota += trans->size; - for(const auto& trans : serverInstance->getFileServer()->running_file_transfers()) - server_used_quota += trans->remaining_bytes(); - if(server_quota >= 0 && server_quota * 1024 * 1024 < (int64_t) server_used_quota) return {findError("file_transfer_server_quota_exceeded")}; + { + auto server_quota = this->server->properties()[property::VIRTUALSERVER_DOWNLOAD_QUOTA].as(); + auto server_used_quota = this->server->properties()[property::VIRTUALSERVER_MONTH_BYTES_DOWNLOADED].as(); + server_used_quota += key->size; + for(const auto& trans : serverInstance->getFileServer()->pending_file_transfers()) + server_used_quota += trans->size; + for(const auto& trans : serverInstance->getFileServer()->running_file_transfers()) + server_used_quota += trans->remaining_bytes(); + if(server_quota >= 0 && server_quota * 1024 * 1024 < (int64_t) server_used_quota) return {findError("file_transfer_server_quota_exceeded")}; - auto client_quota = this->permissionValue(permission::PERMTEST_ORDERED, permission::i_ft_quota_mb_download_per_client, this->currentChannel); - auto client_used_quota = this->properties()[property::CLIENT_MONTH_BYTES_DOWNLOADED].as(); - client_used_quota += key->size; - for(const auto& trans : serverInstance->getFileServer()->pending_file_transfers(_this.lock())) - client_used_quota += trans->size; - for(const auto& trans : serverInstance->getFileServer()->running_file_transfers(_this.lock())) - client_used_quota += trans->remaining_bytes(); + auto client_quota = this->permissionValue(permission::PERMTEST_ORDERED, permission::i_ft_quota_mb_download_per_client, this->currentChannel); + auto client_used_quota = this->properties()[property::CLIENT_MONTH_BYTES_DOWNLOADED].as(); + client_used_quota += key->size; + for(const auto& trans : serverInstance->getFileServer()->pending_file_transfers(_this.lock())) + client_used_quota += trans->size; + for(const auto& trans : serverInstance->getFileServer()->running_file_transfers(_this.lock())) + client_used_quota += trans->remaining_bytes(); - if(client_quota >= 0 && client_quota * 1024 * 1024 < (int64_t) client_used_quota) return {findError("file_transfer_client_quota_exceeded")}; - } + if(client_quota >= 0 && client_quota * 1024 * 1024 < (int64_t) client_used_quota) return {findError("file_transfer_client_quota_exceeded")}; + } Command result(this->getExternalType() == CLIENT_TEAMSPEAK ? "notifystartdownload" : ""); result["clientftfid"] = cmd["clientftfid"].as(); @@ -3984,22 +3984,22 @@ CommandResult ConnectedClient::handleCommandFTInitDownload(Command &cmd) { result["serverftfid"] = key->key_id; result["ftkey"] = key->key; - auto bindings = serverInstance->getFileServer()->list_bindings(); - if(!bindings.empty()) { - result["port"] = net::port(bindings[0]->address); - string ip = ""; - for(auto& entry : bindings) { - if(net::is_anybind(entry->address)) { - ip = ""; - break; - } - ip += net::to_string(entry->address, false) + ","; - } - if(!ip.empty()) - result["ip"] = ip; - } else { - return {findError("server_is_not_running"), "file server is not bound to any address"}; - } + auto bindings = serverInstance->getFileServer()->list_bindings(); + if(!bindings.empty()) { + result["port"] = net::port(bindings[0]->address); + string ip = ""; + for(auto& entry : bindings) { + if(net::is_anybind(entry->address)) { + ip = ""; + break; + } + ip += net::to_string(entry->address, false) + ","; + } + if(!ip.empty()) + result["ip"] = ip; + } else { + return {findError("server_is_not_running"), "file server is not bound to any address"}; + } result["size"] = key->size; this->sendCommand(result); @@ -4026,57 +4026,57 @@ Example: */ CommandResult ConnectedClient::handleCommandFTGetFileInfo(ts::Command &cmd) { - CMD_REQ_SERVER; - CMD_REQ_FSERVER; - CMD_RESET_IDLE; + CMD_REQ_SERVER; + CMD_REQ_FSERVER; + CMD_RESET_IDLE; - Command result(this->getExternalType() == CLIENT_TEAMSPEAK ? "notifyfileinfo" : ""); + Command result(this->getExternalType() == CLIENT_TEAMSPEAK ? "notifyfileinfo" : ""); - int result_index = 0; - for(int index = 0; index < cmd.bulkCount(); index++) { - auto& request = cmd[index]; - std::shared_ptr file; - if (request.has("cid") && request["cid"].as() != 0) { //Channel - auto channel = this->server->channelTree->findChannel(request["cid"].as()); - if (!channel) return {findError("channel_invalid_id"), "Cant resolve channel"}; - if (!channel->passwordMatch(cmd["cpw"]) && !this->permissionGranted(permission::PERMTEST_ORDERED, permission::b_ft_ignore_password, 1, channel, true)) - return cmd["cpw"].string().empty() ? CommandResultPermissionError{permission::b_ft_ignore_password} : CommandResult{findError("channel_invalid_password"), ""}; + int result_index = 0; + for(int index = 0; index < cmd.bulkCount(); index++) { + auto& request = cmd[index]; + std::shared_ptr file; + if (request.has("cid") && request["cid"].as() != 0) { //Channel + auto channel = this->server->channelTree->findChannel(request["cid"].as()); + if (!channel) return {findError("channel_invalid_id"), "Cant resolve channel"}; + if (!channel->passwordMatch(cmd["cpw"]) && !this->permissionGranted(permission::PERMTEST_ORDERED, permission::b_ft_ignore_password, 1, channel, true)) + return cmd["cpw"].string().empty() ? CommandResultPermissionError{permission::b_ft_ignore_password} : CommandResult{findError("channel_invalid_password"), ""}; - CHANNEL_PERMISSION_TEST(permission::i_ft_file_browse_power, permission::i_ft_needed_file_browse_power, channel, true); - file = serverInstance->getFileServer()->findFile(request["name"],serverInstance->getFileServer()->resolveDirectory(this->server, channel, request["path"])); - } else { - std::shared_ptr directory; - if (!request.has("path") || request["path"].as() == "/") { - directory = serverInstance->getFileServer()->avatarDirectory(this->server); - } else if (request["path"].as() == "/icons" || request["path"].as() == "/icons/") { - directory = serverInstance->getFileServer()->iconDirectory(this->server); - } else { - cerr << "Unknown requested directory: '" << request["path"].as() << "'" << endl; - return CommandResult::NotImplemented; - } + CHANNEL_PERMISSION_TEST(permission::i_ft_file_browse_power, permission::i_ft_needed_file_browse_power, channel, true); + file = serverInstance->getFileServer()->findFile(request["name"],serverInstance->getFileServer()->resolveDirectory(this->server, channel, request["path"])); + } else { + std::shared_ptr directory; + if (!request.has("path") || request["path"].as() == "/") { + directory = serverInstance->getFileServer()->avatarDirectory(this->server); + } else if (request["path"].as() == "/icons" || request["path"].as() == "/icons/") { + directory = serverInstance->getFileServer()->iconDirectory(this->server); + } else { + cerr << "Unknown requested directory: '" << request["path"].as() << "'" << endl; + return CommandResult::NotImplemented; + } - if(!directory) continue; - file = serverInstance->getFileServer()->findFile(cmd["name"].as(), directory); - } - if(!file) continue; + if(!directory) continue; + file = serverInstance->getFileServer()->findFile(cmd["name"].as(), directory); + } + if(!file) continue; - result[result_index]["cid"] = request["cid"].as(); - result[result_index]["name"] = request["name"]; - result[result_index]["path"] = request["path"]; - result[result_index]["type"] = file->type; - result[result_index]["datetime"] = duration_cast(file->lastChanged.time_since_epoch()).count(); - if (file->type == file::FileType::FILE) - result[result_index]["size"] = static_pointer_cast(file)->fileSize; - else - result[result_index]["size"] = 0; - result_index++; - } + result[result_index]["cid"] = request["cid"].as(); + result[result_index]["name"] = request["name"]; + result[result_index]["path"] = request["path"]; + result[result_index]["type"] = file->type; + result[result_index]["datetime"] = duration_cast(file->lastChanged.time_since_epoch()).count(); + if (file->type == file::FileType::FILE) + result[result_index]["size"] = static_pointer_cast(file)->fileSize; + else + result[result_index]["size"] = 0; + result_index++; + } - if(result_index == 0) - return {findError("database_empty_result")}; - this->sendCommand(result); + if(result_index == 0) + return {findError("database_empty_result")}; + this->sendCommand(result); - return CommandResult::Success; + return CommandResult::Success; } //notifybanlist banid=3 ip name uid=zbex8X3bFRTIKLI7mzeyJGZsh64= lastnickname=Wolf\sC++\sXXXX created=1510357269 duration=3600 invokername=WolverinDEV invokercldbid=5 invokeruid=xxjnc14LmvTk+Lyrm8OOeo4tOqw= reason=Prefix\sFake\s\p\sName enforcements=3 CommandResult ConnectedClient::handleCommandBanList(Command &cmd) { @@ -4088,7 +4088,7 @@ CommandResult ConnectedClient::handleCommandBanList(Command &cmd) { sid = cmd["sid"]; if (sid == 0) { - if(!this->permission_granted(this->cached_permission_value(permission::b_client_ban_list_global), 1)) + if(!this->permission_granted(this->cached_permission_value(permission::b_client_ban_list_global), 1)) return CommandResultPermissionError{permission::b_client_ban_list_global}; } else { auto server = serverInstance->getVoiceServerManager()->findServerById(sid); @@ -4114,7 +4114,7 @@ CommandResult ConnectedClient::handleCommandBanList(Command &cmd) { notify[index]["ip"] = "hidden"; notify[index]["name"] = elm->name; notify[index]["uid"] = elm->uid; - notify[index]["hwid"] = elm->hwid; + notify[index]["hwid"] = elm->hwid; notify[index]["lastnickname"] = elm->name; //Maybe update? notify[index]["created"] = chrono::duration_cast(elm->created.time_since_epoch()).count(); @@ -4142,17 +4142,17 @@ CommandResult ConnectedClient::handleCommandBanAdd(Command &cmd) { string ip = cmd[0].has("ip") ? cmd["ip"].string() : ""; string name = cmd[0].has("name") ? cmd["name"].string() : ""; string uid = cmd[0].has("uid") ? cmd["uid"].string() : ""; - string hwid = cmd[0].has("hwid") ? cmd["hwid"].string() : ""; + string hwid = cmd[0].has("hwid") ? cmd["hwid"].string() : ""; string banreason = cmd[0].has("banreason") ? cmd["banreason"].string() : "No reason set"; auto time = cmd[0].has("time") ? cmd["time"].as() : 0L; - ServerId sid = this->getServerId(); - if (cmd[0].has("sid")) - sid = cmd["sid"]; + ServerId sid = this->getServerId(); + if (cmd[0].has("sid")) + sid = cmd["sid"]; if (sid == 0) { - if(!this->permission_granted(this->cached_permission_value(permission::b_client_ban_create_global), 1)) - return CommandResultPermissionError{permission::b_client_ban_create_global}; + if(!this->permission_granted(this->cached_permission_value(permission::b_client_ban_create_global), 1)) + return CommandResultPermissionError{permission::b_client_ban_create_global}; } else { auto server = serverInstance->getVoiceServerManager()->findServerById(sid); if (!server) return {findError("parameter_invalid"), ""}; @@ -4171,21 +4171,21 @@ CommandResult ConnectedClient::handleCommandBanAdd(Command &cmd) { auto existing = serverInstance->banManager()->findBanExact(sid, banreason, uid, ip, name, hwid); bool banned = false; if(existing) { - if(existing->invokerDbId == this->getClientDatabaseId()) { - if(existing->until == until) return {findError("database_duplicate_entry")}; - else { - existing->until = until; - serverInstance->banManager()->updateBan(existing); - banned = true; - } - } else if(!banned) { - serverInstance->banManager()->unban(existing); - } + if(existing->invokerDbId == this->getClientDatabaseId()) { + if(existing->until == until) return {findError("database_duplicate_entry")}; + else { + existing->until = until; + serverInstance->banManager()->updateBan(existing); + banned = true; + } + } else if(!banned) { + serverInstance->banManager()->unban(existing); + } } if(!banned) serverInstance->banManager()->registerBan(sid, this->getClientDatabaseId(), banreason, uid, ip, name, hwid, until); - for(auto server : (this->server ? std::deque>{this->server} : serverInstance->getVoiceServerManager()->serverInstances())) - server->testBanStateChange(_this.lock()); + for(auto server : (this->server ? std::deque>{this->server} : serverInstance->getVoiceServerManager()->serverInstances())) + server->testBanStateChange(_this.lock()); return CommandResult::Success; } @@ -4193,16 +4193,16 @@ CommandResult ConnectedClient::handleCommandBanEdit(Command &cmd) { CMD_RESET_IDLE; CMD_CHK_AND_INC_FLOOD_POINTS(25); - ServerId sid = this->getServerId(); - if (cmd[0].has("sid")) - sid = cmd["sid"]; + ServerId sid = this->getServerId(); + if (cmd[0].has("sid")) + sid = cmd["sid"]; auto ban = serverInstance->banManager()->findBanById(sid, cmd["banid"].as()); if (!ban) return {findError("database_empty_result"), "empty"}; if (sid == 0) { - if(!this->permission_granted(this->cached_permission_value(permission::b_client_ban_edit_global), 1)) - return CommandResultPermissionError{permission::b_client_ban_edit_global}; + if(!this->permission_granted(this->cached_permission_value(permission::b_client_ban_edit_global), 1)) + return CommandResultPermissionError{permission::b_client_ban_edit_global}; } else { auto server = serverInstance->getVoiceServerManager()->findServerById(sid); if (!server) return {findError("parameter_invalid"), ""}; @@ -4231,20 +4231,20 @@ CommandResult ConnectedClient::handleCommandBanEdit(Command &cmd) { ban->reason = cmd["reason"].as(); changed |= true; } - if (cmd[0].has("banreason")) { - ban->reason = cmd["banreason"].as(); - changed |= true; - } + if (cmd[0].has("banreason")) { + ban->reason = cmd["banreason"].as(); + changed |= true; + } if (cmd[0].has("time")) { ban->until = ban->created + seconds(cmd["time"].as()); changed |= true; } - if (cmd[0].has("hwid")) { - ban->hwid = cmd["hwid"].as(); - changed |= true; - } + if (cmd[0].has("hwid")) { + ban->hwid = cmd["hwid"].as(); + changed |= true; + } if (changed) serverInstance->banManager()->updateBan(ban); @@ -4265,23 +4265,23 @@ CommandResult ConnectedClient::handleCommandBanClient(Command &cmd) { chrono::time_point until = time > 0 ? chrono::system_clock::now() + chrono::seconds(time) : chrono::time_point(); const auto no_nickname = cmd.hasParm("no-nickname"); - const auto no_hwid = cmd.hasParm("no-hardware-id"); - const auto no_ip = cmd.hasParm("no-ip"); + const auto no_hwid = cmd.hasParm("no-hardware-id"); + const auto no_ip = cmd.hasParm("no-ip"); deque> target_clients; if (cmd[0].has("uid")) { target_clients = this->server->findClientsByUid(uid = cmd["uid"].string()); for(const auto& client : target_clients) - if(client->getType() == ClientType::CLIENT_MUSIC) - return {findError("client_invalid_id"), "You cant ban a music bot!"}; + if(client->getType() == ClientType::CLIENT_MUSIC) + return {findError("client_invalid_id"), "You cant ban a music bot!"}; } else { target_clients = {this->server->findClient(cmd["clid"].as())}; if(!target_clients[0]) { - return {findError("client_invalid_id"), "Could not find target client"}; + return {findError("client_invalid_id"), "Could not find target client"}; + } + if(target_clients[0]->getType() == ClientType::CLIENT_MUSIC) { + return {findError("client_invalid_id"), "You cant ban a music bot!"}; } - if(target_clients[0]->getType() == ClientType::CLIENT_MUSIC) { - return {findError("client_invalid_id"), "You cant ban a music bot!"}; - } uid = target_clients[0]->getUid(); } @@ -4292,61 +4292,61 @@ CommandResult ConnectedClient::handleCommandBanClient(Command &cmd) { if (!info.empty()) target_dbid = info[0]->cldbid; else - return {findError("client_unknown")}; + return {findError("client_unknown")}; } if (target_dbid != 0) { if (this->server->calculatePermission(permission::PERMTEST_ORDERED, target_dbid, permission::i_client_needed_ban_power, ClientType::CLIENT_TEAMSPEAK, nullptr) > this->permissionValue(permission::PERMTEST_ORDERED, permission::i_client_ban_power)) return CommandResultPermissionError(permission::i_client_ban_power); if (this->server->calculatePermission(permission::PERMTEST_ORDERED, target_dbid, permission::b_client_ignore_bans, ClientType::CLIENT_TEAMSPEAK, nullptr) >= 1) return CommandResultPermissionError(permission::b_client_ignore_bans); } - deque ban_ids; + deque ban_ids; auto _id = serverInstance->banManager()->registerBan(this->getServer()->getServerId(), this->getClientDatabaseId(), reason, uid, "", "", "", until); - ban_ids.push_back(_id); + ban_ids.push_back(_id); - auto b_ban_name = this->cached_permission_value(permission::b_client_ban_name) == permNotGranted || this->permission_granted(this->cached_permission_value(permission::b_client_ban_name), 1, false); - auto b_ban_ip = this->cached_permission_value(permission::b_client_ban_ip) == permNotGranted || this->permission_granted(this->cached_permission_value(permission::b_client_ban_ip), 1, false); - auto b_ban_hwid = this->cached_permission_value(permission::b_client_ban_hwid) == permNotGranted || this->permission_granted(this->cached_permission_value(permission::b_client_ban_hwid), 1, false); + auto b_ban_name = this->cached_permission_value(permission::b_client_ban_name) == permNotGranted || this->permission_granted(this->cached_permission_value(permission::b_client_ban_name), 1, false); + auto b_ban_ip = this->cached_permission_value(permission::b_client_ban_ip) == permNotGranted || this->permission_granted(this->cached_permission_value(permission::b_client_ban_ip), 1, false); + auto b_ban_hwid = this->cached_permission_value(permission::b_client_ban_hwid) == permNotGranted || this->permission_granted(this->cached_permission_value(permission::b_client_ban_hwid), 1, false); - auto max_value = this->cached_permission_value(permission::i_client_ban_max_bantime); - if(max_value != permNotGranted && max_value != -1) { - if(time > max_value) return CommandResultPermissionError{permission::i_client_ban_max_bantime}; - if(time == 0) return CommandResultPermissionError{permission::i_client_ban_max_bantime}; - } + auto max_value = this->cached_permission_value(permission::i_client_ban_max_bantime); + if(max_value != permNotGranted && max_value != -1) { + if(time > max_value) return CommandResultPermissionError{permission::i_client_ban_max_bantime}; + if(time == 0) return CommandResultPermissionError{permission::i_client_ban_max_bantime}; + } for (const auto &client : target_clients) { if (client->getType() != CLIENT_TEAMSPEAK && client->getType() != CLIENT_QUERY) continue; //Remember if you add new type you have to change stuff here this->server->notify_client_ban(client, this->ref(), reason, time); - client->closeConnection(system_clock::now() + seconds(2)); + client->closeConnection(system_clock::now() + seconds(2)); - string entry_name, entry_ip, entry_hardware_id; - if(b_ban_name && !no_nickname) { - entry_name = client->getDisplayName(); - } - if(b_ban_ip && !no_ip && !config::server::disable_ip_saving) { - entry_ip = client->getPeerIp(); - } - if(b_ban_hwid && !no_hwid) { - entry_hardware_id = client->getHardwareId(); - } - auto exact = serverInstance->banManager()->findBanExact(this->getServer()->getServerId(), reason, "", entry_ip, entry_name, entry_hardware_id); - if(exact) { - exact->until = until; - exact->invokerDbId = this->getClientDatabaseId(); - serverInstance->banManager()->updateBan(exact); - ban_ids.push_back(exact->banId); - } else { - auto id = serverInstance->banManager()->registerBan(this->getServer()->getServerId(), this->getClientDatabaseId(), reason, "", entry_ip, entry_name, entry_hardware_id, until); - ban_ids.push_back(id); - } + string entry_name, entry_ip, entry_hardware_id; + if(b_ban_name && !no_nickname) { + entry_name = client->getDisplayName(); + } + if(b_ban_ip && !no_ip && !config::server::disable_ip_saving) { + entry_ip = client->getPeerIp(); + } + if(b_ban_hwid && !no_hwid) { + entry_hardware_id = client->getHardwareId(); + } + auto exact = serverInstance->banManager()->findBanExact(this->getServer()->getServerId(), reason, "", entry_ip, entry_name, entry_hardware_id); + if(exact) { + exact->until = until; + exact->invokerDbId = this->getClientDatabaseId(); + serverInstance->banManager()->updateBan(exact); + ban_ids.push_back(exact->banId); + } else { + auto id = serverInstance->banManager()->registerBan(this->getServer()->getServerId(), this->getClientDatabaseId(), reason, "", entry_ip, entry_name, entry_hardware_id, until); + ban_ids.push_back(id); + } } - this->server->testBanStateChange(_this.lock()); + this->server->testBanStateChange(_this.lock()); if (this->getType() == CLIENT_QUERY) { Command notify(""); int index = 0; for(const auto& ban_id : ban_ids) - notify[index++]["banid"] = ban_id; + notify[index++]["banid"] = ban_id; this->sendCommand(notify); } @@ -4357,17 +4357,17 @@ CommandResult ConnectedClient::handleCommandBanDel(Command &cmd) { CMD_RESET_IDLE; CMD_CHK_AND_INC_FLOOD_POINTS(5); - ServerId sid = this->getServerId(); - if (cmd[0].has("sid")) - sid = cmd["sid"]; + ServerId sid = this->getServerId(); + if (cmd[0].has("sid")) + sid = cmd["sid"]; auto ban = serverInstance->banManager()->findBanById(sid, cmd["banid"].as()); if (!ban) return {findError("database_empty_result"), "empty"}; if (sid == 0) { - const auto permission = ban->invokerDbId == this->getClientDatabaseId() ? permission::b_client_ban_delete_own_global : permission::b_client_ban_delete_global; - if(!this->permission_granted(this->cached_permission_value(permission), 1)) - return CommandResultPermissionError{permission}; + const auto permission = ban->invokerDbId == this->getClientDatabaseId() ? permission::b_client_ban_delete_own_global : permission::b_client_ban_delete_global; + if(!this->permission_granted(this->cached_permission_value(permission), 1)) + return CommandResultPermissionError{permission}; } else { auto server = serverInstance->getVoiceServerManager()->findServerById(sid); if (!server) return {findError("parameter_invalid"), ""}; @@ -4390,37 +4390,37 @@ CommandResult ConnectedClient::handleCommandBanDelAll(Command &cmd) { } CommandResult ConnectedClient::handleCommandBanTriggerList(ts::Command &cmd) { - CMD_REQ_SERVER; - CMD_RESET_IDLE; - CMD_CHK_AND_INC_FLOOD_POINTS(25); - CACHED_PERM_CHECK(permission::b_client_ban_trigger_list, 1, true); + CMD_REQ_SERVER; + CMD_RESET_IDLE; + CMD_CHK_AND_INC_FLOOD_POINTS(25); + CACHED_PERM_CHECK(permission::b_client_ban_trigger_list, 1, true); - CMD_REQ_PARM("banid"); + CMD_REQ_PARM("banid"); - auto record = serverInstance->banManager()->findBanById(this->getServerId(), cmd["banid"]); - if(!record) return {findError("parameter_invalid"), "Invalid ban id"}; + auto record = serverInstance->banManager()->findBanById(this->getServerId(), cmd["banid"]); + if(!record) return {findError("parameter_invalid"), "Invalid ban id"}; - Command notify(this->getExternalType() == CLIENT_TEAMSPEAK ? "notifybantriggerlist" : ""); - notify["banid"] = record->banId; - notify["serverid"] = record->serverId; + Command notify(this->getExternalType() == CLIENT_TEAMSPEAK ? "notifybantriggerlist" : ""); + notify["banid"] = record->banId; + notify["serverid"] = record->serverId; - auto allow_ip = this->permission_granted(this->cached_permission_value(permission::b_client_remoteaddress_view), 1); - int index = 0; - for (auto &entry : serverInstance->banManager()->trigger_list(record, this->getServerId(), cmd[0].has("offset") ? cmd["offset"].as() : 0, cmd[0].has("limit") ? cmd["limit"].as() : -1)) { - notify[index]["client_unique_identifier"] = entry->unique_id; - notify[index]["client_hardware_identifier"] = entry->hardware_id; - notify[index]["client_nickname"] = entry->name; - if(allow_ip) - notify[index]["connection_client_ip"] = entry->ip; - else - notify[index]["connection_client_ip"] = "hidden"; - notify[index]["timestamp"] = duration_cast(entry->timestamp.time_since_epoch()).count(); - index++; - } - if (index == 0) return {findError("database_empty_result")}; + auto allow_ip = this->permission_granted(this->cached_permission_value(permission::b_client_remoteaddress_view), 1); + int index = 0; + for (auto &entry : serverInstance->banManager()->trigger_list(record, this->getServerId(), cmd[0].has("offset") ? cmd["offset"].as() : 0, cmd[0].has("limit") ? cmd["limit"].as() : -1)) { + notify[index]["client_unique_identifier"] = entry->unique_id; + notify[index]["client_hardware_identifier"] = entry->hardware_id; + notify[index]["client_nickname"] = entry->name; + if(allow_ip) + notify[index]["connection_client_ip"] = entry->ip; + else + notify[index]["connection_client_ip"] = "hidden"; + notify[index]["timestamp"] = duration_cast(entry->timestamp.time_since_epoch()).count(); + index++; + } + if (index == 0) return {findError("database_empty_result")}; - this->sendCommand(notify); - return CommandResult::Success; + this->sendCommand(notify); + return CommandResult::Success; } CommandResult ConnectedClient::handleCommandTokenList(Command &cmd) { @@ -4450,7 +4450,7 @@ CommandResult ConnectedClient::handleCommandTokenAdd(Command &cmd) { CMD_REQ_SERVER; CMD_RESET_IDLE; CMD_CHK_AND_INC_FLOOD_POINTS(5); - CACHED_PERM_CHECK(permission::b_virtualserver_token_add, 1, true); + CACHED_PERM_CHECK(permission::b_virtualserver_token_add, 1, true); TokenType ttype = static_cast(cmd["tokentype"].as()); auto gId = cmd["tokenid1"].as(); @@ -4463,9 +4463,9 @@ CommandResult ConnectedClient::handleCommandTokenAdd(Command &cmd) { if(group->type() == GroupType::GROUP_TYPE_TEMPLATE) return {findError("parameter_invalid"), "invalid server group type"}; if(group->target() == GroupTarget::GROUPTARGET_SERVER) - GROUP_PERMISSION_TEST(permission::i_server_group_member_add_power, permission::i_server_group_needed_member_add_power, group, true); + GROUP_PERMISSION_TEST(permission::i_server_group_member_add_power, permission::i_server_group_needed_member_add_power, group, true); else - GROUP_PERMISSION_TEST(permission::i_channel_group_member_add_power, permission::i_channel_group_needed_member_add_power, group, true); + GROUP_PERMISSION_TEST(permission::i_channel_group_member_add_power, permission::i_channel_group_needed_member_add_power, group, true); } if (ttype == TokenType::TOKEN_CHANNEL) { if (!this->server->channelTree->findChannel(cId)) return {findError("channel_invalid_id"), "Cant resolve channel"}; @@ -4487,7 +4487,7 @@ CommandResult ConnectedClient::handleCommandTokenUse(Command &cmd) { CMD_REQ_SERVER; CMD_RESET_IDLE; CMD_CHK_AND_INC_FLOOD_POINTS(5); - CACHED_PERM_CHECK(permission::b_virtualserver_token_use, 1, true); + CACHED_PERM_CHECK(permission::b_virtualserver_token_use, 1, true); auto strToken = cmd["token"].string(); auto token = this->server->tokenManager->findToken(strToken); @@ -4496,7 +4496,7 @@ CommandResult ConnectedClient::handleCommandTokenUse(Command &cmd) { auto serverGroup = this->server->groups->findGroup(token->groupId); if (!serverGroup) return {findError("token_invalid_id"), "Token invalid groupId"}; - this->server->properties()[property::VIRTUALSERVER_ASK_FOR_PRIVILEGEKEY] = false; //TODO test if its the default token + this->server->properties()[property::VIRTUALSERVER_ASK_FOR_PRIVILEGEKEY] = false; //TODO test if its the default token std::shared_ptr channel = nullptr; if (token->channelId != 0) { @@ -4505,21 +4505,21 @@ CommandResult ConnectedClient::handleCommandTokenUse(Command &cmd) { this->server->groups->setChannelGroup(this->getClientDatabaseId(), serverGroup, channel); } else { - if(!this->server->groups->hasServerGroupAssigned(this->getClientDatabaseId(), serverGroup)) + if(!this->server->groups->hasServerGroupAssigned(this->getClientDatabaseId(), serverGroup)) this->server->groups->addServerGroup(this->getClientDatabaseId(), serverGroup); - else { - return CommandResult::Success; - } + else { + return CommandResult::Success; + } } if (this->server->notifyClientPropertyUpdates(_this.lock(), this->server->groups->update_server_group_property(_this.lock(), true, this->getChannel()))) { - if(this->update_cached_permissions()) /* update cached calculated permissions */ - this->sendNeededPermissions(false); /* cached permissions had changed, notify the client */ + if(this->update_cached_permissions()) /* update cached calculated permissions */ + this->sendNeededPermissions(false); /* cached permissions had changed, notify the client */ { for (auto &viewer : this->server->getClients()) { - if(viewer->isClientVisible(_this.lock(), true)) - viewer->notifyServerGroupClientAdd(this->server->serverRoot, _this.lock(), serverGroup); + if(viewer->isClientVisible(_this.lock(), true)) + viewer->notifyServerGroupClientAdd(this->server->serverRoot, _this.lock(), serverGroup); } } this->notifyServerGroupClientAdd(this->server->serverRoot, _this.lock(), serverGroup); @@ -4532,16 +4532,16 @@ CommandResult ConnectedClient::handleCommandTokenDelete(Command &cmd) { CMD_REQ_SERVER; CMD_RESET_IDLE; CMD_CHK_AND_INC_FLOOD_POINTS(5); - CACHED_PERM_CHECK(permission::b_virtualserver_token_delete, 1, true); + CACHED_PERM_CHECK(permission::b_virtualserver_token_delete, 1, true); auto strToken = cmd["token"].string(); auto token = this->server->tokenManager->findToken(strToken); if (!token) return {findError("token_invalid_id"), "Invalid token. (Token not registered)"}; this->server->tokenManager->deleteToke(token->token); if(token->token == this->server->properties()[property::VIRTUALSERVER_AUTOGENERATED_PRIVILEGEKEY].as()) { - this->server->properties()[property::VIRTUALSERVER_AUTOGENERATED_PRIVILEGEKEY] = ""; - this->server->properties()[property::VIRTUALSERVER_ASK_FOR_PRIVILEGEKEY] = false; - logMessage(this->getServerId(), "{} Deleting the default server token. Don't ask anymore for this a token!", CLIENT_STR_LOG_PREFIX); + this->server->properties()[property::VIRTUALSERVER_AUTOGENERATED_PRIVILEGEKEY] = ""; + this->server->properties()[property::VIRTUALSERVER_ASK_FOR_PRIVILEGEKEY] = false; + logMessage(this->getServerId(), "{} Deleting the default server token. Don't ask anymore for this a token!", CLIENT_STR_LOG_PREFIX); } return CommandResult::Success; } @@ -4660,16 +4660,16 @@ CommandResult ConnectedClient::handleCommandClientDBEdit(Command &cmd) { for (auto &elm : cmd[0].keys()) { if (elm == "cldbid") continue; - auto info = property::info(elm); - if(*info == property::CLIENT_UNDEFINED) { - logError(this->getServerId(), "Client " + this->getDisplayName() + " tried to change someone's db entry, but the entry in unknown: " + elm); - continue; - } - if(!info->validate_input(cmd[elm].as())) { - logError(this->getServerId(), "Client " + this->getDisplayName() + " tried to change a property to an invalid value. (Value: '" + cmd[elm].as() + "', Property: '" + info->name + "')"); - continue; - } - (*props)[info] = cmd[elm].string(); + auto info = property::info(elm); + if(*info == property::CLIENT_UNDEFINED) { + logError(this->getServerId(), "Client " + this->getDisplayName() + " tried to change someone's db entry, but the entry in unknown: " + elm); + continue; + } + if(!info->validate_input(cmd[elm].as())) { + logError(this->getServerId(), "Client " + this->getDisplayName() + " tried to change a property to an invalid value. (Value: '" + cmd[elm].as() + "', Property: '" + info->name + "')"); + continue; + } + (*props)[info] = cmd[elm].string(); } return CommandResult::Success; @@ -4703,12 +4703,12 @@ CommandResult ConnectedClient::handleCommandPluginCmd(Command &cmd) { /* else if(mode == PluginTargetMode::PLUGINCMD_SEND_COMMAND) { - auto target = _this.lock(); - if(cmd[0].has("target")) - target = this->server->findClient(cmd["target"].as()); - if(!target) return {findError("client_invalid_id"), "invalid target id"}; + auto target = _this.lock(); + if(cmd[0].has("target")) + target = this->server->findClient(cmd["target"].as()); + if(!target) return {findError("client_invalid_id"), "invalid target id"}; - target->sendCommand(Command(cmd["command"].string()), cmd[0].has("low") ? cmd["low"] : false); + target->sendCommand(Command(cmd["command"].string()), cmd[0].has("low") ? cmd["low"] : false); } */ @@ -4717,238 +4717,238 @@ CommandResult ConnectedClient::handleCommandPluginCmd(Command &cmd) { } CommandResult ConnectedClient::handleCommandClientEdit(ts::Command &cmd) { - CMD_REQ_SERVER; + CMD_REQ_SERVER; - auto client = this->server->findClient(cmd["clid"].as()); - if (!client) return {findError("client_invalid_id"), "invalid client id"}; - return this->handleCommandClientEdit(cmd, client); + auto client = this->server->findClient(cmd["clid"].as()); + if (!client) return {findError("client_invalid_id"), "invalid client id"}; + return this->handleCommandClientEdit(cmd, client); } CommandResult ConnectedClient::handleCommandClientEdit(Command &cmd, const std::shared_ptr& client) { assert(client); auto self = client == this; - CMD_CHK_AND_INC_FLOOD_POINTS(self ? 15 : 25); - CMD_RESET_IDLE; + CMD_CHK_AND_INC_FLOOD_POINTS(self ? 15 : 25); + CMD_RESET_IDLE; bool update_talk_rights = false; - unique_ptr> nickname_lock; + unique_ptr> nickname_lock; deque> keys; for(const auto& key : cmd[0].keys()) { - if(key == "return_code") continue; - if(key == "clid") continue; + if(key == "return_code") continue; + if(key == "clid") continue; - const auto &info = property::info(key); - if(*info == property::CLIENT_UNDEFINED) { - logError(this->getServerId(), R"([{}] Tried to change a not existing client property for {}. (Key: "{}", Value: "{}"))", CLIENT_STR_LOG_PREFIX, CLIENT_STR_LOG_PREFIX_(client), key, cmd[key].string()); - continue; - } + const auto &info = property::info(key); + if(*info == property::CLIENT_UNDEFINED) { + logError(this->getServerId(), R"([{}] Tried to change a not existing client property for {}. (Key: "{}", Value: "{}"))", CLIENT_STR_LOG_PREFIX, CLIENT_STR_LOG_PREFIX_(client), key, cmd[key].string()); + continue; + } - if((info->flags & property::FLAG_USER_EDITABLE) == 0) { - logError(this->getServerId(), R"([{}] Tried to change a not user editable client property for {}. (Key: "{}", Value: "{}"))", CLIENT_STR_LOG_PREFIX, CLIENT_STR_LOG_PREFIX_(client), key, cmd[key].string()); - continue; - } + if((info->flags & property::FLAG_USER_EDITABLE) == 0) { + logError(this->getServerId(), R"([{}] Tried to change a not user editable client property for {}. (Key: "{}", Value: "{}"))", CLIENT_STR_LOG_PREFIX, CLIENT_STR_LOG_PREFIX_(client), key, cmd[key].string()); + continue; + } - if(!info->validate_input(cmd[key].as())) { - logError(this->getServerId(), R"([{}] Tried to change a client property to an invalid value for {}. (Key: "{}", Value: "{}"))", CLIENT_STR_LOG_PREFIX, CLIENT_STR_LOG_PREFIX_(client), key, cmd[key].string()); - continue; - } - if(client->properties()[info].as() == cmd[key].as()) continue; + if(!info->validate_input(cmd[key].as())) { + logError(this->getServerId(), R"([{}] Tried to change a client property to an invalid value for {}. (Key: "{}", Value: "{}"))", CLIENT_STR_LOG_PREFIX, CLIENT_STR_LOG_PREFIX_(client), key, cmd[key].string()); + continue; + } + if(client->properties()[info].as() == cmd[key].as()) continue; - if (*info == property::CLIENT_DESCRIPTION) { - if (self) - PERM_CHECKR(permission::b_client_modify_own_description, 1, true); - else if(client->getType() == ClientType::CLIENT_MUSIC) { - if(client->properties()[property::CLIENT_OWNER] != this->getClientDatabaseId()) { - CACHED_PERM_CHECK(permission::i_client_music_modify_power, client->permissionValue(permission::PERMTEST_ORDERED, permission::i_client_music_needed_modify_power, client->currentChannel), true); - } - } else { - PERM_CHECKR(permission::b_client_modify_description, 1, true); - } + if (*info == property::CLIENT_DESCRIPTION) { + if (self) + PERM_CHECKR(permission::b_client_modify_own_description, 1, true); + else if(client->getType() == ClientType::CLIENT_MUSIC) { + if(client->properties()[property::CLIENT_OWNER] != this->getClientDatabaseId()) { + CACHED_PERM_CHECK(permission::i_client_music_modify_power, client->permissionValue(permission::PERMTEST_ORDERED, permission::i_client_music_needed_modify_power, client->currentChannel), true); + } + } else { + PERM_CHECKR(permission::b_client_modify_description, 1, true); + } - string value = cmd["client_description"].string(); - if (count_characters(value) > 200) return {findError("parameter_invalid"), "Invalid description length. A maximum of 200 characters is allowed!"}; - } else if (*info == property::CLIENT_IS_TALKER) { - PERM_CHECK_CHANNELR(permission::b_client_set_flag_talker, 1, client->getChannel(), true); - cmd["client_is_talker"] = cmd["client_is_talker"].as(); - cmd["client_talk_request"] = 0; - update_talk_rights = true; + string value = cmd["client_description"].string(); + if (count_characters(value) > 200) return {findError("parameter_invalid"), "Invalid description length. A maximum of 200 characters is allowed!"}; + } else if (*info == property::CLIENT_IS_TALKER) { + PERM_CHECK_CHANNELR(permission::b_client_set_flag_talker, 1, client->getChannel(), true); + cmd["client_is_talker"] = cmd["client_is_talker"].as(); + cmd["client_talk_request"] = 0; + update_talk_rights = true; - keys.emplace_back(property::CLIENT_IS_TALKER, "client_is_talker"); - keys.emplace_back(property::CLIENT_TALK_REQUEST, "client_talk_request"); - continue; - } else if(*info == property::CLIENT_NICKNAME) { - if(!self) { - if(client->getType() != ClientType::CLIENT_MUSIC) return {findError("client_invalid_type")}; - if(client->properties()[property::CLIENT_OWNER] != this->getClientDatabaseId()) { - CACHED_PERM_CHECK(permission::i_client_music_rename_power, client->permissionValue(permission::PERMTEST_ORDERED, permission::i_client_music_needed_rename_power, client->currentChannel), true); - } - } + keys.emplace_back(property::CLIENT_IS_TALKER, "client_is_talker"); + keys.emplace_back(property::CLIENT_TALK_REQUEST, "client_talk_request"); + continue; + } else if(*info == property::CLIENT_NICKNAME) { + if(!self) { + if(client->getType() != ClientType::CLIENT_MUSIC) return {findError("client_invalid_type")}; + if(client->properties()[property::CLIENT_OWNER] != this->getClientDatabaseId()) { + CACHED_PERM_CHECK(permission::i_client_music_rename_power, client->permissionValue(permission::PERMTEST_ORDERED, permission::i_client_music_needed_rename_power, client->currentChannel), true); + } + } - string name = cmd["client_nickname"].string(); - if (count_characters(name) < 3) return {findError("parameter_invalid"), "Invalid name length. A minimum of 3 characters is required!"}; - if (count_characters(name) > 30) return {findError("parameter_invalid"), "Invalid name length. A maximum of 30 characters is allowed!"}; + string name = cmd["client_nickname"].string(); + if (count_characters(name) < 3) return {findError("parameter_invalid"), "Invalid name length. A minimum of 3 characters is required!"}; + if (count_characters(name) > 30) return {findError("parameter_invalid"), "Invalid name length. A maximum of 30 characters is allowed!"}; - auto banIgnore = this->permissionGranted(permission::PERMTEST_ORDERED, permission::b_client_ignore_bans, 1, this->currentChannel); - if (!banIgnore) { - auto banRecord = serverInstance->banManager()->findBanByName(this->getServerId(), name); - if (banRecord) return {findError("client_nickname_inuse"), string() + "This nickname is " + (banRecord->serverId == 0 ? "globally " : "") + "banned for the reason: " + banRecord->reason}; - } - if (this->server) { - nickname_lock = std::make_unique>(this->server->client_nickname_lock); - bool self = false; - for (const auto &cl : this->server->getClients()) { - if (cl->getDisplayName() == cmd["client_nickname"].string()) { - if(cl == this) - self = true; - else - return {findError("client_nickname_inuse"), "This nickname is already in use"}; - } - } - if(self) { - nickname_lock.reset(); - continue; - } - } - } else if(*info == property::CLIENT_PLAYER_VOLUME) { - if(client->getType() != ClientType::CLIENT_MUSIC) return {findError("client_invalid_type")}; - if(client->properties()[property::CLIENT_OWNER] != this->getClientDatabaseId()) { - CACHED_PERM_CHECK(permission::i_client_music_modify_power, client->permissionValue(permission::PERMTEST_ORDERED, permission::i_client_music_needed_modify_power, client->currentChannel), true); - } - auto bot = dynamic_pointer_cast(client); - assert(bot); + auto banIgnore = this->permissionGranted(permission::PERMTEST_ORDERED, permission::b_client_ignore_bans, 1, this->currentChannel); + if (!banIgnore) { + auto banRecord = serverInstance->banManager()->findBanByName(this->getServerId(), name); + if (banRecord) return {findError("client_nickname_inuse"), string() + "This nickname is " + (banRecord->serverId == 0 ? "globally " : "") + "banned for the reason: " + banRecord->reason}; + } + if (this->server) { + nickname_lock = std::make_unique>(this->server->client_nickname_lock); + bool self = false; + for (const auto &cl : this->server->getClients()) { + if (cl->getDisplayName() == cmd["client_nickname"].string()) { + if(cl == this) + self = true; + else + return {findError("client_nickname_inuse"), "This nickname is already in use"}; + } + } + if(self) { + nickname_lock.reset(); + continue; + } + } + } else if(*info == property::CLIENT_PLAYER_VOLUME) { + if(client->getType() != ClientType::CLIENT_MUSIC) return {findError("client_invalid_type")}; + if(client->properties()[property::CLIENT_OWNER] != this->getClientDatabaseId()) { + CACHED_PERM_CHECK(permission::i_client_music_modify_power, client->permissionValue(permission::PERMTEST_ORDERED, permission::i_client_music_needed_modify_power, client->currentChannel), true); + } + auto bot = dynamic_pointer_cast(client); + assert(bot); - auto volume = cmd["player_volume"].as(); - auto max_volume = this->cached_permission_value(permission::i_client_music_create_modify_max_volume); - if(max_volume != permNotGranted && !this->permission_granted(max_volume, volume * 100)) return CommandResultPermissionError{permission::i_client_music_create_modify_max_volume}; + auto volume = cmd["player_volume"].as(); + auto max_volume = this->cached_permission_value(permission::i_client_music_create_modify_max_volume); + if(max_volume != permNotGranted && !this->permission_granted(max_volume, volume * 100)) return CommandResultPermissionError{permission::i_client_music_create_modify_max_volume}; - bot->volume_modifier(cmd["player_volume"]); - } else if(*info == property::CLIENT_IS_CHANNEL_COMMANDER) { - if(!self) { - if(client->getType() != ClientType::CLIENT_MUSIC) return {findError("client_invalid_type")}; - if(client->properties()[property::CLIENT_OWNER] != this->getClientDatabaseId()) { - CACHED_PERM_CHECK(permission::i_client_music_modify_power, client->permissionValue(permission::PERMTEST_ORDERED, permission::i_client_music_needed_modify_power, client->currentChannel), true); - } - } + bot->volume_modifier(cmd["player_volume"]); + } else if(*info == property::CLIENT_IS_CHANNEL_COMMANDER) { + if(!self) { + if(client->getType() != ClientType::CLIENT_MUSIC) return {findError("client_invalid_type")}; + if(client->properties()[property::CLIENT_OWNER] != this->getClientDatabaseId()) { + CACHED_PERM_CHECK(permission::i_client_music_modify_power, client->permissionValue(permission::PERMTEST_ORDERED, permission::i_client_music_needed_modify_power, client->currentChannel), true); + } + } - if(cmd["client_is_channel_commander"].as()) { - CACHED_PERM_CHECK(permission::b_client_use_channel_commander, 1); - } - } else if(*info == property::CLIENT_IS_PRIORITY_SPEAKER) { - //FIXME allow other to remove this thing - if(!self) { - if(client->getType() != ClientType::CLIENT_MUSIC) - return {findError("client_invalid_type")}; - if(client->properties()[property::CLIENT_OWNER] != this->getClientDatabaseId()) { - CACHED_PERM_CHECK(permission::i_client_music_modify_power, client->permissionValue(permission::PERMTEST_ORDERED, permission::i_client_music_needed_modify_power, client->currentChannel), true); - } - } + if(cmd["client_is_channel_commander"].as()) { + CACHED_PERM_CHECK(permission::b_client_use_channel_commander, 1); + } + } else if(*info == property::CLIENT_IS_PRIORITY_SPEAKER) { + //FIXME allow other to remove this thing + if(!self) { + if(client->getType() != ClientType::CLIENT_MUSIC) + return {findError("client_invalid_type")}; + if(client->properties()[property::CLIENT_OWNER] != this->getClientDatabaseId()) { + CACHED_PERM_CHECK(permission::i_client_music_modify_power, client->permissionValue(permission::PERMTEST_ORDERED, permission::i_client_music_needed_modify_power, client->currentChannel), true); + } + } - if(cmd["client_is_priority_speaker"].as()) { - PERM_CHECK_CHANNELR(permission::b_client_use_priority_speaker, 1, this->currentChannel, true); - } - } else if (self && key == "client_talk_request") { - CMD_CHK_AND_INC_FLOOD_POINTS(20); - CACHED_PERM_CHECK(permission::b_client_request_talker, 1, true); + if(cmd["client_is_priority_speaker"].as()) { + PERM_CHECK_CHANNELR(permission::b_client_use_priority_speaker, 1, this->currentChannel, true); + } + } else if (self && key == "client_talk_request") { + CMD_CHK_AND_INC_FLOOD_POINTS(20); + CACHED_PERM_CHECK(permission::b_client_request_talker, 1, true); - if (cmd["client_talk_request"].as()) - cmd["client_talk_request"] = duration_cast(system_clock::now().time_since_epoch()).count(); - else - cmd["client_talk_request"] = 0; - keys.emplace_back(property::CLIENT_TALK_REQUEST, "client_talk_request"); - continue; - } else if (self && key == "client_badges") { - std::string str = cmd[key]; - size_t index = 0; - int badgesTags = 0; - do { - index = str.find("badges", index); - if (index < str.length()) badgesTags++; - index++; - } while (index < str.length() && index != 0); - if (badgesTags >= 2) { - if (!this->permissionGranted(permission::PERMTEST_ORDERED, permission::b_client_allow_invalid_badges, 1, this->currentChannel)) - ((VoiceClient *) this)->disconnect(VREASON_SERVER_KICK, config::messages::kick_invalid_badges, this->server ? this->server->serverAdmin : dynamic_pointer_cast(serverInstance->getInitialServerAdmin()), true); - return {findError("parameter_invalid"), "Invalid badges"}; - } - //FIXME stuff here - } else if(!self && key == "client_version") { - if(client->getType() != ClientType::CLIENT_MUSIC) return {findError("client_invalid_type")}; - if(client->properties()[property::CLIENT_OWNER] != this->getClientDatabaseId()) { - CACHED_PERM_CHECK(permission::i_client_music_modify_power, client->permissionValue(permission::PERMTEST_ORDERED, permission::i_client_music_needed_modify_power, client->currentChannel), true); - } - } else if(!self && key == "client_platform") { - if(client->getType() != ClientType::CLIENT_MUSIC) return {findError("client_invalid_type")}; - if(client->properties()[property::CLIENT_OWNER] != this->getClientDatabaseId()) { - CACHED_PERM_CHECK(permission::i_client_music_modify_power, client->permissionValue(permission::PERMTEST_ORDERED, permission::i_client_music_needed_modify_power, client->currentChannel), true); - } - } else if(!self && key == "client_country") { - if(client->getType() != ClientType::CLIENT_MUSIC) return {findError("client_invalid_type")}; - if(client->properties()[property::CLIENT_OWNER] != this->getClientDatabaseId()) { - CACHED_PERM_CHECK(permission::i_client_music_modify_power, client->permissionValue(permission::PERMTEST_ORDERED, permission::i_client_music_needed_modify_power, client->currentChannel), true); - } - } else if(!self && (*info == property::CLIENT_FLAG_NOTIFY_SONG_CHANGE/* || *info == property::CLIENT_NOTIFY_SONG_MESSAGE*/)) { - if(client->getType() != ClientType::CLIENT_MUSIC) return {findError("client_invalid_type")}; - if(client->properties()[property::CLIENT_OWNER] != this->getClientDatabaseId()) { - CACHED_PERM_CHECK(permission::i_client_music_modify_power, client->permissionValue(permission::PERMTEST_ORDERED, permission::i_client_music_needed_modify_power, client->currentChannel), true); - } - } else if(!self && key == "client_uptime_mode") { - if(client->getType() != ClientType::CLIENT_MUSIC) return {findError("client_invalid_type")}; - if(client->properties()[property::CLIENT_OWNER] != this->getClientDatabaseId()) { - CACHED_PERM_CHECK(permission::i_client_music_modify_power, client->permissionValue(permission::PERMTEST_ORDERED, permission::i_client_music_needed_modify_power, client->currentChannel), true); - } + if (cmd["client_talk_request"].as()) + cmd["client_talk_request"] = duration_cast(system_clock::now().time_since_epoch()).count(); + else + cmd["client_talk_request"] = 0; + keys.emplace_back(property::CLIENT_TALK_REQUEST, "client_talk_request"); + continue; + } else if (self && key == "client_badges") { + std::string str = cmd[key]; + size_t index = 0; + int badgesTags = 0; + do { + index = str.find("badges", index); + if (index < str.length()) badgesTags++; + index++; + } while (index < str.length() && index != 0); + if (badgesTags >= 2) { + if (!this->permissionGranted(permission::PERMTEST_ORDERED, permission::b_client_allow_invalid_badges, 1, this->currentChannel)) + ((VoiceClient *) this)->disconnect(VREASON_SERVER_KICK, config::messages::kick_invalid_badges, this->server ? this->server->serverAdmin : dynamic_pointer_cast(serverInstance->getInitialServerAdmin()), true); + return {findError("parameter_invalid"), "Invalid badges"}; + } + //FIXME stuff here + } else if(!self && key == "client_version") { + if(client->getType() != ClientType::CLIENT_MUSIC) return {findError("client_invalid_type")}; + if(client->properties()[property::CLIENT_OWNER] != this->getClientDatabaseId()) { + CACHED_PERM_CHECK(permission::i_client_music_modify_power, client->permissionValue(permission::PERMTEST_ORDERED, permission::i_client_music_needed_modify_power, client->currentChannel), true); + } + } else if(!self && key == "client_platform") { + if(client->getType() != ClientType::CLIENT_MUSIC) return {findError("client_invalid_type")}; + if(client->properties()[property::CLIENT_OWNER] != this->getClientDatabaseId()) { + CACHED_PERM_CHECK(permission::i_client_music_modify_power, client->permissionValue(permission::PERMTEST_ORDERED, permission::i_client_music_needed_modify_power, client->currentChannel), true); + } + } else if(!self && key == "client_country") { + if(client->getType() != ClientType::CLIENT_MUSIC) return {findError("client_invalid_type")}; + if(client->properties()[property::CLIENT_OWNER] != this->getClientDatabaseId()) { + CACHED_PERM_CHECK(permission::i_client_music_modify_power, client->permissionValue(permission::PERMTEST_ORDERED, permission::i_client_music_needed_modify_power, client->currentChannel), true); + } + } else if(!self && (*info == property::CLIENT_FLAG_NOTIFY_SONG_CHANGE/* || *info == property::CLIENT_NOTIFY_SONG_MESSAGE*/)) { + if(client->getType() != ClientType::CLIENT_MUSIC) return {findError("client_invalid_type")}; + if(client->properties()[property::CLIENT_OWNER] != this->getClientDatabaseId()) { + CACHED_PERM_CHECK(permission::i_client_music_modify_power, client->permissionValue(permission::PERMTEST_ORDERED, permission::i_client_music_needed_modify_power, client->currentChannel), true); + } + } else if(!self && key == "client_uptime_mode") { + if(client->getType() != ClientType::CLIENT_MUSIC) return {findError("client_invalid_type")}; + if(client->properties()[property::CLIENT_OWNER] != this->getClientDatabaseId()) { + CACHED_PERM_CHECK(permission::i_client_music_modify_power, client->permissionValue(permission::PERMTEST_ORDERED, permission::i_client_music_needed_modify_power, client->currentChannel), true); + } - if(cmd[key].as() == MusicClient::UptimeMode::TIME_SINCE_SERVER_START) { - cmd["client_lastconnected"] = duration_cast(this->server->startTimestamp.time_since_epoch()).count(); - } else { - string value = client->properties()[property::CLIENT_CREATED]; - if(value.empty()) - value = "0"; - cmd["client_lastconnected"] = value; - } + if(cmd[key].as() == MusicClient::UptimeMode::TIME_SINCE_SERVER_START) { + cmd["client_lastconnected"] = duration_cast(this->server->startTimestamp.time_since_epoch()).count(); + } else { + string value = client->properties()[property::CLIENT_CREATED]; + if(value.empty()) + value = "0"; + cmd["client_lastconnected"] = value; + } - keys.emplace_back(property::CLIENT_LASTCONNECTED, "client_lastconnected"); - } else if(!self && *info == property::CLIENT_BOT_TYPE) { - auto type = cmd["client_bot_type"].as(); - if(type == MusicClient::Type::TEMPORARY) { - CACHED_PERM_CHECK(permission::b_client_music_modify_temporary, 1, true); - } else if(type == MusicClient::Type::SEMI_PERMANENT) { - CACHED_PERM_CHECK(permission::b_client_music_modify_semi_permanent, 1, true); - } else if(type == MusicClient::Type::PERMANENT) { - CACHED_PERM_CHECK(permission::b_client_music_modify_permanent, 1, true); - } else - return {findError("parameter_invalid")}; - } else if(*info == property::CLIENT_AWAY_MESSAGE) { - if(!self) continue; + keys.emplace_back(property::CLIENT_LASTCONNECTED, "client_lastconnected"); + } else if(!self && *info == property::CLIENT_BOT_TYPE) { + auto type = cmd["client_bot_type"].as(); + if(type == MusicClient::Type::TEMPORARY) { + CACHED_PERM_CHECK(permission::b_client_music_modify_temporary, 1, true); + } else if(type == MusicClient::Type::SEMI_PERMANENT) { + CACHED_PERM_CHECK(permission::b_client_music_modify_semi_permanent, 1, true); + } else if(type == MusicClient::Type::PERMANENT) { + CACHED_PERM_CHECK(permission::b_client_music_modify_permanent, 1, true); + } else + return {findError("parameter_invalid")}; + } else if(*info == property::CLIENT_AWAY_MESSAGE) { + if(!self) continue; - if(cmd["client_away_message"].string().length() > 256) - return {findError("parameter_invalid")}; - } else if(!self) { /* dont edit random properties of other clients. For us self its allowed to edit the rest without permissions */ - continue; - } + if(cmd["client_away_message"].string().length() > 256) + return {findError("parameter_invalid")}; + } else if(!self) { /* dont edit random properties of other clients. For us self its allowed to edit the rest without permissions */ + continue; + } - keys.emplace_back((property::ClientProperties) info->property_index, key); + keys.emplace_back((property::ClientProperties) info->property_index, key); } - deque updates; - for(const auto& key : keys) { - if(key.first == property::CLIENT_IS_PRIORITY_SPEAKER) { - client->clientPermissions->set_permission(permission::b_client_is_priority_speaker, {1, 0}, cmd["client_is_priority_speaker"].as() ? permission::v2::PermissionUpdateType::set_value : permission::v2::PermissionUpdateType::delete_value, permission::v2::PermissionUpdateType::do_nothing); - } - client->properties()[key.first] = cmd[0][key.second].value(); - updates.push_back(key.first); + deque updates; + for(const auto& key : keys) { + if(key.first == property::CLIENT_IS_PRIORITY_SPEAKER) { + client->clientPermissions->set_permission(permission::b_client_is_priority_speaker, {1, 0}, cmd["client_is_priority_speaker"].as() ? permission::v2::PermissionUpdateType::set_value : permission::v2::PermissionUpdateType::delete_value, permission::v2::PermissionUpdateType::do_nothing); + } + client->properties()[key.first] = cmd[0][key.second].value(); + updates.push_back(key.first); } - if(update_talk_rights) - client->updateTalkRights(client->properties()[property::CLIENT_TALK_POWER]); + if(update_talk_rights) + client->updateTalkRights(client->properties()[property::CLIENT_TALK_POWER]); - if(this->server) - this->server->notifyClientPropertyUpdates(client, updates); - nickname_lock.reset(); + if(this->server) + this->server->notifyClientPropertyUpdates(client, updates); + nickname_lock.reset(); return CommandResult::Success; } CommandResult ConnectedClient::handleCommandClientUpdate(Command &cmd) { - return this->handleCommandClientEdit(cmd, _this.lock()); + return this->handleCommandClientEdit(cmd, _this.lock()); } CommandResult ConnectedClient::handleCommandClientMute(Command &cmd) { @@ -4958,12 +4958,12 @@ CommandResult ConnectedClient::handleCommandClientMute(Command &cmd) { auto client = this->server->findClient(cmd["clid"].as()); if (!client || client->getClientId() == this->getClientId()) return {findError("client_invalid_id"), "invalid client id"}; - { - unique_lock channel_lock(this->channel_lock); - for(const auto& weak : this->mutedClients) - if(weak.lock() == client) return CommandResult::Success; - this->mutedClients.push_back(client); - } + { + unique_lock channel_lock(this->channel_lock); + for(const auto& weak : this->mutedClients) + if(weak.lock() == client) return CommandResult::Success; + this->mutedClients.push_back(client); + } if (config::voice::notifyMuted) client->notifyTextMessage(ChatMessageMode::TEXTMODE_PRIVATE, _this.lock(), client->getClientId(), 0, system_clock::now(), config::messages::mute_notify_message); @@ -4978,13 +4978,13 @@ CommandResult ConnectedClient::handleCommandClientUnmute(Command &cmd) { auto client = this->server->findClient(cmd["clid"].as()); if (!client || client->getClientId() == this->getClientId()) return {findError("client_invalid_id"), "invalid client id"}; - { - unique_lock channel_lock(this->channel_lock); - this->mutedClients.erase(std::remove_if(this->mutedClients.begin(), this->mutedClients.end(), [client](const weak_ptr& weak) { - auto c = weak.lock(); - return !c || c == client; - }), this->mutedClients.end()); - } + { + unique_lock channel_lock(this->channel_lock); + this->mutedClients.erase(std::remove_if(this->mutedClients.begin(), this->mutedClients.end(), [client](const weak_ptr& weak) { + auto c = weak.lock(); + return !c || c == client; + }), this->mutedClients.end()); + } if (config::voice::notifyMuted) client->notifyTextMessage(ChatMessageMode::TEXTMODE_PRIVATE, _this.lock(), client->getClientId(), 0, system_clock::now(), config::messages::unmute_notify_message); @@ -4998,7 +4998,7 @@ CommandResult ConnectedClient::handleCommandClientList(Command &cmd) { bool allow_ip = false; if (cmd.hasParm("ip")) - allow_ip = this->permission_granted(this->cached_permission_value(permission::b_client_remoteaddress_view), 1); + allow_ip = this->permission_granted(this->cached_permission_value(permission::b_client_remoteaddress_view), 1); Command result(""); int index = 0; @@ -5047,7 +5047,7 @@ CommandResult ConnectedClient::handleCommandClientList(Command &cmd) { result[index]["client_icon_id"] = client->properties()[property::CLIENT_ICON_ID].as(); if (cmd.hasParm("voice")) { - result[index]["client_talk_power"] = client->properties()[property::CLIENT_TALK_POWER].as(); + result[index]["client_talk_power"] = client->properties()[property::CLIENT_TALK_POWER].as(); result[index]["client_flag_talking"] = client->properties()[property::CLIENT_FLAG_TALKING].as(); result[index]["client_input_muted"] = client->properties()[property::CLIENT_INPUT_MUTED].as(); result[index]["client_output_muted"] = client->properties()[property::CLIENT_OUTPUT_MUTED].as(); @@ -5092,14 +5092,14 @@ CommandResult ConnectedClient::handleCommandWhoAmI(Command &cmd) { result["client_login_name"] = this->properties()[property::CLIENT_LOGIN_NAME].as(); result["client_unique_identifier"] = this->getUid(); - { - auto query = dynamic_cast(this); - if(query) { - auto account = query->getQueryAccount(); - result["client_origin_server_id"] = account ? account->bound_server : 0; - } else - result["client_origin_server_id"] = 0; - } + { + auto query = dynamic_cast(this); + if(query) { + auto account = query->getQueryAccount(); + result["client_origin_server_id"] = account ? account->bound_server : 0; + } else + result["client_origin_server_id"] = 0; + } this->sendCommand(result); return CommandResult::Success; @@ -5141,18 +5141,18 @@ CommandResult ConnectedClient::handleCommandClientGetDBIDfromUID(Command &cmd) { deque unique_ids; for(int index = 0; index < cmd.bulkCount(); index++) - unique_ids.push_back(cmd[index]["cluid"].as()); + unique_ids.push_back(cmd[index]["cluid"].as()); auto res = serverInstance->databaseHelper()->queryDatabaseInfoByUid(this->server, unique_ids); if (res.empty()) return {findError("database_empty_result"), "empty!"}; - Command result(this->getExternalType() == CLIENT_TEAMSPEAK ? "notifyclientdbidfromuid" : ""); - int result_index = 0; - for(auto& info : res) { - result[result_index]["cluid"] = info->uniqueId; - result[result_index]["cldbid"] = info->cldbid; - result_index++; - } + Command result(this->getExternalType() == CLIENT_TEAMSPEAK ? "notifyclientdbidfromuid" : ""); + int result_index = 0; + for(auto& info : res) { + result[result_index]["cluid"] = info->uniqueId; + result[result_index]["cldbid"] = info->cldbid; + result_index++; + } this->sendCommand(result); return CommandResult::Success; } @@ -5161,81 +5161,81 @@ CommandResult ConnectedClient::handleCommandClientGetNameFromDBID(Command &cmd) CMD_REQ_SERVER; CMD_RESET_IDLE; - deque dbids; - for(int index = 0; index < cmd.bulkCount(); index++) - dbids.push_back(cmd[index]["cldbid"].as()); + deque dbids; + for(int index = 0; index < cmd.bulkCount(); index++) + dbids.push_back(cmd[index]["cldbid"].as()); - auto res = serverInstance->databaseHelper()->queryDatabaseInfo(this->server, dbids); - if (res.empty()) return {findError("database_empty_result"), "empty!"}; + auto res = serverInstance->databaseHelper()->queryDatabaseInfo(this->server, dbids); + if (res.empty()) return {findError("database_empty_result"), "empty!"}; - Command result(this->getExternalType() == CLIENT_TEAMSPEAK ? "notifyclientgetnamefromdbid" : ""); - int result_index = 0; - for(auto& info : res) { - result[result_index]["cluid"] = info->uniqueId; - result[result_index]["cldbid"] = info->cldbid; - result[result_index]["name"] = info->lastName; - result[result_index]["clname"] = info->lastName; - result_index++; - } - this->sendCommand(result); - return CommandResult::Success; + Command result(this->getExternalType() == CLIENT_TEAMSPEAK ? "notifyclientgetnamefromdbid" : ""); + int result_index = 0; + for(auto& info : res) { + result[result_index]["cluid"] = info->uniqueId; + result[result_index]["cldbid"] = info->cldbid; + result[result_index]["name"] = info->lastName; + result[result_index]["clname"] = info->lastName; + result_index++; + } + this->sendCommand(result); + return CommandResult::Success; } CommandResult ConnectedClient::handleCommandClientGetNameFromUid(Command &cmd) { CMD_REQ_SERVER; CMD_RESET_IDLE; - deque unique_ids; - for(int index = 0; index < cmd.bulkCount(); index++) - unique_ids.push_back(cmd[index]["cluid"].as()); + deque unique_ids; + for(int index = 0; index < cmd.bulkCount(); index++) + unique_ids.push_back(cmd[index]["cluid"].as()); - auto res = serverInstance->databaseHelper()->queryDatabaseInfoByUid(this->server, unique_ids); - if (res.empty()) return {findError("database_empty_result"), "empty!"}; + auto res = serverInstance->databaseHelper()->queryDatabaseInfoByUid(this->server, unique_ids); + if (res.empty()) return {findError("database_empty_result"), "empty!"}; - Command result(this->getExternalType() == CLIENT_TEAMSPEAK ? "notifyclientnamefromuid" : ""); - int result_index = 0; - for(auto& info : res) { - result[result_index]["cluid"] = info->uniqueId; - result[result_index]["cldbid"] = info->cldbid; - result[result_index]["name"] = info->lastName; - result[result_index]["clname"] = info->lastName; - result_index++; - } - this->sendCommand(result); - return CommandResult::Success; + Command result(this->getExternalType() == CLIENT_TEAMSPEAK ? "notifyclientnamefromuid" : ""); + int result_index = 0; + for(auto& info : res) { + result[result_index]["cluid"] = info->uniqueId; + result[result_index]["cldbid"] = info->cldbid; + result[result_index]["name"] = info->lastName; + result[result_index]["clname"] = info->lastName; + result_index++; + } + this->sendCommand(result); + return CommandResult::Success; } CommandResult ConnectedClient::handleCommandClientGetUidFromClid(Command &cmd) { CMD_REQ_SERVER; CMD_RESET_IDLE; - bool error = false; - bool found = false; - auto client_list = this->server->getClients(); + bool error = false; + bool found = false; + auto client_list = this->server->getClients(); - Command notify(this->getExternalType() == CLIENT_TEAMSPEAK ? "notifyclientgetuidfromclid" : ""); - int result_index = 0; + Command notify(this->getExternalType() == CLIENT_TEAMSPEAK ? "notifyclientgetuidfromclid" : ""); + int result_index = 0; - for(int index = 0; index < cmd.bulkCount(); index++) { - auto client_id = cmd[index]["clid"].as(); - for(const auto& entry : client_list) { - if(entry->getClientId() == client_id) { - notify[result_index]["clname"] = entry->getDisplayName(); - notify[result_index]["clid"] = entry->getClientId(); - notify[result_index]["cluid"] = entry->getUid(); - notify[result_index]["cldbid"] = entry->getClientDatabaseId(); - result_index++; - found = true; - } - } - if(found) found = false; - else error = false; - } + for(int index = 0; index < cmd.bulkCount(); index++) { + auto client_id = cmd[index]["clid"].as(); + for(const auto& entry : client_list) { + if(entry->getClientId() == client_id) { + notify[result_index]["clname"] = entry->getDisplayName(); + notify[result_index]["clid"] = entry->getClientId(); + notify[result_index]["cluid"] = entry->getUid(); + notify[result_index]["cldbid"] = entry->getClientDatabaseId(); + result_index++; + found = true; + } + } + if(found) found = false; + else error = false; + } - if(result_index > 0) - this->sendCommand(notify); - if(error) - return {findError("database_empty_result"), "empty!"}; + if(result_index > 0) + this->sendCommand(notify); + if(error) + return {findError("database_empty_result"), "empty!"}; return CommandResult::Success; } @@ -5246,41 +5246,41 @@ CommandResult ConnectedClient::handleCommandClientAddPerm(Command &cmd) { auto cldbid = cmd["cldbid"].as(); if(!serverInstance->databaseHelper()->validClientDatabaseId(this->server, cldbid)) - return {findError("client_invalid_id"), "invalid client id"}; + return {findError("client_invalid_id"), "invalid client id"}; auto mgr = serverInstance->databaseHelper()->loadClientPermissionManager(this->server, cldbid); PERM_CHECKR(permission::i_client_permission_modify_power, this->server->calculatePermission(permission::PERMTEST_ORDERED, cldbid, permission::i_client_needed_permission_modify_power, ClientType::CLIENT_TEAMSPEAK, nullptr), true); - auto maxValue = this->getPermissionGrantValue(permission::PERMTEST_ORDERED, permission::i_permission_modify_power, this->currentChannel); - bool ignoreGrant = this->permissionGranted(permission::PERMTEST_ORDERED, permission::b_permission_modify_power_ignore, 1, this->currentChannel); - bool conOnError = cmd[0].has("continueonerror"); - auto update_channels = false; + auto maxValue = this->getPermissionGrantValue(permission::PERMTEST_ORDERED, permission::i_permission_modify_power, this->currentChannel); + bool ignoreGrant = this->permissionGranted(permission::PERMTEST_ORDERED, permission::b_permission_modify_power_ignore, 1, this->currentChannel); + bool conOnError = cmd[0].has("continueonerror"); + auto update_channels = false; for (int index = 0; index < cmd.bulkCount(); index++) { PARSE_PERMISSION(cmd); auto val = cmd[index]["permvalue"].as(); - if(permission_require_granted_value(permType) && val > maxValue) - return CommandResultPermissionError{permission::i_permission_modify_power}; + if(permission_require_granted_value(permType) && val > maxValue) + return CommandResultPermissionError{permission::i_permission_modify_power}; - if(!ignoreGrant && !this->permissionGrantGranted(permission::PERMTEST_ORDERED, permType, 1, this->currentChannel)) - return CommandResultPermissionError{permission::i_permission_modify_power}; + if(!ignoreGrant && !this->permissionGrantGranted(permission::PERMTEST_ORDERED, permType, 1, this->currentChannel)) + return CommandResultPermissionError{permission::i_permission_modify_power}; if (grant) { - mgr->set_permission(permType, {0, cmd[index]["permvalue"]}, permission::v2::do_nothing, permission::v2::set_value); + mgr->set_permission(permType, {0, cmd[index]["permvalue"]}, permission::v2::do_nothing, permission::v2::set_value); } else { - mgr->set_permission(permType, {cmd[index]["permvalue"], 0}, permission::v2::set_value, permission::v2::do_nothing, cmd[index]["permskip"] ? 1 : 0, cmd[index]["permnegated"] ? 1 : 0); - update_channels |= permission_is_client_property(permType); + mgr->set_permission(permType, {cmd[index]["permvalue"], 0}, permission::v2::set_value, permission::v2::do_nothing, cmd[index]["permskip"] ? 1 : 0, cmd[index]["permnegated"] ? 1 : 0); + update_channels |= permission_is_client_property(permType); } } - serverInstance->databaseHelper()->saveClientPermissions(this->server, cldbid, mgr); - auto onlineClients = this->server->findClientsByCldbId(cldbid); + serverInstance->databaseHelper()->saveClientPermissions(this->server, cldbid, mgr); + auto onlineClients = this->server->findClientsByCldbId(cldbid); if (!onlineClients.empty()) for (const auto &elm : onlineClients) { - if(elm->update_cached_permissions()) /* update cached calculated permissions */ - elm->sendNeededPermissions(false); /* cached permissions had changed, notify the client */ - if(update_channels) - elm->updateChannelClientProperties(true, true); - elm->join_state_id++; /* join permission may changed, all channels need to be recalculate dif needed */ + if(elm->update_cached_permissions()) /* update cached calculated permissions */ + elm->sendNeededPermissions(false); /* cached permissions had changed, notify the client */ + if(update_channels) + elm->updateChannelClientProperties(true, true); + elm->join_state_id++; /* join permission may changed, all channels need to be recalculate dif needed */ } return CommandResult::Success; @@ -5293,38 +5293,38 @@ CommandResult ConnectedClient::handleCommandClientDelPerm(Command &cmd) { auto cldbid = cmd["cldbid"].as(); if(!serverInstance->databaseHelper()->validClientDatabaseId(this->server, cldbid)) - return {findError("client_invalid_id"), "invalid client id"}; + return {findError("client_invalid_id"), "invalid client id"}; auto mgr = serverInstance->databaseHelper()->loadClientPermissionManager(this->server, cldbid); PERM_CHECKR(permission::i_client_permission_modify_power, this->server->calculatePermission(permission::PERMTEST_ORDERED, cldbid, permission::i_client_needed_permission_modify_power, ClientType::CLIENT_TEAMSPEAK, nullptr), true); - bool ignoreGrant = this->permissionGranted(permission::PERMTEST_ORDERED, permission::b_permission_modify_power_ignore, 1, this->currentChannel); - bool conOnError = cmd[0].has("continueonerror"); + bool ignoreGrant = this->permissionGranted(permission::PERMTEST_ORDERED, permission::b_permission_modify_power_ignore, 1, this->currentChannel); + bool conOnError = cmd[0].has("continueonerror"); auto onlineClients = this->server->findClientsByCldbId(cmd["cldbid"]); auto update_channel = false; for (int index = 0; index < cmd.bulkCount(); index++) { PARSE_PERMISSION(cmd) - if(!ignoreGrant && !this->permissionGrantGranted(permission::PERMTEST_ORDERED, permType, 1, this->currentChannel)) - return CommandResultPermissionError{permission::i_permission_modify_power}; + if(!ignoreGrant && !this->permissionGrantGranted(permission::PERMTEST_ORDERED, permType, 1, this->currentChannel)) + return CommandResultPermissionError{permission::i_permission_modify_power}; - if (grant) { - mgr->set_permission(permType, permission::v2::empty_permission_values, permission::v2::do_nothing, permission::v2::delete_value); - } else { - mgr->set_permission(permType, permission::v2::empty_permission_values, permission::v2::delete_value, permission::v2::do_nothing); - update_channel |= permission_is_client_property(permType); - } + if (grant) { + mgr->set_permission(permType, permission::v2::empty_permission_values, permission::v2::do_nothing, permission::v2::delete_value); + } else { + mgr->set_permission(permType, permission::v2::empty_permission_values, permission::v2::delete_value, permission::v2::do_nothing); + update_channel |= permission_is_client_property(permType); + } } - serverInstance->databaseHelper()->saveClientPermissions(this->server, cldbid, mgr); - if (!onlineClients.empty()) - for (const auto &elm : onlineClients) { - if(elm->update_cached_permissions()) /* update cached calculated permissions */ - elm->sendNeededPermissions(false); /* cached permissions had changed, notify the client */ - if(update_channel) - elm->updateChannelClientProperties(true, true); - elm->join_state_id++; /* join permission may changed, all channels need to be recalculate dif needed */ - } + serverInstance->databaseHelper()->saveClientPermissions(this->server, cldbid, mgr); + if (!onlineClients.empty()) + for (const auto &elm : onlineClients) { + if(elm->update_cached_permissions()) /* update cached calculated permissions */ + elm->sendNeededPermissions(false); /* cached permissions had changed, notify the client */ + if(update_channel) + elm->updateChannelClientProperties(true, true); + elm->join_state_id++; /* join permission may changed, all channels need to be recalculate dif needed */ + } return CommandResult::Success; } @@ -5345,9 +5345,9 @@ CommandResult ConnectedClient::handleCommandChannelClientPermList(Command &cmd) CMD_RESET_IDLE; CMD_CHK_AND_INC_FLOOD_POINTS(5); PERM_CHECKR(permission::b_virtualserver_channelclient_permission_list, 1, true); - RESOLVE_CHANNEL_R(cmd["cid"], true); - auto channel = dynamic_pointer_cast(l_channel->entry); - if(!channel) return {ErrorType::VSError}; + RESOLVE_CHANNEL_R(cmd["cid"], true); + auto channel = dynamic_pointer_cast(l_channel->entry); + if(!channel) return {ErrorType::VSError}; if(!serverInstance->databaseHelper()->validClientDatabaseId(this->server, cmd["cldbid"])) return {findError("client_invalid_id"), "invalid client id"}; auto mgr = serverInstance->databaseHelper()->loadClientPermissionManager(this->server, cmd["cldbid"].as()); @@ -5356,40 +5356,40 @@ CommandResult ConnectedClient::handleCommandChannelClientPermList(Command &cmd) auto permissions = mgr->channel_permissions(channel->channelId()); if(permissions.empty()) - return {ErrorType::DBEmpty}; + return {ErrorType::DBEmpty}; int index = 0; - res[index]["cid"] = channel->channelId(); - res[index]["cldbid"] = cmd["cldbid"].as(); + res[index]["cid"] = channel->channelId(); + res[index]["cldbid"] = cmd["cldbid"].as(); - auto sids = cmd.hasParm("permsid"); - auto permission_mapper = serverInstance->getPermissionMapper(); - auto type = this->getType(); + auto sids = cmd.hasParm("permsid"); + auto permission_mapper = serverInstance->getPermissionMapper(); + auto type = this->getType(); for (const auto &permission_data : permissions) { - auto& permission = std::get<1>(permission_data); + auto& permission = std::get<1>(permission_data); if(permission.flags.value_set) { - if (sids) - res[index]["permsid"] = permission_mapper->permission_name(type, get<0>(permission_data)); - else - res[index]["permid"] = get<0>(permission_data); - res[index]["permvalue"] = permission.values.value; + if (sids) + res[index]["permsid"] = permission_mapper->permission_name(type, get<0>(permission_data)); + else + res[index]["permid"] = get<0>(permission_data); + res[index]["permvalue"] = permission.values.value; - res[index]["permnegated"] = permission.flags.negate; - res[index]["permskip"] = permission.flags.skip; - index++; + res[index]["permnegated"] = permission.flags.negate; + res[index]["permskip"] = permission.flags.skip; + index++; } if(permission.flags.grant_set) { - if (sids) - res[index]["permsid"] = permission_mapper->permission_name_grant(type, get<0>(permission_data)); - else - res[index]["permid"] = (get<0>(permission_data) | PERM_ID_GRANT); - res[index]["permvalue"] = permission.values.grant; - res[index]["permnegated"] = 0; - res[index]["permskip"] = 0; - index++; + if (sids) + res[index]["permsid"] = permission_mapper->permission_name_grant(type, get<0>(permission_data)); + else + res[index]["permid"] = (get<0>(permission_data) | PERM_ID_GRANT); + res[index]["permvalue"] = permission.values.grant; + res[index]["permnegated"] = 0; + res[index]["permskip"] = 0; + index++; } } @@ -5398,64 +5398,64 @@ CommandResult ConnectedClient::handleCommandChannelClientPermList(Command &cmd) } CommandResult ConnectedClient::handleCommandChannelClientDelPerm(Command &cmd) { - CMD_REF_SERVER(server_ref); + CMD_REF_SERVER(server_ref); CMD_RESET_IDLE; CMD_CHK_AND_INC_FLOOD_POINTS(5); auto cldbid = cmd["cldbid"].as(); if (!serverInstance->databaseHelper()->validClientDatabaseId(this->server, cldbid)) - return {findError("parameter_invalid"), "Invalid manager db id"}; + return {findError("parameter_invalid"), "Invalid manager db id"}; - auto mgr = serverInstance->databaseHelper()->loadClientPermissionManager(this->server, cldbid); - PERM_CHECKR(permission::i_client_permission_modify_power, this->server->calculatePermission(permission::PERMTEST_ORDERED, cmd["cldbid"], permission::i_client_needed_permission_modify_power, ClientType::CLIENT_TEAMSPEAK, nullptr), true); + auto mgr = serverInstance->databaseHelper()->loadClientPermissionManager(this->server, cldbid); + PERM_CHECKR(permission::i_client_permission_modify_power, this->server->calculatePermission(permission::PERMTEST_ORDERED, cmd["cldbid"], permission::i_client_needed_permission_modify_power, ClientType::CLIENT_TEAMSPEAK, nullptr), true); RESOLVE_CHANNEL_R(cmd["cid"], true); - auto channel = dynamic_pointer_cast(l_channel->entry); - if(!channel) return {ErrorType::VSError}; + auto channel = dynamic_pointer_cast(l_channel->entry); + if(!channel) return {ErrorType::VSError}; - bool ignoreGrant = this->permissionGranted(permission::PERMTEST_ORDERED, permission::b_permission_modify_power_ignore, 1, this->currentChannel); - bool conOnError = cmd[0].has("continueonerror"), update_view = false; + bool ignoreGrant = this->permissionGranted(permission::PERMTEST_ORDERED, permission::b_permission_modify_power_ignore, 1, this->currentChannel); + bool conOnError = cmd[0].has("continueonerror"), update_view = false; auto cll = this->server->findClientsByCldbId(cldbid); for (int index = 0; index < cmd.bulkCount(); index++) { - PARSE_PERMISSION(cmd); + PARSE_PERMISSION(cmd); - if(!ignoreGrant && !this->permissionGrantGranted(permission::PERMTEST_ORDERED, permType, 1, this->currentChannel)) - return CommandResultPermissionError{permission::i_permission_modify_power}; + if(!ignoreGrant && !this->permissionGrantGranted(permission::PERMTEST_ORDERED, permType, 1, this->currentChannel)) + return CommandResultPermissionError{permission::i_permission_modify_power}; - if (grant) { - mgr->set_channel_permission(permType, channel->channelId(), permission::v2::empty_permission_values, permission::v2::do_nothing, permission::v2::delete_value); - } else { - mgr->set_channel_permission(permType, channel->channelId(), permission::v2::empty_permission_values, permission::v2::delete_value, permission::v2::do_nothing); - update_view = permType == permission::b_channel_ignore_view_power || permType == permission::i_channel_view_power; - } + if (grant) { + mgr->set_channel_permission(permType, channel->channelId(), permission::v2::empty_permission_values, permission::v2::do_nothing, permission::v2::delete_value); + } else { + mgr->set_channel_permission(permType, channel->channelId(), permission::v2::empty_permission_values, permission::v2::delete_value, permission::v2::do_nothing); + update_view = permType == permission::b_channel_ignore_view_power || permType == permission::i_channel_view_power; + } } - serverInstance->databaseHelper()->saveClientPermissions(this->server, cldbid, mgr); + serverInstance->databaseHelper()->saveClientPermissions(this->server, cldbid, mgr); if (!cll.empty()) { - for (const auto &elm : cll) { - if(elm->update_cached_permissions()) /* update cached calculated permissions */ - elm->sendNeededPermissions(false); /* cached permissions had changed, notify the client */ + for (const auto &elm : cll) { + if(elm->update_cached_permissions()) /* update cached calculated permissions */ + elm->sendNeededPermissions(false); /* cached permissions had changed, notify the client */ - if(elm->currentChannel == channel) { - elm->updateChannelClientProperties(true, true); - } else if(update_view) { - unique_lock client_channel_lock(this->channel_lock); + if(elm->currentChannel == channel) { + elm->updateChannelClientProperties(true, true); + } else if(update_view) { + unique_lock client_channel_lock(this->channel_lock); - auto elm_channel = elm->currentChannel; - if(elm_channel) { - deque deleted; - for(const auto& update_entry : elm->channels->update_channel_path(l_channel, this->server->channelTree->findLinkedChannel(elm->currentChannel->channelId()))) { - if(update_entry.first) - elm->notifyChannelShow(update_entry.second->channel(), update_entry.second->previous_channel); - else deleted.push_back(update_entry.second->channelId()); - } - if(!deleted.empty()) - elm->notifyChannelHide(deleted, false); /* we've locked the tree before */ - } - } + auto elm_channel = elm->currentChannel; + if(elm_channel) { + deque deleted; + for(const auto& update_entry : elm->channels->update_channel_path(l_channel, this->server->channelTree->findLinkedChannel(elm->currentChannel->channelId()))) { + if(update_entry.first) + elm->notifyChannelShow(update_entry.second->channel(), update_entry.second->previous_channel); + else deleted.push_back(update_entry.second->channelId()); + } + if(!deleted.empty()) + elm->notifyChannelHide(deleted, false); /* we've locked the tree before */ + } + } - elm->join_state_id++; /* join permission may changed, all channels need to be recalculate dif needed */ - } + elm->join_state_id++; /* join permission may changed, all channels need to be recalculate dif needed */ + } } return CommandResult::Success; @@ -5466,66 +5466,66 @@ CommandResult ConnectedClient::handleCommandChannelClientAddPerm(Command &cmd) { CMD_RESET_IDLE; CMD_CHK_AND_INC_FLOOD_POINTS(5); - auto cldbid = cmd["cldbid"].as(); + auto cldbid = cmd["cldbid"].as(); if (!serverInstance->databaseHelper()->validClientDatabaseId(this->server, cldbid)) - return {findError("parameter_invalid"), "Invalid client db id"}; + return {findError("parameter_invalid"), "Invalid client db id"}; - RESOLVE_CHANNEL_R(cmd["cid"], true); - auto channel = dynamic_pointer_cast(l_channel->entry); - if(!channel) return {ErrorType::VSError}; + RESOLVE_CHANNEL_R(cmd["cid"], true); + auto channel = dynamic_pointer_cast(l_channel->entry); + if(!channel) return {ErrorType::VSError}; auto mgr = serverInstance->databaseHelper()->loadClientPermissionManager(this->server, cldbid); - PERM_CHECK_CHANNELR(permission::i_client_permission_modify_power, this->server->calculatePermission(permission::PERMTEST_ORDERED, cmd["cldbid"], permission::i_client_needed_permission_modify_power, ClientType::CLIENT_TEAMSPEAK, channel), channel, true); + PERM_CHECK_CHANNELR(permission::i_client_permission_modify_power, this->server->calculatePermission(permission::PERMTEST_ORDERED, cmd["cldbid"], permission::i_client_needed_permission_modify_power, ClientType::CLIENT_TEAMSPEAK, channel), channel, true); - auto maxValue = this->getPermissionGrantValue(permission::PERMTEST_ORDERED, permission::i_permission_modify_power, this->currentChannel); - bool ignoreGrant = this->permissionGranted(permission::PERMTEST_ORDERED, permission::b_permission_modify_power_ignore, 1, this->currentChannel); - bool conOnError = cmd[0].has("continueonerror"); + auto maxValue = this->getPermissionGrantValue(permission::PERMTEST_ORDERED, permission::i_permission_modify_power, this->currentChannel); + bool ignoreGrant = this->permissionGranted(permission::PERMTEST_ORDERED, permission::b_permission_modify_power_ignore, 1, this->currentChannel); + bool conOnError = cmd[0].has("continueonerror"); auto onlineClientInstances = this->server->findClientsByCldbId(cldbid); bool update_view = false; for (int index = 0; index < cmd.bulkCount(); index++) { - PARSE_PERMISSION(cmd); + PARSE_PERMISSION(cmd); - auto val = cmd[index]["permvalue"].as(); - if(permission_require_granted_value(permType) && val > maxValue) - return CommandResultPermissionError{permission::i_permission_modify_power}; + auto val = cmd[index]["permvalue"].as(); + if(permission_require_granted_value(permType) && val > maxValue) + return CommandResultPermissionError{permission::i_permission_modify_power}; - if(!ignoreGrant && !this->permissionGrantGranted(permission::PERMTEST_ORDERED, permType, 1, this->currentChannel)) - return CommandResultPermissionError{permission::i_permission_modify_power}; + if(!ignoreGrant && !this->permissionGrantGranted(permission::PERMTEST_ORDERED, permType, 1, this->currentChannel)) + return CommandResultPermissionError{permission::i_permission_modify_power}; - if (grant) { - mgr->set_channel_permission(permType, channel->channelId(), {0, cmd[index]["permvalue"]}, permission::v2::do_nothing, permission::v2::set_value); - } else { - mgr->set_channel_permission(permType, channel->channelId(), {cmd[index]["permvalue"], 0}, permission::v2::set_value, permission::v2::do_nothing, cmd[index]["permskip"] ? 1 : 0, cmd[index]["permnegated"] ? 1 : 0); - update_view = permType == permission::b_channel_ignore_view_power || permType == permission::i_channel_view_power; - } + if (grant) { + mgr->set_channel_permission(permType, channel->channelId(), {0, cmd[index]["permvalue"]}, permission::v2::do_nothing, permission::v2::set_value); + } else { + mgr->set_channel_permission(permType, channel->channelId(), {cmd[index]["permvalue"], 0}, permission::v2::set_value, permission::v2::do_nothing, cmd[index]["permskip"] ? 1 : 0, cmd[index]["permnegated"] ? 1 : 0); + update_view = permType == permission::b_channel_ignore_view_power || permType == permission::i_channel_view_power; + } } - serverInstance->databaseHelper()->saveClientPermissions(this->server, cldbid, mgr); + serverInstance->databaseHelper()->saveClientPermissions(this->server, cldbid, mgr); if (!onlineClientInstances.empty()) for (const auto &elm : onlineClientInstances) { - if (elm->update_cached_permissions()) /* update cached calculated permissions */ - elm->sendNeededPermissions(false); /* cached permissions had changed, notify the client */ + if (elm->update_cached_permissions()) /* update cached calculated permissions */ + elm->sendNeededPermissions(false); /* cached permissions had changed, notify the client */ - if(elm->currentChannel == channel) { - elm->updateChannelClientProperties(true, true); - } else if(update_view) { - unique_lock client_channel_lock(this->channel_lock); + if(elm->currentChannel == channel) { + elm->updateChannelClientProperties(true, true); + } else if(update_view) { + unique_lock client_channel_lock(this->channel_lock); - auto elm_channel = elm->currentChannel; - if(elm_channel) { - deque deleted; - for(const auto& update_entry : elm->channels->update_channel_path(l_channel, this->server->channelTree->findLinkedChannel(elm->currentChannel->channelId()))) { - if(update_entry.first) - elm->notifyChannelShow(update_entry.second->channel(), update_entry.second->previous_channel); - else deleted.push_back(update_entry.second->channelId()); - } - if(!deleted.empty()) - elm->notifyChannelHide(deleted, false); /* we've locked the tree before */ - } - } - elm->join_state_id++; /* join permission may changed, all channels need to be recalculate dif needed */ + auto elm_channel = elm->currentChannel; + if(elm_channel) { + deque deleted; + for(const auto& update_entry : elm->channels->update_channel_path(l_channel, this->server->channelTree->findLinkedChannel(elm->currentChannel->channelId()))) { + if(update_entry.first) + elm->notifyChannelShow(update_entry.second->channel(), update_entry.second->previous_channel); + else deleted.push_back(update_entry.second->channelId()); + } + if(!deleted.empty()) + elm->notifyChannelHide(deleted, false); /* we've locked the tree before */ + } + } + elm->join_state_id++; /* join permission may changed, all channels need to be recalculate dif needed */ } return CommandResult::Success; @@ -5539,7 +5539,7 @@ CommandResult ConnectedClient::handleCommandClientDbInfo(Command &cmd) { deque cldbids; for(int index = 0; index < cmd.bulkCount(); index++) - cldbids.push_back(cmd[index]["cldbid"]); + cldbids.push_back(cmd[index]["cldbid"]); auto basic = serverInstance->databaseHelper()->queryDatabaseInfo(this->server, cldbids); if (basic.empty()) return {findError("database_empty_result"), "empty!"}; @@ -5549,35 +5549,35 @@ CommandResult ConnectedClient::handleCommandClientDbInfo(Command &cmd) { size_t index = 0; for(const auto& info : basic) { - res[index]["client_base64HashClientUID"] = hex::hex(base64::validate(info->uniqueId) ? base64::decode(info->uniqueId) : info->uniqueId, 'a', 'q'); - res[index]["client_unique_identifier"] = info->uniqueId; - res[index]["client_nickname"] = info->lastName; - res[index]["client_database_id"] = info->cldbid; - res[index]["client_created"] = chrono::duration_cast(info->created.time_since_epoch()).count(); - res[index]["client_lastconnected"] = chrono::duration_cast(info->lastjoin.time_since_epoch()).count(); - res[index]["client_totalconnections"] = info->connections; - res[index]["client_database_id"] = info->cldbid; + res[index]["client_base64HashClientUID"] = hex::hex(base64::validate(info->uniqueId) ? base64::decode(info->uniqueId) : info->uniqueId, 'a', 'q'); + res[index]["client_unique_identifier"] = info->uniqueId; + res[index]["client_nickname"] = info->lastName; + res[index]["client_database_id"] = info->cldbid; + res[index]["client_created"] = chrono::duration_cast(info->created.time_since_epoch()).count(); + res[index]["client_lastconnected"] = chrono::duration_cast(info->lastjoin.time_since_epoch()).count(); + res[index]["client_totalconnections"] = info->connections; + res[index]["client_database_id"] = info->cldbid; - auto props = serverInstance->databaseHelper()->loadClientProperties(this->server, info->cldbid, ClientType::CLIENT_TEAMSPEAK); - if (allow_ip) - res[index]["client_lastip"] = (*props)[property::CONNECTION_CLIENT_IP].as(); - else - res[index]["client_lastip"] = "hidden"; + auto props = serverInstance->databaseHelper()->loadClientProperties(this->server, info->cldbid, ClientType::CLIENT_TEAMSPEAK); + if (allow_ip) + res[index]["client_lastip"] = (*props)[property::CONNECTION_CLIENT_IP].as(); + else + res[index]["client_lastip"] = "hidden"; - res[index]["client_icon_id"] = (*props)[property::CLIENT_ICON_ID].as(); - res[index]["client_badges"] = (*props)[property::CLIENT_BADGES].as(); - res[index]["client_version"] = (*props)[property::CLIENT_VERSION].as(); - res[index]["client_platform"] = (*props)[property::CLIENT_PLATFORM].as(); - res[index]["client_hwid"] = (*props)[property::CLIENT_HARDWARE_ID].as(); - res[index]["client_total_bytes_downloaded"] = (*props)[property::CLIENT_TOTAL_BYTES_DOWNLOADED].as(); - res[index]["client_total_bytes_uploaded"] = (*props)[property::CLIENT_TOTAL_BYTES_UPLOADED].as(); - res[index]["client_month_bytes_downloaded"] = (*props)[property::CLIENT_MONTH_BYTES_DOWNLOADED].as(); - res[index]["client_month_bytes_uploaded"] = (*props)[property::CLIENT_MONTH_BYTES_DOWNLOADED].as(); - res[index]["client_description"] = (*props)[property::CLIENT_DESCRIPTION].as(); - res[index]["client_flag_avatar"] = (*props)[property::CLIENT_FLAG_AVATAR].as(); + res[index]["client_icon_id"] = (*props)[property::CLIENT_ICON_ID].as(); + res[index]["client_badges"] = (*props)[property::CLIENT_BADGES].as(); + res[index]["client_version"] = (*props)[property::CLIENT_VERSION].as(); + res[index]["client_platform"] = (*props)[property::CLIENT_PLATFORM].as(); + res[index]["client_hwid"] = (*props)[property::CLIENT_HARDWARE_ID].as(); + res[index]["client_total_bytes_downloaded"] = (*props)[property::CLIENT_TOTAL_BYTES_DOWNLOADED].as(); + res[index]["client_total_bytes_uploaded"] = (*props)[property::CLIENT_TOTAL_BYTES_UPLOADED].as(); + res[index]["client_month_bytes_downloaded"] = (*props)[property::CLIENT_MONTH_BYTES_DOWNLOADED].as(); + res[index]["client_month_bytes_uploaded"] = (*props)[property::CLIENT_MONTH_BYTES_DOWNLOADED].as(); + res[index]["client_description"] = (*props)[property::CLIENT_DESCRIPTION].as(); + res[index]["client_flag_avatar"] = (*props)[property::CLIENT_FLAG_AVATAR].as(); - res[index]["client_month_online_time"] = (*props)[property::CLIENT_MONTH_ONLINE_TIME].as(); - res[index]["client_total_online_time"] = (*props)[property::CLIENT_TOTAL_ONLINE_TIME].as(); + res[index]["client_month_online_time"] = (*props)[property::CLIENT_MONTH_ONLINE_TIME].as(); + res[index]["client_total_online_time"] = (*props)[property::CLIENT_TOTAL_ONLINE_TIME].as(); index++; } @@ -5662,48 +5662,48 @@ CommandResult ConnectedClient::handleCommandClientInfo(Command &cmd) { CMD_REQ_SERVER; CMD_RESET_IDLE; - Command res(this->getExternalType() == ClientType::CLIENT_TEAMSPEAK ? "notifyclientinfo" : ""); - bool trigger_error = false; - bool view_remote = this->permission_granted(this->cached_permission_value(permission::b_client_remoteaddress_view), 1); + Command res(this->getExternalType() == ClientType::CLIENT_TEAMSPEAK ? "notifyclientinfo" : ""); + bool trigger_error = false; + bool view_remote = this->permission_granted(this->cached_permission_value(permission::b_client_remoteaddress_view), 1); - int result_index = 0; + int result_index = 0; for(int index = 0; index < cmd.bulkCount(); index++) { - auto client_id = cmd[index]["clid"].as(); - if(client_id == 0) continue; + auto client_id = cmd[index]["clid"].as(); + if(client_id == 0) continue; - auto client = this->server->findClient(client_id); - if(!client) { - trigger_error = true; - continue; - } + auto client = this->server->findClient(client_id); + if(!client) { + trigger_error = true; + continue; + } - for (const auto &key : client->properties()->list_properties(property::FLAG_CLIENT_VIEW | property::FLAG_CLIENT_VARIABLE | property::FLAG_CLIENT_INFO, this->getType() == CLIENT_TEAMSPEAK ? property::FLAG_NEW : (uint16_t) 0)) - res[result_index][key.type().name] = key.value(); - if(view_remote) - res[result_index]["connection_client_ip"] = client->properties()[property::CONNECTION_CLIENT_IP].as(); - else - res[result_index]["connection_client_ip"] = "hidden"; - res[result_index]["client_idle_time"] = duration_cast(system_clock::now() - client->idleTimestamp).count(); - res[result_index]["connection_connected_time"] = duration_cast(system_clock::now() - client->connectTimestamp).count(); - { - auto channel = client->currentChannel; - if(channel) - res[result_index]["cid"] = channel->channelId(); - else - res[result_index]["cid"] = 0; - } + for (const auto &key : client->properties()->list_properties(property::FLAG_CLIENT_VIEW | property::FLAG_CLIENT_VARIABLE | property::FLAG_CLIENT_INFO, this->getType() == CLIENT_TEAMSPEAK ? property::FLAG_NEW : (uint16_t) 0)) + res[result_index][key.type().name] = key.value(); + if(view_remote) + res[result_index]["connection_client_ip"] = client->properties()[property::CONNECTION_CLIENT_IP].as(); + else + res[result_index]["connection_client_ip"] = "hidden"; + res[result_index]["client_idle_time"] = duration_cast(system_clock::now() - client->idleTimestamp).count(); + res[result_index]["connection_connected_time"] = duration_cast(system_clock::now() - client->connectTimestamp).count(); + { + auto channel = client->currentChannel; + if(channel) + res[result_index]["cid"] = channel->channelId(); + else + res[result_index]["cid"] = 0; + } - result_index++; + result_index++; } if(result_index > 0) { - this->sendCommand(res); + this->sendCommand(res); } - if(trigger_error || result_index == 0) - return {findError("client_invalid_id"), "invalid client id"}; - else + if(trigger_error || result_index == 0) + return {findError("client_invalid_id"), "invalid client id"}; + else return CommandResult::Success; } @@ -5737,7 +5737,7 @@ CommandResult ConnectedClient::handleCommandVersion(Command &) { Command res(""); res["version"] = build::version()->string(false); res["build_count"] = build::buildCount(); - res["build"] = duration_cast(build::version()->timestamp.time_since_epoch()).count(); + res["build"] = duration_cast(build::version()->timestamp.time_since_epoch()).count(); #ifdef WINDOWS res["platform"] = "Windows"; #else @@ -5855,7 +5855,7 @@ CommandResult ConnectedClient::handleCommandPermGet(Command &cmd) { deque requrested; auto permission_mapper = serverInstance->getPermissionMapper(); - auto type = this->getType(); + auto type = this->getType(); for (int index = 0; index < cmd.bulkCount(); index++) { permission::PermissionType permType = permission::unknown; if (cmd[index].has("permid")) @@ -5887,67 +5887,67 @@ CommandResult ConnectedClient::handleCommandPermIdGetByName(Command &cmd) { } CommandResult ConnectedClient::handleCommandPermFind(Command &cmd) { - struct PermissionEntry { - permission::PermissionType permission_type; - permission::PermissionValue permission_value; - permission::PermissionSqlType type; + struct PermissionEntry { + permission::PermissionType permission_type; + permission::PermissionValue permission_value; + permission::PermissionSqlType type; - GroupId group_id; - ChannelId channel_id; - ClientDbId client_id; + GroupId group_id; + ChannelId channel_id; + ClientDbId client_id; - bool negate; - bool skip; - }; + bool negate; + bool skip; + }; CMD_REQ_SERVER; CMD_RESET_IDLE; CMD_CHK_AND_INC_FLOOD_POINTS(5); PERM_CHECKR(permission::b_virtualserver_permission_find, 1, true); - deque, bool>> permissions; - std::shared_ptr permission; - for(size_t index = 0; index < cmd.bulkCount(); index++) { - bool granted = false; - if (cmd[index].has("permid")) { - permission = permission::resolvePermissionData((permission::PermissionType) (cmd[index]["permid"].as() & (~PERM_ID_GRANT))); - granted = (cmd[index]["permid"].as() & PERM_ID_GRANT) > 0; + deque, bool>> permissions; + std::shared_ptr permission; + for(size_t index = 0; index < cmd.bulkCount(); index++) { + bool granted = false; + if (cmd[index].has("permid")) { + permission = permission::resolvePermissionData((permission::PermissionType) (cmd[index]["permid"].as() & (~PERM_ID_GRANT))); + granted = (cmd[index]["permid"].as() & PERM_ID_GRANT) > 0; - if(permission->type == permission::PermissionType::unknown) - return {findError("parameter_invalid"), "could not resolve permission (id=" + cmd[index]["permid"].string() + ")"}; - } else if (cmd[index].has("permsid")) { - permission = permission::resolvePermissionData(cmd[index]["permsid"].as()); //TODO: Map the other way around - granted = permission->grant_name == cmd[index]["permsid"].as(); + if(permission->type == permission::PermissionType::unknown) + return {findError("parameter_invalid"), "could not resolve permission (id=" + cmd[index]["permid"].string() + ")"}; + } else if (cmd[index].has("permsid")) { + permission = permission::resolvePermissionData(cmd[index]["permsid"].as()); //TODO: Map the other way around + granted = permission->grant_name == cmd[index]["permsid"].as(); - if(permission->type == permission::PermissionType::unknown) - return {findError("parameter_invalid"), "could not resolve permission (id=" + cmd[index]["permid"].string() + ")"}; - } else { - continue; - } + if(permission->type == permission::PermissionType::unknown) + return {findError("parameter_invalid"), "could not resolve permission (id=" + cmd[index]["permid"].string() + ")"}; + } else { + continue; + } - permissions.emplace_back(pair, bool>{{permission->name, permission->type}, granted}); - } + permissions.emplace_back(pair, bool>{{permission->name, permission->type}, granted}); + } - if(permissions.empty()) - return {findError("database_empty_result")}; + if(permissions.empty()) + return {findError("database_empty_result")}; - map flags; - map quick_mapping; - string query_string; - for(const auto& entry : permissions) { - if(flags[entry.first.first] == 0) { - quick_mapping[entry.first.first] = entry.first.second; - query_string += string(query_string.empty() ? "" : " OR ") + "`permId` = '" + entry.first.first + "'"; - } + map flags; + map quick_mapping; + string query_string; + for(const auto& entry : permissions) { + if(flags[entry.first.first] == 0) { + quick_mapping[entry.first.first] = entry.first.second; + query_string += string(query_string.empty() ? "" : " OR ") + "`permId` = '" + entry.first.first + "'"; + } - flags[entry.first.first] |= entry.second ? 2 : 1; - } + flags[entry.first.first] |= entry.second ? 2 : 1; + } deque> entries; //`serverId` INT NOT NULL, `type` INT, `id` INT, `channelId` INT, `permId` VARCHAR(" UNKNOWN_KEY_LENGTH "), `value` INT, `grant` INT sql::command(this->sql, "SELECT `permId`, `type`, `id`, `channelId`, `value`, `grant`, `flag_skip`, `flag_negate` FROM `permissions` WHERE `serverId` = :sid AND (" + query_string + ") AND `type` != :playlist", - variable{":sid", this->server->getServerId()}, - variable{":playlist", permission::SQL_PERM_PLAYLIST} + variable{":sid", this->server->getServerId()}, + variable{":playlist", permission::SQL_PERM_PLAYLIST} ).query([&](int length, string* values, string* columns) { permission::PermissionSqlType type = permission::SQL_PERM_GROUP; uint64_t id = 0; @@ -5955,25 +5955,25 @@ CommandResult ConnectedClient::handleCommandPermFind(Command &cmd) { permission::PermissionValue value = 0, granted_value = 0; string permission_name; - bool negate = false, skip = false; + bool negate = false, skip = false; for (int index = 0; index < length; index++) { try { if(columns[index] == "type") - type = static_cast(stoll(values[index])); + type = static_cast(stoll(values[index])); else if(columns[index] == "permId") - permission_name = values[index]; + permission_name = values[index]; else if(columns[index] == "id") - id = static_cast(stoll(values[index])); + id = static_cast(stoll(values[index])); else if(columns[index] == "channelId") - channel_id = static_cast(stoll(values[index])); + channel_id = static_cast(stoll(values[index])); else if(columns[index] == "value") - value = static_cast(stoll(values[index])); + value = static_cast(stoll(values[index])); else if(columns[index] == "grant") - granted_value = static_cast(stoll(values[index])); + granted_value = static_cast(stoll(values[index])); else if(columns[index] == "flag_negate") - negate = !values[index].empty() && stol(values[index]) == 1; + negate = !values[index].empty() && stol(values[index]) == 1; else if(columns[index] == "flag_skip") - skip = !values[index].empty() && stol(values[index]) == 1; + skip = !values[index].empty() && stol(values[index]) == 1; } catch(std::exception& ex) { debugMessage(this->getServerId(), "[{}] 'permfind' iterates over invalid permission entry. Key: {}, Value: {}, Error: {}", CLIENT_STR_LOG_PREFIX, columns[index], values[index], ex.what()); return 0; @@ -5995,7 +5995,7 @@ CommandResult ConnectedClient::handleCommandPermFind(Command &cmd) { result->group_id = id; if(gr->target() == GROUPTARGET_CHANNEL) - result->channel_id = 1; + result->channel_id = 1; } else if(type == permission::SQL_PERM_USER) { result->client_id = id; } @@ -6005,104 +6005,104 @@ CommandResult ConnectedClient::handleCommandPermFind(Command &cmd) { } /* granted */ - if((flags[permission_name] & 0x2) > 0 && granted_value > 0) { - auto result = make_unique(); - result->permission_type = (permission::PermissionType) (quick_mapping[permission_name] | PERM_ID_GRANT); - result->permission_value = granted_value; - result->type = type; - result->channel_id = channel_id; - result->negate = negate; - result->skip = skip; + if((flags[permission_name] & 0x2) > 0 && granted_value > 0) { + auto result = make_unique(); + result->permission_type = (permission::PermissionType) (quick_mapping[permission_name] | PERM_ID_GRANT); + result->permission_value = granted_value; + result->type = type; + result->channel_id = channel_id; + result->negate = negate; + result->skip = skip; - if (type == permission::SQL_PERM_GROUP) { - auto gr = this->server->groups->findGroup(id); - if (!gr) return 0; + if (type == permission::SQL_PERM_GROUP) { + auto gr = this->server->groups->findGroup(id); + if (!gr) return 0; - result->group_id = id; - if(gr->target() == GROUPTARGET_CHANNEL) - result->channel_id = 1; - } else if(type == permission::SQL_PERM_USER) { - result->client_id = id; - } + result->group_id = id; + if(gr->target() == GROUPTARGET_CHANNEL) + result->channel_id = 1; + } else if(type == permission::SQL_PERM_USER) { + result->client_id = id; + } - if(result) - entries.push_back(std::move(result)); - } + if(result) + entries.push_back(std::move(result)); + } return 0; }); - struct CommandPerm { - permission::PermissionType p; - permission::PermissionValue v; - int64_t id1; - int64_t id2; - uint8_t t; - }; + struct CommandPerm { + permission::PermissionType p; + permission::PermissionValue v; + int64_t id1; + int64_t id2; + uint8_t t; + }; - std::deque perms; - perms.resize(entries.size()); - size_t index = 0; - for(const auto& entry : entries) { - auto& perm = perms[index++]; + std::deque perms; + perms.resize(entries.size()); + size_t index = 0; + for(const auto& entry : entries) { + auto& perm = perms[index++]; - perm.p = entry->permission_type; - perm.v = entry->permission_value; + perm.p = entry->permission_type; + perm.v = entry->permission_value; - if(entry->type == permission::SQL_PERM_USER) { - if(entry->channel_id > 0) { - perm.id1 = entry->client_id; - perm.id2 = entry->channel_id; - perm.t = 4; /* client channel */ - } else { - perm.id1 = 0; - perm.id2 = entry->client_id; - perm.t = 1; /* client server */ - } - } else if(entry->type == permission::SQL_PERM_CHANNEL) { - perm.id1 = 0; - perm.id2 = entry->channel_id; - perm.t = 2; /* channel permission */ - } else if(entry->type == permission::SQL_PERM_GROUP) { - if(entry->channel_id > 0) { - perm.id1 = entry->group_id; - perm.id2 = 0; - perm.t = 3; /* channel group */ - } else { - perm.id1 = entry->group_id; - perm.id2 = 0; - perm.t = 0; /* server group */ - } - } - } + if(entry->type == permission::SQL_PERM_USER) { + if(entry->channel_id > 0) { + perm.id1 = entry->client_id; + perm.id2 = entry->channel_id; + perm.t = 4; /* client channel */ + } else { + perm.id1 = 0; + perm.id2 = entry->client_id; + perm.t = 1; /* client server */ + } + } else if(entry->type == permission::SQL_PERM_CHANNEL) { + perm.id1 = 0; + perm.id2 = entry->channel_id; + perm.t = 2; /* channel permission */ + } else if(entry->type == permission::SQL_PERM_GROUP) { + if(entry->channel_id > 0) { + perm.id1 = entry->group_id; + perm.id2 = 0; + perm.t = 3; /* channel group */ + } else { + perm.id1 = entry->group_id; + perm.id2 = 0; + perm.t = 0; /* server group */ + } + } + } - sort(perms.begin(), perms.end(), [](const CommandPerm& a, const CommandPerm& b) { - if(a.t < b.t) return true; - else if(b.t < a.t) return false; + sort(perms.begin(), perms.end(), [](const CommandPerm& a, const CommandPerm& b) { + if(a.t < b.t) return true; + else if(b.t < a.t) return false; - if(a.id1 < b.id1) return true; - else if(b.id1 < a.id1) return false; + if(a.id1 < b.id1) return true; + else if(b.id1 < a.id1) return false; - if(a.id2 < b.id2) return true; - else if(b.id2 < a.id2) return false; + if(a.id2 < b.id2) return true; + else if(b.id2 < a.id2) return false; - if(a.p < b.p) return true; - else if(b.p < a.p) return false; + if(a.p < b.p) return true; + else if(b.p < a.p) return false; - return &a > &b; - }); + return &a > &b; + }); Command result(""); index = 0; // http://yat.qa/ressourcen/server-query-kommentare/#permfind for(const auto& e : perms) { - result[index]["p"] = e.p; - result[index]["v"] = e.v; - result[index]["id1"] = e.id1; - result[index]["id2"] = e.id2; - result[index]["t"] = e.t; - index++; + result[index]["p"] = e.p; + result[index]["v"] = e.v; + result[index]["id1"] = e.id1; + result[index]["id2"] = e.id2; + result[index]["t"] = e.t; + index++; } if(index == 0) return {findError("database_empty_result")}; @@ -6116,182 +6116,182 @@ CommandResult ConnectedClient::handleCommandPermFind(Command &cmd) { * - Alle rechte des channels */ CommandResult ConnectedClient::handleCommandPermOverview(Command &cmd) { - CMD_REQ_SERVER; - CMD_RESET_IDLE; - CMD_CHK_AND_INC_FLOOD_POINTS(5); + CMD_REQ_SERVER; + CMD_RESET_IDLE; + CMD_CHK_AND_INC_FLOOD_POINTS(5); - auto client_dbid = cmd["cldbid"].as(); - if(!serverInstance->databaseHelper()->validClientDatabaseId(this->getServer(), client_dbid)) return {findError("client_invalid_id")}; + auto client_dbid = cmd["cldbid"].as(); + if(!serverInstance->databaseHelper()->validClientDatabaseId(this->getServer(), client_dbid)) return {findError("client_invalid_id")}; - if(client_dbid == this->getClientDatabaseId()) { - CACHED_PERM_CHECK(permission::b_client_permissionoverview_own, 1, true); - } else { - CACHED_PERM_CHECK(permission::b_client_permissionoverview_view, 1, true); - } + if(client_dbid == this->getClientDatabaseId()) { + CACHED_PERM_CHECK(permission::b_client_permissionoverview_own, 1, true); + } else { + CACHED_PERM_CHECK(permission::b_client_permissionoverview_view, 1, true); + } - string channel_query, perm_query; + string channel_query, perm_query; - auto channel = this->server ? this->server->channelTree->findChannel(cmd["cid"]) : serverInstance->getChannelTree()->findChannel(cmd["cid"]); - if(!channel) return {findError("channel_invalid_id")}; + auto channel = this->server ? this->server->channelTree->findChannel(cmd["cid"]) : serverInstance->getChannelTree()->findChannel(cmd["cid"]); + if(!channel) return {findError("channel_invalid_id")}; - auto server_groups = this->server->getGroupManager()->getServerGroups(client_dbid, ClientType::CLIENT_TEAMSPEAK); - auto channel_group = this->server->getGroupManager()->getChannelGroup(client_dbid, channel, true); - auto permission_manager = serverInstance->databaseHelper()->loadClientPermissionManager(this->getServer(), client_dbid); + auto server_groups = this->server->getGroupManager()->getServerGroups(client_dbid, ClientType::CLIENT_TEAMSPEAK); + auto channel_group = this->server->getGroupManager()->getChannelGroup(client_dbid, channel, true); + auto permission_manager = serverInstance->databaseHelper()->loadClientPermissionManager(this->getServer(), client_dbid); - Command result(this->getExternalType() == ClientType::CLIENT_TEAMSPEAK ? "notifypermoverview" : ""); - size_t index = 0; - result["cldbid"] = client_dbid; - result["cid"] = channel->channelId(); - if(cmd["return_code"].size() > 0) - result["return_code"] = cmd["return_code"].string(); + Command result(this->getExternalType() == ClientType::CLIENT_TEAMSPEAK ? "notifypermoverview" : ""); + size_t index = 0; + result["cldbid"] = client_dbid; + result["cid"] = channel->channelId(); + if(cmd["return_code"].size() > 0) + result["return_code"] = cmd["return_code"].string(); - for(const auto& server_group : server_groups) { - auto permission_manager = server_group->group->permissions(); - for(const auto& permission_data : permission_manager->permissions()) { - auto& permission = std::get<1>(permission_data); - if(permission.flags.value_set) { - result[index]["t"] = 0; /* server group */ - result[index]["id1"] = server_group->group->groupId(); - result[index]["id2"] = 0; + for(const auto& server_group : server_groups) { + auto permission_manager = server_group->group->permissions(); + for(const auto& permission_data : permission_manager->permissions()) { + auto& permission = std::get<1>(permission_data); + if(permission.flags.value_set) { + result[index]["t"] = 0; /* server group */ + result[index]["id1"] = server_group->group->groupId(); + result[index]["id2"] = 0; - result[index]["p"] = std::get<0>(permission_data); - result[index]["v"] = permission.values.value; - result[index]["n"] = permission.flags.negate; - result[index]["s"] = permission.flags.skip; - index++; - } - if(permission.flags.grant_set) { - result[index]["t"] = 0; /* server group */ - result[index]["id1"] = server_group->group->groupId(); - result[index]["id2"] = 0; + result[index]["p"] = std::get<0>(permission_data); + result[index]["v"] = permission.values.value; + result[index]["n"] = permission.flags.negate; + result[index]["s"] = permission.flags.skip; + index++; + } + if(permission.flags.grant_set) { + result[index]["t"] = 0; /* server group */ + result[index]["id1"] = server_group->group->groupId(); + result[index]["id2"] = 0; - result[index]["p"] = (std::get<0>(permission_data) | PERM_ID_GRANT); - result[index]["v"] = permission.values.grant; - result[index]["n"] = false; - result[index]["s"] = false; - index++; - } - } - } + result[index]["p"] = (std::get<0>(permission_data) | PERM_ID_GRANT); + result[index]["v"] = permission.values.grant; + result[index]["n"] = false; + result[index]["s"] = false; + index++; + } + } + } - { - for(const auto& permission_data : permission_manager->permissions()) { - auto& permission = std::get<1>(permission_data); - if(permission.flags.value_set) { - result[index]["t"] = 1; /* client */ - result[index]["id1"] = client_dbid; - result[index]["id2"] = 0; + { + for(const auto& permission_data : permission_manager->permissions()) { + auto& permission = std::get<1>(permission_data); + if(permission.flags.value_set) { + result[index]["t"] = 1; /* client */ + result[index]["id1"] = client_dbid; + result[index]["id2"] = 0; - result[index]["p"] = std::get<0>(permission_data); - result[index]["v"] = permission.values.value; - result[index]["n"] = permission.flags.negate; - result[index]["s"] = permission.flags.skip; - index++; - } - if(permission.flags.grant_set) { - result[index]["t"] = 1; /* client */ - result[index]["id1"] = client_dbid; - result[index]["id2"] = 0; + result[index]["p"] = std::get<0>(permission_data); + result[index]["v"] = permission.values.value; + result[index]["n"] = permission.flags.negate; + result[index]["s"] = permission.flags.skip; + index++; + } + if(permission.flags.grant_set) { + result[index]["t"] = 1; /* client */ + result[index]["id1"] = client_dbid; + result[index]["id2"] = 0; - result[index]["p"] = (std::get<0>(permission_data) | PERM_ID_GRANT); - result[index]["v"] = permission.values.grant; - result[index]["n"] = false; - result[index]["s"] = false; - index++; - } - } - } + result[index]["p"] = (std::get<0>(permission_data) | PERM_ID_GRANT); + result[index]["v"] = permission.values.grant; + result[index]["n"] = false; + result[index]["s"] = false; + index++; + } + } + } - { - auto permission_manager = channel->permissions(); - for(const auto& permission_data : permission_manager->permissions()) { - auto& permission = std::get<1>(permission_data); - if(permission.flags.value_set) { - result[index]["t"] = 2; /* server channel */ - result[index]["id1"] = channel->channelId(); - result[index]["id2"] = 0; + { + auto permission_manager = channel->permissions(); + for(const auto& permission_data : permission_manager->permissions()) { + auto& permission = std::get<1>(permission_data); + if(permission.flags.value_set) { + result[index]["t"] = 2; /* server channel */ + result[index]["id1"] = channel->channelId(); + result[index]["id2"] = 0; - result[index]["p"] = std::get<0>(permission_data); - result[index]["v"] = permission.values.value; - result[index]["n"] = permission.flags.negate; - result[index]["s"] = permission.flags.skip; - index++; - } - if(permission.flags.grant_set) { - result[index]["t"] = 2; /* server channel */ - result[index]["id1"] = channel->channelId(); - result[index]["id2"] = 0; + result[index]["p"] = std::get<0>(permission_data); + result[index]["v"] = permission.values.value; + result[index]["n"] = permission.flags.negate; + result[index]["s"] = permission.flags.skip; + index++; + } + if(permission.flags.grant_set) { + result[index]["t"] = 2; /* server channel */ + result[index]["id1"] = channel->channelId(); + result[index]["id2"] = 0; - result[index]["p"] = (std::get<0>(permission_data) | PERM_ID_GRANT); - result[index]["v"] = permission.values.grant; - result[index]["n"] = false; - result[index]["s"] = false; - index++; - } - } - } + result[index]["p"] = (std::get<0>(permission_data) | PERM_ID_GRANT); + result[index]["v"] = permission.values.grant; + result[index]["n"] = false; + result[index]["s"] = false; + index++; + } + } + } - { - auto permission_manager = channel_group->group->permissions(); - for(const auto& permission_data : permission_manager->permissions()) { - auto& permission = std::get<1>(permission_data); - if(permission.flags.value_set) { - result[index]["t"] = 3; /* channel group */ - result[index]["id1"] = channel_group->channelId; - result[index]["id2"] = channel_group->group->groupId(); + { + auto permission_manager = channel_group->group->permissions(); + for(const auto& permission_data : permission_manager->permissions()) { + auto& permission = std::get<1>(permission_data); + if(permission.flags.value_set) { + result[index]["t"] = 3; /* channel group */ + result[index]["id1"] = channel_group->channelId; + result[index]["id2"] = channel_group->group->groupId(); - result[index]["p"] = std::get<0>(permission_data); - result[index]["v"] = permission.values.value; - result[index]["n"] = permission.flags.negate; - result[index]["s"] = permission.flags.skip; - index++; - } - if(permission.flags.grant_set) { - result[index]["t"] = 3; /* channel group */ - result[index]["id1"] = channel_group->channelId; - result[index]["id2"] = channel_group->group->groupId(); + result[index]["p"] = std::get<0>(permission_data); + result[index]["v"] = permission.values.value; + result[index]["n"] = permission.flags.negate; + result[index]["s"] = permission.flags.skip; + index++; + } + if(permission.flags.grant_set) { + result[index]["t"] = 3; /* channel group */ + result[index]["id1"] = channel_group->channelId; + result[index]["id2"] = channel_group->group->groupId(); - result[index]["p"] = (std::get<0>(permission_data) | PERM_ID_GRANT); - result[index]["v"] = permission.values.grant; - result[index]["n"] = false; - result[index]["s"] = false; - index++; - } - } - } + result[index]["p"] = (std::get<0>(permission_data) | PERM_ID_GRANT); + result[index]["v"] = permission.values.grant; + result[index]["n"] = false; + result[index]["s"] = false; + index++; + } + } + } - { - for(const auto& permission_data : permission_manager->channel_permissions()) { - auto& permission = std::get<2>(permission_data); - if(permission.flags.value_set) { - result[index]["t"] = 4; /* client channel */ - result[index]["id1"] = std::get<1>(permission_data); - result[index]["id2"] = client_dbid; + { + for(const auto& permission_data : permission_manager->channel_permissions()) { + auto& permission = std::get<2>(permission_data); + if(permission.flags.value_set) { + result[index]["t"] = 4; /* client channel */ + result[index]["id1"] = std::get<1>(permission_data); + result[index]["id2"] = client_dbid; - result[index]["p"] = std::get<0>(permission_data); - result[index]["v"] = permission.values.value; - result[index]["n"] = permission.flags.negate; - result[index]["s"] = permission.flags.skip; - index++; - } - if(permission.flags.grant_set) { - result[index]["t"] = 1; /* client */ - result[index]["id1"] = std::get<1>(permission_data); - result[index]["id2"] = client_dbid; + result[index]["p"] = std::get<0>(permission_data); + result[index]["v"] = permission.values.value; + result[index]["n"] = permission.flags.negate; + result[index]["s"] = permission.flags.skip; + index++; + } + if(permission.flags.grant_set) { + result[index]["t"] = 1; /* client */ + result[index]["id1"] = std::get<1>(permission_data); + result[index]["id2"] = client_dbid; - result[index]["p"] = (std::get<0>(permission_data) | PERM_ID_GRANT); - result[index]["v"] = permission.values.grant; - result[index]["n"] = false; - result[index]["s"] = false; - index++; - } - } - } + result[index]["p"] = (std::get<0>(permission_data) | PERM_ID_GRANT); + result[index]["v"] = permission.values.grant; + result[index]["n"] = false; + result[index]["s"] = false; + index++; + } + } + } - if (index == 0) return {findError("database_empty_result"), ""}; - this->sendCommand(result); + if (index == 0) return {findError("database_empty_result"), ""}; + this->sendCommand(result); - return CommandResult::Success; + return CommandResult::Success; } CommandResult ConnectedClient::handleCommandChannelFind(Command &cmd) { @@ -6327,7 +6327,7 @@ CommandResult ConnectedClient::handleCommandChannelInfo(Command &cmd) { res[prop.type().name] = prop.value(); res["seconds_empty"] = channel->emptySince(); - res["pid"] = res["cpid"].string(); + res["pid"] = res["cpid"].string(); this->sendCommand(res); return CommandResult::Success; @@ -6339,16 +6339,16 @@ CommandResult ConnectedClient::handleCommandClientSetServerQueryLogin(Command &c if(!cmd[0].has("client_login_password")) cmd["client_login_password"] = ""; std::string password = cmd["client_login_password"]; - if(password.empty()) - password = rnd_string(QUERY_PASSWORD_LENGTH); + if(password.empty()) + password = rnd_string(QUERY_PASSWORD_LENGTH); - auto old = serverInstance->getQueryServer()->find_query_account_by_name(cmd["client_login_name"]); + auto old = serverInstance->getQueryServer()->find_query_account_by_name(cmd["client_login_name"]); if (old) { - if(old->unique_id == this->getUid()) { - serverInstance->getQueryServer()->change_query_password(old, password); - } else { - return {findError("client_not_logged_in")}; - } + if(old->unique_id == this->getUid()) { + serverInstance->getQueryServer()->change_query_password(old, password); + } else { + return {findError("client_not_logged_in")}; + } } else { serverInstance->getQueryServer()->create_query_account(cmd["client_login_name"], this->getServerId(), this->getUid(), password); } @@ -6450,7 +6450,7 @@ CommandResult ConnectedClient::handleCommandComplainDel(Command &cmd) { if (entry->invoker == this->getClientDatabaseId()) CACHED_PERM_CHECK(permission::b_client_complain_delete_own, 1, true); else - CACHED_PERM_CHECK(permission::b_client_complain_delete, 1, true); + CACHED_PERM_CHECK(permission::b_client_complain_delete, 1, true); this->server->complains->deleteComplain(entry); @@ -6462,7 +6462,7 @@ CommandResult ConnectedClient::handleCommandComplainDelAll(Command &cmd) { CMD_RESET_IDLE; CMD_CHK_AND_INC_FLOOD_POINTS(25); - CACHED_PERM_CHECK(permission::b_client_complain_delete, 1, true); + CACHED_PERM_CHECK(permission::b_client_complain_delete, 1, true); ClientDbId tid = cmd["tcldbid"]; if (!this->server->complains->deleteComplainsFromTarget(tid)) return {findError("database_empty_result"), "empty!"}; return CommandResult::Success; @@ -6470,301 +6470,301 @@ CommandResult ConnectedClient::handleCommandComplainDelAll(Command &cmd) { CommandResult ConnectedClient::handleCommandMusicBotCreate(Command& cmd) { - if(!config::music::enabled) return {findError("music_disabled")}; + if(!config::music::enabled) return {findError("music_disabled")}; - CMD_REQ_SERVER; - CMD_RESET_IDLE; - CMD_CHK_AND_INC_FLOOD_POINTS(25); + CMD_REQ_SERVER; + CMD_RESET_IDLE; + CMD_CHK_AND_INC_FLOOD_POINTS(25); - if(this->server->musicManager->max_bots() != -1 && this->server->musicManager->max_bots() <= this->server->musicManager->current_bot_count()){ - if(config::license->isPremium()) - return {findError("music_limit_reached"), ""}; - else - return {findError("music_limit_reached"), strobf("You reached the server music bot limit. You could increase this limit by extend your server with a premium license.").string()}; - } + if(this->server->musicManager->max_bots() != -1 && this->server->musicManager->max_bots() <= this->server->musicManager->current_bot_count()){ + if(config::license->isPremium()) + return {findError("music_limit_reached"), ""}; + else + return {findError("music_limit_reached"), strobf("You reached the server music bot limit. You could increase this limit by extend your server with a premium license.").string()}; + } - auto permissions_list = this->permissionValues(permission::PERMTEST_ORDERED, { - permission::i_client_music_limit, - permission::b_client_music_create_permanent, - permission::b_client_music_create_semi_permanent, - permission::b_client_music_create_temporary, - permission::i_channel_join_power, - permission::i_client_music_delete_power, - permission::i_client_music_create_modify_max_volume - }, this->currentChannel); + auto permissions_list = this->permissionValues(permission::PERMTEST_ORDERED, { + permission::i_client_music_limit, + permission::b_client_music_create_permanent, + permission::b_client_music_create_semi_permanent, + permission::b_client_music_create_temporary, + permission::i_channel_join_power, + permission::i_client_music_delete_power, + permission::i_client_music_create_modify_max_volume + }, this->currentChannel); - auto permissions = map(permissions_list.begin(), permissions_list.end()); + auto permissions = map(permissions_list.begin(), permissions_list.end()); - auto max_bots = permissions[permission::i_client_music_limit]; - if(max_bots >= 0) { - auto ownBots = this->server->musicManager->listBots(this->getClientDatabaseId()); - if(ownBots.size() > max_bots) - return {findError("music_client_limit_reached"), ""}; - } + auto max_bots = permissions[permission::i_client_music_limit]; + if(max_bots >= 0) { + auto ownBots = this->server->musicManager->listBots(this->getClientDatabaseId()); + if(ownBots.size() > max_bots) + return {findError("music_client_limit_reached"), ""}; + } - MusicClient::Type::value create_type; - if(cmd[0].has("type")) { - create_type = cmd["type"].as(); - switch(create_type) { - case MusicClient::Type::PERMANENT: - if(permissions[permission::b_client_music_create_permanent] != 1) - return CommandResultPermissionError{permission::b_client_music_create_permanent}; - break; - case MusicClient::Type::SEMI_PERMANENT: - if(permissions[permission::b_client_music_create_semi_permanent] != 1) - return CommandResultPermissionError{permission::b_client_music_create_semi_permanent}; - break; - case MusicClient::Type::TEMPORARY: - if(permissions[permission::b_client_music_create_temporary] != 1) - return CommandResultPermissionError{permission::b_client_music_create_temporary}; - break; - default: - return {ErrorType::VSError}; - } - } else { - if(permissions[permission::b_client_music_create_permanent] == 1) - create_type = MusicClient::Type::PERMANENT; - else if(permissions[permission::b_client_music_create_semi_permanent] == 1) - create_type = MusicClient::Type::SEMI_PERMANENT; - else if(permissions[permission::b_client_music_create_temporary] == 1) - create_type = MusicClient::Type::TEMPORARY; - else - return CommandResultPermissionError{permission::b_client_music_create_temporary}; - } + MusicClient::Type::value create_type; + if(cmd[0].has("type")) { + create_type = cmd["type"].as(); + switch(create_type) { + case MusicClient::Type::PERMANENT: + if(permissions[permission::b_client_music_create_permanent] != 1) + return CommandResultPermissionError{permission::b_client_music_create_permanent}; + break; + case MusicClient::Type::SEMI_PERMANENT: + if(permissions[permission::b_client_music_create_semi_permanent] != 1) + return CommandResultPermissionError{permission::b_client_music_create_semi_permanent}; + break; + case MusicClient::Type::TEMPORARY: + if(permissions[permission::b_client_music_create_temporary] != 1) + return CommandResultPermissionError{permission::b_client_music_create_temporary}; + break; + default: + return {ErrorType::VSError}; + } + } else { + if(permissions[permission::b_client_music_create_permanent] == 1) + create_type = MusicClient::Type::PERMANENT; + else if(permissions[permission::b_client_music_create_semi_permanent] == 1) + create_type = MusicClient::Type::SEMI_PERMANENT; + else if(permissions[permission::b_client_music_create_temporary] == 1) + create_type = MusicClient::Type::TEMPORARY; + else + return CommandResultPermissionError{permission::b_client_music_create_temporary}; + } - shared_lock server_channel_lock(this->server->channel_tree_lock); + shared_lock server_channel_lock(this->server->channel_tree_lock); auto channel = cmd[0].has("cid") ? this->server->channelTree->findChannel(cmd["cid"]) : this->currentChannel; if(!channel) { if(cmd[0].has("cid")) return {findError("channel_invalid_id")}; } else { - CHANNEL_PERMISSION_TEST(permission::i_channel_description_view_power, permission::i_channel_needed_description_view_power, channel, false); - auto permission_granted = this->calculate_permission_value(permission::i_channel_join_power, channel->channelId()); - if(!channel->permission_granted(permission::i_channel_needed_join_power, permission_granted, false)) + CHANNEL_PERMISSION_TEST(permission::i_channel_description_view_power, permission::i_channel_needed_description_view_power, channel, false); + auto permission_granted = this->calculate_permission_value(permission::i_channel_join_power, channel->channelId()); + if(!channel->permission_granted(permission::i_channel_needed_join_power, permission_granted, false)) channel = nullptr; } if(!channel) { - channel = this->server->channelTree->getDefaultChannel(); + channel = this->server->channelTree->getDefaultChannel(); } - auto bot = this->server->musicManager->createBot(this->getClientDatabaseId()); - if(!bot) return {ErrorType::VSError, ""}; - bot->set_bot_type(create_type); - { - if(permissions[permission::i_client_music_create_modify_max_volume] > 0) { - auto max_volume = min(100, max(0, permissions[permission::i_client_music_create_modify_max_volume])); - if(max_volume >= 0) - bot->volume_modifier(max_volume / 100.f); - } - } - this->selectedBot = bot; + auto bot = this->server->musicManager->createBot(this->getClientDatabaseId()); + if(!bot) return {ErrorType::VSError, ""}; + bot->set_bot_type(create_type); + { + if(permissions[permission::i_client_music_create_modify_max_volume] > 0) { + auto max_volume = min(100, max(0, permissions[permission::i_client_music_create_modify_max_volume])); + if(max_volume >= 0) + bot->volume_modifier(max_volume / 100.f); + } + } + this->selectedBot = bot; - { - server_channel_lock.unlock(); - unique_lock server_channel_w_lock(this->server->channel_tree_lock); - this->server->client_move( - bot, - channel, - nullptr, - "music bot created", - ViewReasonId::VREASON_USER_ACTION, - false, - server_channel_w_lock - ); - } - bot->properties()[property::CLIENT_LAST_CHANNEL] = channel ? channel->channelId() : 0; - bot->properties()[property::CLIENT_COUNTRY] = config::geo::countryFlag; + { + server_channel_lock.unlock(); + unique_lock server_channel_w_lock(this->server->channel_tree_lock); + this->server->client_move( + bot, + channel, + nullptr, + "music bot created", + ViewReasonId::VREASON_USER_ACTION, + false, + server_channel_w_lock + ); + } + bot->properties()[property::CLIENT_LAST_CHANNEL] = channel ? channel->channelId() : 0; + bot->properties()[property::CLIENT_COUNTRY] = config::geo::countryFlag; - if(permissions[permission::i_client_music_delete_power] > 0) { - bot->clientPermissions->set_permission(permission::i_client_music_needed_delete_power, {permissions[permission::i_client_music_delete_power],0}, permission::v2::set_value, permission::v2::do_nothing); - } + if(permissions[permission::i_client_music_delete_power] > 0) { + bot->clientPermissions->set_permission(permission::i_client_music_needed_delete_power, {permissions[permission::i_client_music_delete_power],0}, permission::v2::set_value, permission::v2::do_nothing); + } - Command notify(this->getExternalType() == ClientType::CLIENT_TEAMSPEAK ? "notifymusiccreated" : ""); - notify["bot_id"] = bot->getClientDatabaseId(); - this->sendCommand(notify); + Command notify(this->getExternalType() == ClientType::CLIENT_TEAMSPEAK ? "notifymusiccreated" : ""); + notify["bot_id"] = bot->getClientDatabaseId(); + this->sendCommand(notify); - return CommandResult::Success; + return CommandResult::Success; } CommandResult ConnectedClient::handleCommandMusicBotDelete(Command& cmd) { - if(!config::music::enabled) return {findError("music_disabled")}; + if(!config::music::enabled) return {findError("music_disabled")}; - CMD_REQ_SERVER; - CMD_RESET_IDLE; - CMD_CHK_AND_INC_FLOOD_POINTS(25); + CMD_REQ_SERVER; + CMD_RESET_IDLE; + CMD_CHK_AND_INC_FLOOD_POINTS(25); - auto bot = this->server->musicManager->findBotById(cmd["bot_id"]); - if(!bot) return {findError("music_invalid_id")}; + auto bot = this->server->musicManager->findBotById(cmd["bot_id"]); + if(!bot) return {findError("music_invalid_id")}; - bool permPower = this->permissionGranted(permission::PERMTEST_ORDERED, permission::i_client_music_delete_power, bot->permissionValue(permission::PERMTEST_ORDERED, permission::i_client_music_needed_delete_power)); - if(bot->getOwner() != this->getClientDatabaseId()) { - if(!permPower) return CommandResultPermissionError{permission::i_client_music_delete_power}; - } + bool permPower = this->permissionGranted(permission::PERMTEST_ORDERED, permission::i_client_music_delete_power, bot->permissionValue(permission::PERMTEST_ORDERED, permission::i_client_music_needed_delete_power)); + if(bot->getOwner() != this->getClientDatabaseId()) { + if(!permPower) return CommandResultPermissionError{permission::i_client_music_delete_power}; + } - this->server->musicManager->deleteBot(bot); - return CommandResult::Success; + this->server->musicManager->deleteBot(bot); + return CommandResult::Success; } CommandResult ConnectedClient::handleCommandMusicBotSetSubscription(ts::Command &cmd) { - if(!config::music::enabled) return {findError("music_disabled")}; + if(!config::music::enabled) return {findError("music_disabled")}; - auto bot = this->server->musicManager->findBotById(cmd["bot_id"]); - if(!bot && cmd["bot_id"].as() != 0) return {findError("music_invalid_id")}; + auto bot = this->server->musicManager->findBotById(cmd["bot_id"]); + if(!bot && cmd["bot_id"].as() != 0) return {findError("music_invalid_id")}; - { - auto old_bot = this->subscribed_bot.lock(); - if(old_bot) - old_bot->remove_subscriber(_this.lock()); - } + { + auto old_bot = this->subscribed_bot.lock(); + if(old_bot) + old_bot->remove_subscriber(_this.lock()); + } - if(bot) { - bot->add_subscriber(_this.lock()); - this->subscribed_bot = bot; - } + if(bot) { + bot->add_subscriber(_this.lock()); + this->subscribed_bot = bot; + } - return CommandResult::Success; + return CommandResult::Success; } void apply_song(Command& command, const std::shared_ptr& element, int index = 0) { - if(!element) return; + if(!element) return; - command[index]["song_id"] = element ? element->getSongId() : 0; - command[index]["song_url"] = element ? element->getUrl() : ""; - command[index]["song_invoker"] = element ? element->getInvoker() : 0; - command[index]["song_loaded"] = false; + command[index]["song_id"] = element ? element->getSongId() : 0; + command[index]["song_url"] = element ? element->getUrl() : ""; + command[index]["song_invoker"] = element ? element->getInvoker() : 0; + command[index]["song_loaded"] = false; - auto entry = dynamic_pointer_cast(element); - if(entry) { - auto data = entry->song_loaded_data(); - command[index]["song_loaded"] = entry->song_loaded() && data; + auto entry = dynamic_pointer_cast(element); + if(entry) { + auto data = entry->song_loaded_data(); + command[index]["song_loaded"] = entry->song_loaded() && data; - if(entry->song_loaded() && data) { - command[index]["song_title"] = data->title; - command[index]["song_description"] = data->description; - command[index]["song_thumbnail"] = data->thumbnail; - command[index]["song_length"] = data->length.count(); - } - } + if(entry->song_loaded() && data) { + command[index]["song_title"] = data->title; + command[index]["song_description"] = data->description; + command[index]["song_thumbnail"] = data->thumbnail; + command[index]["song_length"] = data->length.count(); + } + } } CommandResult ConnectedClient::handleCommandMusicBotPlayerInfo(Command& cmd) { - if(!config::music::enabled) return {findError("music_disabled")}; + if(!config::music::enabled) return {findError("music_disabled")}; - CMD_REQ_SERVER; - CMD_RESET_IDLE; - CMD_CHK_AND_INC_FLOOD_POINTS(5); + CMD_REQ_SERVER; + CMD_RESET_IDLE; + CMD_CHK_AND_INC_FLOOD_POINTS(5); - auto bot = this->server->musicManager->findBotById(cmd["bot_id"]); - if(!bot) return {findError("music_invalid_id")}; + auto bot = this->server->musicManager->findBotById(cmd["bot_id"]); + if(!bot) return {findError("music_invalid_id")}; - Command result(this->getExternalType() == CLIENT_TEAMSPEAK ? "notifymusicplayerinfo" : ""); - result["bot_id"] = bot->getClientDatabaseId(); + Command result(this->getExternalType() == CLIENT_TEAMSPEAK ? "notifymusicplayerinfo" : ""); + result["bot_id"] = bot->getClientDatabaseId(); - result["player_state"] =(int) bot->player_state(); - if(bot->current_player()) { - result["player_buffered_index"] = bot->current_player()->bufferedUntil().count(); - result["player_replay_index"] = bot->current_player()->currentIndex().count(); - result["player_max_index"] = bot->current_player()->length().count(); - result["player_seekable"] = bot->current_player()->seek_supported(); + result["player_state"] =(int) bot->player_state(); + if(bot->current_player()) { + result["player_buffered_index"] = bot->current_player()->bufferedUntil().count(); + result["player_replay_index"] = bot->current_player()->currentIndex().count(); + result["player_max_index"] = bot->current_player()->length().count(); + result["player_seekable"] = bot->current_player()->seek_supported(); - result["player_title"] = bot->current_player()->songTitle(); - result["player_description"] = bot->current_player()->songDescription(); - } else { - result["player_buffered_index"] = 0; - result["player_replay_index"] = 0; - result["player_max_index"] = 0; - result["player_seekable"] = 0; - result["player_title"] = ""; - result["player_description"] = ""; - } + result["player_title"] = bot->current_player()->songTitle(); + result["player_description"] = bot->current_player()->songDescription(); + } else { + result["player_buffered_index"] = 0; + result["player_replay_index"] = 0; + result["player_max_index"] = 0; + result["player_seekable"] = 0; + result["player_title"] = ""; + result["player_description"] = ""; + } - apply_song(result, bot->current_song()); - this->sendCommand(result); + apply_song(result, bot->current_song()); + this->sendCommand(result); - return CommandResult::Success; + return CommandResult::Success; } CommandResult ConnectedClient::handleCommandMusicBotPlayerAction(Command& cmd) { - if(!config::music::enabled) return {findError("music_disabled")}; + if(!config::music::enabled) return {findError("music_disabled")}; - CMD_REQ_SERVER; - CMD_RESET_IDLE; - CMD_CHK_AND_INC_FLOOD_POINTS(25); + CMD_REQ_SERVER; + CMD_RESET_IDLE; + CMD_CHK_AND_INC_FLOOD_POINTS(25); - auto bot = this->server->musicManager->findBotById(cmd["bot_id"]); - if(!bot) return {findError("music_invalid_id")}; + auto bot = this->server->musicManager->findBotById(cmd["bot_id"]); + if(!bot) return {findError("music_invalid_id")}; PERM_CHECK_CHANNELR(permission::i_client_music_play_power, bot->permissionValue(permission::PERMTEST_ORDERED, permission::i_client_music_needed_play_power, bot->currentChannel), this->currentChannel, true); - if(cmd["action"] == 0) { - bot->stopMusic(); - } else if(cmd["action"] == 1) { - bot->playMusic(); - } else if(cmd["action"] == 2) { - bot->player_pause(); - } else if(cmd["action"] == 3) { - bot->forwardSong(); - } else if(cmd["action"] == 4) { - bot->rewindSong(); - } else if(cmd["action"] == 5) { - if(!bot->current_player()) return {findError("music_no_player")}; - bot->current_player()->forward(::music::PlayerUnits(cmd["units"].as())); - } else if(cmd["action"] == 6) { - if(!bot->current_player()) return {findError("music_no_player")}; - bot->current_player()->rewind(::music::PlayerUnits(cmd["units"].as())); - } else return {findError("music_invalid_action")}; + if(cmd["action"] == 0) { + bot->stopMusic(); + } else if(cmd["action"] == 1) { + bot->playMusic(); + } else if(cmd["action"] == 2) { + bot->player_pause(); + } else if(cmd["action"] == 3) { + bot->forwardSong(); + } else if(cmd["action"] == 4) { + bot->rewindSong(); + } else if(cmd["action"] == 5) { + if(!bot->current_player()) return {findError("music_no_player")}; + bot->current_player()->forward(::music::PlayerUnits(cmd["units"].as())); + } else if(cmd["action"] == 6) { + if(!bot->current_player()) return {findError("music_no_player")}; + bot->current_player()->rewind(::music::PlayerUnits(cmd["units"].as())); + } else return {findError("music_invalid_action")}; - return CommandResult::Success; + return CommandResult::Success; } CommandResult ConnectedClient::handleCommandPlaylistList(ts::Command &cmd) { - CMD_REQ_SERVER; - CMD_RESET_IDLE; - CMD_CHK_AND_INC_FLOOD_POINTS(25); + CMD_REQ_SERVER; + CMD_RESET_IDLE; + CMD_CHK_AND_INC_FLOOD_POINTS(25); - auto self_dbid = this->getClientDatabaseId(); - auto playlist_view_power = this->cached_permission_value(permission::i_playlist_view_power); - auto playlists = this->server->musicManager->playlists(); + auto self_dbid = this->getClientDatabaseId(); + auto playlist_view_power = this->cached_permission_value(permission::i_playlist_view_power); + auto playlists = this->server->musicManager->playlists(); - playlists.erase(find_if(playlists.begin(), playlists.end(), [&](const shared_ptr& playlist) { - if(playlist->properties()[property::PLAYLIST_OWNER_DBID] == self_dbid) - return false; + playlists.erase(find_if(playlists.begin(), playlists.end(), [&](const shared_ptr& playlist) { + if(playlist->properties()[property::PLAYLIST_OWNER_DBID] == self_dbid) + return false; - auto needed_view_power = playlist->permissions()->getPermissionValue(permission::i_playlist_needed_view_power); - return !this->permission_granted(playlist_view_power, needed_view_power, false); - }), playlists.end()); + auto needed_view_power = playlist->permissions()->getPermissionValue(permission::i_playlist_needed_view_power); + return !this->permission_granted(playlist_view_power, needed_view_power, false); + }), playlists.end()); - if(playlists.empty()) - return {ErrorType::DBEmpty}; + if(playlists.empty()) + return {ErrorType::DBEmpty}; - Command notify(this->notify_response_command("notifyplaylistlist")); + Command notify(this->notify_response_command("notifyplaylistlist")); - size_t index = 0; - for(const auto& entry : playlists) { - notify[index]["playlist_id"] = entry->playlist_id(); - auto bot = entry->current_bot(); - notify[index]["playlist_bot_id"] = bot ? bot->getClientDatabaseId() : 0; - notify[index]["playlist_title"] = entry->properties()[property::PLAYLIST_TITLE].value(); - notify[index]["playlist_type"] = entry->properties()[property::PLAYLIST_TYPE].value(); - notify[index]["playlist_owner_dbid"] = entry->properties()[property::PLAYLIST_OWNER_DBID].value(); - notify[index]["playlist_owner_name"] = entry->properties()[property::PLAYLIST_OWNER_NAME].value(); - notify[index]["needed_power_modify"] = entry->permissions()->getPermissionValue(permission::i_playlist_needed_modify_power); - notify[index]["needed_power_permission_modify"] = entry->permissions()->getPermissionValue(permission::i_playlist_needed_permission_modify_power); - notify[index]["needed_power_delete"] = entry->permissions()->getPermissionValue(permission::i_playlist_needed_delete_power); - notify[index]["needed_power_song_add"] = entry->permissions()->getPermissionValue(permission::i_playlist_song_needed_add_power); - notify[index]["needed_power_song_move"] = entry->permissions()->getPermissionValue(permission::i_playlist_song_needed_move_power); - notify[index]["needed_power_song_remove"] = entry->permissions()->getPermissionValue(permission::i_playlist_song_needed_remove_power); - index++; - } + size_t index = 0; + for(const auto& entry : playlists) { + notify[index]["playlist_id"] = entry->playlist_id(); + auto bot = entry->current_bot(); + notify[index]["playlist_bot_id"] = bot ? bot->getClientDatabaseId() : 0; + notify[index]["playlist_title"] = entry->properties()[property::PLAYLIST_TITLE].value(); + notify[index]["playlist_type"] = entry->properties()[property::PLAYLIST_TYPE].value(); + notify[index]["playlist_owner_dbid"] = entry->properties()[property::PLAYLIST_OWNER_DBID].value(); + notify[index]["playlist_owner_name"] = entry->properties()[property::PLAYLIST_OWNER_NAME].value(); + notify[index]["needed_power_modify"] = entry->permissions()->getPermissionValue(permission::i_playlist_needed_modify_power); + notify[index]["needed_power_permission_modify"] = entry->permissions()->getPermissionValue(permission::i_playlist_needed_permission_modify_power); + notify[index]["needed_power_delete"] = entry->permissions()->getPermissionValue(permission::i_playlist_needed_delete_power); + notify[index]["needed_power_song_add"] = entry->permissions()->getPermissionValue(permission::i_playlist_song_needed_add_power); + notify[index]["needed_power_song_move"] = entry->permissions()->getPermissionValue(permission::i_playlist_song_needed_move_power); + notify[index]["needed_power_song_remove"] = entry->permissions()->getPermissionValue(permission::i_playlist_song_needed_remove_power); + index++; + } - this->sendCommand(notify); - return CommandResult::Success; + this->sendCommand(notify); + return CommandResult::Success; } CommandResult ConnectedClient::handleCommandPlaylistCreate(ts::Command &cmd) { - CMD_REF_SERVER(ref_server); - CMD_RESET_IDLE; - CMD_CHK_AND_INC_FLOOD_POINTS(25); - CACHED_PERM_CHECK(permission::b_playlist_create, 1, true); + CMD_REF_SERVER(ref_server); + CMD_RESET_IDLE; + CMD_CHK_AND_INC_FLOOD_POINTS(25); + CACHED_PERM_CHECK(permission::b_playlist_create, 1, true); { auto max_playlists = this->cached_permission_value(permission::i_max_playlists); @@ -6775,10 +6775,10 @@ CommandResult ConnectedClient::handleCommandPlaylistCreate(ts::Command &cmd) { } } - auto playlist = ref_server->musicManager->create_playlist(this->getClientDatabaseId(), this->getDisplayName()); - if(!playlist) return {ErrorType::VSError}; + auto playlist = ref_server->musicManager->create_playlist(this->getClientDatabaseId(), this->getDisplayName()); + if(!playlist) return {ErrorType::VSError}; - playlist->properties()[property::PLAYLIST_TYPE] = music::Playlist::Type::GENERAL; + playlist->properties()[property::PLAYLIST_TYPE] = music::Playlist::Type::GENERAL; { auto max_songs = this->cached_permission_value(permission::i_max_playlist_size); @@ -6786,419 +6786,419 @@ CommandResult ConnectedClient::handleCommandPlaylistCreate(ts::Command &cmd) { playlist->properties()[property::PLAYLIST_MAX_SONGS] = max_songs; } - playlist->permissions()->setPermission(permission::i_playlist_song_needed_remove_power, this->cached_permission_value(permission::i_playlist_song_remove_power), nullptr); - playlist->permissions()->setPermission(permission::i_playlist_needed_delete_power, this->cached_permission_value(permission::i_playlist_delete_power), nullptr); - playlist->permissions()->setPermission(permission::i_playlist_needed_modify_power, this->cached_permission_value(permission::i_playlist_modify_power), nullptr); - playlist->permissions()->setPermission(permission::i_playlist_needed_permission_modify_power, this->cached_permission_value(permission::i_playlist_permission_modify_power), nullptr); + playlist->permissions()->setPermission(permission::i_playlist_song_needed_remove_power, this->cached_permission_value(permission::i_playlist_song_remove_power), nullptr); + playlist->permissions()->setPermission(permission::i_playlist_needed_delete_power, this->cached_permission_value(permission::i_playlist_delete_power), nullptr); + playlist->permissions()->setPermission(permission::i_playlist_needed_modify_power, this->cached_permission_value(permission::i_playlist_modify_power), nullptr); + playlist->permissions()->setPermission(permission::i_playlist_needed_permission_modify_power, this->cached_permission_value(permission::i_playlist_permission_modify_power), nullptr); - Command notify(this->notify_response_command("notifyplaylistcreated")); - notify["playlist_id"] = playlist->playlist_id(); - this->sendCommand(notify); + Command notify(this->notify_response_command("notifyplaylistcreated")); + notify["playlist_id"] = playlist->playlist_id(); + this->sendCommand(notify); - return CommandResult::Success; + return CommandResult::Success; } CommandResult ConnectedClient::handleCommandPlaylistDelete(ts::Command &cmd) { - CMD_REF_SERVER(ref_server); - CMD_RESET_IDLE; - CMD_CHK_AND_INC_FLOOD_POINTS(25); + CMD_REF_SERVER(ref_server); + CMD_RESET_IDLE; + CMD_CHK_AND_INC_FLOOD_POINTS(25); - auto playlist = ref_server->musicManager->find_playlist(cmd["playlist_id"]); - if(!playlist) return {findError("playlist_invalid_id")}; + auto playlist = ref_server->musicManager->find_playlist(cmd["playlist_id"]); + if(!playlist) return {findError("playlist_invalid_id")}; - if(playlist->properties()[property::PLAYLIST_OWNER_DBID] != this->getClientDatabaseId()) - CACHED_PERM_CHECK(permission::i_playlist_delete_power, playlist->permissions()->getPermissionValue(permission::i_playlist_needed_delete_power)); + if(playlist->properties()[property::PLAYLIST_OWNER_DBID] != this->getClientDatabaseId()) + CACHED_PERM_CHECK(permission::i_playlist_delete_power, playlist->permissions()->getPermissionValue(permission::i_playlist_needed_delete_power)); - string error; - if(!ref_server->musicManager->delete_playlist(playlist->playlist_id(), error)) { - logError(this->getServerId(), "Failed to delete playlist with id {}. Error: {}", playlist->playlist_id(), error); - return {ErrorType::VSError, error}; - } + string error; + if(!ref_server->musicManager->delete_playlist(playlist->playlist_id(), error)) { + logError(this->getServerId(), "Failed to delete playlist with id {}. Error: {}", playlist->playlist_id(), error); + return {ErrorType::VSError, error}; + } - return CommandResult::Success; + return CommandResult::Success; } CommandResult ConnectedClient::handleCommandPlaylistInfo(ts::Command &cmd) { - CMD_REF_SERVER(ref_server); - CMD_RESET_IDLE; - CMD_CHK_AND_INC_FLOOD_POINTS(25); + CMD_REF_SERVER(ref_server); + CMD_RESET_IDLE; + CMD_CHK_AND_INC_FLOOD_POINTS(25); - auto playlist = ref_server->musicManager->find_playlist(cmd["playlist_id"]); - if(!playlist) return {findError("playlist_invalid_id")}; + auto playlist = ref_server->musicManager->find_playlist(cmd["playlist_id"]); + if(!playlist) return {findError("playlist_invalid_id")}; - if(playlist->properties()[property::PLAYLIST_OWNER_DBID] != this->getClientDatabaseId()) - CACHED_PERM_CHECK(permission::i_playlist_view_power, playlist->permissions()->getPermissionValue(permission::i_playlist_needed_view_power)); + if(playlist->properties()[property::PLAYLIST_OWNER_DBID] != this->getClientDatabaseId()) + CACHED_PERM_CHECK(permission::i_playlist_view_power, playlist->permissions()->getPermissionValue(permission::i_playlist_needed_view_power)); - Command notify(this->notify_response_command("notifyplaylistinfo")); - for(const auto& property : playlist->properties().list_properties(property::FLAG_PLAYLIST_VARIABLE)) { - notify[property.type().name] = property.value(); - } - this->sendCommand(notify); + Command notify(this->notify_response_command("notifyplaylistinfo")); + for(const auto& property : playlist->properties().list_properties(property::FLAG_PLAYLIST_VARIABLE)) { + notify[property.type().name] = property.value(); + } + this->sendCommand(notify); - return CommandResult::Success; + return CommandResult::Success; } CommandResult ConnectedClient::handleCommandPlaylistEdit(ts::Command &cmd) { - CMD_REF_SERVER(ref_server); - CMD_RESET_IDLE; - CMD_CHK_AND_INC_FLOOD_POINTS(25); + CMD_REF_SERVER(ref_server); + CMD_RESET_IDLE; + CMD_CHK_AND_INC_FLOOD_POINTS(25); - auto playlist = ref_server->musicManager->find_playlist(cmd["playlist_id"]); - if(!playlist) return {findError("playlist_invalid_id")}; + auto playlist = ref_server->musicManager->find_playlist(cmd["playlist_id"]); + if(!playlist) return {findError("playlist_invalid_id")}; - if(playlist->properties()[property::PLAYLIST_OWNER_DBID] != this->getClientDatabaseId()) - CACHED_PERM_CHECK(permission::i_playlist_modify_power, playlist->permissions()->getPermissionValue(permission::i_playlist_needed_modify_power)); + if(playlist->properties()[property::PLAYLIST_OWNER_DBID] != this->getClientDatabaseId()) + CACHED_PERM_CHECK(permission::i_playlist_modify_power, playlist->permissions()->getPermissionValue(permission::i_playlist_needed_modify_power)); - deque, string>> properties; + deque, string>> properties; - for(const auto& key : cmd[0].keys()) { - if(key == "playlist_id") continue; - if(key == "return_code") continue; + for(const auto& key : cmd[0].keys()) { + if(key == "playlist_id") continue; + if(key == "return_code") continue; - auto property = property::info(key); - if(*property == property::PLAYLIST_UNDEFINED) { - logError(this->getServerId(), R"([{}] Tried to edit a not existing playlist property "{}" to "{}")", CLIENT_STR_LOG_PREFIX, key, cmd[key].string()); - continue; - } + auto property = property::info(key); + if(*property == property::PLAYLIST_UNDEFINED) { + logError(this->getServerId(), R"([{}] Tried to edit a not existing playlist property "{}" to "{}")", CLIENT_STR_LOG_PREFIX, key, cmd[key].string()); + continue; + } - if((property->flags & property::FLAG_USER_EDITABLE) == 0) { - logError(this->getServerId(), "[{}] Tried to change a playlist property which is not changeable. (Key: {}, Value: \"{}\")", CLIENT_STR_LOG_PREFIX, key, cmd[key].string()); - continue; - } + if((property->flags & property::FLAG_USER_EDITABLE) == 0) { + logError(this->getServerId(), "[{}] Tried to change a playlist property which is not changeable. (Key: {}, Value: \"{}\")", CLIENT_STR_LOG_PREFIX, key, cmd[key].string()); + continue; + } - if(!property->validate_input(cmd[key].as())) { - logError(this->getServerId(), "[{}] Tried to change a playlist property to an invalid value. (Key: {}, Value: \"{}\")", CLIENT_STR_LOG_PREFIX, key, cmd[key].string()); - continue; - } + if(!property->validate_input(cmd[key].as())) { + logError(this->getServerId(), "[{}] Tried to change a playlist property to an invalid value. (Key: {}, Value: \"{}\")", CLIENT_STR_LOG_PREFIX, key, cmd[key].string()); + continue; + } - if(*property == property::PLAYLIST_CURRENT_SONG_ID) { - auto song_id = cmd[key].as(); - auto song = song_id > 0 ? playlist->find_song(song_id) : nullptr; - if(song_id != 0 && !song) - return {findError("playlist_invalid_song_id")}; - } else if(*property == property::PLAYLIST_MAX_SONGS) { + if(*property == property::PLAYLIST_CURRENT_SONG_ID) { + auto song_id = cmd[key].as(); + auto song = song_id > 0 ? playlist->find_song(song_id) : nullptr; + if(song_id != 0 && !song) + return {findError("playlist_invalid_song_id")}; + } else if(*property == property::PLAYLIST_MAX_SONGS) { auto value = cmd[key].as(); auto max_value = this->cached_permission_value(permission::i_max_playlist_size); if(!this->permission_granted(max_value, value, false)) return CommandResultPermissionError{permission::i_max_playlist_size}; } - properties.emplace_back(property, key); - } - for(const auto& property : properties) { - if(*property.first == property::PLAYLIST_CURRENT_SONG_ID) { - playlist->set_current_song(cmd[property.second]); - continue; - } + properties.emplace_back(property, key); + } + for(const auto& property : properties) { + if(*property.first == property::PLAYLIST_CURRENT_SONG_ID) { + playlist->set_current_song(cmd[property.second]); + continue; + } - playlist->properties()[property.first] = cmd[property.second].string(); - } - return CommandResult::Success; + playlist->properties()[property.first] = cmd[property.second].string(); + } + return CommandResult::Success; } CommandResult ConnectedClient::handleCommandPlaylistPermList(ts::Command &cmd) { - CMD_REF_SERVER(ref_server); - CMD_RESET_IDLE; - CMD_CHK_AND_INC_FLOOD_POINTS(25); + CMD_REF_SERVER(ref_server); + CMD_RESET_IDLE; + CMD_CHK_AND_INC_FLOOD_POINTS(25); - auto playlist = ref_server->musicManager->find_playlist(cmd["playlist_id"]); - if(!playlist) return {findError("playlist_invalid_id")}; + auto playlist = ref_server->musicManager->find_playlist(cmd["playlist_id"]); + if(!playlist) return {findError("playlist_invalid_id")}; - if(playlist->properties()[property::PLAYLIST_OWNER_DBID] != this->getClientDatabaseId()) - CACHED_PERM_CHECK(permission::b_virtualserver_playlist_permission_list, 1, true); + if(playlist->properties()[property::PLAYLIST_OWNER_DBID] != this->getClientDatabaseId()) + CACHED_PERM_CHECK(permission::b_virtualserver_playlist_permission_list, 1, true); - auto permissions = playlist->permissions()->listPermissions(PERM_FLAG_PUBLIC); - if(permissions.empty()) - return {ErrorType::VSError}; + auto permissions = playlist->permissions()->listPermissions(PERM_FLAG_PUBLIC); + if(permissions.empty()) + return {ErrorType::VSError}; - Command result(this->notify_response_command("notifyplaylistpermlist")); - int index = 0; - result["playlist_id"] = playlist->playlist_id(); - for (const auto &elm : permissions) { - if(elm->hasValue()) { - result[index]["permid"] = elm->type->type; + Command result(this->notify_response_command("notifyplaylistpermlist")); + int index = 0; + result["playlist_id"] = playlist->playlist_id(); + for (const auto &elm : permissions) { + if(elm->hasValue()) { + result[index]["permid"] = elm->type->type; - result[index]["permvalue"] = elm->value; - result[index]["permnegated"] = elm->flag_negate; - result[index]["permskip"] = elm->flag_skip; - index++; - } - if(elm->hasGrant()) { - result[index]["permid"] = (uint16_t) (elm->type->type | PERM_ID_GRANT); - result[index]["permvalue"] = elm->granted; - result[index]["permnegated"] = 0; - result[index]["permskip"] = 0; - index++; - } - } - this->sendCommand(result); + result[index]["permvalue"] = elm->value; + result[index]["permnegated"] = elm->flag_negate; + result[index]["permskip"] = elm->flag_skip; + index++; + } + if(elm->hasGrant()) { + result[index]["permid"] = (uint16_t) (elm->type->type | PERM_ID_GRANT); + result[index]["permvalue"] = elm->granted; + result[index]["permnegated"] = 0; + result[index]["permskip"] = 0; + index++; + } + } + this->sendCommand(result); - return CommandResult::Success; + return CommandResult::Success; } CommandResult ConnectedClient::handleCommandPlaylistAddPerm(ts::Command &cmd) { - CMD_REF_SERVER(ref_server); - CMD_RESET_IDLE; - CMD_CHK_AND_INC_FLOOD_POINTS(5); + CMD_REF_SERVER(ref_server); + CMD_RESET_IDLE; + CMD_CHK_AND_INC_FLOOD_POINTS(5); - auto playlist = ref_server->musicManager->find_playlist(cmd["playlist_id"]); - if(!playlist) return {findError("playlist_invalid_id")}; + auto playlist = ref_server->musicManager->find_playlist(cmd["playlist_id"]); + if(!playlist) return {findError("playlist_invalid_id")}; - if(playlist->properties()[property::PLAYLIST_OWNER_DBID] != this->getClientDatabaseId()) - CACHED_PERM_CHECK(permission::i_playlist_permission_modify_power, playlist->permissions()->getPermissionValue(permission::i_playlist_needed_permission_modify_power), true); + if(playlist->properties()[property::PLAYLIST_OWNER_DBID] != this->getClientDatabaseId()) + CACHED_PERM_CHECK(permission::i_playlist_permission_modify_power, playlist->permissions()->getPermissionValue(permission::i_playlist_needed_permission_modify_power), true); - auto maxValue = this->getPermissionGrantValue(permission::i_permission_modify_power, this->currentChannel); - bool ignoreGrant = this->permission_granted(this->cached_permission_value(permission::b_permission_modify_power_ignore), 1, true); + auto maxValue = this->getPermissionGrantValue(permission::i_permission_modify_power, this->currentChannel); + bool ignoreGrant = this->permission_granted(this->cached_permission_value(permission::b_permission_modify_power_ignore), 1, true); - bool conOnError = cmd[0].has("continueonerror"); - for (int index = 0; index < cmd.bulkCount(); index++) { - PARSE_PERMISSION(cmd); + bool conOnError = cmd[0].has("continueonerror"); + for (int index = 0; index < cmd.bulkCount(); index++) { + PARSE_PERMISSION(cmd); - auto val = cmd[index]["permvalue"].as(); - if(permission_require_granted_value(permType) && val > maxValue) - return CommandResultPermissionError{permission::i_permission_modify_power}; + auto val = cmd[index]["permvalue"].as(); + if(permission_require_granted_value(permType) && val > maxValue) + return CommandResultPermissionError{permission::i_permission_modify_power}; - if(!ignoreGrant && !this->permissionGrantGranted(permission::PERMTEST_ORDERED, permType, 1, this->currentChannel)) - return CommandResultPermissionError{permission::i_permission_modify_power}; + if(!ignoreGrant && !this->permissionGrantGranted(permission::PERMTEST_ORDERED, permType, 1, this->currentChannel)) + return CommandResultPermissionError{permission::i_permission_modify_power}; - if (grant) { - playlist->permissions()->setPermissionGranted(permType, cmd[index]["permvalue"], nullptr); - } else { + if (grant) { + playlist->permissions()->setPermissionGranted(permType, cmd[index]["permvalue"], nullptr); + } else { - playlist->permissions()->setPermission(permType, cmd[index]["permvalue"], nullptr, cmd[index]["permskip"], cmd[index]["permnegated"]); - } - } + playlist->permissions()->setPermission(permType, cmd[index]["permvalue"], nullptr, cmd[index]["permskip"], cmd[index]["permnegated"]); + } + } - return CommandResult::Success; + return CommandResult::Success; } CommandResult ConnectedClient::handleCommandPlaylistDelPerm(ts::Command &cmd) { - CMD_REF_SERVER(ref_server); - CMD_RESET_IDLE; - CMD_CHK_AND_INC_FLOOD_POINTS(5); + CMD_REF_SERVER(ref_server); + CMD_RESET_IDLE; + CMD_CHK_AND_INC_FLOOD_POINTS(5); - auto playlist = ref_server->musicManager->find_playlist(cmd["playlist_id"]); - if(!playlist) return {findError("playlist_invalid_id")}; + auto playlist = ref_server->musicManager->find_playlist(cmd["playlist_id"]); + if(!playlist) return {findError("playlist_invalid_id")}; - if(playlist->properties()[property::PLAYLIST_OWNER_DBID] != this->getClientDatabaseId()) - CACHED_PERM_CHECK(permission::i_playlist_permission_modify_power, playlist->permissions()->getPermissionValue(permission::i_playlist_needed_permission_modify_power), true); + if(playlist->properties()[property::PLAYLIST_OWNER_DBID] != this->getClientDatabaseId()) + CACHED_PERM_CHECK(permission::i_playlist_permission_modify_power, playlist->permissions()->getPermissionValue(permission::i_playlist_needed_permission_modify_power), true); - bool ignoreGrant = this->permission_granted(this->cached_permission_value(permission::b_permission_modify_power_ignore), 1, true); - bool conOnError = cmd[0].has("continueonerror"); + bool ignoreGrant = this->permission_granted(this->cached_permission_value(permission::b_permission_modify_power_ignore), 1, true); + bool conOnError = cmd[0].has("continueonerror"); - for (int index = 0; index < cmd.bulkCount(); index++) { - PARSE_PERMISSION(cmd); + for (int index = 0; index < cmd.bulkCount(); index++) { + PARSE_PERMISSION(cmd); - if(!ignoreGrant && !this->permissionGrantGranted(permission::PERMTEST_ORDERED, permType, 1, this->currentChannel)) - return CommandResultPermissionError{permission::i_permission_modify_power}; + if(!ignoreGrant && !this->permissionGrantGranted(permission::PERMTEST_ORDERED, permType, 1, this->currentChannel)) + return CommandResultPermissionError{permission::i_permission_modify_power}; - if (grant) { - playlist->permissions()->setPermissionGranted(permType, permNotGranted, nullptr); - } else { - playlist->permissions()->deletePermission(permType, nullptr); - } - } + if (grant) { + playlist->permissions()->setPermissionGranted(permType, permNotGranted, nullptr); + } else { + playlist->permissions()->deletePermission(permType, nullptr); + } + } - return CommandResult::Success; + return CommandResult::Success; } CommandResult ConnectedClient::handleCommandPlaylistSongList(ts::Command &cmd) { - CMD_REF_SERVER(ref_server); - CMD_RESET_IDLE; - CMD_CHK_AND_INC_FLOOD_POINTS(25); + CMD_REF_SERVER(ref_server); + CMD_RESET_IDLE; + CMD_CHK_AND_INC_FLOOD_POINTS(25); - auto playlist = ref_server->musicManager->find_playlist(cmd["playlist_id"]); - if(!playlist) return {findError("playlist_invalid_id")}; + auto playlist = ref_server->musicManager->find_playlist(cmd["playlist_id"]); + if(!playlist) return {findError("playlist_invalid_id")}; - if(playlist->properties()[property::PLAYLIST_OWNER_DBID] != this->getClientDatabaseId()) - CACHED_PERM_CHECK(permission::i_playlist_view_power, playlist->permissions()->getPermissionValue(permission::i_playlist_needed_view_power)); + if(playlist->properties()[property::PLAYLIST_OWNER_DBID] != this->getClientDatabaseId()) + CACHED_PERM_CHECK(permission::i_playlist_view_power, playlist->permissions()->getPermissionValue(permission::i_playlist_needed_view_power)); - auto songs = playlist->list_songs(); - if(songs.empty()) - return {ErrorType::DBEmpty}; + auto songs = playlist->list_songs(); + if(songs.empty()) + return {ErrorType::DBEmpty}; - Command notify(this->notify_response_command("notifyplaylistsonglist")); - notify["playlist_id"] = playlist->playlist_id(); + Command notify(this->notify_response_command("notifyplaylistsonglist")); + notify["playlist_id"] = playlist->playlist_id(); - size_t index = 0; - for(const auto& song : songs) { - notify[index]["song_id"] = song->id; - notify[index]["song_invoker"] = song->invoker; - notify[index]["song_previous_song_id"] = song->previous_song_id; - notify[index]["song_url"] = song->url; - notify[index]["song_url_loader"] = song->url_loader; - notify[index]["song_loaded"] = song->loaded; - notify[index]["song_metadata"] = song->metadata; - index++; - } - this->sendCommand(notify); + size_t index = 0; + for(const auto& song : songs) { + notify[index]["song_id"] = song->id; + notify[index]["song_invoker"] = song->invoker; + notify[index]["song_previous_song_id"] = song->previous_song_id; + notify[index]["song_url"] = song->url; + notify[index]["song_url_loader"] = song->url_loader; + notify[index]["song_loaded"] = song->loaded; + notify[index]["song_metadata"] = song->metadata; + index++; + } + this->sendCommand(notify); - return CommandResult::Success; + return CommandResult::Success; } CommandResult ConnectedClient::handleCommandPlaylistSongAdd(ts::Command &cmd) { - CMD_REF_SERVER(ref_server); - CMD_RESET_IDLE; - CMD_CHK_AND_INC_FLOOD_POINTS(25); + CMD_REF_SERVER(ref_server); + CMD_RESET_IDLE; + CMD_CHK_AND_INC_FLOOD_POINTS(25); - auto playlist = ref_server->musicManager->find_playlist(cmd["playlist_id"]); - if(!playlist) return {findError("playlist_invalid_id")}; + auto playlist = ref_server->musicManager->find_playlist(cmd["playlist_id"]); + if(!playlist) return {findError("playlist_invalid_id")}; - if(playlist->properties()[property::PLAYLIST_OWNER_DBID] != this->getClientDatabaseId()) - CACHED_PERM_CHECK(permission::i_playlist_song_add_power, playlist->permissions()->getPermissionValue(permission::i_playlist_song_needed_add_power)); + if(playlist->properties()[property::PLAYLIST_OWNER_DBID] != this->getClientDatabaseId()) + CACHED_PERM_CHECK(permission::i_playlist_song_add_power, playlist->permissions()->getPermissionValue(permission::i_playlist_song_needed_add_power)); - if(!cmd[0].has("invoker")) - cmd["invoker"] = ""; - if(!cmd[0].has("previous")) { - auto songs = playlist->list_songs(); - if(songs.empty()) - cmd["previous"] = "0"; - else - cmd["previous"] = songs.back()->id; - } + if(!cmd[0].has("invoker")) + cmd["invoker"] = ""; + if(!cmd[0].has("previous")) { + auto songs = playlist->list_songs(); + if(songs.empty()) + cmd["previous"] = "0"; + else + cmd["previous"] = songs.back()->id; + } - auto song = playlist->add_song(_this.lock(), cmd["url"], cmd["invoker"], cmd["previous"]); - if(!song) return {ErrorType::VSError}; + auto song = playlist->add_song(_this.lock(), cmd["url"], cmd["invoker"], cmd["previous"]); + if(!song) return {ErrorType::VSError}; - Command notify(this->notify_response_command("notifyplaylistsongadd")); - notify["song_id"] = song->id; - this->sendCommand(notify); + Command notify(this->notify_response_command("notifyplaylistsongadd")); + notify["song_id"] = song->id; + this->sendCommand(notify); - return CommandResult::Success; + return CommandResult::Success; } CommandResult ConnectedClient::handleCommandPlaylistSongReorder(ts::Command &cmd) { - CMD_REF_SERVER(ref_server); - CMD_RESET_IDLE; - CMD_CHK_AND_INC_FLOOD_POINTS(25); + CMD_REF_SERVER(ref_server); + CMD_RESET_IDLE; + CMD_CHK_AND_INC_FLOOD_POINTS(25); - auto playlist = ref_server->musicManager->find_playlist(cmd["playlist_id"]); - if(!playlist) return {findError("playlist_invalid_id")}; + auto playlist = ref_server->musicManager->find_playlist(cmd["playlist_id"]); + if(!playlist) return {findError("playlist_invalid_id")}; - if(playlist->properties()[property::PLAYLIST_OWNER_DBID] != this->getClientDatabaseId()) - CACHED_PERM_CHECK(permission::i_playlist_song_move_power, playlist->permissions()->getPermissionValue(permission::i_playlist_song_needed_move_power)); + if(playlist->properties()[property::PLAYLIST_OWNER_DBID] != this->getClientDatabaseId()) + CACHED_PERM_CHECK(permission::i_playlist_song_move_power, playlist->permissions()->getPermissionValue(permission::i_playlist_song_needed_move_power)); - SongId song_id = cmd["song_id"]; - SongId previous_id = cmd["song_previous_song_id"]; + SongId song_id = cmd["song_id"]; + SongId previous_id = cmd["song_previous_song_id"]; - auto song = playlist->find_song(song_id); - if(!song) return {findError("playlist_invalid_song_id")}; + auto song = playlist->find_song(song_id); + if(!song) return {findError("playlist_invalid_song_id")}; - if(!playlist->reorder_song(song_id, previous_id)) - return {ErrorType::VSError}; + if(!playlist->reorder_song(song_id, previous_id)) + return {ErrorType::VSError}; - return CommandResult::Success; + return CommandResult::Success; } CommandResult ConnectedClient::handleCommandPlaylistSongRemove(ts::Command &cmd) { - CMD_REF_SERVER(ref_server); - CMD_RESET_IDLE; - CMD_CHK_AND_INC_FLOOD_POINTS(25); + CMD_REF_SERVER(ref_server); + CMD_RESET_IDLE; + CMD_CHK_AND_INC_FLOOD_POINTS(25); - auto playlist = ref_server->musicManager->find_playlist(cmd["playlist_id"]); - if(!playlist) return {findError("playlist_invalid_id")}; + auto playlist = ref_server->musicManager->find_playlist(cmd["playlist_id"]); + if(!playlist) return {findError("playlist_invalid_id")}; - if(playlist->properties()[property::PLAYLIST_OWNER_DBID] != this->getClientDatabaseId()) - CACHED_PERM_CHECK(permission::i_playlist_song_remove_power, playlist->permissions()->getPermissionValue(permission::i_playlist_song_needed_remove_power)); + if(playlist->properties()[property::PLAYLIST_OWNER_DBID] != this->getClientDatabaseId()) + CACHED_PERM_CHECK(permission::i_playlist_song_remove_power, playlist->permissions()->getPermissionValue(permission::i_playlist_song_needed_remove_power)); - SongId song_id = cmd["song_id"]; + SongId song_id = cmd["song_id"]; - auto song = playlist->find_song(song_id); - if(!song) return {findError("playlist_invalid_song_id")}; + auto song = playlist->find_song(song_id); + if(!song) return {findError("playlist_invalid_song_id")}; - if(!playlist->delete_song(song_id)) - return {ErrorType::VSError}; + if(!playlist->delete_song(song_id)) + return {ErrorType::VSError}; - return CommandResult::Success; + return CommandResult::Success; } CommandResult ConnectedClient::handleCommandMusicBotQueueList(Command& cmd) { - return CommandResult::NotImplemented; //FIXME + return CommandResult::NotImplemented; //FIXME - /* - CMD_REQ_SERVER; - CMD_RESET_IDLE; - CMD_CHK_AND_INC_FLOOD_POINTS(25); + /* + CMD_REQ_SERVER; + CMD_RESET_IDLE; + CMD_CHK_AND_INC_FLOOD_POINTS(25); - auto bot = this->server->musicManager->findBotById(cmd["bot_id"]); - if(!bot) return {findError("music_invalid_id")}; + auto bot = this->server->musicManager->findBotById(cmd["bot_id"]); + if(!bot) return {findError("music_invalid_id")}; PERM_CHECK_CHANNELR(permission::i_client_music_info, bot->permissionValue(permission::PERMTEST_ORDERED, permission::i_client_music_needed_info, bot->currentChannel), this->currentChannel, true); bool bulked = cmd.hasParm("bulk") || cmd.hasParm("balk") || cmd.hasParm("pipe") || cmd.hasParm("bar") || cmd.hasParm("paypal"); - int command_index = 0; + int command_index = 0; - Command notify(this->getExternalType() == CLIENT_VOICE ? "notifymusicqueueentry" : ""); - { - auto history = bot->queue()->history(); - for(int index = history.size(); index > 0; index--) { - if(!bulked) - notify = Command(this->getExternalType() == CLIENT_VOICE ? "notifymusicqueueentry" : ""); + Command notify(this->getExternalType() == CLIENT_VOICE ? "notifymusicqueueentry" : ""); + { + auto history = bot->queue()->history(); + for(int index = history.size(); index > 0; index--) { + if(!bulked) + notify = Command(this->getExternalType() == CLIENT_VOICE ? "notifymusicqueueentry" : ""); - apply_song(notify, history[index - 1], command_index); - notify[command_index]["queue_index"] = -index; + apply_song(notify, history[index - 1], command_index); + notify[command_index]["queue_index"] = -index; - if(!bulked) - this->sendCommand(notify); - else - command_index++; - } - } + if(!bulked) + this->sendCommand(notify); + else + command_index++; + } + } - { - if(!bulked) - notify = Command(this->getExternalType() == CLIENT_VOICE ? "notifymusicqueueentry" : ""); + { + if(!bulked) + notify = Command(this->getExternalType() == CLIENT_VOICE ? "notifymusicqueueentry" : ""); - auto song = bot->queue()->currentSong(); - apply_song(notify, song, command_index); - notify[command_index]["queue_index"] = 0; + auto song = bot->queue()->currentSong(); + apply_song(notify, song, command_index); + notify[command_index]["queue_index"] = 0; - if(!bulked) - this->sendCommand(notify); - else if(song) - command_index++; - } + if(!bulked) + this->sendCommand(notify); + else if(song) + command_index++; + } - { - auto queue = bot->queue()->queueEntries(); - for(int index = 0; index < queue.size(); index++) { - if(!bulked) - notify = Command(this->getExternalType() == CLIENT_VOICE ? "notifymusicqueueentry" : ""); + { + auto queue = bot->queue()->queueEntries(); + for(int index = 0; index < queue.size(); index++) { + if(!bulked) + notify = Command(this->getExternalType() == CLIENT_VOICE ? "notifymusicqueueentry" : ""); - apply_song(notify, queue[index], command_index); - notify[command_index]["queue_index"] = index + 1; + apply_song(notify, queue[index], command_index); + notify[command_index]["queue_index"] = index + 1; - if(!bulked) - this->sendCommand(notify); - else - command_index++; - } - } + if(!bulked) + this->sendCommand(notify); + else + command_index++; + } + } - debugMessage(this->getServerId(),"Send: {}",notify.build()); - if(bulked) { - if(command_index > 0) { - this->sendCommand(notify); - } else return { ErrorType::DBEmpty }; - } + debugMessage(this->getServerId(),"Send: {}",notify.build()); + if(bulked) { + if(command_index > 0) { + this->sendCommand(notify); + } else return { ErrorType::DBEmpty }; + } - if(this->getExternalType() == CLIENT_VOICE) { - Command notify("notifymusicqueuefinish"); - notify["bot_id"] = bot->getClientDatabaseId(); - this->sendCommand(notify); - } + if(this->getExternalType() == CLIENT_VOICE) { + Command notify("notifymusicqueuefinish"); + notify["bot_id"] = bot->getClientDatabaseId(); + this->sendCommand(notify); + } - return CommandResult::Success; - */ + return CommandResult::Success; + */ } CommandResult ConnectedClient::handleCommandMusicBotQueueAdd(Command& cmd) { - return CommandResult::NotImplemented; //FIXME + return CommandResult::NotImplemented; //FIXME - /* + /* CMD_REQ_SERVER; CMD_RESET_IDLE; CMD_CHK_AND_INC_FLOOD_POINTS(25); @@ -7207,16 +7207,16 @@ CommandResult ConnectedClient::handleCommandMusicBotQueueAdd(Command& cmd) { if(!bot) return {findError("music_invalid_id")}; PERM_CHECK_CHANNELR(permission::i_client_music_play_power, bot->permissionValue(permission::PERMTEST_ORDERED, permission::i_client_music_needed_play_power, bot->currentChannel), this->currentChannel, true); - MusicClient::loader_t loader; + MusicClient::loader_t loader; auto& type = cmd[0]["type"]; if((type.castable() && type.as() == 0) || type.as() == "yt") { loader = bot->ytLoader(this->getServer()); } else if((type.castable() && type.as() == 1) || type.as() == "ffmpeg") { loader = bot->ffmpegLoader(this->getServer()); } else if((type.castable() && type.as() == 2) || type.as() == "channel") { - loader = bot->channelLoader(this->getServer()); + loader = bot->channelLoader(this->getServer()); } else if((type.castable() && type.as() == -1) || type.as() == "any") { - loader = bot->providerLoader(this->getServer(), ""); + loader = bot->providerLoader(this->getServer(), ""); } if(!loader) return {findError("music_invalid_action")}; @@ -7232,9 +7232,9 @@ CommandResult ConnectedClient::handleCommandMusicBotQueueAdd(Command& cmd) { } CommandResult ConnectedClient::handleCommandMusicBotQueueRemove(Command& cmd) { - return CommandResult::NotImplemented; //FIXME + return CommandResult::NotImplemented; //FIXME - /* + /* CMD_REQ_SERVER; CMD_RESET_IDLE; CMD_CHK_AND_INC_FLOOD_POINTS(25); @@ -7245,13 +7245,13 @@ CommandResult ConnectedClient::handleCommandMusicBotQueueRemove(Command& cmd) { std::deque> songs; for(int index = 0; index < cmd.bulkCount(); index++) { - auto entry = bot->queue()->find_queue(cmd["song_id"]); - if(!entry) { - if(cmd.hasParm("skip_error")) continue; - return {ErrorType::DBEmpty}; - } + auto entry = bot->queue()->find_queue(cmd["song_id"]); + if(!entry) { + if(cmd.hasParm("skip_error")) continue; + return {ErrorType::DBEmpty}; + } - songs.push_back(move(entry)); + songs.push_back(move(entry)); } for(const auto& entry : songs) @@ -7264,96 +7264,96 @@ CommandResult ConnectedClient::handleCommandMusicBotQueueRemove(Command& cmd) { } CommandResult ConnectedClient::handleCommandMusicBotQueueReorder(Command& cmd) { - return CommandResult::NotImplemented; //FIXME + return CommandResult::NotImplemented; //FIXME - /* - CMD_REQ_SERVER; - CMD_RESET_IDLE; - CMD_CHK_AND_INC_FLOOD_POINTS(25); + /* + CMD_REQ_SERVER; + CMD_RESET_IDLE; + CMD_CHK_AND_INC_FLOOD_POINTS(25); - auto bot = this->server->musicManager->findBotById(cmd["bot_id"]); - if(!bot) return {findError("music_invalid_id")}; - PERM_CHECK_CHANNELR(permission::i_client_music_play_power, bot->permissionValue(permission::PERMTEST_ORDERED, permission::i_client_music_needed_play_power, bot->currentChannel), this->currentChannel, true); + auto bot = this->server->musicManager->findBotById(cmd["bot_id"]); + if(!bot) return {findError("music_invalid_id")}; + PERM_CHECK_CHANNELR(permission::i_client_music_play_power, bot->permissionValue(permission::PERMTEST_ORDERED, permission::i_client_music_needed_play_power, bot->currentChannel), this->currentChannel, true); - auto entry = bot->queue()->find_queue(cmd["song_id"]); - if(!entry) return {ErrorType::DBEmpty}; + auto entry = bot->queue()->find_queue(cmd["song_id"]); + if(!entry) return {ErrorType::DBEmpty}; - auto order = bot->queue()->changeOrder(entry, cmd["index"]); - if(order < 0) return {ErrorType::VSError}; - this->server->forEachClient([&](shared_ptr client) { - client->notifyMusicQueueOrderChange(bot, entry, order, _this.lock()); - }); - return CommandResult::Success; - */ + auto order = bot->queue()->changeOrder(entry, cmd["index"]); + if(order < 0) return {ErrorType::VSError}; + this->server->forEachClient([&](shared_ptr client) { + client->notifyMusicQueueOrderChange(bot, entry, order, _this.lock()); + }); + return CommandResult::Success; + */ } CommandResult ConnectedClient::handleCommandMusicBotPlaylistAssign(ts::Command &cmd) { - CMD_REF_SERVER(ref_server); - CMD_RESET_IDLE; - CMD_CHK_AND_INC_FLOOD_POINTS(25); + CMD_REF_SERVER(ref_server); + CMD_RESET_IDLE; + CMD_CHK_AND_INC_FLOOD_POINTS(25); - auto bot = ref_server->musicManager->findBotById(cmd["bot_id"]); - if(!bot) return {findError("music_invalid_id")}; - if(bot->getOwner() != this->getClientDatabaseId()) - PERM_CHECK_CHANNELR(permission::i_client_music_play_power, bot->permissionValue(permission::PERMTEST_ORDERED, permission::i_client_music_needed_play_power, bot->currentChannel), this->currentChannel, true); + auto bot = ref_server->musicManager->findBotById(cmd["bot_id"]); + if(!bot) return {findError("music_invalid_id")}; + if(bot->getOwner() != this->getClientDatabaseId()) + PERM_CHECK_CHANNELR(permission::i_client_music_play_power, bot->permissionValue(permission::PERMTEST_ORDERED, permission::i_client_music_needed_play_power, bot->currentChannel), this->currentChannel, true); - auto playlist = ref_server->musicManager->find_playlist(cmd["playlist_id"]); - if(!playlist && cmd["playlist_id"] != 0) return {findError("playlist_invalid_id")}; + auto playlist = ref_server->musicManager->find_playlist(cmd["playlist_id"]); + if(!playlist && cmd["playlist_id"] != 0) return {findError("playlist_invalid_id")}; - if(ref_server->musicManager->find_bot_by_playlist(playlist)) - return {findError("playlist_already_in_use")}; + if(ref_server->musicManager->find_bot_by_playlist(playlist)) + return {findError("playlist_already_in_use")}; - if(playlist && playlist->properties()[property::PLAYLIST_OWNER_DBID] != this->getClientDatabaseId()) - CACHED_PERM_CHECK(permission::i_playlist_view_power, playlist->permissions()->getPermissionValue(permission::i_playlist_needed_view_power)); + if(playlist && playlist->properties()[property::PLAYLIST_OWNER_DBID] != this->getClientDatabaseId()) + CACHED_PERM_CHECK(permission::i_playlist_view_power, playlist->permissions()->getPermissionValue(permission::i_playlist_needed_view_power)); - if(!ref_server->musicManager->assign_playlist(bot, playlist)) - return {ErrorType::VSError}; + if(!ref_server->musicManager->assign_playlist(bot, playlist)) + return {ErrorType::VSError}; - return CommandResult::Success; + return CommandResult::Success; } CommandResult ConnectedClient::handleCommandHelp(Command& cmd) { - CMD_RESET_IDLE; - CMD_CHK_AND_INC_FLOOD_POINTS(5); - PERM_CHECKR(permission::b_serverinstance_help_view, 1, false); + CMD_RESET_IDLE; + CMD_CHK_AND_INC_FLOOD_POINTS(5); + PERM_CHECKR(permission::b_serverinstance_help_view, 1, false); - string command = cmd[0].has("command") ? cmd["command"].as() : ""; - if(command.empty()) - for(const auto& key : cmd[0].keys()) { - command = key; - break; - } - if(command.empty()) - command = "help"; - std::transform(command.begin(), command.end(), command.begin(), ::tolower); + string command = cmd[0].has("command") ? cmd["command"].as() : ""; + if(command.empty()) + for(const auto& key : cmd[0].keys()) { + command = key; + break; + } + if(command.empty()) + command = "help"; + std::transform(command.begin(), command.end(), command.begin(), ::tolower); - auto file = fs::u8path("commanddocs/" + command + ".txt"); - if(!fs::exists(file)) return {findError("file_not_found"), "Could not resolve file " + file.string()}; + auto file = fs::u8path("commanddocs/" + command + ".txt"); + if(!fs::exists(file)) return {findError("file_not_found"), "Could not resolve file " + file.string()}; - string line; - ifstream stream(file); - if(!stream) return {findError("file_io_error"), "Could not read documentation file " + file.string()}; - while(getline(stream, line)) - this->sendCommand(Command{line}); + string line; + ifstream stream(file); + if(!stream) return {findError("file_io_error"), "Could not read documentation file " + file.string()}; + while(getline(stream, line)) + this->sendCommand(Command{line}); - return CommandResult::Success; + return CommandResult::Success; } CommandResult ConnectedClient::handleCommandPermReset(ts::Command& cmd) { - CMD_REQ_FSERVER; - CMD_RESET_IDLE; - CMD_CHK_AND_INC_FLOOD_POINTS(50); - PERM_CHECKR(permission::b_virtualserver_permission_reset, 1, true); + CMD_REQ_FSERVER; + CMD_RESET_IDLE; + CMD_CHK_AND_INC_FLOOD_POINTS(50); + PERM_CHECKR(permission::b_virtualserver_permission_reset, 1, true); - string token; - if(!this->server->resetPermissions(token)) - return {ErrorType::VSError, "Could not reset permissions!"}; + string token; + if(!this->server->resetPermissions(token)) + return {ErrorType::VSError, "Could not reset permissions!"}; - Command result(""); - result["token"] = token; - this->sendCommand(result); + Command result(""); + result["token"] = token; + this->sendCommand(result); - return CommandResult::Success; + return CommandResult::Success; } CommandResult ConnectedClient::handleCommandLogView(ts::Command& cmd) { @@ -7377,8 +7377,8 @@ CommandResult ConnectedClient::handleCommandLogView(ts::Command& cmd) { log_path = dynamic_pointer_cast(sink)->filename(); break; } else if(dynamic_pointer_cast(sink)) { - log_path = dynamic_pointer_cast(sink)->filename(); - break; + log_path = dynamic_pointer_cast(sink)->filename(); + break; } } if(log_path.empty()) @@ -7502,504 +7502,504 @@ CommandResult ConnectedClient::handleCommandLogView(ts::Command& cmd) { } CommandResult ConnectedClient::handleCommandUpdateMyTsId(ts::Command &) { - if(config::voice::suppress_myts_warnings) return CommandResult::Success; - return CommandResult::NotImplemented; + if(config::voice::suppress_myts_warnings) return CommandResult::Success; + return CommandResult::NotImplemented; } CommandResult ConnectedClient::handleCommandUpdateMyTsData(ts::Command &) { - if(config::voice::suppress_myts_warnings) return CommandResult::Success; - return CommandResult::NotImplemented; + if(config::voice::suppress_myts_warnings) return CommandResult::Success; + return CommandResult::NotImplemented; } CommandResult ConnectedClient::handleCommandQueryList(ts::Command &cmd) { - OptionalServerId server_id = EmptyServerId; - if(this->getExternalType() == ClientType::CLIENT_TEAMSPEAK) - server_id = this->getServerId(); + OptionalServerId server_id = EmptyServerId; + if(this->getExternalType() == ClientType::CLIENT_TEAMSPEAK) + server_id = this->getServerId(); - if(cmd[0].has("server_id")) - server_id = cmd["server_id"]; - if(cmd[0].has("sid")) - server_id = cmd["sid"]; + if(cmd[0].has("server_id")) + server_id = cmd["server_id"]; + if(cmd[0].has("sid")) + server_id = cmd["sid"]; - auto server = server_id == EmptyServerId ? nullptr : serverInstance->getVoiceServerManager()->findServerById(server_id); - if(!server && server_id != EmptyServerId && server_id != 0) - return {findError("server_invalid_id")}; + auto server = server_id == EmptyServerId ? nullptr : serverInstance->getVoiceServerManager()->findServerById(server_id); + if(!server && server_id != EmptyServerId && server_id != 0) + return {findError("server_invalid_id")}; - auto global_list = this->permissionGranted(permission::PERMTEST_ORDERED, permission::b_client_query_list, 1, nullptr, true, nullptr, server, true); - auto own_list = global_list || this->permissionGranted(permission::PERMTEST_ORDERED, permission::b_client_query_list_own, 1, nullptr, true, nullptr, server, true); + auto global_list = this->permissionGranted(permission::PERMTEST_ORDERED, permission::b_client_query_list, 1, nullptr, true, nullptr, server, true); + auto own_list = global_list || this->permissionGranted(permission::PERMTEST_ORDERED, permission::b_client_query_list_own, 1, nullptr, true, nullptr, server, true); - if(!own_list && !global_list) - return CommandResultPermissionError{permission::b_client_query_list}; + if(!own_list && !global_list) + return CommandResultPermissionError{permission::b_client_query_list}; - auto accounts = serverInstance->getQueryServer()->list_query_accounts(server_id); - if(!global_list) { - accounts.erase(remove_if(accounts.begin(), accounts.end(), [&](const std::shared_ptr& account) { - return account->unique_id != this->getUid(); - }), accounts.end()); - } + auto accounts = serverInstance->getQueryServer()->list_query_accounts(server_id); + if(!global_list) { + accounts.erase(remove_if(accounts.begin(), accounts.end(), [&](const std::shared_ptr& account) { + return account->unique_id != this->getUid(); + }), accounts.end()); + } - if(accounts.empty()) - return {ErrorType::DBEmpty}; + if(accounts.empty()) + return {ErrorType::DBEmpty}; - Command result(this->getExternalType() == ClientType::CLIENT_TEAMSPEAK ? "notifyquerylist" : ""); - result["server_id"] = server_id; - result["flag_own"] = own_list; - result["flag_all"] = global_list; + Command result(this->getExternalType() == ClientType::CLIENT_TEAMSPEAK ? "notifyquerylist" : ""); + result["server_id"] = server_id; + result["flag_own"] = own_list; + result["flag_all"] = global_list; - size_t index = 0; - for(const auto& account : accounts) { - result[index]["client_unique_identifier"] = account->unique_id; - result[index]["client_login_name"] = account->username; - result[index]["client_bound_server"] = account->bound_server; - index++; - } + size_t index = 0; + for(const auto& account : accounts) { + result[index]["client_unique_identifier"] = account->unique_id; + result[index]["client_login_name"] = account->username; + result[index]["client_bound_server"] = account->bound_server; + index++; + } - this->sendCommand(result); + this->sendCommand(result); - return CommandResult::Success; + return CommandResult::Success; } CommandResult ConnectedClient::handleCommandQueryCreate(ts::Command &cmd) { - OptionalServerId server_id = this->getServerId(); - if(cmd[0].has("server_id")) - server_id = cmd["server_id"]; - if(cmd[0].has("sid")) - server_id = cmd["sid"]; + OptionalServerId server_id = this->getServerId(); + if(cmd[0].has("server_id")) + server_id = cmd["server_id"]; + if(cmd[0].has("sid")) + server_id = cmd["sid"]; - auto server = server_id == EmptyServerId ? nullptr : serverInstance->getVoiceServerManager()->findServerById(server_id); - if(!server && server_id != EmptyServerId && server_id != 0) - return {findError("server_invalid_id")}; + auto server = server_id == EmptyServerId ? nullptr : serverInstance->getVoiceServerManager()->findServerById(server_id); + if(!server && server_id != EmptyServerId && server_id != 0) + return {findError("server_invalid_id")}; - if(!this->permissionGranted(permission::PERMTEST_ORDERED, permission::b_client_query_create, 1, nullptr, true, nullptr, server, true)) - return CommandResultPermissionError{permission::b_client_query_create}; + if(!this->permissionGranted(permission::PERMTEST_ORDERED, permission::b_client_query_create, 1, nullptr, true, nullptr, server, true)) + return CommandResultPermissionError{permission::b_client_query_create}; - auto username = cmd["client_login_name"].as(); - auto password = cmd[0].has("client_login_password") ? cmd["client_login_password"].as() : ""; + auto username = cmd["client_login_name"].as(); + auto password = cmd[0].has("client_login_password") ? cmd["client_login_password"].as() : ""; - if(password.empty()) - password = rnd_string(QUERY_PASSWORD_LENGTH); + if(password.empty()) + password = rnd_string(QUERY_PASSWORD_LENGTH); - auto account = serverInstance->getQueryServer()->find_query_account_by_name(username); - if(account) return {findError("query_already_exists")}; + auto account = serverInstance->getQueryServer()->find_query_account_by_name(username); + if(account) return {findError("query_already_exists")}; - account = serverInstance->getQueryServer()->create_query_account(username, server_id, this->getUid(), password); - if(!account) - return {ErrorType::VSError}; + account = serverInstance->getQueryServer()->create_query_account(username, server_id, this->getUid(), password); + if(!account) + return {ErrorType::VSError}; - Command result(this->getExternalType() == ClientType::CLIENT_TEAMSPEAK ? "notifyquerycreated" : ""); - result["client_unique_identifier"] = account->unique_id; - result["client_login_name"] = account->username; - result["client_login_password"] = password; - result["client_bound_server"] = account->bound_server; - this->sendCommand(result); + Command result(this->getExternalType() == ClientType::CLIENT_TEAMSPEAK ? "notifyquerycreated" : ""); + result["client_unique_identifier"] = account->unique_id; + result["client_login_name"] = account->username; + result["client_login_password"] = password; + result["client_bound_server"] = account->bound_server; + this->sendCommand(result); - return CommandResult::Success; + return CommandResult::Success; } CommandResult ConnectedClient::handleCommandQueryDelete(ts::Command &cmd) { - auto username = cmd["client_login_name"].as(); - auto account = serverInstance->getQueryServer()->find_query_account_by_name(username); - if(!account) - return {findError("query_not_exists")}; + auto username = cmd["client_login_name"].as(); + auto account = serverInstance->getQueryServer()->find_query_account_by_name(username); + if(!account) + return {findError("query_not_exists")}; - auto server = serverInstance->getVoiceServerManager()->findServerById(account->bound_server); - /* If the server is not existing anymore, we're asking for global permissions - if(!server && account->bounded_server != 0) - return {findError("server_invalid_id")}; - */ + auto server = serverInstance->getVoiceServerManager()->findServerById(account->bound_server); + /* If the server is not existing anymore, we're asking for global permissions + if(!server && account->bounded_server != 0) + return {findError("server_invalid_id")}; + */ - auto delete_all = this->permissionGranted(permission::PERMTEST_ORDERED, permission::b_client_query_delete, 1, nullptr, true, nullptr, server, true); - auto delete_own = delete_all || this->permissionGranted(permission::PERMTEST_ORDERED, permission::b_client_query_delete_own, 1, nullptr, true, nullptr, server, true); + auto delete_all = this->permissionGranted(permission::PERMTEST_ORDERED, permission::b_client_query_delete, 1, nullptr, true, nullptr, server, true); + auto delete_own = delete_all || this->permissionGranted(permission::PERMTEST_ORDERED, permission::b_client_query_delete_own, 1, nullptr, true, nullptr, server, true); - if(account->unique_id == this->getUid()) { - if(!delete_own) - return CommandResultPermissionError{permission::b_client_query_delete_own}; - } else { - if(!delete_all) - return CommandResultPermissionError{permission::b_client_query_delete}; - } - if(account->unique_id == "serveradmin" && account->username == "serveradmin") - return ErrorType::VSError; + if(account->unique_id == this->getUid()) { + if(!delete_own) + return CommandResultPermissionError{permission::b_client_query_delete_own}; + } else { + if(!delete_all) + return CommandResultPermissionError{permission::b_client_query_delete}; + } + if(account->unique_id == "serveradmin" && account->username == "serveradmin") + return ErrorType::VSError; - serverInstance->getQueryServer()->delete_query_account(account); + serverInstance->getQueryServer()->delete_query_account(account); - return CommandResult::Success; + return CommandResult::Success; } CommandResult ConnectedClient::handleCommandQueryRename(ts::Command &cmd) { - auto username = cmd["client_login_name"].as(); - auto new_username = cmd["client_new_login_name"].as(); + auto username = cmd["client_login_name"].as(); + auto new_username = cmd["client_new_login_name"].as(); - auto account = serverInstance->getQueryServer()->find_query_account_by_name(username); - if(!account) - return {findError("query_not_exists")}; + auto account = serverInstance->getQueryServer()->find_query_account_by_name(username); + if(!account) + return {findError("query_not_exists")}; - auto server = serverInstance->getVoiceServerManager()->findServerById(account->bound_server); - if(!server && account->bound_server != 0) - return {findError("server_invalid_id")}; + auto server = serverInstance->getVoiceServerManager()->findServerById(account->bound_server); + if(!server && account->bound_server != 0) + return {findError("server_invalid_id")}; - auto rename_all = this->permissionGranted(permission::PERMTEST_ORDERED, permission::b_client_query_rename, 1, nullptr, true, nullptr, server, true); - auto rename_own = rename_all || this->permissionGranted(permission::PERMTEST_ORDERED, permission::b_client_query_rename_own, 1, nullptr, true, nullptr, server, true); + auto rename_all = this->permissionGranted(permission::PERMTEST_ORDERED, permission::b_client_query_rename, 1, nullptr, true, nullptr, server, true); + auto rename_own = rename_all || this->permissionGranted(permission::PERMTEST_ORDERED, permission::b_client_query_rename_own, 1, nullptr, true, nullptr, server, true); - if(account->unique_id == this->getUid()) { - if(!rename_own) - return CommandResultPermissionError{permission::b_client_query_rename_own}; - } else { - if(!rename_all) - return CommandResultPermissionError{permission::b_client_query_rename}; - } + if(account->unique_id == this->getUid()) { + if(!rename_own) + return CommandResultPermissionError{permission::b_client_query_rename_own}; + } else { + if(!rename_all) + return CommandResultPermissionError{permission::b_client_query_rename}; + } - auto target_account = serverInstance->getQueryServer()->find_query_account_by_name(new_username); - if(target_account) return {findError("query_already_exists")}; + auto target_account = serverInstance->getQueryServer()->find_query_account_by_name(new_username); + if(target_account) return {findError("query_already_exists")}; - if(account->unique_id == "serveradmin" && account->username == "serveradmin") - return ErrorType::VSError; + if(account->unique_id == "serveradmin" && account->username == "serveradmin") + return ErrorType::VSError; - if(!serverInstance->getQueryServer()->rename_query_account(account, new_username)) - return {ErrorType::VSError}; + if(!serverInstance->getQueryServer()->rename_query_account(account, new_username)) + return {ErrorType::VSError}; - return CommandResult::Success; + return CommandResult::Success; } CommandResult ConnectedClient::handleCommandQueryChangePassword(ts::Command &cmd) { - auto username = cmd["client_login_name"].as(); - auto account = serverInstance->getQueryServer()->find_query_account_by_name(username); - if(!account) - return {findError("query_not_exists")}; + auto username = cmd["client_login_name"].as(); + auto account = serverInstance->getQueryServer()->find_query_account_by_name(username); + if(!account) + return {findError("query_not_exists")}; - auto server = serverInstance->getVoiceServerManager()->findServerById(account->bound_server); - if(!server && account->bound_server != 0) - return {findError("server_invalid_id")}; + auto server = serverInstance->getVoiceServerManager()->findServerById(account->bound_server); + if(!server && account->bound_server != 0) + return {findError("server_invalid_id")}; - auto change_all = this->permissionGranted(permission::PERMTEST_ORDERED, server ? permission::b_client_query_change_password : permission::b_client_query_change_password_global, 1, nullptr, true, nullptr, server, true); - auto change_own = change_all || this->permissionGranted(permission::PERMTEST_ORDERED, permission::b_client_query_change_own_password, 1, nullptr, true, nullptr, server, true); + auto change_all = this->permissionGranted(permission::PERMTEST_ORDERED, server ? permission::b_client_query_change_password : permission::b_client_query_change_password_global, 1, nullptr, true, nullptr, server, true); + auto change_own = change_all || this->permissionGranted(permission::PERMTEST_ORDERED, permission::b_client_query_change_own_password, 1, nullptr, true, nullptr, server, true); - auto password = cmd[0].has("client_login_password") ? cmd["client_login_password"].as() : ""; + auto password = cmd[0].has("client_login_password") ? cmd["client_login_password"].as() : ""; - if(password.empty()) - password = rnd_string(QUERY_PASSWORD_LENGTH); + if(password.empty()) + password = rnd_string(QUERY_PASSWORD_LENGTH); - if(account->unique_id == this->getUid()) { - if(!change_own) - return CommandResultPermissionError{permission::b_client_query_change_own_password}; - } else { - if(!change_all) - return CommandResultPermissionError{server ? permission::b_client_query_change_password : permission::b_client_query_change_password_global}; - } + if(account->unique_id == this->getUid()) { + if(!change_own) + return CommandResultPermissionError{permission::b_client_query_change_own_password}; + } else { + if(!change_all) + return CommandResultPermissionError{server ? permission::b_client_query_change_password : permission::b_client_query_change_password_global}; + } - if(!serverInstance->getQueryServer()->change_query_password(account, password)) - return {ErrorType::VSError}; + if(!serverInstance->getQueryServer()->change_query_password(account, password)) + return {ErrorType::VSError}; - Command result(this->getExternalType() == ClientType::CLIENT_TEAMSPEAK ? "notifyquerypasswordchanges" : ""); - result["client_login_name"] = account->username; - result["client_login_password"] = password; - this->sendCommand(result); + Command result(this->getExternalType() == ClientType::CLIENT_TEAMSPEAK ? "notifyquerypasswordchanges" : ""); + result["client_login_name"] = account->username; + result["client_login_password"] = password; + this->sendCommand(result); - return CommandResult::Success; + return CommandResult::Success; } CommandResult ConnectedClient::handleCommandDummy_IpChange(ts::Command &cmd) { - CMD_REF_SERVER(server); - logMessage(this->getServerId(), "[{}] Address changed from {} to {}", CLIENT_STR_LOG_PREFIX, cmd["old_ip"].string(), cmd["new_ip"].string()); + CMD_REF_SERVER(server); + logMessage(this->getServerId(), "[{}] Address changed from {} to {}", CLIENT_STR_LOG_PREFIX, cmd["old_ip"].string(), cmd["new_ip"].string()); - if(geoloc::provider_vpn && !this->permissionGranted(permission::PERMTEST_ORDERED, permission::b_client_ignore_vpn, 1)) { - auto provider = this->isAddressV4() ? geoloc::provider_vpn->resolveInfoV4(this->getPeerIp(), true) : geoloc::provider_vpn->resolveInfoV6(this->getPeerIp(), true); - if(provider) { - this->disconnect(strvar::transform(ts::config::messages::kick_vpn, strvar::StringValue{"provider.name", provider->name}, strvar::StringValue{"provider.website", provider->side})); - return CommandResult::Success; - } - } + if(geoloc::provider_vpn && !this->permissionGranted(permission::PERMTEST_ORDERED, permission::b_client_ignore_vpn, 1)) { + auto provider = this->isAddressV4() ? geoloc::provider_vpn->resolveInfoV4(this->getPeerIp(), true) : geoloc::provider_vpn->resolveInfoV6(this->getPeerIp(), true); + if(provider) { + this->disconnect(strvar::transform(ts::config::messages::kick_vpn, strvar::StringValue{"provider.name", provider->name}, strvar::StringValue{"provider.website", provider->side})); + return CommandResult::Success; + } + } - string new_country = config::geo::countryFlag; - if(geoloc::provider) { - auto loc = this->isAddressV4() ? geoloc::provider->resolveInfoV4(this->getPeerIp(), false) : geoloc::provider->resolveInfoV6(this->getPeerIp(), false); - if(loc) { - logError(this->getServerId(), "[{}] Received new ip location. IP {} traced to {} ({}).", CLIENT_STR_LOG_PREFIX, this->getLoggingPeerIp(), loc->name, loc->identifier); - this->properties()[property::CLIENT_COUNTRY] = loc->identifier; - server->notifyClientPropertyUpdates(_this.lock(), deque{property::CLIENT_COUNTRY}); - new_country = loc->identifier; - } else { - logError(this->getServerId(), "[{}] Failed to resolve ip location for IP {}.", CLIENT_STR_LOG_PREFIX, this->getLoggingPeerIp()); - } - } + string new_country = config::geo::countryFlag; + if(geoloc::provider) { + auto loc = this->isAddressV4() ? geoloc::provider->resolveInfoV4(this->getPeerIp(), false) : geoloc::provider->resolveInfoV6(this->getPeerIp(), false); + if(loc) { + logError(this->getServerId(), "[{}] Received new ip location. IP {} traced to {} ({}).", CLIENT_STR_LOG_PREFIX, this->getLoggingPeerIp(), loc->name, loc->identifier); + this->properties()[property::CLIENT_COUNTRY] = loc->identifier; + server->notifyClientPropertyUpdates(_this.lock(), deque{property::CLIENT_COUNTRY}); + new_country = loc->identifier; + } else { + logError(this->getServerId(), "[{}] Failed to resolve ip location for IP {}.", CLIENT_STR_LOG_PREFIX, this->getLoggingPeerIp()); + } + } - this->properties()[property::CONNECTION_CLIENT_IP] = this->getLoggingPeerIp(); - return CommandResult::Success; + this->properties()[property::CONNECTION_CLIENT_IP] = this->getLoggingPeerIp(); + return CommandResult::Success; } //conversationhistory cid=1 [cpw=xxx] [timestamp_begin] [timestamp_end (0 := no end)] [message_count (default 25| max 100)] [-merge] CommandResult ConnectedClient::handleCommandConversationHistory(ts::Command &command) { - CMD_REF_SERVER(ref_server); - CMD_CHK_AND_INC_FLOOD_POINTS(25); + CMD_REF_SERVER(ref_server); + CMD_CHK_AND_INC_FLOOD_POINTS(25); - if(!command[0].has("cid") || !command[0]["cid"].castable()) - return {findError("conversation_invalid_id")}; + if(!command[0].has("cid") || !command[0]["cid"].castable()) + return {findError("conversation_invalid_id")}; - auto conversation_id = command[0]["cid"].as(); - /* test if we have access to the conversation */ - { - /* test if we're able to see the channel */ - { - shared_lock channel_view_lock(this->channel_lock); - auto channel = this->channel_view()->find_channel(conversation_id); - if(!channel) - return {findError("conversation_invalid_id")}; - if(channel->channel()->properties()[property::CHANNEL_FLAG_CONVERSATION_PRIVATE].as()) - return {findError("conversation_is_private")}; - } + auto conversation_id = command[0]["cid"].as(); + /* test if we have access to the conversation */ + { + /* test if we're able to see the channel */ + { + shared_lock channel_view_lock(this->channel_lock); + auto channel = this->channel_view()->find_channel(conversation_id); + if(!channel) + return {findError("conversation_invalid_id")}; + if(channel->channel()->properties()[property::CHANNEL_FLAG_CONVERSATION_PRIVATE].as()) + return {findError("conversation_is_private")}; + } - /* test if there is a channel password or join power which denies that we see the conversation */ - { - shared_lock channel_view_lock(ref_server->channel_tree_lock); - auto channel = ref_server->getChannelTree()->findChannel(conversation_id); - if(!channel) /* should never happen! */ - return {findError("conversation_invalid_id")}; + /* test if there is a channel password or join power which denies that we see the conversation */ + { + shared_lock channel_view_lock(ref_server->channel_tree_lock); + auto channel = ref_server->getChannelTree()->findChannel(conversation_id); + if(!channel) /* should never happen! */ + return {findError("conversation_invalid_id")}; - if(!command[0].has("cpw")) - command[0]["cpw"] = ""; + if(!command[0].has("cpw")) + command[0]["cpw"] = ""; - if (!channel->passwordMatch(command["cpw"], true)) - if (!this->permissionGranted(permission::PERMTEST_ORDERED, permission::b_channel_join_ignore_password, 1, channel, true)) - return {findError("channel_invalid_password"), "invalid password"}; + if (!channel->passwordMatch(command["cpw"], true)) + if (!this->permissionGranted(permission::PERMTEST_ORDERED, permission::b_channel_join_ignore_password, 1, channel, true)) + return {findError("channel_invalid_password"), "invalid password"}; - if(!this->permissionGranted(permission::PERMTEST_ORDERED, permission::b_channel_ignore_join_power, 1, channel, true)) { - CHANNEL_PERMISSION_TEST(permission::i_channel_join_power, permission::i_channel_needed_join_power, channel, false); - } - } - } + if(!this->permissionGranted(permission::PERMTEST_ORDERED, permission::b_channel_ignore_join_power, 1, channel, true)) { + CHANNEL_PERMISSION_TEST(permission::i_channel_join_power, permission::i_channel_needed_join_power, channel, false); + } + } + } - auto conversation_manager = ref_server->conversation_manager(); - auto conversation = conversation_manager->get(conversation_id); - if(!conversation) - return {ErrorType::DBEmpty}; + auto conversation_manager = ref_server->conversation_manager(); + auto conversation = conversation_manager->get(conversation_id); + if(!conversation) + return {ErrorType::DBEmpty}; - system_clock::time_point timestamp_begin = system_clock::now(); - system_clock::time_point timestamp_end; - size_t message_count = 25; + system_clock::time_point timestamp_begin = system_clock::now(); + system_clock::time_point timestamp_end; + size_t message_count = 25; - if(command[0].has("timestamp_begin")) - timestamp_begin = system_clock::time_point{} + milliseconds(command[0]["timestamp_begin"].as()); + if(command[0].has("timestamp_begin")) + timestamp_begin = system_clock::time_point{} + milliseconds(command[0]["timestamp_begin"].as()); - if(command[0].has("timestamp_end")) - timestamp_end = system_clock::time_point{} + milliseconds(command[0]["timestamp_end"].as()); + if(command[0].has("timestamp_end")) + timestamp_end = system_clock::time_point{} + milliseconds(command[0]["timestamp_end"].as()); - if(command[0].has("message_count")) - message_count = command[0]["message_count"].as(); + if(command[0].has("message_count")) + message_count = command[0]["message_count"].as(); - if(timestamp_begin < timestamp_end) - return {findError("parameter_invalid")}; - if(message_count > 100) - message_count = 100; + if(timestamp_begin < timestamp_end) + return {findError("parameter_invalid")}; + if(message_count > 100) + message_count = 100; - auto messages = conversation->message_history(timestamp_begin, message_count + 1, timestamp_end); /* query one more to test for more data */ - if(messages.empty()) - return {ErrorType::DBEmpty}; - bool more_data = messages.size() > message_count; - if(more_data) - messages.pop_back(); + auto messages = conversation->message_history(timestamp_begin, message_count + 1, timestamp_end); /* query one more to test for more data */ + if(messages.empty()) + return {ErrorType::DBEmpty}; + bool more_data = messages.size() > message_count; + if(more_data) + messages.pop_back(); - Command notify(this->notify_response_command("notifyconversationhistory")); - size_t index = 0; - size_t length = 0; - bool merge = command.hasParm("merge"); + Command notify(this->notify_response_command("notifyconversationhistory")); + size_t index = 0; + size_t length = 0; + bool merge = command.hasParm("merge"); - for(auto it = messages.rbegin(); it != messages.rend(); it++) { - if(index == 0) { - notify[index]["cid"] = conversation_id; - notify[index]["flag_volatile"] = conversation->volatile_only(); - } + for(auto it = messages.rbegin(); it != messages.rend(); it++) { + if(index == 0) { + notify[index]["cid"] = conversation_id; + notify[index]["flag_volatile"] = conversation->volatile_only(); + } - auto& message = *it; - notify[index]["timestamp"] = duration_cast(message->message_timestamp.time_since_epoch()).count(); - notify[index]["sender_database_id"] = message->sender_database_id; - notify[index]["sender_unique_id"] = message->sender_unique_id; - notify[index]["sender_name"] = message->sender_name; + auto& message = *it; + notify[index]["timestamp"] = duration_cast(message->message_timestamp.time_since_epoch()).count(); + notify[index]["sender_database_id"] = message->sender_database_id; + notify[index]["sender_unique_id"] = message->sender_unique_id; + notify[index]["sender_name"] = message->sender_name; - notify[index]["msg"] = message->message; - length += message->message.size(); - length += message->sender_name.size(); - length += message->sender_unique_id.size(); - if(length > 1024 * 8 || !merge) { - index = 0; - this->sendCommand(notify); - notify = Command{this->notify_response_command("notifyconversationhistory")}; - } else - index++; - } - if(index > 0) - this->sendCommand(notify); + notify[index]["msg"] = message->message; + length += message->message.size(); + length += message->sender_name.size(); + length += message->sender_unique_id.size(); + if(length > 1024 * 8 || !merge) { + index = 0; + this->sendCommand(notify); + notify = Command{this->notify_response_command("notifyconversationhistory")}; + } else + index++; + } + if(index > 0) + this->sendCommand(notify); - if(more_data) - return {findError("conversation_more_data")}; + if(more_data) + return {findError("conversation_more_data")}; - return CommandResult::Success; + return CommandResult::Success; } CommandResult ConnectedClient::handleCommandConversationFetch(ts::Command &cmd) { - CMD_REF_SERVER(ref_server); - CMD_CHK_AND_INC_FLOOD_POINTS(25); + CMD_REF_SERVER(ref_server); + CMD_CHK_AND_INC_FLOOD_POINTS(25); - Command result(this->notify_response_command("notifyconversationindex")); - size_t result_index = 0; + Command result(this->notify_response_command("notifyconversationindex")); + size_t result_index = 0; - auto conversation_manager = ref_server->conversation_manager(); - for(size_t index = 0; index < cmd.bulkCount(); index++) { - auto& bulk = cmd[index]; + auto conversation_manager = ref_server->conversation_manager(); + for(size_t index = 0; index < cmd.bulkCount(); index++) { + auto& bulk = cmd[index]; - if(!bulk.has("cid") || !bulk["cid"].castable()) - continue; - auto conversation_id = bulk["cid"].as(); + if(!bulk.has("cid") || !bulk["cid"].castable()) + continue; + auto conversation_id = bulk["cid"].as(); - auto& result_bulk = result[result_index++]; - result_bulk["cid"] = conversation_id; + auto& result_bulk = result[result_index++]; + result_bulk["cid"] = conversation_id; - /* test if we have access to the conversation */ - { - /* test if we're able to see the channel */ - { - shared_lock channel_view_lock(this->channel_lock); - auto channel = this->channel_view()->find_channel(conversation_id); - if(!channel) { - auto error = findError("conversation_invalid_id"); - result_bulk["error_id"] = error.errorId; - result_bulk["error_msg"] = error.message; - continue; - } - if(channel->channel()->properties()[property::CHANNEL_FLAG_CONVERSATION_PRIVATE].as()) { - auto error = findError("conversation_is_private"); - result_bulk["error_id"] = error.errorId; - result_bulk["error_msg"] = error.message; - continue; - } - } + /* test if we have access to the conversation */ + { + /* test if we're able to see the channel */ + { + shared_lock channel_view_lock(this->channel_lock); + auto channel = this->channel_view()->find_channel(conversation_id); + if(!channel) { + auto error = findError("conversation_invalid_id"); + result_bulk["error_id"] = error.errorId; + result_bulk["error_msg"] = error.message; + continue; + } + if(channel->channel()->properties()[property::CHANNEL_FLAG_CONVERSATION_PRIVATE].as()) { + auto error = findError("conversation_is_private"); + result_bulk["error_id"] = error.errorId; + result_bulk["error_msg"] = error.message; + continue; + } + } - /* test if there is a channel password or join power which denies that we see the conversation */ - { - shared_lock channel_view_lock(ref_server->channel_tree_lock); - auto channel = ref_server->getChannelTree()->findChannel(conversation_id); - if(!channel) { /* should never happen! */ - auto error = findError("conversation_invalid_id"); - result_bulk["error_id"] = error.errorId; - result_bulk["error_msg"] = error.message; - continue; - } + /* test if there is a channel password or join power which denies that we see the conversation */ + { + shared_lock channel_view_lock(ref_server->channel_tree_lock); + auto channel = ref_server->getChannelTree()->findChannel(conversation_id); + if(!channel) { /* should never happen! */ + auto error = findError("conversation_invalid_id"); + result_bulk["error_id"] = error.errorId; + result_bulk["error_msg"] = error.message; + continue; + } - if(!bulk.has("cpw")) - bulk["cpw"] = ""; + if(!bulk.has("cpw")) + bulk["cpw"] = ""; - if (!channel->passwordMatch(bulk["cpw"], true)) - if (!this->permissionGranted(permission::PERMTEST_ORDERED, permission::b_channel_join_ignore_password, 1, channel, true)) { - auto error = findError("channel_invalid_password"); - result_bulk["error_id"] = error.errorId; - result_bulk["error_msg"] = error.message; - continue; - } + if (!channel->passwordMatch(bulk["cpw"], true)) + if (!this->permissionGranted(permission::PERMTEST_ORDERED, permission::b_channel_join_ignore_password, 1, channel, true)) { + auto error = findError("channel_invalid_password"); + result_bulk["error_id"] = error.errorId; + result_bulk["error_msg"] = error.message; + continue; + } - if(!this->permissionGranted(permission::PERMTEST_ORDERED, permission::b_channel_ignore_join_power, 1, channel, true)) { - auto permission_granted = this->calculate_permission_value(permission::i_channel_join_power, channel->channelId()); - if(!channel->permission_granted(permission::i_channel_needed_join_power, permission_granted, false)) { - auto error = findError("server_insufficeient_permissions"); - result_bulk["error_id"] = error.errorId; - result_bulk["error_msg"] = error.message; - result_bulk["failed_permid"] = permission::i_channel_join_power; - continue; - } - } - } - } + if(!this->permissionGranted(permission::PERMTEST_ORDERED, permission::b_channel_ignore_join_power, 1, channel, true)) { + auto permission_granted = this->calculate_permission_value(permission::i_channel_join_power, channel->channelId()); + if(!channel->permission_granted(permission::i_channel_needed_join_power, permission_granted, false)) { + auto error = findError("server_insufficeient_permissions"); + result_bulk["error_id"] = error.errorId; + result_bulk["error_msg"] = error.message; + result_bulk["failed_permid"] = permission::i_channel_join_power; + continue; + } + } + } + } - auto conversation = conversation_manager->get(conversation_id); - if(conversation) - result_bulk["timestamp"] = duration_cast(conversation->last_message().time_since_epoch()).count(); - else - result_bulk["timestamp"] = 0; - result_bulk["flag_volatile"] = conversation->volatile_only(); - } - if(result_index == 0) - return {ErrorType::DBEmpty}; - this->sendCommand(result); + auto conversation = conversation_manager->get(conversation_id); + if(conversation) + result_bulk["timestamp"] = duration_cast(conversation->last_message().time_since_epoch()).count(); + else + result_bulk["timestamp"] = 0; + result_bulk["flag_volatile"] = conversation->volatile_only(); + } + if(result_index == 0) + return {ErrorType::DBEmpty}; + this->sendCommand(result); - return CommandResult::Success; + return CommandResult::Success; } CommandResult ConnectedClient::handleCommandConversationMessageDelete(ts::Command &cmd) { - CMD_REF_SERVER(ref_server); - CMD_CHK_AND_INC_FLOOD_POINTS(25); + CMD_REF_SERVER(ref_server); + CMD_CHK_AND_INC_FLOOD_POINTS(25); - auto conversation_manager = ref_server->conversation_manager(); - std::shared_ptr current_conversation; - ChannelId current_conversation_id = 0; + auto conversation_manager = ref_server->conversation_manager(); + std::shared_ptr current_conversation; + ChannelId current_conversation_id = 0; - for(size_t index = 0; index < cmd.bulkCount(); index++) { - auto &bulk = cmd[index]; + for(size_t index = 0; index < cmd.bulkCount(); index++) { + auto &bulk = cmd[index]; - if(!bulk.has("cid") || !bulk["cid"].castable()) - continue; + if(!bulk.has("cid") || !bulk["cid"].castable()) + continue; - /* test if we have access to the conversation */ - if(current_conversation_id != bulk["cid"].as()) { - current_conversation_id = bulk["cid"].as(); + /* test if we have access to the conversation */ + if(current_conversation_id != bulk["cid"].as()) { + current_conversation_id = bulk["cid"].as(); - /* test if we're able to see the channel */ - { - shared_lock channel_view_lock(this->channel_lock); - auto channel = this->channel_view()->find_channel(current_conversation_id); - if(!channel) - return findError("conversation_invalid_id"); - } + /* test if we're able to see the channel */ + { + shared_lock channel_view_lock(this->channel_lock); + auto channel = this->channel_view()->find_channel(current_conversation_id); + if(!channel) + return findError("conversation_invalid_id"); + } - /* test if there is a channel password or join power which denies that we see the conversation */ - { - shared_lock channel_view_lock(ref_server->channel_tree_lock); - auto channel = ref_server->getChannelTree()->findChannel(current_conversation_id); - if(!channel) - return findError("conversation_invalid_id"); + /* test if there is a channel password or join power which denies that we see the conversation */ + { + shared_lock channel_view_lock(ref_server->channel_tree_lock); + auto channel = ref_server->getChannelTree()->findChannel(current_conversation_id); + if(!channel) + return findError("conversation_invalid_id"); - if(!bulk.has("cpw")) - bulk["cpw"] = ""; + if(!bulk.has("cpw")) + bulk["cpw"] = ""; - if (!channel->passwordMatch(bulk["cpw"], true)) - if (!this->permissionGranted(permission::PERMTEST_ORDERED, permission::b_channel_join_ignore_password, 1, channel, true)) - return findError("channel_invalid_password"); + if (!channel->passwordMatch(bulk["cpw"], true)) + if (!this->permissionGranted(permission::PERMTEST_ORDERED, permission::b_channel_join_ignore_password, 1, channel, true)) + return findError("channel_invalid_password"); - if (!this->permissionGranted(permission::PERMTEST_ORDERED, permission::b_channel_conversation_message_delete, 1, channel)) - return CommandResultPermissionError{permission::b_channel_conversation_message_delete}; + if (!this->permissionGranted(permission::PERMTEST_ORDERED, permission::b_channel_conversation_message_delete, 1, channel)) + return CommandResultPermissionError{permission::b_channel_conversation_message_delete}; - if(!this->permissionGranted(permission::PERMTEST_ORDERED, permission::b_channel_ignore_join_power, 1, channel, true)) { - auto permission_granted = this->calculate_permission_value(permission::i_channel_join_power, channel->channelId()); - if(!channel->permission_granted(permission::i_channel_needed_join_power, permission_granted, false)) - return CommandResultPermissionError{permission::i_channel_needed_join_power}; - } - } - } + if(!this->permissionGranted(permission::PERMTEST_ORDERED, permission::b_channel_ignore_join_power, 1, channel, true)) { + auto permission_granted = this->calculate_permission_value(permission::i_channel_join_power, channel->channelId()); + if(!channel->permission_granted(permission::i_channel_needed_join_power, permission_granted, false)) + return CommandResultPermissionError{permission::i_channel_needed_join_power}; + } + } + } - current_conversation = conversation_manager->get(current_conversation_id); - if(!current_conversation) continue; + current_conversation = conversation_manager->get(current_conversation_id); + if(!current_conversation) continue; - auto timestamp_begin = system_clock::time_point{} + milliseconds{bulk["timestamp_begin"]}; - auto timestamp_end = system_clock::time_point{} + milliseconds{bulk.has("timestamp_end") ? bulk["timestamp_end"].as() : 0}; - auto limit = bulk.has("limit") ? bulk["limit"].as() : 1; - if(limit > 100) - limit = 100; - auto delete_count = current_conversation->delete_messages(timestamp_end, limit, timestamp_begin, bulk["cldbid"]); - if(delete_count > 0) { - for(const auto& client : ref_server->getClients()) { - if(client->connectionState() != ConnectionState::CONNECTED) - continue; + auto timestamp_begin = system_clock::time_point{} + milliseconds{bulk["timestamp_begin"]}; + auto timestamp_end = system_clock::time_point{} + milliseconds{bulk.has("timestamp_end") ? bulk["timestamp_end"].as() : 0}; + auto limit = bulk.has("limit") ? bulk["limit"].as() : 1; + if(limit > 100) + limit = 100; + auto delete_count = current_conversation->delete_messages(timestamp_end, limit, timestamp_begin, bulk["cldbid"]); + if(delete_count > 0) { + for(const auto& client : ref_server->getClients()) { + if(client->connectionState() != ConnectionState::CONNECTED) + continue; - auto type = client->getType(); - if(type == ClientType::CLIENT_INTERNAL || type == ClientType::CLIENT_MUSIC) - continue; + auto type = client->getType(); + if(type == ClientType::CLIENT_INTERNAL || type == ClientType::CLIENT_MUSIC) + continue; - client->notifyConversationMessageDelete(current_conversation_id, timestamp_begin, timestamp_end, bulk["cldbid"], delete_count); - } - } - } + client->notifyConversationMessageDelete(current_conversation_id, timestamp_begin, timestamp_end, bulk["cldbid"], delete_count); + } + } + } - return CommandResult::Success; + return CommandResult::Success; } diff --git a/server/src/client/ConnectedClientNotifyHandler.cpp b/server/src/client/ConnectedClientNotifyHandler.cpp index 0c92570..82519ea 100644 --- a/server/src/client/ConnectedClientNotifyHandler.cpp +++ b/server/src/client/ConnectedClientNotifyHandler.cpp @@ -25,319 +25,319 @@ extern ts::server::InstanceHandler* serverInstance; #define INVOKER(command, invoker) \ do { \ if(invoker) { \ - command["invokerid"] = invoker->getClientId(); \ - command["invokername"] = invoker->getDisplayName(); \ - command["invokeruid"] = invoker->getUid(); \ + command["invokerid"] = invoker->getClientId(); \ + command["invokername"] = invoker->getDisplayName(); \ + command["invokeruid"] = invoker->getUid(); \ } else { \ - command["invokerid"] = 0; \ - command["invokername"] = "undefined"; \ - command["invokeruid"] = "undefined"; \ - } \ + command["invokerid"] = 0; \ + command["invokername"] = "undefined"; \ + command["invokeruid"] = "undefined"; \ + } \ } while(0) bool ConnectedClient::notifyServerGroupList() { - Command cmd(this->getExternalType() == CLIENT_TEAMSPEAK ? "notifyservergrouplist" : ""); - int index = 0; + Command cmd(this->getExternalType() == CLIENT_TEAMSPEAK ? "notifyservergrouplist" : ""); + int index = 0; - for (const auto& group : (this->server ? this->server->groups : serverInstance->getGroupManager().get())->availableServerGroups(true)) { - if(group->target() == GroupTarget::GROUPTARGET_CHANNEL) { - cmd[index]["cgid"] = group->groupId(); - } else { - cmd[index]["sgid"] = group->groupId(); - } - for (const auto &prop : group->properties().list_properties(property::FLAG_GROUP_VIEW, this->getType() == CLIENT_TEAMSPEAK ? property::FLAG_NEW : (uint16_t) 0)) - cmd[index][prop.type().name] = prop.value(); + for (const auto& group : (this->server ? this->server->groups : serverInstance->getGroupManager().get())->availableServerGroups(true)) { + if(group->target() == GroupTarget::GROUPTARGET_CHANNEL) { + cmd[index]["cgid"] = group->groupId(); + } else { + cmd[index]["sgid"] = group->groupId(); + } + for (const auto &prop : group->properties().list_properties(property::FLAG_GROUP_VIEW, this->getType() == CLIENT_TEAMSPEAK ? property::FLAG_NEW : (uint16_t) 0)) + cmd[index][prop.type().name] = prop.value(); - auto modify_power = group->permissions()->permission_value_flagged(permission::i_displayed_group_needed_modify_power); - auto add_power = group->permissions()->permission_value_flagged(permission::i_displayed_group_needed_member_add_power); - auto remove_power = group->permissions()->permission_value_flagged(permission::i_displayed_group_needed_member_remove_power); - cmd[index]["n_modifyp"] = modify_power.has_value ? modify_power.value : 0; - cmd[index]["n_member_addp"] = add_power.has_value ? add_power.value : 0; - cmd[index]["n_member_removep"] = remove_power.has_value ? remove_power.value : 0; - index++; - } + auto modify_power = group->permissions()->permission_value_flagged(permission::i_displayed_group_needed_modify_power); + auto add_power = group->permissions()->permission_value_flagged(permission::i_displayed_group_needed_member_add_power); + auto remove_power = group->permissions()->permission_value_flagged(permission::i_displayed_group_needed_member_remove_power); + cmd[index]["n_modifyp"] = modify_power.has_value ? modify_power.value : 0; + cmd[index]["n_member_addp"] = add_power.has_value ? add_power.value : 0; + cmd[index]["n_member_removep"] = remove_power.has_value ? remove_power.value : 0; + index++; + } - this->sendCommand(cmd); - return true; + this->sendCommand(cmd); + return true; } bool ConnectedClient::notifyGroupPermList(const std::shared_ptr& group, bool as_sid) { - Command cmd(this->getExternalType() == CLIENT_TEAMSPEAK ? group->target() == GROUPTARGET_SERVER ? "notifyservergrouppermlist" : "notifychannelgrouppermlist" : ""); - if (group->target() == GROUPTARGET_SERVER) - cmd["sgid"] = group->groupId(); - else - cmd["cgid"] = group->groupId(); + Command cmd(this->getExternalType() == CLIENT_TEAMSPEAK ? group->target() == GROUPTARGET_SERVER ? "notifyservergrouppermlist" : "notifychannelgrouppermlist" : ""); + if (group->target() == GROUPTARGET_SERVER) + cmd["sgid"] = group->groupId(); + else + cmd["cgid"] = group->groupId(); - int index = 0; + int index = 0; - auto permissions = group->permissions()->permissions(); - auto permission_mapper = serverInstance->getPermissionMapper(); - auto client_type = this->getType(); - for (const auto &permission_data : permissions) { - auto& permission = get<1>(permission_data); - if(!permission.flags.value_set) - continue; + auto permissions = group->permissions()->permissions(); + auto permission_mapper = serverInstance->getPermissionMapper(); + auto client_type = this->getType(); + for (const auto &permission_data : permissions) { + auto& permission = get<1>(permission_data); + if(!permission.flags.value_set) + continue; - if(as_sid) { - cmd[index]["permsid"] = permission_mapper->permission_name(client_type, get<0>(permission_data)); - } else { - cmd[index]["permid"] = (uint16_t) get<0>(permission_data); - } - cmd[index]["permvalue"] = permission.values.value; - cmd[index]["permnegated"] = permission.flags.negate; - cmd[index]["permskip"] = permission.flags.skip; - index++; - } - for (const auto &permission_data : permissions) { - auto& permission = get<1>(permission_data); - if(!permission.flags.grant_set) - continue; + if(as_sid) { + cmd[index]["permsid"] = permission_mapper->permission_name(client_type, get<0>(permission_data)); + } else { + cmd[index]["permid"] = (uint16_t) get<0>(permission_data); + } + cmd[index]["permvalue"] = permission.values.value; + cmd[index]["permnegated"] = permission.flags.negate; + cmd[index]["permskip"] = permission.flags.skip; + index++; + } + for (const auto &permission_data : permissions) { + auto& permission = get<1>(permission_data); + if(!permission.flags.grant_set) + continue; - if(as_sid) { - cmd[index]["permsid"] = permission_mapper->permission_name_grant(client_type, get<0>(permission_data)); - } else { - cmd[index]["permid"] = (uint16_t) (get<0>(permission_data) | PERM_ID_GRANT); - } - cmd[index]["permvalue"] = permission.values.grant; - cmd[index]["permnegated"] = permission.flags.negate; - cmd[index]["permskip"] = permission.flags.skip; - index++; - } + if(as_sid) { + cmd[index]["permsid"] = permission_mapper->permission_name_grant(client_type, get<0>(permission_data)); + } else { + cmd[index]["permid"] = (uint16_t) (get<0>(permission_data) | PERM_ID_GRANT); + } + cmd[index]["permvalue"] = permission.values.grant; + cmd[index]["permnegated"] = permission.flags.negate; + cmd[index]["permskip"] = permission.flags.skip; + index++; + } - if (index == 0) - return false; + if (index == 0) + return false; - this->sendCommand(cmd); - return true; + this->sendCommand(cmd); + return true; } bool ConnectedClient::notifyClientPermList(ClientDbId cldbid, const std::shared_ptr& mgr, bool perm_sids) { - Command res(this->getExternalType() == CLIENT_TEAMSPEAK ? "notifyclientpermlist" : ""); + Command res(this->getExternalType() == CLIENT_TEAMSPEAK ? "notifyclientpermlist" : ""); - auto permissions = mgr->permissions(); - if(permissions.empty()) - return false; + auto permissions = mgr->permissions(); + if(permissions.empty()) + return false; - int index = 0; - res[index]["cldbid"] = cldbid; + int index = 0; + res[index]["cldbid"] = cldbid; - auto permission_mapper = serverInstance->getPermissionMapper(); - auto client_type = this->getType(); - for (const auto &permission_data : permissions) { - auto& permission = std::get<1>(permission_data); - if(permission.flags.value_set) { - if (perm_sids) - res[index]["permsid"] = permission_mapper->permission_name(client_type, get<0>(permission_data)); - else - res[index]["permid"] = get<0>(permission_data); - res[index]["permvalue"] = permission.values.value; + auto permission_mapper = serverInstance->getPermissionMapper(); + auto client_type = this->getType(); + for (const auto &permission_data : permissions) { + auto& permission = std::get<1>(permission_data); + if(permission.flags.value_set) { + if (perm_sids) + res[index]["permsid"] = permission_mapper->permission_name(client_type, get<0>(permission_data)); + else + res[index]["permid"] = get<0>(permission_data); + res[index]["permvalue"] = permission.values.value; - res[index]["permnegated"] = permission.flags.negate; - res[index]["permskip"] = permission.flags.skip; - index++; - } + res[index]["permnegated"] = permission.flags.negate; + res[index]["permskip"] = permission.flags.skip; + index++; + } - if(permission.flags.grant_set) { - if (perm_sids) - res[index]["permsid"] = permission_mapper->permission_name_grant(client_type, get<0>(permission_data)); - else - res[index]["permid"] = (get<0>(permission_data) | PERM_ID_GRANT); - res[index]["permvalue"] = permission.values.grant; - res[index]["permnegated"] = 0; - res[index]["permskip"] = 0; - index++; - } - } + if(permission.flags.grant_set) { + if (perm_sids) + res[index]["permsid"] = permission_mapper->permission_name_grant(client_type, get<0>(permission_data)); + else + res[index]["permid"] = (get<0>(permission_data) | PERM_ID_GRANT); + res[index]["permvalue"] = permission.values.grant; + res[index]["permnegated"] = 0; + res[index]["permskip"] = 0; + index++; + } + } - this->sendCommand(res); - return true; + this->sendCommand(res); + return true; } bool ConnectedClient::notifyChannelGroupList() { - Command cmd(this->getExternalType() == CLIENT_TEAMSPEAK ? "notifychannelgrouplist" : ""); - int index = 0; - for (const auto &group : (this->server ? this->server->groups : serverInstance->getGroupManager().get())->availableChannelGroups(true)) { - if(group->target() == GroupTarget::GROUPTARGET_CHANNEL) { - cmd[index]["cgid"] = group->groupId(); - } else { - cmd[index]["sgid"] = group->groupId(); - } - for (auto &prop : group->properties().list_properties(property::FLAG_GROUP_VIEW, this->getType() == CLIENT_TEAMSPEAK ? property::FLAG_NEW : (uint16_t) 0)) - cmd[index][prop.type().name] = prop.value(); + Command cmd(this->getExternalType() == CLIENT_TEAMSPEAK ? "notifychannelgrouplist" : ""); + int index = 0; + for (const auto &group : (this->server ? this->server->groups : serverInstance->getGroupManager().get())->availableChannelGroups(true)) { + if(group->target() == GroupTarget::GROUPTARGET_CHANNEL) { + cmd[index]["cgid"] = group->groupId(); + } else { + cmd[index]["sgid"] = group->groupId(); + } + for (auto &prop : group->properties().list_properties(property::FLAG_GROUP_VIEW, this->getType() == CLIENT_TEAMSPEAK ? property::FLAG_NEW : (uint16_t) 0)) + cmd[index][prop.type().name] = prop.value(); - auto modify_power = group->permissions()->permission_value_flagged(permission::i_displayed_group_needed_modify_power); - auto add_power = group->permissions()->permission_value_flagged(permission::i_displayed_group_needed_member_add_power); - auto remove_power = group->permissions()->permission_value_flagged(permission::i_displayed_group_needed_member_remove_power); - cmd[index]["n_modifyp"] = modify_power.has_value ? modify_power.value : 0; - cmd[index]["n_member_addp"] = add_power.has_value ? add_power.value : 0; - cmd[index]["n_member_removep"] = remove_power.has_value ? remove_power.value : 0; - index++; - } - if(index == 0) return false; - this->sendCommand(cmd); - return true; + auto modify_power = group->permissions()->permission_value_flagged(permission::i_displayed_group_needed_modify_power); + auto add_power = group->permissions()->permission_value_flagged(permission::i_displayed_group_needed_member_add_power); + auto remove_power = group->permissions()->permission_value_flagged(permission::i_displayed_group_needed_member_remove_power); + cmd[index]["n_modifyp"] = modify_power.has_value ? modify_power.value : 0; + cmd[index]["n_member_addp"] = add_power.has_value ? add_power.value : 0; + cmd[index]["n_member_removep"] = remove_power.has_value ? remove_power.value : 0; + index++; + } + if(index == 0) return false; + this->sendCommand(cmd); + return true; } bool ConnectedClient::notifyTextMessage(ChatMessageMode mode, const shared_ptr &invoker, uint64_t targetId, ChannelId channel_id, const std::chrono::system_clock::time_point& timestamp, const string &textMessage) { - //notifytextmessage targetmode=1 msg=asdasd target=2 invokerid=1 invokername=WolverinDEV invokeruid=xxjnc14LmvTk+Lyrm8OOeo4tOqw= - Command cmd("notifytextmessage"); - INVOKER(cmd, invoker); - cmd["targetmode"] = mode; - cmd["target"] = targetId; - cmd["msg"] = textMessage; - cmd["timestamp"] = floor(timestamp.time_since_epoch()).count(); - if(this->getType() != ClientType::CLIENT_TEAMSPEAK) - cmd["cid"] = channel_id; - this->sendCommand(cmd); - return true; + //notifytextmessage targetmode=1 msg=asdasd target=2 invokerid=1 invokername=WolverinDEV invokeruid=xxjnc14LmvTk+Lyrm8OOeo4tOqw= + Command cmd("notifytextmessage"); + INVOKER(cmd, invoker); + cmd["targetmode"] = mode; + cmd["target"] = targetId; + cmd["msg"] = textMessage; + cmd["timestamp"] = floor(timestamp.time_since_epoch()).count(); + if(this->getType() != ClientType::CLIENT_TEAMSPEAK) + cmd["cid"] = channel_id; + this->sendCommand(cmd); + return true; } bool ConnectedClient::notifyServerGroupClientAdd(const shared_ptr &invoker, const shared_ptr &client, const shared_ptr &group) { - Command cmd("notifyservergroupclientadded"); - INVOKER(cmd, invoker); + Command cmd("notifyservergroupclientadded"); + INVOKER(cmd, invoker); - cmd["sgid"] = group->groupId(); - cmd["clid"] = client->getClientId(); - cmd["name"] = client->getDisplayName(); - cmd["cluid"] = client->getUid(); + cmd["sgid"] = group->groupId(); + cmd["clid"] = client->getClientId(); + cmd["name"] = client->getDisplayName(); + cmd["cluid"] = client->getUid(); - this->sendCommand(cmd); - return true; + this->sendCommand(cmd); + return true; } bool ConnectedClient::notifyServerGroupClientRemove(std::shared_ptr invoker, std::shared_ptr client, std::shared_ptr group) { - Command cmd("notifyservergroupclientdeleted"); - INVOKER(cmd, invoker); + Command cmd("notifyservergroupclientdeleted"); + INVOKER(cmd, invoker); - cmd["sgid"] = group->groupId(); - cmd["clid"] = client->getClientId(); - cmd["name"] = client->getDisplayName(); - cmd["cluid"] = client->getUid(); + cmd["sgid"] = group->groupId(); + cmd["clid"] = client->getClientId(); + cmd["name"] = client->getDisplayName(); + cmd["cluid"] = client->getUid(); - this->sendCommand(cmd); - return true; + this->sendCommand(cmd); + return true; } bool ConnectedClient::notifyClientChannelGroupChanged( - const std::shared_ptr &invoker, - const std::shared_ptr &client, - const std::shared_ptr& channel, - const std::shared_ptr& inherited, - const std::shared_ptr& group, - bool lock_channel_tree) { - assert(!lock_channel_tree); /* not supported */ + const std::shared_ptr &invoker, + const std::shared_ptr &client, + const std::shared_ptr& channel, + const std::shared_ptr& inherited, + const std::shared_ptr& group, + bool lock_channel_tree) { + assert(!lock_channel_tree); /* not supported */ - if(!this->isClientVisible(client, false) && client != this) return false; - if(client->getChannel() != channel) return false; + if(!this->isClientVisible(client, false) && client != this) return false; + if(client->getChannel() != channel) return false; - assert(client); - assert(channel); - assert(inherited); - assert(group); - Command cmd("notifyclientchannelgroupchanged"); - INVOKER(cmd, invoker); + assert(client); + assert(channel); + assert(inherited); + assert(group); + Command cmd("notifyclientchannelgroupchanged"); + INVOKER(cmd, invoker); - cmd["cgid"] = group->groupId(); - cmd["clid"] = client->getClientId(); - cmd["cid"] = channel->channelId(); - cmd["cgi"] = inherited ? inherited->channelId() : channel->channelId(); //The inherited channel + cmd["cgid"] = group->groupId(); + cmd["clid"] = client->getClientId(); + cmd["cid"] = channel->channelId(); + cmd["cgi"] = inherited ? inherited->channelId() : channel->channelId(); //The inherited channel - this->sendCommand(cmd); - return true; + this->sendCommand(cmd); + return true; } bool ConnectedClient::notifyConnectionInfo(const shared_ptr &target, const shared_ptr &info) { Command notify("notifyconnectioninfo"); notify["clid"] = target->getClientId(); - auto not_set = this->getType() == CLIENT_TEAMSPEAK ? 0 : -1; + auto not_set = this->getType() == CLIENT_TEAMSPEAK ? 0 : -1; /* we deliver data to the web client as well, because its a bit dump :D */ if(target->getClientId() != this->getClientId()) { - auto report = target->connectionStatistics->full_report(); + auto report = target->connectionStatistics->full_report(); - /* default values which normally sets the client */ - notify["connection_bandwidth_received_last_minute_control"] = not_set; - notify["connection_bandwidth_received_last_minute_keepalive"] = not_set; - notify["connection_bandwidth_received_last_minute_speech"] = not_set; - notify["connection_bandwidth_received_last_second_control"] = not_set; - notify["connection_bandwidth_received_last_second_keepalive"] = not_set; - notify["connection_bandwidth_received_last_second_speech"] = not_set; + /* default values which normally sets the client */ + notify["connection_bandwidth_received_last_minute_control"] = not_set; + notify["connection_bandwidth_received_last_minute_keepalive"] = not_set; + notify["connection_bandwidth_received_last_minute_speech"] = not_set; + notify["connection_bandwidth_received_last_second_control"] = not_set; + notify["connection_bandwidth_received_last_second_keepalive"] = not_set; + notify["connection_bandwidth_received_last_second_speech"] = not_set; - notify["connection_bandwidth_sent_last_minute_control"] = not_set; - notify["connection_bandwidth_sent_last_minute_keepalive"] = not_set; - notify["connection_bandwidth_sent_last_minute_speech"] = not_set; - notify["connection_bandwidth_sent_last_second_control"] = not_set; - notify["connection_bandwidth_sent_last_second_keepalive"] = not_set; - notify["connection_bandwidth_sent_last_second_speech"] = not_set; + notify["connection_bandwidth_sent_last_minute_control"] = not_set; + notify["connection_bandwidth_sent_last_minute_keepalive"] = not_set; + notify["connection_bandwidth_sent_last_minute_speech"] = not_set; + notify["connection_bandwidth_sent_last_second_control"] = not_set; + notify["connection_bandwidth_sent_last_second_keepalive"] = not_set; + notify["connection_bandwidth_sent_last_second_speech"] = not_set; - /* its flipped here because the report is out of the clients view */ - notify["connection_bytes_received_control"] = not_set; - notify["connection_bytes_received_keepalive"] = not_set; - notify["connection_bytes_received_speech"] = not_set; - notify["connection_bytes_sent_control"] = not_set; - notify["connection_bytes_sent_keepalive"] = not_set; - notify["connection_bytes_sent_speech"] = not_set; + /* its flipped here because the report is out of the clients view */ + notify["connection_bytes_received_control"] = not_set; + notify["connection_bytes_received_keepalive"] = not_set; + notify["connection_bytes_received_speech"] = not_set; + notify["connection_bytes_sent_control"] = not_set; + notify["connection_bytes_sent_keepalive"] = not_set; + notify["connection_bytes_sent_speech"] = not_set; - /* its flipped here because the report is out of the clients view */ - notify["connection_packets_received_control"] = not_set; - notify["connection_packets_received_keepalive"] = not_set; - notify["connection_packets_received_speech"] = not_set; - notify["connection_packets_sent_control"] = not_set; - notify["connection_packets_sent_keepalive"] = not_set; - notify["connection_packets_sent_speech"] = not_set; + /* its flipped here because the report is out of the clients view */ + notify["connection_packets_received_control"] = not_set; + notify["connection_packets_received_keepalive"] = not_set; + notify["connection_packets_received_speech"] = not_set; + notify["connection_packets_sent_control"] = not_set; + notify["connection_packets_sent_keepalive"] = not_set; + notify["connection_packets_sent_speech"] = not_set; - notify["connection_server2client_packetloss_control"] = not_set; - notify["connection_server2client_packetloss_keepalive"] = not_set; - notify["connection_server2client_packetloss_speech"] = not_set; - notify["connection_server2client_packetloss_total"] = not_set; + notify["connection_server2client_packetloss_control"] = not_set; + notify["connection_server2client_packetloss_keepalive"] = not_set; + notify["connection_server2client_packetloss_speech"] = not_set; + notify["connection_server2client_packetloss_total"] = not_set; - notify["connection_ping"] = 0; - notify["connection_ping_deviation"] = 0; + notify["connection_ping"] = 0; + notify["connection_ping_deviation"] = 0; - notify["connection_connected_time"] = 0; - notify["connection_idle_time"] = 0; + notify["connection_connected_time"] = 0; + notify["connection_idle_time"] = 0; - /* its flipped here because the report is out of the clients view */ - notify["connection_filetransfer_bandwidth_sent"] = report.file_bytes_received; - notify["connection_filetransfer_bandwidth_received"] = report.file_bytes_sent; + /* its flipped here because the report is out of the clients view */ + notify["connection_filetransfer_bandwidth_sent"] = report.file_bytes_received; + notify["connection_filetransfer_bandwidth_received"] = report.file_bytes_sent; } - if(info) { - for(const auto& elm : info->properties) { - notify[elm.first] = elm.second; - } - } else { - //Fill in some server stuff - if(dynamic_pointer_cast(target)) - notify["connection_ping"] = floor(dynamic_pointer_cast(target)->calculatePing()).count(); - else if(dynamic_pointer_cast(target)) - notify["connection_ping"] = floor(dynamic_pointer_cast(target)->client_ping()).count(); + if(info) { + for(const auto& elm : info->properties) { + notify[elm.first] = elm.second; + } + } else { + //Fill in some server stuff + if(dynamic_pointer_cast(target)) + notify["connection_ping"] = floor(dynamic_pointer_cast(target)->calculatePing()).count(); + else if(dynamic_pointer_cast(target)) + notify["connection_ping"] = floor(dynamic_pointer_cast(target)->client_ping()).count(); - if(target->getType() == ClientType::CLIENT_TEASPEAK || target->getType() == ClientType::CLIENT_TEAMSPEAK || target->getType() == ClientType::CLIENT_WEB) { - auto report = target->connectionStatistics->full_report(); + if(target->getType() == ClientType::CLIENT_TEASPEAK || target->getType() == ClientType::CLIENT_TEAMSPEAK || target->getType() == ClientType::CLIENT_WEB) { + auto report = target->connectionStatistics->full_report(); - /* its flipped here because the report is out of the clients view */ - notify["connection_bytes_received_control"] = report.connection_bytes_sent[stats::ConnectionStatistics::category::COMMAND]; - notify["connection_bytes_received_keepalive"] = report.connection_bytes_sent[stats::ConnectionStatistics::category::ACK]; - notify["connection_bytes_received_speech"] = report.connection_bytes_sent[stats::ConnectionStatistics::category::VOICE]; - notify["connection_bytes_sent_control"] = report.connection_bytes_sent[stats::ConnectionStatistics::category::COMMAND]; - notify["connection_bytes_sent_keepalive"] = report.connection_bytes_sent[stats::ConnectionStatistics::category::ACK]; - notify["connection_bytes_sent_speech"] = report.connection_bytes_sent[stats::ConnectionStatistics::category::VOICE]; + /* its flipped here because the report is out of the clients view */ + notify["connection_bytes_received_control"] = report.connection_bytes_sent[stats::ConnectionStatistics::category::COMMAND]; + notify["connection_bytes_received_keepalive"] = report.connection_bytes_sent[stats::ConnectionStatistics::category::ACK]; + notify["connection_bytes_received_speech"] = report.connection_bytes_sent[stats::ConnectionStatistics::category::VOICE]; + notify["connection_bytes_sent_control"] = report.connection_bytes_sent[stats::ConnectionStatistics::category::COMMAND]; + notify["connection_bytes_sent_keepalive"] = report.connection_bytes_sent[stats::ConnectionStatistics::category::ACK]; + notify["connection_bytes_sent_speech"] = report.connection_bytes_sent[stats::ConnectionStatistics::category::VOICE]; - /* its flipped here because the report is out of the clients view */ - notify["connection_packets_received_control"] = report.connection_packets_sent[stats::ConnectionStatistics::category::COMMAND]; - notify["connection_packets_received_keepalive"] = report.connection_packets_sent[stats::ConnectionStatistics::category::ACK]; - notify["connection_packets_received_speech"] = report.connection_packets_sent[stats::ConnectionStatistics::category::VOICE]; - notify["connection_packets_sent_control"] = report.connection_packets_sent[stats::ConnectionStatistics::category::COMMAND]; - notify["connection_packets_sent_keepalive"] = report.connection_packets_sent[stats::ConnectionStatistics::category::ACK]; - notify["connection_packets_sent_speech"] = report.connection_packets_sent[stats::ConnectionStatistics::category::VOICE]; - } - } + /* its flipped here because the report is out of the clients view */ + notify["connection_packets_received_control"] = report.connection_packets_sent[stats::ConnectionStatistics::category::COMMAND]; + notify["connection_packets_received_keepalive"] = report.connection_packets_sent[stats::ConnectionStatistics::category::ACK]; + notify["connection_packets_received_speech"] = report.connection_packets_sent[stats::ConnectionStatistics::category::VOICE]; + notify["connection_packets_sent_control"] = report.connection_packets_sent[stats::ConnectionStatistics::category::COMMAND]; + notify["connection_packets_sent_keepalive"] = report.connection_packets_sent[stats::ConnectionStatistics::category::ACK]; + notify["connection_packets_sent_speech"] = report.connection_packets_sent[stats::ConnectionStatistics::category::VOICE]; + } + } if(target->getClientId() == this->getClientId() || this->permissionGranted(permission::PERMTEST_ORDERED, permission::b_client_remoteaddress_view, 1, target->currentChannel, true)) { notify["connection_client_ip"] = target->getLoggingPeerIp(); @@ -345,12 +345,12 @@ bool ConnectedClient::notifyConnectionInfo(const shared_ptr &ta } //Needs to be filled out - notify["connection_client2server_packetloss_speech"] = not_set; - notify["connection_client2server_packetloss_keepalive"] = not_set; - notify["connection_client2server_packetloss_control"] = not_set; - notify["connection_client2server_packetloss_total"] = not_set; + notify["connection_client2server_packetloss_speech"] = not_set; + notify["connection_client2server_packetloss_keepalive"] = not_set; + notify["connection_client2server_packetloss_control"] = not_set; + notify["connection_client2server_packetloss_total"] = not_set; - notify["connection_connected_time"] = chrono::duration_cast(chrono::system_clock::now() - target->connectTimestamp).count(); + notify["connection_connected_time"] = chrono::duration_cast(chrono::system_clock::now() - target->connectTimestamp).count(); notify["connection_idle_time"] = chrono::duration_cast(chrono::system_clock::now() - target->idleTimestamp).count(); this->sendCommand(notify); return true; @@ -362,11 +362,11 @@ bool ConnectedClient::notifyClientMoved(const shared_ptr &clien std::string msg, std::shared_ptr invoker, bool lock_channel_tree) { - assert(!lock_channel_tree); - assert(client->getClientId() > 0); - assert(client->currentChannel); - assert(target_channel); - assert(this->isClientVisible(client, false) || &*client == this); + assert(!lock_channel_tree); + assert(client->getClientId() > 0); + assert(client->currentChannel); + assert(target_channel); + assert(this->isClientVisible(client, false) || &*client == this); Command mv("notifyclientmoved"); @@ -384,29 +384,29 @@ bool ConnectedClient::notifyClientMoved(const shared_ptr &clien } bool ConnectedClient::notifyClientUpdated(const std::shared_ptr &client, const deque> &props, bool lock) { - shared_lock channel_lock(this->channel_lock, defer_lock); - if(lock) - channel_lock.lock(); + shared_lock channel_lock(this->channel_lock, defer_lock); + if(lock) + channel_lock.lock(); - if(!this->isClientVisible(client, false) && client != this) - return false; + if(!this->isClientVisible(client, false) && client != this) + return false; - auto client_id = client->getClientId(); - if(client_id == 0) { - logError(this->getServerId(), "{} Attempted to send a clientupdate for client id 0. Updated client: {}", CLIENT_STR_LOG_PREFIX, CLIENT_STR_LOG_PREFIX_(client)); - return false; - } - Command response("notifyclientupdated"); - response["clid"] = client_id; - for (const auto &prop : props) { - if(lastOnlineTimestamp.time_since_epoch().count() > 0 && (prop->property_index == property::CLIENT_TOTAL_ONLINE_TIME || prop->property_index == property::CLIENT_MONTH_ONLINE_TIME)) - response[prop->name] = client->properties()[prop].as() + duration_cast(system_clock::now() - client->lastOnlineTimestamp).count(); - else - response[prop->name] = client->properties()[prop].value(); - } + auto client_id = client->getClientId(); + if(client_id == 0) { + logError(this->getServerId(), "{} Attempted to send a clientupdate for client id 0. Updated client: {}", CLIENT_STR_LOG_PREFIX, CLIENT_STR_LOG_PREFIX_(client)); + return false; + } + Command response("notifyclientupdated"); + response["clid"] = client_id; + for (const auto &prop : props) { + if(lastOnlineTimestamp.time_since_epoch().count() > 0 && (prop->property_index == property::CLIENT_TOTAL_ONLINE_TIME || prop->property_index == property::CLIENT_MONTH_ONLINE_TIME)) + response[prop->name] = client->properties()[prop].as() + duration_cast(system_clock::now() - client->lastOnlineTimestamp).count(); + else + response[prop->name] = client->properties()[prop].value(); + } - this->sendCommand(response); - return true; + this->sendCommand(response); + return true; } bool ConnectedClient::notifyPluginCmd(std::string name, std::string msg, std::shared_ptr sender) { @@ -435,7 +435,7 @@ bool ConnectedClient::notifyClientChatClosed(const shared_ptr & } bool ConnectedClient::notifyChannelMoved(const std::shared_ptr &channel, ChannelId order, const std::shared_ptr &invoker) { - if(!channel || !this->channels->channel_visible(channel)) return false; + if(!channel || !this->channels->channel_visible(channel)) return false; Command notify("notifychannelmoved"); INVOKER(notify, invoker); @@ -453,7 +453,7 @@ bool ConnectedClient::notifyChannelCreate(const std::shared_ptr &c if(prop.type() == property::CHANNEL_ORDER) notify[prop.type().name] = orderId; else if(prop.type() == property::CHANNEL_DESCRIPTION) - continue; + continue; else notify[prop.type().name] = prop.value(); } @@ -463,62 +463,62 @@ bool ConnectedClient::notifyChannelCreate(const std::shared_ptr &c } bool ConnectedClient::notifyChannelHide(const std::deque &channel_ids, bool lock_channel_tree) { - if(channel_ids.empty()) return true; + if(channel_ids.empty()) return true; - if(this->getType() == ClientType::CLIENT_TEAMSPEAK) { //Voice hasnt that event - shared_lock server_channel_lock(this->server->channel_tree_lock, defer_lock); - unique_lock client_channel_lock(this->channel_lock, defer_lock); - if(lock_channel_tree) { - server_channel_lock.lock(); - client_channel_lock.lock(); - } else { - assert(mutex_locked(this->channel_lock)); - } + if(this->getType() == ClientType::CLIENT_TEAMSPEAK) { //Voice hasnt that event + shared_lock server_channel_lock(this->server->channel_tree_lock, defer_lock); + unique_lock client_channel_lock(this->channel_lock, defer_lock); + if(lock_channel_tree) { + server_channel_lock.lock(); + client_channel_lock.lock(); + } else { + assert(mutex_locked(this->channel_lock)); + } - deque> clients_to_remove; - { - for(const auto& w_client : this->visibleClients) { - auto client = w_client.lock(); - if(!client) continue; - if(client->currentChannel) { - auto id = client->currentChannel->channelId(); - for(const auto cid : channel_ids) { - if(cid == id) { - clients_to_remove.push_back(client); - break; - } - } - } - } - } + deque> clients_to_remove; + { + for(const auto& w_client : this->visibleClients) { + auto client = w_client.lock(); + if(!client) continue; + if(client->currentChannel) { + auto id = client->currentChannel->channelId(); + for(const auto cid : channel_ids) { + if(cid == id) { + clients_to_remove.push_back(client); + break; + } + } + } + } + } - //TODO: May send a unsubscribe and remove the clients like that? - this->notifyClientLeftView(clients_to_remove, "Channel gone out of view", false, ViewReasonServerLeft); - return this->notifyChannelDeleted(channel_ids, this->server->serverRoot); - } else { - Command notify("notifychannelhide"); - int index = 0; - for(const auto& channel : channel_ids) - notify[index++]["cid"] = channel; - this->sendCommand(notify); - } - return true; + //TODO: May send a unsubscribe and remove the clients like that? + this->notifyClientLeftView(clients_to_remove, "Channel gone out of view", false, ViewReasonServerLeft); + return this->notifyChannelDeleted(channel_ids, this->server->serverRoot); + } else { + Command notify("notifychannelhide"); + int index = 0; + for(const auto& channel : channel_ids) + notify[index++]["cid"] = channel; + this->sendCommand(notify); + } + return true; } bool ConnectedClient::notifyChannelShow(const std::shared_ptr &channel, ts::ChannelId orderId) { - if(!channel) - return false; + if(!channel) + return false; - auto result = false; + auto result = false; if(this->getType() == ClientType::CLIENT_TEAMSPEAK) { //Voice hasn't that event - result = this->notifyChannelCreate(channel, orderId, this->server->serverRoot); + result = this->notifyChannelCreate(channel, orderId, this->server->serverRoot); } else { Command notify("notifychannelshow"); for (auto &prop : channel->properties().list_properties(property::FLAG_CHANNEL_VARIABLE | property::FLAG_CHANNEL_VIEW, this->getType() == CLIENT_TEAMSPEAK ? property::FLAG_NEW : (uint16_t) 0)) { if(prop.type() == property::CHANNEL_ORDER) { - notify[prop.type().name] = orderId; + notify[prop.type().name] = orderId; } else if(prop.type() == property::CHANNEL_DESCRIPTION) { - continue; + continue; } else notify[prop.type().name] = prop.value(); @@ -526,12 +526,12 @@ bool ConnectedClient::notifyChannelShow(const std::shared_ptr this->sendCommand(notify); } if(result && this->subscribeToAll) - this->subscribeChannel({channel}, false, true); + this->subscribeChannel({channel}, false, true); return true; } bool ConnectedClient::notifyChannelDescriptionChanged(std::shared_ptr channel) { - if(!this->channels->channel_visible(channel)) return false; + if(!this->channels->channel_visible(channel)) return false; Command notifyDesChanges("notifychanneldescriptionchanged"); notifyDesChanges["cid"] = channel->channelId(); this->sendCommand(notifyDesChanges); @@ -539,7 +539,7 @@ bool ConnectedClient::notifyChannelDescriptionChanged(std::shared_ptr channel) { - if(!this->channels->channel_visible(channel)) return false; + if(!this->channels->channel_visible(channel)) return false; Command notifyDesChanges("notifychannelpasswordchanged"); notifyDesChanges["cid"] = channel->channelId(); this->sendCommand(notifyDesChanges); @@ -547,147 +547,147 @@ bool ConnectedClient::notifyChannelPasswordChanged(std::shared_ptr } bool ConnectedClient::notifyClientEnterView(const std::shared_ptr &client, const std::shared_ptr &invoker, const std::string& reason, const std::shared_ptr &to, ViewReasonId reasonId, const std::shared_ptr &from, bool lock_channel_tree) { - sassert(client && client->getClientId() > 0); - sassert(to); - sassert(!lock_channel_tree); /* we don't support locking */ - sassert(!this->isClientVisible(client, false) || &*client == this); + sassert(client && client->getClientId() > 0); + sassert(to); + sassert(!lock_channel_tree); /* we don't support locking */ + sassert(!this->isClientVisible(client, false) || &*client == this); - switch (reasonId) { - case ViewReasonId::VREASON_MOVED: - case ViewReasonId::VREASON_BAN: - case ViewReasonId::VREASON_CHANNEL_KICK: - case ViewReasonId::VREASON_SERVER_KICK: - if(!invoker) { - logCritical(this->getServerId(), "{} ConnectedClient::notifyClientEnterView() => missing invoker for reason id {}", CLIENT_STR_LOG_PREFIX, reasonId); - if(this->server) - ;//invoker = this->server->serverRoot.get(); - } - break; - default: - break; - } + switch (reasonId) { + case ViewReasonId::VREASON_MOVED: + case ViewReasonId::VREASON_BAN: + case ViewReasonId::VREASON_CHANNEL_KICK: + case ViewReasonId::VREASON_SERVER_KICK: + if(!invoker) { + logCritical(this->getServerId(), "{} ConnectedClient::notifyClientEnterView() => missing invoker for reason id {}", CLIENT_STR_LOG_PREFIX, reasonId); + if(this->server) + ;//invoker = this->server->serverRoot.get(); + } + break; + default: + break; + } - Command cmd("notifycliententerview"); + Command cmd("notifycliententerview"); - cmd["cfid"] = from ? from->channelId() : 0; - cmd["ctid"] = to ? to->channelId() : 0; - cmd["reasonid"] = reasonId; - INVOKER(cmd, invoker); - switch (reasonId) { - case ViewReasonId::VREASON_MOVED: - case ViewReasonId::VREASON_BAN: - case ViewReasonId::VREASON_CHANNEL_KICK: - case ViewReasonId::VREASON_SERVER_KICK: - cmd["reasonmsg"] = reason; - break; - default: - break; - } + cmd["cfid"] = from ? from->channelId() : 0; + cmd["ctid"] = to ? to->channelId() : 0; + cmd["reasonid"] = reasonId; + INVOKER(cmd, invoker); + switch (reasonId) { + case ViewReasonId::VREASON_MOVED: + case ViewReasonId::VREASON_BAN: + case ViewReasonId::VREASON_CHANNEL_KICK: + case ViewReasonId::VREASON_SERVER_KICK: + cmd["reasonmsg"] = reason; + break; + default: + break; + } - for (const auto &elm : client->properties()->list_properties(property::FLAG_CLIENT_VIEW, this->getType() == CLIENT_TEAMSPEAK ? property::FLAG_NEW : (uint16_t) 0)) { - cmd[elm.type().name] = elm.value(); - } + for (const auto &elm : client->properties()->list_properties(property::FLAG_CLIENT_VIEW, this->getType() == CLIENT_TEAMSPEAK ? property::FLAG_NEW : (uint16_t) 0)) { + cmd[elm.type().name] = elm.value(); + } - visibleClients.emplace_back(client); - this->sendCommand(cmd); + visibleClients.emplace_back(client); + this->sendCommand(cmd); - return true; + return true; } bool ConnectedClient::notifyClientEnterView(const std::deque> &clients, const ts::ViewReasonSystemT &_vrs) { - if(clients.empty()) - return true; - assert(mutex_locked(this->channel_lock)); + if(clients.empty()) + return true; + assert(mutex_locked(this->channel_lock)); - Command cmd("notifycliententerview"); + Command cmd("notifycliententerview"); - cmd["cfid"] = 0; - cmd["reasonid"] = ViewReasonId::VREASON_SYSTEM; - ChannelId current_channel = 0; + cmd["cfid"] = 0; + cmd["reasonid"] = ViewReasonId::VREASON_SYSTEM; + ChannelId current_channel = 0; - size_t index = 0; - auto it = clients.begin(); - while(it != clients.end()) { - auto client = *(it++); + size_t index = 0; + auto it = clients.begin(); + while(it != clients.end()) { + auto client = *(it++); - if(this->isClientVisible(client, false)) - continue; + if(this->isClientVisible(client, false)) + continue; - auto channel = client->getChannel(); - sassert(!channel || channel->channelId() != 0); - if(!channel) /* hmm suspecious */ - continue; + auto channel = client->getChannel(); + sassert(!channel || channel->channelId() != 0); + if(!channel) /* hmm suspecious */ + continue; - if(current_channel != channel->channelId()) { - if(current_channel == 0) - cmd[index]["ctid"] = (current_channel = channel->channelId()); - else { - it--; /* we still have to send him */ - break; - } - } + if(current_channel != channel->channelId()) { + if(current_channel == 0) + cmd[index]["ctid"] = (current_channel = channel->channelId()); + else { + it--; /* we still have to send him */ + break; + } + } - this->visibleClients.push_back(client); - for (const auto &elm : client->properties()->list_properties(property::FLAG_CLIENT_VIEW, this->getType() == CLIENT_TEAMSPEAK ? property::FLAG_NEW : (uint16_t) 0)) { - cmd[index][elm.type().name] = elm.value(); - } + this->visibleClients.push_back(client); + for (const auto &elm : client->properties()->list_properties(property::FLAG_CLIENT_VIEW, this->getType() == CLIENT_TEAMSPEAK ? property::FLAG_NEW : (uint16_t) 0)) { + cmd[index][elm.type().name] = elm.value(); + } - index++; - if(index > 16) /* max 16 clients per packet */ - break; - } + index++; + if(index > 16) /* max 16 clients per packet */ + break; + } - if(index > 0) - this->sendCommand(cmd); + if(index > 0) + this->sendCommand(cmd); - if(it != clients.end()) - return this->notifyClientEnterView({it, clients.end()}, _vrs); - return true; + if(it != clients.end()) + return this->notifyClientEnterView({it, clients.end()}, _vrs); + return true; } bool ConnectedClient::notifyChannelEdited( - const std::shared_ptr &channel, - const std::vector &properties, - const std::shared_ptr &invoker, - bool) { - auto v_channel = this->channels->find_channel(channel->channelId()); - if(!v_channel) return false; //Not visible? Important do not remove! + const std::shared_ptr &channel, + const std::vector &properties, + const std::shared_ptr &invoker, + bool) { + auto v_channel = this->channels->find_channel(channel->channelId()); + if(!v_channel) return false; //Not visible? Important do not remove! - bool send_description_change{false}; + bool send_description_change{false}; - Command notify("notifychanneledited"); - for(auto prop : properties) { - const auto& prop_info = property::impl::info(prop); + Command notify("notifychanneledited"); + for(auto prop : properties) { + const auto& prop_info = property::impl::info(prop); - if(prop == property::CHANNEL_ORDER) - notify[prop_info->name] = v_channel->previous_channel; - else if(prop == property::CHANNEL_DESCRIPTION) { - send_description_change = true; - } else { - notify[prop_info->name] = channel->properties()[prop].as(); - } - } + if(prop == property::CHANNEL_ORDER) + notify[prop_info->name] = v_channel->previous_channel; + else if(prop == property::CHANNEL_DESCRIPTION) { + send_description_change = true; + } else { + notify[prop_info->name] = channel->properties()[prop].as(); + } + } - notify["cid"] = channel->channelId(); - notify["reasonid"] = ViewReasonId::VREASON_EDITED; + notify["cid"] = channel->channelId(); + notify["reasonid"] = ViewReasonId::VREASON_EDITED; - INVOKER(notify, invoker); - this->sendCommand(notify); + INVOKER(notify, invoker); + this->sendCommand(notify); - if(send_description_change) { - Command notify_dchange{"notifychanneldescriptionchanged"}; - notify_dchange["cid"] = channel->channelId(); - this->sendCommand(notify_dchange); - } + if(send_description_change) { + Command notify_dchange{"notifychanneldescriptionchanged"}; + notify_dchange["cid"] = channel->channelId(); + this->sendCommand(notify_dchange); + } - return true; + return true; } bool ConnectedClient::notifyChannelDeleted(const deque& channel_ids, const std::shared_ptr& invoker) { - if(channel_ids.empty()) - return true; + if(channel_ids.empty()) + return true; - Command notify("notifychanneldeleted"); + Command notify("notifychanneldeleted"); int index = 0; for (const auto& channel_id : channel_ids) @@ -704,11 +704,11 @@ bool ConnectedClient::notifyServerUpdated(std::shared_ptr invok Command response("notifyserverupdated"); for (const auto& elm : this->server->properties().list_properties(property::FLAG_SERVER_VARIABLE, this->getType() == CLIENT_TEAMSPEAK ? property::FLAG_NEW : (uint16_t) 0)) { - if(elm.type() == property::VIRTUALSERVER_MIN_WINPHONE_VERSION) - continue; + if(elm.type() == property::VIRTUALSERVER_MIN_WINPHONE_VERSION) + continue; - //if(elm->type() == property::VIRTUALSERVER_RESERVED_SLOTS) - response[elm.type().name] = elm.value(); + //if(elm->type() == property::VIRTUALSERVER_RESERVED_SLOTS) + response[elm.type().name] = elm.value(); } if(getType() == CLIENT_QUERY) @@ -753,71 +753,71 @@ bool ConnectedClient::notifyMusicQueueAdd(const shared_ptr& bot, co notify["song_id"] = entry->getSongId(); notify["url"] = entry->getUrl(); notify["index"] = index; - INVOKER(notify, invoker); + INVOKER(notify, invoker); this->sendCommand(notify); return true; } bool ConnectedClient::notifyMusicQueueRemove(const std::shared_ptr &bot, const std::deque> &entry, const std::shared_ptr& invoker) { - Command notify("notifymusicqueueremove"); - notify["bot_id"] = bot->getClientDatabaseId(); - int index = 0; - for(const auto& song : entry) - notify[index++]["song_id"] = song->getSongId(); - INVOKER(notify, invoker); - this->sendCommand(notify); - return true; + Command notify("notifymusicqueueremove"); + notify["bot_id"] = bot->getClientDatabaseId(); + int index = 0; + for(const auto& song : entry) + notify[index++]["song_id"] = song->getSongId(); + INVOKER(notify, invoker); + this->sendCommand(notify); + return true; } bool ConnectedClient::notifyMusicQueueOrderChange(const std::shared_ptr &bot, const std::shared_ptr &entry, int order, const std::shared_ptr& invoker) { - Command notify("notifymusicqueueorderchange"); - notify["bot_id"] = bot->getClientDatabaseId(); - notify["song_id"] = entry->getSongId(); - notify["index"] = order; - INVOKER(notify, invoker); - this->sendCommand(notify); - return true; + Command notify("notifymusicqueueorderchange"); + notify["bot_id"] = bot->getClientDatabaseId(); + notify["song_id"] = entry->getSongId(); + notify["index"] = order; + INVOKER(notify, invoker); + this->sendCommand(notify); + return true; } bool ConnectedClient::notifyMusicPlayerStatusUpdate(const std::shared_ptr &bot) { - Command notify("notifymusicstatusupdate"); - notify["bot_id"] = bot->getClientDatabaseId(); + Command notify("notifymusicstatusupdate"); + notify["bot_id"] = bot->getClientDatabaseId(); - auto player = bot->current_player(); - if(player) { - notify["player_buffered_index"] = player->bufferedUntil().count(); - notify["player_replay_index"] = player->currentIndex().count(); - } else { - notify["player_buffered_index"] = 0; - notify["player_replay_index"] = 0; - } - this->sendCommand(notify); - return true; + auto player = bot->current_player(); + if(player) { + notify["player_buffered_index"] = player->bufferedUntil().count(); + notify["player_replay_index"] = player->currentIndex().count(); + } else { + notify["player_buffered_index"] = 0; + notify["player_replay_index"] = 0; + } + this->sendCommand(notify); + return true; } extern void apply_song(Command& command, const std::shared_ptr& element, int index = 0); bool ConnectedClient::notifyMusicPlayerSongChange(const std::shared_ptr &bot, const shared_ptr &newEntry) { - Command notify("notifymusicplayersongchange"); - notify["bot_id"] = bot->getClientDatabaseId(); + Command notify("notifymusicplayersongchange"); + notify["bot_id"] = bot->getClientDatabaseId(); - if(newEntry) { - apply_song(notify, newEntry); - } else { - notify["song_id"] = 0; - } - this->sendCommand(notify); - return true; + if(newEntry) { + apply_song(notify, newEntry); + } else { + notify["song_id"] = 0; + } + this->sendCommand(notify); + return true; } bool ConnectedClient::notifyConversationMessageDelete(const ts::ChannelId channel_id, const std::chrono::system_clock::time_point& begin, const std::chrono::system_clock::time_point& end, ts::ClientDbId client_id, size_t size) { - Command notify("notifyconversationmessagedelete"); + Command notify("notifyconversationmessagedelete"); - notify["cid"] = channel_id; - notify["timestamp_begin"] = floor(begin.time_since_epoch()).count(); - notify["timestamp_end"] = floor(end.time_since_epoch()).count(); - notify["cldbid"] = client_id; - notify["limit"] = size; + notify["cid"] = channel_id; + notify["timestamp_begin"] = floor(begin.time_since_epoch()).count(); + notify["timestamp_end"] = floor(end.time_since_epoch()).count(); + notify["cldbid"] = client_id; + notify["limit"] = size; - this->sendCommand(notify); - return true; + this->sendCommand(notify); + return true; } \ No newline at end of file diff --git a/server/src/client/ConnectedClientTextCommandHandler.cpp b/server/src/client/ConnectedClientTextCommandHandler.cpp index 804b8a2..458a19b 100644 --- a/server/src/client/ConnectedClientTextCommandHandler.cpp +++ b/server/src/client/ConnectedClientTextCommandHandler.cpp @@ -47,8 +47,8 @@ if(!var) { \ return true; \ } \ if(!ignore_disabled && var->properties()[property::CLIENT_DISABLED].as()) { \ - send_message(serverInstance->musicRoot(), strobf("This bot has been disabled. Upgrade your TeaSpeak license to use more bots.").string()); \ - return true; \ + send_message(serverInstance->musicRoot(), strobf("This bot has been disabled. Upgrade your TeaSpeak license to use more bots.").string()); \ + return true; \ } inline string filterUrl(string in){ @@ -424,11 +424,11 @@ bool ConnectedClient::handle_text_command( return true; } - auto max_volume = this->cached_permission_value(permission::i_client_music_create_modify_max_volume); - if(max_volume != permNotGranted && !this->permission_granted(max_volume, volume, true)) { - send_message(bot, "You don't have the permission to use higher volumes that " + to_string(max_volume) + "%"); - return true; - } + auto max_volume = this->cached_permission_value(permission::i_client_music_create_modify_max_volume); + if(max_volume != permNotGranted && !this->permission_granted(max_volume, volume, true)) { + send_message(bot, "You don't have the permission to use higher volumes that " + to_string(max_volume) + "%"); + return true; + } bot->volume_modifier(volume / 100); send_message(bot, "Volume successfully changed to " + bot_volume(bot->volumeModifier())); @@ -641,8 +641,8 @@ bool ConnectedClient::handle_text_command( if(!vc) return false; send_message(_this.lock(), "You'll timeout"); - Command cmd("notifyclientupdated"); - vc->sendCommand(cmd); + Command cmd("notifyclientupdated"); + vc->sendCommand(cmd); return true; } else if(TARG(0, "resetip")) { auto vc = dynamic_pointer_cast(_this.lock()); @@ -658,9 +658,9 @@ bool ConnectedClient::handle_text_command( send_message(_this.lock(), "Done :)"); return true; } else if(TARG(0, "binary")) { - send_message(_this.lock(), "Send binary message"); - this->sendCommand(Command{"\02\03\04 \22"}); - return true; + send_message(_this.lock(), "Send binary message"); + this->sendCommand(Command{"\02\03\04 \22"}); + return true; } send_message(_this.lock(), "Invalid protocol command!"); send_message(_this.lock(), "- generation"); diff --git a/server/src/client/DataClient.cpp b/server/src/client/DataClient.cpp index c9c4778..ca02344 100644 --- a/server/src/client/DataClient.cpp +++ b/server/src/client/DataClient.cpp @@ -19,7 +19,7 @@ DataClient::DataClient(sql::SqlManager* database, const std::shared_ptrclientPermissions = nullptr; + this->clientPermissions = nullptr; this->_properties = nullptr; } @@ -35,45 +35,45 @@ bool DataClient::loadDataForCurrentServer() { //TODO for query ClientDbId client_db_id = 0; sql::command(this->sql, "SELECT `cldbid`,`firstConnect`,`connections` FROM `clients` WHERE `serverId` = :sid AND `clientUid` = :uid LIMIT 1", - variable{":sid", server_id}, - variable{":uid", this->getUid()} + variable{":sid", server_id}, + variable{":uid", this->getUid()} ).query([&](DataClient* cl, int length, string* values, string* names){ - for (int index = 0; index < length; index++) { - try { - if (names[index] == "cldbid") { - client_db_id = stoull(values[index]); - } else if (names[index] == "firstConnect") { - cl->properties()[property::CLIENT_CREATED] = values[index]; - } else if (names[index] == "connections") { - cl->properties()[property::CLIENT_TOTALCONNECTIONS] = values[index]; - } else { - logWarning(LOG_INSTANCE, "Received unknown column with name {} within client list", names[index]); - } - } catch(const std::exception& ex) { - logError(server_id, "Failed to load client {} base properties from database. Colum parsing for column {} failed. Value: {}. Message: {}", + for (int index = 0; index < length; index++) { + try { + if (names[index] == "cldbid") { + client_db_id = stoull(values[index]); + } else if (names[index] == "firstConnect") { + cl->properties()[property::CLIENT_CREATED] = values[index]; + } else if (names[index] == "connections") { + cl->properties()[property::CLIENT_TOTALCONNECTIONS] = values[index]; + } else { + logWarning(LOG_INSTANCE, "Received unknown column with name {} within client list", names[index]); + } + } catch(const std::exception& ex) { + logError(server_id, "Failed to load client {} base properties from database. Colum parsing for column {} failed. Value: {}. Message: {}", this->getUid(), names[index], values[index], ex.what() - ); - return 0; - } - } + ); + return 0; + } + } return 0; }, this); if(client_db_id == 0) - return false; + return false; - this->properties()[property::CLIENT_DATABASE_ID] = client_db_id; /* do this before the property saving (it saved the cldbid as well!)*/ + this->properties()[property::CLIENT_DATABASE_ID] = client_db_id; /* do this before the property saving (it saved the cldbid as well!)*/ - //Load general properties - deque copied; - for(const auto& prop : this->_properties->list_properties()){ - if((prop.type().flags & property::FLAG_GLOBAL) == 0) continue; - if(prop.type().default_value == prop.value()) continue; - copied.push_back(prop); - } + //Load general properties + deque copied; + for(const auto& prop : this->_properties->list_properties()){ + if((prop.type().flags & property::FLAG_GLOBAL) == 0) continue; + if(prop.type().default_value == prop.value()) continue; + copied.push_back(prop); + } if(!ref_server) { if(this->getType() == ClientType::CLIENT_WEB || this->getType() == ClientType::CLIENT_TEAMSPEAK) @@ -96,7 +96,7 @@ bool DataClient::loadDataForCurrentServer() { //TODO for query else if(prop.type() == property::CLIENT_LASTCONNECTED) query = "UPDATE `clients` SET `lastConnect` = :value WHERE `serverId` = :sid AND `cldbid` = :cldbid"; else - return; + return; debugMessage(server_id, "[Property] Updating general client table property for client {}. Key: {} Value: {}", client_db_id, prop.type().name, prop.value()); sql::command(this->sql, query, variable{":sid", 0}, variable{":cldbid", client_db_id}, variable{":value", prop.value()}).executeLater() @@ -106,150 +106,150 @@ bool DataClient::loadDataForCurrentServer() { //TODO for query this->_properties->toggleSave(false); for(const auto& e : copied) { - auto p = this->properties()->find(e.type().type_property, e.type().property_index); + auto p = this->properties()->find(e.type().type_property, e.type().property_index); p = e.value(); p.setModified(false); } this->_properties->toggleSave(true); - this->clientPermissions = serverInstance->databaseHelper()->loadClientPermissionManager(ref_server, this->getClientDatabaseId()); + this->clientPermissions = serverInstance->databaseHelper()->loadClientPermissionManager(ref_server, this->getClientDatabaseId()); //Setup / fix stuff if(!this->properties()[property::CLIENT_FLAG_AVATAR].as().empty()){ if( !ref_server || !serverInstance->getFileServer()->findFile("/avatar_" + this->getAvatarId(),serverInstance->getFileServer()->avatarDirectory(ref_server))) { - if(config::server::delete_missing_icon_permissions) + if(config::server::delete_missing_icon_permissions) this->properties()[property::CLIENT_FLAG_AVATAR] = ""; } } if(ref_server) - this->properties()[property::CLIENT_UNREAD_MESSAGES] = ref_server->letters->unread_letter_count(this->getUid()); + this->properties()[property::CLIENT_UNREAD_MESSAGES] = ref_server->letters->unread_letter_count(this->getUid()); return true; } permission::v2::PermissionFlaggedValue DataClient::permissionValueFlagged(permission::PermissionType type, const std::shared_ptr& target, std::shared_ptr cache, std::shared_ptr server, bool server_defined) { - if(!server_defined && !server) - server = this->server; + if(!server_defined && !server) + server = this->server; - if(server) { - auto result = server->calculatePermissions2(this->getClientDatabaseId(), {type}, this->getType(), target ? target->channelId() : 0, false, cache); - if(result.empty() || result[0].first != type) - return permission::v2::empty_permission_flagged_value; - return result[0].second; - } else { - /* if you're not bound to a channel then you cant be bound to a server as well. */ - bool permission_set = false; - permission::PermissionValue result = permNotGranted; - for(const auto &gr : serverInstance->getGroupManager()->getServerGroups(this->getClientDatabaseId(), this->getType())){ - auto group_permissions = gr->group->permissions(); - auto flagged_permissions = group_permissions->permission_value_flagged(type); - if(flagged_permissions.has_value) { - if(flagged_permissions.value > result || flagged_permissions.value == -1) { - result = flagged_permissions.value; - permission_set = true; - } - } - } - return {result, permission_set}; - } + if(server) { + auto result = server->calculatePermissions2(this->getClientDatabaseId(), {type}, this->getType(), target ? target->channelId() : 0, false, cache); + if(result.empty() || result[0].first != type) + return permission::v2::empty_permission_flagged_value; + return result[0].second; + } else { + /* if you're not bound to a channel then you cant be bound to a server as well. */ + bool permission_set = false; + permission::PermissionValue result = permNotGranted; + for(const auto &gr : serverInstance->getGroupManager()->getServerGroups(this->getClientDatabaseId(), this->getType())){ + auto group_permissions = gr->group->permissions(); + auto flagged_permissions = group_permissions->permission_value_flagged(type); + if(flagged_permissions.has_value) { + if(flagged_permissions.value > result || flagged_permissions.value == -1) { + result = flagged_permissions.value; + permission_set = true; + } + } + } + return {result, permission_set}; + } } permission::PermissionValue DataClient::permissionValue(permission::PermissionTestType test, permission::PermissionType type, const std::shared_ptr& target, std::shared_ptr cache, std::shared_ptr server, bool server_defined) { - auto result = this->permissionValueFlagged(type, target, cache, server, server_defined); - if(result.has_value) - return result.value; - return permNotGranted; + auto result = this->permissionValueFlagged(type, target, cache, server, server_defined); + if(result.has_value) + return result.value; + return permNotGranted; } std::deque> DataClient::permissionValues(permission::PermissionTestType test, const std::deque& permissions, const std::shared_ptr &channel, std::shared_ptr cache, std::shared_ptr server, bool server_defined) { - if(!server_defined && !server) server = this->server; + if(!server_defined && !server) server = this->server; - if(server) return server->calculatePermissions(test, this->getClientDatabaseId(), permissions, this->getType(), channel, cache); - deque> result; + if(server) return server->calculatePermissions(test, this->getClientDatabaseId(), permissions, this->getType(), channel, cache); + deque> result; - /* when you're not bound to any channel cou could only have server group permissions */ - /* we're loading here all server groups */ - { - auto server_groups = serverInstance->getGroupManager()->getServerGroups(this->getClientDatabaseId(), this->getType()); - for(const auto& permission : permissions) { - permission::PermissionValue value = permNotGranted; + /* when you're not bound to any channel cou could only have server group permissions */ + /* we're loading here all server groups */ + { + auto server_groups = serverInstance->getGroupManager()->getServerGroups(this->getClientDatabaseId(), this->getType()); + for(const auto& permission : permissions) { + permission::PermissionValue value = permNotGranted; - for(const auto &gr : serverInstance->getGroupManager()->getServerGroups(this->getClientDatabaseId(), this->getType())){ - auto group_permissions = gr->group->permissions(); - auto flagged_permissions = group_permissions->permission_value_flagged(permission); - if(flagged_permissions.has_value) - if(flagged_permissions.value > value || flagged_permissions.value == -1) value = flagged_permissions.value; - } + for(const auto &gr : serverInstance->getGroupManager()->getServerGroups(this->getClientDatabaseId(), this->getType())){ + auto group_permissions = gr->group->permissions(); + auto flagged_permissions = group_permissions->permission_value_flagged(permission); + if(flagged_permissions.has_value) + if(flagged_permissions.value > value || flagged_permissions.value == -1) value = flagged_permissions.value; + } - result.emplace_back(permission, value); - } - } + result.emplace_back(permission, value); + } + } - return result; + return result; } permission::PermissionValue DataClient::getPermissionGrantValue(permission::PermissionTestType test, permission::PermissionType type, const std::shared_ptr& target) { - permission::PermissionValue result = permNotGranted; + permission::PermissionValue result = permNotGranted; - if(this->server) { - return this->server->calculatePermissionGrant(test, this->getClientDatabaseId(), type, this->getType(), target); - } else { - /* if you're not bound to a channel then you cant be bound to a server as well. */ - //TODO: Implement negate flag! - for(const auto &gr : serverInstance->getGroupManager()->getServerGroups(this->getClientDatabaseId(), this->getType())){ - auto group_permissions = gr->group->permissions(); - auto flagged_permissions = group_permissions->permission_granted_flagged(type); - if(flagged_permissions.has_value) - if(flagged_permissions.value > result || flagged_permissions.value == -1) result = flagged_permissions.value; - } - } + if(this->server) { + return this->server->calculatePermissionGrant(test, this->getClientDatabaseId(), type, this->getType(), target); + } else { + /* if you're not bound to a channel then you cant be bound to a server as well. */ + //TODO: Implement negate flag! + for(const auto &gr : serverInstance->getGroupManager()->getServerGroups(this->getClientDatabaseId(), this->getType())){ + auto group_permissions = gr->group->permissions(); + auto flagged_permissions = group_permissions->permission_granted_flagged(type); + if(flagged_permissions.has_value) + if(flagged_permissions.value > result || flagged_permissions.value == -1) result = flagged_permissions.value; + } + } - return result; + return result; } bool DataClient::permissionGranted(PermissionTestType test, permission::PermissionType type, permission::PermissionValue required, const shared_ptr& target, bool force_granted, std::shared_ptr cache, std::shared_ptr server, bool server_defined) { auto value = this->permissionValue(test, type, target, cache, server, server_defined); - bool result = this->permission_granted(value, required, force_granted); + bool result = this->permission_granted(value, required, force_granted); #ifdef DEBUG_PERMISSION - { - auto serverId = 0; - auto client = dynamic_cast(this); - if(client) - serverId = client->getServerId(); - debugMessage(serverId, "[Permission] Value test result for test type {}.", test); - debugMessage(serverId, - "[Permission] Permission: {} Required value: {} Gained value: {} Force required: {} Channel: {} Result: {}", - permission::resolvePermissionData(type)->name, - required, value, force_granted, (target ? target->name() : "none"), result - ); - }; + { + auto serverId = 0; + auto client = dynamic_cast(this); + if(client) + serverId = client->getServerId(); + debugMessage(serverId, "[Permission] Value test result for test type {}.", test); + debugMessage(serverId, + "[Permission] Permission: {} Required value: {} Gained value: {} Force required: {} Channel: {} Result: {}", + permission::resolvePermissionData(type)->name, + required, value, force_granted, (target ? target->name() : "none"), result + ); + }; #endif return result; } bool DataClient::permissionGrantGranted(PermissionTestType test, permission::PermissionType type, permission::PermissionValue required, const shared_ptr& target, bool force_granted) { - auto value = this->getPermissionGrantValue(test, type, target); - bool result = this->permission_granted(value, required, force_granted); + auto value = this->getPermissionGrantValue(test, type, target); + bool result = this->permission_granted(value, required, force_granted); #ifdef DEBUG_PERMISSION - { - auto serverId = 0; - auto client = dynamic_cast(this); - if(client) - serverId = client->getServerId(); - debugMessage(serverId, "[Permission] Grant test result for test type {}.", test); - debugMessage(serverId, - "[Permission] Permission: {} Required value: {} Gained value: {} Force required: {} Channel: {} Result: {}", - permission::resolvePermissionData(type)->name, - required, value, force_granted, (target ? target->name() : "none"), result - ); - }; + { + auto serverId = 0; + auto client = dynamic_cast(this); + if(client) + serverId = client->getServerId(); + debugMessage(serverId, "[Permission] Grant test result for test type {}.", test); + debugMessage(serverId, + "[Permission] Permission: {} Required value: {} Gained value: {} Force required: {} Channel: {} Result: {}", + permission::resolvePermissionData(type)->name, + required, value, force_granted, (target ? target->name() : "none"), result + ); + }; #endif - return result; + return result; } std::vector> DataClient::assignedServerGroups() { @@ -269,11 +269,11 @@ bool DataClient::serverGroupAssigned(const shared_ptr &group) { } bool DataClient::channelGroupAssigned(const shared_ptr &group, const shared_ptr &channel) { - if(!channel) return false; - auto gr = this->assignedChannelGroup(channel); - sassert(gr); - if(!gr) return false; - return gr->group == group; + if(!channel) return false; + auto gr = this->assignedChannelGroup(channel); + sassert(gr); + if(!gr) return false; + return gr->group == group; } std::string DataClient::getAvatarId() { diff --git a/server/src/client/DataClient.h b/server/src/client/DataClient.h index c61b89b..d3e9e0b 100644 --- a/server/src/client/DataClient.h +++ b/server/src/client/DataClient.h @@ -74,12 +74,12 @@ namespace ts { std::shared_ptr server = nullptr, bool server_defined = false); - virtual permission::v2::PermissionFlaggedValue permissionValueFlagged( - permission::PermissionType, - const std::shared_ptr& channel = nullptr, - std::shared_ptr cache = nullptr, - std::shared_ptr server = nullptr, - bool server_defined = false + virtual permission::v2::PermissionFlaggedValue permissionValueFlagged( + permission::PermissionType, + const std::shared_ptr& channel = nullptr, + std::shared_ptr cache = nullptr, + std::shared_ptr server = nullptr, + bool server_defined = false ); virtual bool permissionGranted(permission::PermissionTestType test, @@ -88,8 +88,8 @@ namespace ts { const std::shared_ptr& channel = nullptr, bool required = true, std::shared_ptr cache = nullptr, - std::shared_ptr server = nullptr, - bool server_defined = false); + std::shared_ptr server = nullptr, + bool server_defined = false); inline permission::PermissionValue getPermissionGrantValue(permission::PermissionType type, const std::shared_ptr& targetChannel = nullptr) { return this->getPermissionGrantValue(permission::PERMTEST_ORDERED, type, targetChannel); @@ -104,22 +104,22 @@ namespace ts { return DataClient::permission_granted({value, value != permNotGranted}, required, enforce_required); } - static inline bool permission_granted(ts::permission::v2::PermissionFlaggedValue value, ts::permission::PermissionValue required, bool enforce_required = true) { - if(required == permNotGranted || required == 0) { - if(enforce_required) - return value.value == -1 || value.value > 0; - else - return true; - } else if(!value.has_value) { - return false; - } else { - if(value.value == -1) - return true; - else if(value.value >= required) - return true; - } - return false; - } + static inline bool permission_granted(ts::permission::v2::PermissionFlaggedValue value, ts::permission::PermissionValue required, bool enforce_required = true) { + if(required == permNotGranted || required == 0) { + if(enforce_required) + return value.value == -1 || value.value > 0; + else + return true; + } else if(!value.has_value) { + return false; + } else { + if(value.value == -1) + return true; + else if(value.value >= required) + return true; + } + return false; + } virtual std::vector> assignedServerGroups(); virtual std::shared_ptr assignedChannelGroup(const std::shared_ptr &); diff --git a/server/src/client/SpeakingClient.cpp b/server/src/client/SpeakingClient.cpp index 68671ac..580c6f7 100644 --- a/server/src/client/SpeakingClient.cpp +++ b/server/src/client/SpeakingClient.cpp @@ -22,75 +22,75 @@ using namespace ts::protocol; //#define PKT_LOG_WHISPER bool SpeakingClient::shouldReceiveVoice(const std::shared_ptr &sender) { - //if(this->properties()[property::CLIENT_AWAY].as()) return false; - if(!this->properties()[property::CLIENT_OUTPUT_HARDWARE].as()) return false; - if(this->properties()[property::CLIENT_OUTPUT_MUTED].as()) return false; + //if(this->properties()[property::CLIENT_AWAY].as()) return false; + if(!this->properties()[property::CLIENT_OUTPUT_HARDWARE].as()) return false; + if(this->properties()[property::CLIENT_OUTPUT_MUTED].as()) return false; - { - shared_lock client_lock(this->channel_lock); - for(const auto& entry : this->mutedClients) - if(entry.lock() == sender) - return false; - } - return true; + { + shared_lock client_lock(this->channel_lock); + for(const auto& entry : this->mutedClients) + if(entry.lock() == sender) + return false; + } + return true; } bool SpeakingClient::shouldReceiveVoiceWhisper(const std::shared_ptr &sender) { - if(!this->shouldReceiveVoice(sender)) - return false; + if(!this->shouldReceiveVoice(sender)) + return false; - auto required_permission = this->cached_permission_value(permission::i_client_needed_whisper_power); - if(required_permission == permNotGranted) - return true; + auto required_permission = this->cached_permission_value(permission::i_client_needed_whisper_power); + if(required_permission == permNotGranted) + return true; - auto granted_permission = sender->cached_permission_value(permission::i_client_whisper_power); - return granted_permission >= required_permission; + auto granted_permission = sender->cached_permission_value(permission::i_client_whisper_power); + return granted_permission >= required_permission; } void SpeakingClient::handlePacketVoice(const pipes::buffer_view& data, bool head, bool fragmented) { - auto server = this->getServer(); - auto self = _this.lock(); - if(!self || !server) return; + auto server = this->getServer(); + auto self = _this.lock(); + if(!self || !server) return; if(data.length() < 3) { - this->disconnect("invalid packet (Voice; Length: " + to_string(data.length()) + ")"); - return; + this->disconnect("invalid packet (Voice; Length: " + to_string(data.length()) + ")"); + return; } auto current_channel = this->currentChannel; if(!current_channel) { return; } if(!this->allowedToTalk) { return; } - this->updateSpeak(false, system_clock::now()); - this->resetIdleTime(); + this->updateSpeak(false, system_clock::now()); + this->resetIdleTime(); auto target_clients = this->server->getClientsByChannel(current_channel); target_clients.erase(std::remove_if(target_clients.begin(), target_clients.end(), [&](const shared_ptr& client) { - if(client == this) return true; - auto speaking_client = dynamic_pointer_cast(client); - if(!speaking_client) return true; + if(client == this) return true; + auto speaking_client = dynamic_pointer_cast(client); + if(!speaking_client) return true; - return !speaking_client->shouldReceiveVoice(self); + return !speaking_client->shouldReceiveVoice(self); }), target_clients.end()); if(target_clients.empty()) { - return; + return; } - VoicePacketFlags flags{}; - flags.head = head; - flags.fragmented = fragmented; - flags.new_protocol = false; - { - //crypt_mode = 1 | disabled - //crypt_mode = 2 | enabled - auto crypt_mode = this->server->voice_encryption_mode(); - if(crypt_mode == 0) - flags.encrypted = !current_channel->properties()[property::CHANNEL_CODEC_IS_UNENCRYPTED].as(); - else - flags.encrypted = crypt_mode == 2; - } + VoicePacketFlags flags{}; + flags.head = head; + flags.fragmented = fragmented; + flags.new_protocol = false; + { + //crypt_mode = 1 | disabled + //crypt_mode = 2 | enabled + auto crypt_mode = this->server->voice_encryption_mode(); + if(crypt_mode == 0) + flags.encrypted = !current_channel->properties()[property::CHANNEL_CODEC_IS_UNENCRYPTED].as(); + else + flags.encrypted = crypt_mode == 2; + } uint16_t vpacketId = be2le16((char*) data.data_ptr()); - auto codec = (uint8_t) data[2]; + auto codec = (uint8_t) data[2]; #ifdef PKT_LOG_VOICE logTrace(lstream << CLIENT_LOG_PREFIX << "Voice length: " << data.length() << " -> id: " << vpacketId << " codec: " << (int) codec << " head: " << head << " fragmented: " << fragmented); #endif @@ -102,13 +102,13 @@ void SpeakingClient::handlePacketVoice(const pipes::buffer_view& data, bool head buffer[4] = codec; if(data.length() - 3 > 0) { - memcpy(&buffer[5], &data[3], data.length() - 3); + memcpy(&buffer[5], &data[3], data.length() - 3); } - for (const auto& client : target_clients) { - auto speaking_client = static_pointer_cast(client); - speaking_client->send_voice_packet(pipes::buffer_view{buffer, data.length() + 2}, flags); - } + for (const auto& client : target_clients) { + auto speaking_client = static_pointer_cast(client); + speaking_client->send_voice_packet(pipes::buffer_view{buffer, data.length() + 2}, flags); + } } //2 + 2 + 8 @@ -116,20 +116,20 @@ void SpeakingClient::handlePacketVoice(const pipes::buffer_view& data, bool head //#define PKT_LOG_WHISPER enum WhisperType { - SERVER_GROUP = 0, - CHANNEL_GROUP = 1, - CHANNEL_COMMANDER = 2, - ALL = 3 + SERVER_GROUP = 0, + CHANNEL_GROUP = 1, + CHANNEL_COMMANDER = 2, + ALL = 3 }; enum WhisperTarget { - CHANNEL_ALL = 0, - CHANNEL_CURRENT = 1, - CHANNEL_PARENT = 2, - CHANNEL_ALL_PARENT = 3, - CHANNEL_FAMILY = 4, - CHANNEL_COMPLETE_FAMILY = 5, - CHANNEL_SUBCHANNELS = 6 + CHANNEL_ALL = 0, + CHANNEL_CURRENT = 1, + CHANNEL_PARENT = 2, + CHANNEL_ALL_PARENT = 3, + CHANNEL_FAMILY = 4, + CHANNEL_COMPLETE_FAMILY = 5, + CHANNEL_SUBCHANNELS = 6 }; //All clients => type := SERVER_GROUP and target_id := 0 @@ -137,192 +137,192 @@ enum WhisperTarget { //Channel group => type := CHANNEL_GROUP and target_id := //Channel commander => type := CHANNEL_COMMANDER and target_id := 0 void SpeakingClient::handlePacketVoiceWhisper(const pipes::buffer_view& data, bool new_packet) { - if(data.length() < 5) { - this->disconnect("Invalid packet (Voice whisper)"); - logMessage(this->getServerId(), "{} Tried to send a too short whisper packet. Length: {}", CLIENT_STR_LOG_PREFIX, data.length()); - return; - } + if(data.length() < 5) { + this->disconnect("Invalid packet (Voice whisper)"); + logMessage(this->getServerId(), "{} Tried to send a too short whisper packet. Length: {}", CLIENT_STR_LOG_PREFIX, data.length()); + return; + } - uint16_t offset = 0; - auto vpacketId = be2le16((char*) data.data_ptr(), offset, &offset); - auto codec = (uint8_t) data[offset++]; + uint16_t offset = 0; + auto vpacketId = be2le16((char*) data.data_ptr(), offset, &offset); + auto codec = (uint8_t) data[offset++]; - VoicePacketFlags flags{}; - flags.head = false; - flags.fragmented = false; - flags.new_protocol = new_packet; + VoicePacketFlags flags{}; + flags.head = false; + flags.fragmented = false; + flags.new_protocol = new_packet; - if(new_packet) { - if(data.length() < 7) { - this->disconnect("Invalid packet (Voice whisper | New)"); - logMessage(this->getServerId(), "{} Tried to send a too short whisper packet. Length: {}", CLIENT_STR_LOG_PREFIX, data.length()); - return; - } + if(new_packet) { + if(data.length() < 7) { + this->disconnect("Invalid packet (Voice whisper | New)"); + logMessage(this->getServerId(), "{} Tried to send a too short whisper packet. Length: {}", CLIENT_STR_LOG_PREFIX, data.length()); + return; + } - auto type = (WhisperType) data[offset++]; - auto target = (WhisperTarget) data[offset++]; - auto type_id = be2le64((char*) data.data_ptr(), offset, &offset); - this->resetIdleTime(); + auto type = (WhisperType) data[offset++]; + auto target = (WhisperTarget) data[offset++]; + auto type_id = be2le64((char*) data.data_ptr(), offset, &offset); + this->resetIdleTime(); - size_t data_length = data.length() - offset; + size_t data_length = data.length() - offset; #ifdef PKT_LOG_WHISPER - logTrace(this->getServerId(), "{} Whisper data length: {}. Type: {}. Target: {}. Target ID: {}.", CLIENT_STR_LOG_PREFIX, data_length, type, target, type_id); + logTrace(this->getServerId(), "{} Whisper data length: {}. Type: {}. Target: {}. Target ID: {}.", CLIENT_STR_LOG_PREFIX, data_length, type, target, type_id); #endif - deque> available_clients; - for(const auto& client : this->server->getClients()) { - auto speakingClient = dynamic_pointer_cast(client); - if(!speakingClient || client == this) continue; - if(!speakingClient->currentChannel) continue; + deque> available_clients; + for(const auto& client : this->server->getClients()) { + auto speakingClient = dynamic_pointer_cast(client); + if(!speakingClient || client == this) continue; + if(!speakingClient->currentChannel) continue; - if(type == WhisperType::ALL) { - available_clients.push_back(speakingClient); - } else if(type == WhisperType::SERVER_GROUP) { - if(type_id == 0) - available_clients.push_back(speakingClient); - else { - shared_lock client_lock(this->channel_lock); - for(const auto& id : client->cached_server_groups) { - if(id == type_id) { - available_clients.push_back(speakingClient); - break; - } - } - } - } else if(type == WhisperType::CHANNEL_GROUP) { - if(client->cached_channel_group == type_id) - available_clients.push_back(speakingClient); - } else if(type == WhisperType::CHANNEL_COMMANDER) { - if(client->properties()[property::CLIENT_IS_CHANNEL_COMMANDER].as()) - available_clients.push_back(speakingClient); - } - } + if(type == WhisperType::ALL) { + available_clients.push_back(speakingClient); + } else if(type == WhisperType::SERVER_GROUP) { + if(type_id == 0) + available_clients.push_back(speakingClient); + else { + shared_lock client_lock(this->channel_lock); + for(const auto& id : client->cached_server_groups) { + if(id == type_id) { + available_clients.push_back(speakingClient); + break; + } + } + } + } else if(type == WhisperType::CHANNEL_GROUP) { + if(client->cached_channel_group == type_id) + available_clients.push_back(speakingClient); + } else if(type == WhisperType::CHANNEL_COMMANDER) { + if(client->properties()[property::CLIENT_IS_CHANNEL_COMMANDER].as()) + available_clients.push_back(speakingClient); + } + } - if(target == WhisperTarget::CHANNEL_CURRENT) { - available_clients.erase(std::remove_if(available_clients.begin(), available_clients.end(), [&](const shared_ptr& target) { - return target->currentChannel != this->currentChannel; - }), available_clients.end()); - } else if(target == WhisperTarget::CHANNEL_PARENT) { - auto current_parent = this->currentChannel->parent(); - if(!current_parent) return; + if(target == WhisperTarget::CHANNEL_CURRENT) { + available_clients.erase(std::remove_if(available_clients.begin(), available_clients.end(), [&](const shared_ptr& target) { + return target->currentChannel != this->currentChannel; + }), available_clients.end()); + } else if(target == WhisperTarget::CHANNEL_PARENT) { + auto current_parent = this->currentChannel->parent(); + if(!current_parent) return; - available_clients.erase(std::remove_if(available_clients.begin(), available_clients.end(), [&](const shared_ptr& target) { - return target->currentChannel != current_parent; - }), available_clients.end()); - } else if(target == WhisperTarget::CHANNEL_ALL_PARENT) { - shared_ptr current_parent; - { - current_parent = this->currentChannel->parent(); - if(!current_parent) return; - } - available_clients.erase(std::remove_if(available_clients.begin(), available_clients.end(), [&](const shared_ptr& target) { - auto tmp_parent = current_parent; - while(tmp_parent && tmp_parent != target->currentChannel) - tmp_parent = tmp_parent->parent(); - return target->currentChannel != tmp_parent; - }), available_clients.end()); - } else if(target == WhisperTarget::CHANNEL_FAMILY) { - shared_ptr current = this->currentChannel; - available_clients.erase(std::remove_if(available_clients.begin(), available_clients.end(), [&](const shared_ptr& target) { - auto tmp_current = target->currentChannel; - while(tmp_current && tmp_current != current) - tmp_current = tmp_current->parent(); - return current != tmp_current; - }), available_clients.end()); - } else if(target == WhisperTarget::CHANNEL_COMPLETE_FAMILY) { - shared_ptr current = this->currentChannel; - while(current && current->parent()) current = current->parent(); - available_clients.erase(std::remove_if(available_clients.begin(), available_clients.end(), [&](const shared_ptr& target) { - auto tmp_current = target->currentChannel; - while(tmp_current && tmp_current != current) - tmp_current = tmp_current->parent(); - return current != tmp_current; - }), available_clients.end()); - } else if(target == WhisperTarget::CHANNEL_SUBCHANNELS) { - shared_ptr current = this->currentChannel; - available_clients.erase(std::remove_if(available_clients.begin(), available_clients.end(), [&](const shared_ptr& target) { - return target->currentChannel->parent() != current; - }), available_clients.end()); - } - if(available_clients.empty()) return; + available_clients.erase(std::remove_if(available_clients.begin(), available_clients.end(), [&](const shared_ptr& target) { + return target->currentChannel != current_parent; + }), available_clients.end()); + } else if(target == WhisperTarget::CHANNEL_ALL_PARENT) { + shared_ptr current_parent; + { + current_parent = this->currentChannel->parent(); + if(!current_parent) return; + } + available_clients.erase(std::remove_if(available_clients.begin(), available_clients.end(), [&](const shared_ptr& target) { + auto tmp_parent = current_parent; + while(tmp_parent && tmp_parent != target->currentChannel) + tmp_parent = tmp_parent->parent(); + return target->currentChannel != tmp_parent; + }), available_clients.end()); + } else if(target == WhisperTarget::CHANNEL_FAMILY) { + shared_ptr current = this->currentChannel; + available_clients.erase(std::remove_if(available_clients.begin(), available_clients.end(), [&](const shared_ptr& target) { + auto tmp_current = target->currentChannel; + while(tmp_current && tmp_current != current) + tmp_current = tmp_current->parent(); + return current != tmp_current; + }), available_clients.end()); + } else if(target == WhisperTarget::CHANNEL_COMPLETE_FAMILY) { + shared_ptr current = this->currentChannel; + while(current && current->parent()) current = current->parent(); + available_clients.erase(std::remove_if(available_clients.begin(), available_clients.end(), [&](const shared_ptr& target) { + auto tmp_current = target->currentChannel; + while(tmp_current && tmp_current != current) + tmp_current = tmp_current->parent(); + return current != tmp_current; + }), available_clients.end()); + } else if(target == WhisperTarget::CHANNEL_SUBCHANNELS) { + shared_ptr current = this->currentChannel; + available_clients.erase(std::remove_if(available_clients.begin(), available_clients.end(), [&](const shared_ptr& target) { + return target->currentChannel->parent() != current; + }), available_clients.end()); + } + if(available_clients.empty()) return; - //Create the packet data - char packet_buffer[OUT_WHISPER_PKT_OFFSET + data_length]; - if(offset < data.length()) - memcpy(&packet_buffer[OUT_WHISPER_PKT_OFFSET], &data[offset], data_length); + //Create the packet data + char packet_buffer[OUT_WHISPER_PKT_OFFSET + data_length]; + if(offset < data.length()) + memcpy(&packet_buffer[OUT_WHISPER_PKT_OFFSET], &data[offset], data_length); - le2be16(vpacketId, packet_buffer, 0); - le2be16(this->getClientId(), packet_buffer, 2); - packet_buffer[4] = codec; + le2be16(vpacketId, packet_buffer, 0); + le2be16(this->getClientId(), packet_buffer, 2); + packet_buffer[4] = codec; - VoicePacketFlags flags{}; - auto data = pipes::buffer_view(packet_buffer, OUT_WHISPER_PKT_OFFSET + data_length); - for(const auto& cl : available_clients){ - if(cl->shouldReceiveVoiceWhisper(_this.lock())) - cl->send_voice_whisper_packet(data, flags); - } + VoicePacketFlags flags{}; + auto data = pipes::buffer_view(packet_buffer, OUT_WHISPER_PKT_OFFSET + data_length); + for(const auto& cl : available_clients){ + if(cl->shouldReceiveVoiceWhisper(_this.lock())) + cl->send_voice_whisper_packet(data, flags); + } - this->updateSpeak(false, system_clock::now()); - } else { - auto clientCount = (uint8_t) data[offset++]; - auto channelCount = (uint8_t) data[offset++]; - if(data.length() < 5 + channelCount * 2 + clientCount * 8) { - logMessage(this->getServerId(), "{} Tried to send a too short whisper packet. Length: {} Required: {}", CLIENT_STR_LOG_PREFIX, data.length(), to_string(5 + channelCount * 2 + clientCount * 8)); - return; - } + this->updateSpeak(false, system_clock::now()); + } else { + auto clientCount = (uint8_t) data[offset++]; + auto channelCount = (uint8_t) data[offset++]; + if(data.length() < 5 + channelCount * 2 + clientCount * 8) { + logMessage(this->getServerId(), "{} Tried to send a too short whisper packet. Length: {} Required: {}", CLIENT_STR_LOG_PREFIX, data.length(), to_string(5 + channelCount * 2 + clientCount * 8)); + return; + } - this->resetIdleTime(); - ChannelId channelIds[clientCount]; - ClientId clientIds[channelCount]; + this->resetIdleTime(); + ChannelId channelIds[clientCount]; + ClientId clientIds[channelCount]; - for(uint8_t index = 0; index < clientCount; index++) - channelIds[index] = be2le64((char*) data.data_ptr(), offset, &offset); - for(uint8_t index = 0; index < channelCount; index++) - clientIds[index] = be2le16((char*) data.data_ptr(), offset, &offset); + for(uint8_t index = 0; index < clientCount; index++) + channelIds[index] = be2le64((char*) data.data_ptr(), offset, &offset); + for(uint8_t index = 0; index < channelCount; index++) + clientIds[index] = be2le16((char*) data.data_ptr(), offset, &offset); - size_t dataLength = data.length() - offset; + size_t dataLength = data.length() - offset; #ifdef PKT_LOG_WHISPER - logTrace(this->getServerId(), "{} Whisper data length: {}. Client count: {}. Channel count: {}.", CLIENT_STR_LOG_PREFIX, dataLength, clientCount, channelCount); + logTrace(this->getServerId(), "{} Whisper data length: {}. Client count: {}. Channel count: {}.", CLIENT_STR_LOG_PREFIX, dataLength, clientCount, channelCount); #endif - //Create the packet data - char packetBuffer[OUT_WHISPER_PKT_OFFSET + dataLength]; - if(offset < data.length()) - memcpy(&packetBuffer[OUT_WHISPER_PKT_OFFSET], &data[offset], dataLength); + //Create the packet data + char packetBuffer[OUT_WHISPER_PKT_OFFSET + dataLength]; + if(offset < data.length()) + memcpy(&packetBuffer[OUT_WHISPER_PKT_OFFSET], &data[offset], dataLength); - le2be16(vpacketId, packetBuffer, 0); - le2be16(this->getClientId(), packetBuffer, 2); - packetBuffer[4] = codec; + le2be16(vpacketId, packetBuffer, 0); + le2be16(this->getClientId(), packetBuffer, 2); + packetBuffer[4] = codec; - VoicePacketFlags flags{}; - auto data = pipes::buffer_view(packetBuffer, OUT_WHISPER_PKT_OFFSET + dataLength); + VoicePacketFlags flags{}; + auto data = pipes::buffer_view(packetBuffer, OUT_WHISPER_PKT_OFFSET + dataLength); - for(const auto& cl : this->server->getClients()){ //Faster? - auto speakingClient = dynamic_pointer_cast(cl); - if(!speakingClient || cl == this) continue; - if(!cl->currentChannel) continue; + for(const auto& cl : this->server->getClients()){ //Faster? + auto speakingClient = dynamic_pointer_cast(cl); + if(!speakingClient || cl == this) continue; + if(!cl->currentChannel) continue; - auto clientChannelId = cl->currentChannel->channelId(); - auto clientId = cl->getClientId(); + auto clientChannelId = cl->currentChannel->channelId(); + auto clientId = cl->getClientId(); - for(uint8_t index = 0; index < clientCount; index++) - if(channelIds[index] == clientChannelId) goto handleSend; - for(uint8_t index = 0; index < channelCount; index++) - if(clientIds[index] == clientId) goto handleSend; - continue; + for(uint8_t index = 0; index < clientCount; index++) + if(channelIds[index] == clientChannelId) goto handleSend; + for(uint8_t index = 0; index < channelCount; index++) + if(clientIds[index] == clientId) goto handleSend; + continue; - handleSend: - if(speakingClient->shouldReceiveVoiceWhisper(_this.lock())) - speakingClient->send_voice_whisper_packet(data, flags); - } + handleSend: + if(speakingClient->shouldReceiveVoiceWhisper(_this.lock())) + speakingClient->send_voice_whisper_packet(data, flags); + } - this->updateSpeak(false, system_clock::now()); - } + this->updateSpeak(false, system_clock::now()); + } } #define TEST_PARM(type) \ do {\ - if(!cmd[0][key].castable())\ - return {findError("parameter_invalid"), "Invalid type for " + key};\ + if(!cmd[0][key].castable())\ + return {findError("parameter_invalid"), "Invalid type for " + key};\ } while(false) auto regex_wildcard = std::regex(".*"); @@ -330,12 +330,12 @@ auto regex_wildcard = std::regex(".*"); #define S(x) #x #define HWID_REGEX(name, pattern) \ auto regex_hwid_ ##name = [](){ \ - try { \ - return std::regex(pattern); \ - } catch (std::exception& ex) { \ - logError(0, "Failed to parse regex for " S(name)); \ - } \ - return regex_wildcard; \ + try { \ + return std::regex(pattern); \ + } catch (std::exception& ex) { \ + logError(0, "Failed to parse regex for " S(name)); \ + } \ + return regex_wildcard; \ }(); HWID_REGEX(windows, "^[a-z0-9]{32},[a-z0-9]{32}$"); @@ -344,7 +344,7 @@ HWID_REGEX(android, "^[a-z0-9]{16}$"); HWID_REGEX(ios, "^[A-Z0-9]{8}-[A-Z0-9]{4}-[A-Z0-9]{4}-[A-Z0-9]{4}-[A-Z0-9]{12}$"); CommandResult SpeakingClient::handleCommandClientInit(Command& cmd) { - TIMING_START(timings); + TIMING_START(timings); { lock_guard lock(this->server->join_attempts_lock); @@ -358,439 +358,439 @@ CommandResult SpeakingClient::handleCommandClientInit(Command& cmd) { } TIMING_STEP(timings, "join atmp c"); - 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 "); - this->server->getGroupManager()->enableCache(this->getClientDatabaseId()); - TIMING_STEP(timings, "gr cache "); + 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 "); + this->server->getGroupManager()->enableCache(this->getClientDatabaseId()); + TIMING_STEP(timings, "gr cache "); - const static vector available_parameters = { - "client_nickname", + const static vector available_parameters = { + "client_nickname", "client_version", - "client_platform", - "client_input_muted", - "client_input_hardware", - "client_output_hardware", - "client_output_muted", - "client_default_channel", - "client_default_channel_password", - "client_server_password", - "client_meta_data", - "client_version_sign", - "client_key_offset", - "client_nickname_phonetic", - "client_default_token", - "client_badges=badges", - "client_badges", - "client_myteamspeak_id", - "client_integrations", - "client_active_integrations_info", - "client_browser_engine", + "client_platform", + "client_input_muted", + "client_input_hardware", + "client_output_hardware", + "client_output_muted", + "client_default_channel", + "client_default_channel_password", + "client_server_password", + "client_meta_data", + "client_version_sign", + "client_key_offset", + "client_nickname_phonetic", + "client_default_token", + "client_badges=badges", + "client_badges", + "client_myteamspeak_id", + "client_integrations", + "client_active_integrations_info", + "client_browser_engine", - "client_away", - "client_away_message", + "client_away", + "client_away_message", - "hwid", - "myTeamspeakId", - "acTime", - "userPubKey", - "authSign", - "pubSign", - "pubSignCert" - }; + "hwid", + "myTeamspeakId", + "acTime", + "userPubKey", + "authSign", + "pubSign", + "pubSignCert" + }; - for(const auto& key : cmd[0].keys()) { - if(key == "return_code") continue; - bool parm_allowed = false; - for(const auto& _allowed_key : available_parameters) { - if(_allowed_key == key) { - parm_allowed = true; - break; - } - } - if(!parm_allowed) { - debugMessage(this->getServerId(), "{} Tried to insert a not allowed parameter within clientinit (Key: {}, Value: {})", CLIENT_STR_LOG_PREFIX, key,cmd[key].string()); - continue; - } + for(const auto& key : cmd[0].keys()) { + if(key == "return_code") continue; + bool parm_allowed = false; + for(const auto& _allowed_key : available_parameters) { + if(_allowed_key == key) { + parm_allowed = true; + break; + } + } + if(!parm_allowed) { + debugMessage(this->getServerId(), "{} Tried to insert a not allowed parameter within clientinit (Key: {}, Value: {})", CLIENT_STR_LOG_PREFIX, key,cmd[key].string()); + continue; + } - if(key == "myTeamspeakId") { - this->properties()[property::CLIENT_MYTEAMSPEAK_ID] = cmd[key].string(); - continue; - } else if(key == "acTime") continue; - else if(key == "userPubKey") continue; - else if(key == "authSign") continue; - else if(key == "pubSign") continue; - else if(key == "pubSignCert") continue; - else if(key == "client_version" || key == "client_platform") { - for(auto& character : cmd[key].string()) - if(!isascii(character)) - return {findError("parameter_invalid"), "invalid version or platform"}; - } + if(key == "myTeamspeakId") { + this->properties()[property::CLIENT_MYTEAMSPEAK_ID] = cmd[key].string(); + continue; + } else if(key == "acTime") continue; + else if(key == "userPubKey") continue; + else if(key == "authSign") continue; + else if(key == "pubSign") continue; + else if(key == "pubSignCert") continue; + else if(key == "client_version" || key == "client_platform") { + for(auto& character : cmd[key].string()) + if(!isascii(character)) + return {findError("parameter_invalid"), "invalid version or platform"}; + } - const auto &info = property::info(key); - if(*info == property::CLIENT_UNDEFINED) { + const auto &info = property::info(key); + if(*info == property::CLIENT_UNDEFINED) { logError(this->getServerId(), "{} Tried to pass a unknown value {}. Please report this, if you're sure that this key should be known!", CLIENT_STR_LOG_PREFIX, key); continue; - //return {findError("parameter_invalid"), "Unknown property " + key}; - } - if(!info->validate_input(cmd[key].as())) return {findError("parameter_invalid"), "Unknown value type for " + key}; + //return {findError("parameter_invalid"), "Unknown property " + key}; + } + if(!info->validate_input(cmd[key].as())) return {findError("parameter_invalid"), "Unknown value type for " + key}; - this->properties()[info] = cmd[key].as(); - } - debugMessage(this->getServerId(), "{} Got client init. (HWID: {})", CLIENT_STR_LOG_PREFIX, this->getHardwareId()); - TIMING_STEP(timings, "props apply"); + this->properties()[info] = cmd[key].as(); + } + debugMessage(this->getServerId(), "{} Got client init. (HWID: {})", CLIENT_STR_LOG_PREFIX, this->getHardwareId()); + TIMING_STEP(timings, "props apply"); - auto permissions_list = this->permissionValues(permission::PERMTEST_ORDERED, { - permission::b_virtualserver_join_ignore_password, - permission::b_client_ignore_bans, - permission::b_client_ignore_vpn, + auto permissions_list = this->permissionValues(permission::PERMTEST_ORDERED, { + permission::b_virtualserver_join_ignore_password, + permission::b_client_ignore_bans, + permission::b_client_ignore_vpn, - permission::i_client_max_clones_uid, - permission::i_client_max_clones_ip, - permission::i_client_max_clones_hwid, + permission::i_client_max_clones_uid, + permission::i_client_max_clones_ip, + permission::i_client_max_clones_hwid, - permission::b_client_enforce_valid_hwid, + permission::b_client_enforce_valid_hwid, - permission::b_client_use_reserved_slot - }, nullptr); - auto permissions = map(permissions_list.begin(), permissions_list.end()); - TIMING_STEP(timings, "perm calc 1"); + permission::b_client_use_reserved_slot + }, nullptr); + auto permissions = map(permissions_list.begin(), permissions_list.end()); + TIMING_STEP(timings, "perm calc 1"); - if(geoloc::provider_vpn && permissions[permission::b_client_ignore_vpn] == permNotGranted) { - auto provider = this->isAddressV4() ? geoloc::provider_vpn->resolveInfoV4(this->getPeerIp(), true) : geoloc::provider_vpn->resolveInfoV6(this->getPeerIp(), true); - if(provider) - return {findError(0xD01), strvar::transform(ts::config::messages::kick_vpn, strvar::StringValue{"provider.name", provider->name}, strvar::StringValue{"provider.website", provider->side})}; - } + if(geoloc::provider_vpn && permissions[permission::b_client_ignore_vpn] == permNotGranted) { + auto provider = this->isAddressV4() ? geoloc::provider_vpn->resolveInfoV4(this->getPeerIp(), true) : geoloc::provider_vpn->resolveInfoV6(this->getPeerIp(), true); + if(provider) + return {findError(0xD01), strvar::transform(ts::config::messages::kick_vpn, strvar::StringValue{"provider.name", provider->name}, strvar::StringValue{"provider.website", provider->side})}; + } - if(this->getType() == ClientType::CLIENT_TEAMSPEAK && (permissions[permission::b_client_enforce_valid_hwid] == permNotGranted && permissions[permission::b_client_enforce_valid_hwid] == 0)) { - auto hwid = this->properties()[property::CLIENT_HARDWARE_ID].as(); - if( - !std::regex_match(hwid, regex_hwid_windows) && - !std::regex_match(hwid, regex_hwid_unix) && - !std::regex_match(hwid, regex_hwid_android) && - !std::regex_match(hwid, regex_hwid_ios) - ) { - return {findError("parameter_invalid"), config::messages::kick_invalid_hardware_id}; - } - } - TIMING_STEP(timings, "valid hw ip"); + if(this->getType() == ClientType::CLIENT_TEAMSPEAK && (permissions[permission::b_client_enforce_valid_hwid] == permNotGranted && permissions[permission::b_client_enforce_valid_hwid] == 0)) { + auto hwid = this->properties()[property::CLIENT_HARDWARE_ID].as(); + if( + !std::regex_match(hwid, regex_hwid_windows) && + !std::regex_match(hwid, regex_hwid_unix) && + !std::regex_match(hwid, regex_hwid_android) && + !std::regex_match(hwid, regex_hwid_ios) + ) { + return {findError("parameter_invalid"), config::messages::kick_invalid_hardware_id}; + } + } + TIMING_STEP(timings, "valid hw ip"); - auto ignorePassword = permissions[permission::b_virtualserver_join_ignore_password] > 0 && permissions[permission::b_virtualserver_join_ignore_password] != permNotGranted; - if(!ignorePassword) - if(!this->server->verifyServerPassword(cmd["client_server_password"].string(), true)) return {findError("server_invalid_password"), "invalid server password"}; + auto ignorePassword = permissions[permission::b_virtualserver_join_ignore_password] > 0 && permissions[permission::b_virtualserver_join_ignore_password] != permNotGranted; + if(!ignorePassword) + if(!this->server->verifyServerPassword(cmd["client_server_password"].string(), true)) return {findError("server_invalid_password"), "invalid server password"}; - size_t clones_uid = 0; - size_t clones_ip = 0; - size_t clones_hwid = 0; + size_t clones_uid = 0; + size_t clones_ip = 0; + size_t clones_hwid = 0; - auto _own_hwid = this->getHardwareId(); - this->server->forEachClient([&](const shared_ptr& client) { - if(client->getExternalType() != CLIENT_TEAMSPEAK) return; - if(client->getUid() == this->getUid()) - clones_uid++; - if(client->getPeerIp() == this->getPeerIp()) - clones_ip++; - if(client->getHardwareId() == _own_hwid) - clones_hwid++; - }); + auto _own_hwid = this->getHardwareId(); + this->server->forEachClient([&](const shared_ptr& client) { + if(client->getExternalType() != CLIENT_TEAMSPEAK) return; + if(client->getUid() == this->getUid()) + clones_uid++; + if(client->getPeerIp() == this->getPeerIp()) + clones_ip++; + if(client->getHardwareId() == _own_hwid) + clones_hwid++; + }); - if(permissions[permission::i_client_max_clones_uid] > 0 && clones_uid >= permissions[permission::i_client_max_clones_uid]) { - logMessage(this->getServerId(), "{} Disconnecting because there are already {} uid clones connected. (Allowed: {})", CLIENT_STR_LOG_PREFIX, clones_uid, permissions[permission::i_client_max_clones_uid]); - return {findError("client_too_many_clones_connected"), "too many clones connected (uid)"}; - } + if(permissions[permission::i_client_max_clones_uid] > 0 && clones_uid >= permissions[permission::i_client_max_clones_uid]) { + logMessage(this->getServerId(), "{} Disconnecting because there are already {} uid clones connected. (Allowed: {})", CLIENT_STR_LOG_PREFIX, clones_uid, permissions[permission::i_client_max_clones_uid]); + return {findError("client_too_many_clones_connected"), "too many clones connected (uid)"}; + } - if(permissions[permission::i_client_max_clones_ip] > 0 && clones_ip >= permissions[permission::i_client_max_clones_ip]) { - logMessage(this->getServerId(), "{} Disconnecting because there are already {} ip clones connected. (Allowed: {})", CLIENT_STR_LOG_PREFIX, clones_ip, permissions[permission::i_client_max_clones_ip]); - return {findError("client_too_many_clones_connected"), "too many clones connected (ip)"}; - } + if(permissions[permission::i_client_max_clones_ip] > 0 && clones_ip >= permissions[permission::i_client_max_clones_ip]) { + logMessage(this->getServerId(), "{} Disconnecting because there are already {} ip clones connected. (Allowed: {})", CLIENT_STR_LOG_PREFIX, clones_ip, permissions[permission::i_client_max_clones_ip]); + return {findError("client_too_many_clones_connected"), "too many clones connected (ip)"}; + } - if(permissions[permission::i_client_max_clones_hwid] > 0 && clones_hwid >= permissions[permission::i_client_max_clones_hwid] && !_own_hwid.empty()) { - logMessage(this->getServerId(), "{} Disconnecting because there are already {} hwid clones connected. (Allowed: {})", CLIENT_STR_LOG_PREFIX, clones_hwid, permissions[permission::i_client_max_clones_hwid]); - return {findError("client_too_many_clones_connected"), "too many clones connected (hwid)"}; - } - TIMING_STEP(timings, "max clones "); + if(permissions[permission::i_client_max_clones_hwid] > 0 && clones_hwid >= permissions[permission::i_client_max_clones_hwid] && !_own_hwid.empty()) { + logMessage(this->getServerId(), "{} Disconnecting because there are already {} hwid clones connected. (Allowed: {})", CLIENT_STR_LOG_PREFIX, clones_hwid, permissions[permission::i_client_max_clones_hwid]); + return {findError("client_too_many_clones_connected"), "too many clones connected (hwid)"}; + } + TIMING_STEP(timings, "max clones "); - auto banEntry = this->resolveActiveBan(this->getPeerIp()); - if(banEntry) { - logMessage(this->getServerId(), "{} Disconnecting while init because of ban record. Record id {} at server {}", - CLIENT_STR_LOG_PREFIX, - banEntry->banId, - banEntry->serverId); - serverInstance->banManager()->trigger_ban(banEntry, this->getServerId(), this->getUid(), this->getHardwareId(), this->getDisplayName(), this->getPeerIp()); + auto banEntry = this->resolveActiveBan(this->getPeerIp()); + if(banEntry) { + logMessage(this->getServerId(), "{} Disconnecting while init because of ban record. Record id {} at server {}", + CLIENT_STR_LOG_PREFIX, + banEntry->banId, + banEntry->serverId); + serverInstance->banManager()->trigger_ban(banEntry, this->getServerId(), this->getUid(), this->getHardwareId(), this->getDisplayName(), this->getPeerIp()); - string fullReason = string() + "You are banned " + (banEntry->serverId == 0 ? "globally" : "from this server") + ". Reason: \"" + banEntry->reason + "\". Ban expires "; + string fullReason = string() + "You are banned " + (banEntry->serverId == 0 ? "globally" : "from this server") + ". Reason: \"" + banEntry->reason + "\". Ban expires "; - string time; - if(banEntry->until.time_since_epoch().count() != 0){ - time += "in "; - auto seconds = chrono::ceil(banEntry->until - chrono::system_clock::now()).count(); - tm p{}; - memset(&p, 0, sizeof(p)); + string time; + if(banEntry->until.time_since_epoch().count() != 0){ + time += "in "; + auto seconds = chrono::ceil(banEntry->until - chrono::system_clock::now()).count(); + tm p{}; + memset(&p, 0, sizeof(p)); - while(seconds >= 365 * 24 * 60 * 60){ - p.tm_year++; - seconds -= 365 * 24 * 60 * 60; - } - while(seconds >= 24 * 60 * 60){ - p.tm_yday++; - seconds -= 24 * 60 * 60; - } - while(seconds >= 60 * 60){ - p.tm_hour++; - seconds -= 60 * 60; - } - while(seconds >= 60){ - p.tm_min++; - seconds -= 60; - } - p.tm_sec = (int) seconds; + while(seconds >= 365 * 24 * 60 * 60){ + p.tm_year++; + seconds -= 365 * 24 * 60 * 60; + } + while(seconds >= 24 * 60 * 60){ + p.tm_yday++; + seconds -= 24 * 60 * 60; + } + while(seconds >= 60 * 60){ + p.tm_hour++; + seconds -= 60 * 60; + } + while(seconds >= 60){ + p.tm_min++; + seconds -= 60; + } + p.tm_sec = (int) seconds; - if(p.tm_year > 0) - time += to_string(p.tm_year) + " years, "; - if(p.tm_yday > 0) - time += to_string(p.tm_yday) + " days, "; - if(p.tm_hour > 0) - time += to_string(p.tm_hour) + " hours, "; - if(p.tm_min > 0) - time += to_string(p.tm_min) + " minutes, "; - if(p.tm_sec > 0) - time += to_string(p.tm_sec) + " seconds, "; - if(time.empty()) time = "now, "; - time = time.substr(0, time.length() - 2); - } else time = "never"; - fullReason += time + "!"; + if(p.tm_year > 0) + time += to_string(p.tm_year) + " years, "; + if(p.tm_yday > 0) + time += to_string(p.tm_yday) + " days, "; + if(p.tm_hour > 0) + time += to_string(p.tm_hour) + " hours, "; + if(p.tm_min > 0) + time += to_string(p.tm_min) + " minutes, "; + if(p.tm_sec > 0) + time += to_string(p.tm_sec) + " seconds, "; + if(time.empty()) time = "now, "; + time = time.substr(0, time.length() - 2); + } else time = "never"; + fullReason += time + "!"; - return {findError(0xD01), fullReason}; - } - TIMING_STEP(timings, "ban resolve"); + return {findError(0xD01), fullReason}; + } + TIMING_STEP(timings, "ban resolve"); - size_t count = 0; - { - for(const auto &cl : this->server->getClients()) - if((cl->getType() == CLIENT_TEAMSPEAK || cl->getType() == CLIENT_WEB || cl->getType() == CLIENT_TEASPEAK || cl->getType() == CLIENT_MUSIC)) - if(cl->connectionState() <= ConnectionState::CONNECTED && cl->connectionState() >= ConnectionState::INIT_HIGH) - count++; - } + size_t count = 0; + { + for(const auto &cl : this->server->getClients()) + if((cl->getType() == CLIENT_TEAMSPEAK || cl->getType() == CLIENT_WEB || cl->getType() == CLIENT_TEASPEAK || cl->getType() == CLIENT_MUSIC)) + if(cl->connectionState() <= ConnectionState::CONNECTED && cl->connectionState() >= ConnectionState::INIT_HIGH) + count++; + } - auto maxClients = this->server->properties()[property::VIRTUALSERVER_MAXCLIENTS].as(); - auto reserved = this->server->properties()[property::VIRTUALSERVER_RESERVED_SLOTS].as(); + auto maxClients = this->server->properties()[property::VIRTUALSERVER_MAXCLIENTS].as(); + auto reserved = this->server->properties()[property::VIRTUALSERVER_RESERVED_SLOTS].as(); - bool allowReserved = permissions[permission::b_client_use_reserved_slot] != permNotGranted && permissions[permission::b_client_use_reserved_slot] != 0; - if(reserved > maxClients){ - if(!allowReserved) return {findError("server_maxclients_reached"), "server max clients reached"}; - } else if(maxClients - (allowReserved ? 0 : reserved) <= count) - return {findError("server_maxclients_reached"), "server max clients reached"}; - TIMING_STEP(timings, "max clients"); + bool allowReserved = permissions[permission::b_client_use_reserved_slot] != permNotGranted && permissions[permission::b_client_use_reserved_slot] != 0; + if(reserved > maxClients){ + if(!allowReserved) return {findError("server_maxclients_reached"), "server max clients reached"}; + } else if(maxClients - (allowReserved ? 0 : reserved) <= count) + return {findError("server_maxclients_reached"), "server max clients reached"}; + TIMING_STEP(timings, "max clients"); - auto old_last_connected = this->properties()[property::CLIENT_LASTCONNECTED].as(); - this->properties()[property::CONNECTION_CLIENT_IP] = this->getLoggingPeerIp(); - this->properties()[property::CLIENT_LASTCONNECTED] = std::chrono::duration_cast(std::chrono::system_clock::now().time_since_epoch()).count(); - this->properties()[property::CLIENT_TOTALCONNECTIONS]++; - { - auto time_point = system_clock::time_point() + seconds(old_last_connected); - if(time_point < build::version()->timestamp) { - logMessage(this->getServerId(), "{} Client may cached a old permission list (Server is newer than the client's last join)", CLIENT_STR_LOG_PREFIX); - TIMING_STEP(timings, "pre dummy p"); - Command _dummy("dummy_permissionslist"); - this->handleCommandPermissionList(_dummy); - TIMING_STEP(timings, "pst dummy p"); - } - } + auto old_last_connected = this->properties()[property::CLIENT_LASTCONNECTED].as(); + this->properties()[property::CONNECTION_CLIENT_IP] = this->getLoggingPeerIp(); + this->properties()[property::CLIENT_LASTCONNECTED] = std::chrono::duration_cast(std::chrono::system_clock::now().time_since_epoch()).count(); + this->properties()[property::CLIENT_TOTALCONNECTIONS]++; + { + auto time_point = system_clock::time_point() + seconds(old_last_connected); + if(time_point < build::version()->timestamp) { + logMessage(this->getServerId(), "{} Client may cached a old permission list (Server is newer than the client's last join)", CLIENT_STR_LOG_PREFIX); + TIMING_STEP(timings, "pre dummy p"); + Command _dummy("dummy_permissionslist"); + this->handleCommandPermissionList(_dummy); + TIMING_STEP(timings, "pst dummy p"); + } + } - this->postCommandHandler.emplace_back([&](){ - auto self = dynamic_pointer_cast(_this.lock()); - std::thread([self](){ - threads::MutexLock l1(self->disconnectLock); - if(self->state != ConnectionState::INIT_HIGH) return; - try { - self->processJoin(); - } catch (std::exception& ex) { - logError(self->getServerId(), "Failed to proceed client join for {}. Got exception with message {}", CLIENT_STR_LOG_PREFIX_(self), ex.what()); - self->closeConnection(); - } - }).detach(); - }); + this->postCommandHandler.emplace_back([&](){ + auto self = dynamic_pointer_cast(_this.lock()); + std::thread([self](){ + threads::MutexLock l1(self->disconnectLock); + if(self->state != ConnectionState::INIT_HIGH) return; + try { + self->processJoin(); + } catch (std::exception& ex) { + logError(self->getServerId(), "Failed to proceed client join for {}. Got exception with message {}", CLIENT_STR_LOG_PREFIX_(self), ex.what()); + self->closeConnection(); + } + }).detach(); + }); - debugMessage(this->getServerId(), "{} Client init timings: {}", CLIENT_STR_LOG_PREFIX, TIMING_FINISH(timings)); - return CommandResult::Success; + debugMessage(this->getServerId(), "{} Client init timings: {}", CLIENT_STR_LOG_PREFIX, TIMING_FINISH(timings)); + return CommandResult::Success; } /* must be triggered while helding an execute lock */ //Note: Client permissions are may not really void SpeakingClient::processJoin() { - TIMING_START(timings); - auto ref_server = this->server; + TIMING_START(timings); + auto ref_server = this->server; - this->resetIdleTime(); - threads::MutexLock lock(this->command_lock); //Don't process any commands! + this->resetIdleTime(); + threads::MutexLock lock(this->command_lock); //Don't process any commands! - if(this->state != ConnectionState::INIT_HIGH) { - logError(this->getServerId(), "{} Invalid processJoin() connection state!", CLIENT_STR_LOG_PREFIX); - return; - } + if(this->state != ConnectionState::INIT_HIGH) { + logError(this->getServerId(), "{} Invalid processJoin() connection state!", CLIENT_STR_LOG_PREFIX); + return; + } - TIMING_STEP(timings, "setup "); - ref_server->registerClient(_this.lock()); - TIMING_STEP(timings, "server reg "); - ref_server->getGroupManager()->cleanupAssignments(this->getClientDatabaseId()); - TIMING_STEP(timings, "grp cleanup"); - ref_server->getGroupManager()->update_server_group_property(_this.lock(), true, nullptr); - TIMING_STEP(timings, "grp apply "); + TIMING_STEP(timings, "setup "); + ref_server->registerClient(_this.lock()); + TIMING_STEP(timings, "server reg "); + ref_server->getGroupManager()->cleanupAssignments(this->getClientDatabaseId()); + TIMING_STEP(timings, "grp cleanup"); + ref_server->getGroupManager()->update_server_group_property(_this.lock(), true, nullptr); + TIMING_STEP(timings, "grp apply "); - this->properties()[property::CLIENT_COUNTRY] = config::geo::countryFlag; - if(geoloc::provider) { - auto loc = this->isAddressV4() ? geoloc::provider->resolveInfoV4(this->getPeerIp(), false) : geoloc::provider->resolveInfoV6(this->getPeerIp(), false); - if(loc) { - debugMessage(this->getServerId(), "Client " + this->getDisplayName() + "|" + this->getLoggingPeerIp() + " comes from " + loc->name + "|" + loc->identifier); - this->properties()[property::CLIENT_COUNTRY] = loc->identifier; - } else { - logError(ref_server ? ref_server->getServerId() : 0, "Could not resolve country for ip " + this->getLoggingPeerIp() + "|" + this->getDisplayName()); - } - } - //this->updateChannelClientProperties(false); /* will be already updated via assignChannel */ - if(ref_server->properties()[property::VIRTUALSERVER_HOSTMESSAGE_MODE].as() == 3 && !ref_server->properties()[property::VIRTUALSERVER_HOSTMESSAGE].as().empty()) { - auto weak = this->_this; - threads::Thread([weak](){ - threads::self::sleep_for(milliseconds(2000)); - auto client = weak.lock(); - if(!client || !client->server) return; + this->properties()[property::CLIENT_COUNTRY] = config::geo::countryFlag; + if(geoloc::provider) { + auto loc = this->isAddressV4() ? geoloc::provider->resolveInfoV4(this->getPeerIp(), false) : geoloc::provider->resolveInfoV6(this->getPeerIp(), false); + if(loc) { + debugMessage(this->getServerId(), "Client " + this->getDisplayName() + "|" + this->getLoggingPeerIp() + " comes from " + loc->name + "|" + loc->identifier); + this->properties()[property::CLIENT_COUNTRY] = loc->identifier; + } else { + logError(ref_server ? ref_server->getServerId() : 0, "Could not resolve country for ip " + this->getLoggingPeerIp() + "|" + this->getDisplayName()); + } + } + //this->updateChannelClientProperties(false); /* will be already updated via assignChannel */ + if(ref_server->properties()[property::VIRTUALSERVER_HOSTMESSAGE_MODE].as() == 3 && !ref_server->properties()[property::VIRTUALSERVER_HOSTMESSAGE].as().empty()) { + auto weak = this->_this; + threads::Thread([weak](){ + threads::self::sleep_for(milliseconds(2000)); + auto client = weak.lock(); + if(!client || !client->server) return; - client->disconnect(client->server->properties()[property::VIRTUALSERVER_HOSTMESSAGE].as()); - }).detach(); - } + client->disconnect(client->server->properties()[property::VIRTUALSERVER_HOSTMESSAGE].as()); + }).detach(); + } - TIMING_STEP(timings, "ip 2 loc as"); //IP to location assignment - this->sendServerInit(); - debugMessage(this->getServerId(), "Client id: " + to_string(this->getClientId())); - TIMING_STEP(timings, "notify sini"); + TIMING_STEP(timings, "ip 2 loc as"); //IP to location assignment + this->sendServerInit(); + debugMessage(this->getServerId(), "Client id: " + to_string(this->getClientId())); + TIMING_STEP(timings, "notify sini"); - if(!ref_server->assignDefaultChannel(this->ref(), false)) { - this->notifyError({findError("vs_critical"), "Could not assign default channel!"}); - this->closeConnection(system_clock::now() + seconds(1)); - return; - } - TIMING_STEP(timings, "assign chan"); + if(!ref_server->assignDefaultChannel(this->ref(), false)) { + this->notifyError({findError("vs_critical"), "Could not assign default channel!"}); + this->closeConnection(system_clock::now() + seconds(1)); + return; + } + TIMING_STEP(timings, "assign chan"); - this->sendChannelList(true); - this->state = ConnectionState::CONNECTED; - TIMING_STEP(timings, "send chan t"); + this->sendChannelList(true); + this->state = ConnectionState::CONNECTED; + TIMING_STEP(timings, "send chan t"); - /* trick the join method */ - auto channel = this->currentChannel; - this->currentChannel = nullptr; - { - /* enforce an update of these properties */ - this->properties()[property::CLIENT_CHANNEL_GROUP_INHERITED_CHANNEL_ID] = "0"; - this->properties()[property::CLIENT_CHANNEL_GROUP_ID] = "0"; - this->properties()[property::CLIENT_TALK_POWER] = "0"; + /* trick the join method */ + auto channel = this->currentChannel; + this->currentChannel = nullptr; + { + /* enforce an update of these properties */ + this->properties()[property::CLIENT_CHANNEL_GROUP_INHERITED_CHANNEL_ID] = "0"; + this->properties()[property::CLIENT_CHANNEL_GROUP_ID] = "0"; + this->properties()[property::CLIENT_TALK_POWER] = "0"; - unique_lock server_channel_lock(this->server->channel_tree_lock); - this->server->client_move(this->ref(), channel, nullptr, "", ViewReasonId::VREASON_USER_ACTION, false, server_channel_lock); - this->subscribeChannel({this->currentChannel}, false, true); - } - TIMING_STEP(timings, "join move "); + unique_lock server_channel_lock(this->server->channel_tree_lock); + this->server->client_move(this->ref(), channel, nullptr, "", ViewReasonId::VREASON_USER_ACTION, false, server_channel_lock); + this->subscribeChannel({this->currentChannel}, false, true); + } + TIMING_STEP(timings, "join move "); - this->properties()->triggerAllModified(); - this->notifyServerGroupList(); - this->notifyChannelGroupList(); - TIMING_STEP(timings, "notify grou"); - logMessage(this->getServerId(), "Voice client {}/{} ({}) from {} joined.", - this->getClientDatabaseId(), - this->getUid(), - this->getDisplayName(), - this->getLoggingPeerIp() + ":" + to_string(this->getPeerPort()) - ); + this->properties()->triggerAllModified(); + this->notifyServerGroupList(); + this->notifyChannelGroupList(); + TIMING_STEP(timings, "notify grou"); + logMessage(this->getServerId(), "Voice client {}/{} ({}) from {} joined.", + this->getClientDatabaseId(), + this->getUid(), + this->getDisplayName(), + this->getLoggingPeerIp() + ":" + to_string(this->getPeerPort()) + ); - this->connectTimestamp = chrono::system_clock::now(); - this->idleTimestamp = chrono::system_clock::now(); + this->connectTimestamp = chrono::system_clock::now(); + this->idleTimestamp = chrono::system_clock::now(); - debugMessage(this->getServerId(), "{} Client join timings: {}", CLIENT_STR_LOG_PREFIX, TIMING_FINISH(timings)); + debugMessage(this->getServerId(), "{} Client join timings: {}", CLIENT_STR_LOG_PREFIX, TIMING_FINISH(timings)); } void SpeakingClient::processLeave() { - auto ownLock = _this.lock(); - auto server = this->getServer(); - if(server){ - logMessage(this->getServerId(), "Voice client {}/{} ({}) from {} left.", this->getClientDatabaseId(), this->getUid(), this->getDisplayName(), this->getLoggingPeerIp() + ":" + to_string(this->getPeerPort())); - { - unique_lock server_channel_lock(this->server->channel_tree_lock); - server->unregisterClient(ownLock, "disconnected", server_channel_lock); /* already moves client to void if needed */ - } - server->groups->disableCache(ownLock->getClientDatabaseId()); - server->musicManager->cleanup_client_bots(this->getClientDatabaseId()); - //ref_server = nullptr; Removed caused nullptr exceptions - } - { //Delete own viewing clients - /* - * No need, are only weak references! - threads::MutexLock l(this->viewLock); - this->visibleClients.clear(); - this->mutedClients.clear(); - */ - } + auto ownLock = _this.lock(); + auto server = this->getServer(); + if(server){ + logMessage(this->getServerId(), "Voice client {}/{} ({}) from {} left.", this->getClientDatabaseId(), this->getUid(), this->getDisplayName(), this->getLoggingPeerIp() + ":" + to_string(this->getPeerPort())); + { + unique_lock server_channel_lock(this->server->channel_tree_lock); + server->unregisterClient(ownLock, "disconnected", server_channel_lock); /* already moves client to void if needed */ + } + server->groups->disableCache(ownLock->getClientDatabaseId()); + server->musicManager->cleanup_client_bots(this->getClientDatabaseId()); + //ref_server = nullptr; Removed caused nullptr exceptions + } + { //Delete own viewing clients + /* + * No need, are only weak references! + threads::MutexLock l(this->viewLock); + this->visibleClients.clear(); + this->mutedClients.clear(); + */ + } } void SpeakingClient::triggerVoiceEnd() { - this->properties()[property::CLIENT_FLAG_TALKING] = false; + this->properties()[property::CLIENT_FLAG_TALKING] = false; } void SpeakingClient::updateSpeak(bool onlyUpdate, const std::chrono::system_clock::time_point &now) { - threads::MutexLock lock(this->speak_lock); - if(this->speak_last_packet + this->speak_accuracy < now) { - if(this->speak_last_packet > this->speak_begin) { - if(!this->properties()[property::CLIENT_FLAG_TALKING].as()) - this->properties()[property::CLIENT_FLAG_TALKING] = true; - this->speak_time += duration_cast(this->speak_last_packet - this->speak_begin); - } else { - if(this->properties()[property::CLIENT_FLAG_TALKING].as()) - this->properties()[property::CLIENT_FLAG_TALKING] = false; - } + threads::MutexLock lock(this->speak_lock); + if(this->speak_last_packet + this->speak_accuracy < now) { + if(this->speak_last_packet > this->speak_begin) { + if(!this->properties()[property::CLIENT_FLAG_TALKING].as()) + this->properties()[property::CLIENT_FLAG_TALKING] = true; + this->speak_time += duration_cast(this->speak_last_packet - this->speak_begin); + } else { + if(this->properties()[property::CLIENT_FLAG_TALKING].as()) + this->properties()[property::CLIENT_FLAG_TALKING] = false; + } - this->speak_begin = now; - this->speak_last_packet = now; - } - if(!onlyUpdate) - this->speak_last_packet = now; + this->speak_begin = now; + this->speak_last_packet = now; + } + if(!onlyUpdate) + this->speak_last_packet = now; } void SpeakingClient::tick(const std::chrono::system_clock::time_point &time) { - ConnectedClient::tick(time); - ALARM_TIMER(A1, "SpeakingClient::tick", milliseconds(2)); - this->updateSpeak(true, time); + ConnectedClient::tick(time); + ALARM_TIMER(A1, "SpeakingClient::tick", milliseconds(2)); + this->updateSpeak(true, time); - if(this->state == ConnectionState::CONNECTED) { - if(this->max_idle_time.has_value) { - auto max_idle = this->max_idle_time.value; - if(max_idle > 0 && this->idleTimestamp.time_since_epoch().count() > 0 && duration_cast(time - this->idleTimestamp).count() > max_idle) { - this->server->notify_client_kick(this->ref(), this->server->getServerRoot(), ts::config::messages::idle_time_exceeded, nullptr); - this->closeConnection(system_clock::now() + seconds(1)); - } - } + if(this->state == ConnectionState::CONNECTED) { + if(this->max_idle_time.has_value) { + auto max_idle = this->max_idle_time.value; + if(max_idle > 0 && this->idleTimestamp.time_since_epoch().count() > 0 && duration_cast(time - this->idleTimestamp).count() > max_idle) { + this->server->notify_client_kick(this->ref(), this->server->getServerRoot(), ts::config::messages::idle_time_exceeded, nullptr); + this->closeConnection(system_clock::now() + seconds(1)); + } + } - } + } } void SpeakingClient::updateChannelClientProperties(bool channel_lock, bool notify) { - ConnectedClient::updateChannelClientProperties(channel_lock, notify); - this->max_idle_time = this->permissionValueFlagged(permission::i_client_max_idletime, this->currentChannel); + ConnectedClient::updateChannelClientProperties(channel_lock, notify); + this->max_idle_time = this->permissionValueFlagged(permission::i_client_max_idletime, this->currentChannel); } CommandResult SpeakingClient::handleCommand(Command &command) { - if(this->connectionState() == ConnectionState::INIT_HIGH) { - if(this->handshake.state == HandshakeState::BEGIN || this->handshake.state == HandshakeState::IDENTITY_PROOF) { - CommandResult result; - if(command.command() == "handshakebegin") - result = this->handleCommandHandshakeBegin(command); - else if(command.command() == "handshakeindentityproof") - result = this->handleCommandHandshakeIdentityProof(command); - else - result = {findError("client_not_logged_in")}; + if(this->connectionState() == ConnectionState::INIT_HIGH) { + if(this->handshake.state == HandshakeState::BEGIN || this->handshake.state == HandshakeState::IDENTITY_PROOF) { + CommandResult result; + if(command.command() == "handshakebegin") + result = this->handleCommandHandshakeBegin(command); + else if(command.command() == "handshakeindentityproof") + result = this->handleCommandHandshakeIdentityProof(command); + else + result = {findError("client_not_logged_in")}; - if(!result) - this->closeConnection(system_clock::now() + seconds(1)); - return result; - } - } - return ConnectedClient::handleCommand(command); + if(!result) + this->closeConnection(system_clock::now() + seconds(1)); + return result; + } + } + return ConnectedClient::handleCommand(command); } diff --git a/server/src/client/SpeakingClient.h b/server/src/client/SpeakingClient.h index 6dcee7b..e9701d0 100644 --- a/server/src/client/SpeakingClient.h +++ b/server/src/client/SpeakingClient.h @@ -8,90 +8,90 @@ namespace ts { class TSServer; class SpeakingClient : public ConnectedClient { public: - struct VoicePacketFlags { - bool encrypted : 1; - bool head : 1; - bool fragmented : 1; /* used by MONO. IDK What this is */ - bool new_protocol : 1; - char _unused : 4; + struct VoicePacketFlags { + bool encrypted : 1; + bool head : 1; + bool fragmented : 1; /* used by MONO. IDK What this is */ + bool new_protocol : 1; + char _unused : 4; - VoicePacketFlags() : encrypted{false}, head{false}, fragmented{false}, new_protocol{false}, _unused{0} { } - }; - static_assert(sizeof(VoicePacketFlags) == 1); + VoicePacketFlags() : encrypted{false}, head{false}, fragmented{false}, new_protocol{false}, _unused{0} { } + }; + static_assert(sizeof(VoicePacketFlags) == 1); - enum HandshakeState { - BEGIN, - IDENTITY_PROOF, - SUCCEEDED - }; - enum IdentityType : uint8_t { - TEASPEAK_FORUM, - TEAMSPEAK, - NICKNAME, + enum HandshakeState { + BEGIN, + IDENTITY_PROOF, + SUCCEEDED + }; + enum IdentityType : uint8_t { + TEASPEAK_FORUM, + TEAMSPEAK, + NICKNAME, - UNSET = 0xff - }; + UNSET = 0xff + }; SpeakingClient(sql::SqlManager* a, const std::shared_ptr& b) : ConnectedClient(a, b) { - speak_begin = std::chrono::system_clock::now(); - speak_last_packet = std::chrono::system_clock::now(); + speak_begin = std::chrono::system_clock::now(); + speak_last_packet = std::chrono::system_clock::now(); }; ~SpeakingClient() override = default; - //Voice - virtual void send_voice_packet(const pipes::buffer_view& /* voice packet data */, const VoicePacketFlags& /* flags */) = 0; - virtual bool shouldReceiveVoice(const std::shared_ptr &sender); + //Voice + virtual void send_voice_packet(const pipes::buffer_view& /* voice packet data */, const VoicePacketFlags& /* flags */) = 0; + virtual bool shouldReceiveVoice(const std::shared_ptr &sender); - //Whisper - bool shouldReceiveVoiceWhisper(const std::shared_ptr &sender); - virtual void send_voice_whisper_packet(const pipes::buffer_view& /* voice packet data */, const VoicePacketFlags& /* flags */) = 0; + //Whisper + bool shouldReceiveVoiceWhisper(const std::shared_ptr &sender); + virtual void send_voice_whisper_packet(const pipes::buffer_view& /* voice packet data */, const VoicePacketFlags& /* flags */) = 0; - inline std::chrono::milliseconds takeSpokenTime() { - auto time = this->speak_time; - this->speak_time = std::chrono::milliseconds(0); - return time; - } - protected: - void tick(const std::chrono::system_clock::time_point &time) override; + inline std::chrono::milliseconds takeSpokenTime() { + auto time = this->speak_time; + this->speak_time = std::chrono::milliseconds(0); + return time; + } + protected: + void tick(const std::chrono::system_clock::time_point &time) override; - protected: - public: - void updateChannelClientProperties(bool channel_lock, bool notify) override; + protected: + public: + void updateChannelClientProperties(bool channel_lock, bool notify) override; - protected: - CommandResult handleCommand(Command &command) override; + protected: + CommandResult handleCommand(Command &command) override; - public: - void handlePacketVoice(const pipes::buffer_view&, bool head, bool fragmented); - virtual void handlePacketVoiceWhisper(const pipes::buffer_view&, bool /* new */); + public: + void handlePacketVoice(const pipes::buffer_view&, bool head, bool fragmented); + virtual void handlePacketVoiceWhisper(const pipes::buffer_view&, bool /* new */); - void processJoin(); - void processLeave(); + void processJoin(); + void processLeave(); - virtual CommandResult handleCommandHandshakeBegin(Command&); - virtual CommandResult handleCommandHandshakeIdentityProof(Command &); - virtual CommandResult handleCommandClientInit(Command&); + virtual CommandResult handleCommandHandshakeBegin(Command&); + virtual CommandResult handleCommandHandshakeIdentityProof(Command &); + virtual CommandResult handleCommandClientInit(Command&); - void triggerVoiceEnd(); - inline void updateSpeak(bool onlyUpdate, const std::chrono::system_clock::time_point &time); - std::chrono::milliseconds speak_accuracy = std::chrono::seconds{1}; + void triggerVoiceEnd(); + inline void updateSpeak(bool onlyUpdate, const std::chrono::system_clock::time_point &time); + std::chrono::milliseconds speak_accuracy = std::chrono::seconds{1}; - threads::Mutex speak_lock; - std::chrono::milliseconds speak_time = std::chrono::milliseconds{0}; - std::chrono::system_clock::time_point speak_begin; - std::chrono::system_clock::time_point speak_last_packet; + threads::Mutex speak_lock; + std::chrono::milliseconds speak_time = std::chrono::milliseconds{0}; + std::chrono::system_clock::time_point speak_begin; + std::chrono::system_clock::time_point speak_last_packet; - permission::v2::PermissionFlaggedValue max_idle_time{permission::v2::empty_permission_flagged_value}; - struct { - HandshakeState state{HandshakeState::BEGIN}; + permission::v2::PermissionFlaggedValue max_idle_time{permission::v2::empty_permission_flagged_value}; + struct { + HandshakeState state{HandshakeState::BEGIN}; - IdentityType identityType{IdentityType::UNSET}; - std::string proof_message; - //TeamSpeak - std::shared_ptr identityKey; - //TeaSpeak - std::shared_ptr identityData; - } handshake; + IdentityType identityType{IdentityType::UNSET}; + std::string proof_message; + //TeamSpeak + std::shared_ptr identityKey; + //TeaSpeak + std::shared_ptr identityData; + } handshake; }; } } \ No newline at end of file diff --git a/server/src/client/SpeakingClientHandshake.cpp b/server/src/client/SpeakingClientHandshake.cpp index 2e645cb..5c633dd 100644 --- a/server/src/client/SpeakingClientHandshake.cpp +++ b/server/src/client/SpeakingClientHandshake.cpp @@ -8,7 +8,7 @@ #include #if defined(TCP_CORK) && !defined(TCP_NOPUSH) - #define TCP_NOPUSH TCP_CORK + #define TCP_NOPUSH TCP_CORK #endif using namespace std; @@ -18,136 +18,136 @@ using namespace ts::server; using namespace ts::protocol; void free_ecc(ecc_key* key) { - if(!key) return; - ecc_free(key); - delete key; + if(!key) return; + ecc_free(key); + delete key; } CommandResult SpeakingClient::handleCommandHandshakeBegin(Command& cmd) { //If !result than the connection will be closed! - if(this->handshake.state != HandshakeState::BEGIN) - return {findError("web_handshake_invalid"), "invalid connection state!"}; + if(this->handshake.state != HandshakeState::BEGIN) + return {findError("web_handshake_invalid"), "invalid connection state!"}; - auto intention = cmd["intention"].as(); - if(intention != 0) - return {findError("web_handshake_unsupported"), ""}; + auto intention = cmd["intention"].as(); + if(intention != 0) + return {findError("web_handshake_unsupported"), ""}; - auto authenticationMethod = cmd["authentication_method"].as(); - if(authenticationMethod == IdentityType::TEAMSPEAK) { - this->handshake.identityType = IdentityType::TEAMSPEAK; - - auto identity = base64::decode(cmd["publicKey"]); - this->properties()[property::CLIENT_UNIQUE_IDENTIFIER] = base64::encode(digest::sha1(cmd["publicKey"].string())); + auto authenticationMethod = cmd["authentication_method"].as(); + if(authenticationMethod == IdentityType::TEAMSPEAK) { + this->handshake.identityType = IdentityType::TEAMSPEAK; + + auto identity = base64::decode(cmd["publicKey"]); + this->properties()[property::CLIENT_UNIQUE_IDENTIFIER] = base64::encode(digest::sha1(cmd["publicKey"].string())); - this->handshake.identityKey = shared_ptr(new ecc_key{}, free_ecc); - if(ecc_import((u_char*) identity.data(), identity.length(), this->handshake.identityKey.get()) != CRYPT_OK) { - this->handshake.identityKey = nullptr; - return {findError("web_handshake_invalid"), "invalid ecc key state!"}; - } + this->handshake.identityKey = shared_ptr(new ecc_key{}, free_ecc); + if(ecc_import((u_char*) identity.data(), identity.length(), this->handshake.identityKey.get()) != CRYPT_OK) { + this->handshake.identityKey = nullptr; + return {findError("web_handshake_invalid"), "invalid ecc key state!"}; + } auto message = "TeaSpeak, made with love and coffee by WolverinDEV (#" + base64::encode(rnd_string(32)) + ")"; - this->handshake.proof_message = digest::sha256(message); + this->handshake.proof_message = digest::sha256(message); - this->sendCommand(Command("handshakeidentityproof", { + this->sendCommand(Command("handshakeidentityproof", { {"message",message}, {"digest", "SHA-256"} })); - this->handshake.state = HandshakeState::IDENTITY_PROOF; - } else if(authenticationMethod == IdentityType::TEASPEAK_FORUM) { - this->handshake.identityType = IdentityType::TEASPEAK_FORUM; - try { - this->handshake.identityData = make_shared(); - this->handshake.proof_message = cmd["data"].string(); + this->handshake.state = HandshakeState::IDENTITY_PROOF; + } else if(authenticationMethod == IdentityType::TEASPEAK_FORUM) { + this->handshake.identityType = IdentityType::TEASPEAK_FORUM; + try { + this->handshake.identityData = make_shared(); + this->handshake.proof_message = cmd["data"].string(); - std::string error{}; - Json::CharReaderBuilder rbuilder{}; - const std::unique_ptr reader(rbuilder.newCharReader()); + std::string error{}; + Json::CharReaderBuilder rbuilder{}; + const std::unique_ptr reader(rbuilder.newCharReader()); - auto& json_str = this->handshake.proof_message; - if(!reader->parse(json_str.data(), json_str.data() + json_str.size(), &*this->handshake.identityData, &error)) { - debugMessage(this->getServerId(), "[{}] Failed to parse forum account data: {}", error); - return {findError("web_handshake_invalid"), "invalid json!"}; - } + auto& json_str = this->handshake.proof_message; + if(!reader->parse(json_str.data(), json_str.data() + json_str.size(), &*this->handshake.identityData, &error)) { + debugMessage(this->getServerId(), "[{}] Failed to parse forum account data: {}", error); + return {findError("web_handshake_invalid"), "invalid json!"}; + } - auto& json_data = *this->handshake.identityData; - if(json_data["user_id"].isNull()) - return {findError("web_handshake_invalid"), "Missing json data (user_id)!"}; - if(json_data["user_name"].isNull()) - return {findError("web_handshake_invalid"), "Missing json data (user_name)!"}; - if(json_data["user_group"].isNull()) - return {findError("web_handshake_invalid"), "Missing json data (user_group)!"}; - if(json_data["user_groups"].isNull()) - return {findError("web_handshake_invalid"), "Missing json data (user_groups)!"}; - if(json_data["data_age"].isNull()) - return {findError("web_handshake_invalid"), "Missing json data (data_age)!"}; + auto& json_data = *this->handshake.identityData; + if(json_data["user_id"].isNull()) + return {findError("web_handshake_invalid"), "Missing json data (user_id)!"}; + if(json_data["user_name"].isNull()) + return {findError("web_handshake_invalid"), "Missing json data (user_name)!"}; + if(json_data["user_group"].isNull()) + return {findError("web_handshake_invalid"), "Missing json data (user_group)!"}; + if(json_data["user_groups"].isNull()) + return {findError("web_handshake_invalid"), "Missing json data (user_groups)!"}; + if(json_data["data_age"].isNull()) + return {findError("web_handshake_invalid"), "Missing json data (data_age)!"}; - //Type test - json_data["user_id"].asInt64(); + //Type test + json_data["user_id"].asInt64(); - if(json_data["data_age"].asUInt64() < duration_cast((system_clock::now() - hours(72)).time_since_epoch()).count()) - return {findError("web_handshake_invalid"), "Provided data is too old!"}; + if(json_data["data_age"].asUInt64() < duration_cast((system_clock::now() - hours(72)).time_since_epoch()).count()) + return {findError("web_handshake_invalid"), "Provided data is too old!"}; - this->properties()[property::CLIENT_UNIQUE_IDENTIFIER] = base64::encode(digest::sha1("TeaSpeak-Forum#" + json_data["user_id"].asString())); + this->properties()[property::CLIENT_UNIQUE_IDENTIFIER] = base64::encode(digest::sha1("TeaSpeak-Forum#" + json_data["user_id"].asString())); - this->properties()[property::CLIENT_TEAFORO_ID] = json_data["user_id"].asInt64(); - this->properties()[property::CLIENT_TEAFORO_NAME] = json_data["user_name"].asString(); + this->properties()[property::CLIENT_TEAFORO_ID] = json_data["user_id"].asInt64(); + this->properties()[property::CLIENT_TEAFORO_NAME] = json_data["user_name"].asString(); - { - ///* 0x01 := Banned | 0x02 := Stuff | 0x04 := Premium */ - uint64_t flags = 0; + { + ///* 0x01 := Banned | 0x02 := Stuff | 0x04 := Premium */ + uint64_t flags = 0; - if(json_data["is_banned"].isBool() && json_data["is_banned"].asBool()) - flags |= 0x01U; + if(json_data["is_banned"].isBool() && json_data["is_banned"].asBool()) + flags |= 0x01U; - if(json_data["is_staff"].isBool() && json_data["is_staff"].asBool()) - flags |= 0x02U; + if(json_data["is_staff"].isBool() && json_data["is_staff"].asBool()) + flags |= 0x02U; - if(json_data["is_premium"].isBool() && json_data["is_premium"].asBool()) - flags |= 0x04U; + if(json_data["is_premium"].isBool() && json_data["is_premium"].asBool()) + flags |= 0x04U; - this->properties()[property::CLIENT_TEAFORO_FLAGS] = flags; - } - } catch (Json::Exception& exception) { - return {findError("web_handshake_invalid"), "invalid json!"}; - } - this->sendCommand(Command("handshakeidentityproof")); - this->handshake.state = HandshakeState::IDENTITY_PROOF; - } else if(authenticationMethod == IdentityType::NICKNAME) { - if(!config::server::authentication::name) - return {findError("web_handshake_identity_unsupported"), "Name authentication has been disabled"}; + this->properties()[property::CLIENT_TEAFORO_FLAGS] = flags; + } + } catch (Json::Exception& exception) { + return {findError("web_handshake_invalid"), "invalid json!"}; + } + this->sendCommand(Command("handshakeidentityproof")); + this->handshake.state = HandshakeState::IDENTITY_PROOF; + } else if(authenticationMethod == IdentityType::NICKNAME) { + if(!config::server::authentication::name) + return {findError("web_handshake_identity_unsupported"), "Name authentication has been disabled"}; - this->handshake.state = HandshakeState::SUCCEEDED; - this->handshake.identityType = IdentityType::NICKNAME; - this->properties()[property::CLIENT_UNIQUE_IDENTIFIER] = base64::encode(digest::sha1("UserName#" + cmd["client_nickname"].string())); - } else { - return {findError("web_handshake_identity_unsupported"), ""}; - } - return CommandResult::Success; + this->handshake.state = HandshakeState::SUCCEEDED; + this->handshake.identityType = IdentityType::NICKNAME; + this->properties()[property::CLIENT_UNIQUE_IDENTIFIER] = base64::encode(digest::sha1("UserName#" + cmd["client_nickname"].string())); + } else { + return {findError("web_handshake_identity_unsupported"), ""}; + } + return CommandResult::Success; } CommandResult SpeakingClient::handleCommandHandshakeIdentityProof(Command& cmd) { - if(this->handshake.state != HandshakeState::IDENTITY_PROOF) - return {findError("web_handshake_invalid"), "invalid connection state!"}; + if(this->handshake.state != HandshakeState::IDENTITY_PROOF) + return {findError("web_handshake_invalid"), "invalid connection state!"}; - if(this->handshake.identityType == IdentityType::TEASPEAK_FORUM) { - auto encodedProof = cmd["proof"].string(); - auto proof = base64::decode(encodedProof); + if(this->handshake.identityType == IdentityType::TEASPEAK_FORUM) { + auto encodedProof = cmd["proof"].string(); + auto proof = base64::decode(encodedProof); - auto key = serverInstance->sslManager()->getRsaKey("teaforo_sign"); - if(!key) return {findError("web_handshake_identity_unsupported"), "Missing server public key!"}; - if(!serverInstance->sslManager()->verifySign(key, this->handshake.proof_message, proof)) return {findError("web_handshake_identity_proof_failed"), ""}; + auto key = serverInstance->sslManager()->getRsaKey("teaforo_sign"); + if(!key) return {findError("web_handshake_identity_unsupported"), "Missing server public key!"}; + if(!serverInstance->sslManager()->verifySign(key, this->handshake.proof_message, proof)) return {findError("web_handshake_identity_proof_failed"), ""}; - this->properties()[property::CLIENT_TEAFORO_ID] = (int64_t) (*this->handshake.identityData)["user_id"].asInt64(); - this->properties()[property::CLIENT_TEAFORO_NAME] = (*this->handshake.identityData)["user_name"].asString(); - this->handshake.state = HandshakeState::SUCCEEDED; - } else if(this->handshake.identityType == IdentityType::TEAMSPEAK) { - auto proof = base64::decode(cmd["proof"]); + this->properties()[property::CLIENT_TEAFORO_ID] = (int64_t) (*this->handshake.identityData)["user_id"].asInt64(); + this->properties()[property::CLIENT_TEAFORO_NAME] = (*this->handshake.identityData)["user_name"].asString(); + this->handshake.state = HandshakeState::SUCCEEDED; + } else if(this->handshake.identityType == IdentityType::TEAMSPEAK) { + auto proof = base64::decode(cmd["proof"]); - int result; - if(ecc_verify_hash((u_char*) proof.data(), proof.length(), (u_char*) this->handshake.proof_message.data(), this->handshake.proof_message.length(), &result, this->handshake.identityKey.get()) != CRYPT_OK) return {findError("web_handshake_identity_proof_failed"), ""}; - if(!result) return {findError("web_handshake_identity_proof_failed"), ""}; - this->handshake.state = HandshakeState::SUCCEEDED; - } else - return {findError("web_handshake_invalid"), "identity isn't required to proof authentication"}; + int result; + if(ecc_verify_hash((u_char*) proof.data(), proof.length(), (u_char*) this->handshake.proof_message.data(), this->handshake.proof_message.length(), &result, this->handshake.identityKey.get()) != CRYPT_OK) return {findError("web_handshake_identity_proof_failed"), ""}; + if(!result) return {findError("web_handshake_identity_proof_failed"), ""}; + this->handshake.state = HandshakeState::SUCCEEDED; + } else + return {findError("web_handshake_invalid"), "identity isn't required to proof authentication"}; - return CommandResult::Success; + return CommandResult::Success; } \ No newline at end of file diff --git a/server/src/client/file/FileClient.cpp b/server/src/client/file/FileClient.cpp index 17b3d82..550f945 100644 --- a/server/src/client/file/FileClient.cpp +++ b/server/src/client/file/FileClient.cpp @@ -15,228 +15,228 @@ namespace fs = std::experimental::filesystem; #define BUFFER_SIZE (size_t) 2048 FileClient::FileClient(FileServer* handle, int socketFd) : handle(handle), clientFd(socketFd) { - memtrack::allocated(this); + memtrack::allocated(this); this->last_io_action = system_clock::now(); int enabled = 1; - if(setsockopt(socketFd, IPPROTO_TCP, TCP_NODELAY, &enabled, sizeof enabled) < 0) - logError(LOG_FT, "{} Cant enable TCP no delay for socket {}. Error: {}/{}", this->client_prefix(), socketFd, errno, strerror(errno)); + if(setsockopt(socketFd, IPPROTO_TCP, TCP_NODELAY, &enabled, sizeof enabled) < 0) + logError(LOG_FT, "{} Cant enable TCP no delay for socket {}. Error: {}/{}", this->client_prefix(), socketFd, errno, strerror(errno)); this->readEvent = event_new(this->handle->ioLoop, socketFd, EV_READ|EV_PERSIST, [](int a, short b, void* c){ ((FileClient*) c)->handleMessageRead(a, b, c); }, this); this->writeEvent = event_new(this->handle->ioLoop, socketFd, EV_TIMEOUT | EV_WRITE, [](int a, short b, void* c){ ((FileClient*) c)->handleMessageWrite(a, b, c); }, this); - this->ssl_handler.direct_process(pipes::PROCESS_DIRECTION_OUT, true); - this->ssl_handler.direct_process(pipes::PROCESS_DIRECTION_IN, true); + this->ssl_handler.direct_process(pipes::PROCESS_DIRECTION_OUT, true); + this->ssl_handler.direct_process(pipes::PROCESS_DIRECTION_IN, true); this->ssl_handler.callback_write(bind(&FileClient::sendRawMessage, this, placeholders::_1)); - this->ssl_handler.callback_data(bind(&FileClient::handle_ssl_message, this, placeholders::_1)); + this->ssl_handler.callback_data(bind(&FileClient::handle_ssl_message, this, placeholders::_1)); /* this->ssl_handler.callback_data([&](const pipes::buffer_view& msg) { - if(this->ftType == FTType::TeaWeb_HTTPS) { - this->handle_http_message(msg); - } else if(this->ftType == FTType::TeaWeb_SSL_WS) { - this->ws_handler.process_incoming_data(msg); - } else { - logError(LOG_FT, "{} Decoded SSL packet, but transfer type isn't SSL!", this->client_prefix()); - } + if(this->ftType == FTType::TeaWeb_HTTPS) { + this->handle_http_message(msg); + } else if(this->ftType == FTType::TeaWeb_SSL_WS) { + this->ws_handler.process_incoming_data(msg); + } else { + logError(LOG_FT, "{} Decoded SSL packet, but transfer type isn't SSL!", this->client_prefix()); + } }); */ //FIXME init ssl error handler? - this->ws_handler.direct_process(pipes::PROCESS_DIRECTION_OUT, true); - this->ws_handler.direct_process(pipes::PROCESS_DIRECTION_IN, true); + this->ws_handler.direct_process(pipes::PROCESS_DIRECTION_OUT, true); + this->ws_handler.direct_process(pipes::PROCESS_DIRECTION_IN, true); - /* - this->ws_handler.callback_data([&](const pipes::WSMessage& message) { - if(this->state_transfere == T_INITIALIZE) { - this->applyKey(message.data.string()); //Got the key :) - } else if(this->state_transfere == T_TRANSFER) { - if(this->pendingKey->upload) - this->uploadWriteBytes(message.data.string()); - else { - logError(LOG_FT, "{} Invalid write (Just download)", this->client_prefix()); - this->disconnect(); - } - } - }); - */ - this->ws_handler.callback_data(bind(&FileClient::handle_ws_message,this, placeholders::_1)); - this->ws_handler.callback_write([&](const pipes::buffer_view& data) { - this->ssl_handler.send(data); - }); - this->ws_handler.on_connect = [&]() {}; - this->ws_handler.on_disconnect = [&](const std::string&) {}; + /* + this->ws_handler.callback_data([&](const pipes::WSMessage& message) { + if(this->state_transfere == T_INITIALIZE) { + this->applyKey(message.data.string()); //Got the key :) + } else if(this->state_transfere == T_TRANSFER) { + if(this->pendingKey->upload) + this->uploadWriteBytes(message.data.string()); + else { + logError(LOG_FT, "{} Invalid write (Just download)", this->client_prefix()); + this->disconnect(); + } + } + }); + */ + this->ws_handler.callback_data(bind(&FileClient::handle_ws_message,this, placeholders::_1)); + this->ws_handler.callback_write([&](const pipes::buffer_view& data) { + this->ssl_handler.send(data); + }); + this->ws_handler.on_connect = [&]() {}; + this->ws_handler.on_disconnect = [&](const std::string&) {}; } FileClient::~FileClient() { if(this->thread_flush.joinable()){ - this->state_connection = C_DISCONNECTED; + this->state_connection = C_DISCONNECTED; - if(this->thread_flush.get_id() != this_thread::get_id()) - this->thread_flush.join(); - else - this->thread_flush.detach(); + if(this->thread_flush.get_id() != this_thread::get_id()) + this->thread_flush.join(); + else + this->thread_flush.detach(); } - memtrack::freed(this); + memtrack::freed(this); } size_t FileClient::used_bandwidth() { - auto now = system_clock::now(); + auto now = system_clock::now(); - size_t tranfarred_bytes = 0; - auto timeout = now - seconds(1); - { - lock_guard lock(this->bandwidth_lock); - for(auto it = this->bandwidth.end(); it != this->bandwidth.begin();) { - --it; + size_t tranfarred_bytes = 0; + auto timeout = now - seconds(1); + { + lock_guard lock(this->bandwidth_lock); + for(auto it = this->bandwidth.end(); it != this->bandwidth.begin();) { + --it; - if((*it)->timestamp < timeout) { - this->bandwidth.erase(this->bandwidth.begin(), it); - break; - } - tranfarred_bytes += (*it)->length; - } - } - return tranfarred_bytes; + if((*it)->timestamp < timeout) { + this->bandwidth.erase(this->bandwidth.begin(), it); + break; + } + tranfarred_bytes += (*it)->length; + } + } + return tranfarred_bytes; } std::string FileClient::client_prefix() { - bool hide_ip = config::server::disable_ip_saving; - if(!hide_ip) { - auto client = this->client; - if(client) { - auto server = client->getServer(); - if(server) { - hide_ip = server->disable_ip_saving(); - } - } - } - std::string address = ""; - if(hide_ip) - address = "X.X.X.X:" + to_string(net::port(this->remote_address)); - else - address = net::to_string(this->remote_address); - if(this->client) return "[" + to_string(this->client->getServerId()) + "|" + address + "| " + this->client->getDisplayName() + "]"; - return "[0|" + address + "|unconnected]"; + bool hide_ip = config::server::disable_ip_saving; + if(!hide_ip) { + auto client = this->client; + if(client) { + auto server = client->getServer(); + if(server) { + hide_ip = server->disable_ip_saving(); + } + } + } + std::string address = ""; + if(hide_ip) + address = "X.X.X.X:" + to_string(net::port(this->remote_address)); + else + address = net::to_string(this->remote_address); + if(this->client) return "[" + to_string(this->client->getServerId()) + "|" + address + "| " + this->client->getDisplayName() + "]"; + return "[0|" + address + "|unconnected]"; } size_t FileClient::transferred_bytes() { - return this->bytesHandled; + return this->bytesHandled; } size_t FileClient::remaining_bytes() { - threads::MutexLock lock(this->tickLock); - if(!this->pendingKey) return 0; - return this->pendingKey->size - this->bytesHandled; + threads::MutexLock lock(this->tickLock); + if(!this->pendingKey) return 0; + return this->pendingKey->size - this->bytesHandled; } void FileClient::disconnect(std::chrono::milliseconds timeout) { - auto own_lock = _this.lock(); - if(!own_lock) return; + auto own_lock = _this.lock(); + if(!own_lock) return; - bool apply_flush = timeout.count() > 0; - debugMessage(LOG_FT, "{} Disconnecting client. Connection state: {} Flush IO: {}", this->client_prefix(), (int) this->state_connection, apply_flush); - unique_lock tick_lock(this->tickLock); + bool apply_flush = timeout.count() > 0; + debugMessage(LOG_FT, "{} Disconnecting client. Connection state: {} Flush IO: {}", this->client_prefix(), (int) this->state_connection, apply_flush); + unique_lock tick_lock(this->tickLock); - if(this->state_connection == C_DISCONNECTED) return; /* we're already disconnected */ + if(this->state_connection == C_DISCONNECTED) return; /* we're already disconnected */ - if(apply_flush && this->state_connection == C_DISCONNECTING) return; /* we're already flushing the IO */ + if(apply_flush && this->state_connection == C_DISCONNECTING) return; /* we're already flushing the IO */ - if(this->ftType == FTType::TeaWeb_SSL_WS) { - this->ws_handler.disconnect(1000, "disconnected"); - }; + if(this->ftType == FTType::TeaWeb_SSL_WS) { + this->ws_handler.disconnect(1000, "disconnected"); + }; - this->state_connection = apply_flush ? C_DISCONNECTING : C_DISCONNECTED; - if(this->readEvent) - event_del_noblock(this->readEvent); + this->state_connection = apply_flush ? C_DISCONNECTING : C_DISCONNECTED; + if(this->readEvent) + event_del_noblock(this->readEvent); - if(apply_flush){ - lock_guard flush_lock(this->thread_flush_lock); - assert(!this->thread_flush.joinable()); + if(apply_flush){ + lock_guard flush_lock(this->thread_flush_lock); + assert(!this->thread_flush.joinable()); - this->thread_flush = std::thread([own_lock, timeout]{ - auto beg = system_clock::now(); - while(own_lock->state_connection == C_DISCONNECTING && beg + timeout > system_clock::now()){ - { - lock_guard buffer_lock(own_lock->bufferLock); - if(own_lock->read_queue.empty() && own_lock->write_queue.empty()) break; - } - usleep(10 * 1000); - } - unique_lock l(own_lock->tickLock); - if(own_lock->state_connection != C_DISCONNECTING) return; - own_lock->disconnectFinal(l, true); - }); - { - auto native_handle = this->thread_flush.native_handle(); - pthread_setname_np(native_handle, "FileClient flush"); - } - } else { - unique_lock flush_lock(this->thread_flush_lock); - if(this->thread_flush.joinable()) { - flush_lock.unlock(); - this->thread_flush.join(); - flush_lock.lock(); - } - disconnectFinal(tick_lock, false); - } + this->thread_flush = std::thread([own_lock, timeout]{ + auto beg = system_clock::now(); + while(own_lock->state_connection == C_DISCONNECTING && beg + timeout > system_clock::now()){ + { + lock_guard buffer_lock(own_lock->bufferLock); + if(own_lock->read_queue.empty() && own_lock->write_queue.empty()) break; + } + usleep(10 * 1000); + } + unique_lock l(own_lock->tickLock); + if(own_lock->state_connection != C_DISCONNECTING) return; + own_lock->disconnectFinal(l, true); + }); + { + auto native_handle = this->thread_flush.native_handle(); + pthread_setname_np(native_handle, "FileClient flush"); + } + } else { + unique_lock flush_lock(this->thread_flush_lock); + if(this->thread_flush.joinable()) { + flush_lock.unlock(); + this->thread_flush.join(); + flush_lock.lock(); + } + disconnectFinal(tick_lock, false); + } } void FileClient::disconnectFinal(unique_lock& l, bool lock_flush_thread) { auto ownLock = _this.lock(); - unique_lock l1(this->bufferLock); + unique_lock l1(this->bufferLock); - this->state_connection = C_DISCONNECTED; - { - unique_lock flush_lock(this->thread_flush_lock, try_to_lock); - if(flush_lock.owns_lock() || !lock_flush_thread) { - if(this->thread_flush.joinable()) { - if(this->thread_flush.get_id() == this_thread::get_id()) - this->thread_flush.detach(); - else - this->thread_flush.join(); - } - } - } + this->state_connection = C_DISCONNECTED; + { + unique_lock flush_lock(this->thread_flush_lock, try_to_lock); + if(flush_lock.owns_lock() || !lock_flush_thread) { + if(this->thread_flush.joinable()) { + if(this->thread_flush.get_id() == this_thread::get_id()) + this->thread_flush.detach(); + else + this->thread_flush.join(); + } + } + } - if(this->readEvent){ - auto event = this->readEvent; - this->readEvent = nullptr; - l1.unlock(); - l.unlock(); - event_del_block(event); - event_free(event); - l.lock(); - l1.lock(); - } + if(this->readEvent){ + auto event = this->readEvent; + this->readEvent = nullptr; + l1.unlock(); + l.unlock(); + event_del_block(event); + event_free(event); + l.lock(); + l1.lock(); + } if(this->writeEvent){ - auto event = this->writeEvent; - this->writeEvent = nullptr; - l1.unlock(); - l.unlock(); - event_del_block(event); - event_free(event); - l.lock(); - l1.lock(); + auto event = this->writeEvent; + this->writeEvent = nullptr; + l1.unlock(); + l.unlock(); + event_del_block(event); + event_free(event); + l.lock(); + l1.lock(); } if(this->clientFd > 0) { - shutdown(this->clientFd, SHUT_RDWR); + shutdown(this->clientFd, SHUT_RDWR); close(this->clientFd); } this->clientFd = -1; - { - threads::MutexLock l2(this->handle->clientLock); - auto& clList = this->handle->connectedClients; - auto elm = find(clList.begin(), clList.end(), _this.lock()); - if(elm != clList.end()) clList.erase(elm); - else logError(LOG_FT, "{} Invalid ft client list!", this->client_prefix()); - } + { + threads::MutexLock l2(this->handle->clientLock); + auto& clList = this->handle->connectedClients; + auto elm = find(clList.begin(), clList.end(), _this.lock()); + if(elm != clList.end()) clList.erase(elm); + else logError(LOG_FT, "{} Invalid ft client list!", this->client_prefix()); + } - this->read_queue.clear(); - this->write_queue.clear(); + this->read_queue.clear(); + this->write_queue.clear(); if(this->fstream){ this->fstream->flush(); @@ -253,138 +253,138 @@ bool FileClient::tick() { if(this->state_connection == C_DISCONNECTED) return false; if(this->state_connection != C_DISCONNECTING) { - if(last_io_action.time_since_epoch().count() == 0) - last_io_action = system_clock::now(); - else if(last_io_action + minutes(1) < system_clock::now()) { - logMessage(LOG_FT, "{} Timed out after one minute of silence!", this->client_prefix()); - this->disconnect(); - return false; - } - } + if(last_io_action.time_since_epoch().count() == 0) + last_io_action = system_clock::now(); + else if(last_io_action + minutes(1) < system_clock::now()) { + logMessage(LOG_FT, "{} Timed out after one minute of silence!", this->client_prefix()); + this->disconnect(); + return false; + } + } /* decode incomming stuff */ bool flag_reexecute = false; - { - /* types which require an extra layer of decode */ - if(this->ftType == FTType::TeaWeb_SSL || this->ftType == FTType::TeaWeb_SSL_HTTP || this->ftType == FTType::TeaWeb_SSL_WS || this->ftType == FTType::TeaWeb_HTTP) { - pipes::buffer buffer; - { - lock_guard buffer_lock(this->bufferLock); - if(this->read_queue.empty()) { - flag_reexecute = false; - } else { - flag_reexecute = true; + { + /* types which require an extra layer of decode */ + if(this->ftType == FTType::TeaWeb_SSL || this->ftType == FTType::TeaWeb_SSL_HTTP || this->ftType == FTType::TeaWeb_SSL_WS || this->ftType == FTType::TeaWeb_HTTP) { + pipes::buffer buffer; + { + lock_guard buffer_lock(this->bufferLock); + if(this->read_queue.empty()) { + flag_reexecute = false; + } else { + flag_reexecute = true; - buffer = this->read_queue.front(); - this->read_queue.pop_front(); - } - } + buffer = this->read_queue.front(); + this->read_queue.pop_front(); + } + } - if(flag_reexecute) { - if(this->ftType == FTType::TeaWeb_HTTP) - this->handle_http_message(buffer); - else - this->ssl_handler.process_incoming_data(buffer); - } - } else if(this->ftType == FTType::TeamSpeak) { - flag_reexecute |= this->handle_ts_message(); - } else if(this->ftType == FTType::Unknown) { - /* we need at least 16 bytes to detect any type */ - if(this->availableBytes() >= 16) { - auto header = this->peekBytes(16); - if(header.find("GET") != -1 || header.find("POST") != -1 || header.find("OPTIONS") != -1) { - debugMessage(LOG_FT, "{} Using HTTP only!", this->client_prefix()); - this->ftType = FTType::TeaWeb_HTTP; - return true; - } else if(pipes::SSL::isSSLHeader(header) && serverInstance->sslManager()->web_ssl_options()) { - debugMessage(LOG_FT, "{} Encrypting pipe with SSL", this->client_prefix()); - this->ftType = FTType::TeaWeb_SSL; - this->ssl_handler.initialize(serverInstance->sslManager()->web_ssl_options()); - return true; - } else { - debugMessage(LOG_FT, "{} Transferring data with the TS protocol", this->client_prefix()); - this->ftType = FTType::TeamSpeak; - return true; - } - } - } else { - logError(LOG_FT, "{} Ticked client with unknown protocol type. Closing connection.", this->client_prefix()); - this->disconnect(); - return false; - } - } + if(flag_reexecute) { + if(this->ftType == FTType::TeaWeb_HTTP) + this->handle_http_message(buffer); + else + this->ssl_handler.process_incoming_data(buffer); + } + } else if(this->ftType == FTType::TeamSpeak) { + flag_reexecute |= this->handle_ts_message(); + } else if(this->ftType == FTType::Unknown) { + /* we need at least 16 bytes to detect any type */ + if(this->availableBytes() >= 16) { + auto header = this->peekBytes(16); + if(header.find("GET") != -1 || header.find("POST") != -1 || header.find("OPTIONS") != -1) { + debugMessage(LOG_FT, "{} Using HTTP only!", this->client_prefix()); + this->ftType = FTType::TeaWeb_HTTP; + return true; + } else if(pipes::SSL::isSSLHeader(header) && serverInstance->sslManager()->web_ssl_options()) { + debugMessage(LOG_FT, "{} Encrypting pipe with SSL", this->client_prefix()); + this->ftType = FTType::TeaWeb_SSL; + this->ssl_handler.initialize(serverInstance->sslManager()->web_ssl_options()); + return true; + } else { + debugMessage(LOG_FT, "{} Transferring data with the TS protocol", this->client_prefix()); + this->ftType = FTType::TeamSpeak; + return true; + } + } + } else { + logError(LOG_FT, "{} Ticked client with unknown protocol type. Closing connection.", this->client_prefix()); + this->disconnect(); + return false; + } + } - if(this->state_transfer == T_TRANSFER) { - if(this->pendingKey) { - /* test for download */ - if(!this->pendingKey->upload) { - /* lets send some data :) */ + if(this->state_transfer == T_TRANSFER) { + if(this->pendingKey) { + /* test for download */ + if(!this->pendingKey->upload) { + /* lets send some data :) */ - if(!fstream) { - logError(LOG_FT, "{} Missing file stream! Disconnecting client...", this->client_prefix()); - this->disconnect(); - return false; - } - size_t count = 0; - { - lock_guard lock(this->bufferLock); - count = this->write_queue.size(); - } + if(!fstream) { + logError(LOG_FT, "{} Missing file stream! Disconnecting client...", this->client_prefix()); + this->disconnect(); + return false; + } + size_t count = 0; + { + lock_guard lock(this->bufferLock); + count = this->write_queue.size(); + } - if(count <= (1024 * 512) / BUFFER_SIZE){ //Max buffer 500Kb - if(!fstream->good()){ - logError(LOG_FT, "{} Cant finish file download. File isn't good anymore!", this->client_prefix()); - this->disconnect(); - return false; - } + if(count <= (1024 * 512) / BUFFER_SIZE){ //Max buffer 500Kb + if(!fstream->good()){ + logError(LOG_FT, "{} Cant finish file download. File isn't good anymore!", this->client_prefix()); + this->disconnect(); + return false; + } - pipes::buffer writeBuffer(BUFFER_SIZE); - auto read = fstream->readsome((char*) writeBuffer.data_ptr(), writeBuffer.length()); - if(read < 0){ - logError(LOG_FT, "{} Invalid file read. Read {} bytes of max {}. Index {}/{}", this->client_prefix(), read, writeBuffer.length(), this->bytesHandled, this->pendingKey->size); - this->disconnect(); - return false; - } else if(read == 0){ - if(this->bytesHandled != this->pendingKey->size){ - logError(LOG_FT, "{} Invalid end of file. Expected {} bytes and attempted to read at {}", this->client_prefix(), this->pendingKey->size, this->bytesHandled); - this->disconnect(); - return false; - } - } + pipes::buffer writeBuffer(BUFFER_SIZE); + auto read = fstream->readsome((char*) writeBuffer.data_ptr(), writeBuffer.length()); + if(read < 0){ + logError(LOG_FT, "{} Invalid file read. Read {} bytes of max {}. Index {}/{}", this->client_prefix(), read, writeBuffer.length(), this->bytesHandled, this->pendingKey->size); + this->disconnect(); + return false; + } else if(read == 0){ + if(this->bytesHandled != this->pendingKey->size){ + logError(LOG_FT, "{} Invalid end of file. Expected {} bytes and attempted to read at {}", this->client_prefix(), this->pendingKey->size, this->bytesHandled); + this->disconnect(); + return false; + } + } - this->bytesHandled += read; - this->client->getConnectionStatistics()->logFileTransferOut(read); - if(read > 0){ - writeBuffer.resize(read); - this->sendMessage(writeBuffer); - flag_reexecute |= true; - } - } - } + this->bytesHandled += read; + this->client->getConnectionStatistics()->logFileTransferOut(read); + if(read > 0){ + writeBuffer.resize(read); + this->sendMessage(writeBuffer); + flag_reexecute |= true; + } + } + } - /* lets test if we're done */ - if(this->bytesHandled == this->pendingKey->size){ - auto time = duration_cast(system_clock::now() - this->connect_timestamp).count(); - logMessage(LOG_FT, "{} File transfer completed. Transferred {} bytes in {} milliseconds. Waiting for disconnect.", this->client_prefix(), this->bytesHandled, time); + /* lets test if we're done */ + if(this->bytesHandled == this->pendingKey->size){ + auto time = duration_cast(system_clock::now() - this->connect_timestamp).count(); + logMessage(LOG_FT, "{} File transfer completed. Transferred {} bytes in {} milliseconds. Waiting for disconnect.", this->client_prefix(), this->bytesHandled, time); - this->close_file_handle(); - this->pendingKey.reset(); - this->state_transfer = T_DONE; - this->finished_timestamp = system_clock::now(); + this->close_file_handle(); + this->pendingKey.reset(); + this->state_transfer = T_DONE; + this->finished_timestamp = system_clock::now(); - /* we expect TS3 to hangup the connection */ - if(this->ftType != FTType::TeamSpeak) - this->disconnect(seconds(5)); - return false; - } - } - } else if(this->state_transfer == T_DONE) { - if(this->finished_timestamp + seconds(2) < system_clock::now() && this->state_connection == C_CONNECTED) { - debugMessage(LOG_FT, "{} Disconnecting client after 2 seconds after finish!", this->client_prefix()); - this->disconnect(seconds(5)); - } - } + /* we expect TS3 to hangup the connection */ + if(this->ftType != FTType::TeamSpeak) + this->disconnect(seconds(5)); + return false; + } + } + } else if(this->state_transfer == T_DONE) { + if(this->finished_timestamp + seconds(2) < system_clock::now() && this->state_connection == C_CONNECTED) { + debugMessage(LOG_FT, "{} Disconnecting client after 2 seconds after finish!", this->client_prefix()); + this->disconnect(seconds(5)); + } + } return flag_reexecute; } @@ -405,55 +405,55 @@ bool FileClient::applyKey(const string &key) { return false; } - this->client = this->pendingKey->owner.lock(); - if(!this->client) { - logError(LOG_FT, "{} Tried connect with an invalid key (client offline)! (Key: {})", this->client_prefix(), key); - this->disconnect(); - return false; - } + this->client = this->pendingKey->owner.lock(); + if(!this->client) { + logError(LOG_FT, "{} Tried connect with an invalid key (client offline)! (Key: {})", this->client_prefix(), key); + this->disconnect(); + return false; + } debugMessage(LOG_FT, "{} Initialized file transfer for file '{}' (Type: {})", this->client_prefix(), this->pendingKey->targetFile, pendingKey->upload ? "upload" : "download"); - if(pendingKey->offset == 0 && pendingKey->upload) { - try { - fs::remove(fs::u8path(pendingKey->targetFile)); - } catch (std::exception& e) {} - } + if(pendingKey->offset == 0 && pendingKey->upload) { + try { + fs::remove(fs::u8path(pendingKey->targetFile)); + } catch (std::exception& e) {} + } fstream = new std::fstream(); fstream->open(pendingKey->targetFile, (pendingKey->upload ? ios::out : ios::in) | ios::binary | ios::app); - if(!*fstream) { - logError(LOG_FT, "{} Failed to open target file {} for {}", this->client_prefix(), this->pendingKey->targetFile, pendingKey->upload ? "upload" : "download"); - delete fstream; - this->fstream = nullptr; - return false; - } - fstream->seekg(0, std::ios::beg); + if(!*fstream) { + logError(LOG_FT, "{} Failed to open target file {} for {}", this->client_prefix(), this->pendingKey->targetFile, pendingKey->upload ? "upload" : "download"); + delete fstream; + this->fstream = nullptr; + return false; + } + fstream->seekg(0, std::ios::beg); auto fsize = fstream->tellg(); fstream->seekg(0, std::ios::end); fsize = fstream->tellg() - fsize; fstream->seekg(this->pendingKey->offset, std::ios::beg); - if(!*fstream) { - logError(LOG_FT, "{} Failed to seek within file {} for {}", this->client_prefix(), this->pendingKey->targetFile, pendingKey->upload ? "upload" : "download"); - delete fstream; - this->fstream = nullptr; - return false; - } + if(!*fstream) { + logError(LOG_FT, "{} Failed to seek within file {} for {}", this->client_prefix(), this->pendingKey->targetFile, pendingKey->upload ? "upload" : "download"); + delete fstream; + this->fstream = nullptr; + return false; + } debugMessage(LOG_FT, "{} Received local file size {}. Target size if {}", this->client_prefix(), fsize, pendingKey->size); - if(this->state_transfer == T_INITIALIZE) - this->state_transfer = T_TRANSFER; + if(this->state_transfer == T_INITIALIZE) + this->state_transfer = T_TRANSFER; - this->connect_timestamp = system_clock::now(); + this->connect_timestamp = system_clock::now(); return true; } bool FileClient::uploadWriteBytes(const pipes::buffer_view& message) { - this->client->getConnectionStatistics()->logFileTransferIn(message.length()); - if(!fstream->good()){ - logError(LOG_FT, "{} uploadWriteBytes(...) called with invalid fstream!", this->client_prefix()); - return false; - } + this->client->getConnectionStatistics()->logFileTransferIn(message.length()); + if(!fstream->good()){ + logError(LOG_FT, "{} uploadWriteBytes(...) called with invalid fstream!", this->client_prefix()); + return false; + } fstream->write(message.data_ptr(), message.length()); if(!fstream->good()){ @@ -467,252 +467,252 @@ bool FileClient::uploadWriteBytes(const pipes::buffer_view& message) { } void FileClient::close_file_handle() { - if(this->fstream) { - this->fstream->flush(); - this->fstream->close(); - delete this->fstream; - this->fstream = nullptr; - } + if(this->fstream) { + this->fstream->flush(); + this->fstream->close(); + delete this->fstream; + this->fstream = nullptr; + } } void FileClient::sendMessage(const pipes::buffer_view& message) { - switch(this->ftType) { - case FTType::TeamSpeak: - case FTType::TeaWeb_HTTP: - this->sendRawMessage(message); - break; - case FTType::TeaWeb_SSL: - case FTType::TeaWeb_SSL_HTTP: - this->ssl_handler.send(message); - break; - case FTType::TeaWeb_SSL_WS: - this->ws_handler.send({pipes::BINARY, message.own_buffer()}); - break; - default: - /* Dont log an error because the timeout disconnect function uses this without knowing which proto */ - __asm__("nop"); - return; - } + switch(this->ftType) { + case FTType::TeamSpeak: + case FTType::TeaWeb_HTTP: + this->sendRawMessage(message); + break; + case FTType::TeaWeb_SSL: + case FTType::TeaWeb_SSL_HTTP: + this->ssl_handler.send(message); + break; + case FTType::TeaWeb_SSL_WS: + this->ws_handler.send({pipes::BINARY, message.own_buffer()}); + break; + default: + /* Dont log an error because the timeout disconnect function uses this without knowing which proto */ + __asm__("nop"); + return; + } } void FileClient::handle_ssl_message(const pipes::buffer_view &buffer) { - if(this->ftType == FTType::TeaWeb_SSL_HTTP) - this->handle_http_message(buffer); - else if(this->ftType == FTType::TeaWeb_SSL_WS) - this->ws_handler.process_incoming_data(buffer); - else if(this->ftType == FTType::TeaWeb_SSL) { /* lets detect if we have HTTP or WebSocket */ - this->read_buffer += buffer; - this->handle_http_header(); - } + if(this->ftType == FTType::TeaWeb_SSL_HTTP) + this->handle_http_message(buffer); + else if(this->ftType == FTType::TeaWeb_SSL_WS) + this->ws_handler.process_incoming_data(buffer); + else if(this->ftType == FTType::TeaWeb_SSL) { /* lets detect if we have HTTP or WebSocket */ + this->read_buffer += buffer; + this->handle_http_header(); + } } void FileClient::handle_http_header() { - auto header_end = this->read_buffer.find("\r\n\r\n"); - if(header_end == string::npos) { - if(this->read_buffer.length() > 1024 * 1024 * 4) { - this->read_buffer = pipes::buffer{}; - logMessage(LOG_FT, "{} Client tried to fillup server memory. Disconnecting client.", this->client_prefix()); - this->disconnect(); - return; - } - return; - } + auto header_end = this->read_buffer.find("\r\n\r\n"); + if(header_end == string::npos) { + if(this->read_buffer.length() > 1024 * 1024 * 4) { + this->read_buffer = pipes::buffer{}; + logMessage(LOG_FT, "{} Client tried to fillup server memory. Disconnecting client.", this->client_prefix()); + this->disconnect(); + return; + } + return; + } - auto raw_request = this->read_buffer.view(0, header_end).string(); - http::HttpRequest request{}; - if(!http::parse_request(raw_request, request)) { - logError(LOG_FT, "{} Failed to parse HTTP request. Disconnecting client.", this->client_prefix()); - this->disconnect(); - return; - } + auto raw_request = this->read_buffer.view(0, header_end).string(); + http::HttpRequest request{}; + if(!http::parse_request(raw_request, request)) { + logError(LOG_FT, "{} Failed to parse HTTP request. Disconnecting client.", this->client_prefix()); + this->disconnect(); + return; + } - auto header_upgrade = request.findHeader("Upgrade"); - if(header_upgrade && header_upgrade.values[0] == "websocket") { - debugMessage(LOG_FT, "{} Received WebSocket upgrade. Upgrading connection.", this->client_prefix(), raw_request); - if(this->ftType == FTType::TeaWeb_SSL) - this->ftType = FTType::TeaWeb_SSL_WS; - else { - //TODO: WebSocket only! - } - this->ws_handler.initialize(); - this->ws_handler.process_incoming_data(this->read_buffer); - this->read_buffer = pipes::buffer{}; - this->handle->tickFileClient(_this.lock()); /* we require a manual reticking */ - return; - } else { - /* process the http request header */ - if(this->ftType == FTType::TeaWeb_SSL) - this->ftType = FTType::TeaWeb_SSL_HTTP; + auto header_upgrade = request.findHeader("Upgrade"); + if(header_upgrade && header_upgrade.values[0] == "websocket") { + debugMessage(LOG_FT, "{} Received WebSocket upgrade. Upgrading connection.", this->client_prefix(), raw_request); + if(this->ftType == FTType::TeaWeb_SSL) + this->ftType = FTType::TeaWeb_SSL_WS; + else { + //TODO: WebSocket only! + } + this->ws_handler.initialize(); + this->ws_handler.process_incoming_data(this->read_buffer); + this->read_buffer = pipes::buffer{}; + this->handle->tickFileClient(_this.lock()); /* we require a manual reticking */ + return; + } else { + /* process the http request header */ + if(this->ftType == FTType::TeaWeb_SSL) + this->ftType = FTType::TeaWeb_SSL_HTTP; - http::HttpResponse response{}; - response.setHeader("Connection", {"close"}); /* close the connection instance, we dont want multiple requests */ - response.setHeader("Access-Control-Allow-Methods", {"GET, POST"}); - response.setHeader("Access-Control-Allow-Origin", {"*"}); - response.setHeader("Access-Control-Allow-Headers", request.findHeader("Access-Control-Request-Headers").values); //access-control-allow-headers - response.setHeader("Access-Control-Max-Age", {"86400"}); - response.setHeader("Access-Control-Expose-Headers", {"*, Content-Length, X-media-bytes, Content-Disposition"}); + http::HttpResponse response{}; + response.setHeader("Connection", {"close"}); /* close the connection instance, we dont want multiple requests */ + response.setHeader("Access-Control-Allow-Methods", {"GET, POST"}); + response.setHeader("Access-Control-Allow-Origin", {"*"}); + response.setHeader("Access-Control-Allow-Headers", request.findHeader("Access-Control-Request-Headers").values); //access-control-allow-headers + response.setHeader("Access-Control-Max-Age", {"86400"}); + response.setHeader("Access-Control-Expose-Headers", {"*, Content-Length, X-media-bytes, Content-Disposition"}); - /* test for a preflight request: https://developer.mozilla.org/en-US/docs/Glossary/preflight_request */ - auto request_method = request.findHeader("Access-Control-Request-Method"); - if(request_method) { - debugMessage(LOG_FT, "{} Received preflight request. Sending close response with allow and let the client reconnect.", this->client_prefix()); + /* test for a preflight request: https://developer.mozilla.org/en-US/docs/Glossary/preflight_request */ + auto request_method = request.findHeader("Access-Control-Request-Method"); + if(request_method) { + debugMessage(LOG_FT, "{} Received preflight request. Sending close response with allow and let the client reconnect.", this->client_prefix()); - response.code = http::code::_200; + response.code = http::code::_200; - this->read_buffer = this->read_buffer.range(header_end + 4); - auto raw_response = response.build(); - this->sendMessage(pipes::buffer_view{raw_response.data(), raw_response.length()}); - this->disconnect(seconds(5)); /* write our response & flush */ - return; - } else { - auto transfer_key = request.findHeader("transfer-key"); - auto download_name = request.findHeader("download-name"); + this->read_buffer = this->read_buffer.range(header_end + 4); + auto raw_response = response.build(); + this->sendMessage(pipes::buffer_view{raw_response.data(), raw_response.length()}); + this->disconnect(seconds(5)); /* write our response & flush */ + return; + } else { + auto transfer_key = request.findHeader("transfer-key"); + auto download_name = request.findHeader("download-name"); - if(!transfer_key) { - response.code = http::code::code(400, "Bad Request"); - debugMessage(LOG_FT, "{} Received invalid HTTP request (Missing transfer key).", this->client_prefix()); - } else { - if(!this->applyKey(transfer_key.values[0]) || !this->pendingKey) { - response.code = http::code::code(404, "Not Found"); - debugMessage(LOG_FT, "{} Received invalid HTTP request (Invalid transfer key: {}).", this->client_prefix(), transfer_key.values[0]); - } else { - response.code = http::code::code(200, "OK"); - response.setHeader("Content-Length", {to_string(this->pendingKey->size)}); + if(!transfer_key) { + response.code = http::code::code(400, "Bad Request"); + debugMessage(LOG_FT, "{} Received invalid HTTP request (Missing transfer key).", this->client_prefix()); + } else { + if(!this->applyKey(transfer_key.values[0]) || !this->pendingKey) { + response.code = http::code::code(404, "Not Found"); + debugMessage(LOG_FT, "{} Received invalid HTTP request (Invalid transfer key: {}).", this->client_prefix(), transfer_key.values[0]); + } else { + response.code = http::code::code(200, "OK"); + response.setHeader("Content-Length", {to_string(this->pendingKey->size)}); - if(!this->pendingKey->upload) { - response.setHeader("Content-type", {"application/octet-stream; "}); - response.setHeader("Content-Transfer-Encoding", {"binary"}); - response.setHeader("Content-Disposition", {"attachment; filename=\"" + http::encode_url(download_name ? download_name.values[0] : "TeaWeb Download") + "\""}); + if(!this->pendingKey->upload) { + response.setHeader("Content-type", {"application/octet-stream; "}); + response.setHeader("Content-Transfer-Encoding", {"binary"}); + response.setHeader("Content-Disposition", {"attachment; filename=\"" + http::encode_url(download_name ? download_name.values[0] : "TeaWeb Download") + "\""}); - if(this->pendingKey->size > 1) { - char header_buffer[64]; - auto read = fstream->readsome(header_buffer, 64); - if(read > 0) - response.setHeader("X-media-bytes", {base64::encode(header_buffer, read)}); - fstream->seekg(this->pendingKey->offset, std::ios::beg); - } - } - } - } + if(this->pendingKey->size > 1) { + char header_buffer[64]; + auto read = fstream->readsome(header_buffer, 64); + if(read > 0) + response.setHeader("X-media-bytes", {base64::encode(header_buffer, read)}); + fstream->seekg(this->pendingKey->offset, std::ios::beg); + } + } + } + } - if(!this->pendingKey || !this->pendingKey->upload) { - auto raw_response = response.build(); - this->sendMessage(pipes::buffer_view{raw_response.data(), raw_response.length()}); - } - if(response.code->code != 200) { - this->disconnect(seconds(5)) /* write our response & flush */; - return; - } + if(!this->pendingKey || !this->pendingKey->upload) { + auto raw_response = response.build(); + this->sendMessage(pipes::buffer_view{raw_response.data(), raw_response.length()}); + } + if(response.code->code != 200) { + this->disconnect(seconds(5)) /* write our response & flush */; + return; + } - this->http_init = true; - auto overhead = this->read_buffer.range(header_end + 4); - this->read_buffer = pipes::buffer{}; /* reset the read buffer */ - if(overhead.length() > 0) - this->handle_http_message(overhead); - this->handle->tickFileClient(_this.lock()); /* we require a manual reticking */ - } - } + this->http_init = true; + auto overhead = this->read_buffer.range(header_end + 4); + this->read_buffer = pipes::buffer{}; /* reset the read buffer */ + if(overhead.length() > 0) + this->handle_http_message(overhead); + this->handle->tickFileClient(_this.lock()); /* we require a manual reticking */ + } + } } bool FileClient::handle_ts_message() { - if(this->state_transfer == T_INITIALIZE) { - if(this->availableBytes() >= 16) { - return this->applyKey(this->getBytes(16)); - } else - return false; - } else if(this->state_transfer == T_TRANSFER) { - if(!this->pendingKey) - return false; + if(this->state_transfer == T_INITIALIZE) { + if(this->availableBytes() >= 16) { + return this->applyKey(this->getBytes(16)); + } else + return false; + } else if(this->state_transfer == T_TRANSFER) { + if(!this->pendingKey) + return false; - if(!this->pendingKey->upload) - return false; /* should never happen! */ + if(!this->pendingKey->upload) + return false; /* should never happen! */ - bool reexecute = false; - pipes::buffer buffer; - { - lock_guard buffer_lock(this->bufferLock); - if(this->read_queue.empty()) - return false; /* nothing to upload */ + bool reexecute = false; + pipes::buffer buffer; + { + lock_guard buffer_lock(this->bufferLock); + if(this->read_queue.empty()) + return false; /* nothing to upload */ - buffer = this->read_queue.front(); - this->read_queue.pop_front(); - reexecute |= !this->read_queue.empty(); - } + buffer = this->read_queue.front(); + this->read_queue.pop_front(); + reexecute |= !this->read_queue.empty(); + } - if(!this->uploadWriteBytes(buffer)) - return false; /* error already handeled by uploadWriteBytes(...) */ + if(!this->uploadWriteBytes(buffer)) + return false; /* error already handeled by uploadWriteBytes(...) */ - return reexecute; - } + return reexecute; + } - return false; + return false; } void FileClient::handle_ws_message(const pipes::WSMessage &message) { - if(this->state_transfer == T_INITIALIZE) { - debugMessage(LOG_FT, "{} Received transfer key: {}", this->client_prefix(), message.data.string()); - if(this->applyKey(message.data.string())) - this->handle->tickFileClient(_this.lock()); /* we require a manual reticking */ - return; - } else if(this->state_transfer == T_TRANSFER) { - if(this->pendingKey->upload) - this->uploadWriteBytes(message.data); - else { - logError(LOG_FT, "{} Invalid write (Just download)", this->client_prefix()); - this->disconnect(); - } - } + if(this->state_transfer == T_INITIALIZE) { + debugMessage(LOG_FT, "{} Received transfer key: {}", this->client_prefix(), message.data.string()); + if(this->applyKey(message.data.string())) + this->handle->tickFileClient(_this.lock()); /* we require a manual reticking */ + return; + } else if(this->state_transfer == T_TRANSFER) { + if(this->pendingKey->upload) + this->uploadWriteBytes(message.data); + else { + logError(LOG_FT, "{} Invalid write (Just download)", this->client_prefix()); + this->disconnect(); + } + } } void FileClient::handle_http_message(const pipes::buffer_view &message) { - if(!this->http_init) { - this->read_buffer += message; - this->handle_http_header(); - return; - } - if(!https_upload_init) { - //------WebKitFormBoundaryaWP8XAzMBnMOJznv\r\nContent-Disposition: form-data; name=\"file\"; filename=\"blob\"\r\nContent-Type: application/octet-stream\r\n\r\n - this->read_buffer += message; + if(!this->http_init) { + this->read_buffer += message; + this->handle_http_header(); + return; + } + if(!https_upload_init) { + //------WebKitFormBoundaryaWP8XAzMBnMOJznv\r\nContent-Disposition: form-data; name=\"file\"; filename=\"blob\"\r\nContent-Type: application/octet-stream\r\n\r\n + this->read_buffer += message; - auto header_end = this->read_buffer.find("\r\n\r\n"); - if(header_end == string::npos) { - if(this->read_buffer.length() > 1024 * 1024 * 4) { - this->read_buffer = pipes::buffer{}; - logMessage(LOG_FT, "{} Client tried to fillup server memory. Disconnecting client.", this->client_prefix()); - this->disconnect(); - return; - } - return; - } + auto header_end = this->read_buffer.find("\r\n\r\n"); + if(header_end == string::npos) { + if(this->read_buffer.length() > 1024 * 1024 * 4) { + this->read_buffer = pipes::buffer{}; + logMessage(LOG_FT, "{} Client tried to fillup server memory. Disconnecting client.", this->client_prefix()); + this->disconnect(); + return; + } + return; + } - https_upload_init = true; - auto overhead = this->read_buffer.view(header_end + 4); - this->read_buffer = pipes::buffer{}; - if(!overhead.empty()) - this->handle_http_message(overhead); - return; - } - if(!this->pendingKey || !this->pendingKey->upload) { - logError(LOG_FT, "{} HTTP Invalid request", this->client_prefix()); - return; - } + https_upload_init = true; + auto overhead = this->read_buffer.view(header_end + 4); + this->read_buffer = pipes::buffer{}; + if(!overhead.empty()) + this->handle_http_message(overhead); + return; + } + if(!this->pendingKey || !this->pendingKey->upload) { + logError(LOG_FT, "{} HTTP Invalid request", this->client_prefix()); + return; + } - auto bytes_to_write = this->pendingKey->size - this->bytesHandled; /* ignore boundaries */ - if(bytes_to_write < message.length()) - this->uploadWriteBytes(message.view(0, bytes_to_write)); - else - this->uploadWriteBytes(message); + auto bytes_to_write = this->pendingKey->size - this->bytesHandled; /* ignore boundaries */ + if(bytes_to_write < message.length()) + this->uploadWriteBytes(message.view(0, bytes_to_write)); + else + this->uploadWriteBytes(message); - if(this->bytesHandled == this->pendingKey->size){ - http::HttpResponse response{}; - response.setHeader("Connection", {"close"}); /* close the connection instance, we dont want multiple requests */ - response.setHeader("Access-Control-Allow-Methods", {"GET, POST"}); - response.setHeader("Access-Control-Allow-Origin", {"*"}); - response.setHeader("Access-Control-Allow-Headers", {"*"}); - response.setHeader("Access-Control-Max-Age", {"86400"}); - response.setHeader("Access-Control-Expose-Headers", {"*"}); + if(this->bytesHandled == this->pendingKey->size){ + http::HttpResponse response{}; + response.setHeader("Connection", {"close"}); /* close the connection instance, we dont want multiple requests */ + response.setHeader("Access-Control-Allow-Methods", {"GET, POST"}); + response.setHeader("Access-Control-Allow-Origin", {"*"}); + response.setHeader("Access-Control-Allow-Headers", {"*"}); + response.setHeader("Access-Control-Max-Age", {"86400"}); + response.setHeader("Access-Control-Expose-Headers", {"*"}); - auto raw_response = response.build(); - this->sendMessage(pipes::buffer_view{raw_response.data(), raw_response.length()}); - } + auto raw_response = response.build(); + this->sendMessage(pipes::buffer_view{raw_response.data(), raw_response.length()}); + } } \ No newline at end of file diff --git a/server/src/client/file/FileClient.h b/server/src/client/file/FileClient.h index 7b44893..abfdf9b 100644 --- a/server/src/client/file/FileClient.h +++ b/server/src/client/file/FileClient.h @@ -17,32 +17,32 @@ namespace ts { class FileServer; enum FTType { - Unknown, + Unknown, TeamSpeak, TeaWeb_SSL, /* not yet decided if the protocol is HTTP or WebSocket */ TeaWeb_SSL_WS, - TeaWeb_SSL_HTTP, + TeaWeb_SSL_HTTP, - TeaWeb_HTTP + TeaWeb_HTTP }; class FileClient { friend class FileServer; public: - enum TransferState { - T_INITIALIZE, - T_TRANSFER, - T_DONE - }; - enum ConnectionState { - C_CONNECTED, - C_DISCONNECTING, - C_DISCONNECTED - }; + enum TransferState { + T_INITIALIZE, + T_TRANSFER, + T_DONE + }; + enum ConnectionState { + C_CONNECTED, + C_DISCONNECTING, + C_DISCONNECTED + }; struct BandwidthEntry { std::chrono::system_clock::time_point timestamp; - uint16_t length = 0; + uint16_t length = 0; }; FileClient(FileServer* handle, int socketFd); @@ -56,7 +56,7 @@ namespace ts { std::string client_prefix(); size_t transferred_bytes(); - size_t remaining_bytes(); + size_t remaining_bytes(); protected: void disconnectFinal(std::unique_lock& /* tick lock */, bool /* handle flush thread */); @@ -78,7 +78,7 @@ namespace ts { /* http header parser. header must be stored with read buffer! */ void handle_http_header(); - /* Final protocol handlers */ + /* Final protocol handlers */ void handle_http_message(const pipes::buffer_view&); void handle_ws_message(const pipes::WSMessage&); bool handle_ts_message(); @@ -95,32 +95,32 @@ namespace ts { bool event_read_hold = false; ::event* readEvent = nullptr; - bool event_write_hold = false; + bool event_write_hold = false; ::event* writeEvent = nullptr; threads::Mutex tickLock; std::recursive_timed_mutex bufferLock; std::deque write_queue; - std::deque read_queue; - pipes::buffer read_buffer; /* buffer which contains fragments of decoded data, e.g. HTTP request. Access only within tickLock locked */ + std::deque read_queue; + pipes::buffer read_buffer; /* buffer which contains fragments of decoded data, e.g. HTTP request. Access only within tickLock locked */ FTType ftType = FTType::Unknown; - pipes::WebSocket ws_handler; + pipes::WebSocket ws_handler; pipes::SSL ssl_handler; bool https_upload_init = false; - bool http_init = false; /* general flag if the HTTP header has been parsed */ + bool http_init = false; /* general flag if the HTTP header has been parsed */ std::shared_ptr client; std::shared_ptr pendingKey = nullptr; std::chrono::time_point last_io_action; - std::chrono::time_point connect_timestamp; - std::chrono::time_point finished_timestamp; + std::chrono::time_point connect_timestamp; + std::chrono::time_point finished_timestamp; std::fstream* fstream = nullptr; size_t bytesHandled = 0; - ConnectionState state_connection = ConnectionState::C_CONNECTED; - TransferState state_transfer = TransferState::T_INITIALIZE; + ConnectionState state_connection = ConnectionState::C_CONNECTED; + TransferState state_transfer = TransferState::T_INITIALIZE; size_t availableBytes(); std::string getBytes(size_t); diff --git a/server/src/client/file/FileClientIO.cpp b/server/src/client/file/FileClientIO.cpp index 9505dd8..746a728 100644 --- a/server/src/client/file/FileClientIO.cpp +++ b/server/src/client/file/FileClientIO.cpp @@ -17,63 +17,63 @@ void FileClient::sendRawMessage(const pipes::buffer_view &message) { if(!this->event_write_hold) event_add(this->writeEvent, nullptr); else { - __asm__("nop"); + __asm__("nop"); } } void FileClient::handleMessageWrite(int fd, short, void *) { auto self = this->_this.lock(); - if(!self) return; + if(!self) return; - decltype(this->pendingKey) pending_key; - { + decltype(this->pendingKey) pending_key; + { - lock_guard l(this->tickLock); - pending_key = this->pendingKey; - } + lock_guard l(this->tickLock); + pending_key = this->pendingKey; + } unique_lock lock(this->bufferLock, defer_lock); - if(!lock.try_lock_for(milliseconds(5))) { - logWarning(LOG_FT, "{} Buffer lock locked, could not write data!", this->client_prefix()); - return; /* somebody else doing a action and will (hopefully) readd the event */ - } - if(self->state_connection == C_DISCONNECTED) return; + if(!lock.try_lock_for(milliseconds(5))) { + logWarning(LOG_FT, "{} Buffer lock locked, could not write data!", this->client_prefix()); + return; /* somebody else doing a action and will (hopefully) readd the event */ + } + if(self->state_connection == C_DISCONNECTED) return; - auto used = this->used_bandwidth(); - if(pending_key) { //Delay the write <3 :P - if(pending_key->max_bandwhidth >= 0 && used > pending_key->max_bandwhidth) { - event_write_hold = true; - logTrace(LOG_FT, "{} Exceeded bandwidth limit of {} bytes (Used {} bytes). Temporary skipping write event!", this->client_prefix(), pending_key->max_bandwhidth, this->used_bandwidth()); - return; - } - } - event_write_hold = false; + auto used = this->used_bandwidth(); + if(pending_key) { //Delay the write <3 :P + if(pending_key->max_bandwhidth >= 0 && used > pending_key->max_bandwhidth) { + event_write_hold = true; + logTrace(LOG_FT, "{} Exceeded bandwidth limit of {} bytes (Used {} bytes). Temporary skipping write event!", this->client_prefix(), pending_key->max_bandwhidth, this->used_bandwidth()); + return; + } + } + event_write_hold = false; - pipes::buffer* buffer = nullptr; + pipes::buffer* buffer = nullptr; while(true) { - if(this->write_queue.empty()) { - lock.unlock(); /* unlock write buffer because we're ticking */ - if(pending_key && !pending_key->upload) - this->handle->tickFileClient(_this.lock()); //We have to fill up again - return; - } + if(this->write_queue.empty()) { + lock.unlock(); /* unlock write buffer because we're ticking */ + if(pending_key && !pending_key->upload) + this->handle->tickFileClient(_this.lock()); //We have to fill up again + return; + } - buffer = &this->write_queue.front(); - if(buffer->empty()) { - this->write_queue.pop_front(); - buffer = nullptr; - continue; - } - break; + buffer = &this->write_queue.front(); + if(buffer->empty()) { + this->write_queue.pop_front(); + buffer = nullptr; + continue; + } + break; } ssize_t length = buffer->length(); - if(pending_key && pending_key->max_bandwhidth >= 0) { - if(pending_key->max_bandwhidth < length && pending_key->max_bandwhidth > used) length = pending_key->max_bandwhidth - used; - } + if(pending_key && pending_key->max_bandwhidth >= 0) { + if(pending_key->max_bandwhidth < length && pending_key->max_bandwhidth > used) length = pending_key->max_bandwhidth - used; + } length = send(fd, buffer->data_ptr(), length, MSG_NOSIGNAL); - //logTrace(LOG_FT, "{} Wrote {} bytes", this->client_prefix(), length); + //logTrace(LOG_FT, "{} Wrote {} bytes", this->client_prefix(), length); if(length == -1) { if (errno == EINTR || errno == EAGAIN) { @@ -82,167 +82,167 @@ void FileClient::handleMessageWrite(int fd, short, void *) { } else { logError(LOG_FT, "{} Failed to write some data! ({}/{})", this->client_prefix(), errno, strerror(errno)); - lock.unlock(); + lock.unlock(); self->disconnect(); return; } } else { - *buffer = buffer->range(length); + *buffer = buffer->range(length); - auto entry = make_unique(); - entry->length = length ; - entry->timestamp = system_clock::now(); - { - lock_guard b_lock(this->bandwidth_lock); - this->bandwidth.push_back(move(entry)); - } + auto entry = make_unique(); + entry->length = length ; + entry->timestamp = system_clock::now(); + { + lock_guard b_lock(this->bandwidth_lock); + this->bandwidth.push_back(move(entry)); + } } - last_io_action = system_clock::now(); + last_io_action = system_clock::now(); if(buffer->empty()) { - this->write_queue.pop_front(); - buffer = nullptr; + this->write_queue.pop_front(); + buffer = nullptr; } if(pending_key && this->bytesHandled != pending_key->size) { - if(this->write_queue.size() < 4) this->handle->tickFileClient(_this.lock()); + if(this->write_queue.size() < 4) this->handle->tickFileClient(_this.lock()); } if(!this->write_queue.empty()) { - event_add(this->writeEvent, nullptr); + event_add(this->writeEvent, nullptr); } self.reset(); } void FileClient::handleMessageRead(int fd, short, void *) { auto self = this->_this.lock(); - if(self->state_connection != C_CONNECTED) return; + if(self->state_connection != C_CONNECTED) return; - decltype(this->pendingKey) pending_key; - { + decltype(this->pendingKey) pending_key; + { - lock_guard l(this->tickLock); - pending_key = this->pendingKey; - } - size_t buffer_length = 1024; - if(pending_key && pending_key->max_bandwhidth >= 0) { - auto used = this->used_bandwidth(); - if(used < pending_key->max_bandwhidth) { - buffer_length = pending_key->max_bandwhidth - used; - if(buffer_length > 1024) buffer_length = 1024; - } else { - logTrace(LOG_FT, "{} Exceeded bandwidth limit of {} bytes (Used {} bytes). Temporary removing read event!", this->client_prefix(), pending_key->max_bandwhidth, used); - { - lock_guard lock(this->bufferLock); - if(this->readEvent) - event_del_noblock(this->readEvent); - } - this->event_read_hold = true; - return; - } - } - pipes::buffer buffer(buffer_length); + lock_guard l(this->tickLock); + pending_key = this->pendingKey; + } + size_t buffer_length = 1024; + if(pending_key && pending_key->max_bandwhidth >= 0) { + auto used = this->used_bandwidth(); + if(used < pending_key->max_bandwhidth) { + buffer_length = pending_key->max_bandwhidth - used; + if(buffer_length > 1024) buffer_length = 1024; + } else { + logTrace(LOG_FT, "{} Exceeded bandwidth limit of {} bytes (Used {} bytes). Temporary removing read event!", this->client_prefix(), pending_key->max_bandwhidth, used); + { + lock_guard lock(this->bufferLock); + if(this->readEvent) + event_del_noblock(this->readEvent); + } + this->event_read_hold = true; + return; + } + } + pipes::buffer buffer(buffer_length); auto length = recv(fd, buffer.data_ptr(), buffer.length(), 0); if(length < 0){ if(errno == EINTR || errno == EAGAIN) ;//event_add(this->readEvent, nullptr); else { - { - lock_guard lock(this->bufferLock); - if(this->readEvent) - event_del_noblock(this->readEvent); - } - if(this->state_connection == C_CONNECTED) { - logError(LOG_FT, "{} Failed to read some data! ({}/{})", this->client_prefix(), errno, strerror(errno)); - self->disconnect(); - } + { + lock_guard lock(this->bufferLock); + if(this->readEvent) + event_del_noblock(this->readEvent); + } + if(this->state_connection == C_CONNECTED) { + logError(LOG_FT, "{} Failed to read some data! ({}/{})", this->client_prefix(), errno, strerror(errno)); + self->disconnect(); + } } return; } else if(length == 0){ - { - lock_guard lock(this->bufferLock); - if(this->readEvent) - event_del_noblock(this->readEvent); - } - if(this->state_connection == C_CONNECTED) { - if(this->state_transfer == T_TRANSFER) - logWarning(LOG_FT, "{} Transfer hang up. Remote peer closed the connection.", this->client_prefix()); - else - logMessage(LOG_FT, "{} Remote peer has closed the connection before initializing a transfer.", this->client_prefix()); - self->disconnect(seconds(3)); - } - return; + { + lock_guard lock(this->bufferLock); + if(this->readEvent) + event_del_noblock(this->readEvent); + } + if(this->state_connection == C_CONNECTED) { + if(this->state_transfer == T_TRANSFER) + logWarning(LOG_FT, "{} Transfer hang up. Remote peer closed the connection.", this->client_prefix()); + else + logMessage(LOG_FT, "{} Remote peer has closed the connection before initializing a transfer.", this->client_prefix()); + self->disconnect(seconds(3)); + } + return; } - last_io_action = system_clock::now(); + last_io_action = system_clock::now(); buffer.resize(length); - { - lock_guard lock(this->bufferLock); - if(self->state_connection != C_CONNECTED) return; /* drop the buffer because we're not connected anymore */ + { + lock_guard lock(this->bufferLock); + if(self->state_connection != C_CONNECTED) return; /* drop the buffer because we're not connected anymore */ - this->read_queue.push_back(std::move(buffer)); - } + this->read_queue.push_back(std::move(buffer)); + } this->handle->tickFileClient(_this.lock()); - auto entry = make_unique(); - entry->length = length ; - entry->timestamp = system_clock::now(); - //logTrace(LOG_FT, "{} Readed {} bytes", this->client_prefix(), length); - { - lock_guard lock(this->bandwidth_lock); - this->bandwidth.push_back(move(entry)); - } + auto entry = make_unique(); + entry->length = length ; + entry->timestamp = system_clock::now(); + //logTrace(LOG_FT, "{} Readed {} bytes", this->client_prefix(), length); + { + lock_guard lock(this->bandwidth_lock); + this->bandwidth.push_back(move(entry)); + } } size_t FileClient::availableBytes() { - lock_guard lock(this->bufferLock); + lock_guard lock(this->bufferLock); size_t available = 0; for(const auto& buf : this->read_queue) - available += buf.length(); + available += buf.length(); return available; } std::string FileClient::peekBytes(size_t size) { ssize_t required = size; - lock_guard lock(this->bufferLock); + lock_guard lock(this->bufferLock); string result; - result.reserve(size); + result.reserve(size); for(pipes::buffer& buf : this->read_queue) { - if(required <= 0) break; - if(buf.length() > required) { - result += string(buf.data_ptr(), required); - required = 0; - } else { - result += string(buf.data_ptr(), buf.length()); - required -= buf.length(); - } + if(required <= 0) break; + if(buf.length() > required) { + result += string(buf.data_ptr(), required); + required = 0; + } else { + result += string(buf.data_ptr(), buf.length()); + required -= buf.length(); + } } return result; } std::string FileClient::getBytes(size_t size) { - lock_guard lock(this->bufferLock); + lock_guard lock(this->bufferLock); - string result; - result.reserve(size); + string result; + result.reserve(size); - while(!this->read_queue.empty()) { - if(size <= 0) break; + while(!this->read_queue.empty()) { + if(size <= 0) break; - auto& buf = this->read_queue.front(); - if(buf.length() > size) { - result += string((char*) buf.pipes::buffer_view::data_ptr(), size); - buf = buf.range(size); - size = 0; - } else { - result += string((char*) buf.data_ptr(), buf.length()); - size -= buf.length(); - this->read_queue.pop_front(); - } - } + auto& buf = this->read_queue.front(); + if(buf.length() > size) { + result += string((char*) buf.pipes::buffer_view::data_ptr(), size); + buf = buf.range(size); + size = 0; + } else { + result += string((char*) buf.data_ptr(), buf.length()); + size -= buf.length(); + this->read_queue.pop_front(); + } + } return result; } \ No newline at end of file diff --git a/server/src/client/music/MusicClient.cpp b/server/src/client/music/MusicClient.cpp index b970cc7..1eff2c2 100644 --- a/server/src/client/music/MusicClient.cpp +++ b/server/src/client/music/MusicClient.cpp @@ -12,85 +12,85 @@ using namespace ts; using namespace ts::server; MusicClient::loader_t MusicClient::ytLoader() { - return providerLoader("YouTube"); + return providerLoader("YouTube"); } MusicClient::loader_t MusicClient::ffmpegLoader() { - return providerLoader("FFMpeg"); + return providerLoader("FFMpeg"); } MusicClient::loader_t MusicClient::channelLoader() { - return providerLoader("ChannelProvider"); + return providerLoader("ChannelProvider"); } MusicClient::loader_t MusicClient::failedLoader(const std::string &message) { - return make_shared([message] (const shared_ptr, const shared_ptr&, std::string& error) mutable -> std::shared_ptr { - error = message; - return nullptr; - }); + return make_shared([message] (const shared_ptr, const shared_ptr&, std::string& error) mutable -> std::shared_ptr { + error = message; + return nullptr; + }); } MusicClient::loader_t MusicClient::providerLoader(const std::string &name, shared_ptr<::music::manager::PlayerProvider> provider) { - return make_shared([name, provider, used_provider = std::move(provider)] (const shared_ptr server, const shared_ptr& entry, std::string& error) mutable -> std::shared_ptr { - if(!used_provider) - used_provider = ::music::manager::resolveProvider(name, entry->getUrl()); + return make_shared([name, provider, used_provider = std::move(provider)] (const shared_ptr server, const shared_ptr& entry, std::string& error) mutable -> std::shared_ptr { + if(!used_provider) + used_provider = ::music::manager::resolveProvider(name, entry->getUrl()); - if(!used_provider){ - error = "Could not resolve '" + name + "' provider!"; - return nullptr; - } + if(!used_provider){ + error = "Could not resolve '" + name + "' provider!"; + return nullptr; + } - auto promise = used_provider->createPlayer(entry->getUrl(), nullptr, &*server); - auto player = promise.waitAndGet(nullptr, system_clock::now() + seconds(60)); - if(!player) { - if(promise.state() == threads::FutureState::WORKING) - error = "load timeout"; - else - error = promise.errorMegssage(); - return nullptr; - } + auto promise = used_provider->createPlayer(entry->getUrl(), nullptr, &*server); + auto player = promise.waitAndGet(nullptr, system_clock::now() + seconds(60)); + if(!player) { + if(promise.state() == threads::FutureState::WORKING) + error = "load timeout"; + else + error = promise.errorMegssage(); + return nullptr; + } - { - auto data = make_shared(); - data->player = player; - data->title = player->songTitle(); - data->description = player->songDescription(); - data->length = player->length(); + { + auto data = make_shared(); + data->player = player; + data->title = player->songTitle(); + data->description = player->songDescription(); + data->length = player->length(); - for(const auto& thumbnail : player->thumbnails()) { - if(thumbnail->type() == ::music::THUMBNAIL_URL) { - data->thumbnail = static_pointer_cast<::music::ThumbnailUrl>(thumbnail)->url(); - break; - } - } + for(const auto& thumbnail : player->thumbnails()) { + if(thumbnail->type() == ::music::THUMBNAIL_URL) { + data->thumbnail = static_pointer_cast<::music::ThumbnailUrl>(thumbnail)->url(); + break; + } + } - return data; - } - }); + return data; + } + }); } MusicClient::MusicClient(const std::shared_ptr& handle, const std::string& uniqueId) : ConnectedClient(handle->getSql(), handle) { - memtrack::allocated(this); + memtrack::allocated(this); this->state = CONNECTED; this->properties()[property::CLIENT_TYPE] = ClientType::CLIENT_TEAMSPEAK; this->properties()[property::CLIENT_TYPE_EXACT] = ClientType::CLIENT_MUSIC; this->properties()[property::CLIENT_UNIQUE_IDENTIFIER] = uniqueId; - { - //FIXME handle encoder create error, or assume that this never happens - int error = 0; - this->voice.encoder = opus_encoder_create(48000, 2, OPUS_APPLICATION_AUDIO, &error); - } + { + //FIXME handle encoder create error, or assume that this never happens + int error = 0; + this->voice.encoder = opus_encoder_create(48000, 2, OPUS_APPLICATION_AUDIO, &error); + } - this->_player_state = SLEEPING; + this->_player_state = SLEEPING; } MusicClient::~MusicClient() { if(this->voice.encoder) { opus_encoder_destroy(this->voice.encoder); - this->voice.encoder = nullptr; + this->voice.encoder = nullptr; } - memtrack::freed(this); + memtrack::freed(this); } void MusicClient::sendCommand(const ts::Command &command, bool low) { } @@ -108,53 +108,53 @@ bool MusicClient::closeConnection(const std::chrono::system_clock::time_point&) } bool MusicClient::disconnect(const std::string &reason) { - this->player_reset(false); - if(this->currentChannel) - this->properties()[property::CLIENT_LAST_CHANNEL] = this->currentChannel->channelId(); - return true; + this->player_reset(false); + if(this->currentChannel) + this->properties()[property::CLIENT_LAST_CHANNEL] = this->currentChannel->channelId(); + return true; } bool server::MusicClient::notifyClientMoved( - const std::shared_ptr &client, - const std::shared_ptr &target_channel, - ViewReasonId reason, - std::string msg, - std::shared_ptr invoker, + const std::shared_ptr &client, + const std::shared_ptr &target_channel, + ViewReasonId reason, + std::string msg, + std::shared_ptr invoker, bool lock_channel_tree) { - if(&*client == this) { - this->properties()[property::CLIENT_LAST_CHANNEL] = target_channel ? target_channel->channelId() : 0; - } - return true; + if(&*client == this) { + this->properties()[property::CLIENT_LAST_CHANNEL] = target_channel ? target_channel->channelId() : 0; + } + return true; } void MusicClient::initialize_bot() { - this->_player_state = this->properties()[property::CLIENT_PLAYER_STATE]; - if(this->_player_state == ReplayState::LOADING) - this->_player_state = ReplayState::PLAYING; + this->_player_state = this->properties()[property::CLIENT_PLAYER_STATE]; + if(this->_player_state == ReplayState::LOADING) + this->_player_state = ReplayState::PLAYING; - if(!this->properties()->has(property::CLIENT_COUNTRY) || this->properties()[property::CLIENT_COUNTRY].value().empty()) - this->properties()[property::CLIENT_COUNTRY] = config::geo::countryFlag; - if(this->properties()[property::CLIENT_UPTIME_MODE].as() == MusicClient::UptimeMode::TIME_SINCE_SERVER_START) { - this->properties()[property::CLIENT_LASTCONNECTED] = duration_cast(this->server->start_timestamp().time_since_epoch()).count(); - } else { - this->properties()[property::CLIENT_LASTCONNECTED] = this->properties()[property::CLIENT_CREATED].value(); - } + if(!this->properties()->has(property::CLIENT_COUNTRY) || this->properties()[property::CLIENT_COUNTRY].value().empty()) + this->properties()[property::CLIENT_COUNTRY] = config::geo::countryFlag; + if(this->properties()[property::CLIENT_UPTIME_MODE].as() == MusicClient::UptimeMode::TIME_SINCE_SERVER_START) { + this->properties()[property::CLIENT_LASTCONNECTED] = duration_cast(this->server->start_timestamp().time_since_epoch()).count(); + } else { + this->properties()[property::CLIENT_LASTCONNECTED] = this->properties()[property::CLIENT_CREATED].value(); + } - this->properties()[property::CLIENT_LASTCONNECTED] = this->properties()[property::CLIENT_CREATED].value(); + this->properties()[property::CLIENT_LASTCONNECTED] = this->properties()[property::CLIENT_CREATED].value(); - ChannelId last = this->properties()[property::CLIENT_LAST_CHANNEL]; - auto channel = this->server->getChannelTree()->findChannel(last); - if(!channel) channel = this->server->getChannelTree()->getDefaultChannel(); + ChannelId last = this->properties()[property::CLIENT_LAST_CHANNEL]; + auto channel = this->server->getChannelTree()->findChannel(last); + if(!channel) channel = this->server->getChannelTree()->getDefaultChannel(); - if(this->getClientId() == 0 || this->server->findClient(this->getClientId()) != _this.lock()) { - this->server->registerClient(_this.lock()); - } - { - unique_lock server_channel_lock(this->server->channel_tree_lock); - this->server->client_move(this->ref(), channel, nullptr, "", ViewReasonId::VREASON_USER_ACTION, true, server_channel_lock); - } - this->player.volume_modifier = this->properties()[property::CLIENT_PLAYER_VOLUME]; + if(this->getClientId() == 0 || this->server->findClient(this->getClientId()) != _this.lock()) { + this->server->registerClient(_this.lock()); + } + { + unique_lock server_channel_lock(this->server->channel_tree_lock); + this->server->client_move(this->ref(), channel, nullptr, "", ViewReasonId::VREASON_USER_ACTION, true, server_channel_lock); + } + this->player.volume_modifier = this->properties()[property::CLIENT_PLAYER_VOLUME]; } void MusicClient::broadcast_text_message(const std::string &message) { @@ -175,62 +175,62 @@ void MusicClient::apply_volume(const std::shared_ptr<::music::SampleSegment> &se } void MusicClient::add_subscriber(const std::shared_ptr &client) { - lock_guard lock(this->subscriber_lock); - this->subscribers.erase(remove_if(this->subscribers.begin(), this->subscribers.end(), [&](const std::tuple, system_clock::time_point>& entry) { - auto e = get<0>(entry).lock(); - return !e || e == client; - }), this->subscribers.end()); + lock_guard lock(this->subscriber_lock); + this->subscribers.erase(remove_if(this->subscribers.begin(), this->subscribers.end(), [&](const std::tuple, system_clock::time_point>& entry) { + auto e = get<0>(entry).lock(); + return !e || e == client; + }), this->subscribers.end()); - this->subscribers.push_back({client, system_clock::time_point{}}); + this->subscribers.push_back({client, system_clock::time_point{}}); } bool MusicClient::is_subscriber(const std::shared_ptr &client) { - lock_guard lock(this->subscriber_lock); - for(const auto& entry : this->subscribers) - if(std::get<0>(entry).lock() == client) - return true; - return false; + lock_guard lock(this->subscriber_lock); + for(const auto& entry : this->subscribers) + if(std::get<0>(entry).lock() == client) + return true; + return false; } void MusicClient::remove_subscriber(const std::shared_ptr &client) { - lock_guard lock(this->subscriber_lock); - this->subscribers.erase(remove_if(this->subscribers.begin(), this->subscribers.end(), [&](const std::tuple, system_clock::time_point>& entry) { - auto e = get<0>(entry).lock(); - return !e || e == client; - }), this->subscribers.end()); + lock_guard lock(this->subscriber_lock); + this->subscribers.erase(remove_if(this->subscribers.begin(), this->subscribers.end(), [&](const std::tuple, system_clock::time_point>& entry) { + auto e = get<0>(entry).lock(); + return !e || e == client; + }), this->subscribers.end()); } void MusicClient::changePlayerState(ReplayState state) { - if(this->_player_state == state) return; - this->_player_state = state; - this->properties()[property::CLIENT_PLAYER_STATE] = state; - this->properties()[property::CLIENT_FLAG_TALKING] = state == ReplayState::PLAYING; - this->server->notifyClientPropertyUpdates(_this.lock(), deque{property::CLIENT_PLAYER_STATE}); + if(this->_player_state == state) return; + this->_player_state = state; + this->properties()[property::CLIENT_PLAYER_STATE] = state; + this->properties()[property::CLIENT_FLAG_TALKING] = state == ReplayState::PLAYING; + this->server->notifyClientPropertyUpdates(_this.lock(), deque{property::CLIENT_PLAYER_STATE}); } void MusicClient::notifySongChange(const std::shared_ptr& song) { - this->server->forEachClient([&](shared_ptr client) { - client->notifyMusicPlayerSongChange(dynamic_pointer_cast(_this.lock()), song); - }); + this->server->forEachClient([&](shared_ptr client) { + client->notifyMusicPlayerSongChange(dynamic_pointer_cast(_this.lock()), song); + }); } void MusicClient::handle_event_song_ended() { - this->replay_next_song(); + this->replay_next_song(); } void MusicClient::handle_event_song_dry() { - this->replay_next_song(); + this->replay_next_song(); } void MusicClient::handle_event_song_replay_failed() { - this->replay_next_song(); + this->replay_next_song(); } void MusicClient::replay_next_song() { - auto playlist = this->playlist(); - if(playlist) { - auto song = playlist->pop_next(); - if(song) - this->replay_song(song); - } + auto playlist = this->playlist(); + if(playlist) { + auto song = playlist->pop_next(); + if(song) + this->replay_song(song); + } } \ No newline at end of file diff --git a/server/src/client/music/MusicClient.h b/server/src/client/music/MusicClient.h index 62026ad..9c2b49a 100644 --- a/server/src/client/music/MusicClient.h +++ b/server/src/client/music/MusicClient.h @@ -12,37 +12,37 @@ namespace ts { class MusicClient : public ConnectedClient { friend class music::MusicBotManager; public: - struct UptimeMode { - enum value { - TIME_SINCE_CREATED = 0, - TIME_SINCE_SERVER_START - }; - }; + struct UptimeMode { + enum value { + TIME_SINCE_CREATED = 0, + TIME_SINCE_SERVER_START + }; + }; - struct Type { - enum value { - PERMANENT, - SEMI_PERMANENT, - TEMPORARY - }; - }; + struct Type { + enum value { + PERMANENT, + SEMI_PERMANENT, + TEMPORARY + }; + }; - //Helper methodes - typedef std::shared_ptr loader_t; - static loader_t ytLoader(); - static loader_t ffmpegLoader(); - static loader_t channelLoader(); - static loader_t failedLoader(const std::string& /* error */); - static loader_t providerLoader(const std::string& name = "", std::shared_ptr<::music::manager::PlayerProvider> = nullptr); + //Helper methodes + typedef std::shared_ptr loader_t; + static loader_t ytLoader(); + static loader_t ffmpegLoader(); + static loader_t channelLoader(); + static loader_t failedLoader(const std::string& /* error */); + static loader_t providerLoader(const std::string& name = "", std::shared_ptr<::music::manager::PlayerProvider> = nullptr); - enum ReplayState { - SLEEPING = 0, - LOADING, + enum ReplayState { + SLEEPING = 0, + LOADING, - PLAYING, - PAUSED, - STOPPED - }; + PLAYING, + PAUSED, + STOPPED + }; MusicClient(const std::shared_ptr&,const std::string&); virtual ~MusicClient() override; @@ -52,109 +52,109 @@ namespace ts { this->_this = _this; } - //Basic TeaSpeak stuff + //Basic TeaSpeak stuff void sendCommand(const ts::Command &command, bool low) override; - bool disconnect(const std::string &reason) override; + bool disconnect(const std::string &reason) override; bool closeConnection(const std::chrono::system_clock::time_point& = std::chrono::system_clock::time_point()) override; void initialize_bot(); - //Music stuff - inline Type::value get_bot_type() { return this->properties()[property::CLIENT_BOT_TYPE]; } - inline void set_bot_type(Type::value type) { this->properties()[property::CLIENT_BOT_TYPE] = type; } + //Music stuff + inline Type::value get_bot_type() { return this->properties()[property::CLIENT_BOT_TYPE]; } + inline void set_bot_type(Type::value type) { this->properties()[property::CLIENT_BOT_TYPE] = type; } - ClientDbId getOwner() { return this->properties()[property::CLIENT_OWNER]; } - ReplayState player_state() { return this->_player_state; } + ClientDbId getOwner() { return this->properties()[property::CLIENT_OWNER]; } + ReplayState player_state() { return this->_player_state; } - std::shared_ptr playlist() { return this->_playlist; } + std::shared_ptr playlist() { return this->_playlist; } - void player_reset(bool /* trigger change */); - void stopMusic(); - void playMusic(); - void player_pause(); + void player_reset(bool /* trigger change */); + void stopMusic(); + void playMusic(); + void player_pause(); void forwardSong(); - void rewindSong(); + void rewindSong(); - std::shared_ptr current_player(); - std::shared_ptr current_song(); + std::shared_ptr current_player(); + std::shared_ptr current_song(); float volumeModifier(){ return this->player.volume_modifier; } void volume_modifier(float vol); void add_subscriber(const std::shared_ptr&); - void remove_subscriber(const std::shared_ptr&); - bool is_subscriber(const std::shared_ptr&); + void remove_subscriber(const std::shared_ptr&); + bool is_subscriber(const std::shared_ptr&); protected: - public: - bool notifyClientMoved( - const std::shared_ptr &client, - const std::shared_ptr &target_channel, - ViewReasonId reason, - std::string msg, - std::shared_ptr invoker, - bool lock_channel_tree + public: + bool notifyClientMoved( + const std::shared_ptr &client, + const std::shared_ptr &target_channel, + ViewReasonId reason, + std::string msg, + std::shared_ptr invoker, + bool lock_channel_tree ) override; - protected: + protected: - void broadcast_text_message(const std::string &message); + void broadcast_text_message(const std::string &message); void tick(const std::chrono::system_clock::time_point &time) override; std::chrono::system_clock::time_point next_music_tick; void execute_music_tick(const std::shared_ptr&); - void schedule_music_tick(const std::unique_lock&, const std::shared_ptr&, const std::chrono::system_clock::time_point&, bool = false); - void schedule_stop_broadcast(); - void broadcast_music_stop(); /* only invoke while ticking */ + void schedule_music_tick(const std::unique_lock&, const std::shared_ptr&, const std::chrono::system_clock::time_point&, bool = false); + void schedule_stop_broadcast(); + void broadcast_music_stop(); /* only invoke while ticking */ void musicEventHandler(const std::weak_ptr&,::music::MusicEvent); - ReplayState _player_state; - std::recursive_timed_mutex current_song_lock; - std::recursive_timed_mutex current_song_tick_lock; - std::atomic_uint8_t current_song_lock_fail{0}; + ReplayState _player_state; + std::recursive_timed_mutex current_song_lock; + std::recursive_timed_mutex current_song_tick_lock; + std::atomic_uint8_t current_song_lock_fail{0}; - std::shared_ptr _current_song; + std::shared_ptr _current_song; std::shared_ptr _playlist; ts::music::MusicBotManager* manager = nullptr; struct { - bool last_frame_silence = true; - uint8_t frame_header = 0; + bool last_frame_silence = true; + uint8_t frame_header = 0; - OpusEncoder* encoder = nullptr; - uint16_t packet_id = 0; + OpusEncoder* encoder = nullptr; + uint16_t packet_id = 0; } voice; struct { - threads::tracking_id task = 0; - float volume_modifier = 1.f; + threads::tracking_id task = 0; + float volume_modifier = 1.f; } player; - std::mutex subscriber_lock; - std::deque, std::chrono::system_clock::time_point>> subscribers; + std::mutex subscriber_lock; + std::deque, std::chrono::system_clock::time_point>> subscribers; void apply_volume(const std::shared_ptr<::music::SampleSegment> &); - void replay_song(const std::shared_ptr &, const std::function&)>& /* loaded callback */ = NULL); + void replay_song(const std::shared_ptr &, const std::function&)>& /* loaded callback */ = NULL); - void changePlayerState(ReplayState state); - void notifySongChange(const std::shared_ptr&); + void changePlayerState(ReplayState state); + void notifySongChange(const std::shared_ptr&); - void handle_event_song_ended(); - void handle_event_song_replay_failed(); - void handle_event_song_dry(); /* repeating */ + void handle_event_song_ended(); + void handle_event_song_replay_failed(); + void handle_event_song_dry(); /* repeating */ - void replay_next_song(); + void replay_next_song(); - inline void set_playlist(const std::shared_ptr& playlist) { - this->properties()[property::CLIENT_PLAYLIST_ID] = playlist ? playlist->playlist_id() : 0; - this->_playlist = playlist; - if(playlist) - playlist->_current_bot = std::dynamic_pointer_cast(this->ref()); - } + inline void set_playlist(const std::shared_ptr& playlist) { + this->properties()[property::CLIENT_PLAYLIST_ID] = playlist ? playlist->playlist_id() : 0; + this->_playlist = playlist; + if(playlist) + playlist->_current_bot = std::dynamic_pointer_cast(this->ref()); + } }; } } diff --git a/server/src/client/music/MusicClientPlayer.cpp b/server/src/client/music/MusicClientPlayer.cpp index 06f1e0f..c41d237 100644 --- a/server/src/client/music/MusicClientPlayer.cpp +++ b/server/src/client/music/MusicClientPlayer.cpp @@ -18,284 +18,284 @@ using namespace ts::server; extern InstanceHandler* serverInstance; void MusicClient::player_reset(bool trigger_change) { - unique_lock song_lock(this->current_song_lock); - auto song = std::move(this->_current_song); - song_lock.unlock(); + unique_lock song_lock(this->current_song_lock); + auto song = std::move(this->_current_song); + song_lock.unlock(); - if(this->player.task > 0) - music::MusicBotManager::tick_music.cancelExecute(this->player.task); + if(this->player.task > 0) + music::MusicBotManager::tick_music.cancelExecute(this->player.task); - if(song) { - auto player = song->get_player(); - if(player) { - player->unregisterEventHandler(EVENT_HANDLER_NAME); - threads::Thread(THREAD_EXECUTE_LATER | THREAD_SAVE_OPERATIONS, [player]{ - player->stop(); //May hangs a littlebit up - }).name("song stopper").execute().detach(); - } + if(song) { + auto player = song->get_player(); + if(player) { + player->unregisterEventHandler(EVENT_HANDLER_NAME); + threads::Thread(THREAD_EXECUTE_LATER | THREAD_SAVE_OPERATIONS, [player]{ + player->stop(); //May hangs a littlebit up + }).name("song stopper").execute().detach(); + } - if(trigger_change) { - this->schedule_stop_broadcast(); - this->changePlayerState(ReplayState::SLEEPING); - this->_current_song = nullptr; - this->notifySongChange(nullptr); - } - } + if(trigger_change) { + this->schedule_stop_broadcast(); + this->changePlayerState(ReplayState::SLEEPING); + this->_current_song = nullptr; + this->notifySongChange(nullptr); + } + } } //Important: Handle the event first and the set sleeping because sleeping could cause a dry event #define REPLAY_ERROR(this, message) \ do { \ - this->player_reset(false); \ - this->handle_event_song_replay_failed(); \ - if(!this->current_song()) { \ + this->player_reset(false); \ + this->handle_event_song_replay_failed(); \ + if(!this->current_song()) { \ this->_player_state = ReplayState::SLEEPING; \ } \ - this->broadcast_text_message(message); \ - return; \ + this->broadcast_text_message(message); \ + return; \ } while(0) void MusicClient::replay_song(const shared_ptr &entry, const std::function&)>& loaded_callback) { - if(!entry) { - this->notifySongChange(nullptr); - this->changePlayerState(ReplayState::SLEEPING); - return; - } + if(!entry) { + this->notifySongChange(nullptr); + this->changePlayerState(ReplayState::SLEEPING); + return; + } - unique_lock song_lock(this->current_song_lock); - auto loader = entry->get_loader(this->getServer(), true); - if(!loader) REPLAY_ERROR(this, "An error occurrent while trying to replay the next song! (loader is empty)"); - this->changePlayerState(ReplayState::LOADING); - this->_current_song = entry; - song_lock.unlock(); + unique_lock song_lock(this->current_song_lock); + auto loader = entry->get_loader(this->getServer(), true); + if(!loader) REPLAY_ERROR(this, "An error occurrent while trying to replay the next song! (loader is empty)"); + this->changePlayerState(ReplayState::LOADING); + this->_current_song = entry; + song_lock.unlock(); - weak_ptr weak_self = dynamic_pointer_cast(this->ref()); - loader->waitAndGetLater([weak_self, loader, entry, loaded_callback](std::shared_ptr loaded_data){ - auto self = weak_self.lock(); - if(!self) return; + weak_ptr weak_self = dynamic_pointer_cast(this->ref()); + loader->waitAndGetLater([weak_self, loader, entry, loaded_callback](std::shared_ptr loaded_data){ + auto self = weak_self.lock(); + if(!self) return; - auto player = loaded_data ? loaded_data->player : nullptr; - //If song available this should be executed within a second thread - ts::music::MusicBotManager::tick_music.execute([weak_self, self, loader, entry, player, loaded_callback] { - if(self->current_song() != entry) { - debugMessage(self->getServerId(), "Music loaded but not anymore required! Dropping entry"); - return; - } + auto player = loaded_data ? loaded_data->player : nullptr; + //If song available this should be executed within a second thread + ts::music::MusicBotManager::tick_music.execute([weak_self, self, loader, entry, player, loaded_callback] { + if(self->current_song() != entry) { + debugMessage(self->getServerId(), "Music loaded but not anymore required! Dropping entry"); + return; + } - if(loader->succeeded() && player) { - if(!player->initialize(2)) //TODO Channel count dynamic - REPLAY_ERROR(self, "An error occurrent while trying to replay the next song! (Failed to initialize player. (" + player->error() + "))"); + if(loader->succeeded() && player) { + if(!player->initialize(2)) //TODO Channel count dynamic + REPLAY_ERROR(self, "An error occurrent while trying to replay the next song! (Failed to initialize player. (" + player->error() + "))"); - weak_ptr weak_song = entry; - player->registerEventHandler(EVENT_HANDLER_NAME, [weak_song, weak_self](::music::MusicEvent event) { - auto locked = weak_self.lock(); - if(locked) { - locked->musicEventHandler(weak_song, event); - } - }); - self->changePlayerState(ReplayState::PAUSED); + weak_ptr weak_song = entry; + player->registerEventHandler(EVENT_HANDLER_NAME, [weak_song, weak_self](::music::MusicEvent event) { + auto locked = weak_self.lock(); + if(locked) { + locked->musicEventHandler(weak_song, event); + } + }); + self->changePlayerState(ReplayState::PAUSED); - if(self->properties()[property::CLIENT_FLAG_NOTIFY_SONG_CHANGE].as()) { - string invoker = "unknown"; - { - auto info_list = serverInstance->databaseHelper()->queryDatabaseInfo(self->getServer(), {entry->getInvoker()}); - if(!info_list.empty()) { - auto info = info_list.front(); - invoker = "[URL=client://0/" + info->uniqueId + "~WolverinDEV]" + info->lastName + "[/URL]"; - } - } + if(self->properties()[property::CLIENT_FLAG_NOTIFY_SONG_CHANGE].as()) { + string invoker = "unknown"; + { + auto info_list = serverInstance->databaseHelper()->queryDatabaseInfo(self->getServer(), {entry->getInvoker()}); + if(!info_list.empty()) { + auto info = info_list.front(); + invoker = "[URL=client://0/" + info->uniqueId + "~WolverinDEV]" + info->lastName + "[/URL]"; + } + } - auto info = entry->song_loaded_data(); - if(info) { - auto message = strvar::transform(config::messages::music::song_announcement, - strvar::StringValue{"title", info->title}, - strvar::StringValue{"description", info->description}, - strvar::StringValue{"url", entry->getUrl()}, - strvar::StringValue{"invoker", invoker} - ); - self->broadcast_text_message(message); - } else { - auto message = strvar::transform(config::messages::music::song_announcement, + auto info = entry->song_loaded_data(); + if(info) { + auto message = strvar::transform(config::messages::music::song_announcement, + strvar::StringValue{"title", info->title}, + strvar::StringValue{"description", info->description}, + strvar::StringValue{"url", entry->getUrl()}, + strvar::StringValue{"invoker", invoker} + ); + self->broadcast_text_message(message); + } else { + auto message = strvar::transform(config::messages::music::song_announcement, strvar::StringValue{"title", "unknown title"}, strvar::StringValue{"description", "unknown"}, strvar::StringValue{"url", entry->getUrl()}, strvar::StringValue{"invoker", invoker} - ); - self->broadcast_text_message(message); - } - } - if(loaded_callback) - loaded_callback(player); - else - self->playMusic(); - } else { - if(loader->state() == threads::FutureState::WORKING) { - REPLAY_ERROR(self, "An error occurrent while trying to replay the next song! (Loader load timeout!)"); - } else { - REPLAY_ERROR(self, "Got an error while trying to load next song: " + loader->errorMegssage()); - } - } - }); - }, nullptr, system_clock::now() + seconds(30)); + ); + self->broadcast_text_message(message); + } + } + if(loaded_callback) + loaded_callback(player); + else + self->playMusic(); + } else { + if(loader->state() == threads::FutureState::WORKING) { + REPLAY_ERROR(self, "An error occurrent while trying to replay the next song! (Loader load timeout!)"); + } else { + REPLAY_ERROR(self, "Got an error while trying to load next song: " + loader->errorMegssage()); + } + } + }); + }, nullptr, system_clock::now() + seconds(30)); - this->notifySongChange(entry); + this->notifySongChange(entry); } void MusicClient::tick(const std::chrono::system_clock::time_point &time) { - ConnectedClient::tick(time); + ConnectedClient::tick(time); - if(this->_player_state == ReplayState::SLEEPING) - this->handle_event_song_dry(); - else if(!this->current_song()) { - auto playlist = this->playlist(); - if(playlist) { /* may bot just got initialized */ - auto song = playlist->pop_next(); - if(song) { - auto player_state = this->_player_state; - this->replay_song(song, [player_state](const shared_ptr<::music::MusicPlayer>& player){ - if(player_state == ReplayState::STOPPED) - player->stop(); - else if(player_state == ReplayState::PAUSED) - player->pause(); - else - player->play(); - }); - } - } - if(!playlist || !this->current_song()){ - this->changePlayerState(ReplayState::SLEEPING); - } - } + if(this->_player_state == ReplayState::SLEEPING) + this->handle_event_song_dry(); + else if(!this->current_song()) { + auto playlist = this->playlist(); + if(playlist) { /* may bot just got initialized */ + auto song = playlist->pop_next(); + if(song) { + auto player_state = this->_player_state; + this->replay_song(song, [player_state](const shared_ptr<::music::MusicPlayer>& player){ + if(player_state == ReplayState::STOPPED) + player->stop(); + else if(player_state == ReplayState::PAUSED) + player->pause(); + else + player->play(); + }); + } + } + if(!playlist || !this->current_song()){ + this->changePlayerState(ReplayState::SLEEPING); + } + } } std::shared_ptr MusicClient::current_song() { - unique_lock song_lock(this->current_song_lock, defer_lock_t{}); - if(!song_lock.try_lock_for(milliseconds(5))) - return nullptr; /* failed to acquire lock */ + unique_lock song_lock(this->current_song_lock, defer_lock_t{}); + if(!song_lock.try_lock_for(milliseconds(5))) + return nullptr; /* failed to acquire lock */ - return this->_current_song; + return this->_current_song; } std::shared_ptr<::music::MusicPlayer> MusicClient::current_player() { - auto song = this->current_song(); - return song ? song->get_player() : nullptr; + auto song = this->current_song(); + return song ? song->get_player() : nullptr; } void MusicClient::playMusic() { - auto self = dynamic_pointer_cast(_this.lock()); - ts::music::MusicBotManager::tick_music.execute([self]{ - auto playlist = self->playlist(); - if(playlist->properties()[property::PLAYLIST_FLAG_FINISHED].as()) { - playlist->properties()[property::PLAYLIST_FLAG_FINISHED] = false; - debugMessage(self->getServerId(), "{} Received play, but playlist had finished. Restarting playlist.", CLIENT_STR_LOG_PREFIX_(self)); - } + auto self = dynamic_pointer_cast(_this.lock()); + ts::music::MusicBotManager::tick_music.execute([self]{ + auto playlist = self->playlist(); + if(playlist->properties()[property::PLAYLIST_FLAG_FINISHED].as()) { + playlist->properties()[property::PLAYLIST_FLAG_FINISHED] = false; + debugMessage(self->getServerId(), "{} Received play, but playlist had finished. Restarting playlist.", CLIENT_STR_LOG_PREFIX_(self)); + } - auto player = self->current_player(); - if(player) - player->play(); - else - debugMessage(self->getServerId(), "Tried to start music without a music player!"); - }); + auto player = self->current_player(); + if(player) + player->play(); + else + debugMessage(self->getServerId(), "Tried to start music without a music player!"); + }); } void MusicClient::stopMusic() { - auto self = dynamic_pointer_cast(_this.lock()); - ts::music::MusicBotManager::tick_music.execute([self]{ - auto player = self->current_player(); - if(player) - player->stop(); - else - debugMessage(self->getServerId(), "Tried to stop music without a music player!"); - }); + auto self = dynamic_pointer_cast(_this.lock()); + ts::music::MusicBotManager::tick_music.execute([self]{ + auto player = self->current_player(); + if(player) + player->stop(); + else + debugMessage(self->getServerId(), "Tried to stop music without a music player!"); + }); } void MusicClient::player_pause() { - auto self = dynamic_pointer_cast(_this.lock()); - ts::music::MusicBotManager::tick_music.execute([self]{ - auto player = self->current_player(); - if(player) - player->pause(); - else - debugMessage(self->getServerId(), "Tried to pause music without a music player!"); - }); + auto self = dynamic_pointer_cast(_this.lock()); + ts::music::MusicBotManager::tick_music.execute([self]{ + auto player = self->current_player(); + if(player) + player->pause(); + else + debugMessage(self->getServerId(), "Tried to pause music without a music player!"); + }); } void MusicClient::forwardSong() { - auto song = this->current_song(); - this->handle_event_song_ended(); - if(song == this->current_song()) - this->player_reset(true); + auto song = this->current_song(); + this->handle_event_song_ended(); + if(song == this->current_song()) + this->player_reset(true); } void MusicClient::rewindSong() { - this->player_reset(true); - logError(this->getServerId(), "MusicClient::rewindSong hasn't been implemented yet!"); + this->player_reset(true); + logError(this->getServerId(), "MusicClient::rewindSong hasn't been implemented yet!"); } void MusicClient::musicEventHandler(const std::weak_ptr& weak_player, ::music::MusicEvent event) { - unique_lock song_lock(this->current_song_lock); - auto player = weak_player.lock(); - if(!player || player != this->_current_song) return; + unique_lock song_lock(this->current_song_lock); + auto player = weak_player.lock(); + if(!player || player != this->_current_song) return; - logTrace(this->server ? this->server->getServerId() : 0, "[{}] Got event " + to_string(event), CLIENT_STR_LOG_PREFIX); - if(event == ::music::EVENT_PLAY){ - debugMessage(this->getServerId(), "[MusicBot] Got play event from music player. Starting bot!"); + logTrace(this->server ? this->server->getServerId() : 0, "[{}] Got event " + to_string(event), CLIENT_STR_LOG_PREFIX); + if(event == ::music::EVENT_PLAY){ + debugMessage(this->getServerId(), "[MusicBot] Got play event from music player. Starting bot!"); - this->next_music_tick = system_clock::time_point(); - this->schedule_music_tick(song_lock, this->_current_song, this->next_music_tick); - song_lock.unlock(); + this->next_music_tick = system_clock::time_point(); + this->schedule_music_tick(song_lock, this->_current_song, this->next_music_tick); + song_lock.unlock(); - this->changePlayerState(ReplayState::PLAYING); - } else if(event == ::music::EVENT_PAUSE || event == ::music::EVENT_STOP){ - debugMessage(this->getServerId(), "[MusicBot] Got stop or pause event from player. Stopping bot!"); - music::MusicBotManager::tick_music.cancelExecute(this->player.task); - this->player.task = 0; - song_lock.unlock(); + this->changePlayerState(ReplayState::PLAYING); + } else if(event == ::music::EVENT_PAUSE || event == ::music::EVENT_STOP){ + debugMessage(this->getServerId(), "[MusicBot] Got stop or pause event from player. Stopping bot!"); + music::MusicBotManager::tick_music.cancelExecute(this->player.task); + this->player.task = 0; + song_lock.unlock(); - this->changePlayerState(event == ::music::EVENT_PAUSE ? ReplayState::PAUSED : ReplayState::STOPPED); - this->schedule_stop_broadcast(); - } else if(event == ::music::EVENT_END) { - debugMessage(this->getServerId(), "[MusicBot] Got end event from music player. Playing next song!"); - song_lock.unlock(); + this->changePlayerState(event == ::music::EVENT_PAUSE ? ReplayState::PAUSED : ReplayState::STOPPED); + this->schedule_stop_broadcast(); + } else if(event == ::music::EVENT_END) { + debugMessage(this->getServerId(), "[MusicBot] Got end event from music player. Playing next song!"); + song_lock.unlock(); - auto song = this->current_song(); - this->handle_event_song_ended(); - if(song == this->current_song()) - this->player_reset(true); - } + auto song = this->current_song(); + this->handle_event_song_ended(); + if(song == this->current_song()) + this->player_reset(true); + } } void MusicClient::volume_modifier(float vol) { - this->player.volume_modifier = vol; - this->properties()[property::CLIENT_PLAYER_VOLUME] = this->player.volume_modifier; - this->server->notifyClientPropertyUpdates(_this.lock(), std::deque{property::CLIENT_PLAYER_VOLUME}); + this->player.volume_modifier = vol; + this->properties()[property::CLIENT_PLAYER_VOLUME] = this->player.volume_modifier; + this->server->notifyClientPropertyUpdates(_this.lock(), std::deque{property::CLIENT_PLAYER_VOLUME}); } void MusicClient::schedule_music_tick(const unique_lock& song_lock, const std::shared_ptr &song, const std::chrono::system_clock::time_point& timepoint, bool ignore_lock) { - if(!ignore_lock) { - assert(song_lock.owns_lock() && song_lock.mutex() == &this->current_song_lock); - } - if(this->_current_song != song) return; //abort scheduling + if(!ignore_lock) { + assert(song_lock.owns_lock() && song_lock.mutex() == &this->current_song_lock); + } + if(this->_current_song != song) return; //abort scheduling - weak_ptr weak_self = dynamic_pointer_cast(this->ref()); - weak_ptr weak_song = song; - this->player.task = music::MusicBotManager::tick_music.executeLater([weak_self, weak_song] { - shared_ptr self = weak_self.lock(); - auto song = weak_song.lock(); + weak_ptr weak_self = dynamic_pointer_cast(this->ref()); + weak_ptr weak_song = song; + this->player.task = music::MusicBotManager::tick_music.executeLater([weak_self, weak_song] { + shared_ptr self = weak_self.lock(); + auto song = weak_song.lock(); - if(!self || !song) return; /* we're outdated */ + if(!self || !song) return; /* we're outdated */ - unique_lock song_tick_lock(self->current_song_tick_lock, try_to_lock_t{}); - if(!song_tick_lock.owns_lock()) { - if(self->current_song_lock_fail++ > 10) { + unique_lock song_tick_lock(self->current_song_tick_lock, try_to_lock_t{}); + if(!song_tick_lock.owns_lock()) { + if(self->current_song_lock_fail++ > 10) { logError(self->getServerId(), "[{}][Music] Failed to invoke next tick over 10 times. Ticking lock is still acquired. Breaking reschedule loop.", CLIENT_STR_LOG_PREFIX_(self)); return; } unique_lock song_lock(self->current_song_lock, try_to_lock_t{}); - /* + /* if(!song_lock) { logError(self->getServerId(), "[{}][Music] Failed to invoke next tick. Ticking and song lock is still acquired. Breaking loop", CLIENT_STR_LOG_PREFIX_(self)); } else { @@ -303,154 +303,154 @@ void MusicClient::schedule_music_tick(const unique_lock& logError(self->getServerId(), "[{}][Music] Failed to invoke next tick. Ticking lock is still acquired. Rescheduling in 20ms.", CLIENT_STR_LOG_PREFIX_(self)); self->schedule_music_tick(song_lock, song, system_clock::now() + milliseconds(20), true); //} - return; //Current tick is still going on - } + return; //Current tick is still going on + } self->current_song_lock_fail = 0; - self->execute_music_tick(song); - }, timepoint); + self->execute_music_tick(song); + }, timepoint); } void MusicClient::schedule_stop_broadcast() { - weak_ptr weak_self = dynamic_pointer_cast(this->ref()); + weak_ptr weak_self = dynamic_pointer_cast(this->ref()); - this->player.task = music::MusicBotManager::tick_music.executeLater([weak_self] { - auto self = weak_self.lock(); + this->player.task = music::MusicBotManager::tick_music.executeLater([weak_self] { + auto self = weak_self.lock(); - if(!self) return; /* we're outdated */ + if(!self) return; /* we're outdated */ - unique_lock song_tick_lock(self->current_song_tick_lock, defer_lock_t{}); - if(!song_tick_lock.try_lock_for(milliseconds(5))) { - logError(self->getServerId(), "[{}][Music] Failed to schedule stop. Ticking lock is still acquired.", CLIENT_STR_LOG_PREFIX_(self)); - return; //Current tick is still going on - } + unique_lock song_tick_lock(self->current_song_tick_lock, defer_lock_t{}); + if(!song_tick_lock.try_lock_for(milliseconds(5))) { + logError(self->getServerId(), "[{}][Music] Failed to schedule stop. Ticking lock is still acquired.", CLIENT_STR_LOG_PREFIX_(self)); + return; //Current tick is still going on + } - self->broadcast_music_stop(); - }, system_clock::now()); + self->broadcast_music_stop(); + }, system_clock::now()); } void MusicClient::broadcast_music_stop() { - if(this->voice.last_frame_silence) return; + if(this->voice.last_frame_silence) return; - size_t buffer_size = 750; - size_t voice_header_length = 5; - char voice_buffer[buffer_size]; + size_t buffer_size = 750; + size_t voice_header_length = 5; + char voice_buffer[buffer_size]; - this->voice.last_frame_silence = true; - le2be16(this->voice.packet_id++, voice_buffer, 0); - le2be16(this->getClientId(), voice_buffer, 2); - voice_buffer[4] = 5; //Voice type opus music + this->voice.last_frame_silence = true; + le2be16(this->voice.packet_id++, voice_buffer, 0); + le2be16(this->getClientId(), voice_buffer, 2); + voice_buffer[4] = 5; //Voice type opus music - SpeakingClient::VoicePacketFlags flags{}; - for(const auto& cl : this->server->getClientsByChannel(this->currentChannel)) - if(cl->shouldReceiveVoice(_this.lock())) - cl->send_voice_packet(pipes::buffer_view{voice_buffer, voice_header_length}, flags); + SpeakingClient::VoicePacketFlags flags{}; + for(const auto& cl : this->server->getClientsByChannel(this->currentChannel)) + if(cl->shouldReceiveVoice(_this.lock())) + cl->send_voice_packet(pipes::buffer_view{voice_buffer, voice_header_length}, flags); } void MusicClient::execute_music_tick(const shared_ptr& song) { - unique_lock song_lock(this->current_song_lock); - if(this->_current_song != song) return; /* old music */ - - auto player = song->get_player(); - if(!player || player->state() != ::music::STATE_PLAYING) return; - song_lock.unlock(); + unique_lock song_lock(this->current_song_lock); + if(this->_current_song != song) return; /* old music */ + + auto player = song->get_player(); + if(!player || player->state() != ::music::STATE_PLAYING) return; + song_lock.unlock(); - system_clock::time_point next_schedule; - { - if(this->next_music_tick.time_since_epoch().count() == 0) - this->next_music_tick = system_clock::now(); - else if(this->next_music_tick + milliseconds(10) < system_clock::now()) { - logWarning(this->getServerId(), "[{}] Resend task contained a delay of over 10ms! ({}ms)", CLIENT_STR_LOG_PREFIX, duration_cast(system_clock::now() - this->next_music_tick).count()); - } - if(this->next_music_tick + milliseconds(20) < system_clock::now()) { - this->next_music_tick = system_clock::now(); - } else - this->next_music_tick += milliseconds(20); + system_clock::time_point next_schedule; + { + if(this->next_music_tick.time_since_epoch().count() == 0) + this->next_music_tick = system_clock::now(); + else if(this->next_music_tick + milliseconds(10) < system_clock::now()) { + logWarning(this->getServerId(), "[{}] Resend task contained a delay of over 10ms! ({}ms)", CLIENT_STR_LOG_PREFIX, duration_cast(system_clock::now() - this->next_music_tick).count()); + } + if(this->next_music_tick + milliseconds(20) < system_clock::now()) { + this->next_music_tick = system_clock::now(); + } else + this->next_music_tick += milliseconds(20); - next_schedule = this->next_music_tick; - } - auto timestamp_begin = system_clock::now(); + next_schedule = this->next_music_tick; + } + auto timestamp_begin = system_clock::now(); - size_t buffer_size = 750; - size_t voice_header_length = 5; - char voice_buffer[buffer_size]; - auto next_segment = player->popNextSegment(); + size_t buffer_size = 750; + size_t voice_header_length = 5; + char voice_buffer[buffer_size]; + auto next_segment = player->popNextSegment(); - if(next_segment) { - this->apply_volume(next_segment); - ssize_t length = 0; - if(next_segment->segmentLength == player->preferredSampleCount()) - length = opus_encode(this->voice.encoder, next_segment->segments, next_segment->segmentLength, reinterpret_cast(&voice_buffer[voice_header_length]), buffer_size - voice_header_length); - if(length <= 0) { - logError(this->getServerId(), "[{}] opus_encode(...) returns invalid code: {}", CLIENT_STR_LOG_PREFIX, length); - } else { - le2be16(this->voice.packet_id++, voice_buffer, 0); - le2be16(this->getClientId(), voice_buffer, 2); - voice_buffer[4] = 5; //Voice type opus music + if(next_segment) { + this->apply_volume(next_segment); + ssize_t length = 0; + if(next_segment->segmentLength == player->preferredSampleCount()) + length = opus_encode(this->voice.encoder, next_segment->segments, next_segment->segmentLength, reinterpret_cast(&voice_buffer[voice_header_length]), buffer_size - voice_header_length); + if(length <= 0) { + logError(this->getServerId(), "[{}] opus_encode(...) returns invalid code: {}", CLIENT_STR_LOG_PREFIX, length); + } else { + le2be16(this->voice.packet_id++, voice_buffer, 0); + le2be16(this->getClientId(), voice_buffer, 2); + voice_buffer[4] = 5; //Voice type opus music - SpeakingClient::VoicePacketFlags flags{}; + SpeakingClient::VoicePacketFlags flags{}; - /* test if we've to recover from silence */ - if(this->voice.last_frame_silence) { - this->voice.frame_header = 5; - this->voice.last_frame_silence = false; - } - if(this->voice.frame_header > 0) { - this->voice.frame_header--; - flags.head = true; - } + /* test if we've to recover from silence */ + if(this->voice.last_frame_silence) { + this->voice.frame_header = 5; + this->voice.last_frame_silence = false; + } + if(this->voice.frame_header > 0) { + this->voice.frame_header--; + flags.head = true; + } - auto buffer = pipes::buffer_view{voice_buffer, voice_header_length + length}; - for(const auto& cl : this->server->getClientsByChannel(this->currentChannel)) - if(cl->shouldReceiveVoice(_this.lock())) - cl->send_voice_packet(buffer, flags); - } - } else { - this->broadcast_music_stop(); - } - auto timestamp_begin_subscriber = system_clock::now(); + auto buffer = pipes::buffer_view{voice_buffer, voice_header_length + length}; + for(const auto& cl : this->server->getClientsByChannel(this->currentChannel)) + if(cl->shouldReceiveVoice(_this.lock())) + cl->send_voice_packet(buffer, flags); + } + } else { + this->broadcast_music_stop(); + } + auto timestamp_begin_subscriber = system_clock::now(); - { - auto now = system_clock::now(); + { + auto now = system_clock::now(); - std::deque> subs; - { - lock_guard sub_lock(this->subscriber_lock); - for(auto& sub : this->subscribers) { - auto& time_point = std::get<1>(sub); - if(time_point > now) continue; - if(time_point.time_since_epoch().count() == 0) - time_point = now; - time_point = time_point + seconds(1); /* TODO allow this interval to be configurable */ + std::deque> subs; + { + lock_guard sub_lock(this->subscriber_lock); + for(auto& sub : this->subscribers) { + auto& time_point = std::get<1>(sub); + if(time_point > now) continue; + if(time_point.time_since_epoch().count() == 0) + time_point = now; + time_point = time_point + seconds(1); /* TODO allow this interval to be configurable */ - auto client = std::get<0>(sub).lock(); - if(!client) continue; + auto client = std::get<0>(sub).lock(); + if(!client) continue; - subs.push_back(std::move(client)); - } - } + subs.push_back(std::move(client)); + } + } - auto self = dynamic_pointer_cast(this->ref()); - for(const auto& sub : subs) - sub->notifyMusicPlayerStatusUpdate(self); - } - auto timestamp_begin_schedule = system_clock::now(); + auto self = dynamic_pointer_cast(this->ref()); + for(const auto& sub : subs) + sub->notifyMusicPlayerStatusUpdate(self); + } + auto timestamp_begin_schedule = system_clock::now(); - song_lock.lock(); - if(player->state() != ::music::STATE_PLAYING) return; /* status had been changed */ - this->schedule_music_tick(song_lock, song, next_schedule); - auto timestamp_end = system_clock::now(); - if(timestamp_end - timestamp_begin > milliseconds(5)) { - logWarning(this->getServerId(), "[{}] Ticking used more than 5ms to proceed ({}ms). Details: encode={},subscriber={},schedule={}.", - CLIENT_STR_LOG_PREFIX, - duration_cast(timestamp_end - timestamp_begin).count(), + song_lock.lock(); + if(player->state() != ::music::STATE_PLAYING) return; /* status had been changed */ + this->schedule_music_tick(song_lock, song, next_schedule); + auto timestamp_end = system_clock::now(); + if(timestamp_end - timestamp_begin > milliseconds(5)) { + logWarning(this->getServerId(), "[{}] Ticking used more than 5ms to proceed ({}ms). Details: encode={},subscriber={},schedule={}.", + CLIENT_STR_LOG_PREFIX, + duration_cast(timestamp_end - timestamp_begin).count(), - duration_cast(timestamp_begin_subscriber - timestamp_begin).count(), - duration_cast(timestamp_begin_schedule - timestamp_begin_subscriber).count(), - duration_cast(timestamp_end - timestamp_begin_schedule).count() - ); - } + duration_cast(timestamp_begin_subscriber - timestamp_begin).count(), + duration_cast(timestamp_begin_schedule - timestamp_begin_subscriber).count(), + duration_cast(timestamp_end - timestamp_begin_schedule).count() + ); + } } diff --git a/server/src/client/music/MusicQueue.cpp b/server/src/client/music/MusicQueue.cpp index 3b26b65..1001cf6 100644 --- a/server/src/client/music/MusicQueue.cpp +++ b/server/src/client/music/MusicQueue.cpp @@ -10,122 +10,122 @@ using namespace std; using namespace std::chrono; MusicQueue::MusicQueue(ts::server::MusicClient* self) { - this->handle = self; + this->handle = self; } MusicQueue::~MusicQueue() {} std::shared_ptr MusicQueue::currentSong() { - return this->current; + return this->current; } void MusicQueue::notifySongEnded() { - if(this->currentSong()) { - this->getNextSong(); - } + if(this->currentSong()) { + this->getNextSong(); + } } void MusicQueue::notifySongAbort() { - this->getNextSong(); //TODO retry count! + this->getNextSong(); //TODO retry count! } std::shared_ptr MusicQueue::rewind() { - threads::MutexLock lock(this->lock); + threads::MutexLock lock(this->lock); - if(this->current) - this->queue.push_front(std::move(this->current)); - if(!this->_history.empty()) { - this->current = dynamic_pointer_cast(this->_history.front()); - this->_history.pop_front(); - } + if(this->current) + this->queue.push_front(std::move(this->current)); + if(!this->_history.empty()) { + this->current = dynamic_pointer_cast(this->_history.front()); + this->_history.pop_front(); + } - return this->current; + return this->current; } std::shared_ptr MusicQueue::getNextSong() { - threads::MutexLock lock(this->lock); - if(this->current) { - this->_history.push_front(this->current); - for(int index = 10; index < this->_history.size(); index++) { //Last ten songs full cache! - if(!dynamic_pointer_cast(this->_history[index])) break; + threads::MutexLock lock(this->lock); + if(this->current) { + this->_history.push_front(this->current); + for(int index = 10; index < this->_history.size(); index++) { //Last ten songs full cache! + if(!dynamic_pointer_cast(this->_history[index])) break; - auto element = this->_history[index]; - this->_history[index] = make_shared(element->getUrl(), element->getInvoker(), element->getSongId()); - } - } + auto element = this->_history[index]; + this->_history[index] = make_shared(element->getUrl(), element->getInvoker(), element->getSongId()); + } + } - if(this->queue.empty()) this->current = nullptr; - else { - this->current = this->queue.front(); - this->queue.pop_front(); - } - return this->current; + if(this->queue.empty()) this->current = nullptr; + else { + this->current = this->queue.front(); + this->queue.pop_front(); + } + return this->current; } std::shared_ptr MusicQueue::find_queue(ts::SongId id) { - threads::MutexLock lock(this->lock); - for(const auto& element : this->queue) - if(element->getSongId() == id) - return element; - return nullptr; + threads::MutexLock lock(this->lock); + for(const auto& element : this->queue) + if(element->getSongId() == id) + return element; + return nullptr; } std::shared_ptr MusicQueue::find(ts::SongId id) { - threads::MutexLock lock(this->lock); - for(const auto& element : this->queue) - if(element->getSongId() == id) - return element; - for(const auto& element : this->_history) - if(element->getSongId() == id) - return element; - if(this->current && this->current->getSongId() == id) - return this->current; + threads::MutexLock lock(this->lock); + for(const auto& element : this->queue) + if(element->getSongId() == id) + return element; + for(const auto& element : this->_history) + if(element->getSongId() == id) + return element; + if(this->current && this->current->getSongId() == id) + return this->current; - return nullptr; + return nullptr; } std::deque> MusicQueue::queueEntries() { - return this->queue; + return this->queue; } std::deque> MusicQueue::history() { - return this->_history; + return this->_history; } bool MusicQueue::deleteEntry(const std::shared_ptr& entry) { - if(!entry) return false; + if(!entry) return false; - threads::MutexLock lock(this->lock); - auto found = std::find(this->queue.begin(), this->queue.end(), entry); - if(found == this->queue.end()) return false; - this->queue.erase(found); - return true; + threads::MutexLock lock(this->lock); + auto found = std::find(this->queue.begin(), this->queue.end(), entry); + if(found == this->queue.end()) return false; + this->queue.erase(found); + return true; } int MusicQueue::changeOrder(const std::shared_ptr& entry, int position) { - threads::MutexLock lock(this->lock); - auto found = std::find(this->queue.begin(), this->queue.end(), entry); - if(found == this->queue.end()) return -1; - this->queue.erase(found); + threads::MutexLock lock(this->lock); + auto found = std::find(this->queue.begin(), this->queue.end(), entry); + if(found == this->queue.end()) return -1; + this->queue.erase(found); - if(position < 0) position = (int) this->queue.size(); - if(position > this->queue.size()) position = (int) this->queue.size(); - this->queue.insert(this->queue.begin(), entry); - return position; + if(position < 0) position = (int) this->queue.size(); + if(position > this->queue.size()) position = (int) this->queue.size(); + this->queue.insert(this->queue.begin(), entry); + return position; } std::shared_ptr MusicQueue::insertEntry(const std::string &url, const tsclient &invoker, const std::shared_ptr &loader) { - auto entry = PlayableSong::create({ - 0, - url, - invoker ? invoker->getClientDatabaseId() : 0 - }, loader); - { - entry->set_song_id(this->song_id_index++); - threads::MutexLock lock(this->lock); - this->queue.push_back(entry); - } + auto entry = PlayableSong::create({ + 0, + url, + invoker ? invoker->getClientDatabaseId() : 0 + }, loader); + { + entry->set_song_id(this->song_id_index++); + threads::MutexLock lock(this->lock); + this->queue.push_back(entry); + } - entry->get_loader(this->handle->getServer(), true); - return entry; + entry->get_loader(this->handle->getServer(), true); + return entry; } \ No newline at end of file diff --git a/server/src/client/music/MusicQueue.h b/server/src/client/music/MusicQueue.h index 3b7daad..1cb5646 100644 --- a/server/src/client/music/MusicQueue.h +++ b/server/src/client/music/MusicQueue.h @@ -7,55 +7,55 @@ #include namespace ts { - namespace server { - class MusicClient; - } + namespace server { + class MusicClient; + } - namespace music { - class MusicHistoryEntry : public SongInfo { - public: - MusicHistoryEntry(std::string url, ClientDbId invoker, ts::SongId songId) : url(std::move(url)), invoker(invoker), id(songId) {} + namespace music { + class MusicHistoryEntry : public SongInfo { + public: + MusicHistoryEntry(std::string url, ClientDbId invoker, ts::SongId songId) : url(std::move(url)), invoker(invoker), id(songId) {} - std::string getUrl() const override { return this->url; } - ts::ClientDbId getInvoker() const override { return this->invoker; } - ts::SongId getSongId() const override { return this->id; } - private: - ts::SongId id; - std::string url; - ts::ClientDbId invoker; - }; + std::string getUrl() const override { return this->url; } + ts::ClientDbId getInvoker() const override { return this->invoker; } + ts::SongId getSongId() const override { return this->id; } + private: + ts::SongId id; + std::string url; + ts::ClientDbId invoker; + }; - class MusicQueue { - public: - MusicQueue(server::MusicClient*); - ~MusicQueue(); + class MusicQueue { + public: + MusicQueue(server::MusicClient*); + ~MusicQueue(); - std::shared_ptr currentSong(); - std::deque> queueEntries(); - std::deque> history(); //near to past + std::shared_ptr currentSong(); + std::deque> queueEntries(); + std::deque> history(); //near to past - std::shared_ptr rewind(); - std::shared_ptr getNextSong(); - std::shared_ptr find_queue(ts::SongId); - std::shared_ptr find(ts::SongId); + std::shared_ptr rewind(); + std::shared_ptr getNextSong(); + std::shared_ptr find_queue(ts::SongId); + std::shared_ptr find(ts::SongId); - bool deleteEntry(const std::shared_ptr&); - int changeOrder(const std::shared_ptr&, int position); + bool deleteEntry(const std::shared_ptr&); + int changeOrder(const std::shared_ptr&, int position); - std::shared_ptr insertEntry(const std::string& url, const tsclient& invoker, const std::shared_ptr& loader = nullptr); + std::shared_ptr insertEntry(const std::string& url, const tsclient& invoker, const std::shared_ptr& loader = nullptr); - void notifySongEnded(); - void notifySongAbort(); - private: - server::MusicClient* handle; + void notifySongEnded(); + void notifySongAbort(); + private: + server::MusicClient* handle; - threads::Mutex lock; - std::shared_ptr current; - std::deque> queue; - uint32_t song_id_index = 1; + threads::Mutex lock; + std::shared_ptr current; + std::deque> queue; + uint32_t song_id_index = 1; - std::deque> _history; - }; - } + std::deque> _history; + }; + } } \ No newline at end of file diff --git a/server/src/client/music/Song.cpp b/server/src/client/music/Song.cpp index aa5c9b5..fba98da 100644 --- a/server/src/client/music/Song.cpp +++ b/server/src/client/music/Song.cpp @@ -12,51 +12,51 @@ using namespace std::chrono; PlayableSong::~PlayableSong() = default; shared_ptr PlayableSong::get_loader(const std::shared_ptr& server, bool spawn_new) { - if(!spawn_new || this->_loader_future) return this->_loader_future; + if(!spawn_new || this->_loader_future) return this->_loader_future; - this->_loader_future = make_shared(); + this->_loader_future = make_shared(); - auto ref = this->ref(); - auto future = this->_loader_future; - auto function = this->_loader_function; + auto ref = this->ref(); + auto future = this->_loader_future; + auto function = this->_loader_function; - if(function && ref) { - weak_ptr weak_server = server; - /* async loading */ - std::thread([weak_server, ref, future, function]{ - auto server = weak_server.lock(); - if(!server) { - future->executionFailed("broken server reference"); - return; - } - string error; - try { - auto result = (*function)(server, ref, error); - if(result) { - ref->data_loaded = result; - future->executionSucceed(std::move(result)); - } else { - if(error.empty()) - error = "empty result"; + if(function && ref) { + weak_ptr weak_server = server; + /* async loading */ + std::thread([weak_server, ref, future, function]{ + auto server = weak_server.lock(); + if(!server) { + future->executionFailed("broken server reference"); + return; + } + string error; + try { + auto result = (*function)(server, ref, error); + if(result) { + ref->data_loaded = result; + future->executionSucceed(std::move(result)); + } else { + if(error.empty()) + error = "empty result"; - future->executionFailed(error); - } - } catch(std::exception& ex) { - future->executionFailed("Got an error while loading song. (" + string(ex.what()) + ")"); - } - /* - //Never throw non std::exception exceptions! - catch(...) { + future->executionFailed(error); + } + } catch(std::exception& ex) { + future->executionFailed("Got an error while loading song. (" + string(ex.what()) + ")"); + } + /* + //Never throw non std::exception exceptions! + catch(...) { - } - */ - }).detach(); - } else { - if(ref) - this->_loader_future->executionFailed("missing loader function"); - else - this->_loader_future->executionFailed("self lock expired"); - } + } + */ + }).detach(); + } else { + if(ref) + this->_loader_future->executionFailed("missing loader function"); + else + this->_loader_future->executionFailed("self lock expired"); + } - return this->_loader_future; + return this->_loader_future; } \ No newline at end of file diff --git a/server/src/client/music/Song.h b/server/src/client/music/Song.h index 359a690..2bcb2c7 100644 --- a/server/src/client/music/Song.h +++ b/server/src/client/music/Song.h @@ -8,87 +8,87 @@ #include namespace ts { - namespace server { - class TSServer; - } + namespace server { + class TSServer; + } - namespace music { - using MusicPlayer = ::music::MusicPlayer; + namespace music { + using MusicPlayer = ::music::MusicPlayer; - class SongInfo { - public: - virtual ts::SongId getSongId() const = 0; - virtual std::string getUrl() const = 0; - virtual ts::ClientDbId getInvoker() const = 0; - }; + class SongInfo { + public: + virtual ts::SongId getSongId() const = 0; + virtual std::string getUrl() const = 0; + virtual ts::ClientDbId getInvoker() const = 0; + }; - class PlayableSong : public SongInfo { - public: - struct StaticData { - ts::SongId _song_id; - std::string _song_url; - ts::ClientDbId _song_invoker; - }; + class PlayableSong : public SongInfo { + public: + struct StaticData { + ts::SongId _song_id; + std::string _song_url; + ts::ClientDbId _song_invoker; + }; - struct LoadedData { - std::string title; - std::string description; - std::string thumbnail; - ::music::PlayerUnits length = ::music::PlayerUnits(0); + struct LoadedData { + std::string title; + std::string description; + std::string thumbnail; + ::music::PlayerUnits length = ::music::PlayerUnits(0); - std::shared_ptr player; - }; + std::shared_ptr player; + }; - typedef std::function(const std::shared_ptr& /* server */, const std::shared_ptr& entry, std::string& error)> song_loader_t; - typedef threads::Future> song_future_t; + typedef std::function(const std::shared_ptr& /* server */, const std::shared_ptr& entry, std::string& error)> song_loader_t; + typedef threads::Future> song_future_t; - public: - inline static std::shared_ptr create(StaticData data, std::shared_ptr _loaderFunction) { - struct shared_allocator : public PlayableSong { }; + public: + inline static std::shared_ptr create(StaticData data, std::shared_ptr _loaderFunction) { + struct shared_allocator : public PlayableSong { }; - auto object = std::make_shared(); - object->_self = object; - object->_loader_function = std::move(_loaderFunction); - object->data_static = std::move(data); - return object; - } + auto object = std::make_shared(); + object->_self = object; + object->_loader_function = std::move(_loaderFunction); + object->data_static = std::move(data); + return object; + } - virtual ~PlayableSong(); + virtual ~PlayableSong(); - inline std::shared_ptr song_loader() { return this->_loader_function; } - std::shared_ptr get_loader(const std::shared_ptr&, bool /* create if not exists */ = true); - inline std::shared_ptr get_player() { - auto data = this->song_loaded_data(); - if(!data) return nullptr; + inline std::shared_ptr song_loader() { return this->_loader_function; } + std::shared_ptr get_loader(const std::shared_ptr&, bool /* create if not exists */ = true); + inline std::shared_ptr get_player() { + auto data = this->song_loaded_data(); + if(!data) return nullptr; - return data->player; - } + return data->player; + } - std::string getUrl() const override { return this->data_static._song_url; } - ts::ClientDbId getInvoker() const override { return this->data_static._song_invoker; } - ts::SongId getSongId() const override { return this->data_static._song_id; } + std::string getUrl() const override { return this->data_static._song_url; } + ts::ClientDbId getInvoker() const override { return this->data_static._song_invoker; } + ts::SongId getSongId() const override { return this->data_static._song_id; } - inline bool song_loaded() const { return this->data_loaded != nullptr; } - inline std::shared_ptr song_loaded_data() const { return this->data_loaded; } - inline std::shared_ptr ref() { return this->_self.lock(); } + inline bool song_loaded() const { return this->data_loaded != nullptr; } + inline std::shared_ptr song_loaded_data() const { return this->data_loaded; } + inline std::shared_ptr ref() { return this->_self.lock(); } - inline void set_song_id(SongId id) { - this->data_static._song_id = id; - } - private: - PlayableSong() = default; + inline void set_song_id(SongId id) { + this->data_static._song_id = id; + } + private: + PlayableSong() = default; - /* general */ - std::weak_ptr _self; + /* general */ + std::weak_ptr _self; - /* static song data */ - StaticData data_static; - std::shared_ptr data_loaded; + /* static song data */ + StaticData data_static; + std::shared_ptr data_loaded; - /* loader things */ - std::shared_ptr _loader_function; - std::shared_ptr _loader_future; + /* loader things */ + std::shared_ptr _loader_function; + std::shared_ptr _loader_future; - }; - } + }; + } } \ No newline at end of file diff --git a/server/src/client/music/internal_provider/channel_replay/ChannelProvider.cpp b/server/src/client/music/internal_provider/channel_replay/ChannelProvider.cpp index a35eebd..8b4457f 100644 --- a/server/src/client/music/internal_provider/channel_replay/ChannelProvider.cpp +++ b/server/src/client/music/internal_provider/channel_replay/ChannelProvider.cpp @@ -15,165 +15,165 @@ using namespace ts; using namespace ts::server; struct AudioFileInfo { - std::string absolute_path; - std::string description; + std::string absolute_path; + std::string description; }; extern ts::server::InstanceHandler* serverInstance; threads::Future> ChannelProvider::createPlayer(const std::string &url, void*, void* ptr_server) { - auto server = ((TSServer*) ptr_server)->ref(); - threads::Future> future; + auto server = ((TSServer*) ptr_server)->ref(); + threads::Future> future; - if(server) { - std::thread([future, server, url, ptr_server]{ - auto f_server = serverInstance->getFileServer(); - if(!f_server) { - future.executionFailed("instance missing file server"); - return; - } + if(server) { + std::thread([future, server, url, ptr_server]{ + auto f_server = serverInstance->getFileServer(); + if(!f_server) { + future.executionFailed("instance missing file server"); + return; + } - Command command(""); - try { - command = Command::parse(pipes::buffer_view{url.data(), url.length()}, false); - } catch(std::exception& ex) { - future.executionFailed("failed to parse data"); - return; - } + Command command(""); + try { + command = Command::parse(pipes::buffer_view{url.data(), url.length()}, false); + } catch(std::exception& ex) { + future.executionFailed("failed to parse data"); + return; + } - try { - auto channel_id = command["cid"].as(); - auto name = command["name"].string(); - auto path = command["path"].string(); + try { + auto channel_id = command["cid"].as(); + auto name = command["name"].string(); + auto path = command["path"].string(); - auto channel = server->getChannelTree()->findChannel(channel_id); - if(!channel) { - future.executionFailed("could not find target channel"); - return; - } + auto channel = server->getChannelTree()->findChannel(channel_id); + if(!channel) { + future.executionFailed("could not find target channel"); + return; + } - auto directory = f_server->resolveDirectory(server, channel, path); - if(!directory) { - future.executionFailed("invalid file path"); - return; - } + auto directory = f_server->resolveDirectory(server, channel, path); + if(!directory) { + future.executionFailed("invalid file path"); + return; + } - auto file = f_server->findFile(name, directory); - if(!file) { - future.executionFailed("could not find file"); - return; - } + auto file = f_server->findFile(name, directory); + if(!file) { + future.executionFailed("could not find file"); + return; + } - if(file->type != file::FileType::FILE) { - future.executionFailed("file isnt a file"); - return; - } + if(file->type != file::FileType::FILE) { + future.executionFailed("file isnt a file"); + return; + } - auto info = make_shared(); - info->absolute_path = file->path + "/" + file->name; - info->description = file->name; + auto info = make_shared(); + info->absolute_path = file->path + "/" + file->name; + info->description = file->name; - auto ffmpeg = ::music::manager::resolveProvider("FFMpeg", ""); - if(!ffmpeg) { - future.executionFailed("missing ffmpeg"); - return; - } + auto ffmpeg = ::music::manager::resolveProvider("FFMpeg", ""); + if(!ffmpeg) { + future.executionFailed("missing ffmpeg"); + return; + } - { - auto ffmpeg_data = (::music::FFMpegData::FileReplay*) malloc(sizeof(::music::FFMpegData::FileReplay)); - if(!ffmpeg_data) { - future.executionFailed("failed to allocate memory"); - return; - } + { + auto ffmpeg_data = (::music::FFMpegData::FileReplay*) malloc(sizeof(::music::FFMpegData::FileReplay)); + if(!ffmpeg_data) { + future.executionFailed("failed to allocate memory"); + return; + } - ffmpeg_data->version = ::music::FFMpegData::CURRENT_VERSION; - ffmpeg_data->type = ::music::FFMpegData::REPLAY_FILE; - ffmpeg_data->_free = ::free; + ffmpeg_data->version = ::music::FFMpegData::CURRENT_VERSION; + ffmpeg_data->type = ::music::FFMpegData::REPLAY_FILE; + ffmpeg_data->_free = ::free; - ffmpeg_data->file_path = (char*) malloc(info->absolute_path.length() + 1); - ffmpeg_data->file_path[info->absolute_path.length()] = '\0'; - ffmpeg_data->file_description = (char*) malloc(info->description.length() + 1); - ffmpeg_data->file_description[info->description.length()] = '\0'; + ffmpeg_data->file_path = (char*) malloc(info->absolute_path.length() + 1); + ffmpeg_data->file_path[info->absolute_path.length()] = '\0'; + ffmpeg_data->file_description = (char*) malloc(info->description.length() + 1); + ffmpeg_data->file_description[info->description.length()] = '\0'; - memcpy(ffmpeg_data->file_path, info->absolute_path.data(), info->absolute_path.length()); - memcpy(ffmpeg_data->file_description, info->description.data(), info->description.length()); + memcpy(ffmpeg_data->file_path, info->absolute_path.data(), info->absolute_path.length()); + memcpy(ffmpeg_data->file_description, info->description.data(), info->description.length()); - auto p = ffmpeg->createPlayer("", ffmpeg_data, ptr_server); - p.wait(); - if(p.failed()) - future.executionFailed(p.errorMegssage()); - else - future.executionSucceed(*p.get()); - } - return; - } catch(std::exception& ex) { - future.executionFailed("failed to process data"); - return; - } - }).detach(); - } else { - future.executionFailed("invalid bot"); - } + auto p = ffmpeg->createPlayer("", ffmpeg_data, ptr_server); + p.wait(); + if(p.failed()) + future.executionFailed(p.errorMegssage()); + else + future.executionSucceed(*p.get()); + } + return; + } catch(std::exception& ex) { + future.executionFailed("failed to process data"); + return; + } + }).detach(); + } else { + future.executionFailed("invalid bot"); + } - return future; + return future; } bool ChannelProvider::acceptString(const std::string &str) { - if(str.find("cid=") == string::npos) return false; - if(str.find("path=") == string::npos) return false; - if(str.find("name=") == string::npos) return false; + if(str.find("cid=") == string::npos) return false; + if(str.find("path=") == string::npos) return false; + if(str.find("name=") == string::npos) return false; - return true; + return true; } vector ChannelProvider::availableFormats() { - return {}; + return {}; } vector ChannelProvider::availableProtocols() { - return {"query"}; + return {"query"}; } std::shared_ptr<::music::manager::PlayerProvider> ChannelProvider::create_provider() { - /* - std::shared_ptr config = make_shared(); + /* + std::shared_ptr config = make_shared(); - { - auto config_path = fs::u8path("providers/config_channel.ini"); - music::log::log(music::log::debug, "[YT-DL] Using config file located at " + config_path.string()); - if(fs::exists(config_path)) { - INIReader ini_reader(config_path.string()); + { + auto config_path = fs::u8path("providers/config_channel.ini"); + music::log::log(music::log::debug, "[YT-DL] Using config file located at " + config_path.string()); + if(fs::exists(config_path)) { + INIReader ini_reader(config_path.string()); - if(ini_reader.ParseError()) { - music::log::log(music::log::err, "[YT-DL] Could not parse config! Using default values"); - } else { - config->youtubedl_command = ini_reader.Get("general", "youtubedl_command", config->youtubedl_command); - config->commands.version = ini_reader.Get("commands", "version", config->commands.version); - config->commands.query_video = ini_reader.Get("commands", "query_video", config->commands.query_video); - music::log::log(music::log::info, "[YT-DL] Config successfully loaded"); - } - } else { - music::log::log(music::log::debug, "[YT-DL] Missing configuration file. Using default values"); - } - } - */ + if(ini_reader.ParseError()) { + music::log::log(music::log::err, "[YT-DL] Could not parse config! Using default values"); + } else { + config->youtubedl_command = ini_reader.Get("general", "youtubedl_command", config->youtubedl_command); + config->commands.version = ini_reader.Get("commands", "version", config->commands.version); + config->commands.query_video = ini_reader.Get("commands", "query_video", config->commands.query_video); + music::log::log(music::log::info, "[YT-DL] Config successfully loaded"); + } + } else { + music::log::log(music::log::debug, "[YT-DL] Missing configuration file. Using default values"); + } + } + */ - return std::shared_ptr(new ChannelProvider(), [](ChannelProvider* provider){ - if(!provider) return; - delete provider; - }); + return std::shared_ptr(new ChannelProvider(), [](ChannelProvider* provider){ + if(!provider) return; + delete provider; + }); } threads::Future> ChannelProvider::query_info(const std::string &string, void *pVoid, void *pVoid1) { - auto info = make_shared(); + auto info = make_shared(); - info->type = UrlType::TYPE_VIDEO; - info->url = string; - info->title = ""; - info->description = ""; - info->metadata = {}; + info->type = UrlType::TYPE_VIDEO; + info->url = string; + info->title = ""; + info->description = ""; + info->metadata = {}; - threads::Future> result; - result.executionSucceed(info); - return result; + threads::Future> result; + result.executionSucceed(info); + return result; } diff --git a/server/src/client/music/internal_provider/channel_replay/ChannelProvider.h b/server/src/client/music/internal_provider/channel_replay/ChannelProvider.h index 9515cff..f446c13 100644 --- a/server/src/client/music/internal_provider/channel_replay/ChannelProvider.h +++ b/server/src/client/music/internal_provider/channel_replay/ChannelProvider.h @@ -4,27 +4,27 @@ #include namespace music { - namespace provider { - class ChannelProvider : public manager::PlayerProvider { - public: - static std::shared_ptr<::music::manager::PlayerProvider> create_provider(); + namespace provider { + class ChannelProvider : public manager::PlayerProvider { + public: + static std::shared_ptr<::music::manager::PlayerProvider> create_provider(); - ChannelProvider() { - this->providerName = "ChannelProvider"; - this->providerDescription = "Allows you to playback files uploaded within the channel or the music directory"; - } + ChannelProvider() { + this->providerName = "ChannelProvider"; + this->providerDescription = "Allows you to playback files uploaded within the channel or the music directory"; + } - virtual ~ChannelProvider() = default; + virtual ~ChannelProvider() = default; - threads::Future> query_info(const std::string &string, void *pVoid, void *pVoid1) override; + threads::Future> query_info(const std::string &string, void *pVoid, void *pVoid1) override; - threads::Future> createPlayer(const std::string &string, void*, void*) override; + threads::Future> createPlayer(const std::string &string, void*, void*) override; - bool acceptString(const std::string &str) override; - std::vector availableFormats() override; - std::vector availableProtocols() override; + bool acceptString(const std::string &str) override; + std::vector availableFormats() override; + std::vector availableProtocols() override; - private: - }; - } + private: + }; + } } \ No newline at end of file diff --git a/server/src/client/query/QueryClient.cpp b/server/src/client/query/QueryClient.cpp index a3043c7..1ceef9a 100644 --- a/server/src/client/query/QueryClient.cpp +++ b/server/src/client/query/QueryClient.cpp @@ -22,37 +22,37 @@ using namespace ts::server; //#define DEBUG_TRAFFIC QueryClient::QueryClient(QueryServer* handle, int sockfd) : ConnectedClient(handle->sql, nullptr), handle(handle), clientFd(sockfd) { - memtrack::allocated(this); + memtrack::allocated(this); int enabled = 1; int disabled = 0; setsockopt(sockfd, SOL_SOCKET, SO_KEEPALIVE, &enabled, sizeof(enabled)); if(setsockopt(sockfd, IPPROTO_TCP, TCP_NOPUSH, &disabled, sizeof disabled) < 0) { - logError(this->getServerId(), "[Query] Could not disable nopush for {} ({}/{})", CLIENT_STR_LOG_PREFIX_(this), errno, strerror(errno)); + logError(this->getServerId(), "[Query] Could not disable nopush for {} ({}/{})", CLIENT_STR_LOG_PREFIX_(this), errno, strerror(errno)); + } + if(setsockopt(sockfd, IPPROTO_TCP, TCP_NODELAY, &enabled, sizeof enabled) < 0) { + logError(this->getServerId(), "[Query] Could not disable no delay for {} ({}/{})", CLIENT_STR_LOG_PREFIX_(this), errno, strerror(errno)); } - if(setsockopt(sockfd, IPPROTO_TCP, TCP_NODELAY, &enabled, sizeof enabled) < 0) { - logError(this->getServerId(), "[Query] Could not disable no delay for {} ({}/{})", CLIENT_STR_LOG_PREFIX_(this), errno, strerror(errno)); - } - this->readEvent = event_new(this->handle->eventLoop, this->clientFd, EV_READ | EV_PERSIST, [](int a, short b, void* c){ ((QueryClient*) c)->handleMessageRead(a, b, c); }, this); - this->writeEvent = event_new(this->handle->eventLoop, this->clientFd, EV_WRITE, [](int a, short b, void* c){ ((QueryClient*) c)->handleMessageWrite(a, b, c); }, this); + this->readEvent = event_new(this->handle->eventLoop, this->clientFd, EV_READ | EV_PERSIST, [](int a, short b, void* c){ ((QueryClient*) c)->handleMessageRead(a, b, c); }, this); + this->writeEvent = event_new(this->handle->eventLoop, this->clientFd, EV_WRITE, [](int a, short b, void* c){ ((QueryClient*) c)->handleMessageWrite(a, b, c); }, this); - this->state = ConnectionState::CONNECTED; - connectedTimestamp = system_clock::now(); + this->state = ConnectionState::CONNECTED; + connectedTimestamp = system_clock::now(); this->resetEventMask(); } void QueryClient::applySelfLock(const std::shared_ptr &cl) { - this->_this = cl; + this->_this = cl; } QueryClient::~QueryClient() { - memtrack::freed(this); -// if(this->closeLock.tryLock() != 0) -// logCritical("Query manager deleted, but is still in usage! (closeLock)"); -// if(this->bufferLock.tryLock() != 0) -// logCritical("Query manager deleted, but is still in usage! (bufferLock)"); - this->ssl_handler.finalize(); + memtrack::freed(this); +// if(this->closeLock.tryLock() != 0) +// logCritical("Query manager deleted, but is still in usage! (closeLock)"); +// if(this->bufferLock.tryLock() != 0) +// logCritical("Query manager deleted, but is still in usage! (bufferLock)"); + this->ssl_handler.finalize(); } void QueryClient::preInitialize() { @@ -64,43 +64,43 @@ void QueryClient::preInitialize() { DatabaseHelper::assignDatabaseId(this->sql, this->getServerId(), _this.lock()); - if(ts::config::query::sslMode == 0) { - this->connectionType = ConnectionType::PLAIN; + if(ts::config::query::sslMode == 0) { + this->connectionType = ConnectionType::PLAIN; this->postInitialize(); - } + } } void QueryClient::postInitialize() { lock_guard lock(this->lock_packet_handle); /* we dont want to handle anything while we're initializing */ - this->connectTimestamp = system_clock::now(); - this->properties()[property::CLIENT_LASTCONNECTED] = duration_cast(this->connectTimestamp.time_since_epoch()).count(); + this->connectTimestamp = system_clock::now(); + this->properties()[property::CLIENT_LASTCONNECTED] = duration_cast(this->connectTimestamp.time_since_epoch()).count(); - if(ts::config::query::sslMode == 1 && this->connectionType != ConnectionType::SSL_ENCRIPTED) { - this->notifyError({findError("failed_connection_initialisation"), "Please use a SSL encryption!"}); - this->disconnect("Please us a SSL encryption for more security.\nThe server denies also all other connections!"); - return; - } + if(ts::config::query::sslMode == 1 && this->connectionType != ConnectionType::SSL_ENCRIPTED) { + this->notifyError({findError("failed_connection_initialisation"), "Please use a SSL encryption!"}); + this->disconnect("Please us a SSL encryption for more security.\nThe server denies also all other connections!"); + return; + } writeMessage(config::query::motd); - assert(this->handle); - if(this->handle->ip_blacklist) { - assert(this->handle->ip_blacklist); - if(this->handle->ip_blacklist->contains(this->remote_address)) { - Command cmd("error"); - auto err = findError("client_login_not_permitted"); - cmd["id"] = err.errorId; - cmd["msg"] = err.message; - cmd["extra_msg"] = "You're not permitted to use the query interface! (Your blacklisted)"; - this->sendCommand(cmd); - this->disconnect("blacklisted"); - return;; - } - if(this->handle->ip_whitelist) - this->whitelisted = this->handle->ip_whitelist->contains(this->remote_address); - else - this->whitelisted = false; - debugMessage(LOG_QUERY, "Got new query client from {}. Whitelisted: {}", this->getLoggingPeerIp(), this->whitelisted); - } + assert(this->handle); + if(this->handle->ip_blacklist) { + assert(this->handle->ip_blacklist); + if(this->handle->ip_blacklist->contains(this->remote_address)) { + Command cmd("error"); + auto err = findError("client_login_not_permitted"); + cmd["id"] = err.errorId; + cmd["msg"] = err.message; + cmd["extra_msg"] = "You're not permitted to use the query interface! (Your blacklisted)"; + this->sendCommand(cmd); + this->disconnect("blacklisted"); + return;; + } + if(this->handle->ip_whitelist) + this->whitelisted = this->handle->ip_whitelist->contains(this->remote_address); + else + this->whitelisted = false; + debugMessage(LOG_QUERY, "Got new query client from {}. Whitelisted: {}", this->getLoggingPeerIp(), this->whitelisted); + } if(!this->whitelisted) { threads::MutexLock lock(this->handle->loginLock); @@ -116,15 +116,15 @@ void QueryClient::postInitialize() { } } - this->update_cached_permissions(); + this->update_cached_permissions(); } void QueryClient::writeMessage(const std::string& message) { if(this->state == ConnectionState::DISCONNECTED || !this->handle) return; - if(this->connectionType == ConnectionType::PLAIN) this->writeRawMessage(message); - else if(this->connectionType == ConnectionType::SSL_ENCRIPTED) this->ssl_handler.send(pipes::buffer_view{(void*) message.data(), message.length()}); - else logCritical(LOG_GENERAL, "Invalid query connection type to write to!"); + if(this->connectionType == ConnectionType::PLAIN) this->writeRawMessage(message); + else if(this->connectionType == ConnectionType::SSL_ENCRIPTED) this->ssl_handler.send(pipes::buffer_view{(void*) message.data(), message.length()}); + else logCritical(LOG_GENERAL, "Invalid query connection type to write to!"); } @@ -138,15 +138,15 @@ bool QueryClient::disconnect(const std::string &reason) { } bool QueryClient::closeConnection(const std::chrono::system_clock::time_point& flushTimeout) { - auto ownLock = dynamic_pointer_cast(_this.lock()); - if(!ownLock) return false; + auto ownLock = dynamic_pointer_cast(_this.lock()); + if(!ownLock) return false; - unique_lock handleLock(this->lock_packet_handle); - unique_lock lock(this->closeLock); + unique_lock handleLock(this->lock_packet_handle); + unique_lock lock(this->closeLock); bool flushing = flushTimeout.time_since_epoch().count() != 0; if(this->state == ConnectionState::DISCONNECTED || (flushing && this->state == ConnectionState::DISCONNECTING)) return false; - this->state = flushing ? ConnectionState::DISCONNECTING : ConnectionState::DISCONNECTED; + this->state = flushing ? ConnectionState::DISCONNECTING : ConnectionState::DISCONNECTED; if(this->readEvent) { //Attention dont trigger this within the read thread! event_del_block(this->readEvent); @@ -154,20 +154,20 @@ bool QueryClient::closeConnection(const std::chrono::system_clock::time_point& f this->readEvent = nullptr; } - if(this->server){ - { - unique_lock channel_lock(this->server->channel_tree_lock); - this->server->unregisterClient(_this.lock(), "disconnected", channel_lock); - } - this->server->groups->disableCache(this->getClientDatabaseId()); - this->server = nullptr; - } + if(this->server){ + { + unique_lock channel_lock(this->server->channel_tree_lock); + this->server->unregisterClient(_this.lock(), "disconnected", channel_lock); + } + this->server->groups->disableCache(this->getClientDatabaseId()); + this->server = nullptr; + } if(flushing){ this->flushThread = new threads::Thread(THREAD_SAVE_OPERATIONS | THREAD_EXECUTE_LATER, [ownLock, flushTimeout](){ while(ownLock->state == ConnectionState::DISCONNECTING && flushTimeout > system_clock::now()){ { - std::lock_guard buffer_lock(ownLock->buffer_lock); + std::lock_guard buffer_lock(ownLock->buffer_lock); if(ownLock->readQueue.empty() && ownLock->writeQueue.empty()) break; } usleep(10 * 1000); @@ -176,9 +176,9 @@ bool QueryClient::closeConnection(const std::chrono::system_clock::time_point& f }); flushThread->name("Flush thread QC").execute(); } else { - threads::MutexLock l1(this->flushThreadLock); - handleLock.unlock(); - lock.unlock(); + threads::MutexLock l1(this->flushThreadLock); + handleLock.unlock(); + lock.unlock(); if(this->flushThread){ threads::NegatedMutexLock l(this->closeLock); this->flushThread->join(); @@ -189,114 +189,114 @@ bool QueryClient::closeConnection(const std::chrono::system_clock::time_point& f } void QueryClient::disconnectFinal() { - lock_guard lock_tick(this->lock_query_tick); - lock_guard lock_handle(this->lock_packet_handle); - threads::MutexLock lock_close(this->closeLock); - std::unique_lock buffer_lock(this->buffer_lock, try_to_lock); + lock_guard lock_tick(this->lock_query_tick); + lock_guard lock_handle(this->lock_packet_handle); + threads::MutexLock lock_close(this->closeLock); + std::unique_lock buffer_lock(this->buffer_lock, try_to_lock); - if(final_disconnected) { - logError(LOG_QUERY, "Tried to disconnect a client twice!"); - return; - } - final_disconnected = true; + if(final_disconnected) { + logError(LOG_QUERY, "Tried to disconnect a client twice!"); + return; + } + final_disconnected = true; this->state = ConnectionState::DISCONNECTED; - { - threads::MutexTryLock l(this->flushThreadLock); - if(!!l) { - if(this->flushThread) { - this->flushThread->detach(); - delete this->flushThread; //Release the captured this lock - this->flushThread = nullptr; - } - } - } + { + threads::MutexTryLock l(this->flushThreadLock); + if(!!l) { + if(this->flushThread) { + this->flushThread->detach(); + delete this->flushThread; //Release the captured this lock + this->flushThread = nullptr; + } + } + } if(this->writeEvent) { - event_del_block(this->writeEvent); + event_del_block(this->writeEvent); event_free(this->writeEvent); this->writeEvent = nullptr; } if(this->readEvent) { - event_del_block(this->readEvent); + event_del_block(this->readEvent); event_free(this->readEvent); this->readEvent = nullptr; } if(this->clientFd > 0) { - if(shutdown(this->clientFd, SHUT_RDWR) < 0) - debugMessage(LOG_QUERY, "Could not shutdown query client socket! {} ({})", errno, strerror(errno)); - if(close(this->clientFd) < 0) - debugMessage(LOG_QUERY, "Failed to close the query client socket! {} ({})", errno, strerror(errno)); + if(shutdown(this->clientFd, SHUT_RDWR) < 0) + debugMessage(LOG_QUERY, "Could not shutdown query client socket! {} ({})", errno, strerror(errno)); + if(close(this->clientFd) < 0) + debugMessage(LOG_QUERY, "Failed to close the query client socket! {} ({})", errno, strerror(errno)); this->clientFd = -1; } if(this->server) { - { - unique_lock channel_lock(this->server->channel_tree_lock); - this->server->unregisterClient(_this.lock(), "disconnected", channel_lock); - } + { + unique_lock channel_lock(this->server->channel_tree_lock); + this->server->unregisterClient(_this.lock(), "disconnected", channel_lock); + } this->server->groups->disableCache(this->getClientDatabaseId()); this->server = nullptr; } - this->readQueue.clear(); - this->writeQueue.clear(); + this->readQueue.clear(); + this->writeQueue.clear(); - if(this->handle) + if(this->handle) this->handle->unregisterConnection(dynamic_pointer_cast(_this.lock())); } void QueryClient::writeRawMessage(const std::string &message) { - { - std::lock_guard lock(this->buffer_lock); - this->writeQueue.push_back(message); - } - if(this->writeEvent) event_add(this->writeEvent, nullptr); + { + std::lock_guard lock(this->buffer_lock); + this->writeQueue.push_back(message); + } + if(this->writeEvent) event_add(this->writeEvent, nullptr); } void QueryClient::handleMessageWrite(int fd, short, void *) { auto ownLock = _this.lock(); - std::unique_lock buffer_lock(this->buffer_lock, try_to_lock); - if(this->state == ConnectionState::DISCONNECTED) return; + std::unique_lock buffer_lock(this->buffer_lock, try_to_lock); + if(this->state == ConnectionState::DISCONNECTED) return; if(!buffer_lock.owns_lock()) { - if(this->writeEvent) - event_add(this->writeEvent, nullptr); + if(this->writeEvent) + event_add(this->writeEvent, nullptr); return; } - int writes = 0; - string buffer; - while(writes < 10 && !this->writeQueue.empty()) { - if(buffer.empty()) { - buffer = std::move(this->writeQueue.front()); - this->writeQueue.pop_front(); - } - auto length = send(fd, buffer.data(), buffer.length(), MSG_NOSIGNAL); + int writes = 0; + string buffer; + while(writes < 10 && !this->writeQueue.empty()) { + if(buffer.empty()) { + buffer = std::move(this->writeQueue.front()); + this->writeQueue.pop_front(); + } + auto length = send(fd, buffer.data(), buffer.length(), MSG_NOSIGNAL); #ifdef DEBUG_TRAFFIC - debugMessage("Write " + to_string(buffer.length())); - hexDump((void *) buffer.data(), buffer.length()); + debugMessage("Write " + to_string(buffer.length())); + hexDump((void *) buffer.data(), buffer.length()); #endif - if(length == -1) { - if (errno == EINTR || errno == EAGAIN) { - if(this->writeEvent) - event_add(this->writeEvent, nullptr); - return; - } - else { - logError(LOG_QUERY, "{} Failed to write message: {} ({} => {})", CLIENT_STR_LOG_PREFIX, length, errno, strerror(errno)); - threads::Thread([=](){ ownLock->closeConnection(); }).detach(); - return; - } - } else { - if(buffer.length() == length) - buffer = ""; - else - buffer = buffer.substr(length); - } - writes++; - } - if(!buffer.empty()) - this->writeQueue.push_front(buffer); + if(length == -1) { + if (errno == EINTR || errno == EAGAIN) { + if(this->writeEvent) + event_add(this->writeEvent, nullptr); + return; + } + else { + logError(LOG_QUERY, "{} Failed to write message: {} ({} => {})", CLIENT_STR_LOG_PREFIX, length, errno, strerror(errno)); + threads::Thread([=](){ ownLock->closeConnection(); }).detach(); + return; + } + } else { + if(buffer.length() == length) + buffer = ""; + else + buffer = buffer.substr(length); + } + writes++; + } + if(!buffer.empty()) + this->writeQueue.push_front(buffer); if(!this->writeQueue.empty() && this->writeEvent) event_add(this->writeEvent, nullptr); @@ -305,8 +305,8 @@ void QueryClient::handleMessageWrite(int fd, short, void *) { void QueryClient::handleMessageRead(int fd, short, void *) { auto ownLock = dynamic_pointer_cast(_this.lock()); if(!ownLock) { - logCritical(LOG_QUERY, "Could not get own lock!"); - return; + logCritical(LOG_QUERY, "Could not get own lock!"); + return; } @@ -320,201 +320,201 @@ void QueryClient::handleMessageRead(int fd, short, void *) { logMessage(LOG_QUERY, "{} Connection closed. Client disconnected.", CLIENT_STR_LOG_PREFIX); event_del_noblock(this->readEvent); std::thread([ownLock]{ - ownLock->closeConnection(); + ownLock->closeConnection(); }).detach(); } else { - logError(LOG_QUERY, "{} Failed to read! Code: {} errno: {} message: {}", CLIENT_STR_LOG_PREFIX, length, errno, strerror(errno)); - event_del_noblock(this->readEvent); + logError(LOG_QUERY, "{} Failed to read! Code: {} errno: {} message: {}", CLIENT_STR_LOG_PREFIX, length, errno, strerror(errno)); + event_del_noblock(this->readEvent); threads::Thread(THREAD_SAVE_OPERATIONS, [ownLock](){ ownLock->closeConnection(); }).detach(); } return; } - buffer.resize(length); - { - std::lock_guard buffer_lock(this->buffer_lock); - if(this->state == ConnectionState::DISCONNECTED) - return; - this->readQueue.push_back(std::move(buffer)); + buffer.resize(length); + { + std::lock_guard buffer_lock(this->buffer_lock); + if(this->state == ConnectionState::DISCONNECTED) + return; + this->readQueue.push_back(std::move(buffer)); #ifdef DEBUG_TRAFFIC - debugMessage("Read " + to_string(buffer.length())); - hexDump((void *) buffer.data(), buffer.length()); + debugMessage("Read " + to_string(buffer.length())); + hexDump((void *) buffer.data(), buffer.length()); #endif - } + } - if(this->handle) - this->handle->executePool()->execute([ownLock]() { - int counter = 0; - while(ownLock->tickIOMessageProgress() && counter++ < 15); - }); + if(this->handle) + this->handle->executePool()->execute([ownLock]() { + int counter = 0; + while(ownLock->tickIOMessageProgress() && counter++ < 15); + }); } bool QueryClient::tickIOMessageProgress() { - lock_guard lock(this->lock_packet_handle); - if(!this->handle || this->state == ConnectionState::DISCONNECTED || this->state == ConnectionState::DISCONNECTING) return false; + lock_guard lock(this->lock_packet_handle); + if(!this->handle || this->state == ConnectionState::DISCONNECTED || this->state == ConnectionState::DISCONNECTING) return false; - string message; - bool next = false; - { - std::lock_guard buffer_lock(this->buffer_lock); - if(this->readQueue.empty()) return false; - message = std::move(this->readQueue.front()); - this->readQueue.pop_front(); - next |= this->readQueue.empty(); - } + string message; + bool next = false; + { + std::lock_guard buffer_lock(this->buffer_lock); + if(this->readQueue.empty()) return false; + message = std::move(this->readQueue.front()); + this->readQueue.pop_front(); + next |= this->readQueue.empty(); + } - if(this->connectionType == ConnectionType::PLAIN) { - int count = 0; - while(this->handleMessage(pipes::buffer_view{(void*) message.data(), message.length()}) && count++ < 15) message = ""; - next |= count == 15; - } else if(this->connectionType == ConnectionType::SSL_ENCRIPTED) { - this->ssl_handler.process_incoming_data(pipes::buffer_view{(void*) message.data(), message.length()}); - } else if(this->connectionType == ConnectionType::UNKNOWN) { - if(config::query::sslMode != 0 && pipes::SSL::isSSLHeader(message)) { - this->initializeSSL(); + if(this->connectionType == ConnectionType::PLAIN) { + int count = 0; + while(this->handleMessage(pipes::buffer_view{(void*) message.data(), message.length()}) && count++ < 15) message = ""; + next |= count == 15; + } else if(this->connectionType == ConnectionType::SSL_ENCRIPTED) { + this->ssl_handler.process_incoming_data(pipes::buffer_view{(void*) message.data(), message.length()}); + } else if(this->connectionType == ConnectionType::UNKNOWN) { + if(config::query::sslMode != 0 && pipes::SSL::isSSLHeader(message)) { + this->initializeSSL(); - /* - * - Content - * \x16 - * -Version (1) - * \x03 \x00 - * - length (2) - * \x00 \x04 - * - * - Header - * \x00 -> hello request (3) - * \x05 -> length (4) - */ + /* + * - Content + * \x16 + * -Version (1) + * \x03 \x00 + * - length (2) + * \x00 \x04 + * + * - Header + * \x00 -> hello request (3) + * \x05 -> length (4) + */ - //this->writeRawMessage(string("\x16\x03\x01\x00\x05\x00\x00\x00\x00\x00", 10)); - } else { - this->connectionType = ConnectionType::PLAIN; - this->postInitialize(); - } - next = true; - { - std::lock_guard buffer_lock(this->buffer_lock); - this->readQueue.push_front(std::move(message)); - } - } - return next; + //this->writeRawMessage(string("\x16\x03\x01\x00\x05\x00\x00\x00\x00\x00", 10)); + } else { + this->connectionType = ConnectionType::PLAIN; + this->postInitialize(); + } + next = true; + { + std::lock_guard buffer_lock(this->buffer_lock); + this->readQueue.push_front(std::move(message)); + } + } + return next; } extern InstanceHandler* serverInstance; void QueryClient::initializeSSL() { - this->connectionType = ConnectionType::SSL_ENCRIPTED; + this->connectionType = ConnectionType::SSL_ENCRIPTED; - this->ssl_handler.direct_process(pipes::PROCESS_DIRECTION_OUT, true); - this->ssl_handler.direct_process(pipes::PROCESS_DIRECTION_IN, true); + this->ssl_handler.direct_process(pipes::PROCESS_DIRECTION_OUT, true); + this->ssl_handler.direct_process(pipes::PROCESS_DIRECTION_IN, true); - this->ssl_handler.callback_data(std::bind(&QueryClient::handleMessage, this, placeholders::_1)); - this->ssl_handler.callback_write(std::bind(&QueryClient::writeRawMessage, this, placeholders::_1)); - this->ssl_handler.callback_initialized = std::bind(&QueryClient::postInitialize, this); + this->ssl_handler.callback_data(std::bind(&QueryClient::handleMessage, this, placeholders::_1)); + this->ssl_handler.callback_write(std::bind(&QueryClient::writeRawMessage, this, placeholders::_1)); + this->ssl_handler.callback_initialized = std::bind(&QueryClient::postInitialize, this); - this->ssl_handler.callback_error([&](int code, const std::string& message) { - if(code == PERROR_SSL_ACCEPT) { - this->disconnect("invalid accept"); - } else if(code == PERROR_SSL_TIMEOUT) - this->disconnect("invalid accept (timeout)"); - else - logError(LOG_QUERY, "Got unknown ssl error ({} | {})", code, message); - }); + this->ssl_handler.callback_error([&](int code, const std::string& message) { + if(code == PERROR_SSL_ACCEPT) { + this->disconnect("invalid accept"); + } else if(code == PERROR_SSL_TIMEOUT) + this->disconnect("invalid accept (timeout)"); + else + logError(LOG_QUERY, "Got unknown ssl error ({} | {})", code, message); + }); - { - auto context = serverInstance->sslManager()->getQueryContext(); + { + auto context = serverInstance->sslManager()->getQueryContext(); - auto options = make_shared(); - options->type = pipes::SSL::SERVER; - options->context_method = TLS_method(); - options->default_keypair({context->privateKey, context->certificate}); - if(!this->ssl_handler.initialize(options)) { - logError(LOG_QUERY, "[{}] Failed to setup ssl!", CLIENT_STR_LOG_PREFIX); - } - } + auto options = make_shared(); + options->type = pipes::SSL::SERVER; + options->context_method = TLS_method(); + options->default_keypair({context->privateKey, context->certificate}); + if(!this->ssl_handler.initialize(options)) { + logError(LOG_QUERY, "[{}] Failed to setup ssl!", CLIENT_STR_LOG_PREFIX); + } + } } bool QueryClient::handleMessage(const pipes::buffer_view& message) { - { - threads::MutexLock l(this->closeLock); - if(this->state == ConnectionState::DISCONNECTED) - return false; - } + { + threads::MutexLock l(this->closeLock); + if(this->state == ConnectionState::DISCONNECTED) + return false; + } #ifdef DEBUG_TRAFFIC - debugMessage("Handling message " + to_string(message.length())); - hexDump((void *) message.data(), message.length()); + debugMessage("Handling message " + to_string(message.length())); + hexDump((void *) message.data(), message.length()); #endif - string command; - { - this->lineBuffer += message.string(); - int length = 2; - auto pos = this->lineBuffer.find("\r\n"); - if(pos == string::npos) pos = this->lineBuffer.find("\n\r"); - if(pos == string::npos) { - length = 1; - pos = this->lineBuffer.find('\n'); - } + string command; + { + this->lineBuffer += message.string(); + int length = 2; + auto pos = this->lineBuffer.find("\r\n"); + if(pos == string::npos) pos = this->lineBuffer.find("\n\r"); + if(pos == string::npos) { + length = 1; + pos = this->lineBuffer.find('\n'); + } - if(pos != string::npos){ - command = this->lineBuffer.substr(0, pos); - if(this->lineBuffer.size() > pos + length) - this->lineBuffer = this->lineBuffer.substr(pos + length); - else - this->lineBuffer.clear(); - } - if(pos == string::npos) return false; - } + if(pos != string::npos){ + command = this->lineBuffer.substr(0, pos); + if(this->lineBuffer.size() > pos + length) + this->lineBuffer = this->lineBuffer.substr(pos + length); + else + this->lineBuffer.clear(); + } + if(pos == string::npos) return false; + } if(command.empty() || command.find_first_not_of(' ') == string::npos) { //Empty command - logTrace(LOG_QUERY, "[{}:{}] Got query idle command (Empty command or spaces)", this->getLoggingPeerIp(), this->getPeerPort()); + logTrace(LOG_QUERY, "[{}:{}] Got query idle command (Empty command or spaces)", this->getLoggingPeerIp(), this->getPeerPort()); CMD_RESET_IDLE; //if idle time over 5 min than connection drop return true; } if((uint8_t) command[0] == 255) { - string commands{}; + string commands{}; - /* we got a telnet command here */ - while(command.size() >= 2 && (uint8_t) command[0] == 255) { - uint8_t code = command[1]; - uint8_t option = command[2]; + /* we got a telnet command here */ + while(command.size() >= 2 && (uint8_t) command[0] == 255) { + uint8_t code = command[1]; + uint8_t option = command[2]; - if(!commands.empty()) - commands += ", "; - commands += to_string(code) + ":" + to_string(option); - command = command.substr(3); - } + if(!commands.empty()) + commands += ", "; + commands += to_string(code) + ":" + to_string(option); + command = command.substr(3); + } - logTrace(LOG_QUERY, "[{}:{}] Received telnet command(s): {}. Ignoring it.",this->getLoggingPeerIp(), this->getPeerPort(), commands); - CMD_RESET_IDLE; + logTrace(LOG_QUERY, "[{}:{}] Received telnet command(s): {}. Ignoring it.",this->getLoggingPeerIp(), this->getPeerPort(), commands); + CMD_RESET_IDLE; if(command.empty()) - return true; + return true; } - unique_ptr cmd; + unique_ptr cmd; try { - cmd = make_unique(Command::parse(pipes::buffer_view{(void*) command.data(), command.length()}, true, !ts::config::server::strict_ut8_mode)); + cmd = make_unique(Command::parse(pipes::buffer_view{(void*) command.data(), command.length()}, true, !ts::config::server::strict_ut8_mode)); } catch(std::invalid_argument& ex) { - logTrace(LOG_QUERY, "[{}:{}] Failed to parse command (invalid argument): {}", this->getLoggingPeerIp(), this->getPeerPort(), command); - this->notifyError(CommandResult{findError("parameter_convert"), ex.what()}); - return false; + logTrace(LOG_QUERY, "[{}:{}] Failed to parse command (invalid argument): {}", this->getLoggingPeerIp(), this->getPeerPort(), command); + this->notifyError(CommandResult{findError("parameter_convert"), ex.what()}); + return false; } catch(command_malformed_exception& ex) { - logTrace(LOG_QUERY, "[{}:{}] Failed to parse command (malformed command at {}): {}", this->getLoggingPeerIp(), this->getPeerPort(), ex.index(), command); - this->notifyError(CommandResult{findError("parameter_convert"), "invalid character @" + to_string(ex.index())}); - return false; + logTrace(LOG_QUERY, "[{}:{}] Failed to parse command (malformed command at {}): {}", this->getLoggingPeerIp(), this->getPeerPort(), ex.index(), command); + this->notifyError(CommandResult{findError("parameter_convert"), "invalid character @" + to_string(ex.index())}); + return false; } catch(std::exception& ex) { - logTrace(LOG_QUERY, "[{}:{}] Failed to parse command (exception: {}): {}", this->getLoggingPeerIp(), this->getPeerPort(), ex.what(), command); - this->notifyError(CommandResult{ErrorType::VSError, ex.what()}); - return false; + logTrace(LOG_QUERY, "[{}:{}] Failed to parse command (exception: {}): {}", this->getLoggingPeerIp(), this->getPeerPort(), ex.what(), command); + this->notifyError(CommandResult{ErrorType::VSError, ex.what()}); + return false; } - try { - this->handleCommandFull(*cmd); + try { + this->handleCommandFull(*cmd); } catch(std::exception& ex) { - this->notifyError(CommandResult{ErrorType::VSError, ex.what()}); + this->notifyError(CommandResult{ErrorType::VSError, ex.what()}); } return true; } @@ -526,20 +526,20 @@ void QueryClient::sendCommand(const ts::Command &command, bool) { } void QueryClient::tick(const std::chrono::system_clock::time_point &time) { - ConnectedClient::tick(time); + ConnectedClient::tick(time); } void QueryClient::queryTick() { - lock_guard lock_tick(this->lock_query_tick); + lock_guard lock_tick(this->lock_query_tick); if(this->idleTimestamp.time_since_epoch().count() > 0 && system_clock::now() - this->idleTimestamp > minutes(5)){ debugMessage(LOG_QUERY, "Dropping client " + this->getLoggingPeerIp() + "|" + this->getDisplayName() + ". (Timeout)"); - this->closeConnection(system_clock::now() + seconds(1)); + this->closeConnection(system_clock::now() + seconds(1)); } - if(this->connectionType == ConnectionType::UNKNOWN && system_clock::now() - milliseconds(500) > connectedTimestamp) { - this->connectionType = ConnectionType::PLAIN; - this->postInitialize(); - } + if(this->connectionType == ConnectionType::UNKNOWN && system_clock::now() - milliseconds(500) > connectedTimestamp) { + this->connectionType = ConnectionType::PLAIN; + this->postInitialize(); + } } bool QueryClient::notifyChannelSubscribed(const deque> &) { @@ -551,5 +551,5 @@ bool QueryClient::notifyChannelUnsubscribed(const deque } bool QueryClient::ignoresFlood() { - return this->whitelisted || ConnectedClient::ignoresFlood(); + return this->whitelisted || ConnectedClient::ignoresFlood(); } diff --git a/server/src/client/query/QueryClient.h b/server/src/client/query/QueryClient.h index 49139ea..afa381d 100644 --- a/server/src/client/query/QueryClient.h +++ b/server/src/client/query/QueryClient.h @@ -13,11 +13,11 @@ namespace ts { class QueryClient : public ConnectedClient { friend class QueryServer; - enum ConnectionType { - PLAIN, - SSL_ENCRIPTED, - UNKNOWN - }; + enum ConnectionType { + PLAIN, + SSL_ENCRIPTED, + UNKNOWN + }; public: QueryClient(QueryServer*, int sockfd); ~QueryClient() override; @@ -35,32 +35,32 @@ namespace ts { void toggleEvent(QueryEventGroup, QueryEventSpecifier, bool); void resetEventMask(); - bool ignoresFlood() override; + bool ignoresFlood() override; - inline std::shared_ptr getQueryAccount() { return this->query_account; } - protected: + inline std::shared_ptr getQueryAccount() { return this->query_account; } + protected: void preInitialize(); void postInitialize(); void tick(const std::chrono::system_clock::time_point &time) override; void queryTick(); protected: - void initializeSSL(); + void initializeSSL(); bool handleMessage(const pipes::buffer_view&); - bool tickIOMessageProgress(); + bool tickIOMessageProgress(); void handleMessageRead(int, short, void*); void handleMessageWrite(int, short, void*); - void writeRawMessage(const std::string&); + void writeRawMessage(const std::string&); void applySelfLock(const std::shared_ptr &cl); private: QueryServer* handle; - ConnectionType connectionType = ConnectionType::UNKNOWN; + ConnectionType connectionType = ConnectionType::UNKNOWN; - bool whitelisted = false; + bool whitelisted = false; int clientFd = -1; ::event* readEvent = nullptr; @@ -69,22 +69,22 @@ namespace ts { pipes::SSL ssl_handler; - std::mutex buffer_lock; + std::mutex buffer_lock; std::deque writeQueue; - std::deque readQueue; + std::deque readQueue; threads::Mutex flushThreadLock; threads::Thread* flushThread = nullptr; - bool final_disconnected = false; + bool final_disconnected = false; std::string lineBuffer; std::chrono::time_point connectedTimestamp; uint16_t eventMask[QueryEventGroup::QEVENTGROUP_MAX]; - std::recursive_mutex lock_packet_handle; - std::recursive_mutex lock_query_tick; + std::recursive_mutex lock_packet_handle; + std::recursive_mutex lock_query_tick; - std::shared_ptr query_account; + std::shared_ptr query_account; protected: CommandResult handleCommand(Command &command) override; @@ -97,53 +97,53 @@ namespace ts { bool notifyServerUpdated(std::shared_ptr ptr) override; bool notifyClientPoke(std::shared_ptr invoker, std::string msg) override; - bool notifyClientUpdated(const std::shared_ptr &ptr, const std::deque> &deque, bool lock_channel_tree) override; + bool notifyClientUpdated(const std::shared_ptr &ptr, const std::deque> &deque, bool lock_channel_tree) override; - bool notifyPluginCmd(std::string name, std::string msg,std::shared_ptr) override; + bool notifyPluginCmd(std::string name, std::string msg,std::shared_ptr) override; bool notifyClientChatComposing(const std::shared_ptr &ptr) override; bool notifyClientChatClosed(const std::shared_ptr &ptr) override; bool notifyTextMessage(ChatMessageMode mode, const std::shared_ptr &sender, uint64_t targetId, ChannelId channel_id, const std::chrono::system_clock::time_point&, const std::string &textMessage) override; bool notifyServerGroupClientAdd(const std::shared_ptr &invoker, const std::shared_ptr &client, const std::shared_ptr &group) override; bool notifyServerGroupClientRemove(std::shared_ptr invoker, std::shared_ptr client, std::shared_ptr group) override; - bool notifyClientChannelGroupChanged(const std::shared_ptr &invoker, const std::shared_ptr &client, const std::shared_ptr &ptr, const std::shared_ptr &shared_ptr, - const std::shared_ptr &group, bool lock_channel_tree) override; + bool notifyClientChannelGroupChanged(const std::shared_ptr &invoker, const std::shared_ptr &client, const std::shared_ptr &ptr, const std::shared_ptr &shared_ptr, + const std::shared_ptr &group, bool lock_channel_tree) override; - bool notifyChannelMoved(const std::shared_ptr &channel, ChannelId order, const std::shared_ptr &invoker) override; + bool notifyChannelMoved(const std::shared_ptr &channel, ChannelId order, const std::shared_ptr &invoker) override; bool notifyChannelCreate(const std::shared_ptr &channel, ChannelId orderId, const std::shared_ptr &invoker) override; bool notifyChannelDescriptionChanged(std::shared_ptr channel) override; bool notifyChannelPasswordChanged(std::shared_ptr ) override; - bool notifyChannelEdited(const std::shared_ptr &ptr, const std::vector &vector, const std::shared_ptr &sharedPtr, bool b) override; + bool notifyChannelEdited(const std::shared_ptr &ptr, const std::vector &vector, const std::shared_ptr &sharedPtr, bool b) override; - bool notifyChannelDeleted(const std::deque &deque, const std::shared_ptr &ptr) override; + bool notifyChannelDeleted(const std::deque &deque, const std::shared_ptr &ptr) override; - bool notifyMusicQueueAdd(const std::shared_ptr &bot, const std::shared_ptr &entry, int index, const std::shared_ptr &invoker) override; - bool notifyMusicQueueRemove(const std::shared_ptr &bot, const std::deque> &entry, const std::shared_ptr &invoker) override; - bool notifyMusicQueueOrderChange(const std::shared_ptr &bot, const std::shared_ptr &entry, int order, const std::shared_ptr &invoker) override; + bool notifyMusicQueueAdd(const std::shared_ptr &bot, const std::shared_ptr &entry, int index, const std::shared_ptr &invoker) override; + bool notifyMusicQueueRemove(const std::shared_ptr &bot, const std::deque> &entry, const std::shared_ptr &invoker) override; + bool notifyMusicQueueOrderChange(const std::shared_ptr &bot, const std::shared_ptr &entry, int order, const std::shared_ptr &invoker) override; - bool notifyMusicPlayerSongChange(const std::shared_ptr &bot, const std::shared_ptr &newEntry) override; + bool notifyMusicPlayerSongChange(const std::shared_ptr &bot, const std::shared_ptr &newEntry) override; - bool notifyClientEnterView(const std::shared_ptr &client, const std::shared_ptr &invoker, const std::string &string, const std::shared_ptr &to, ViewReasonId reasonId, - const std::shared_ptr &from, bool) override; + bool notifyClientEnterView(const std::shared_ptr &client, const std::shared_ptr &invoker, const std::string &string, const std::shared_ptr &to, ViewReasonId reasonId, + const std::shared_ptr &from, bool) override; - bool notifyClientEnterView(const std::deque> &deque, const ViewReasonSystemT &t) override; + bool notifyClientEnterView(const std::deque> &deque, const ViewReasonSystemT &t) override; - bool - notifyClientMoved(const std::shared_ptr &client, const std::shared_ptr &target_channel, ViewReasonId reason, std::string msg, std::shared_ptr invoker, bool lock_channel_tree) override; + bool + notifyClientMoved(const std::shared_ptr &client, const std::shared_ptr &target_channel, ViewReasonId reason, std::string msg, std::shared_ptr invoker, bool lock_channel_tree) override; - bool notifyClientLeftView(const std::shared_ptr &client, const std::shared_ptr &target_channel, ViewReasonId reasonId, const std::string &reasonMessage, std::shared_ptr invoker, - bool lock_channel_tree) override; + bool notifyClientLeftView(const std::shared_ptr &client, const std::shared_ptr &target_channel, ViewReasonId reasonId, const std::string &reasonMessage, std::shared_ptr invoker, + bool lock_channel_tree) override; - bool notifyClientLeftView(const std::deque> &deque, const std::string &string, bool b, const ViewReasonServerLeftT &t) override; + bool notifyClientLeftView(const std::deque> &deque, const std::string &string, bool b, const ViewReasonServerLeftT &t) override; - bool - notifyClientLeftViewKicked(const std::shared_ptr &client, const std::shared_ptr &target_channel, const std::string &message, std::shared_ptr invoker, bool lock_channel_tree) override; + bool + notifyClientLeftViewKicked(const std::shared_ptr &client, const std::shared_ptr &target_channel, const std::string &message, std::shared_ptr invoker, bool lock_channel_tree) override; - bool notifyClientLeftViewBanned(const std::shared_ptr &client, const std::string &message, std::shared_ptr invoker, size_t length, bool lock_channel_tree) override; + bool notifyClientLeftViewBanned(const std::shared_ptr &client, const std::string &message, std::shared_ptr invoker, size_t length, bool lock_channel_tree) override; - private: + private: CommandResult handleCommandExit(Command&); CommandResult handleCommandLogin(Command&); CommandResult handleCommandLogout(Command&); diff --git a/server/src/client/query/QueryClientCommands.cpp b/server/src/client/query/QueryClientCommands.cpp index b96c471..d53430c 100644 --- a/server/src/client/query/QueryClientCommands.cpp +++ b/server/src/client/query/QueryClientCommands.cpp @@ -17,352 +17,352 @@ using namespace ts; using namespace ts::server; constexpr unsigned int string_hash(const char* str, int h = 0) { - return !str[h] ? 5381 : (string_hash(str, h + 1U) * 33U) ^ str[h]; + return !str[h] ? 5381 : (string_hash(str, h + 1U) * 33U) ^ str[h]; } CommandResult QueryClient::handleCommand(Command& cmd) { - /* - if (cmd.command() == "exit" || cmd.command() == "quit") return this->handleCommandExit(cmd); - else if (cmd.command() == "use" || cmd.command() == "serverselect") return this->handleCommandServerSelect(cmd); - else if (cmd.command() == "serverinfo") return this->handleCommandServerInfo(cmd); - else if (cmd.command() == "channellist") return this->handleCommandChannelList(cmd); - else if (cmd.command() == "login") return this->handleCommandLogin(cmd); - else if (cmd.command() == "logout") return this->handleCommandLogout(cmd); - else if (cmd.command() == "join") return this->handleCommandJoin(cmd); - else if (cmd.command() == "left") return this->handleCommandLeft(cmd); - else if (cmd.command() == "globalmessage" || cmd.command() == "gm") return this->handleCommandGlobalMessage(cmd); + /* + if (cmd.command() == "exit" || cmd.command() == "quit") return this->handleCommandExit(cmd); + else if (cmd.command() == "use" || cmd.command() == "serverselect") return this->handleCommandServerSelect(cmd); + else if (cmd.command() == "serverinfo") return this->handleCommandServerInfo(cmd); + else if (cmd.command() == "channellist") return this->handleCommandChannelList(cmd); + else if (cmd.command() == "login") return this->handleCommandLogin(cmd); + else if (cmd.command() == "logout") return this->handleCommandLogout(cmd); + else if (cmd.command() == "join") return this->handleCommandJoin(cmd); + else if (cmd.command() == "left") return this->handleCommandLeft(cmd); + else if (cmd.command() == "globalmessage" || cmd.command() == "gm") return this->handleCommandGlobalMessage(cmd); - else if (cmd.command() == "serverlist") return this->handleCommandServerList(cmd); - else if (cmd.command() == "servercreate") return this->handleCommandServerCreate(cmd); - else if (cmd.command() == "serverstart") return this->handleCommandServerStart(cmd); - else if (cmd.command() == "serverstop") return this->handleCommandServerStop(cmd); - else if (cmd.command() == "serverdelete") return this->handleCommandServerDelete(cmd); - else if (cmd.command() == "serveridgetbyport") return this->handleCommandServerIdGetByPort(cmd); - else if (cmd.command() == "instanceinfo") return this->handleCommandInstanceInfo(cmd); - else if (cmd.command() == "instanceedit") return this->handleCommandInstanceEdit(cmd); - else if (cmd.command() == "hostinfo") return this->handleCommandHostInfo(cmd); + else if (cmd.command() == "serverlist") return this->handleCommandServerList(cmd); + else if (cmd.command() == "servercreate") return this->handleCommandServerCreate(cmd); + else if (cmd.command() == "serverstart") return this->handleCommandServerStart(cmd); + else if (cmd.command() == "serverstop") return this->handleCommandServerStop(cmd); + else if (cmd.command() == "serverdelete") return this->handleCommandServerDelete(cmd); + else if (cmd.command() == "serveridgetbyport") return this->handleCommandServerIdGetByPort(cmd); + else if (cmd.command() == "instanceinfo") return this->handleCommandInstanceInfo(cmd); + else if (cmd.command() == "instanceedit") return this->handleCommandInstanceEdit(cmd); + else if (cmd.command() == "hostinfo") return this->handleCommandHostInfo(cmd); - else if (cmd.command() == "bindinglist") return this->handleCommandBindingList(cmd); + else if (cmd.command() == "bindinglist") return this->handleCommandBindingList(cmd); - else if (cmd.command() == "serversnapshotdeploy") return this->handleCommandServerSnapshotDeploy(cmd); - else if (cmd.command() == "serversnapshotcreate") return this->handleCommandServerSnapshotCreate(cmd); + else if (cmd.command() == "serversnapshotdeploy") return this->handleCommandServerSnapshotDeploy(cmd); + else if (cmd.command() == "serversnapshotcreate") return this->handleCommandServerSnapshotCreate(cmd); - else if (cmd.command() == "serverprocessstop") return this->handleCommandServerProcessStop(cmd); + else if (cmd.command() == "serverprocessstop") return this->handleCommandServerProcessStop(cmd); - else if (cmd.command() == "servernotifyregister") return this->handleCommandServerNotifyRegister(cmd); - else if (cmd.command() == "servernotifylist") return this->handleCommandServerNotifyList(cmd); - else if (cmd.command() == "servernotifyunregister") return this->handleCommandServerNotifyUnregister(cmd); - */ - auto command = cmd.command(); - auto command_hash = string_hash(command.c_str()); - switch (command_hash) { - case string_hash("exit"): - case string_hash("quit"): - return this->handleCommandExit(cmd); - case string_hash("use"): - case string_hash("serverselect"): - return this->handleCommandServerSelect(cmd); - case string_hash("serverinfo"): - return this->handleCommandServerInfo(cmd); - case string_hash("channellist"): - return this->handleCommandChannelList(cmd); - case string_hash("login"): - return this->handleCommandLogin(cmd); - case string_hash("logout"): - return this->handleCommandLogout(cmd); - case string_hash("join"): - return this->handleCommandJoin(cmd); - case string_hash("left"): - return this->handleCommandLeft(cmd); - case string_hash("globalmessage"): - case string_hash("gm"): - return this->handleCommandGlobalMessage(cmd); - case string_hash("serverlist"): - return this->handleCommandServerList(cmd); - case string_hash("servercreate"): - return this->handleCommandServerCreate(cmd); - case string_hash("serverstart"): - return this->handleCommandServerStart(cmd); - case string_hash("serverstop"): - return this->handleCommandServerStop(cmd); - case string_hash("serverdelete"): - return this->handleCommandServerDelete(cmd); - case string_hash("serveridgetbyport"): - return this->handleCommandServerIdGetByPort(cmd); - case string_hash("instanceinfo"): - return this->handleCommandInstanceInfo(cmd); - case string_hash("instanceedit"): - return this->handleCommandInstanceEdit(cmd); - case string_hash("hostinfo"): - return this->handleCommandHostInfo(cmd); - case string_hash("bindinglist"): - return this->handleCommandBindingList(cmd); - case string_hash("serversnapshotdeploy"): - return this->handleCommandServerSnapshotDeploy(cmd); + else if (cmd.command() == "servernotifyregister") return this->handleCommandServerNotifyRegister(cmd); + else if (cmd.command() == "servernotifylist") return this->handleCommandServerNotifyList(cmd); + else if (cmd.command() == "servernotifyunregister") return this->handleCommandServerNotifyUnregister(cmd); + */ + auto command = cmd.command(); + auto command_hash = string_hash(command.c_str()); + switch (command_hash) { + case string_hash("exit"): + case string_hash("quit"): + return this->handleCommandExit(cmd); + case string_hash("use"): + case string_hash("serverselect"): + return this->handleCommandServerSelect(cmd); + case string_hash("serverinfo"): + return this->handleCommandServerInfo(cmd); + case string_hash("channellist"): + return this->handleCommandChannelList(cmd); + case string_hash("login"): + return this->handleCommandLogin(cmd); + case string_hash("logout"): + return this->handleCommandLogout(cmd); + case string_hash("join"): + return this->handleCommandJoin(cmd); + case string_hash("left"): + return this->handleCommandLeft(cmd); + case string_hash("globalmessage"): + case string_hash("gm"): + return this->handleCommandGlobalMessage(cmd); + case string_hash("serverlist"): + return this->handleCommandServerList(cmd); + case string_hash("servercreate"): + return this->handleCommandServerCreate(cmd); + case string_hash("serverstart"): + return this->handleCommandServerStart(cmd); + case string_hash("serverstop"): + return this->handleCommandServerStop(cmd); + case string_hash("serverdelete"): + return this->handleCommandServerDelete(cmd); + case string_hash("serveridgetbyport"): + return this->handleCommandServerIdGetByPort(cmd); + case string_hash("instanceinfo"): + return this->handleCommandInstanceInfo(cmd); + case string_hash("instanceedit"): + return this->handleCommandInstanceEdit(cmd); + case string_hash("hostinfo"): + return this->handleCommandHostInfo(cmd); + case string_hash("bindinglist"): + return this->handleCommandBindingList(cmd); + case string_hash("serversnapshotdeploy"): + return this->handleCommandServerSnapshotDeploy(cmd); - case string_hash("serversnapshotcreate"): - return this->handleCommandServerSnapshotCreate(cmd); - case string_hash("serverprocessstop"): - return this->handleCommandServerProcessStop(cmd); - case string_hash("servernotifyregister"): - return this->handleCommandServerNotifyRegister(cmd); - case string_hash("servernotifylist"): - return this->handleCommandServerNotifyList(cmd); - case string_hash("servernotifyunregister"): - return this->handleCommandServerNotifyUnregister(cmd); - default: - break; - } + case string_hash("serversnapshotcreate"): + return this->handleCommandServerSnapshotCreate(cmd); + case string_hash("serverprocessstop"): + return this->handleCommandServerProcessStop(cmd); + case string_hash("servernotifyregister"): + return this->handleCommandServerNotifyRegister(cmd); + case string_hash("servernotifylist"): + return this->handleCommandServerNotifyList(cmd); + case string_hash("servernotifyunregister"): + return this->handleCommandServerNotifyUnregister(cmd); + default: + break; + } return ConnectedClient::handleCommand(cmd); } CommandResult QueryClient::handleCommandExit(Command &) { logMessage(LOG_QUERY, "[Query] {}:{} disconnected. (Requested by client)", this->getLoggingPeerIp(), this->getPeerPort()); - this->closeConnection(system_clock::now() + seconds(1)); + this->closeConnection(system_clock::now() + seconds(1)); return CommandResult::Success; } //login client_login_name=andreas client_login_password=meinPW CommandResult QueryClient::handleCommandLogin(Command& cmd) { - CMD_RESET_IDLE; + CMD_RESET_IDLE; - std::string username, password; - if(cmd[0].has("client_login_name") && cmd[0].has("client_login_password")){ - username = cmd["client_login_name"].string(); - password = cmd["client_login_password"].string(); - } else { - username = cmd[0][0].key(); - password = cmd[0][1].key(); - } - debugMessage(LOG_QUERY, "Having query login attempt for username {}", username); + std::string username, password; + if(cmd[0].has("client_login_name") && cmd[0].has("client_login_password")){ + username = cmd["client_login_name"].string(); + password = cmd["client_login_password"].string(); + } else { + username = cmd[0][0].key(); + password = cmd[0][1].key(); + } + debugMessage(LOG_QUERY, "Having query login attempt for username {}", username); - auto _account = serverInstance->getQueryServer()->find_query_account_by_name(username); - auto account = _account ? serverInstance->getQueryServer()->load_password(_account) : nullptr; + auto _account = serverInstance->getQueryServer()->find_query_account_by_name(username); + auto account = _account ? serverInstance->getQueryServer()->load_password(_account) : nullptr; - { - threads::MutexLock lock(this->handle->loginLock); + { + threads::MutexLock lock(this->handle->loginLock); - if(!account) - return {findError("client_invalid_password"), "username or password dose not match"}; + if(!account) + return {findError("client_invalid_password"), "username or password dose not match"}; - if (account->password != password) { - if(!this->whitelisted) { - this->handle->loginAttempts[this->getPeerIp()]++; - if(this->handle->loginAttempts[this->getPeerIp()] > 3) { - this->handle->queryBann[this->getPeerIp()] = system_clock::now() + seconds(serverInstance->properties()[property::SERVERINSTANCE_SERVERQUERY_BAN_TIME].as()); //TODO configurable | Disconnect all others? - this->postCommandHandler.emplace_back([&](){ - this->closeConnection(system_clock::now() + seconds(1)); - }); - return {findError("ban_flooding"), ""}; - } - } - return {findError("client_invalid_password"), "username or password dose not match"}; - } - } - if(!this->properties()[property::CLIENT_LOGIN_NAME].as().empty()) { - Command log("logout"); - if(!this->handleCommandLogout(log)) return {ErrorType::VSError, "Failed to logout!"}; - } + if (account->password != password) { + if(!this->whitelisted) { + this->handle->loginAttempts[this->getPeerIp()]++; + if(this->handle->loginAttempts[this->getPeerIp()] > 3) { + this->handle->queryBann[this->getPeerIp()] = system_clock::now() + seconds(serverInstance->properties()[property::SERVERINSTANCE_SERVERQUERY_BAN_TIME].as()); //TODO configurable | Disconnect all others? + this->postCommandHandler.emplace_back([&](){ + this->closeConnection(system_clock::now() + seconds(1)); + }); + return {findError("ban_flooding"), ""}; + } + } + return {findError("client_invalid_password"), "username or password dose not match"}; + } + } + if(!this->properties()[property::CLIENT_LOGIN_NAME].as().empty()) { + Command log("logout"); + if(!this->handleCommandLogout(log)) return {ErrorType::VSError, "Failed to logout!"}; + } - this->query_account = account; + this->query_account = account; - auto joined_channel = this->currentChannel; - if(this->server) { - { - unique_lock tree_lock(this->server->channel_tree_lock); - if(joined_channel) - 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->groups->disableCache(this->getClientDatabaseId()); - } else serverInstance->getGroupManager()->disableCache(this->getClientDatabaseId()); + auto joined_channel = this->currentChannel; + if(this->server) { + { + unique_lock tree_lock(this->server->channel_tree_lock); + if(joined_channel) + 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->groups->disableCache(this->getClientDatabaseId()); + } 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); - this->properties()[property::CLIENT_LOGIN_NAME] = username; - this->properties()[property::CLIENT_UNIQUE_IDENTIFIER] = account->unique_id; //TODO load from table - this->properties()[property::CLIENT_NICKNAME] = username; + this->properties()[property::CLIENT_LOGIN_NAME] = username; + this->properties()[property::CLIENT_UNIQUE_IDENTIFIER] = account->unique_id; //TODO load from table + this->properties()[property::CLIENT_NICKNAME] = username; - auto target_server = this->server; /* keep the server alive 'ill we've joined the server */ - if(account->bound_server) { - target_server = serverInstance->getVoiceServerManager()->findServerById(account->bound_server); - if(target_server != this->server) - joined_channel = nullptr; - if(!target_server) - return {findError("server_invalid_id"), "server does not exists anymore"}; - } - this->server = target_server; + auto target_server = this->server; /* keep the server alive 'ill we've joined the server */ + if(account->bound_server) { + target_server = serverInstance->getVoiceServerManager()->findServerById(account->bound_server); + if(target_server != this->server) + joined_channel = nullptr; + if(!target_server) + return {findError("server_invalid_id"), "server does not exists anymore"}; + } + this->server = target_server; - DatabaseHelper::assignDatabaseId(this->sql, static_cast(target_server ? target_server->getServerId() : 0), _this.lock()); - if(target_server) { - target_server->groups->enableCache(this->getClientDatabaseId()); - target_server->registerClient(_this.lock()); + DatabaseHelper::assignDatabaseId(this->sql, static_cast(target_server ? target_server->getServerId() : 0), _this.lock()); + if(target_server) { + target_server->groups->enableCache(this->getClientDatabaseId()); + target_server->registerClient(_this.lock()); - { - shared_lock server_tree_lock(target_server->channel_tree_lock); - if(joined_channel) /* needs only notify if we were already on that server within a channel */ - target_server->notifyClientPropertyUpdates(_this.lock(), deque{property::CLIENT_NICKNAME, property::CLIENT_UNIQUE_IDENTIFIER}); + { + shared_lock server_tree_lock(target_server->channel_tree_lock); + if(joined_channel) /* needs only notify if we were already on that server within a channel */ + target_server->notifyClientPropertyUpdates(_this.lock(), deque{property::CLIENT_NICKNAME, property::CLIENT_UNIQUE_IDENTIFIER}); - unique_lock client_tree_lock(this->channel_lock); - this->channels->reset(); - this->channels->insert_channels(target_server->channelTree->tree_head(), true, false); - this->subscribeChannel(this->server->channelTree->channels(), false, false); - } + unique_lock client_tree_lock(this->channel_lock); + this->channels->reset(); + this->channels->insert_channels(target_server->channelTree->tree_head(), true, false); + this->subscribeChannel(this->server->channelTree->channels(), false, false); + } - if(joined_channel) { - unique_lock tree_lock(this->server->channel_tree_lock); - if(joined_channel) - this->server->client_move(this->ref(), joined_channel, nullptr, "", ViewReasonId::VREASON_USER_ACTION, false, tree_lock); - } else if(!this->permissionGranted(permission::PERMTEST_ORDERED, permission::b_virtualserver_select_godmode, 1)) - this->server->assignDefaultChannel(this->ref(), true); - else - this->update_cached_permissions(); - } else { - serverInstance->getGroupManager()->enableCache(this->getClientDatabaseId()); - this->update_cached_permissions(); - } + if(joined_channel) { + unique_lock tree_lock(this->server->channel_tree_lock); + if(joined_channel) + this->server->client_move(this->ref(), joined_channel, nullptr, "", ViewReasonId::VREASON_USER_ACTION, false, tree_lock); + } else if(!this->permissionGranted(permission::PERMTEST_ORDERED, permission::b_virtualserver_select_godmode, 1)) + this->server->assignDefaultChannel(this->ref(), true); + else + this->update_cached_permissions(); + } else { + serverInstance->getGroupManager()->enableCache(this->getClientDatabaseId()); + this->update_cached_permissions(); + } - this->properties()[property::CLIENT_TOTALCONNECTIONS]++; - this->updateChannelClientProperties(true, true); + this->properties()[property::CLIENT_TOTALCONNECTIONS]++; + this->updateChannelClientProperties(true, true); - return CommandResult::Success; + return CommandResult::Success; } CommandResult QueryClient::handleCommandLogout(Command &) { - CMD_RESET_IDLE; - if(this->properties()[property::CLIENT_LOGIN_NAME].as().empty()) return {findError("client_not_logged_in"), "not logged in"}; - this->properties()[property::CLIENT_LOGIN_NAME] = ""; - this->query_account = nullptr; + CMD_RESET_IDLE; + if(this->properties()[property::CLIENT_LOGIN_NAME].as().empty()) return {findError("client_not_logged_in"), "not logged in"}; + this->properties()[property::CLIENT_LOGIN_NAME] = ""; + this->query_account = nullptr; - auto joined = this->currentChannel; - if(this->server){ - { - unique_lock tree_lock(this->server->channel_tree_lock); - if(joined) - 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->groups->disableCache(this->getClientDatabaseId()); - } else serverInstance->getGroupManager()->disableCache(this->getClientDatabaseId()); + auto joined = this->currentChannel; + if(this->server){ + { + unique_lock tree_lock(this->server->channel_tree_lock); + if(joined) + 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->groups->disableCache(this->getClientDatabaseId()); + } else serverInstance->getGroupManager()->disableCache(this->getClientDatabaseId()); - 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())); - DatabaseHelper::assignDatabaseId(this->sql, static_cast(this->server ? this->server->getServerId() : 0), _this.lock()); + 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())); + DatabaseHelper::assignDatabaseId(this->sql, static_cast(this->server ? this->server->getServerId() : 0), _this.lock()); - if(this->server){ - this->server->groups->enableCache(this->getClientDatabaseId()); - this->server->registerClient(this->ref()); + if(this->server){ + this->server->groups->enableCache(this->getClientDatabaseId()); + this->server->registerClient(this->ref()); - { - shared_lock server_channel_r_lock(this->server->channel_tree_lock); - unique_lock client_channel_lock(this->channel_lock); + { + shared_lock server_channel_r_lock(this->server->channel_tree_lock); + unique_lock client_channel_lock(this->channel_lock); - this->channels->reset(); - this->channels->insert_channels(this->server->channelTree->tree_head(), true, false); - this->subscribeChannel(this->server->channelTree->channels(), false, false); - } + this->channels->reset(); + this->channels->insert_channels(this->server->channelTree->tree_head(), true, false); + this->subscribeChannel(this->server->channelTree->channels(), false, false); + } - if(joined) { - unique_lock server_channel_w_lock(this->server->channel_tree_lock, defer_lock); - this->server->client_move(this->ref(), joined, nullptr, "", ViewReasonId::VREASON_USER_ACTION, false, server_channel_w_lock); - } else if(!this->permissionGranted(permission::PERMTEST_ORDERED, permission::b_virtualserver_select_godmode, 1)) { - this->server->assignDefaultChannel(this->ref(), true); - } else { - this->update_cached_permissions(); - } - } else { - serverInstance->getGroupManager()->enableCache(this->getClientDatabaseId()); - this->update_cached_permissions(); - } + if(joined) { + unique_lock server_channel_w_lock(this->server->channel_tree_lock, defer_lock); + this->server->client_move(this->ref(), joined, nullptr, "", ViewReasonId::VREASON_USER_ACTION, false, server_channel_w_lock); + } else if(!this->permissionGranted(permission::PERMTEST_ORDERED, permission::b_virtualserver_select_godmode, 1)) { + this->server->assignDefaultChannel(this->ref(), true); + } else { + this->update_cached_permissions(); + } + } else { + serverInstance->getGroupManager()->enableCache(this->getClientDatabaseId()); + this->update_cached_permissions(); + } - this->updateChannelClientProperties(true, true); + this->updateChannelClientProperties(true, true); - return CommandResult::Success; + return CommandResult::Success; } CommandResult QueryClient::handleCommandServerSelect(Command &cmd) { - CMD_RESET_IDLE; - shared_ptr target; + CMD_RESET_IDLE; + shared_ptr target; - if(cmd[0].has("port")){ - target = serverInstance->getVoiceServerManager()->findServerByPort(cmd["port"].as()); - } else if(cmd[0].has("sid")) { - target = serverInstance->getVoiceServerManager()->findServerById(cmd["sid"].as()); - } + if(cmd[0].has("port")){ + target = serverInstance->getVoiceServerManager()->findServerByPort(cmd["port"].as()); + } else if(cmd[0].has("sid")) { + target = serverInstance->getVoiceServerManager()->findServerById(cmd["sid"].as()); + } - for(auto& parm : cmd[0].keys()) - if(parm.find_first_not_of("0123456789") == string::npos) - target = serverInstance->getVoiceServerManager()->findServerById(static_cast(stol(parm))); + for(auto& parm : cmd[0].keys()) + if(parm.find_first_not_of("0123456789") == string::npos) + target = serverInstance->getVoiceServerManager()->findServerById(static_cast(stol(parm))); - if(!target && (!cmd[0].has("0") && (!cmd[0].has("sid") || !cmd["sid"] == 0))) return {findError("server_invalid_id"), "Invalid server id"}; + if(!target && (!cmd[0].has("0") && (!cmd[0].has("sid") || !cmd["sid"] == 0))) return {findError("server_invalid_id"), "Invalid server id"}; - if(target == this->server) return CommandResult::Success; + if(target == this->server) return CommandResult::Success; - if(target) { - if(this->query_account && this->query_account->bound_server > 0) { - if(target->getServerId() != this->query_account->bound_server) - return {findError("server_invalid_id"), "You're a server bound query, and the target server isn't your origin."}; - } else { - auto allowed = target->calculatePermission(permission::PERMTEST_ORDERED, this->getClientDatabaseId(), permission::b_virtualserver_select, this->getType(), nullptr); - if(allowed != -1 && allowed <= 0) return CommandResultPermissionError{permission::b_virtualserver_select}; - } - } + if(target) { + if(this->query_account && this->query_account->bound_server > 0) { + if(target->getServerId() != this->query_account->bound_server) + return {findError("server_invalid_id"), "You're a server bound query, and the target server isn't your origin."}; + } else { + auto allowed = target->calculatePermission(permission::PERMTEST_ORDERED, this->getClientDatabaseId(), permission::b_virtualserver_select, this->getType(), nullptr); + if(allowed != -1 && allowed <= 0) return CommandResultPermissionError{permission::b_virtualserver_select}; + } + } - { - auto server_locked = this->server; - if(server_locked) { - //unregister manager from old server - { - unique_lock tree_lock(this->server->channel_tree_lock); - if(this->currentChannel) - this->server->client_move(this->ref(), nullptr, nullptr, "", ViewReasonId::VREASON_USER_ACTION, false, tree_lock); - this->server->unregisterClient(_this.lock(), "server switch", tree_lock); - } - server_locked->groups->disableCache(this->getClientDatabaseId()); - this->channels->reset(); - } else - serverInstance->getGroupManager()->disableCache(this->getClientDatabaseId()); - } - this->resetEventMask(); + { + auto server_locked = this->server; + if(server_locked) { + //unregister manager from old server + { + unique_lock tree_lock(this->server->channel_tree_lock); + if(this->currentChannel) + this->server->client_move(this->ref(), nullptr, nullptr, "", ViewReasonId::VREASON_USER_ACTION, false, tree_lock); + this->server->unregisterClient(_this.lock(), "server switch", tree_lock); + } + server_locked->groups->disableCache(this->getClientDatabaseId()); + this->channels->reset(); + } else + serverInstance->getGroupManager()->disableCache(this->getClientDatabaseId()); + } + this->resetEventMask(); - //register at current server - { - //unique_lock server_lock(this->server_lock); - /* We dont need to lock the server because only one command can be executed at the time. Everything else should copy the server once and test the copy and use that ref then */ - this->server = target; - } + //register at current server + { + //unique_lock server_lock(this->server_lock); + /* We dont need to lock the server because only one command can be executed at the time. Everything else should copy the server once and test the copy and use that ref then */ + this->server = target; + } - if(cmd[0].has("client_nickname")) - this->properties()[property::CLIENT_NICKNAME] = cmd["client_nickname"].string(); + if(cmd[0].has("client_nickname")) + this->properties()[property::CLIENT_NICKNAME] = cmd["client_nickname"].string(); - DatabaseHelper::assignDatabaseId(this->sql, static_cast(this->server ? this->server->getServerId() : 0), _this.lock()); - if(this->server) { - this->server->groups->enableCache(this->getClientDatabaseId()); - this->server->registerClient(_this.lock()); + DatabaseHelper::assignDatabaseId(this->sql, static_cast(this->server ? this->server->getServerId() : 0), _this.lock()); + if(this->server) { + this->server->groups->enableCache(this->getClientDatabaseId()); + this->server->registerClient(_this.lock()); - { - shared_lock server_channel_lock(target->channel_tree_lock); - unique_lock client_channel_lock(this->channel_lock); + { + shared_lock server_channel_lock(target->channel_tree_lock); + unique_lock client_channel_lock(this->channel_lock); - this->subscribeToAll = true; - this->channels->insert_channels(this->server->channelTree->tree_head(), true, false); - this->subscribeChannel(this->server->channelTree->channels(), false, false); - } + this->subscribeToAll = true; + this->channels->insert_channels(this->server->channelTree->tree_head(), true, false); + this->subscribeChannel(this->server->channelTree->channels(), false, false); + } - auto negated_enforce_join = this->permissionGranted(permission::PERMTEST_ORDERED, permission::b_virtualserver_select_godmode, 1); - if(!negated_enforce_join) - this->server->assignDefaultChannel(this->ref(), true); - else - this->update_cached_permissions(); - } else { - serverInstance->getGroupManager()->enableCache(this->getClientDatabaseId()); - this->update_cached_permissions(); - } - this->updateChannelClientProperties(true, true); + auto negated_enforce_join = this->permissionGranted(permission::PERMTEST_ORDERED, permission::b_virtualserver_select_godmode, 1); + if(!negated_enforce_join) + this->server->assignDefaultChannel(this->ref(), true); + else + this->update_cached_permissions(); + } else { + serverInstance->getGroupManager()->enableCache(this->getClientDatabaseId()); + this->update_cached_permissions(); + } + this->updateChannelClientProperties(true, true); - return CommandResult::Success; + return CommandResult::Success; } CommandResult QueryClient::handleCommandJoin(Command &) { @@ -391,89 +391,89 @@ CommandResult QueryClient::handleCommandLeft(Command&) { CommandResult QueryClient::handleCommandServerInfo(Command &) { CMD_RESET_IDLE; - PERM_CHECKR(permission::b_virtualserver_info_view, 1, true); + PERM_CHECKR(permission::b_virtualserver_info_view, 1, true); Command cmd(""); for(const auto &prop : (this->server ? this->server->properties() : *serverInstance->getDefaultServerProperties()).list_properties(property::FLAG_SERVER_VIEW | property::FLAG_SERVER_VARIABLE, this->getType() == CLIENT_TEAMSPEAK ? property::FLAG_NEW : (uint16_t) 0)) { - cmd[prop.type().name] = prop.as(); + cmd[prop.type().name] = prop.as(); if(prop.type() == property::VIRTUALSERVER_HOST) - cmd["virtualserver_ip"] = prop.as(); + cmd["virtualserver_ip"] = prop.as(); } - cmd["virtualserver_status"] = this->server ? ServerState::string(this->server->state) : "template"; + cmd["virtualserver_status"] = this->server ? ServerState::string(this->server->state) : "template"; - if(this->server && this->permissionGranted(permission::PERMTEST_ORDERED, permission::b_virtualserver_connectioninfo_view, 1, nullptr, true)) { - auto stats = this->server->getServerStatistics()->statistics(); - auto report = this->server->serverStatistics->dataReport(); - cmd["connection_bandwidth_sent_last_second_total"] = report.send_second; - cmd["connection_bandwidth_sent_last_minute_total"] = report.send_minute; - cmd["connection_bandwidth_received_last_second_total"] = report.recv_second; - cmd["connection_bandwidth_received_last_minute_total"] = report.recv_minute; + if(this->server && this->permissionGranted(permission::PERMTEST_ORDERED, permission::b_virtualserver_connectioninfo_view, 1, nullptr, true)) { + auto stats = this->server->getServerStatistics()->statistics(); + auto report = this->server->serverStatistics->dataReport(); + cmd["connection_bandwidth_sent_last_second_total"] = report.send_second; + cmd["connection_bandwidth_sent_last_minute_total"] = report.send_minute; + cmd["connection_bandwidth_received_last_second_total"] = report.recv_second; + cmd["connection_bandwidth_received_last_minute_total"] = report.recv_minute; - cmd["connection_filetransfer_bandwidth_sent"] = report.file_send; - cmd["connection_filetransfer_bandwidth_received"] = report.file_recv; - cmd["connection_filetransfer_bytes_sent_total"] = (*stats)[property::CONNECTION_FILETRANSFER_BYTES_SENT_TOTAL].as(); - cmd["connection_filetransfer_bytes_received_total"] = (*stats)[property::CONNECTION_FILETRANSFER_BYTES_RECEIVED_TOTAL].as(); + cmd["connection_filetransfer_bandwidth_sent"] = report.file_send; + cmd["connection_filetransfer_bandwidth_received"] = report.file_recv; + cmd["connection_filetransfer_bytes_sent_total"] = (*stats)[property::CONNECTION_FILETRANSFER_BYTES_SENT_TOTAL].as(); + cmd["connection_filetransfer_bytes_received_total"] = (*stats)[property::CONNECTION_FILETRANSFER_BYTES_RECEIVED_TOTAL].as(); - cmd["connection_packets_sent_speech"] = (*stats)[property::CONNECTION_FILETRANSFER_BANDWIDTH_SENT].value(); - cmd["connection_bytes_sent_speech"] = (*stats)[property::CONNECTION_FILETRANSFER_BANDWIDTH_RECEIVED].value(); - cmd["connection_packets_received_speech"] = (*stats)[property::CONNECTION_FILETRANSFER_BYTES_SENT_TOTAL].value(); - cmd["connection_bytes_received_speech"] = (*stats)[property::CONNECTION_FILETRANSFER_BYTES_RECEIVED_TOTAL].value(); + cmd["connection_packets_sent_speech"] = (*stats)[property::CONNECTION_FILETRANSFER_BANDWIDTH_SENT].value(); + cmd["connection_bytes_sent_speech"] = (*stats)[property::CONNECTION_FILETRANSFER_BANDWIDTH_RECEIVED].value(); + cmd["connection_packets_received_speech"] = (*stats)[property::CONNECTION_FILETRANSFER_BYTES_SENT_TOTAL].value(); + cmd["connection_bytes_received_speech"] = (*stats)[property::CONNECTION_FILETRANSFER_BYTES_RECEIVED_TOTAL].value(); - cmd["connection_packets_sent_keepalive"] = (*stats)[property::CONNECTION_PACKETS_SENT_KEEPALIVE].value(); - cmd["connection_packets_received_keepalive"] = (*stats)[property::CONNECTION_PACKETS_RECEIVED_KEEPALIVE].value(); - cmd["connection_bytes_received_keepalive"] = (*stats)[property::CONNECTION_BYTES_RECEIVED_KEEPALIVE].value(); - cmd["connection_bytes_sent_keepalive"] = (*stats)[property::CONNECTION_BYTES_SENT_KEEPALIVE].value(); + cmd["connection_packets_sent_keepalive"] = (*stats)[property::CONNECTION_PACKETS_SENT_KEEPALIVE].value(); + cmd["connection_packets_received_keepalive"] = (*stats)[property::CONNECTION_PACKETS_RECEIVED_KEEPALIVE].value(); + cmd["connection_bytes_received_keepalive"] = (*stats)[property::CONNECTION_BYTES_RECEIVED_KEEPALIVE].value(); + cmd["connection_bytes_sent_keepalive"] = (*stats)[property::CONNECTION_BYTES_SENT_KEEPALIVE].value(); - cmd["connection_packets_sent_control"] = (*stats)[property::CONNECTION_PACKETS_SENT_CONTROL].value(); - cmd["connection_bytes_sent_control"] = (*stats)[property::CONNECTION_BYTES_SENT_CONTROL].value(); - cmd["connection_packets_received_control"] = (*stats)[property::CONNECTION_PACKETS_RECEIVED_CONTROL].value(); - cmd["connection_bytes_received_control"] = (*stats)[property::CONNECTION_BYTES_RECEIVED_CONTROL].value(); + cmd["connection_packets_sent_control"] = (*stats)[property::CONNECTION_PACKETS_SENT_CONTROL].value(); + cmd["connection_bytes_sent_control"] = (*stats)[property::CONNECTION_BYTES_SENT_CONTROL].value(); + cmd["connection_packets_received_control"] = (*stats)[property::CONNECTION_PACKETS_RECEIVED_CONTROL].value(); + cmd["connection_bytes_received_control"] = (*stats)[property::CONNECTION_BYTES_RECEIVED_CONTROL].value(); - cmd["connection_packets_sent_total"] = (*stats)[property::CONNECTION_PACKETS_SENT_TOTAL].value(); - cmd["connection_bytes_sent_total"] = (*stats)[property::CONNECTION_BYTES_SENT_TOTAL].value(); - cmd["connection_packets_received_total"] = (*stats)[property::CONNECTION_PACKETS_RECEIVED_TOTAL].value(); - cmd["connection_bytes_received_total"] = (*stats)[property::CONNECTION_BYTES_RECEIVED_TOTAL].value(); - } else { - cmd["connection_bandwidth_sent_last_second_total"] = "0"; - cmd["connection_bandwidth_sent_last_minute_total"] = "0"; - cmd["connection_bandwidth_received_last_second_total"] = "0"; - cmd["connection_bandwidth_received_last_minute_total"] = "0"; + cmd["connection_packets_sent_total"] = (*stats)[property::CONNECTION_PACKETS_SENT_TOTAL].value(); + cmd["connection_bytes_sent_total"] = (*stats)[property::CONNECTION_BYTES_SENT_TOTAL].value(); + cmd["connection_packets_received_total"] = (*stats)[property::CONNECTION_PACKETS_RECEIVED_TOTAL].value(); + cmd["connection_bytes_received_total"] = (*stats)[property::CONNECTION_BYTES_RECEIVED_TOTAL].value(); + } else { + cmd["connection_bandwidth_sent_last_second_total"] = "0"; + cmd["connection_bandwidth_sent_last_minute_total"] = "0"; + cmd["connection_bandwidth_received_last_second_total"] = "0"; + cmd["connection_bandwidth_received_last_minute_total"] = "0"; - cmd["connection_filetransfer_bandwidth_sent"] = "0"; - cmd["connection_filetransfer_bandwidth_received"] = "0"; - cmd["connection_filetransfer_bytes_sent_total"] = "0"; - cmd["connection_filetransfer_bytes_received_total"] = "0"; + cmd["connection_filetransfer_bandwidth_sent"] = "0"; + cmd["connection_filetransfer_bandwidth_received"] = "0"; + cmd["connection_filetransfer_bytes_sent_total"] = "0"; + cmd["connection_filetransfer_bytes_received_total"] = "0"; - cmd["connection_packets_sent_speech"] = "0"; - cmd["connection_bytes_sent_speech"] = "0"; - cmd["connection_packets_received_speech"] = "0"; - cmd["connection_bytes_received_speech"] = "0"; + cmd["connection_packets_sent_speech"] = "0"; + cmd["connection_bytes_sent_speech"] = "0"; + cmd["connection_packets_received_speech"] = "0"; + cmd["connection_bytes_received_speech"] = "0"; - cmd["connection_packets_sent_keepalive"] = "0"; - cmd["connection_packets_received_keepalive"] = "0"; - cmd["connection_bytes_received_keepalive"] = "0"; - cmd["connection_bytes_sent_keepalive"] = "0"; + cmd["connection_packets_sent_keepalive"] = "0"; + cmd["connection_packets_received_keepalive"] = "0"; + cmd["connection_bytes_received_keepalive"] = "0"; + cmd["connection_bytes_sent_keepalive"] = "0"; - cmd["connection_packets_sent_control"] = "0"; - cmd["connection_bytes_sent_control"] = "0"; - cmd["connection_packets_received_control"] = "0"; - cmd["connection_bytes_received_control"] = "0"; + cmd["connection_packets_sent_control"] = "0"; + cmd["connection_bytes_sent_control"] = "0"; + cmd["connection_packets_received_control"] = "0"; + cmd["connection_bytes_received_control"] = "0"; - cmd["connection_packets_sent_total"] = "0"; - cmd["connection_bytes_sent_total"] = "0"; - cmd["connection_packets_received_total"] = "0"; - cmd["connection_bytes_received_total"] = "0"; - } + cmd["connection_packets_sent_total"] = "0"; + cmd["connection_bytes_sent_total"] = "0"; + cmd["connection_packets_received_total"] = "0"; + cmd["connection_bytes_received_total"] = "0"; + } this->sendCommand(cmd); return CommandResult::Success; } CommandResult QueryClient::handleCommandChannelList(Command& cmd) { - PERM_CHECKR(permission::b_virtualserver_channel_list, 1, true); + PERM_CHECKR(permission::b_virtualserver_channel_list, 1, true); CMD_RESET_IDLE; Command result(""); @@ -481,53 +481,53 @@ CommandResult QueryClient::handleCommandChannelList(Command& cmd) { int index = 0; shared_lock channel_lock(this->server ? this->server->channel_tree_lock : serverInstance->getChannelTreeLock()); auto entries = this->server ? this->channels->channels() : serverInstance->getChannelTree()->channels(); - channel_lock.unlock(); + channel_lock.unlock(); - for(const auto& channel : entries){ - if(!channel) continue; + for(const auto& channel : entries){ + if(!channel) continue; - auto& bulk = result[index]; - bulk["cid"] = channel->channelId(); - bulk["pid"] = channel->properties()[property::CHANNEL_PID].as(); - bulk["channel_name"] = channel->name(); - bulk["channel_order"] = channel->channelOrder(); - bulk["total_clients"] = this->server ? this->server->getClientsByChannel(channel).size() : 0; - /* bulk["channel_needed_subscribe_power"] = channel->permissions()->getPermissionValue(permission::PERMTEST_ORDERED, permission::i_channel_needed_subscribe_power, channel, 0); */ + auto& bulk = result[index]; + bulk["cid"] = channel->channelId(); + bulk["pid"] = channel->properties()[property::CHANNEL_PID].as(); + bulk["channel_name"] = channel->name(); + bulk["channel_order"] = channel->channelOrder(); + bulk["total_clients"] = this->server ? this->server->getClientsByChannel(channel).size() : 0; + /* bulk["channel_needed_subscribe_power"] = channel->permissions()->getPermissionValue(permission::PERMTEST_ORDERED, permission::i_channel_needed_subscribe_power, channel, 0); */ - if(cmd.hasParm("flags")){ - bulk["channel_flag_default"] = channel->properties()[property::CHANNEL_FLAG_DEFAULT].as(); - bulk["channel_flag_password"] = channel->properties()[property::CHANNEL_FLAG_PASSWORD].as(); - bulk["channel_flag_permanent"] = channel->properties()[property::CHANNEL_FLAG_PERMANENT].as(); - bulk["channel_flag_semi_permanent"] = channel->properties()[property::CHANNEL_FLAG_SEMI_PERMANENT].as(); - } - if(cmd.hasParm("voice")){ - bulk["channel_codec"] = channel->properties()[property::CHANNEL_CODEC].as(); - bulk["channel_codec_quality"] = channel->properties()[property::CHANNEL_CODEC_QUALITY].as(); - bulk["channel_needed_talk_power"] = channel->properties()[property::CHANNEL_NEEDED_TALK_POWER].as(); - } - if(cmd.hasParm("icon")){ - bulk["channel_icon_id"] = channel->properties()[property::CHANNEL_ICON_ID].as(); - } - if(cmd.hasParm("limits")){ - bulk["total_clients_family"] = this->server ? this->server->getClientsByChannelRoot(channel, false).size() : 0; - bulk["total_clients"] = this->server ? this->server->getClientsByChannel(channel).size() : 0; + if(cmd.hasParm("flags")){ + bulk["channel_flag_default"] = channel->properties()[property::CHANNEL_FLAG_DEFAULT].as(); + bulk["channel_flag_password"] = channel->properties()[property::CHANNEL_FLAG_PASSWORD].as(); + bulk["channel_flag_permanent"] = channel->properties()[property::CHANNEL_FLAG_PERMANENT].as(); + bulk["channel_flag_semi_permanent"] = channel->properties()[property::CHANNEL_FLAG_SEMI_PERMANENT].as(); + } + if(cmd.hasParm("voice")){ + bulk["channel_codec"] = channel->properties()[property::CHANNEL_CODEC].as(); + bulk["channel_codec_quality"] = channel->properties()[property::CHANNEL_CODEC_QUALITY].as(); + bulk["channel_needed_talk_power"] = channel->properties()[property::CHANNEL_NEEDED_TALK_POWER].as(); + } + if(cmd.hasParm("icon")){ + bulk["channel_icon_id"] = channel->properties()[property::CHANNEL_ICON_ID].as(); + } + if(cmd.hasParm("limits")){ + bulk["total_clients_family"] = this->server ? this->server->getClientsByChannelRoot(channel, false).size() : 0; + bulk["total_clients"] = this->server ? this->server->getClientsByChannel(channel).size() : 0; - bulk["channel_maxclients"] = channel->properties()[property::CHANNEL_MAXCLIENTS].as(); - bulk["channel_maxfamilyclients"] = channel->properties()[property::CHANNEL_MAXFAMILYCLIENTS].as(); + bulk["channel_maxclients"] = channel->properties()[property::CHANNEL_MAXCLIENTS].as(); + bulk["channel_maxfamilyclients"] = channel->properties()[property::CHANNEL_MAXFAMILYCLIENTS].as(); - { - auto needed_power = channel->permissions()->permission_value_flagged(permission::i_channel_subscribe_power); - bulk["channel_needed_subscribe_power"] = needed_power.has_value ? needed_power.value : 0; - } - } - if(cmd.hasParm("topic")) { - bulk["channel_topic"] = channel->properties()[property::CHANNEL_TOPIC].as(); - } - if(cmd.hasParm("times")){ - bulk["seconds_empty"] = channel->emptySince(); - } - index++; - } + { + auto needed_power = channel->permissions()->permission_value_flagged(permission::i_channel_subscribe_power); + bulk["channel_needed_subscribe_power"] = needed_power.has_value ? needed_power.value : 0; + } + } + if(cmd.hasParm("topic")) { + bulk["channel_topic"] = channel->properties()[property::CHANNEL_TOPIC].as(); + } + if(cmd.hasParm("times")){ + bulk["seconds_empty"] = channel->emptySince(); + } + index++; + } this->sendCommand(result); return CommandResult::Success; @@ -535,7 +535,7 @@ CommandResult QueryClient::handleCommandChannelList(Command& cmd) { CommandResult QueryClient::handleCommandServerList(Command& cmd) { CMD_RESET_IDLE; - PERM_CHECKR(permission::b_serverinstance_virtualserver_list, 1, true); + PERM_CHECKR(permission::b_serverinstance_virtualserver_list, 1, true); Command res(""); int index = 0; @@ -543,9 +543,9 @@ CommandResult QueryClient::handleCommandServerList(Command& cmd) { res[index]["virtualserver_id"] = server->getServerId(); res[index]["virtualserver_host"] = server->properties()[property::VIRTUALSERVER_HOST].as(); res[index]["virtualserver_port"] = server->properties()[property::VIRTUALSERVER_PORT].as(); - res[index]["virtualserver_web_host"] = server->properties()[property::VIRTUALSERVER_WEB_HOST].as(); - res[index]["virtualserver_web_port"] = server->properties()[property::VIRTUALSERVER_WEB_PORT].as(); - res[index]["virtualserver_status"] = ServerState::string(server->state); + res[index]["virtualserver_web_host"] = server->properties()[property::VIRTUALSERVER_WEB_HOST].as(); + res[index]["virtualserver_web_port"] = server->properties()[property::VIRTUALSERVER_WEB_PORT].as(); + res[index]["virtualserver_status"] = ServerState::string(server->state); res[index]["virtualserver_clientsonline"] = server->properties()[property::VIRTUALSERVER_CLIENTS_ONLINE].as(); res[index]["virtualserver_queryclientsonline"] = server->properties()[property::VIRTUALSERVER_QUERYCLIENTS_ONLINE].as(); res[index]["virtualserver_maxclients"] = server->properties()[property::VIRTUALSERVER_MAXCLIENTS].as(); @@ -568,84 +568,84 @@ CommandResult QueryClient::handleCommandServerList(Command& cmd) { CommandResult QueryClient::handleCommandServerCreate(Command& cmd) { CMD_RESET_IDLE; - PERM_CHECKR(permission::b_virtualserver_create, 1, true); + PERM_CHECKR(permission::b_virtualserver_create, 1, true); if(serverInstance->getVoiceServerManager()->getState() != ServerManager::STARTED) { - return {findError("vs_critical"), "Server manager isn't started yet or not finished starting"}; + return {findError("vs_critical"), "Server manager isn't started yet or not finished starting"}; } - string startError; - shared_ptr server; + string startError; + shared_ptr server; - milliseconds time_create, time_wait, time_start, time_global; - { - auto start = system_clock::now(); - threads::MutexLock lock(serverInstance->getVoiceServerManager()->server_create_lock); - auto instances = serverInstance->getVoiceServerManager()->serverInstances(); - if(config::server::max_virtual_server != -1 && instances.size() > config::server::max_virtual_server) - return {findError("server_max_vs_reached"), "You reached the via config.yml enabled virtual server limit."}; - if(instances.size() >= 65535) - return {findError("server_max_vs_reached"), "You cant create anymore virtual servers. (Software limit reached)"}; - { - auto end = system_clock::now(); - time_wait = duration_cast(end - start); - } + milliseconds time_create, time_wait, time_start, time_global; + { + auto start = system_clock::now(); + threads::MutexLock lock(serverInstance->getVoiceServerManager()->server_create_lock); + auto instances = serverInstance->getVoiceServerManager()->serverInstances(); + if(config::server::max_virtual_server != -1 && instances.size() > config::server::max_virtual_server) + return {findError("server_max_vs_reached"), "You reached the via config.yml enabled virtual server limit."}; + if(instances.size() >= 65535) + return {findError("server_max_vs_reached"), "You cant create anymore virtual servers. (Software limit reached)"}; + { + auto end = system_clock::now(); + time_wait = duration_cast(end - start); + } - uint16_t freePort = serverInstance->getVoiceServerManager()->next_available_port(); + uint16_t freePort = serverInstance->getVoiceServerManager()->next_available_port(); - std::string host = cmd[0].has("virtualserver_host") ? cmd["virtualserver_host"].as() : config::binding::DefaultVoiceHost; - uint16_t port = cmd[0].has("virtualserver_port") ? cmd["virtualserver_port"].as() : freePort; - { - auto _start = system_clock::now(); - server = serverInstance->getVoiceServerManager()->createServer(host, port); - auto _end = system_clock::now(); - time_create = duration_cast(_end - _start); - } - if(!server) return {findError("vs_critical"), "could not create new server"}; + std::string host = cmd[0].has("virtualserver_host") ? cmd["virtualserver_host"].as() : config::binding::DefaultVoiceHost; + uint16_t port = cmd[0].has("virtualserver_port") ? cmd["virtualserver_port"].as() : freePort; + { + auto _start = system_clock::now(); + server = serverInstance->getVoiceServerManager()->createServer(host, port); + auto _end = system_clock::now(); + time_create = duration_cast(_end - _start); + } + if(!server) return {findError("vs_critical"), "could not create new server"}; - for(const auto& key : cmd[0].keys()){ - if(key == "virtualserver_port") continue; - if(key == "virtualserver_host") continue; + for(const auto& key : cmd[0].keys()){ + if(key == "virtualserver_port") continue; + if(key == "virtualserver_host") continue; - auto info = property::impl::info(key); - if(*info == property::VIRTUALSERVER_UNDEFINED) { - logError(server->getServerId(), "Tried to change unknown server property " + key); - continue; - } - if(!info->validate_input(cmd[key].as())) { - logError(server->getServerId(), "Tried to change " + key + " to an invalid value: " + cmd[key].as()); - continue; - } - server->properties()[info] = cmd[key].string(); - } + auto info = property::impl::info(key); + if(*info == property::VIRTUALSERVER_UNDEFINED) { + logError(server->getServerId(), "Tried to change unknown server property " + key); + continue; + } + if(!info->validate_input(cmd[key].as())) { + logError(server->getServerId(), "Tried to change " + key + " to an invalid value: " + cmd[key].as()); + continue; + } + server->properties()[info] = cmd[key].string(); + } - if(!cmd.hasParm("offline")) { - auto _start = system_clock::now(); - if(!server->start(startError)); - auto _end = system_clock::now(); - time_start = duration_cast(_end - _start); - } - auto end = system_clock::now(); + if(!cmd.hasParm("offline")) { + auto _start = system_clock::now(); + if(!server->start(startError)); + auto _end = system_clock::now(); + time_start = duration_cast(_end - _start); + } + auto end = system_clock::now(); - time_global = duration_cast(end - start); - } + time_global = duration_cast(end - start); + } Command res(""); res["sid"] = server->getServerId(); res["error"] = startError; res["virtualserver_port"] = server->properties()[property::VIRTUALSERVER_PORT].as(); res["token"] = server->tokenManager->avariableTokes().empty() ? "unknown" : server->tokenManager->avariableTokes()[0]->token; - res["time_create"] = time_create.count(); - res["time_start"] = time_start.count(); - res["time_global"] = time_global.count(); - res["time_wait"] = time_wait.count(); + res["time_create"] = time_create.count(); + res["time_start"] = time_start.count(); + res["time_global"] = time_global.count(); + res["time_wait"] = time_wait.count(); this->sendCommand(res); return CommandResult::Success; } CommandResult QueryClient::handleCommandServerDelete(Command& cmd) { CMD_RESET_IDLE; - PERM_CHECKR(permission::b_virtualserver_delete, 1, true); + PERM_CHECKR(permission::b_virtualserver_delete, 1, true); if(serverInstance->getVoiceServerManager()->getState() != ServerManager::STARTED) return {findError("vs_critical"), "Server manager isn't started yet or not finished starting"}; @@ -659,7 +659,7 @@ CommandResult QueryClient::handleCommandServerDelete(Command& cmd) { CommandResult QueryClient::handleCommandServerStart(Command& cmd) { CMD_RESET_IDLE; - PERM_CHECKR(permission::b_virtualserver_start_any, 1, true); + PERM_CHECKR(permission::b_virtualserver_start_any, 1, true); if(serverInstance->getVoiceServerManager()->getState() != ServerManager::STARTED) return {findError("vs_critical"), "Server manager isn't started yet or not finished starting"}; @@ -667,10 +667,10 @@ CommandResult QueryClient::handleCommandServerStart(Command& cmd) { if(!server) return {findError("server_invalid_id"), "invalid bounded server"}; if(server->running()) return {findError("server_running"), "server already running"}; - if(server->calculatePermission(permission::PERMTEST_ORDERED, this->getClientDatabaseId(), permission::b_virtualserver_start, ClientType::CLIENT_QUERY, nullptr) != 1) { - if(!this->permissionGranted(permission::PERMTEST_HIGHEST, permission::b_virtualserver_start_any, 1)) - return CommandResultPermissionError{permission::b_virtualserver_start}; - } + if(server->calculatePermission(permission::PERMTEST_ORDERED, this->getClientDatabaseId(), permission::b_virtualserver_start, ClientType::CLIENT_QUERY, nullptr) != 1) { + if(!this->permissionGranted(permission::PERMTEST_HIGHEST, permission::b_virtualserver_start_any, 1)) + return CommandResultPermissionError{permission::b_virtualserver_start}; + } string err; if(!server->start(err)) return {findError("vs_critical"), err}; @@ -679,7 +679,7 @@ CommandResult QueryClient::handleCommandServerStart(Command& cmd) { CommandResult QueryClient::handleCommandServerStop(Command& cmd) { CMD_RESET_IDLE; - PERM_CHECKR(permission::b_virtualserver_stop, 1, true); + PERM_CHECKR(permission::b_virtualserver_stop, 1, true); if(serverInstance->getVoiceServerManager()->getState() != ServerManager::STARTED) return {findError("vs_critical"), "Server manager isn't started yet or not finished starting"}; @@ -688,8 +688,8 @@ CommandResult QueryClient::handleCommandServerStop(Command& cmd) { if(!server->running()) return {findError("server_is_not_running"), "server is not running"}; if(server->calculatePermission(permission::PERMTEST_ORDERED, this->getClientDatabaseId(), permission::b_virtualserver_stop, ClientType::CLIENT_QUERY, nullptr) != 1) { - if(!this->permissionGranted(permission::PERMTEST_HIGHEST, permission::b_virtualserver_stop_any, 1)) - return CommandResultPermissionError{permission::b_virtualserver_stop}; + if(!this->permissionGranted(permission::PERMTEST_HIGHEST, permission::b_virtualserver_stop_any, 1)) + return CommandResultPermissionError{permission::b_virtualserver_stop}; } server->stop("server stopped"); @@ -698,7 +698,7 @@ CommandResult QueryClient::handleCommandServerStop(Command& cmd) { CommandResult QueryClient::handleCommandInstanceInfo(Command& cmd) { - PERM_CHECKR(permission::b_serverinstance_info_view, 1, true); + PERM_CHECKR(permission::b_serverinstance_info_view, 1, true); Command res(""); for(const auto& e : serverInstance->properties().list_properties(property::FLAG_INSTANCE_VARIABLE, this->getType() == CLIENT_TEAMSPEAK ? property::FLAG_NEW : (uint16_t) 0)) @@ -711,30 +711,30 @@ CommandResult QueryClient::handleCommandInstanceInfo(Command& cmd) { } CommandResult QueryClient::handleCommandInstanceEdit(Command& cmd) { - PERM_CHECKR(permission::b_serverinstance_modify_settings, 1, true); + PERM_CHECKR(permission::b_serverinstance_modify_settings, 1, true); for(const auto &key : cmd[0].keys()){ - auto info = property::impl::info(key); - if(*info == property::SERVERINSTANCE_UNDEFINED) { - logError(LOG_QUERY, "Query {} tried to change a non existing instance property: {}", this->getLoggingPeerIp(), key); - continue; - } - if(!info->validate_input(cmd[key])) { - logError(LOG_QUERY, "Query {} tried to change {} to an invalid value {}", this->getLoggingPeerIp(), key, cmd[key].as()); - continue; - } - if(*info == property::SERVERINSTANCE_VIRTUAL_SERVER_ID_INDEX) { - /* ensure we've a valid id */ - serverInstance->properties()[info] = cmd[key].as(); - continue; - } + auto info = property::impl::info(key); + if(*info == property::SERVERINSTANCE_UNDEFINED) { + logError(LOG_QUERY, "Query {} tried to change a non existing instance property: {}", this->getLoggingPeerIp(), key); + continue; + } + if(!info->validate_input(cmd[key])) { + logError(LOG_QUERY, "Query {} tried to change {} to an invalid value {}", this->getLoggingPeerIp(), key, cmd[key].as()); + continue; + } + if(*info == property::SERVERINSTANCE_VIRTUAL_SERVER_ID_INDEX) { + /* ensure we've a valid id */ + serverInstance->properties()[info] = cmd[key].as(); + continue; + } serverInstance->properties()[info] = cmd[key].string(); } return CommandResult::Success; } CommandResult QueryClient::handleCommandHostInfo(Command &) { - PERM_CHECKR(permission::b_serverinstance_info_view, 1, true); + PERM_CHECKR(permission::b_serverinstance_info_view, 1, true); Command res(""); res["instance_uptime"] = duration_cast(system_clock::now() - serverInstance->getStartTimestamp()).count(); @@ -769,7 +769,7 @@ CommandResult QueryClient::handleCommandHostInfo(Command &) { } CommandResult QueryClient::handleCommandGlobalMessage(Command& cmd) { - PERM_CHECKR(permission::b_serverinstance_textmessage_send, 1, true); + PERM_CHECKR(permission::b_serverinstance_textmessage_send, 1, true); for(const auto &server : serverInstance->getVoiceServerManager()->serverInstances()) if(server->running()) server->broadcastMessage(server->getServerRoot(), cmd["msg"]); @@ -796,7 +796,7 @@ CommandResult QueryClient::handleCommandBindingList(Command& cmd) { //TODO with mapping! CommandResult QueryClient::handleCommandServerSnapshotDeploy(Command& cmd) { - PERM_CHECKR(permission::b_virtualserver_snapshot_deploy, 1, true); + PERM_CHECKR(permission::b_virtualserver_snapshot_deploy, 1, true); CMD_RESET_IDLE; auto start = system_clock::now(); @@ -811,24 +811,24 @@ CommandResult QueryClient::handleCommandServerSnapshotDeploy(Command& cmd) { auto hash = cmd["hash"].string(); if(hash.empty()) return {findError("parameter_invalid"), "Invalid hash (not present)"}; debugMessage(this->getServerId(), "Serversnapshot calculated hash: {}", hash); - bool mapping = cmd.hasParm("mapping"); - cmd.disableParm("mapping"); + bool mapping = cmd.hasParm("mapping"); + cmd.disableParm("mapping"); - bool ignore_hash = cmd.hasParm("ignorehash"); - cmd.disableParm("ignorehash"); + bool ignore_hash = cmd.hasParm("ignorehash"); + cmd.disableParm("ignorehash"); cmd.pop_bulk(); auto str = cmd.build().substr(strlen("serversnapshotdeploy ")); if(!ignore_hash) { - auto buildHash = base64::encode(digest::sha1(str)); - if(buildHash != hash) return {findError("parameter_invalid"), "Invalid hash (Expected: \"" + hash + "\", Got: \"" + buildHash + "\")"}; + auto buildHash = base64::encode(digest::sha1(str)); + if(buildHash != hash) return {findError("parameter_invalid"), "Invalid hash (Expected: \"" + hash + "\", Got: \"" + buildHash + "\")"}; } - unique_lock server_create_lock(serverInstance->getVoiceServerManager()->server_create_lock); + unique_lock server_create_lock(serverInstance->getVoiceServerManager()->server_create_lock); if(port == 0) - port = serverInstance->getVoiceServerManager()->next_available_port(); + port = serverInstance->getVoiceServerManager()->next_available_port(); auto result = serverInstance->getVoiceServerManager()->createServerFromSnapshot(this->server, host, port, cmd, error); - server_create_lock.unlock(); + server_create_lock.unlock(); auto end = system_clock::now(); Command res(""); @@ -857,54 +857,54 @@ CommandResult QueryClient::handleCommandServerSnapshotDeploy(Command& cmd) { } CommandResult QueryClient::handleCommandServerSnapshotCreate(Command& cmd) { - PERM_CHECKR(permission::b_virtualserver_snapshot_create, 1, true); - CMD_RESET_IDLE; - CMD_REQ_SERVER; + PERM_CHECKR(permission::b_virtualserver_snapshot_create, 1, true); + CMD_RESET_IDLE; + CMD_REQ_SERVER; - Command result(""); - string error; + Command result(""); + string error; - int version = cmd[0].has("version") ? cmd[0]["version"] : -1; - if(version == -1 && (cmd.hasParm("lagacy") || cmd.hasParm("legacy"))) - version = 0; - if(!serverInstance->getVoiceServerManager()->createServerSnapshot(result, this->server, version, error)) - return {ErrorType::VSError, error}; + int version = cmd[0].has("version") ? cmd[0]["version"] : -1; + if(version == -1 && (cmd.hasParm("lagacy") || cmd.hasParm("legacy"))) + version = 0; + if(!serverInstance->getVoiceServerManager()->createServerSnapshot(result, this->server, version, error)) + return {ErrorType::VSError, error}; - string data = result.build(); - auto buildHash = base64::encode(digest::sha1(data)); + string data = result.build(); + auto buildHash = base64::encode(digest::sha1(data)); - result.push_bulk_front(); - result[0]["hash"] = buildHash; + result.push_bulk_front(); + result[0]["hash"] = buildHash; - this->sendCommand(result); - return CommandResult::Success; + this->sendCommand(result); + return CommandResult::Success; } extern bool mainThreadActive; CommandResult QueryClient::handleCommandServerProcessStop(Command& cmd) { - PERM_CHECKR(permission::b_serverinstance_stop, 1, true); + PERM_CHECKR(permission::b_serverinstance_stop, 1, true); - if(cmd[0].has("type")) { - if(cmd["type"] == "cancel") { - auto task = ts::server::scheduledShutdown(); - if(!task) return {findError("server_is_not_shutting_down"), "There isn't a shutdown scheduled"}; - ts::server::cancelShutdown(true); - return CommandResult::Success; - } else if(cmd["type"] == "schedule") { - if(!cmd[0].has("time")) return {findError("parameter_missing"), "Missing time"}; - ts::server::scheduleShutdown(system_clock::now() + seconds(cmd["time"].as()), cmd[0].has("msg") ? cmd["msg"].string() : ts::config::messages::applicationStopped); - return CommandResult::Success; - } - } + if(cmd[0].has("type")) { + if(cmd["type"] == "cancel") { + auto task = ts::server::scheduledShutdown(); + if(!task) return {findError("server_is_not_shutting_down"), "There isn't a shutdown scheduled"}; + ts::server::cancelShutdown(true); + return CommandResult::Success; + } else if(cmd["type"] == "schedule") { + if(!cmd[0].has("time")) return {findError("parameter_missing"), "Missing time"}; + ts::server::scheduleShutdown(system_clock::now() + seconds(cmd["time"].as()), cmd[0].has("msg") ? cmd["msg"].string() : ts::config::messages::applicationStopped); + return CommandResult::Success; + } + } - string reason = ts::config::messages::applicationStopped; - if(cmd[0].has("msg")) - reason = cmd["msg"].string(); - if(cmd[0].has("reasonmsg")) - reason = cmd["reasonmsg"].string(); - serverInstance->getVoiceServerManager()->shutdownAll(reason); - mainThreadActive = false; - return CommandResult::Success; + string reason = ts::config::messages::applicationStopped; + if(cmd[0].has("msg")) + reason = cmd["msg"].string(); + if(cmd[0].has("reasonmsg")) + reason = cmd["reasonmsg"].string(); + serverInstance->getVoiceServerManager()->shutdownAll(reason); + mainThreadActive = false; + return CommandResult::Success; } #define XMACRO_EV(evName0, evSpec0, evName1, evSpec1) \ @@ -984,7 +984,7 @@ CommandResult QueryClient::handleCommandServerNotifyUnregister(Command &cmd) { CMD_REQ_PARM("event"); CMD_RESET_IDLE; if(!cmd[0].has("specifier")){ - logMessage(this->getServerId(), "{} Client {}:{} uses the lagacy notify system, which is deprecated!", CLIENT_STR_LOG_PREFIX, this->getLoggingPeerIp(), this->getPeerPort()); + logMessage(this->getServerId(), "{} Client {}:{} uses the lagacy notify system, which is deprecated!", CLIENT_STR_LOG_PREFIX, this->getLoggingPeerIp(), this->getPeerPort()); string event = cmd["event"]; std::transform(event.begin(), event.end(), event.begin(), ::tolower); #include "XMacroEventTypes.h" diff --git a/server/src/client/query/QueryClientNotify.cpp b/server/src/client/query/QueryClientNotify.cpp index 161622d..f01fcdc 100644 --- a/server/src/client/query/QueryClientNotify.cpp +++ b/server/src/client/query/QueryClientNotify.cpp @@ -32,9 +32,9 @@ bool QueryClient::notifyClientNeededPermissions() { return true; //Query gets no needed permissions } -#define CHK_EVENT(group, specifier) \ -do { \ - if(!this->eventActive(QueryEventGroup::group, QueryEventSpecifier::specifier)) return false; \ +#define CHK_EVENT(group, specifier) \ +do { \ + if(!this->eventActive(QueryEventGroup::group, QueryEventSpecifier::specifier)) return false; \ } while(0) bool QueryClient::notifyServerUpdated(shared_ptr ptr) { @@ -43,8 +43,8 @@ bool QueryClient::notifyServerUpdated(shared_ptr ptr) { } bool QueryClient::notifyClientUpdated(const std::shared_ptr &ptr, const std::deque> &deque, bool lock_channel_tree) { - CHK_EVENT(QEVENTGROUP_CLIENT_MISC, QEVENTSPECIFIER_CLIENT_MISC_UPDATE); - return ConnectedClient::notifyClientUpdated(ptr, deque, lock_channel_tree); + CHK_EVENT(QEVENTGROUP_CLIENT_MISC, QEVENTSPECIFIER_CLIENT_MISC_UPDATE); + return ConnectedClient::notifyClientUpdated(ptr, deque, lock_channel_tree); } bool QueryClient::notifyClientPoke(std::shared_ptr invoker, std::string msg) { @@ -86,8 +86,8 @@ bool QueryClient::notifyServerGroupClientRemove(std::shared_ptr bool QueryClient::notifyClientChannelGroupChanged(const std::shared_ptr &invoker, const std::shared_ptr &client, const std::shared_ptr &ptr, const std::shared_ptr &shared_ptr, const std::shared_ptr &group, bool lock_channel_tree) { - CHK_EVENT(QEVENTGROUP_CLIENT_GROUPS, QEVENTSPECIFIER_CLIENT_GROUPS_CHANNEL_CHANGED); - return ConnectedClient::notifyClientChannelGroupChanged(invoker, client, ptr, shared_ptr, group, lock_channel_tree); + CHK_EVENT(QEVENTGROUP_CLIENT_GROUPS, QEVENTSPECIFIER_CLIENT_GROUPS_CHANNEL_CHANGED); + return ConnectedClient::notifyClientChannelGroupChanged(invoker, client, ptr, shared_ptr, group, lock_channel_tree); } bool QueryClient::notifyChannelMoved(const std::shared_ptr &channel, ChannelId order, const std::shared_ptr &invoker) { @@ -112,116 +112,116 @@ bool QueryClient::notifyChannelPasswordChanged(std::shared_ptr cha } bool QueryClient::notifyChannelEdited(const std::shared_ptr &ptr, const std::vector &vector, const std::shared_ptr &sharedPtr, bool b) { - CHK_EVENT(QEVENTGROUP_CHANNEL, QEVENTSPECIFIER_CHANNEL_EDIT); - return ConnectedClient::notifyChannelEdited(ptr, vector, sharedPtr, b); + CHK_EVENT(QEVENTGROUP_CHANNEL, QEVENTSPECIFIER_CHANNEL_EDIT); + return ConnectedClient::notifyChannelEdited(ptr, vector, sharedPtr, b); } bool QueryClient::notifyChannelDeleted(const std::deque &deque, const std::shared_ptr &ptr) { - CHK_EVENT(QEVENTGROUP_CHANNEL, QEVENTSPECIFIER_CHANNEL_DELETED); - return ConnectedClient::notifyChannelDeleted(deque, ptr); + CHK_EVENT(QEVENTGROUP_CHANNEL, QEVENTSPECIFIER_CHANNEL_DELETED); + return ConnectedClient::notifyChannelDeleted(deque, ptr); } bool QueryClient::notifyClientEnterView(const std::deque> &deque, const ViewReasonSystemT &t) { - if(!this->eventActive(QueryEventGroup::QEVENTGROUP_CLIENT_VIEW, QueryEventSpecifier::QEVENTSPECIFIER_CLIENT_VIEW_JOIN)) { - assert(mutex_locked(this->channel_lock)); - this->visibleClients.insert(this->visibleClients.end(), deque.begin(), deque.end()); - return true; - } else - return ConnectedClient::notifyClientEnterView(deque, t); + if(!this->eventActive(QueryEventGroup::QEVENTGROUP_CLIENT_VIEW, QueryEventSpecifier::QEVENTSPECIFIER_CLIENT_VIEW_JOIN)) { + assert(mutex_locked(this->channel_lock)); + this->visibleClients.insert(this->visibleClients.end(), deque.begin(), deque.end()); + return true; + } else + return ConnectedClient::notifyClientEnterView(deque, t); } bool QueryClient::notifyClientEnterView(const std::shared_ptr &client, const std::shared_ptr &invoker, const std::string &string, const std::shared_ptr &to, ts::ViewReasonId reasonId, const std::shared_ptr &from, bool lock) { - if(!this->eventActive(QueryEventGroup::QEVENTGROUP_CLIENT_VIEW, QueryEventSpecifier::QEVENTSPECIFIER_CLIENT_VIEW_JOIN)) { - assert(!lock); - this->visibleClients.push_back(client); - return true; - } - return ConnectedClient::notifyClientEnterView(client, invoker, string, to, reasonId, from, lock); + if(!this->eventActive(QueryEventGroup::QEVENTGROUP_CLIENT_VIEW, QueryEventSpecifier::QEVENTSPECIFIER_CLIENT_VIEW_JOIN)) { + assert(!lock); + this->visibleClients.push_back(client); + return true; + } + return ConnectedClient::notifyClientEnterView(client, invoker, string, to, reasonId, from, lock); } bool QueryClient::notifyClientMoved(const std::shared_ptr &client, const std::shared_ptr &target_channel, ViewReasonId reason, std::string msg, std::shared_ptr invoker, bool lock_channel_tree) { - return ConnectedClient::notifyClientMoved(client, target_channel, reason, msg, invoker, lock_channel_tree); + return ConnectedClient::notifyClientMoved(client, target_channel, reason, msg, invoker, lock_channel_tree); } bool QueryClient::notifyClientLeftView(const std::shared_ptr &client, const std::shared_ptr &target_channel, ViewReasonId reasonId, const std::string &reasonMessage, std::shared_ptr invoker, bool lock_channel_tree) { - if(!this->eventActive(QueryEventGroup::QEVENTGROUP_CLIENT_VIEW, QueryEventSpecifier::QEVENTSPECIFIER_CLIENT_VIEW_LEAVE)) { - this->visibleClients.erase(std::remove_if(this->visibleClients.begin(), this->visibleClients.end(), [&, client](const weak_ptr& weak) { - auto c = weak.lock(); - if(!c) { - logError(this->getServerId(), "{} Got \"dead\" client in visible client list! This can cause a remote client disconnect within the future!", CLIENT_STR_LOG_PREFIX); - return true; - } - return c == client; - }), this->visibleClients.end()); - return true; - } - return ConnectedClient::notifyClientLeftView(client, target_channel, reasonId, reasonMessage, invoker, lock_channel_tree); + if(!this->eventActive(QueryEventGroup::QEVENTGROUP_CLIENT_VIEW, QueryEventSpecifier::QEVENTSPECIFIER_CLIENT_VIEW_LEAVE)) { + this->visibleClients.erase(std::remove_if(this->visibleClients.begin(), this->visibleClients.end(), [&, client](const weak_ptr& weak) { + auto c = weak.lock(); + if(!c) { + logError(this->getServerId(), "{} Got \"dead\" client in visible client list! This can cause a remote client disconnect within the future!", CLIENT_STR_LOG_PREFIX); + return true; + } + return c == client; + }), this->visibleClients.end()); + return true; + } + return ConnectedClient::notifyClientLeftView(client, target_channel, reasonId, reasonMessage, invoker, lock_channel_tree); } bool QueryClient::notifyClientLeftView(const std::deque> &clients, const std::string &string, bool b, const ViewReasonServerLeftT &t) { - if(!this->eventActive(QueryEventGroup::QEVENTGROUP_CLIENT_VIEW, QueryEventSpecifier::QEVENTSPECIFIER_CLIENT_VIEW_LEAVE)) { - this->visibleClients.erase(std::remove_if(this->visibleClients.begin(), this->visibleClients.end(), [&](const weak_ptr& weak) { - auto c = weak.lock(); - if(!c) { - logError(this->getServerId(), "{} Got \"dead\" client in visible client list! This can cause a remote client disconnect within the future!", CLIENT_STR_LOG_PREFIX); - return true; - } - return std::find(clients.begin(), clients.end(), c) != clients.end(); - }), this->visibleClients.end()); - return true; - } - return ConnectedClient::notifyClientLeftView(clients, string, b, t); + if(!this->eventActive(QueryEventGroup::QEVENTGROUP_CLIENT_VIEW, QueryEventSpecifier::QEVENTSPECIFIER_CLIENT_VIEW_LEAVE)) { + this->visibleClients.erase(std::remove_if(this->visibleClients.begin(), this->visibleClients.end(), [&](const weak_ptr& weak) { + auto c = weak.lock(); + if(!c) { + logError(this->getServerId(), "{} Got \"dead\" client in visible client list! This can cause a remote client disconnect within the future!", CLIENT_STR_LOG_PREFIX); + return true; + } + return std::find(clients.begin(), clients.end(), c) != clients.end(); + }), this->visibleClients.end()); + return true; + } + return ConnectedClient::notifyClientLeftView(clients, string, b, t); } bool QueryClient::notifyClientLeftViewKicked(const std::shared_ptr &client, const std::shared_ptr &target_channel, const std::string &message, std::shared_ptr invoker, bool lock_channel_tree) { - if(!this->eventActive(QueryEventGroup::QEVENTGROUP_CLIENT_VIEW, QueryEventSpecifier::QEVENTSPECIFIER_CLIENT_VIEW_LEAVE)) { - this->visibleClients.erase(std::remove_if(this->visibleClients.begin(), this->visibleClients.end(), [&, client](const weak_ptr& weak) { - auto c = weak.lock(); - if(!c) { - logError(this->getServerId(), "{} Got \"dead\" client in visible client list! This can cause a remote client disconnect within the future!", CLIENT_STR_LOG_PREFIX); - return true; - } - return c == client; - }), this->visibleClients.end()); - return true; - } - return ConnectedClient::notifyClientLeftViewKicked(client, target_channel, message, invoker, lock_channel_tree); + if(!this->eventActive(QueryEventGroup::QEVENTGROUP_CLIENT_VIEW, QueryEventSpecifier::QEVENTSPECIFIER_CLIENT_VIEW_LEAVE)) { + this->visibleClients.erase(std::remove_if(this->visibleClients.begin(), this->visibleClients.end(), [&, client](const weak_ptr& weak) { + auto c = weak.lock(); + if(!c) { + logError(this->getServerId(), "{} Got \"dead\" client in visible client list! This can cause a remote client disconnect within the future!", CLIENT_STR_LOG_PREFIX); + return true; + } + return c == client; + }), this->visibleClients.end()); + return true; + } + return ConnectedClient::notifyClientLeftViewKicked(client, target_channel, message, invoker, lock_channel_tree); } bool QueryClient::notifyClientLeftViewBanned(const std::shared_ptr &client, const std::string &message, std::shared_ptr invoker, size_t length, bool lock_channel_tree) { - if(!this->eventActive(QueryEventGroup::QEVENTGROUP_CLIENT_VIEW, QueryEventSpecifier::QEVENTSPECIFIER_CLIENT_VIEW_LEAVE)) { - this->visibleClients.erase(std::remove_if(this->visibleClients.begin(), this->visibleClients.end(), [&, client](const weak_ptr& weak) { - auto c = weak.lock(); - if(!c) { - logError(this->getServerId(), "{} Got \"dead\" client in visible client list! This can cause a remote client disconnect within the future!", CLIENT_STR_LOG_PREFIX); - return true; - } - return c == client; - }), this->visibleClients.end()); - return true; - } - return ConnectedClient::notifyClientLeftViewBanned(client, message, invoker, length, lock_channel_tree); + if(!this->eventActive(QueryEventGroup::QEVENTGROUP_CLIENT_VIEW, QueryEventSpecifier::QEVENTSPECIFIER_CLIENT_VIEW_LEAVE)) { + this->visibleClients.erase(std::remove_if(this->visibleClients.begin(), this->visibleClients.end(), [&, client](const weak_ptr& weak) { + auto c = weak.lock(); + if(!c) { + logError(this->getServerId(), "{} Got \"dead\" client in visible client list! This can cause a remote client disconnect within the future!", CLIENT_STR_LOG_PREFIX); + return true; + } + return c == client; + }), this->visibleClients.end()); + return true; + } + return ConnectedClient::notifyClientLeftViewBanned(client, message, invoker, length, lock_channel_tree); } bool QueryClient::notifyMusicQueueAdd(const std::shared_ptr &bot, const std::shared_ptr &entry, int index, const std::shared_ptr &invoker) { - CHK_EVENT(QEVENTGROUP_MUSIC, QEVENTSPECIFIER_MUSIC_QUEUE); - return ConnectedClient::notifyMusicQueueAdd(bot, entry, index, invoker); + CHK_EVENT(QEVENTGROUP_MUSIC, QEVENTSPECIFIER_MUSIC_QUEUE); + return ConnectedClient::notifyMusicQueueAdd(bot, entry, index, invoker); } bool QueryClient::notifyMusicQueueRemove(const std::shared_ptr &bot, const std::deque> &entry, const std::shared_ptr &invoker) { - CHK_EVENT(QEVENTGROUP_MUSIC, QEVENTSPECIFIER_MUSIC_QUEUE); - return ConnectedClient::notifyMusicQueueRemove(bot, entry, invoker); + CHK_EVENT(QEVENTGROUP_MUSIC, QEVENTSPECIFIER_MUSIC_QUEUE); + return ConnectedClient::notifyMusicQueueRemove(bot, entry, invoker); } bool QueryClient::notifyMusicQueueOrderChange(const std::shared_ptr &bot, const std::shared_ptr &entry, int order, const std::shared_ptr &invoker) { - CHK_EVENT(QEVENTGROUP_MUSIC, QEVENTSPECIFIER_MUSIC_QUEUE); - return ConnectedClient::notifyMusicQueueOrderChange(bot, entry, order, invoker); + CHK_EVENT(QEVENTGROUP_MUSIC, QEVENTSPECIFIER_MUSIC_QUEUE); + return ConnectedClient::notifyMusicQueueOrderChange(bot, entry, order, invoker); } bool QueryClient::notifyMusicPlayerSongChange(const std::shared_ptr &bot, const shared_ptr &newEntry) { - CHK_EVENT(QEVENTGROUP_MUSIC, QEVENTSPECIFIER_MUSIC_PLAYER); - return ConnectedClient::notifyMusicPlayerSongChange(bot, newEntry); + CHK_EVENT(QEVENTGROUP_MUSIC, QEVENTSPECIFIER_MUSIC_PLAYER); + return ConnectedClient::notifyMusicPlayerSongChange(bot, newEntry); } diff --git a/server/src/client/voice/VoiceClient.cpp b/server/src/client/voice/VoiceClient.cpp index 698008c..945170c 100644 --- a/server/src/client/voice/VoiceClient.cpp +++ b/server/src/client/voice/VoiceClient.cpp @@ -17,283 +17,283 @@ using namespace ts::server; using namespace ts::protocol; VoiceClient::VoiceClient(const std::shared_ptr& server, const sockaddr_storage* address) : SpeakingClient(server->server->sql, server->server), voice_server(server) { - assert(address); - memtrack::allocated(this); - memcpy(&this->remote_address, address, sizeof(sockaddr_storage)); + assert(address); + memtrack::allocated(this); + memcpy(&this->remote_address, address, sizeof(sockaddr_storage)); - debugMessage(this->server->getServerId(), " Creating VoiceClient instance at {}", (void*) this); + debugMessage(this->server->getServerId(), " Creating VoiceClient instance at {}", (void*) this); } void VoiceClient::initialize() { - this->event_handle_packet = make_shared>(dynamic_pointer_cast(this->ref()), &VoiceClient::execute_handle_packet); + this->event_handle_packet = make_shared>(dynamic_pointer_cast(this->ref()), &VoiceClient::execute_handle_packet); - this->properties()[property::CLIENT_TYPE] = ClientType::CLIENT_TEAMSPEAK; - this->properties()[property::CLIENT_TYPE_EXACT] = ClientType::CLIENT_TEAMSPEAK; + this->properties()[property::CLIENT_TYPE] = ClientType::CLIENT_TEAMSPEAK; + this->properties()[property::CLIENT_TYPE_EXACT] = ClientType::CLIENT_TEAMSPEAK; - this->state = ConnectionState::INIT_HIGH; - this->connection = new connection::VoiceClientConnection(this); + this->state = ConnectionState::INIT_HIGH; + this->connection = new connection::VoiceClientConnection(this); } VoiceClient::~VoiceClient() { - debugMessage(this->getServerId(), " Deleting VoiceClient instance at {}", (void*) this); + debugMessage(this->getServerId(), " Deleting VoiceClient instance at {}", (void*) this); - this->state = ConnectionState::DISCONNECTED; - delete this->connection; - this->connection = nullptr; + this->state = ConnectionState::DISCONNECTED; + delete this->connection; + this->connection = nullptr; - if(this->flushing_thread) - logCritical(this->getServerId(), "Deleting a VoiceClient which should still be hold within the flush thread!"); + if(this->flushing_thread) + logCritical(this->getServerId(), "Deleting a VoiceClient which should still be hold within the flush thread!"); - memtrack::freed(this); + memtrack::freed(this); } void VoiceClient::sendCommand0(const ts::Command &command, bool low, bool direct, std::unique_ptr> listener) { - auto cmd = command.build(); - if(cmd.empty()) { - logCritical(this->getServerId(), "{} Attempted to send an empty command!", CLIENT_STR_LOG_PREFIX); - return; - } + auto cmd = command.build(); + if(cmd.empty()) { + logCritical(this->getServerId(), "{} Attempted to send an empty command!", CLIENT_STR_LOG_PREFIX); + return; + } - auto packet = make_shared( - low ? protocol::PacketTypeInfo::CommandLow : protocol::PacketTypeInfo::Command, - pipes::buffer_view{(void*) cmd.data(), cmd.length()} - ); - if(low) { - packet->enable_flag(protocol::PacketFlag::NewProtocol); - } - packet->setListener(std::move(listener)); - this->connection->sendPacket(packet, false, direct); + auto packet = make_shared( + low ? protocol::PacketTypeInfo::CommandLow : protocol::PacketTypeInfo::Command, + pipes::buffer_view{(void*) cmd.data(), cmd.length()} + ); + if(low) { + packet->enable_flag(protocol::PacketFlag::NewProtocol); + } + packet->setListener(std::move(listener)); + this->connection->sendPacket(packet, false, direct); #ifdef PKT_LOG_CMD - logTrace(this->getServerId(), "{}[Command][Server -> Client] Sending command {}. Command low: {}. Full command: {}", CLIENT_STR_LOG_PREFIX, command.command(), low, cmd); + logTrace(this->getServerId(), "{}[Command][Server -> Client] Sending command {}. Command low: {}. Full command: {}", CLIENT_STR_LOG_PREFIX, command.command(), low, cmd); #endif } void VoiceClient::sendAcknowledge(uint16_t packetId, bool low) { - char buffer[2]; - le2be16(packetId, buffer); + char buffer[2]; + le2be16(packetId, buffer); - auto packet = make_shared(low ? protocol::PacketTypeInfo::AckLow : protocol::PacketTypeInfo::Ack, pipes::buffer_view{buffer, 2}); - packet->enable_flag(PacketFlag::Unencrypted); - if(!low) packet->enable_flag(protocol::PacketFlag::NewProtocol); - this->connection->sendPacket(packet); + auto packet = make_shared(low ? protocol::PacketTypeInfo::AckLow : protocol::PacketTypeInfo::Ack, pipes::buffer_view{buffer, 2}); + packet->enable_flag(PacketFlag::Unencrypted); + if(!low) packet->enable_flag(protocol::PacketFlag::NewProtocol); + this->connection->sendPacket(packet); #ifdef PKT_LOG_ACK - logTrace(this->getServerId(), "{}[Acknowledge][Server -> Client] Sending acknowledge for {}", CLIENT_STR_LOG_PREFIX, packetId); + logTrace(this->getServerId(), "{}[Acknowledge][Server -> Client] Sending acknowledge for {}", CLIENT_STR_LOG_PREFIX, packetId); #endif } void VoiceClient::tick(const std::chrono::system_clock::time_point &time) { SpeakingClient::tick(time); - { - ALARM_TIMER(A1, "VoiceClient::tick", milliseconds(3)); - if(this->state == ConnectionState::CONNECTED) { - if(this->lastPingRequest > this->lastPingResponse) { //Client is behind :) - if(this->lastPingRequest - this->lastPingResponse > chrono::seconds(20)) { - debugMessage(this->getServerId(), "{} Got a ping timeout. (Last successful ping: {}ms ago. Last request {}ms. Last response {}ms). Trying to recover via command acknowledge.", - CLIENT_STR_LOG_PREFIX, - duration_cast(this->lastPingRequest - this->lastPingResponse).count(), - duration_cast(time - this->lastPingRequest).count(), - duration_cast(time - this->lastPingResponse).count()); + { + ALARM_TIMER(A1, "VoiceClient::tick", milliseconds(3)); + if(this->state == ConnectionState::CONNECTED) { + if(this->lastPingRequest > this->lastPingResponse) { //Client is behind :) + if(this->lastPingRequest - this->lastPingResponse > chrono::seconds(20)) { + debugMessage(this->getServerId(), "{} Got a ping timeout. (Last successful ping: {}ms ago. Last request {}ms. Last response {}ms). Trying to recover via command acknowledge.", + CLIENT_STR_LOG_PREFIX, + duration_cast(this->lastPingRequest - this->lastPingResponse).count(), + duration_cast(time - this->lastPingRequest).count(), + duration_cast(time - this->lastPingResponse).count()); - bool force; - this->request_connection_info(nullptr, force); - this->lastPingResponse = system_clock::now(); - return; - } - } - if(time - this->lastPingRequest >= chrono::milliseconds(1000)) { - //TODO calculate the ping smooth - if(this->lastPingResponse < this->lastPingRequest){ - if(time - this->lastPingRequest >= chrono::milliseconds(1500)) { //Max - this->sendPingRequest(); - } - } else - this->sendPingRequest(); - } - } else if(this->state == ConnectionState::INIT_LOW || this->state == ConnectionState::INIT_HIGH) { - if(this->last_packet_handshake.time_since_epoch().count() != 0) { - if(time - this->last_packet_handshake > seconds(5)) { - debugMessage(this->getServerId(), "{} Got handshake timeout. {}. State: {} Time: {}", CLIENT_STR_LOG_PREFIX, - this->getLoggingPeerIp() + ":" + to_string(this->getPeerPort()), - this->state == ConnectionState::INIT_HIGH ? "INIT_HIGH" : "INIT_LOW", - duration_cast(time - this->last_packet_handshake).count() - ); - this->closeConnection(system_clock::now() + seconds(1)); - } - } - } - } + bool force; + this->request_connection_info(nullptr, force); + this->lastPingResponse = system_clock::now(); + return; + } + } + if(time - this->lastPingRequest >= chrono::milliseconds(1000)) { + //TODO calculate the ping smooth + if(this->lastPingResponse < this->lastPingRequest){ + if(time - this->lastPingRequest >= chrono::milliseconds(1500)) { //Max + this->sendPingRequest(); + } + } else + this->sendPingRequest(); + } + } else if(this->state == ConnectionState::INIT_LOW || this->state == ConnectionState::INIT_HIGH) { + if(this->last_packet_handshake.time_since_epoch().count() != 0) { + if(time - this->last_packet_handshake > seconds(5)) { + debugMessage(this->getServerId(), "{} Got handshake timeout. {}. State: {} Time: {}", CLIENT_STR_LOG_PREFIX, + this->getLoggingPeerIp() + ":" + to_string(this->getPeerPort()), + this->state == ConnectionState::INIT_HIGH ? "INIT_HIGH" : "INIT_LOW", + duration_cast(time - this->last_packet_handshake).count() + ); + this->closeConnection(system_clock::now() + seconds(1)); + } + } + } + } } bool VoiceClient::disconnect(const std::string &reason) { - return this->disconnect(VREASON_SERVER_KICK, reason, this->server->serverRoot, true); + return this->disconnect(VREASON_SERVER_KICK, reason, this->server->serverRoot, true); } bool VoiceClient::disconnect(ts::ViewReasonId reason_id, const std::string &reason, const std::shared_ptr& invoker, bool notify_viewer) { - { - threads::MutexLock disconnect_lock(this->disconnectLock); - if(this->state == ConnectionState::DISCONNECTING || this->state == ConnectionState::DISCONNECTED) return false; //Already disconnecting/disconnected - this->state = ConnectionState::DISCONNECTING; - } + { + threads::MutexLock disconnect_lock(this->disconnectLock); + if(this->state == ConnectionState::DISCONNECTING || this->state == ConnectionState::DISCONNECTED) return false; //Already disconnecting/disconnected + this->state = ConnectionState::DISCONNECTING; + } - Command cmd("notifyclientleftview"); - cmd["reasonmsg"] = reason; - cmd["reasonid"] = reason_id; - cmd["clid"] = this->getClientId(); - cmd["cfid"] = this->currentChannel ? this->currentChannel->channelId() : 0; //Failed when cid = 0???? - cmd["ctid"] = 0; + Command cmd("notifyclientleftview"); + cmd["reasonmsg"] = reason; + cmd["reasonid"] = reason_id; + cmd["clid"] = this->getClientId(); + cmd["cfid"] = this->currentChannel ? this->currentChannel->channelId() : 0; //Failed when cid = 0???? + cmd["ctid"] = 0; - if (invoker) { - cmd["invokerid"] = invoker->getClientId(); - cmd["invokername"] = invoker->getDisplayName(); - cmd["invokeruid"] = invoker->getUid(); - } + if (invoker) { + cmd["invokerid"] = invoker->getClientId(); + cmd["invokername"] = invoker->getDisplayName(); + cmd["invokeruid"] = invoker->getUid(); + } - if(notify_viewer && this->server) { - unique_lock channel_lock(this->server->channel_tree_lock); - this->server->client_move(this->ref(), nullptr, invoker, reason, reason_id, false, channel_lock); - } else { - threads::MutexLock lock(this->command_lock); - auto server_channel = dynamic_pointer_cast(this->currentChannel); - if(server_channel) - server_channel->unregister_client(_this.lock()); - this->currentChannel = nullptr; - } + if(notify_viewer && this->server) { + unique_lock channel_lock(this->server->channel_tree_lock); + this->server->client_move(this->ref(), nullptr, invoker, reason, reason_id, false, channel_lock); + } else { + threads::MutexLock lock(this->command_lock); + auto server_channel = dynamic_pointer_cast(this->currentChannel); + if(server_channel) + server_channel->unregister_client(_this.lock()); + this->currentChannel = nullptr; + } - auto listener = make_unique>(); - auto weak_self = this->_this; - listener->waitAndGetLater([weak_self](bool* success) { - if(weak_self.expired()) return; - auto self = weak_self.lock(); - if(!self || self->state != DISCONNECTING) return; + auto listener = make_unique>(); + auto weak_self = this->_this; + listener->waitAndGetLater([weak_self](bool* success) { + if(weak_self.expired()) return; + auto self = weak_self.lock(); + if(!self || self->state != DISCONNECTING) return; - if(!success || !*success) { - debugMessage(self->getServerId(), "{} Failed to receive disconnect acknowledge!", CLIENT_STR_LOG_PREFIX_(self)); - } else - debugMessage(self->getServerId(), "{} Received disconnect acknowledge!", CLIENT_STR_LOG_PREFIX_(self)); + if(!success || !*success) { + debugMessage(self->getServerId(), "{} Failed to receive disconnect acknowledge!", CLIENT_STR_LOG_PREFIX_(self)); + } else + debugMessage(self->getServerId(), "{} Received disconnect acknowledge!", CLIENT_STR_LOG_PREFIX_(self)); - self->closeConnection(); - }, system_clock::now() + seconds(5)); - this->sendCommand0(cmd, false, false, std::move(listener)); - return true; + self->closeConnection(); + }, system_clock::now() + seconds(5)); + this->sendCommand0(cmd, false, false, std::move(listener)); + return true; } bool VoiceClient::closeConnection(const system_clock::time_point &timeout) { - auto self_lock = dynamic_pointer_cast(_this.lock()); - assert(self_lock); //Should never happen! + auto self_lock = dynamic_pointer_cast(_this.lock()); + assert(self_lock); //Should never happen! - threads::MutexLock disconnect_lock(this->disconnectLock); - bool flush = timeout.time_since_epoch().count() > 0; - if((this->state == ConnectionState::DISCONNECTING && flush && this->flushing_thread) || this->state == ConnectionState::DISCONNECTED){ - debugMessage(this->getServerId(), "{} Tried to disconnect, but isn't connected anymore! State: {}", CLIENT_STR_LOG_PREFIX, this->state); - return false; - } - this->state = flush ? ConnectionState::DISCONNECTING : ConnectionState::DISCONNECTED; + threads::MutexLock disconnect_lock(this->disconnectLock); + bool flush = timeout.time_since_epoch().count() > 0; + if((this->state == ConnectionState::DISCONNECTING && flush && this->flushing_thread) || this->state == ConnectionState::DISCONNECTED){ + debugMessage(this->getServerId(), "{} Tried to disconnect, but isn't connected anymore! State: {}", CLIENT_STR_LOG_PREFIX, this->state); + return false; + } + this->state = flush ? ConnectionState::DISCONNECTING : ConnectionState::DISCONNECTED; - debugMessage(this->getServerId(), "{} Closing voice client connection. (Flush: {})", CLIENT_STR_LOG_PREFIX, flush); - if(flush) { - this->flushing_thread = std::make_shared(THREAD_SAVE_OPERATIONS | THREAD_EXECUTE_LATER, [this, self_lock, timeout](){ - //We could use this here cause its locked by self_locked - debugMessage(this->getServerId(), "{} Awaiting write prepare, write and acknowledge queue flushed", CLIENT_STR_LOG_PREFIX); - auto connection = this->getConnection(); + debugMessage(this->getServerId(), "{} Closing voice client connection. (Flush: {})", CLIENT_STR_LOG_PREFIX, flush); + if(flush) { + this->flushing_thread = std::make_shared(THREAD_SAVE_OPERATIONS | THREAD_EXECUTE_LATER, [this, self_lock, timeout](){ + //We could use this here cause its locked by self_locked + debugMessage(this->getServerId(), "{} Awaiting write prepare, write and acknowledge queue flushed", CLIENT_STR_LOG_PREFIX); + auto connection = this->getConnection(); - this->getConnection()->wait_empty_write_and_prepare_queue(timeout); - debugMessage(this->getServerId(), "{} Write prepare queue progressed", CLIENT_STR_LOG_PREFIX); - while(this->state == DISCONNECTING){ - if(system_clock::now() > timeout){ - debugMessage(this->getServerId(), "{} Cant flush io!", CLIENT_STR_LOG_PREFIX); + this->getConnection()->wait_empty_write_and_prepare_queue(timeout); + debugMessage(this->getServerId(), "{} Write prepare queue progressed", CLIENT_STR_LOG_PREFIX); + while(this->state == DISCONNECTING){ + if(system_clock::now() > timeout){ + debugMessage(this->getServerId(), "{} Cant flush io!", CLIENT_STR_LOG_PREFIX); - if(!this->connection->wait_empty_write_and_prepare_queue(timeout)) { - debugMessage(this->getServerId(), "{} Write queue not empty!", CLIENT_STR_LOG_PREFIX); - } - { - auto reminding = connection->acknowledge_handler.awaiting_acknowledge(); - if(reminding > 0) - debugMessage(this->getServerId(), "{} Could not get acknowledge for all commands before disconnecting. Acknowledges left: {}", CLIENT_STR_LOG_PREFIX, reminding); - } - break; - } - if(!this->connection->wait_empty_write_and_prepare_queue(timeout)) - continue; + if(!this->connection->wait_empty_write_and_prepare_queue(timeout)) { + debugMessage(this->getServerId(), "{} Write queue not empty!", CLIENT_STR_LOG_PREFIX); + } + { + auto reminding = connection->acknowledge_handler.awaiting_acknowledge(); + if(reminding > 0) + debugMessage(this->getServerId(), "{} Could not get acknowledge for all commands before disconnecting. Acknowledges left: {}", CLIENT_STR_LOG_PREFIX, reminding); + } + break; + } + if(!this->connection->wait_empty_write_and_prepare_queue(timeout)) + continue; - { - if(connection->acknowledge_handler.awaiting_acknowledge() > 0) { - usleep(5000); - continue; - } - } - debugMessage(this->getServerId(), "{} Write and acknowledge queue are flushed", CLIENT_STR_LOG_PREFIX); - break; - } - if(this->state != DISCONNECTING) return; - this->finalDisconnect(); - }); - flushing_thread->name("Flush thread VC").execute(); - return true; - } else { - this->state = DISCONNECTED; - auto f_thread = this->flushing_thread; - if(f_thread) { - threads::NegatedMutexLock l(this->disconnectLock); //Unlock the close lock again until the flush queue has finished (may with close may by interupt) - f_thread->join(); - } - this->finalDisconnect(); - } - return true; + { + if(connection->acknowledge_handler.awaiting_acknowledge() > 0) { + usleep(5000); + continue; + } + } + debugMessage(this->getServerId(), "{} Write and acknowledge queue are flushed", CLIENT_STR_LOG_PREFIX); + break; + } + if(this->state != DISCONNECTING) return; + this->finalDisconnect(); + }); + flushing_thread->name("Flush thread VC").execute(); + return true; + } else { + this->state = DISCONNECTED; + auto f_thread = this->flushing_thread; + if(f_thread) { + threads::NegatedMutexLock l(this->disconnectLock); //Unlock the close lock again until the flush queue has finished (may with close may by interupt) + f_thread->join(); + } + this->finalDisconnect(); + } + return true; } void VoiceClient::finalDisconnect() { - auto ownLock = dynamic_pointer_cast(_this.lock()); - assert(ownLock); + auto ownLock = dynamic_pointer_cast(_this.lock()); + assert(ownLock); - threads::MutexLock disconnect_lock(this->disconnectLock); - lock_guard disconnect_lock_final(this->finalDisconnectLock); - if(this->final_disconnected) { - logError(this->getServerId(), "Tried to final disconnect {}/{} twice", this->getLoggingPeerIp() + ":" + to_string(this->getPeerPort()), this->getDisplayName()); - return; - } - this->final_disconnected = true; - this->state = ConnectionState::DISCONNECTED; + threads::MutexLock disconnect_lock(this->disconnectLock); + lock_guard disconnect_lock_final(this->finalDisconnectLock); + if(this->final_disconnected) { + logError(this->getServerId(), "Tried to final disconnect {}/{} twice", this->getLoggingPeerIp() + ":" + to_string(this->getPeerPort()), this->getDisplayName()); + return; + } + this->final_disconnected = true; + this->state = ConnectionState::DISCONNECTED; - threads::MutexLock command_lock(this->command_lock); //We should not progress any commands while disconnecting - //Unload manager cache - this->processLeave(); - { - if(this->flushing_thread) this->flushing_thread->detach(); //The thread itself should be already done or executing this method - this->flushing_thread.reset(); - } - if(this->voice_server) this->voice_server->unregisterConnection(ownLock); + threads::MutexLock command_lock(this->command_lock); //We should not progress any commands while disconnecting + //Unload manager cache + this->processLeave(); + { + if(this->flushing_thread) this->flushing_thread->detach(); //The thread itself should be already done or executing this method + this->flushing_thread.reset(); + } + if(this->voice_server) this->voice_server->unregisterConnection(ownLock); } void VoiceClient::execute_handle_packet(const std::chrono::system_clock::time_point &time) { - this->connection->execute_handle_packet(time); + this->connection->execute_handle_packet(time); } void VoiceClient::send_voice_packet(const pipes::buffer_view &voice_buffer, const SpeakingClient::VoicePacketFlags &flags) { - auto packet = make_shared(PacketTypeInfo::Voice, voice_buffer.length()); - { - PacketFlag::PacketFlags packet_flags = PacketFlag::None; - packet_flags |= flags.encrypted ? 0 : PacketFlag::Unencrypted; - packet_flags |= flags.head ? PacketFlag::Compressed : 0; - packet_flags |= flags.fragmented ? PacketFlag::Fragmented : 0; - packet_flags |= flags.new_protocol ? PacketFlag::NewProtocol : 0; - packet->set_flags(packet_flags); - } + auto packet = make_shared(PacketTypeInfo::Voice, voice_buffer.length()); + { + PacketFlag::PacketFlags packet_flags = PacketFlag::None; + packet_flags |= flags.encrypted ? 0 : PacketFlag::Unencrypted; + packet_flags |= flags.head ? PacketFlag::Compressed : 0; + packet_flags |= flags.fragmented ? PacketFlag::Fragmented : 0; + packet_flags |= flags.new_protocol ? PacketFlag::NewProtocol : 0; + packet->set_flags(packet_flags); + } - memcpy(packet->data().data_ptr(), voice_buffer.data_ptr(), voice_buffer.length()); - this->connection->sendPacket(packet, false, false); + memcpy(packet->data().data_ptr(), voice_buffer.data_ptr(), voice_buffer.length()); + this->connection->sendPacket(packet, false, false); } void VoiceClient::send_voice_whisper_packet(const pipes::buffer_view &voice_buffer, const SpeakingClient::VoicePacketFlags &flags) { - auto packet = make_shared(PacketTypeInfo::VoiceWhisper, voice_buffer.length()); - { - PacketFlag::PacketFlags packet_flags = PacketFlag::None; - packet_flags |= flags.encrypted ? 0 : PacketFlag::Unencrypted; - packet_flags |= flags.head ? PacketFlag::Compressed : 0; - packet_flags |= flags.fragmented ? PacketFlag::Fragmented : 0; - packet_flags |= flags.new_protocol ? PacketFlag::NewProtocol : 0; - packet->set_flags(packet_flags); - } + auto packet = make_shared(PacketTypeInfo::VoiceWhisper, voice_buffer.length()); + { + PacketFlag::PacketFlags packet_flags = PacketFlag::None; + packet_flags |= flags.encrypted ? 0 : PacketFlag::Unencrypted; + packet_flags |= flags.head ? PacketFlag::Compressed : 0; + packet_flags |= flags.fragmented ? PacketFlag::Fragmented : 0; + packet_flags |= flags.new_protocol ? PacketFlag::NewProtocol : 0; + packet->set_flags(packet_flags); + } - memcpy(packet->data().data_ptr(), voice_buffer.data_ptr(), voice_buffer.length()); - this->connection->sendPacket(packet, false, false); + memcpy(packet->data().data_ptr(), voice_buffer.data_ptr(), voice_buffer.length()); + this->connection->sendPacket(packet, false, false); } \ No newline at end of file diff --git a/server/src/client/voice/VoiceClient.h b/server/src/client/voice/VoiceClient.h index e5283f0..5acde66 100644 --- a/server/src/client/voice/VoiceClient.h +++ b/server/src/client/voice/VoiceClient.h @@ -42,7 +42,7 @@ namespace ts { class VoiceClient : public SpeakingClient { friend class TSServer; friend class VoiceServer; - friend class POWHandler; + friend class POWHandler; friend class ts::connection::VoiceClientConnection; friend class ConnectedClient; friend class io::IOServerHandler; @@ -50,12 +50,12 @@ namespace ts { VoiceClient(const std::shared_ptr& server,const sockaddr_storage*); ~VoiceClient(); - bool closeConnection(const std::chrono::system_clock::time_point &timeout = std::chrono::system_clock::time_point()) override; + bool closeConnection(const std::chrono::system_clock::time_point &timeout = std::chrono::system_clock::time_point()) override; bool disconnect(const std::string&) override; - bool disconnect(ViewReasonId /* reason type */, const std::string& /* reason */, const std::shared_ptr& /* invoker */, bool /* notify viewer */); + bool disconnect(ViewReasonId /* reason type */, const std::string& /* reason */, const std::shared_ptr& /* invoker */, bool /* notify viewer */); virtual void sendCommand(const ts::Command &command, bool low = false) { return this->sendCommand0(command, low); } - /* Note: Order is only guaranteed if progressDirectly is on! */ + /* Note: Order is only guaranteed if progressDirectly is on! */ virtual void sendCommand0(const ts::Command &command, bool low = false, bool progressDirectly = false, std::unique_ptr> listener = nullptr); virtual void sendAcknowledge(uint16_t packetId, bool low = false); @@ -66,25 +66,25 @@ namespace ts { connection::VoiceClientConnection* connection; protected: - std::shared_ptr voice_server; + std::shared_ptr voice_server; - void initialize(); + void initialize(); virtual void tick(const std::chrono::system_clock::time_point &time) override; void handlePacketCommand(const std::unique_ptr&); void handlePacketAck(const std::unique_ptr&); void handlePacketVoice(const std::unique_ptr&); void handlePacketPing(const std::unique_ptr&); - void handlePacketInit(const std::unique_ptr&); + void handlePacketInit(const std::unique_ptr&); - //Handshake helpers + //Handshake helpers - public: - void send_voice_packet(const pipes::buffer_view &packet, const VoicePacketFlags &flags) override; - void send_voice_whisper_packet(const pipes::buffer_view &packet, const VoicePacketFlags &flags) override; + public: + void send_voice_packet(const pipes::buffer_view &packet, const VoicePacketFlags &flags) override; + void send_voice_whisper_packet(const pipes::buffer_view &packet, const VoicePacketFlags &flags) override; - protected: + protected: virtual CommandResult handleCommand(Command &command) override; //Some helper method @@ -93,41 +93,41 @@ namespace ts { //Ping/pong uint16_t lastPingId = 0; std::chrono::milliseconds ping = std::chrono::milliseconds(0); - std::chrono::system_clock::time_point lastPingResponse; - std::chrono::system_clock::time_point lastPingRequest; + std::chrono::system_clock::time_point lastPingResponse; + std::chrono::system_clock::time_point lastPingRequest; - std::chrono::system_clock::time_point last_packet_handshake; + std::chrono::system_clock::time_point last_packet_handshake; private: - int socket = 0; - io::pktinfo_storage address_info; + int socket = 0; + io::pktinfo_storage address_info; void finalDisconnect(); - bool final_disconnected = false; + bool final_disconnected = false; //General TS3 manager commands - CommandResult handleCommandClientInitIv(Command&); - CommandResult handleCommandClientEk(const std::unique_ptr&, Command&); + CommandResult handleCommandClientInitIv(Command&); + CommandResult handleCommandClientEk(const std::unique_ptr&, Command&); CommandResult handleCommandClientInit(Command&) override; CommandResult handleCommandClientDisconnect(Command&); - //Locked by finalDisconnect, disconnect and close connection + //Locked by finalDisconnect, disconnect and close connection std::shared_ptr flushing_thread; - struct { - bool client_init = false; - bool new_protocol = false; - bool protocol_encrypted = false; + struct { + bool client_init = false; + bool new_protocol = false; + bool protocol_encrypted = false; - uint32_t client_time = 0; - std::string alpha; - std::string beta; - std::shared_ptr chain_data; - std::shared_ptr remote_key; - } crypto; + uint32_t client_time = 0; + std::string alpha; + std::string beta; + std::shared_ptr chain_data; + std::shared_ptr remote_key; + } crypto; - std::shared_ptr> event_handle_packet; - void execute_handle_packet(const std::chrono::system_clock::time_point& /* scheduled */); + std::shared_ptr> event_handle_packet; + void execute_handle_packet(const std::chrono::system_clock::time_point& /* scheduled */); }; } } \ No newline at end of file diff --git a/server/src/client/voice/VoiceClientCommandHandler.cpp b/server/src/client/voice/VoiceClientCommandHandler.cpp index 90949c0..5313809 100644 --- a/server/src/client/voice/VoiceClientCommandHandler.cpp +++ b/server/src/client/voice/VoiceClientCommandHandler.cpp @@ -17,15 +17,15 @@ using namespace ts; CommandResult VoiceClient::handleCommand(ts::Command &command) { - threads::MutexLock l2(this->command_lock); + threads::MutexLock l2(this->command_lock); if(this->state == ConnectionState::DISCONNECTED) return CommandResult::NotImplemented; if(!this->voice_server) return CommandResult::NotImplemented; if(this->state == ConnectionState::INIT_HIGH && this->handshake.state == HandshakeState::SUCCEEDED) { if(command.command() == "clientinit") - return this->handleCommandClientInit(command); + return this->handleCommandClientInit(command); } else if(command.command() == "clientdisconnect") - return this->handleCommandClientDisconnect(command); + return this->handleCommandClientDisconnect(command); return SpeakingClient::handleCommand(command); } @@ -33,7 +33,7 @@ inline bool calculate_security_level(int& result, ecc_key* pubKey, size_t offset size_t pubLength = 256; char pubBuffer[256]; if((result = ecc_export(reinterpret_cast(pubBuffer), &pubLength, PK_PUBLIC, pubKey)) != CRYPT_OK) - return false; + return false; std::string hashStr = base64_encode(pubBuffer, pubLength) + to_string(offset); char shaBuffer[SHA_DIGEST_LENGTH]; @@ -46,39 +46,39 @@ inline bool calculate_security_level(int& result, ecc_key* pubKey, size_t offset if(shaBuffer[i] == 0) zeroBits += 8; else break; if(i < SHA_DIGEST_LENGTH) { - for(int bit = 0; bit < 8; bit++) { - if((shaBuffer[i] & (1 << bit)) == 0) zeroBits++; - else break; - } + for(int bit = 0; bit < 8; bit++) { + if((shaBuffer[i] & (1 << bit)) == 0) zeroBits++; + else break; + } } - result = zeroBits; - return true; + result = zeroBits; + return true; } CommandResult VoiceClient::handleCommandClientInit(Command &cmd) { - this->crypto.client_init = true; - this->connection->acknowledge_handler.reset(); + this->crypto.client_init = true; + this->connection->acknowledge_handler.reset(); - if(this->getType() == ClientType::CLIENT_TEAMSPEAK) { - int securityLevel; - if(!calculate_security_level(securityLevel, this->crypto.remote_key.get(), cmd["client_key_offset"])) { - logError(this->getServerId(), "[{}] Failed to calculate security level. Error code: {}", CLIENT_STR_LOG_PREFIX, securityLevel); - return {ErrorType::VSError}; - } - if(securityLevel < 8) - return {findError("channel_invalid_security_hash"), "cant calculate security level"}; + if(this->getType() == ClientType::CLIENT_TEAMSPEAK) { + int securityLevel; + if(!calculate_security_level(securityLevel, this->crypto.remote_key.get(), cmd["client_key_offset"])) { + logError(this->getServerId(), "[{}] Failed to calculate security level. Error code: {}", CLIENT_STR_LOG_PREFIX, securityLevel); + return {ErrorType::VSError}; + } + if(securityLevel < 8) + return {findError("channel_invalid_security_hash"), "cant calculate security level"}; - auto requiredLevel = this->getServer()->properties()[property::VIRTUALSERVER_NEEDED_IDENTITY_SECURITY_LEVEL].as(); - if(securityLevel < requiredLevel) return {findError("client_could_not_validate_identity"), to_string(requiredLevel)}; - } + auto requiredLevel = this->getServer()->properties()[property::VIRTUALSERVER_NEEDED_IDENTITY_SECURITY_LEVEL].as(); + if(securityLevel < requiredLevel) return {findError("client_could_not_validate_identity"), to_string(requiredLevel)}; + } - this->lastPingResponse = std::chrono::system_clock::now(); - return SpeakingClient::handleCommandClientInit(cmd); + this->lastPingResponse = std::chrono::system_clock::now(); + return SpeakingClient::handleCommandClientInit(cmd); } CommandResult VoiceClient::handleCommandClientDisconnect(Command& cmd) { - auto reason = cmd["reasonmsg"].size() > 0 ? cmd["reasonmsg"].as() : ""; - this->disconnect(VREASON_SERVER_LEFT, reason, nullptr, true); + auto reason = cmd["reasonmsg"].size() > 0 ? cmd["reasonmsg"].as() : ""; + this->disconnect(VREASON_SERVER_LEFT, reason, nullptr, true); logMessage(this->getServerId(), "{} Got remote disconnect with the reason '{}'", CLIENT_STR_LOG_PREFIX, reason); return CommandResult::Success; } \ No newline at end of file diff --git a/server/src/client/voice/VoiceClientConnection.cpp b/server/src/client/voice/VoiceClientConnection.cpp index a7201f8..1516b46 100644 --- a/server/src/client/voice/VoiceClientConnection.cpp +++ b/server/src/client/voice/VoiceClientConnection.cpp @@ -30,32 +30,32 @@ using namespace ts::protocol; using namespace ts::server; VoiceClientConnection::VoiceClientConnection(VoiceClient* client) : client(client) { - memtrack::allocated(this); + memtrack::allocated(this); - this->crypt_handler.reset(); - debugMessage(client->getServer()->getServerId(), "Allocated new voice client connection at {}", (void*) this); + this->crypt_handler.reset(); + debugMessage(client->getServer()->getServerId(), "Allocated new voice client connection at {}", (void*) this); } VoiceClientConnection::~VoiceClientConnection() { - /* locking here should be useless, but just to ensure! */ - { - lock_guard write_queue_lock(this->write_queue_lock); - this->write_queue.clear(); - } + /* locking here should be useless, but just to ensure! */ + { + lock_guard write_queue_lock(this->write_queue_lock); + this->write_queue.clear(); + } - for(auto& category : this->write_preprocess_queues) { - lock_guard work_lock{category.work_lock}; - lock_guard queue_lock{category.queue_lock}; + for(auto& category : this->write_preprocess_queues) { + lock_guard work_lock{category.work_lock}; + lock_guard queue_lock{category.queue_lock}; - category.queue.clear(); - } - this->client = nullptr; - memtrack::freed(this); + category.queue.clear(); + } + this->client = nullptr; + memtrack::freed(this); } void VoiceClientConnection::triggerWrite() { - if(this->client->voice_server) - this->client->voice_server->triggerWrite(dynamic_pointer_cast(this->client->_this.lock())); + if(this->client->voice_server) + this->client->voice_server->triggerWrite(dynamic_pointer_cast(this->client->_this.lock())); } #ifdef CLIENT_LOG_PREFIX @@ -67,644 +67,644 @@ void VoiceClientConnection::triggerWrite() { void VoiceClientConnection::handleDatagramReceived(const pipes::buffer_view& buffer) { #ifdef FUZZING_TESTING_INCOMMING - #ifdef FIZZING_TESTING_DISABLE_HANDSHAKE - if (this->client->state == ConnectionState::CONNECTED) { - #endif - if ((rand() % FUZZING_TESTING_DROP_MAX) < FUZZING_TESTING_DROP) { - debugMessage(this->client->getServerId(), "{}[FUZZING] Dropping incoming packet of length {}", CLIENT_STR_LOG_PREFIX_(this->client), buffer.length()); - return; - } - #ifdef FIZZING_TESTING_DISABLE_HANDSHAKE - } - #endif + #ifdef FIZZING_TESTING_DISABLE_HANDSHAKE + if (this->client->state == ConnectionState::CONNECTED) { + #endif + if ((rand() % FUZZING_TESTING_DROP_MAX) < FUZZING_TESTING_DROP) { + debugMessage(this->client->getServerId(), "{}[FUZZING] Dropping incoming packet of length {}", CLIENT_STR_LOG_PREFIX_(this->client), buffer.length()); + return; + } + #ifdef FIZZING_TESTING_DISABLE_HANDSHAKE + } + #endif #endif - auto packet = ClientPacket::from_buffer(buffer); + auto packet = ClientPacket::from_buffer(buffer); - auto packet_type = packet->type(); - auto packet_id = packet->packetId(); - auto ordered = packet_type.type() == protocol::COMMAND || packet_type.type() == protocol::COMMAND_LOW; + auto packet_type = packet->type(); + auto packet_id = packet->packetId(); + auto ordered = packet_type.type() == protocol::COMMAND || packet_type.type() == protocol::COMMAND_LOW; - if(packet_type.type() < 0 || packet_type.type() >= this->_packet_buffers.size()) { - logError(this->client->getServerId(), "{} Received invalid packet. Invalid packet type {}. Dropping packet.", CLIENT_STR_LOG_PREFIX_(this->client), packet_type.type()); - return; - } + if(packet_type.type() < 0 || packet_type.type() >= this->_packet_buffers.size()) { + logError(this->client->getServerId(), "{} Received invalid packet. Invalid packet type {}. Dropping packet.", CLIENT_STR_LOG_PREFIX_(this->client), packet_type.type()); + return; + } - auto& read_queue = this->_packet_buffers[packet_type.type()]; - packet->generationId(read_queue.generation(packet_id)); + auto& read_queue = this->_packet_buffers[packet_type.type()]; + packet->generationId(read_queue.generation(packet_id)); - if(ordered) { - unique_lock queue_lock(read_queue.buffer_lock); - auto result = read_queue.accept_index(packet_id); - if(result != 0) { /* packet index is ahead buffer index */ - debugMessage(this->client->getServerId(), "{} Got packet of type {} which is out of the buffer range of {} ({}). Packet ID: {}, Current index: {}. Dropping packet", - CLIENT_STR_LOG_PREFIX_(this->client), - packet_type.name(), - read_queue.capacity(), - result == -1 ? "underflow" : "overflow", - packet_id, - read_queue.current_index() - ); + if(ordered) { + unique_lock queue_lock(read_queue.buffer_lock); + auto result = read_queue.accept_index(packet_id); + if(result != 0) { /* packet index is ahead buffer index */ + debugMessage(this->client->getServerId(), "{} Got packet of type {} which is out of the buffer range of {} ({}). Packet ID: {}, Current index: {}. Dropping packet", + CLIENT_STR_LOG_PREFIX_(this->client), + packet_type.name(), + read_queue.capacity(), + result == -1 ? "underflow" : "overflow", + packet_id, + read_queue.current_index() + ); - if(result == -1) { /* underflow */ - /* we've already got the packet, but the client dosnt know that so we've to send the acknowledge again */ - if(this->client->crypto.protocol_encrypted && (packet->type() == PacketTypeInfo::Command || packet->type() == PacketTypeInfo::CommandLow)){ //needs an acknowledge - this->client->sendAcknowledge(packet->packetId(), packet->type() == PacketTypeInfo::CommandLow); - } - } - return; - } - } + if(result == -1) { /* underflow */ + /* we've already got the packet, but the client dosnt know that so we've to send the acknowledge again */ + if(this->client->crypto.protocol_encrypted && (packet->type() == PacketTypeInfo::Command || packet->type() == PacketTypeInfo::CommandLow)){ //needs an acknowledge + this->client->sendAcknowledge(packet->packetId(), packet->type() == PacketTypeInfo::CommandLow); + } + } + return; + } + } - packet->setEncrypted(!packet->has_flag(PacketFlag::Unencrypted)); // && packet->type() != PacketType::Init1 - if(packet->type() == PacketTypeInfo::Command || packet->type() == PacketTypeInfo::CommandLow){ - packet->setCompressed(packet->has_flag(PacketFlag::Compressed)); - } - //NOTICE I found out that the Compressed flag is set if the packet contains an audio header + packet->setEncrypted(!packet->has_flag(PacketFlag::Unencrypted)); // && packet->type() != PacketType::Init1 + if(packet->type() == PacketTypeInfo::Command || packet->type() == PacketTypeInfo::CommandLow){ + packet->setCompressed(packet->has_flag(PacketFlag::Compressed)); + } + //NOTICE I found out that the Compressed flag is set if the packet contains an audio header - string error = "success"; - if(this->client->state == ConnectionState::INIT_LOW && packet->type() != PacketTypeInfo::Init1){ - //Sends command packet as legacy support (skip step 1-3 | Direct clientinit with default key) - return; - } + string error = "success"; + if(this->client->state == ConnectionState::INIT_LOW && packet->type() != PacketTypeInfo::Init1){ + //Sends command packet as legacy support (skip step 1-3 | Direct clientinit with default key) + return; + } - if(!this->crypt_handler.progressPacketIn(packet.get(), error, false)){ - //FIXME Only try to decrypt by default when its no flood attack! - if(!this->client->crypto.client_init && !this->crypt_handler.use_default()) { - if(!this->crypt_handler.progressPacketIn(packet.get(), error, true)){ - debugMessage( - this->client->getServerId(), - "{} Cant decrypt packet even with default key! Type: {}, Error: {}, Packet ID: {}, Generation: {}", - CLIENT_STR_LOG_PREFIX_(this->client), - packet->type().name(), - error, - packet_id, - packet->generationId() - ); - return; - } else { - debugMessage( - this->client->getServerId(), - "{} Cant decrypt packet with configured key {}. Error: {}. Succeeded with default key!", - CLIENT_STR_LOG_PREFIX_(this->client), - packet->type().name(), - error - ); - } - } else { - bool succeeded = false; - if(packet_type == PacketTypeInfo::Voice) { - /* FIXME: This try and error should not happen! */ - packet->generationId(packet->generationId() + 1); - succeeded = this->crypt_handler.progressPacketIn(packet.get(), error, false); - } - if(succeeded) { - auto old_packet_id = read_queue.current_index(); - read_queue.set_generation_packet(packet->generationId(), packet->packetId()); + if(!this->crypt_handler.progressPacketIn(packet.get(), error, false)){ + //FIXME Only try to decrypt by default when its no flood attack! + if(!this->client->crypto.client_init && !this->crypt_handler.use_default()) { + if(!this->crypt_handler.progressPacketIn(packet.get(), error, true)){ + debugMessage( + this->client->getServerId(), + "{} Cant decrypt packet even with default key! Type: {}, Error: {}, Packet ID: {}, Generation: {}", + CLIENT_STR_LOG_PREFIX_(this->client), + packet->type().name(), + error, + packet_id, + packet->generationId() + ); + return; + } else { + debugMessage( + this->client->getServerId(), + "{} Cant decrypt packet with configured key {}. Error: {}. Succeeded with default key!", + CLIENT_STR_LOG_PREFIX_(this->client), + packet->type().name(), + error + ); + } + } else { + bool succeeded = false; + if(packet_type == PacketTypeInfo::Voice) { + /* FIXME: This try and error should not happen! */ + packet->generationId(packet->generationId() + 1); + succeeded = this->crypt_handler.progressPacketIn(packet.get(), error, false); + } + if(succeeded) { + auto old_packet_id = read_queue.current_index(); + read_queue.set_generation_packet(packet->generationId(), packet->packetId()); - logWarning(this->client->getServerId(), "{} Voice packet generation counter missed generation increasement. From {} to {} from packet id {} to {}", - CLIENT_STR_LOG_PREFIX_(this->client), - packet->generationId() - 1, - packet->generationId(), - old_packet_id, - packet->packetId() - ); - } else { - debugMessage( - this->client->getServerId(), - "{} Cant decrypt packet of type {}. Packet ID: {}, Estimated generation: {}, Full counter: {}. Dropping packet. Error: {}", - CLIENT_STR_LOG_PREFIX_(this->client), - packet->type().name(), - packet->packetId(), - packet->generationId(), - read_queue.full_index(), - error - ); - return; - } - } - } + logWarning(this->client->getServerId(), "{} Voice packet generation counter missed generation increasement. From {} to {} from packet id {} to {}", + CLIENT_STR_LOG_PREFIX_(this->client), + packet->generationId() - 1, + packet->generationId(), + old_packet_id, + packet->packetId() + ); + } else { + debugMessage( + this->client->getServerId(), + "{} Cant decrypt packet of type {}. Packet ID: {}, Estimated generation: {}, Full counter: {}. Dropping packet. Error: {}", + CLIENT_STR_LOG_PREFIX_(this->client), + packet->type().name(), + packet->packetId(), + packet->generationId(), + read_queue.full_index(), + error + ); + return; + } + } + } - if(packet->type() == PacketTypeInfo::Command || packet->type() == PacketTypeInfo::CommandLow){ - if(packet->has_flag(PacketFlag::Unencrypted) && this->client->state != ConnectionState::INIT_HIGH){ - this->client->disconnect("Invalid packet. Command should not be unencrypted!"); - logger::logger(this->client->getServer()->getServerId())->error("{} Voice manager {}/{} tried to send a unencrypted command packet.", CLIENT_STR_LOG_PREFIX_(this->client), client->getDisplayName(), this->client->getLoggingPeerIp()); - return; - } - } + if(packet->type() == PacketTypeInfo::Command || packet->type() == PacketTypeInfo::CommandLow){ + if(packet->has_flag(PacketFlag::Unencrypted) && this->client->state != ConnectionState::INIT_HIGH){ + this->client->disconnect("Invalid packet. Command should not be unencrypted!"); + logger::logger(this->client->getServer()->getServerId())->error("{} Voice manager {}/{} tried to send a unencrypted command packet.", CLIENT_STR_LOG_PREFIX_(this->client), client->getDisplayName(), this->client->getLoggingPeerIp()); + return; + } + } #ifndef CONNECTION_NO_STATISTICS - if(this->client && this->client->getServer()) - this->client->connectionStatistics->logIncomingPacket(*packet); + if(this->client && this->client->getServer()) + this->client->connectionStatistics->logIncomingPacket(*packet); #endif #ifdef LOG_INCOMPING_PACKET_FRAGMENTS - debugMessage(lstream << CLIENT_LOG_PREFIX << "Recived packet. PacketId: " << packet->packetId() << " PacketType: " << packet->type().name() << " Flags: " << packet->flags() << " - " << packet->data() << endl); + debugMessage(lstream << CLIENT_LOG_PREFIX << "Recived packet. PacketId: " << packet->packetId() << " PacketType: " << packet->type().name() << " Flags: " << packet->flags() << " - " << packet->data() << endl); #endif - if(packet->type() == PacketTypeInfo::Command || packet->type() == PacketTypeInfo::CommandLow){ //needs an acknowledge - if(this->client->crypto.protocol_encrypted) { - #ifdef LOG_AUTO_ACK_AUTORESPONSE - logMessage(this->client->getServerId(), "{}[Ack] Sending ack for incoming command packet {}", CLIENT_STR_LOG_PREFIX_(this->client), packet->packetId()); - #endif - this->client->sendAcknowledge(packet->packetId(), packet->type() == PacketTypeInfo::CommandLow); - } else { - debugMessage(this->client->getServerId(), "{}[Ack] Ignoring ack for {}", CLIENT_STR_LOG_PREFIX_(this->client), packet->packetId()); - } - } + if(packet->type() == PacketTypeInfo::Command || packet->type() == PacketTypeInfo::CommandLow){ //needs an acknowledge + if(this->client->crypto.protocol_encrypted) { + #ifdef LOG_AUTO_ACK_AUTORESPONSE + logMessage(this->client->getServerId(), "{}[Ack] Sending ack for incoming command packet {}", CLIENT_STR_LOG_PREFIX_(this->client), packet->packetId()); + #endif + this->client->sendAcknowledge(packet->packetId(), packet->type() == PacketTypeInfo::CommandLow); + } else { + debugMessage(this->client->getServerId(), "{}[Ack] Ignoring ack for {}", CLIENT_STR_LOG_PREFIX_(this->client), packet->packetId()); + } + } - { - unique_lock queue_lock(read_queue.buffer_lock); + { + unique_lock queue_lock(read_queue.buffer_lock); - if(ordered) { /* ordered */ - if(!read_queue.insert_index(packet_id, move(packet))) { - debugMessage(this->client->getServerId(), "{} Got ordered packet of type {} which is out of the buffer range of {}. Packet ID: {}, Full index: {}. Dropping packet", - CLIENT_STR_LOG_PREFIX_(this->client), - packet_type.name(), - read_queue.capacity(), - packet_id, - read_queue.full_index() - ); - /* return; dont stop here because we've to progress the packets */ - } - } else { - //TODO: Needs rethinking because read_queue.push_back increases the index, but this has not to be the packet id - if(!read_queue.push_back(move(packet))) { - debugMessage(this->client->getServerId(), "{} Got unordered packet of type {} which is out of the buffer capacity of {}. Packet ID: {}. Dropping packet.", - CLIENT_STR_LOG_PREFIX_(this->client), - packet_type.name(), - read_queue.capacity(), - packet_id, - read_queue.full_index() - ); - } - { - //A max entropy of 16 packets should not happen. This indicates more that 16 or more packets got lost - auto current_index = read_queue.current_index(); - if(current_index + 16 < packet_id) - read_queue.set_full_index_to(packet_id); - } - } - } + if(ordered) { /* ordered */ + if(!read_queue.insert_index(packet_id, move(packet))) { + debugMessage(this->client->getServerId(), "{} Got ordered packet of type {} which is out of the buffer range of {}. Packet ID: {}, Full index: {}. Dropping packet", + CLIENT_STR_LOG_PREFIX_(this->client), + packet_type.name(), + read_queue.capacity(), + packet_id, + read_queue.full_index() + ); + /* return; dont stop here because we've to progress the packets */ + } + } else { + //TODO: Needs rethinking because read_queue.push_back increases the index, but this has not to be the packet id + if(!read_queue.push_back(move(packet))) { + debugMessage(this->client->getServerId(), "{} Got unordered packet of type {} which is out of the buffer capacity of {}. Packet ID: {}. Dropping packet.", + CLIENT_STR_LOG_PREFIX_(this->client), + packet_type.name(), + read_queue.capacity(), + packet_id, + read_queue.full_index() + ); + } + { + //A max entropy of 16 packets should not happen. This indicates more that 16 or more packets got lost + auto current_index = read_queue.current_index(); + if(current_index + 16 < packet_id) + read_queue.set_full_index_to(packet_id); + } + } + } - auto voice_server = this->client->voice_server; - if(voice_server) - voice_server->schedule_execute(this->client); + auto voice_server = this->client->voice_server; + if(voice_server) + voice_server->schedule_execute(this->client); } bool VoiceClientConnection::verify_encryption(const pipes::buffer_view &packet /* incl. mac etc */) { - if((packet[12] & 0x80) != 0) /* we want an encrypted packet to verify the encryption */ - return false; + if((packet[12] & 0x80) != 0) /* we want an encrypted packet to verify the encryption */ + return false; - auto packet_type = (protocol::PacketType) (packet[12] & 0xF); - if(packet_type == protocol::PING || packet_type == protocol::PONG) return false; /* these packets could never be encrypted */ + auto packet_type = (protocol::PacketType) (packet[12] & 0xF); + if(packet_type == protocol::PING || packet_type == protocol::PONG) return false; /* these packets could never be encrypted */ - auto packet_id = (uint16_t) be2le16(&packet[8]); - auto generation = this->_packet_buffers[packet_type].generation(packet_id); - return this->crypt_handler.verify_encryption(packet, packet_id, generation); + auto packet_id = (uint16_t) be2le16(&packet[8]); + auto generation = this->_packet_buffers[packet_type].generation(packet_id); + return this->crypt_handler.verify_encryption(packet, packet_id, generation); } void VoiceClientConnection::execute_handle_packet(const std::chrono::system_clock::time_point& /* scheduled */) { - if(this->client->state == ConnectionState::DISCONNECTED || !this->client->getServer()) - return; + if(this->client->state == ConnectionState::DISCONNECTED || !this->client->getServer()) + return; - bool reexecute_handle = false; - unique_lock buffer_execute_lock; - auto packet = this->next_reassembled_packet(buffer_execute_lock, reexecute_handle); + bool reexecute_handle = false; + unique_lock buffer_execute_lock; + auto packet = this->next_reassembled_packet(buffer_execute_lock, reexecute_handle); - if(packet){ - auto startTime = system_clock::now(); - try { - const auto packet_type = packet->type(); - if(packet_type == PacketTypeInfo::Command || packet_type == PacketTypeInfo::CommandLow) - this->client->handlePacketCommand(packet); - else if(packet_type == PacketTypeInfo::Ack || packet_type == PacketTypeInfo::AckLow) - this->client->handlePacketAck(packet); - else if(packet_type == PacketTypeInfo::Voice || packet_type == PacketTypeInfo::VoiceWhisper) - this->client->handlePacketVoice(packet); - else if(packet_type == PacketTypeInfo::Ping || packet_type == PacketTypeInfo::Pong) - this->client->handlePacketPing(packet); - else if(packet_type == PacketTypeInfo::Init1) - this->client->handlePacketInit(packet); - } catch (std::exception& ex) { - logCritical(this->client->getServerId(), "{} Exception reached root tree! {}", CLIENT_STR_LOG_PREFIX_(this->client), ex.what()); - } + if(packet){ + auto startTime = system_clock::now(); + try { + const auto packet_type = packet->type(); + if(packet_type == PacketTypeInfo::Command || packet_type == PacketTypeInfo::CommandLow) + this->client->handlePacketCommand(packet); + else if(packet_type == PacketTypeInfo::Ack || packet_type == PacketTypeInfo::AckLow) + this->client->handlePacketAck(packet); + else if(packet_type == PacketTypeInfo::Voice || packet_type == PacketTypeInfo::VoiceWhisper) + this->client->handlePacketVoice(packet); + else if(packet_type == PacketTypeInfo::Ping || packet_type == PacketTypeInfo::Pong) + this->client->handlePacketPing(packet); + else if(packet_type == PacketTypeInfo::Init1) + this->client->handlePacketInit(packet); + } catch (std::exception& ex) { + logCritical(this->client->getServerId(), "{} Exception reached root tree! {}", CLIENT_STR_LOG_PREFIX_(this->client), ex.what()); + } - auto end = system_clock::now(); - if(end - startTime > milliseconds(10)) { - if(packet->type() != PacketTypeInfo::Command && packet->type() != PacketTypeInfo::CommandLow) { - logError(this->client->getServerId(), - "{} Handling of packet {} needs more than 10ms ({}ms)", - CLIENT_STR_LOG_PREFIX_(this->client), - packet->type().name(), - duration_cast(end - startTime).count() - ); - } - } - } - if(buffer_execute_lock.owns_lock()) - buffer_execute_lock.unlock(); + auto end = system_clock::now(); + if(end - startTime > milliseconds(10)) { + if(packet->type() != PacketTypeInfo::Command && packet->type() != PacketTypeInfo::CommandLow) { + logError(this->client->getServerId(), + "{} Handling of packet {} needs more than 10ms ({}ms)", + CLIENT_STR_LOG_PREFIX_(this->client), + packet->type().name(), + duration_cast(end - startTime).count() + ); + } + } + } + if(buffer_execute_lock.owns_lock()) + buffer_execute_lock.unlock(); - auto voice_server = this->client->voice_server; - if(voice_server && reexecute_handle) - this->client->voice_server->schedule_execute(this->client); + auto voice_server = this->client->voice_server; + if(voice_server && reexecute_handle) + this->client->voice_server->schedule_execute(this->client); } /* buffer_execute_lock: lock for in order execution */ unique_ptr VoiceClientConnection::next_reassembled_packet(unique_lock& buffer_execute_lock, bool& more) { - packet_buffer_t* buffer = nullptr; - unique_lock buffer_lock; /* general buffer lock */ + packet_buffer_t* buffer = nullptr; + unique_lock buffer_lock; /* general buffer lock */ - { - auto base_index = this->_packet_buffers_index; - auto select_index = base_index; - auto max_index = this->_packet_buffers.size(); + { + auto base_index = this->_packet_buffers_index; + auto select_index = base_index; + auto max_index = this->_packet_buffers.size(); - for(uint8_t index = 0; index < max_index; index++) { - if(!buffer) - select_index++; + for(uint8_t index = 0; index < max_index; index++) { + if(!buffer) + select_index++; - auto& buf = this->_packet_buffers[base_index++ % max_index]; - unique_lock ring_lock(buf.buffer_lock, try_to_lock); - if(!ring_lock.owns_lock()) continue; + auto& buf = this->_packet_buffers[base_index++ % max_index]; + unique_lock ring_lock(buf.buffer_lock, try_to_lock); + if(!ring_lock.owns_lock()) continue; - if(buf.front_set()) { - if(!buffer) { /* lets still test for reexecute */ - buffer_execute_lock = unique_lock(buf.execute_lock, try_to_lock); - if(!buffer_execute_lock.owns_lock()) continue; + if(buf.front_set()) { + if(!buffer) { /* lets still test for reexecute */ + buffer_execute_lock = unique_lock(buf.execute_lock, try_to_lock); + if(!buffer_execute_lock.owns_lock()) continue; - buffer_lock = move(ring_lock); - buffer = &buf; - } else { - more = true; - break; - } - } - } - this->_packet_buffers_index = static_cast(select_index % max_index); /* guarantee that we will not hangup with commands! */ - } + buffer_lock = move(ring_lock); + buffer = &buf; + } else { + more = true; + break; + } + } + } + this->_packet_buffers_index = static_cast(select_index % max_index); /* guarantee that we will not hangup with commands! */ + } - if(!buffer) - return nullptr; /* we've no packets */ + if(!buffer) + return nullptr; /* we've no packets */ - auto current_packet = &*buffer->slot_value(0); - if(!current_packet) { - logCritical(this->client->getServer()->getServerId(), "buffer->slot_value(0) returned nullptr, but set flag has been set!"); - return buffer->pop_front(); /* should be null! */ - } + auto current_packet = &*buffer->slot_value(0); + if(!current_packet) { + logCritical(this->client->getServer()->getServerId(), "buffer->slot_value(0) returned nullptr, but set flag has been set!"); + return buffer->pop_front(); /* should be null! */ + } - if(current_packet->type() != PacketTypeInfo::Command && current_packet->type() != PacketTypeInfo::CommandLow) { - auto tmp = buffer->pop_front(); /* we don't have to reassemble anything */ - more |= buffer->front_set(); /* set the more flag if we know that we have more of this packet */ - return tmp; - } + if(current_packet->type() != PacketTypeInfo::Command && current_packet->type() != PacketTypeInfo::CommandLow) { + auto tmp = buffer->pop_front(); /* we don't have to reassemble anything */ + more |= buffer->front_set(); /* set the more flag if we know that we have more of this packet */ + return tmp; + } - unique_ptr final_packet; - uint16_t sequence_length = 1; + unique_ptr final_packet; + uint16_t sequence_length = 1; - if(current_packet->has_flag(PacketFlag::Fragmented)) { - size_t buffer_length = ClientPacket::META_SIZE; - do { - if(sequence_length >= buffer->capacity()) { - logError(this->client->getServerId(), "{} Received fragmented packets which have a too long order. Dropping queue, which will cause a client drop.", CLIENT_STR_LOG_PREFIX_(this->client)); - buffer->clear(); - return nullptr; /* we've nothing to handle */ - } + if(current_packet->has_flag(PacketFlag::Fragmented)) { + size_t buffer_length = ClientPacket::META_SIZE; + do { + if(sequence_length >= buffer->capacity()) { + logError(this->client->getServerId(), "{} Received fragmented packets which have a too long order. Dropping queue, which will cause a client drop.", CLIENT_STR_LOG_PREFIX_(this->client)); + buffer->clear(); + return nullptr; /* we've nothing to handle */ + } - buffer_length += current_packet->data_length(); - current_packet = &*buffer->slot_value(sequence_length++); - } while(current_packet && !current_packet->has_flag(PacketFlag::Fragmented)); - if(!current_packet) - return nullptr; /* we haven't found the end yet! */ - buffer_length += current_packet->data_length(); + buffer_length += current_packet->data_length(); + current_packet = &*buffer->slot_value(sequence_length++); + } while(current_packet && !current_packet->has_flag(PacketFlag::Fragmented)); + if(!current_packet) + return nullptr; /* we haven't found the end yet! */ + buffer_length += current_packet->data_length(); - /* okey we have all fragments lets reassemble */ + /* okey we have all fragments lets reassemble */ - /* - * Packet sequence could never be so long. If it is so then the data_length() returned an invalid value. - * We're checking it here because we dont want to make a huge allocation - */ - assert(buffer_length < 512 * 1024 * 1024); + /* + * Packet sequence could never be so long. If it is so then the data_length() returned an invalid value. + * We're checking it here because we dont want to make a huge allocation + */ + assert(buffer_length < 512 * 1024 * 1024); - pipes::buffer packet_buffer{buffer_length}; - char* packet_buffer_ptr = &packet_buffer[0]; - size_t packet_count = 0; + pipes::buffer packet_buffer{buffer_length}; + char* packet_buffer_ptr = &packet_buffer[0]; + size_t packet_count = 0; - unique_ptr packet; + unique_ptr packet; - /* initialize packet flags etc */ - { - packet = buffer->pop_front(); - packet_count++; + /* initialize packet flags etc */ + { + packet = buffer->pop_front(); + packet_count++; - if(!packet) { - logCritical(this->client->getServer()->getServerId(), "buffer->pop_front() returned nullptr, but set flag has been set (0)!"); - return nullptr; - } + if(!packet) { + logCritical(this->client->getServer()->getServerId(), "buffer->pop_front() returned nullptr, but set flag has been set (0)!"); + return nullptr; + } - const auto length = packet->buffer().length(); - memcpy(packet_buffer_ptr, &packet->buffer()[0], length); - packet_buffer_ptr += length; - } + const auto length = packet->buffer().length(); + memcpy(packet_buffer_ptr, &packet->buffer()[0], length); + packet_buffer_ptr += length; + } - while(packet_count < sequence_length) { - packet = buffer->pop_front(); - packet_count++; + while(packet_count < sequence_length) { + packet = buffer->pop_front(); + packet_count++; - if(!packet) { - logCritical(this->client->getServer()->getServerId(), "buffer->pop_front() returned nullptr, but set flag has been set (1)!"); - return nullptr; - } + if(!packet) { + logCritical(this->client->getServer()->getServerId(), "buffer->pop_front() returned nullptr, but set flag has been set (1)!"); + return nullptr; + } - const auto length = packet->data_length(); - memcpy(packet_buffer_ptr, &packet->data()[0], length); - packet_buffer_ptr += length; - } + const auto length = packet->data_length(); + memcpy(packet_buffer_ptr, &packet->data()[0], length); + packet_buffer_ptr += length; + } - if((packet_buffer_ptr - 1) != &packet_buffer[packet_buffer.length() - 1]) { - logCritical(this->client->getServer()->getServerId(), - "Buffer over/underflow: packet_buffer_ptr != &packet_buffer[packet_buffer.length() - 1]; packet_buffer_ptr := {}; packet_buffer.end() := {}", - (void*) packet_buffer_ptr, - (void*) &packet_buffer[packet_buffer.length() - 1] - ); - } + if((packet_buffer_ptr - 1) != &packet_buffer[packet_buffer.length() - 1]) { + logCritical(this->client->getServer()->getServerId(), + "Buffer over/underflow: packet_buffer_ptr != &packet_buffer[packet_buffer.length() - 1]; packet_buffer_ptr := {}; packet_buffer.end() := {}", + (void*) packet_buffer_ptr, + (void*) &packet_buffer[packet_buffer.length() - 1] + ); + } - final_packet = ClientPacket::from_buffer(packet_buffer); - final_packet->setCompressed(final_packet->has_flag(PacketFlag::Compressed)); - } else { - final_packet = buffer->pop_front(); - if(!final_packet) { - logCritical(this->client->getServer()->getServerId(), "buffer->pop_front() returned nullptr, but set flag has been set (3)!"); - return nullptr; - } - } + final_packet = ClientPacket::from_buffer(packet_buffer); + final_packet->setCompressed(final_packet->has_flag(PacketFlag::Compressed)); + } else { + final_packet = buffer->pop_front(); + if(!final_packet) { + logCritical(this->client->getServer()->getServerId(), "buffer->pop_front() returned nullptr, but set flag has been set (3)!"); + return nullptr; + } + } - more |= buffer->front_set(); /* set the more flag if we have more to process */ - buffer_lock.unlock(); + more |= buffer->front_set(); /* set the more flag if we have more to process */ + buffer_lock.unlock(); - std::string error = "success"; - if(!this->compress_handler.progressPacketIn(&*final_packet, error)) { - logError(this->client->getServerId(), "{} Failed to decompress received packet. Error: {}", CLIENT_STR_LOG_PREFIX_(this->client), error); - final_packet = nullptr; - } + std::string error = "success"; + if(!this->compress_handler.progressPacketIn(&*final_packet, error)) { + logError(this->client->getServerId(), "{} Failed to decompress received packet. Error: {}", CLIENT_STR_LOG_PREFIX_(this->client), error); + final_packet = nullptr; + } - return final_packet; + return final_packet; } void VoiceClientConnection::sendPacket(const shared_ptr& original_packet, bool copy, bool prepare_directly) { - if(this->client->state == ConnectionState::DISCONNECTED) - return; + if(this->client->state == ConnectionState::DISCONNECTED) + return; - shared_ptr packet; - if(copy) { - packet = protocol::ServerPacket::from_buffer(original_packet->buffer().dup(buffer::allocate_buffer(original_packet->buffer().length()))); - if(original_packet->getListener()) - packet->setListener(std::move(original_packet->getListener())); - packet->memory_state.flags = original_packet->memory_state.flags; - } else { - packet = original_packet; - } + shared_ptr packet; + if(copy) { + packet = protocol::ServerPacket::from_buffer(original_packet->buffer().dup(buffer::allocate_buffer(original_packet->buffer().length()))); + if(original_packet->getListener()) + packet->setListener(std::move(original_packet->getListener())); + packet->memory_state.flags = original_packet->memory_state.flags; + } else { + packet = original_packet; + } - auto type = WritePreprocessCategory::from_type(packet->type().type()); - auto& queue = this->write_preprocess_queues[type]; - if(prepare_directly) { - vector buffers; - this->prepare_process_count++; + auto type = WritePreprocessCategory::from_type(packet->type().type()); + auto& queue = this->write_preprocess_queues[type]; + if(prepare_directly) { + vector buffers; + this->prepare_process_count++; - { - unique_lock work_lock{queue.work_lock}; - if(!this->prepare_packet_for_write(buffers, packet, work_lock)) { - logError(this->client->getServerId(), "{} Dropping packet!", CLIENT_STR_LOG_PREFIX_(this->client)); - this->prepare_process_count--; - return; - } - } + { + unique_lock work_lock{queue.work_lock}; + if(!this->prepare_packet_for_write(buffers, packet, work_lock)) { + logError(this->client->getServerId(), "{} Dropping packet!", CLIENT_STR_LOG_PREFIX_(this->client)); + this->prepare_process_count--; + return; + } + } - /* enqueue buffers for write */ - { - lock_guard write_queue_lock(this->write_queue_lock); - this->write_queue.insert(this->write_queue.end(), buffers.begin(), buffers.end()); - } - this->prepare_process_count--; /* we're now done preparing */ - } else { - lock_guard queue_lock{queue.queue_lock}; - queue.queue.push_back(packet); - queue.has_work = true; - } - this->triggerWrite(); + /* enqueue buffers for write */ + { + lock_guard write_queue_lock(this->write_queue_lock); + this->write_queue.insert(this->write_queue.end(), buffers.begin(), buffers.end()); + } + this->prepare_process_count--; /* we're now done preparing */ + } else { + lock_guard queue_lock{queue.queue_lock}; + queue.queue.push_back(packet); + queue.has_work = true; + } + this->triggerWrite(); } bool VoiceClientConnection::prepare_packet_for_write(vector &result, const shared_ptr &packet, std::unique_lock& work_lock) { - assert(work_lock.owns_lock()); + assert(work_lock.owns_lock()); - string error = "success"; + string error = "success"; - if(packet->type().compressable() && !packet->memory_state.fragment_entry) { - packet->enable_flag(PacketFlag::Compressed); - if(!this->compress_handler.progressPacketOut(packet.get(), error)) { - logError(this->getClient()->getServerId(), "{} Could not compress outgoing packet.\nThis could cause fatal failed for the client.\nError: {}", error); - return false; - } - } + if(packet->type().compressable() && !packet->memory_state.fragment_entry) { + packet->enable_flag(PacketFlag::Compressed); + if(!this->compress_handler.progressPacketOut(packet.get(), error)) { + logError(this->getClient()->getServerId(), "{} Could not compress outgoing packet.\nThis could cause fatal failed for the client.\nError: {}", error); + return false; + } + } - std::vector> fragments; - fragments.reserve((size_t) (packet->data().length() / packet->type().max_length()) + 1); + std::vector> fragments; + fragments.reserve((size_t) (packet->data().length() / packet->type().max_length()) + 1); - if(packet->data().length() > packet->type().max_length()) { - if(!packet->type().fragmentable()) { - logError(this->client->getServerId(), "{} We've tried to send a too long, not fragmentable, packet. Dropping packet of type {} with length {}", CLIENT_STR_LOG_PREFIX_(this->client), packet->type().name(), packet->data().length()); - return false; - } + if(packet->data().length() > packet->type().max_length()) { + if(!packet->type().fragmentable()) { + logError(this->client->getServerId(), "{} We've tried to send a too long, not fragmentable, packet. Dropping packet of type {} with length {}", CLIENT_STR_LOG_PREFIX_(this->client), packet->type().name(), packet->data().length()); + return false; + } - { //Split packets - auto buffer = packet->data(); + { //Split packets + auto buffer = packet->data(); - const auto max_length = packet->type().max_length(); - while(buffer.length() > max_length * 2) { - fragments.push_back(make_shared(packet->type(), buffer.view(0, max_length).dup(buffer::allocate_buffer(max_length)))); - buffer = buffer.range((size_t) max_length); - } + const auto max_length = packet->type().max_length(); + while(buffer.length() > max_length * 2) { + fragments.push_back(make_shared(packet->type(), buffer.view(0, max_length).dup(buffer::allocate_buffer(max_length)))); + buffer = buffer.range((size_t) max_length); + } - if(buffer.length() > max_length) { //Divide rest by 2 - fragments.push_back(make_shared(packet->type(), buffer.view(0, buffer.length() / 2).dup(buffer::allocate_buffer(buffer.length() / 2)))); - buffer = buffer.range(buffer.length() / 2); - } - fragments.push_back(make_shared(packet->type(), buffer)); + if(buffer.length() > max_length) { //Divide rest by 2 + fragments.push_back(make_shared(packet->type(), buffer.view(0, buffer.length() / 2).dup(buffer::allocate_buffer(buffer.length() / 2)))); + buffer = buffer.range(buffer.length() / 2); + } + fragments.push_back(make_shared(packet->type(), buffer)); - for(const auto& frag : fragments) { - frag->setFragmentedEntry(true); - frag->enable_flag(PacketFlag::NewProtocol); - } - } + for(const auto& frag : fragments) { + frag->setFragmentedEntry(true); + frag->enable_flag(PacketFlag::NewProtocol); + } + } - assert(fragments.size() >= 2); - fragments.front()->enable_flag(PacketFlag::Fragmented); - if(packet->has_flag(PacketFlag::Compressed)) - fragments.front()->enable_flag(PacketFlag::Compressed); + assert(fragments.size() >= 2); + fragments.front()->enable_flag(PacketFlag::Fragmented); + if(packet->has_flag(PacketFlag::Compressed)) + fragments.front()->enable_flag(PacketFlag::Compressed); - fragments.back()->enable_flag(PacketFlag::Fragmented); + fragments.back()->enable_flag(PacketFlag::Fragmented); - if(packet->getListener()) - fragments.back()->setListener(std::move(packet->getListener())); //Move the listener to the last :) - } else { - fragments.push_back(packet); - } + if(packet->getListener()) + fragments.back()->setListener(std::move(packet->getListener())); //Move the listener to the last :) + } else { + fragments.push_back(packet); + } - result.reserve(fragments.size()); + result.reserve(fragments.size()); - /* apply packet ids */ - for(const auto& fragment : fragments) { - if(!fragment->memory_state.id_branded) - fragment->applyPacketId(this->packet_id_manager); - } - work_lock.unlock(); /* the rest could be unordered */ + /* apply packet ids */ + for(const auto& fragment : fragments) { + if(!fragment->memory_state.id_branded) + fragment->applyPacketId(this->packet_id_manager); + } + work_lock.unlock(); /* the rest could be unordered */ - auto statistics = this->client ? this->client->connectionStatistics : nullptr; - for(const auto& fragment : fragments) { - if(!this->crypt_handler.progressPacketOut(fragment.get(), error, false)){ - logError(this->client->getServerId(), "{} Failed to encrypt packet. Error: {}", CLIENT_STR_LOG_PREFIX_(this->client), error); - return false; - } + auto statistics = this->client ? this->client->connectionStatistics : nullptr; + for(const auto& fragment : fragments) { + if(!this->crypt_handler.progressPacketOut(fragment.get(), error, false)){ + logError(this->client->getServerId(), "{} Failed to encrypt packet. Error: {}", CLIENT_STR_LOG_PREFIX_(this->client), error); + return false; + } #ifndef CONNECTION_NO_STATISTICS - if(statistics) - statistics->logOutgoingPacket(*fragment); + if(statistics) + statistics->logOutgoingPacket(*fragment); #endif - this->acknowledge_handler.process_packet(*fragment); - result.push_back(fragment->buffer()); - } + this->acknowledge_handler.process_packet(*fragment); + result.push_back(fragment->buffer()); + } - return true; + return true; } bool VoiceClientConnection::preprocess_write_packets() { - std::shared_ptr packet{nullptr}; - vector buffers{}; - bool flag_more{false}; + std::shared_ptr packet{nullptr}; + vector buffers{}; + bool flag_more{false}; - prepare_process_count++; /* we're not preparing a packet */ - for(auto& category : this->write_preprocess_queues) { - if(!category.has_work) continue; - else if(packet) { - flag_more = true; - break; - } + prepare_process_count++; /* we're not preparing a packet */ + for(auto& category : this->write_preprocess_queues) { + if(!category.has_work) continue; + else if(packet) { + flag_more = true; + break; + } - unique_lock work_lock{category.work_lock, try_to_lock}; - if(!work_lock) continue; /* This particular category will already be processed */ + unique_lock work_lock{category.work_lock, try_to_lock}; + if(!work_lock) continue; /* This particular category will already be processed */ - { - lock_guard buffer_lock{category.queue_lock}; - if(category.queue.empty()) { - category.has_work = false; - continue; - } + { + lock_guard buffer_lock{category.queue_lock}; + if(category.queue.empty()) { + category.has_work = false; + continue; + } - packet = std::move(category.queue.front()); - category.queue.pop_front(); - category.has_work = !category.queue.empty(); - flag_more = category.has_work; - } + packet = std::move(category.queue.front()); + category.queue.pop_front(); + category.has_work = !category.queue.empty(); + flag_more = category.has_work; + } - if(!this->prepare_packet_for_write(buffers, packet, work_lock)) { - logError(this->client->getServerId(), "{} Dropping packet!", CLIENT_STR_LOG_PREFIX_(this->client)); - if(flag_more) - break; - else - continue; /* find out if we have more */ - } + if(!this->prepare_packet_for_write(buffers, packet, work_lock)) { + logError(this->client->getServerId(), "{} Dropping packet!", CLIENT_STR_LOG_PREFIX_(this->client)); + if(flag_more) + break; + else + continue; /* find out if we have more */ + } - if(flag_more) - break; - else - continue; /* find out if we have more */ - } + if(flag_more) + break; + else + continue; /* find out if we have more */ + } - /* enqueue buffers for write */ - if(!buffers.empty()) { - lock_guard write_queue_lock(this->write_queue_lock); - this->write_queue.insert(this->write_queue.end(), buffers.begin(), buffers.end()); - } - this->prepare_process_count--; /* we're now done preparing */ + /* enqueue buffers for write */ + if(!buffers.empty()) { + lock_guard write_queue_lock(this->write_queue_lock); + this->write_queue.insert(this->write_queue.end(), buffers.begin(), buffers.end()); + } + this->prepare_process_count--; /* we're now done preparing */ - return flag_more; + return flag_more; } int VoiceClientConnection::pop_write_buffer(pipes::buffer& target) { - if(this->client->state == DISCONNECTED) - return 2; + if(this->client->state == DISCONNECTED) + return 2; - lock_guard write_queue_lock(this->write_queue_lock); - size_t size = this->write_queue.size(); - if(size == 0) - return 2; + lock_guard write_queue_lock(this->write_queue_lock); + size_t size = this->write_queue.size(); + if(size == 0) + return 2; - target = std::move(this->write_queue.front()); - this->write_queue.pop_front(); + target = std::move(this->write_queue.front()); + this->write_queue.pop_front(); #ifdef FUZZING_TESTING_OUTGOING - #ifdef FIZZING_TESTING_DISABLE_HANDSHAKE - if (this->client->state == ConnectionState::CONNECTED) { - #endif - if ((rand() % FUZZING_TESTING_DROP_MAX) < FUZZING_TESTING_DROP) { - debugMessage(this->client->getServerId(), "{}[FUZZING] Dropping outgoing packet", CLIENT_STR_LOG_PREFIX_(this->client)); - return 0; - } - #ifdef FIZZING_TESTING_DISABLE_HANDSHAKE - } - #endif + #ifdef FIZZING_TESTING_DISABLE_HANDSHAKE + if (this->client->state == ConnectionState::CONNECTED) { + #endif + if ((rand() % FUZZING_TESTING_DROP_MAX) < FUZZING_TESTING_DROP) { + debugMessage(this->client->getServerId(), "{}[FUZZING] Dropping outgoing packet", CLIENT_STR_LOG_PREFIX_(this->client)); + return 0; + } + #ifdef FIZZING_TESTING_DISABLE_HANDSHAKE + } + #endif #endif - return size > 1; + return size > 1; } bool VoiceClientConnection::wait_empty_write_and_prepare_queue(chrono::time_point until) { - while(true) { - for(auto& queue : this->write_preprocess_queues) { - { - lock_guard lock{queue.queue_lock}; - if(!queue.queue.empty()) - goto _wait; - } + while(true) { + for(auto& queue : this->write_preprocess_queues) { + { + lock_guard lock{queue.queue_lock}; + if(!queue.queue.empty()) + goto _wait; + } - { - unique_lock lock{queue.work_lock, try_to_lock}; - if(!lock.owns_lock()) - goto _wait; - } - } - { - lock_guard buffer_lock{this->write_queue_lock}; - if(!this->write_queue.empty()) - goto _wait; - if(this->prepare_process_count != 0) - goto _wait; - } - break; + { + unique_lock lock{queue.work_lock, try_to_lock}; + if(!lock.owns_lock()) + goto _wait; + } + } + { + lock_guard buffer_lock{this->write_queue_lock}; + if(!this->write_queue.empty()) + goto _wait; + if(this->prepare_process_count != 0) + goto _wait; + } + break; - _wait: - if(until.time_since_epoch().count() != 0 && system_clock::now() > until) - return false; + _wait: + if(until.time_since_epoch().count() != 0 && system_clock::now() > until) + return false; - }; - threads::self::sleep_for(milliseconds(5)); - return true; + }; + threads::self::sleep_for(milliseconds(5)); + return true; } void VoiceClientConnection::reset() { - for(auto& queue : this->write_preprocess_queues) { - { - lock_guard lock{queue.queue_lock}; - queue.queue.clear(); - } - } + for(auto& queue : this->write_preprocess_queues) { + { + lock_guard lock{queue.queue_lock}; + queue.queue.clear(); + } + } - this->acknowledge_handler.reset(); - this->crypt_handler.reset(); - this->packet_id_manager.reset(); + this->acknowledge_handler.reset(); + this->crypt_handler.reset(); + this->packet_id_manager.reset(); - { - lock_guard buffer_lock(this->packet_buffer_lock); - for(auto& buffer : this->_packet_buffers) - buffer.reset(); - } + { + lock_guard buffer_lock(this->packet_buffer_lock); + for(auto& buffer : this->_packet_buffers) + buffer.reset(); + } } diff --git a/server/src/client/voice/VoiceClientConnection.h b/server/src/client/voice/VoiceClientConnection.h index e719ca7..3d4cf50 100644 --- a/server/src/client/voice/VoiceClientConnection.h +++ b/server/src/client/voice/VoiceClientConnection.h @@ -26,20 +26,20 @@ namespace ts { namespace server { class VoiceClient; class VoiceServer; - class POWHandler; + class POWHandler; } namespace connection { class VoiceClientConnection { - friend class AcknowledgeManager; + friend class AcknowledgeManager; friend class server::VoiceServer; friend class server::VoiceClient; friend class server::POWHandler; public: - typedef protocol::PacketRingBuffer> packet_buffer_t; - typedef std::array packet_buffers_t; + typedef protocol::PacketRingBuffer> packet_buffer_t; + typedef std::array packet_buffers_t; - explicit VoiceClientConnection(server::VoiceClient*); + explicit VoiceClientConnection(server::VoiceClient*); virtual ~VoiceClientConnection(); void sendPacket(const std::shared_ptr& original_packet, bool copy = false, bool prepare_directly = false); @@ -51,10 +51,10 @@ namespace ts { #ifdef VC_USE_READ_QUEUE bool handleNextDatagram(); #endif - /* - * Split packets waiting in write_process_queue and moves the final buffers to writeQueue. - * @returns true when there are more packets to prepare - */ + /* + * Split packets waiting in write_process_queue and moves the final buffers to writeQueue. + * @returns true when there are more packets to prepare + */ bool preprocess_write_packets(); /* return 2 => Nothing | 1 => More and buffer is set | 0 => Buffer is set, nothing more */ int pop_write_buffer(pipes::buffer& /* buffer */); @@ -62,13 +62,13 @@ namespace ts { bool wait_empty_write_and_prepare_queue(std::chrono::time_point until = std::chrono::time_point()); protocol::PacketIdManager& getPacketIdManager() { return this->packet_id_manager; } - packet_buffers_t& packet_buffers() { return this->_packet_buffers; } + packet_buffers_t& packet_buffers() { return this->_packet_buffers; } - void reset(); + void reset(); //buffer::SortedBufferQueue** getReadQueue() { return this->readTypedQueue; } protected: void handleDatagramReceived(const pipes::buffer_view&); - bool verify_encryption(const pipes::buffer_view& /* full packet */); + bool verify_encryption(const pipes::buffer_view& /* full packet */); void triggerWrite(); private: @@ -81,71 +81,71 @@ namespace ts { //Handle stuff void execute_handle_packet(const std::chrono::system_clock::time_point& /* scheduled */); - std::unique_ptr next_reassembled_packet(std::unique_lock& /* packet channel execute lock */, bool& /* have more */); + std::unique_ptr next_reassembled_packet(std::unique_lock& /* packet channel execute lock */, bool& /* have more */); - /* ---------- Write declarations ---------- */ + /* ---------- Write declarations ---------- */ spin_lock write_queue_lock; /* queue access isn't for long in general */ - std::deque write_queue; + std::deque write_queue; - struct WritePreprocessCategory { - enum value { - PING_PONG = 0, //Ping/Pongs - ACK = 2, - VOICE_WHISPER = 1, //Voice/Whisper - COMMAND = 3, - INIT = 4, + struct WritePreprocessCategory { + enum value { + PING_PONG = 0, //Ping/Pongs + ACK = 2, + VOICE_WHISPER = 1, //Voice/Whisper + COMMAND = 3, + INIT = 4, - MAX = INIT - }; + MAX = INIT + }; - inline static value from_type(protocol::PacketType type) { - switch(type) { - case protocol::PING: - case protocol::PONG: - return value::PING_PONG; + inline static value from_type(protocol::PacketType type) { + switch(type) { + case protocol::PING: + case protocol::PONG: + return value::PING_PONG; - case protocol::VOICE: - case protocol::VOICE_WHISPER: - return value::VOICE_WHISPER; + case protocol::VOICE: + case protocol::VOICE_WHISPER: + return value::VOICE_WHISPER; - case protocol::ACK: - case protocol::ACK_LOW: - return value::ACK; + case protocol::ACK: + case protocol::ACK_LOW: + return value::ACK; - case protocol::COMMAND: - case protocol::COMMAND_LOW: - return value::COMMAND; + case protocol::COMMAND: + case protocol::COMMAND_LOW: + return value::COMMAND; - default: - return value::INIT; - } - } - }; + default: + return value::INIT; + } + } + }; - struct WritePreprocessQueue { - int _zero1{0}; - bool has_work{false}; - std::mutex work_lock{}; + struct WritePreprocessQueue { + int _zero1{0}; + bool has_work{false}; + std::mutex work_lock{}; - spin_lock queue_lock{}; - std::deque> queue{}; + spin_lock queue_lock{}; + std::deque> queue{}; - int _zero{0}; - }; + int _zero{0}; + }; std::array write_preprocess_queues{}; /* ---------- Processing ---------- */ /* automatically locked because packets of the same kind should be lock their "work_lock" from their WritePreprocessQueue object */ - protocol::PacketIdManager packet_id_manager; + protocol::PacketIdManager packet_id_manager; /* this function is thread save :) */ std::atomic prepare_process_count{0}; /* current thread count preparing a packet */ bool prepare_packet_for_write(std::vector &/* buffers which need to be transferred */, const std::shared_ptr &/* the packet */, std::unique_lock& /* work lock */); - std::recursive_mutex packet_buffer_lock; - packet_buffers_t _packet_buffers; - uint8_t _packet_buffers_index = 0; + std::recursive_mutex packet_buffer_lock; + packet_buffers_t _packet_buffers; + uint8_t _packet_buffers_index = 0; }; } diff --git a/server/src/client/voice/VoiceClientHandschake.cpp b/server/src/client/voice/VoiceClientHandschake.cpp index 18db188..07f0562 100644 --- a/server/src/client/voice/VoiceClientHandschake.cpp +++ b/server/src/client/voice/VoiceClientHandschake.cpp @@ -18,163 +18,163 @@ using namespace ts::protocol; using namespace ts::connection; inline void generate_random(uint8_t *destination, size_t length) { - while(length-- > 0) - *(destination++) = (uint8_t) rand(); + while(length-- > 0) + *(destination++) = (uint8_t) rand(); } ts::CommandResult VoiceClient::handleCommandClientInitIv(Command& command) { - this->last_packet_handshake = system_clock::now(); + this->last_packet_handshake = system_clock::now(); - if(this->state == ConnectionState::CONNECTED) { /* we've a reconnect */ - if(system_clock::now() - this->lastPingResponse < seconds(5)) { - logMessage(this->getServerId(), "{} Client initialized session reconnect, but last ping response is not older then 5 seconds ({}). Ignoring attempt", - CLIENT_STR_LOG_PREFIX, - duration_cast(system_clock::now() - this->lastPingResponse).count() - ); - return CommandResult::Success; - } else if(!config::voice::allow_session_reinitialize) { - logMessage(this->getServerId(), "{} Client initialized session reconnect and last ping response is older then 5 seconds ({}). Dropping attempt because its not allowed due to config settings", - CLIENT_STR_LOG_PREFIX, - duration_cast(system_clock::now() - this->lastPingResponse).count() - ); - return CommandResult::Success; - } - logMessage(this->getServerId(), "{} Client initialized reconnect and last ping response is older then 5 seconds ({}). Allowing attempt", - CLIENT_STR_LOG_PREFIX, - duration_cast(system_clock::now() - this->lastPingResponse).count() - ); - { - unique_lock server_channel_lock(this->server->channel_tree_lock); /* we cant get moved if this is locked! */ - if(this->currentChannel) { - this->server->client_move(this->ref(), nullptr, nullptr, config::messages::timeout::connection_reinitialized, ViewReasonId::VREASON_TIMEOUT, false, server_channel_lock); - } - } + if(this->state == ConnectionState::CONNECTED) { /* we've a reconnect */ + if(system_clock::now() - this->lastPingResponse < seconds(5)) { + logMessage(this->getServerId(), "{} Client initialized session reconnect, but last ping response is not older then 5 seconds ({}). Ignoring attempt", + CLIENT_STR_LOG_PREFIX, + duration_cast(system_clock::now() - this->lastPingResponse).count() + ); + return CommandResult::Success; + } else if(!config::voice::allow_session_reinitialize) { + logMessage(this->getServerId(), "{} Client initialized session reconnect and last ping response is older then 5 seconds ({}). Dropping attempt because its not allowed due to config settings", + CLIENT_STR_LOG_PREFIX, + duration_cast(system_clock::now() - this->lastPingResponse).count() + ); + return CommandResult::Success; + } + logMessage(this->getServerId(), "{} Client initialized reconnect and last ping response is older then 5 seconds ({}). Allowing attempt", + CLIENT_STR_LOG_PREFIX, + duration_cast(system_clock::now() - this->lastPingResponse).count() + ); + { + unique_lock server_channel_lock(this->server->channel_tree_lock); /* we cant get moved if this is locked! */ + if(this->currentChannel) { + this->server->client_move(this->ref(), nullptr, nullptr, config::messages::timeout::connection_reinitialized, ViewReasonId::VREASON_TIMEOUT, false, server_channel_lock); + } + } - this->finalDisconnect(); - } else if(this->state == ConnectionState::DISCONNECTING) { - this->closeConnection(); /* executing final disconnect right now! */ - } - this->connection->reset(); - { - auto& read_queue = this->connection->packet_buffers()[protocol::COMMAND]; - unique_lock buffer_lock(read_queue.buffer_lock); - read_queue.set_full_index_to(1); /* the first packet (0) is already the clientinitiv packet */ - } - this->state = ConnectionState::INIT_HIGH; + this->finalDisconnect(); + } else if(this->state == ConnectionState::DISCONNECTING) { + this->closeConnection(); /* executing final disconnect right now! */ + } + this->connection->reset(); + { + auto& read_queue = this->connection->packet_buffers()[protocol::COMMAND]; + unique_lock buffer_lock(read_queue.buffer_lock); + read_queue.set_full_index_to(1); /* the first packet (0) is already the clientinitiv packet */ + } + this->state = ConnectionState::INIT_HIGH; - bool use_teaspeak = command.hasParm("teaspeak"); - if(use_teaspeak) { - debugMessage(this->getServerId(), "{} Client using TeaSpeak with auth type {}", CLIENT_STR_LOG_PREFIX, command["verify_type"].string()); - this->properties()[property::CLIENT_TYPE_EXACT] = ClientType::CLIENT_TEASPEAK; - } + bool use_teaspeak = command.hasParm("teaspeak"); + if(use_teaspeak) { + debugMessage(this->getServerId(), "{} Client using TeaSpeak with auth type {}", CLIENT_STR_LOG_PREFIX, command["verify_type"].string()); + this->properties()[property::CLIENT_TYPE_EXACT] = ClientType::CLIENT_TEASPEAK; + } - /* normal TeamSpeak handling */ - string clientAlpha = base64::decode(command["alpha"]); //random - if(clientAlpha.length() != 10) return {findError("parameter_invalid"), "Invalid alpha"}; + /* normal TeamSpeak handling */ + string clientAlpha = base64::decode(command["alpha"]); //random + if(clientAlpha.length() != 10) return {findError("parameter_invalid"), "Invalid alpha"}; - string clientOmega = base64::decode(command["omega"]); //The identity public key - string ip = command["ip"]; - bool ot = command[0].has("ot") ? command["ot"] : false; + string clientOmega = base64::decode(command["omega"]); //The identity public key + string ip = command["ip"]; + bool ot = command[0].has("ot") ? command["ot"] : false; - this->crypto.remote_key = std::shared_ptr(new ecc_key{}, [](ecc_key* key){ - if(!key) return; - ecc_free(key); - delete key; - }); + this->crypto.remote_key = std::shared_ptr(new ecc_key{}, [](ecc_key* key){ + if(!key) return; + ecc_free(key); + delete key; + }); - auto state = ecc_import((const unsigned char *) clientOmega.data(), clientOmega.length(), this->crypto.remote_key.get()); - if(state != CRYPT_OK) { - this->crypto.remote_key.reset(); - return {ts::findError("client_could_not_validate_identity"), error_to_string(state)}; - } - this->properties()[property::CLIENT_UNIQUE_IDENTIFIER] = base64::encode(digest::sha1(command["omega"].string())); + auto state = ecc_import((const unsigned char *) clientOmega.data(), clientOmega.length(), this->crypto.remote_key.get()); + if(state != CRYPT_OK) { + this->crypto.remote_key.reset(); + return {ts::findError("client_could_not_validate_identity"), error_to_string(state)}; + } + this->properties()[property::CLIENT_UNIQUE_IDENTIFIER] = base64::encode(digest::sha1(command["omega"].string())); - this->crypto.alpha = clientAlpha; - this->crypto.new_protocol = !use_teaspeak && ot && config::experimental_31 && (this->crypto.client_time >= 173265950 || this->crypto.client_time == 5680278000UL); - { - size_t beta_length = this->crypto.new_protocol ? 54 : 10; - char beta[beta_length]; - generate_random((uint8_t *) beta, beta_length); - this->crypto.beta = string(beta, beta_length); - } + this->crypto.alpha = clientAlpha; + this->crypto.new_protocol = !use_teaspeak && ot && config::experimental_31 && (this->crypto.client_time >= 173265950 || this->crypto.client_time == 5680278000UL); + { + size_t beta_length = this->crypto.new_protocol ? 54 : 10; + char beta[beta_length]; + generate_random((uint8_t *) beta, beta_length); + this->crypto.beta = string(beta, beta_length); + } - if(this->crypto.new_protocol) { - //Pre setup - //Generate chain - debugMessage(this->getServerId(), "{} Got client 3.1 protocol with build timestamp {}", CLIENT_STR_LOG_PREFIX, this->crypto.client_time); - this->crypto.chain_data = serverInstance->getTeamSpeakLicense()->license(); - this->crypto.chain_data->chain->addEphemeralEntry(); - auto rawLicense = this->crypto.chain_data->chain->exportChain(); + if(this->crypto.new_protocol) { + //Pre setup + //Generate chain + debugMessage(this->getServerId(), "{} Got client 3.1 protocol with build timestamp {}", CLIENT_STR_LOG_PREFIX, this->crypto.client_time); + this->crypto.chain_data = serverInstance->getTeamSpeakLicense()->license(); + this->crypto.chain_data->chain->addEphemeralEntry(); + auto rawLicense = this->crypto.chain_data->chain->exportChain(); - //Sign license - auto serverOmega = this->getServer()->publicServerKey(); - auto rawServerOmega = base64::decode(serverOmega); + //Sign license + auto serverOmega = this->getServer()->publicServerKey(); + auto rawServerOmega = base64::decode(serverOmega); - auto licenseHash = digest::sha256(rawLicense); - size_t signBufferLength = 128; - char signBuffer[signBufferLength]; - prng_state prngState{}; - memset(&prngState, 0, sizeof(prngState)); - if(ecc_sign_hash((u_char*) licenseHash.data(), licenseHash.length(), (u_char*) signBuffer, &signBufferLength, &prngState, find_prng("sprng"), this->getServer()->serverKey()) != CRYPT_OK) return {findError("vs_critical"), "Could not sign license!"}; - auto proof = base64::encode(signBuffer, signBufferLength); + auto licenseHash = digest::sha256(rawLicense); + size_t signBufferLength = 128; + char signBuffer[signBufferLength]; + prng_state prngState{}; + memset(&prngState, 0, sizeof(prngState)); + if(ecc_sign_hash((u_char*) licenseHash.data(), licenseHash.length(), (u_char*) signBuffer, &signBufferLength, &prngState, find_prng("sprng"), this->getServer()->serverKey()) != CRYPT_OK) return {findError("vs_critical"), "Could not sign license!"}; + auto proof = base64::encode(signBuffer, signBufferLength); - Command initivexpand2("initivexpand2"); - initivexpand2["time"] = duration_cast(system_clock::now().time_since_epoch()).count(); - initivexpand2["l"] = base64::encode(rawLicense); - initivexpand2["beta"] = base64::encode(this->crypto.beta); - initivexpand2["omega"] = serverOmega; - initivexpand2["proof"] = proof; - initivexpand2["tvd"] = ""; - initivexpand2["root"] = base64::encode((char*) this->crypto.chain_data->public_key, 32); - initivexpand2["ot"] = 1; + Command initivexpand2("initivexpand2"); + initivexpand2["time"] = duration_cast(system_clock::now().time_since_epoch()).count(); + initivexpand2["l"] = base64::encode(rawLicense); + initivexpand2["beta"] = base64::encode(this->crypto.beta); + initivexpand2["omega"] = serverOmega; + initivexpand2["proof"] = proof; + initivexpand2["tvd"] = ""; + initivexpand2["root"] = base64::encode((char*) this->crypto.chain_data->public_key, 32); + initivexpand2["ot"] = 1; - this->sendCommand(initivexpand2); - this->handshake.state = HandshakeState::SUCCEEDED; /* we're doing the verify via TeamSpeak */ - } else { - debugMessage(this->getServerId(), "{} Got non client 3.1 protocol with build timestamp {}", CLIENT_STR_LOG_PREFIX, this->crypto.client_time); + this->sendCommand(initivexpand2); + this->handshake.state = HandshakeState::SUCCEEDED; /* we're doing the verify via TeamSpeak */ + } else { + debugMessage(this->getServerId(), "{} Got non client 3.1 protocol with build timestamp {}", CLIENT_STR_LOG_PREFIX, this->crypto.client_time); - auto serverOmega = this->getServer()->publicServerKey(); - if(serverOmega.empty()) return {findError("vs_critical"), "Could not export public key!"}; + auto serverOmega = this->getServer()->publicServerKey(); + if(serverOmega.empty()) return {findError("vs_critical"), "Could not export public key!"}; - { - Command initivexpand("initivexpand"); - initivexpand["alpha"] = base64::encode(clientAlpha); - initivexpand["beta"] = base64::encode(this->crypto.beta); - initivexpand["omega"] = serverOmega; - if(use_teaspeak) { - initivexpand["teaspeak"] = 1; - this->handshake.state = HandshakeState::BEGIN; /* we need to start the handshake */ - } else { - this->handshake.state = HandshakeState::SUCCEEDED; /* we're doing the verify via TeamSpeak */ - } - this->sendCommand0(initivexpand, false, true); //If we setup the encryption now - } + { + Command initivexpand("initivexpand"); + initivexpand["alpha"] = base64::encode(clientAlpha); + initivexpand["beta"] = base64::encode(this->crypto.beta); + initivexpand["omega"] = serverOmega; + if(use_teaspeak) { + initivexpand["teaspeak"] = 1; + this->handshake.state = HandshakeState::BEGIN; /* we need to start the handshake */ + } else { + this->handshake.state = HandshakeState::SUCCEEDED; /* we're doing the verify via TeamSpeak */ + } + this->sendCommand0(initivexpand, false, true); //If we setup the encryption now + } - { - this->connection->getCryptHandler()->block(); //Block until the key is setuped - string error; - if(!this->connection->getCryptHandler()->setupSharedSecret(this->crypto.alpha, this->crypto.beta, this->crypto.remote_key.get(), this->server->serverKey(), error)){ - this->connection->getCryptHandler()->unblock(); - debugMessage(this->server->getServerId(), "Could not setup shared secret! (" + error + ")"); - return {findError("vs_critical"), "Could not setup shared secret"}; - } - this->connection->getCryptHandler()->unblock(); - this->crypto.protocol_encrypted = true; - } - } - return CommandResult::Success; + { + this->connection->getCryptHandler()->block(); //Block until the key is setuped + string error; + if(!this->connection->getCryptHandler()->setupSharedSecret(this->crypto.alpha, this->crypto.beta, this->crypto.remote_key.get(), this->server->serverKey(), error)){ + this->connection->getCryptHandler()->unblock(); + debugMessage(this->server->getServerId(), "Could not setup shared secret! (" + error + ")"); + return {findError("vs_critical"), "Could not setup shared secret"}; + } + this->connection->getCryptHandler()->unblock(); + this->crypto.protocol_encrypted = true; + } + } + return CommandResult::Success; } ts::CommandResult VoiceClient::handleCommandClientEk(const std::unique_ptr& packet, Command& cmd) { - this->last_packet_handshake = system_clock::now(); - debugMessage(this->getServerId(), "{} Got client ek!", CLIENT_STR_LOG_PREFIX); + this->last_packet_handshake = system_clock::now(); + debugMessage(this->getServerId(), "{} Got client ek!", CLIENT_STR_LOG_PREFIX); - auto client_key = base64::decode(cmd["ek"]); - auto x = this->crypto.chain_data->chain->generatePrivateKey(this->crypto.chain_data->root_key, this->crypto.chain_data->root_index); + auto client_key = base64::decode(cmd["ek"]); + auto x = this->crypto.chain_data->chain->generatePrivateKey(this->crypto.chain_data->root_key, this->crypto.chain_data->root_index); - this->connection->getCryptHandler()->setupSharedSecretNew(this->crypto.alpha, this->crypto.beta, (char*) x.data(), client_key.data()); - this->connection->acknowledge_handler.reset(); - this->crypto.protocol_encrypted = true; - this->sendAcknowledge(packet->packetId()); //Send the encrypted acknowledge - return CommandResult::Success; + this->connection->getCryptHandler()->setupSharedSecretNew(this->crypto.alpha, this->crypto.beta, (char*) x.data(), client_key.data()); + this->connection->acknowledge_handler.reset(); + this->crypto.protocol_encrypted = true; + this->sendAcknowledge(packet->packetId()); //Send the encrypted acknowledge + return CommandResult::Success; } \ No newline at end of file diff --git a/server/src/client/voice/VoiceClientPacketHandler.cpp b/server/src/client/voice/VoiceClientPacketHandler.cpp index e75be64..d932567 100644 --- a/server/src/client/voice/VoiceClientPacketHandler.cpp +++ b/server/src/client/voice/VoiceClientPacketHandler.cpp @@ -47,26 +47,26 @@ void VoiceClient::handlePacketPing(const std::unique_ptr uint16_t id = be2le16((char*) packet->data().data_ptr()); if (this->lastPingId == id) { #ifdef PKT_LOG_PING - logMessage(this->getServerId(), "{}[Ping] Got a valid pong for ping {}. Required time: {}", CLIENT_STR_LOG_PREFIX, id, duration_cast(system_clock::now() - this->lastPingRequest).count() / 1000.f); + logMessage(this->getServerId(), "{}[Ping] Got a valid pong for ping {}. Required time: {}", CLIENT_STR_LOG_PREFIX, id, duration_cast(system_clock::now() - this->lastPingRequest).count() / 1000.f); #endif this->lastPingResponse = system_clock::now(); this->ping = duration_cast(this->lastPingResponse - this->lastPingRequest); } #ifdef PKT_LOG_PING else { - logMessage(this->getServerId(), "{}[Ping] Got invalid pong. (Responded pong id {} but expected {})", CLIENT_STR_LOG_PREFIX, packet->packetId(), this->lastPingId); + logMessage(this->getServerId(), "{}[Ping] Got invalid pong. (Responded pong id {} but expected {})", CLIENT_STR_LOG_PREFIX, packet->packetId(), this->lastPingId); } #endif return; } #ifdef PKT_LOG_PING - logMessage(this->getServerId(), "{}[Ping] Sending pong for client requested ping {}", CLIENT_STR_LOG_PREFIX, packet->packetId()); + logMessage(this->getServerId(), "{}[Ping] Sending pong for client requested ping {}", CLIENT_STR_LOG_PREFIX, packet->packetId()); #endif char buffer[2]; le2be16(packet->packetId(), buffer); auto pkt = make_shared(PacketTypeInfo::Pong, pipes::buffer_view{buffer, 2}); - pkt->enable_flag(PacketFlag::Unencrypted); + pkt->enable_flag(PacketFlag::Unencrypted); this->connection->sendPacket(pkt); } @@ -74,7 +74,7 @@ void VoiceClient::handlePacketVoice(const std::unique_ptrtype() == PacketTypeInfo::Voice) { SpeakingClient::handlePacketVoice(packet->data(), packet->has_flag(PacketFlag::Compressed), packet->has_flag(PacketFlag::Fragmented)); } else if(packet->type() == PacketTypeInfo::VoiceWhisper) { - SpeakingClient::handlePacketVoiceWhisper(packet->data(), packet->has_flag(PacketFlag::NewProtocol)); + SpeakingClient::handlePacketVoiceWhisper(packet->data(), packet->has_flag(PacketFlag::NewProtocol)); } } diff --git a/server/src/client/voice/VoiceClientView.cpp b/server/src/client/voice/VoiceClientView.cpp index be55a8b..ad4d1f1 100644 --- a/server/src/client/voice/VoiceClientView.cpp +++ b/server/src/client/voice/VoiceClientView.cpp @@ -15,12 +15,12 @@ void VoiceClient::sendPingRequest() { this->lastPingRequest = std::chrono::system_clock::now(); auto packet = make_shared(PacketTypeInfo::Ping, pipes::buffer_view{}); - packet->enable_flag(PacketFlag::Unencrypted); + packet->enable_flag(PacketFlag::Unencrypted); this->connection->sendPacket(packet, false, true); /* prepare directly so the packet gets a packet id */ this->lastPingId = packet->packetId(); #ifdef PKT_LOG_PING - logMessage(this->getServerId(), "{}[Ping] Sending a ping request with it {}", CLIENT_STR_LOG_PREFIX, this->lastPingId); + logMessage(this->getServerId(), "{}[Ping] Sending a ping request with it {}", CLIENT_STR_LOG_PREFIX, this->lastPingId); #endif } \ No newline at end of file diff --git a/server/src/client/web/VoiceBridge.cpp b/server/src/client/web/VoiceBridge.cpp index 05bc01d..44bf469 100644 --- a/server/src/client/web/VoiceBridge.cpp +++ b/server/src/client/web/VoiceBridge.cpp @@ -12,225 +12,225 @@ using namespace ts::server; using namespace ts::web; void log(pipes::Logger::LogLevel level, const std::string& name, const std::string& message, ...) { - auto max_length = 1024 * 8; - char buffer[max_length]; + auto max_length = 1024 * 8; + char buffer[max_length]; - va_list args; - va_start(args, message); - max_length = vsnprintf(buffer, max_length, message.c_str(), args); - va_end(args); + va_list args; + va_start(args, message); + max_length = vsnprintf(buffer, max_length, message.c_str(), args); + va_end(args); - debugMessage(LOG_GENERAL, "[WebRTC][{}][{}] {}", level, name, string(buffer)); + debugMessage(LOG_GENERAL, "[WebRTC][{}][{}] {}", level, name, string(buffer)); } VoiceBridge::VoiceBridge(const shared_ptr& owner) : _owner(owner) { - auto config = make_shared(); - config->nice_config = make_shared(); + auto config = make_shared(); + config->nice_config = make_shared(); - config->nice_config->ice_port_range = {config::web::webrtc_port_min, config::web::webrtc_port_max}; - for(const auto& entry : config::web::ice_servers) { - auto dp = entry.find(':'); - if(dp == string::npos) continue; - auto host = entry.substr(0, dp); - auto port = entry.substr(dp + 1); - if(port.find_last_not_of("0123456789") != string::npos) continue; + config->nice_config->ice_port_range = {config::web::webrtc_port_min, config::web::webrtc_port_max}; + for(const auto& entry : config::web::ice_servers) { + auto dp = entry.find(':'); + if(dp == string::npos) continue; + auto host = entry.substr(0, dp); + auto port = entry.substr(dp + 1); + if(port.find_last_not_of("0123456789") != string::npos) continue; - if(host == "stun.l.google.com" && port == "9302") - port = "19302"; /* fix for the invalid config value until 1.3.14beta1 :) */ + if(host == "stun.l.google.com" && port == "9302") + port = "19302"; /* fix for the invalid config value until 1.3.14beta1 :) */ - try { - config->nice_config->ice_servers.push_back({host,(uint16_t) stoi(port)}); - } catch(std::exception& ex) {} - } - config->nice_config->ice_servers.push_back({"stun.l.google.com", 19302}); + try { + config->nice_config->ice_servers.push_back({host,(uint16_t) stoi(port)}); + } catch(std::exception& ex) {} + } + config->nice_config->ice_servers.push_back({"stun.l.google.com", 19302}); - config->nice_config->allow_ice_udp = true; - config->nice_config->allow_ice_tcp = false; - config->nice_config->use_upnp = config::web::enable_upnp; + config->nice_config->allow_ice_udp = true; + config->nice_config->allow_ice_tcp = false; + config->nice_config->use_upnp = config::web::enable_upnp; - //FIXME Use the internal thread or a shared worker - /* Not creating the thread here because DataPipes has a better impl with a join - config->nice_config->main_loop = std::shared_ptr(g_main_loop_new(nullptr, false), g_main_loop_unref); - std::thread(g_main_loop_run, config->nice_config->main_loop.get()).detach(); - */ + //FIXME Use the internal thread or a shared worker + /* Not creating the thread here because DataPipes has a better impl with a join + config->nice_config->main_loop = std::shared_ptr(g_main_loop_new(nullptr, false), g_main_loop_unref); + std::thread(g_main_loop_run, config->nice_config->main_loop.get()).detach(); + */ - config->logger = make_shared(); - config->logger->callback_log = log; - //config->sctp.local_port = 5202; //Fire Fox don't support a different port :D + config->logger = make_shared(); + config->logger->callback_log = log; + //config->sctp.local_port = 5202; //Fire Fox don't support a different port :D - this->connection = make_unique(config); + this->connection = make_unique(config); } VoiceBridge::~VoiceBridge() {} int VoiceBridge::server_id() { - auto locked = this->_owner.lock(); - return locked ? locked->getServerId() : 0; + auto locked = this->_owner.lock(); + return locked ? locked->getServerId() : 0; } std::shared_ptr VoiceBridge::owner() { - return this->_owner.lock(); + return this->_owner.lock(); } bool VoiceBridge::initialize(std::string &error) { - if(!this->connection->initialize(error)) return false; + if(!this->connection->initialize(error)) return false; - this->connection->callback_ice_candidate = [&](const rtc::IceCandidate& candidate, bool last_candidate) { - auto function_callback = this->callback_ice_candidate; + this->connection->callback_ice_candidate = [&](const rtc::IceCandidate& candidate, bool last_candidate) { + auto function_callback = this->callback_ice_candidate; - if(function_callback) - function_callback(candidate); - if(last_candidate) { - auto function_callback_end = this->callback_ice_candidate_finished; - this->negotiation_possible = true; - if(function_callback_end) - function_callback_end(); - } - }; - this->connection->callback_new_stream = [&](const std::shared_ptr &channel) { this->handle_media_stream(channel); }; //bind(&VoiceBridge::handle_media_stream, this, placeholders::_1); => crash - this->connection->callback_setup_fail = [&](rtc::PeerConnection::ConnectionComponent comp, const std::string& reason) { - debugMessage(this->server_id(), "{} WebRTC setup failed! Component {} ({})", CLIENT_STR_LOG_PREFIX_(this->owner()), comp, reason); - if(this->callback_failed) - this->callback_failed(); - }; - return true; + if(function_callback) + function_callback(candidate); + if(last_candidate) { + auto function_callback_end = this->callback_ice_candidate_finished; + this->negotiation_possible = true; + if(function_callback_end) + function_callback_end(); + } + }; + this->connection->callback_new_stream = [&](const std::shared_ptr &channel) { this->handle_media_stream(channel); }; //bind(&VoiceBridge::handle_media_stream, this, placeholders::_1); => crash + this->connection->callback_setup_fail = [&](rtc::PeerConnection::ConnectionComponent comp, const std::string& reason) { + debugMessage(this->server_id(), "{} WebRTC setup failed! Component {} ({})", CLIENT_STR_LOG_PREFIX_(this->owner()), comp, reason); + if(this->callback_failed) + this->callback_failed(); + }; + return true; } bool VoiceBridge::parse_offer(const std::string &sdp) { - this->offer_timestamp = chrono::system_clock::now(); - string error; - return this->connection->apply_offer(error, sdp); + this->offer_timestamp = chrono::system_clock::now(); + string error; + return this->connection->apply_offer(error, sdp); } int VoiceBridge::apply_ice(const std::deque>& candidates) { - return this->connection->apply_ice_candidates(candidates); + return this->connection->apply_ice_candidates(candidates); } void VoiceBridge::remote_ice_finished() { - if(negotiation_possible) { - this->connection->execute_negotiation(); - negotiation_required = false; - } else { - negotiation_required = true; - } + if(negotiation_possible) { + this->connection->execute_negotiation(); + negotiation_required = false; + } else { + negotiation_required = true; + } } std::string VoiceBridge::generate_answer() { - return this->connection->generate_answer(true); + return this->connection->generate_answer(true); } void VoiceBridge::execute_tick() { - if(!this->_voice_channel) { - if(this->offer_timestamp.time_since_epoch().count() > 0 && this->offer_timestamp + chrono::seconds(10) < chrono::system_clock::now()) { - this->offer_timestamp = chrono::system_clock::time_point(); - this->connection->callback_setup_fail(rtc::PeerConnection::ConnectionComponent::BASE, "setup timeout"); - } - } - if(this->negotiation_required && this->negotiation_possible) { - this->connection->execute_negotiation(); - negotiation_required = false; - } + if(!this->_voice_channel) { + if(this->offer_timestamp.time_since_epoch().count() > 0 && this->offer_timestamp + chrono::seconds(10) < chrono::system_clock::now()) { + this->offer_timestamp = chrono::system_clock::time_point(); + this->connection->callback_setup_fail(rtc::PeerConnection::ConnectionComponent::BASE, "setup timeout"); + } + } + if(this->negotiation_required && this->negotiation_possible) { + this->connection->execute_negotiation(); + negotiation_required = false; + } } void VoiceBridge::handle_media_stream(const std::shared_ptr &undefined_stream) { - if(undefined_stream->type() == rtc::CHANTYPE_APPLICATION) { - auto stream = dynamic_pointer_cast(undefined_stream); - if(!stream) return; + if(undefined_stream->type() == rtc::CHANTYPE_APPLICATION) { + auto stream = dynamic_pointer_cast(undefined_stream); + if(!stream) return; - stream->callback_datachannel_new = [&](const std::shared_ptr &channel) { this->handle_data_channel(channel); }; //bind(&VoiceBridge::handle_data_channel, this, placeholders::_1); => may crash? - } else if(undefined_stream->type() == rtc::CHANTYPE_AUDIO) { - auto stream = dynamic_pointer_cast(undefined_stream); - if(!stream) return; - this->_audio_channel = stream; + stream->callback_datachannel_new = [&](const std::shared_ptr &channel) { this->handle_data_channel(channel); }; //bind(&VoiceBridge::handle_data_channel, this, placeholders::_1); => may crash? + } else if(undefined_stream->type() == rtc::CHANTYPE_AUDIO) { + auto stream = dynamic_pointer_cast(undefined_stream); + if(!stream) return; + this->_audio_channel = stream; - stream->register_local_extension("urn:ietf:params:rtp-hdrext:ssrc-audio-level"); - //bind(&VoiceBridge::handle_audio_data, this, placeholders::_1, placeholders::_2, placeholders::_3); => may crash? - stream->incoming_data_handler = [&](const std::shared_ptr &channel, const pipes::buffer_view &data, size_t payload_offset) { this->handle_audio_data(channel, data, payload_offset); }; - } else { - logError(this->server_id(), "Got offer for unknown channel of type {}", undefined_stream->type()); - } + stream->register_local_extension("urn:ietf:params:rtp-hdrext:ssrc-audio-level"); + //bind(&VoiceBridge::handle_audio_data, this, placeholders::_1, placeholders::_2, placeholders::_3); => may crash? + stream->incoming_data_handler = [&](const std::shared_ptr &channel, const pipes::buffer_view &data, size_t payload_offset) { this->handle_audio_data(channel, data, payload_offset); }; + } else { + logError(this->server_id(), "Got offer for unknown channel of type {}", undefined_stream->type()); + } } void VoiceBridge::handle_data_channel(const std::shared_ptr &channel) { - if(channel->lable() == "main") { - this->_voice_channel = channel; - debugMessage(this->server_id(), "{} Got voice channel!", CLIENT_STR_LOG_PREFIX_(this->owner())); - this->callback_initialized(); - } + if(channel->lable() == "main") { + this->_voice_channel = channel; + debugMessage(this->server_id(), "{} Got voice channel!", CLIENT_STR_LOG_PREFIX_(this->owner())); + this->callback_initialized(); + } - weak_ptr weak_channel = channel; - channel->callback_binary = [&, weak_channel](const pipes::buffer_view& buffer) { - this->callback_voice_data(buffer.view(2), buffer[0] == 1, buffer[1] == 1); /* buffer.substr(2), buffer[0] == 1, buffer[1] == 1 */ - }; + weak_ptr weak_channel = channel; + channel->callback_binary = [&, weak_channel](const pipes::buffer_view& buffer) { + this->callback_voice_data(buffer.view(2), buffer[0] == 1, buffer[1] == 1); /* buffer.substr(2), buffer[0] == 1, buffer[1] == 1 */ + }; - channel->callback_close = [&, channel] { - if(channel == this->_voice_channel) { - this->_voice_channel = nullptr; - //TODO may callback? - debugMessage(this->server_id(), "{} Voice channel disconnected!", CLIENT_STR_LOG_PREFIX_(this->owner())); - } - }; + channel->callback_close = [&, channel] { + if(channel == this->_voice_channel) { + this->_voice_channel = nullptr; + //TODO may callback? + debugMessage(this->server_id(), "{} Voice channel disconnected!", CLIENT_STR_LOG_PREFIX_(this->owner())); + } + }; } /* - ssize_t sample_count = 960; - ssize_t sample_length = sample_count * 2; - opus_int16 samples[sample_length]; + ssize_t sample_count = 960; + ssize_t sample_length = sample_count * 2; + opus_int16 samples[sample_length]; - if(!this->decoder) { - int error; - this->decoder = opus_decoder_create(48000, 2, &error); - assert(error == 0 && this->decoder); - } - if(!this->encoder) { - int error; - this->encoder = opus_encoder_create(48000, 2, OPUS_APPLICATION_AUDIO, &error); - assert(error == 0 && this->encoder); - } + if(!this->decoder) { + int error; + this->decoder = opus_decoder_create(48000, 2, &error); + assert(error == 0 && this->decoder); + } + if(!this->encoder) { + int error; + this->encoder = opus_encoder_create(48000, 2, OPUS_APPLICATION_AUDIO, &error); + assert(error == 0 && this->encoder); + } - { - sample_count = opus_decode(this->decoder, (u_char*) &data.data()[payload_offset], data.length() - payload_offset, samples, sample_count, 0); - if(sample_count < 0) { - logError(this->server_id(), "Could not decode opus!"); - return; - } - } + { + sample_count = opus_decode(this->decoder, (u_char*) &data.data()[payload_offset], data.length() - payload_offset, samples, sample_count, 0); + if(sample_count < 0) { + logError(this->server_id(), "Could not decode opus!"); + return; + } + } - string target_buffer(512, '\0'); //Way too big! - le2be16(this->voice.packet_id++, (char*) target_buffer.data()); - target_buffer[2] = 5; + string target_buffer(512, '\0'); //Way too big! + le2be16(this->voice.packet_id++, (char*) target_buffer.data()); + target_buffer[2] = 5; - auto encoded = opus_encode(this->encoder, samples, sample_count, (u_char*) &target_buffer[3], 512 - 3); - if(encoded < 0 ){ - logError(this->server_id(), "Could not encode opus!"); - return; - } + auto encoded = opus_encode(this->encoder, samples, sample_count, (u_char*) &target_buffer[3], 512 - 3); + if(encoded < 0 ){ + logError(this->server_id(), "Could not encode opus!"); + return; + } */ void VoiceBridge::handle_audio_data(const std::shared_ptr &channel, const pipes::buffer_view &data, size_t payload_offset) { - if(channel->codec->type != rtc::codec::TypedAudio::OPUS) { - debugMessage(this->server_id(), "{} Got unknown codec ({})!", CLIENT_STR_LOG_PREFIX_(this->owner()), channel->codec->type); - return; - } + if(channel->codec->type != rtc::codec::TypedAudio::OPUS) { + debugMessage(this->server_id(), "{} Got unknown codec ({})!", CLIENT_STR_LOG_PREFIX_(this->owner()), channel->codec->type); + return; + } - auto ac = _audio_channel.lock(); - if(!ac) return; + auto ac = _audio_channel.lock(); + if(!ac) return; - for(const auto& ext : ac->list_extensions(0x02)) { - if(ext->name == "urn:ietf:params:rtp-hdrext:ssrc-audio-level") { - int level; - if(rtc::protocol::rtp_header_extension_parse_audio_level(data, ext->id, &level) == 0) { - //debugMessage(this->server_id(), "Audio level: {}", level); - if(level == 127) return; //Silence - } - break; - } - } - //int level; - //rtc::protocol::rtp_header_extension_parse_audio_level((char*) data.data(), data.length(), 1, &level); - auto target_buffer = buffer::allocate_buffer(data.length() - payload_offset + 3); - le2be16(this->voice.packet_id++, (char*) target_buffer.data_ptr()); - target_buffer[2] = 5; - memcpy(&target_buffer[3], &data[payload_offset], data.length() - payload_offset); + for(const auto& ext : ac->list_extensions(0x02)) { + if(ext->name == "urn:ietf:params:rtp-hdrext:ssrc-audio-level") { + int level; + if(rtc::protocol::rtp_header_extension_parse_audio_level(data, ext->id, &level) == 0) { + //debugMessage(this->server_id(), "Audio level: {}", level); + if(level == 127) return; //Silence + } + break; + } + } + //int level; + //rtc::protocol::rtp_header_extension_parse_audio_level((char*) data.data(), data.length(), 1, &level); + auto target_buffer = buffer::allocate_buffer(data.length() - payload_offset + 3); + le2be16(this->voice.packet_id++, (char*) target_buffer.data_ptr()); + target_buffer[2] = 5; + memcpy(&target_buffer[3], &data[payload_offset], data.length() - payload_offset); - this->callback_voice_data(target_buffer, this->voice.packet_id < 7, false); + this->callback_voice_data(target_buffer, this->voice.packet_id < 7, false); } \ No newline at end of file diff --git a/server/src/client/web/VoiceBridge.h b/server/src/client/web/VoiceBridge.h index ba9615b..e55f9d5 100644 --- a/server/src/client/web/VoiceBridge.h +++ b/server/src/client/web/VoiceBridge.h @@ -5,57 +5,57 @@ #include namespace ts { - namespace server { - class WebClient; - } - namespace web { - class VoiceBridge { - public: - typedef std::function cb_voice_data; - typedef std::function cb_ice_candidate; - typedef std::function cb_ice_candidate_finish; - typedef std::function cb_initialized; - typedef std::function cb_failed; + namespace server { + class WebClient; + } + namespace web { + class VoiceBridge { + public: + typedef std::function cb_voice_data; + typedef std::function cb_ice_candidate; + typedef std::function cb_ice_candidate_finish; + typedef std::function cb_initialized; + typedef std::function cb_failed; - std::shared_ptr voice_channel() { return this->_voice_channel; } + std::shared_ptr voice_channel() { return this->_voice_channel; } - explicit VoiceBridge(const std::shared_ptr&); - virtual ~VoiceBridge(); + explicit VoiceBridge(const std::shared_ptr&); + virtual ~VoiceBridge(); - bool initialize(std::string&); - bool parse_offer(const std::string&); - int apply_ice(const std::deque>& /* candidates */); - void remote_ice_finished(); - std::string generate_answer(); + bool initialize(std::string&); + bool parse_offer(const std::string&); + int apply_ice(const std::deque>& /* candidates */); + void remote_ice_finished(); + std::string generate_answer(); - cb_ice_candidate callback_ice_candidate; - cb_ice_candidate_finish callback_ice_candidate_finished; - cb_voice_data callback_voice_data; - cb_initialized callback_initialized; - cb_failed callback_failed; + cb_ice_candidate callback_ice_candidate; + cb_ice_candidate_finish callback_ice_candidate_finished; + cb_voice_data callback_voice_data; + cb_initialized callback_initialized; + cb_failed callback_failed; - void execute_tick(); - private: + void execute_tick(); + private: - inline int server_id(); - inline std::shared_ptr owner(); + inline int server_id(); + inline std::shared_ptr owner(); - void handle_media_stream(const std::shared_ptr& /* stream */); - void handle_data_channel(const std::shared_ptr & /* channel */); - void handle_audio_data(const std::shared_ptr& /* channel */, const pipes::buffer_view& /* buffer */, size_t /* payload offset */); + void handle_media_stream(const std::shared_ptr& /* stream */); + void handle_data_channel(const std::shared_ptr & /* channel */); + void handle_audio_data(const std::shared_ptr& /* channel */, const pipes::buffer_view& /* buffer */, size_t /* payload offset */); - std::weak_ptr _owner; - std::chrono::system_clock::time_point offer_timestamp; - std::unique_ptr connection; - std::shared_ptr _voice_channel; - std::weak_ptr _audio_channel; - struct { - uint16_t packet_id = 0; - bool muted = true; - } voice; + std::weak_ptr _owner; + std::chrono::system_clock::time_point offer_timestamp; + std::unique_ptr connection; + std::shared_ptr _voice_channel; + std::weak_ptr _audio_channel; + struct { + uint16_t packet_id = 0; + bool muted = true; + } voice; - bool negotiation_possible = false; - bool negotiation_required = false; - }; - } + bool negotiation_possible = false; + bool negotiation_required = false; + }; + } } \ No newline at end of file diff --git a/server/src/client/web/WSWebClient.cpp b/server/src/client/web/WSWebClient.cpp index ae28774..49ffb10 100644 --- a/server/src/client/web/WSWebClient.cpp +++ b/server/src/client/web/WSWebClient.cpp @@ -103,7 +103,7 @@ void WebClient::enqueue_raw_packet(const pipes::buffer_view &msg) { if(this->writeEvent) event_add(this->writeEvent, nullptr); } - this->connectionStatistics->logOutgoingPacket(stats::ConnectionStatistics::category::COMMAND, buffer.length()); + this->connectionStatistics->logOutgoingPacket(stats::ConnectionStatistics::category::COMMAND, buffer.length()); } void WebClient::registerMessageProcess() { @@ -113,14 +113,14 @@ void WebClient::registerMessageProcess() { } inline bool is_ssl_handshake_header(const pipes::buffer_view& buffer) { - if(buffer.length() < 0x05) return false; //Header too small! + if(buffer.length() < 0x05) return false; //Header too small! - if(buffer[0] != 0x16) return false; //recordType=handshake + if(buffer[0] != 0x16) return false; //recordType=handshake - if(buffer[1] < 1 || buffer[1] > 3) return false; //SSL version - if(buffer[2] < 1 || buffer[2] > 3) return false; //TLS version + if(buffer[1] < 1 || buffer[1] > 3) return false; //SSL version + if(buffer[2] < 1 || buffer[2] > 3) return false; //TLS version - return true; + return true; } void WebClient::processNextMessage(const std::chrono::system_clock::time_point& /* scheduled */) { @@ -138,18 +138,18 @@ void WebClient::processNextMessage(const std::chrono::system_clock::time_point& buffer_lock.unlock(); this->connectionStatistics->logIncomingPacket(stats::ConnectionStatistics::category::COMMAND, buffer.length()); - if(!this->ssl_detected) { + if(!this->ssl_detected) { this->ssl_detected = true; this->ssl_encrypted = is_ssl_handshake_header(buffer); if(this->ssl_encrypted) - logMessage(this->getServerId(), "[{}] Using encrypted basic connection.", CLIENT_STR_LOG_PREFIX_(this)); + logMessage(this->getServerId(), "[{}] Using encrypted basic connection.", CLIENT_STR_LOG_PREFIX_(this)); else logMessage(this->getServerId(), "[{}] Using unencrypted basic connection.", CLIENT_STR_LOG_PREFIX_(this)); - } + } if(this->ssl_encrypted) { - this->ssl_handler.process_incoming_data(buffer); + this->ssl_handler.process_incoming_data(buffer); } else { - this->ws_handler.process_incoming_data(buffer); + this->ws_handler.process_incoming_data(buffer); } if(has_next) diff --git a/server/src/client/web/WebClient.cpp b/server/src/client/web/WebClient.cpp index c858767..41ca675 100644 --- a/server/src/client/web/WebClient.cpp +++ b/server/src/client/web/WebClient.cpp @@ -23,130 +23,130 @@ using namespace ts::server; using namespace ts::protocol; WebClient::WebClient(WebControlServer* server, int fd) : SpeakingClient(server->getTS()->getSql(), server->getTS()), handle(server) { - memtrack::allocated(this); + memtrack::allocated(this); - assert(server->getTS()); + assert(server->getTS()); this->state = ConnectionState::INIT_LOW; this->file_descriptor = fd; } void WebClient::initialize() { - this->event_handle_packet = make_shared>(dynamic_pointer_cast(this->ref()), &WebClient::processNextMessage); + this->event_handle_packet = make_shared>(dynamic_pointer_cast(this->ref()), &WebClient::processNextMessage); - int enabled = 1; - int disabled = 0; - setsockopt(this->file_descriptor, SOL_SOCKET, SO_KEEPALIVE, &enabled, sizeof(enabled)); - if(setsockopt(this->file_descriptor, IPPROTO_TCP, TCP_NOPUSH, &disabled, sizeof disabled) < 0) - logError(this->getServerId(), "{} Cant disable nopush! system error: {} => {}", CLIENT_STR_LOG_PREFIX, errno, strerror(errno)); + int enabled = 1; + int disabled = 0; + setsockopt(this->file_descriptor, SOL_SOCKET, SO_KEEPALIVE, &enabled, sizeof(enabled)); + if(setsockopt(this->file_descriptor, IPPROTO_TCP, TCP_NOPUSH, &disabled, sizeof disabled) < 0) + logError(this->getServerId(), "{} Cant disable nopush! system error: {} => {}", CLIENT_STR_LOG_PREFIX, errno, strerror(errno)); - auto event_loop = serverInstance->getWebIoLoop()->next_loop(); - this->readEvent = event_new(event_loop->loop, this->file_descriptor, EV_READ|EV_PERSIST, [](int a, short b, void* c){ ((WebClient*) c)->handleMessageRead(a, b, c); }, this); - this->writeEvent = event_new(event_loop->loop, this->file_descriptor, EV_WRITE, [](int a, short b, void* c){ ((WebClient*) c)->handleMessageWrite(a, b, c); }, this); + auto event_loop = serverInstance->getWebIoLoop()->next_loop(); + this->readEvent = event_new(event_loop->loop, this->file_descriptor, EV_READ|EV_PERSIST, [](int a, short b, void* c){ ((WebClient*) c)->handleMessageRead(a, b, c); }, this); + this->writeEvent = event_new(event_loop->loop, this->file_descriptor, EV_WRITE, [](int a, short b, void* c){ ((WebClient*) c)->handleMessageWrite(a, b, c); }, this); - { - this->ws_handler.direct_process(pipes::PROCESS_DIRECTION_IN, true); - this->ws_handler.direct_process(pipes::PROCESS_DIRECTION_OUT, true); - this->ws_handler.callback_invalid_request = [&](const http::HttpRequest& request, http::HttpResponse& response) { - response.setHeader("Connection", {"close"}); /* close the connection to avoid a second request */ - response.code = http::code::code(301, "Invalid request (Not a web socket!)"); + { + this->ws_handler.direct_process(pipes::PROCESS_DIRECTION_IN, true); + this->ws_handler.direct_process(pipes::PROCESS_DIRECTION_OUT, true); + this->ws_handler.callback_invalid_request = [&](const http::HttpRequest& request, http::HttpResponse& response) { + response.setHeader("Connection", {"close"}); /* close the connection to avoid a second request */ + response.code = http::code::code(301, "Invalid request (Not a web socket!)"); - const auto redirect_target = [&]{ - if(request.parameters.count("forward_url")) - response.setHeader("Location", {request.parameters.at("forward_url")}); - else if(request.findHeader("Origin")) - response.header.push_back({"Location", request.findHeader("Origin").values}); - else - response.header.push_back({"Location", {"https://web.teaspeak.de"}}); - }; + const auto redirect_target = [&]{ + if(request.parameters.count("forward_url")) + response.setHeader("Location", {request.parameters.at("forward_url")}); + else if(request.findHeader("Origin")) + response.header.push_back({"Location", request.findHeader("Origin").values}); + else + response.header.push_back({"Location", {"https://web.teaspeak.de"}}); + }; - /* we're running https */ - if(this->ssl_encrypted) { - redirect_target(); - } else { - /* lets redirect to https */ - if(request.findHeader("Host")) { - const auto redirect_forward = [&](const std::string& url) { - response.setHeader("Location", {"https://" + request.findHeader("Host").values[0] + "/?forward_url=" + http::encode_url(url)}); - }; - if(request.parameters.count("forward_url")) - redirect_forward(request.parameters.at("forward_url")); - else if(request.findHeader("Origin")) - redirect_forward(request.findHeader("Origin").values[0]); - else - redirect_forward("https://web.teaspeak.de"); - } else { - /* we could not find our host */ - redirect_target(); - } - } - }; + /* we're running https */ + if(this->ssl_encrypted) { + redirect_target(); + } else { + /* lets redirect to https */ + if(request.findHeader("Host")) { + const auto redirect_forward = [&](const std::string& url) { + response.setHeader("Location", {"https://" + request.findHeader("Host").values[0] + "/?forward_url=" + http::encode_url(url)}); + }; + if(request.parameters.count("forward_url")) + redirect_forward(request.parameters.at("forward_url")); + else if(request.findHeader("Origin")) + redirect_forward(request.findHeader("Origin").values[0]); + else + redirect_forward("https://web.teaspeak.de"); + } else { + /* we could not find our host */ + redirect_target(); + } + } + }; - this->ws_handler.on_connect = bind(&WebClient::onWSConnected, this); - this->ws_handler.on_disconnect = bind(&WebClient::onWSDisconnected, this, placeholders::_1); + this->ws_handler.on_connect = bind(&WebClient::onWSConnected, this); + this->ws_handler.on_disconnect = bind(&WebClient::onWSDisconnected, this, placeholders::_1); - this->ws_handler.callback_data([&](const pipes::WSMessage& msg){ - this->onWSMessage(msg); - }); - this->ws_handler.callback_write([&](const pipes::buffer_view& data) { - if(this->ssl_encrypted) - this->ssl_handler.send(data); - else - this->enqueue_raw_packet(data); - }); - this->ws_handler.callback_error([&](int error, const std::string& message) { - logError(this->getServerId(), "{} Got ws pipeline error ({} | {})", CLIENT_STR_LOG_PREFIX, error, message); - }); - } + this->ws_handler.callback_data([&](const pipes::WSMessage& msg){ + this->onWSMessage(msg); + }); + this->ws_handler.callback_write([&](const pipes::buffer_view& data) { + if(this->ssl_encrypted) + this->ssl_handler.send(data); + else + this->enqueue_raw_packet(data); + }); + this->ws_handler.callback_error([&](int error, const std::string& message) { + logError(this->getServerId(), "{} Got ws pipeline error ({} | {})", CLIENT_STR_LOG_PREFIX, error, message); + }); + } - { - this->ssl_handler.direct_process(pipes::PROCESS_DIRECTION_IN, true); - this->ssl_handler.direct_process(pipes::PROCESS_DIRECTION_OUT, true); + { + this->ssl_handler.direct_process(pipes::PROCESS_DIRECTION_IN, true); + this->ssl_handler.direct_process(pipes::PROCESS_DIRECTION_OUT, true); - this->ssl_handler.callback_data([&](const pipes::buffer_view& msg) { - this->ws_handler.process_incoming_data(msg); - }); + this->ssl_handler.callback_data([&](const pipes::buffer_view& msg) { + this->ws_handler.process_incoming_data(msg); + }); - this->ssl_handler.callback_write([&](const pipes::buffer_view& msg) { this->enqueue_raw_packet(msg); }); - this->ssl_handler.callback_error([&](int error, const std::string& message) { - logError(this->getServerId(), "{} Got ssl pipeline error ({} | {})", CLIENT_STR_LOG_PREFIX, error, message); - }); - } + this->ssl_handler.callback_write([&](const pipes::buffer_view& msg) { this->enqueue_raw_packet(msg); }); + this->ssl_handler.callback_error([&](int error, const std::string& message) { + logError(this->getServerId(), "{} Got ssl pipeline error ({} | {})", CLIENT_STR_LOG_PREFIX, error, message); + }); + } - this->ssl_handler.initialize(serverInstance->sslManager()->web_ssl_options()); - this->ws_handler.initialize(); + this->ssl_handler.initialize(serverInstance->sslManager()->web_ssl_options()); + this->ws_handler.initialize(); } WebClient::~WebClient() { - memtrack::freed(this); + memtrack::freed(this); } static Json::StreamWriterBuilder stream_write_builder = []{ - Json::StreamWriterBuilder builder; + Json::StreamWriterBuilder builder; - builder["commentStyle"] = "None"; - builder["indentation"] = ""; + builder["commentStyle"] = "None"; + builder["indentation"] = ""; - return builder; + return builder; }(); void WebClient::sendJson(const Json::Value& json) { - unique_ptr writer{stream_write_builder.newStreamWriter()}; + unique_ptr writer{stream_write_builder.newStreamWriter()}; - std::ostringstream stream; - if(writer->write(json, &stream) != 0) { - //TODO log error, but this shall never happen - return; - } - if(!stream.good()) { - //TODO log error - return; - } + std::ostringstream stream; + if(writer->write(json, &stream) != 0) { + //TODO log error, but this shall never happen + return; + } + if(!stream.good()) { + //TODO log error + return; + } - auto data = stream.str(); - pipes::buffer buffer = buffer::allocate_buffer(data.length()); - buffer.write(data.data(), data.length()); + auto data = stream.str(); + pipes::buffer buffer = buffer::allocate_buffer(data.length()); + buffer.write(data.data(), data.length()); this->ws_handler.send({pipes::TEXT, std::move(buffer)}); } @@ -158,121 +158,121 @@ void WebClient::sendCommand(const ts::Command &command, bool low) { } bool WebClient::closeConnection(const std::chrono::system_clock::time_point& timeout) { - bool flushing = timeout.time_since_epoch().count() > 0; + bool flushing = timeout.time_since_epoch().count() > 0; - auto self_lock = dynamic_pointer_cast(_this.lock()); - auto server_lock = this->server; - assert(self_lock); + auto self_lock = dynamic_pointer_cast(_this.lock()); + auto server_lock = this->server; + assert(self_lock); - unique_lock state_lock(this->state_lock); - if(this->state == ConnectionState::DISCONNECTED) return false; - if(this->state == ConnectionState::DISCONNECTING && flushing) return true; - this->state = flushing ? ConnectionState::DISCONNECTING : ConnectionState::DISCONNECTED; + unique_lock state_lock(this->state_lock); + if(this->state == ConnectionState::DISCONNECTED) return false; + if(this->state == ConnectionState::DISCONNECTING && flushing) return true; + this->state = flushing ? ConnectionState::DISCONNECTING : ConnectionState::DISCONNECTED; - unique_lock close_lock(this->close_lock); - state_lock.unlock(); + unique_lock close_lock(this->close_lock); + state_lock.unlock(); - if(this->readEvent) - event_del_noblock(this->readEvent); + if(this->readEvent) + event_del_noblock(this->readEvent); - if(this->server){ - { - unique_lock server_channel_lock(this->server->channel_tree_lock); - this->server->unregisterClient(_this.lock(), "disconnected", server_channel_lock); - } - this->server->groups->disableCache(this->getClientDatabaseId()); - //this->server = nullptr; - } + if(this->server){ + { + unique_lock server_channel_lock(this->server->channel_tree_lock); + this->server->unregisterClient(_this.lock(), "disconnected", server_channel_lock); + } + this->server->groups->disableCache(this->getClientDatabaseId()); + //this->server = nullptr; + } - if(flushing){ - this->flush_thread = std::thread([self_lock, server_lock, timeout] { - bool flag_flushed; - while(true) { - { - lock_guard lock(self_lock->state_lock); - if(self_lock->state != ConnectionState::DISCONNECTING) return; /* somebody else had this problem now */ - } + if(flushing){ + this->flush_thread = std::thread([self_lock, server_lock, timeout] { + bool flag_flushed; + while(true) { + { + lock_guard lock(self_lock->state_lock); + if(self_lock->state != ConnectionState::DISCONNECTING) return; /* somebody else had this problem now */ + } - flag_flushed = true; + flag_flushed = true; - { - lock_guard lock(self_lock->queue_lock); - flag_flushed &= self_lock->queue_read.empty(); - flag_flushed &= self_lock->queue_write.empty(); - } + { + lock_guard lock(self_lock->queue_lock); + flag_flushed &= self_lock->queue_read.empty(); + flag_flushed &= self_lock->queue_write.empty(); + } - if(flag_flushed) - break; + if(flag_flushed) + break; - auto now = system_clock::now(); - if(timeout < now) - break; - else - this_thread::sleep_for(min(duration_cast(timeout - now), milliseconds(10))); - } + auto now = system_clock::now(); + if(timeout < now) + break; + else + this_thread::sleep_for(min(duration_cast(timeout - now), milliseconds(10))); + } - { - lock_guard lock(self_lock->state_lock); - if(self_lock->state != ConnectionState::DISCONNECTING) return; /* somebody else had this problem now */ - self_lock->state = ConnectionState::DISCONNECTED; - } - /* we can lock here again because we've already ensured that we're still disconnecting and updated the status to disconnected. - * So no thread will wait for this thread while close_lock had been locked - */ - lock_guard close_lock(self_lock->close_lock); - self_lock->disconnectFinal(); - }); - } else { - /* lets wait 'till the flush thread recognized that we're overriding close */ - if(this->flush_thread.joinable()) - this->flush_thread.join(); + { + lock_guard lock(self_lock->state_lock); + if(self_lock->state != ConnectionState::DISCONNECTING) return; /* somebody else had this problem now */ + self_lock->state = ConnectionState::DISCONNECTED; + } + /* we can lock here again because we've already ensured that we're still disconnecting and updated the status to disconnected. + * So no thread will wait for this thread while close_lock had been locked + */ + lock_guard close_lock(self_lock->close_lock); + self_lock->disconnectFinal(); + }); + } else { + /* lets wait 'till the flush thread recognized that we're overriding close */ + if(this->flush_thread.joinable()) + this->flush_thread.join(); - disconnectFinal(); - } - return true; + disconnectFinal(); + } + return true; } CommandResult WebClient::handleCommand(Command &command) { - if(this->connectionState() == ConnectionState::INIT_HIGH && this->handshake.state == HandshakeState::SUCCEEDED){ - if(command.command() == "clientinit") { - auto result = this->handleCommandClientInit(command); - if(!result) - this->closeConnection(system_clock::now() + seconds(1)); - return result; - } - } - return SpeakingClient::handleCommand(command); + if(this->connectionState() == ConnectionState::INIT_HIGH && this->handshake.state == HandshakeState::SUCCEEDED){ + if(command.command() == "clientinit") { + auto result = this->handleCommandClientInit(command); + if(!result) + this->closeConnection(system_clock::now() + seconds(1)); + return result; + } + } + return SpeakingClient::handleCommand(command); } void WebClient::tick(const std::chrono::system_clock::time_point& point) { SpeakingClient::tick(point); - { - shared_lock read_voice_bridge_lock(this->voice_bridge_lock); - if(this->voice_bridge) - this->voice_bridge->execute_tick(); - } - if(this->ping.last_request + seconds(1) < point) { - if(this->ping.last_response > this->ping.last_request || this->ping.last_response + this->ping.timeout < point) { - this->ping.current_id++; - this->ping.last_request = point; + { + shared_lock read_voice_bridge_lock(this->voice_bridge_lock); + if(this->voice_bridge) + this->voice_bridge->execute_tick(); + } + if(this->ping.last_request + seconds(1) < point) { + if(this->ping.last_response > this->ping.last_request || this->ping.last_response + this->ping.timeout < point) { + this->ping.current_id++; + this->ping.last_request = point; - char buffer[2]; - le2be8(this->ping.current_id, buffer); - this->ws_handler.send({pipes::PING, {buffer, 2}}); - } - } - if(this->js_ping.last_request + seconds(1) < point) { - if(this->js_ping.last_response > this->js_ping.last_request || this->js_ping.last_request + this->js_ping.timeout < point) { - this->js_ping.current_id++; - this->js_ping.last_request = point; + char buffer[2]; + le2be8(this->ping.current_id, buffer); + this->ws_handler.send({pipes::PING, {buffer, 2}}); + } + } + if(this->js_ping.last_request + seconds(1) < point) { + if(this->js_ping.last_response > this->js_ping.last_request || this->js_ping.last_request + this->js_ping.timeout < point) { + this->js_ping.current_id++; + this->js_ping.last_request = point; - Json::Value jsonCandidate; - jsonCandidate["type"] = "ping"; - jsonCandidate["payload"] = to_string(this->js_ping.current_id); + Json::Value jsonCandidate; + jsonCandidate["type"] = "ping"; + jsonCandidate["payload"] = to_string(this->js_ping.current_id); - this->sendJson(jsonCandidate); - } - } + this->sendJson(jsonCandidate); + } + } } void WebClient::onWSConnected() { @@ -286,100 +286,100 @@ void WebClient::onWSConnected() { this->properties()[property::CLIENT_UNIQUE_IDENTIFIER] = "UnknownWebClient"; this->properties()[property::CLIENT_NICKNAME] = string() + "UnknownWebClient #"; - /* + /* Command init("handshakeidentify"); init["successfully"] = true; this->sendCommand(init); - */ + */ } void WebClient::onWSDisconnected(const string& error) { - string message; - uint16_t close_code = 0; - if(error.length() < 2) - close_code = static_cast(-1); - else { - close_code = be2le16(&error[0]); - message = error.substr(2); - } + string message; + uint16_t close_code = 0; + if(error.length() < 2) + close_code = static_cast(-1); + else { + close_code = be2le16(&error[0]); + message = error.substr(2); + } debugMessage(this->getServerId(), "{} WS disconnected ({}). Application data: {}", CLIENT_STR_LOG_PREFIX, close_code, message); - this->closeConnection(); //TODO? + this->closeConnection(); //TODO? } void WebClient::onWSMessage(const pipes::WSMessage &message) { - if(message.code == pipes::OpCode::TEXT) - this->handleMessage(message.data.string()); - else if(message.code == pipes::OpCode::PING) { - logTrace(this->getServerId(), "{} Received ping on web socket. Application data length: {}. Sending pong", CLIENT_STR_LOG_PREFIX, message.data.length()); - this->ws_handler.send({pipes::PONG, message.data}); - } else if(message.code == pipes::OpCode::PONG) { - if(message.data.length() < 1) { - logError(this->getServerId(), "{} Received pong on web socket with too short payload: {}. Ignoring pong", CLIENT_STR_LOG_PREFIX, message.data.length()); - return; - } + if(message.code == pipes::OpCode::TEXT) + this->handleMessage(message.data.string()); + else if(message.code == pipes::OpCode::PING) { + logTrace(this->getServerId(), "{} Received ping on web socket. Application data length: {}. Sending pong", CLIENT_STR_LOG_PREFIX, message.data.length()); + this->ws_handler.send({pipes::PONG, message.data}); + } else if(message.code == pipes::OpCode::PONG) { + if(message.data.length() < 1) { + logError(this->getServerId(), "{} Received pong on web socket with too short payload: {}. Ignoring pong", CLIENT_STR_LOG_PREFIX, message.data.length()); + return; + } - uint8_t response_id = be2le8(&message.data[0]); - if(response_id != this->ping.current_id) { - debugMessage( - this->getServerId(), - "{} Received pong on web socket which is older than the last request. Delay may over {}ms? (Index: {}, Current index: {})", - CLIENT_STR_LOG_PREFIX, - duration_cast(this->ping.timeout).count(), - response_id, - this->ping.current_id - ); - return; - } - this->ping.last_response = system_clock::now(); - this->ping.value = duration_cast(this->ping.last_response - this->ping.last_request); - /* - debugMessage( - this->getServerId(), - "{} Received pong on web socket. Ping: {}, PingID: {}", - CLIENT_STR_LOG_PREFIX, - (float) duration_cast(this->ping.value).count() / 1000.f, - response_id - ); - */ - } + uint8_t response_id = be2le8(&message.data[0]); + if(response_id != this->ping.current_id) { + debugMessage( + this->getServerId(), + "{} Received pong on web socket which is older than the last request. Delay may over {}ms? (Index: {}, Current index: {})", + CLIENT_STR_LOG_PREFIX, + duration_cast(this->ping.timeout).count(), + response_id, + this->ping.current_id + ); + return; + } + this->ping.last_response = system_clock::now(); + this->ping.value = duration_cast(this->ping.last_response - this->ping.last_request); + /* + debugMessage( + this->getServerId(), + "{} Received pong on web socket. Ping: {}, PingID: {}", + CLIENT_STR_LOG_PREFIX, + (float) duration_cast(this->ping.value).count() / 1000.f, + response_id + ); + */ + } } /* called while helding close_lock*/ void WebClient::disconnectFinal() { - auto self_lock = this->_this.lock(); - { - /* waiting to finish all executes */ - lock_guard lock(this->execute_lock); - } + auto self_lock = this->_this.lock(); + { + /* waiting to finish all executes */ + lock_guard lock(this->execute_lock); + } - if(this->flush_thread.get_id() == this_thread::get_id()) - this->flush_thread.detach(); - else - assert(!this->flush_thread.joinable()); /* shall be already joined via closeConnection(...)*/ + if(this->flush_thread.get_id() == this_thread::get_id()) + this->flush_thread.detach(); + else + assert(!this->flush_thread.joinable()); /* shall be already joined via closeConnection(...)*/ - { - ::event *event_read, *event_write; - { - unique_lock event_lock(this->event_lock); + { + ::event *event_read, *event_write; + { + unique_lock event_lock(this->event_lock); - event_read = this->readEvent; - event_write = this->writeEvent; + event_read = this->readEvent; + event_write = this->writeEvent; - this->readEvent = nullptr; - this->writeEvent = nullptr; - } + this->readEvent = nullptr; + this->writeEvent = nullptr; + } - if(event_read) { - event_del_block(event_read); - event_free(event_read); - } + if(event_read) { + event_del_block(event_read); + event_free(event_read); + } - if(event_write) { - event_del_block(event_write); - event_free(event_write); - } - } + if(event_write) { + event_del_block(event_write); + event_free(event_write); + } + } if(this->file_descriptor > 0){ shutdown(this->file_descriptor, SHUT_RDWR); @@ -388,40 +388,40 @@ void WebClient::disconnectFinal() { } this->processLeave(); - { + { - unique_lock read_voice_bridge_lock(this->voice_bridge_lock); - if(this->voice_bridge) { - //TODO correct close? - this->voice_bridge = nullptr; - } - } + unique_lock read_voice_bridge_lock(this->voice_bridge_lock); + if(this->voice_bridge) { + //TODO correct close? + this->voice_bridge = nullptr; + } + } - this->ssl_handler.finalize(); + this->ssl_handler.finalize(); this->handle->unregisterConnection(static_pointer_cast(self_lock)); } Json::CharReaderBuilder json_reader_builder = []{ - Json::CharReaderBuilder reader_builder; + Json::CharReaderBuilder reader_builder; - return reader_builder; + return reader_builder; }(); void WebClient::handleMessage(const std::string &message) { - /* Not really a need, this will directly be called via the ssl ws pipe, which has been triggered via progress message */ - threads::MutexLock lock(this->execute_lock); + /* Not really a need, this will directly be called via the ssl ws pipe, which has been triggered via progress message */ + threads::MutexLock lock(this->execute_lock); Json::Value val; try { - unique_ptr reader{json_reader_builder.newCharReader()}; + unique_ptr reader{json_reader_builder.newCharReader()}; - string error_message; + string error_message; if(!reader->parse(message.data(),message.data() + message.length(), &val, &error_message)) throw Json::Exception("Could not parse payload! (" + error_message + ")"); } catch (const std::exception& ex) { logError(this->server->getServerId(), "Could not parse web message! Message: " + string(ex.what())); logTrace(this->server->getServerId(), "Payload: " + message); return; } - logTrace(this->server->getServerId(), "[{}] Read message {}", CLIENT_STR_LOG_PREFIX_(this), message); + logTrace(this->server->getServerId(), "[{}] Read message {}", CLIENT_STR_LOG_PREFIX_(this), message); try { if(val["type"].isNull()) { logError(this->server->getServerId(), "[{}] Invalid web json package!"); @@ -433,175 +433,175 @@ void WebClient::handleMessage(const std::string &message) { for(auto it = val["data"][index].begin(); it != val["data"][index].end(); it++) cmd[index][it.key().asString()] = (*it).asString(); } - for (const auto &index : val["flags"]) { - cmd.enableParm(index.asString()); - } + for (const auto &index : val["flags"]) { + cmd.enableParm(index.asString()); + } this->handleCommandFull(cmd, true); } else if(val["type"].asString() == "WebRTC") { auto subType = val["request"].asString(); if(subType == "create") { - unique_lock voice_bridge_lock(this->voice_bridge_lock); + unique_lock voice_bridge_lock(this->voice_bridge_lock); if(this->voice_bridge) { logError(this->server->getServerId(), "[{}] Tried to register a WebRTC channel twice!", CLIENT_STR_LOG_PREFIX_(this)); //return; - this->voice_bridge = nullptr; + this->voice_bridge = nullptr; } //TODO test if bridge already exists! this->voice_bridge = make_unique(dynamic_pointer_cast(_this.lock())); //FIXME Add config - this->voice_bridge->callback_voice_data = [&](const pipes::buffer_view& buffer, bool a, bool b) { - /* may somehow get the "real" packet size? */ - this->connectionStatistics->logIncomingPacket(stats::ConnectionStatistics::category::VOICE, buffer.length()); - this->handlePacketVoice(buffer, a, b); - }; - this->voice_bridge->callback_initialized = [&](){ - debugMessage(this->getServerId(), "{} Voice bridge initialized!", CLIENT_STR_LOG_PREFIX); - }; + this->voice_bridge->callback_voice_data = [&](const pipes::buffer_view& buffer, bool a, bool b) { + /* may somehow get the "real" packet size? */ + this->connectionStatistics->logIncomingPacket(stats::ConnectionStatistics::category::VOICE, buffer.length()); + this->handlePacketVoice(buffer, a, b); + }; + this->voice_bridge->callback_initialized = [&](){ + debugMessage(this->getServerId(), "{} Voice bridge initialized!", CLIENT_STR_LOG_PREFIX); + }; - this->voice_bridge->callback_failed = [&] { - auto vb_ptr = &*this->voice_bridge; /* read only no lock needed */ - std::thread([&, vb_ptr, lock = this->ref()]{ - unique_lock vbl{this->voice_bridge_lock}; - if(&*this->voice_bridge == vb_ptr) - this->voice_bridge.release(); - }).detach(); + this->voice_bridge->callback_failed = [&] { + auto vb_ptr = &*this->voice_bridge; /* read only no lock needed */ + std::thread([&, vb_ptr, lock = this->ref()]{ + unique_lock vbl{this->voice_bridge_lock}; + if(&*this->voice_bridge == vb_ptr) + this->voice_bridge.release(); + }).detach(); - Json::Value response; - response["type"] = "WebRTC"; - response["request"] = "status"; - response["state"] = "failed"; - response["reason"] = "voice bridge setup failed!"; - response["allow_reconnect"] = true; - this->sendJson(response); - }; + Json::Value response; + response["type"] = "WebRTC"; + response["request"] = "status"; + response["state"] = "failed"; + response["reason"] = "voice bridge setup failed!"; + response["allow_reconnect"] = true; + this->sendJson(response); + }; - this->voice_bridge->callback_ice_candidate = [&](const rtc::IceCandidate& ice) { - Json::Value jsonCandidate; - jsonCandidate["type"] = "WebRTC"; - jsonCandidate["request"] = "ice"; - jsonCandidate["msg"]["candidate"] = ice.candidate; - jsonCandidate["msg"]["sdpMid"] = ice.sdpMid; - jsonCandidate["msg"]["sdpMLineIndex"] = ice.sdpMLineIndex; + this->voice_bridge->callback_ice_candidate = [&](const rtc::IceCandidate& ice) { + Json::Value jsonCandidate; + jsonCandidate["type"] = "WebRTC"; + jsonCandidate["request"] = "ice"; + jsonCandidate["msg"]["candidate"] = ice.candidate; + jsonCandidate["msg"]["sdpMid"] = ice.sdpMid; + jsonCandidate["msg"]["sdpMLineIndex"] = ice.sdpMLineIndex; - this->sendJson(jsonCandidate); - }; - this->voice_bridge->callback_ice_candidate_finished = [&]{ - Json::Value jsonCandidate; - jsonCandidate["type"] = "WebRTC"; - jsonCandidate["request"] = "ice_finish"; + this->sendJson(jsonCandidate); + }; + this->voice_bridge->callback_ice_candidate_finished = [&]{ + Json::Value jsonCandidate; + jsonCandidate["type"] = "WebRTC"; + jsonCandidate["request"] = "ice_finish"; - this->sendJson(jsonCandidate); - }; + this->sendJson(jsonCandidate); + }; - auto vbp = &*this->voice_bridge; - voice_bridge_lock.unlock(); - shared_lock read_voice_bridge_lock(this->voice_bridge_lock); + auto vbp = &*this->voice_bridge; + voice_bridge_lock.unlock(); + shared_lock read_voice_bridge_lock(this->voice_bridge_lock); - if(vbp != &*this->voice_bridge) { - Json::Value response; - response["type"] = "WebRTC"; - response["request"] = "status"; - response["state"] = "failed"; - response["reason"] = "initialize failed (obsolete bridge)"; - response["allow_reconnect"] = true; - this->sendJson(response); - return; - } - string error; - if(!this->voice_bridge->initialize(error)) { - Json::Value response; - response["type"] = "WebRTC"; - response["request"] = "status"; - response["state"] = "failed"; - response["reason"] = "initialize failed (" + error + ")"; - response["allow_reconnect"] = true; - this->sendJson(response); - return; - } - if(!this->voice_bridge->parse_offer(val["msg"]["sdp"].asString())) { - Json::Value response; - response["type"] = "WebRTC"; - response["request"] = "status"; - response["state"] = "failed"; - response["reason"] = "offer apply failed (" + error + ")"; - response["allow_reconnect"] = true; - this->sendJson(response); - return; - } else { - auto sdp_response = this->voice_bridge->generate_answer(); - Json::Value response; - response["type"] = "WebRTC"; - response["request"] = "answer"; - response["msg"]["sdp"] = sdp_response; - response["msg"]["type"] = "answer"; - this->sendJson(response); - } - } else if(subType == "ice") { - shared_lock read_voice_bridge_lock(this->voice_bridge_lock); - if(!this->voice_bridge) { - debugMessage(this->getServerId(), "[{}] Received remote ICE candidate without having a voice bridge! Dropping candidate.", CLIENT_STR_LOG_PREFIX); + if(vbp != &*this->voice_bridge) { + Json::Value response; + response["type"] = "WebRTC"; + response["request"] = "status"; + response["state"] = "failed"; + response["reason"] = "initialize failed (obsolete bridge)"; + response["allow_reconnect"] = true; + this->sendJson(response); + return; + } + string error; + if(!this->voice_bridge->initialize(error)) { + Json::Value response; + response["type"] = "WebRTC"; + response["request"] = "status"; + response["state"] = "failed"; + response["reason"] = "initialize failed (" + error + ")"; + response["allow_reconnect"] = true; + this->sendJson(response); + return; + } + if(!this->voice_bridge->parse_offer(val["msg"]["sdp"].asString())) { + Json::Value response; + response["type"] = "WebRTC"; + response["request"] = "status"; + response["state"] = "failed"; + response["reason"] = "offer apply failed (" + error + ")"; + response["allow_reconnect"] = true; + this->sendJson(response); return; } else { - auto candidate_string = val["msg"]["candidate"].asString(); - auto sdp_mid = val["msg"]["sdpMid"].asString(); - auto sdp_line_index = val["msg"]["sdpMLineIndex"].asInt(); + auto sdp_response = this->voice_bridge->generate_answer(); + Json::Value response; + response["type"] = "WebRTC"; + response["request"] = "answer"; + response["msg"]["sdp"] = sdp_response; + response["msg"]["type"] = "answer"; + this->sendJson(response); + } + } else if(subType == "ice") { + shared_lock read_voice_bridge_lock(this->voice_bridge_lock); + if(!this->voice_bridge) { + debugMessage(this->getServerId(), "[{}] Received remote ICE candidate without having a voice bridge! Dropping candidate.", CLIENT_STR_LOG_PREFIX); + return; + } else { + auto candidate_string = val["msg"]["candidate"].asString(); + auto sdp_mid = val["msg"]["sdpMid"].asString(); + auto sdp_line_index = val["msg"]["sdpMLineIndex"].asInt(); - deque> candidates; - candidates.push_back(make_shared(candidate_string, sdp_mid, sdp_line_index)); + deque> candidates; + candidates.push_back(make_shared(candidate_string, sdp_mid, sdp_line_index)); - auto result = this->voice_bridge->apply_ice(candidates); - if(result != candidates.size()) { - logError(this->getServerId(), - "[{}] Failed to apply remote ICE candidate, result: {}. Channel: {} ({}). Candidate: {}", - CLIENT_STR_LOG_PREFIX_(this), - result, - sdp_line_index, - sdp_mid, - candidate_string - ); - } else { - logTrace(this->getServerId(), "[{}] Successfully added ICE candidate for channel {} ({}).", CLIENT_STR_LOG_PREFIX_(this), sdp_line_index, sdp_mid); - } + auto result = this->voice_bridge->apply_ice(candidates); + if(result != candidates.size()) { + logError(this->getServerId(), + "[{}] Failed to apply remote ICE candidate, result: {}. Channel: {} ({}). Candidate: {}", + CLIENT_STR_LOG_PREFIX_(this), + result, + sdp_line_index, + sdp_mid, + candidate_string + ); + } else { + logTrace(this->getServerId(), "[{}] Successfully added ICE candidate for channel {} ({}).", CLIENT_STR_LOG_PREFIX_(this), sdp_line_index, sdp_mid); + } } } else if(subType == "ice_finish") { - shared_lock read_voice_bridge_lock(this->voice_bridge_lock); - if(!this->voice_bridge) { - debugMessage(this->getServerId(), "[{}] Received remote ICE candidate without having a voice bridge! Dropping candidate.", CLIENT_STR_LOG_PREFIX); - return; - } - this->voice_bridge->remote_ice_finished(); + shared_lock read_voice_bridge_lock(this->voice_bridge_lock); + if(!this->voice_bridge) { + debugMessage(this->getServerId(), "[{}] Received remote ICE candidate without having a voice bridge! Dropping candidate.", CLIENT_STR_LOG_PREFIX); + return; + } + this->voice_bridge->remote_ice_finished(); } } else if(val["type"].asString() == "ping") { - Json::Value response; - response["type"] = "pong"; - response["payload"] = val["payload"]; - response["ping_native"] = to_string(duration_cast(this->ping.value).count()); - this->sendJson(response); - return; + Json::Value response; + response["type"] = "pong"; + response["payload"] = val["payload"]; + response["ping_native"] = to_string(duration_cast(this->ping.value).count()); + this->sendJson(response); + return; } else if(val["type"].asString() == "pong") { - auto payload = val["payload"].isString() ? val["payload"].asString() : ""; - uint8_t response_id = 0; - try { - response_id = (uint8_t) stoul(payload); - } catch(std::exception& ex) { - debugMessage(this->getServerId(), "[{}] Failed to parse pong payload."); - return; - } + auto payload = val["payload"].isString() ? val["payload"].asString() : ""; + uint8_t response_id = 0; + try { + response_id = (uint8_t) stoul(payload); + } catch(std::exception& ex) { + debugMessage(this->getServerId(), "[{}] Failed to parse pong payload."); + return; + } - if(response_id != this->js_ping.current_id) { - debugMessage( - this->getServerId(), - "{} Received pong on web socket from javascript which is older than the last request. Delay may over {}ms? (Index: {}, Current index: {})", - CLIENT_STR_LOG_PREFIX, - duration_cast(this->js_ping.timeout).count(), - response_id, - this->js_ping.current_id - ); - return; - } - this->js_ping.last_response = system_clock::now(); - this->js_ping.value = duration_cast(this->js_ping.last_response - this->js_ping.last_request); + if(response_id != this->js_ping.current_id) { + debugMessage( + this->getServerId(), + "{} Received pong on web socket from javascript which is older than the last request. Delay may over {}ms? (Index: {}, Current index: {})", + CLIENT_STR_LOG_PREFIX, + duration_cast(this->js_ping.timeout).count(), + response_id, + this->js_ping.current_id + ); + return; + } + this->js_ping.last_response = system_clock::now(); + this->js_ping.value = duration_cast(this->js_ping.last_response - this->js_ping.last_request); } } catch (const std::exception& ex) { logError(this->server->getServerId(), "Could not handle json packet! Message {}", ex.what()); @@ -612,47 +612,47 @@ void WebClient::handleMessage(const std::string &message) { //candidate:0 1 UDP 2122252543 192.168.43.141 34590 typ host bool WebClient::disconnect(const std::string &reason) { - { - unique_lock server_channel_lock(this->server->channel_tree_lock); - { - unique_lock own_lock(this->channel_lock); - this->notifyClientLeftViewKicked(this->ref(), nullptr, reason, this->server->serverRoot, false); - } - this->server->client_move(this->ref(), nullptr, this->server->serverRoot, reason, ViewReasonId::VREASON_SERVER_KICK, false, server_channel_lock); - this->server->unregisterClient(_this.lock(), "disconnected", server_channel_lock); - } + { + unique_lock server_channel_lock(this->server->channel_tree_lock); + { + unique_lock own_lock(this->channel_lock); + this->notifyClientLeftViewKicked(this->ref(), nullptr, reason, this->server->serverRoot, false); + } + this->server->client_move(this->ref(), nullptr, this->server->serverRoot, reason, ViewReasonId::VREASON_SERVER_KICK, false, server_channel_lock); + this->server->unregisterClient(_this.lock(), "disconnected", server_channel_lock); + } return this->closeConnection(system_clock::now() + seconds(1)); } CommandResult WebClient::handleCommandClientInit(Command &command) { - return SpeakingClient::handleCommandClientInit(command); + return SpeakingClient::handleCommandClientInit(command); } bool WebClient::shouldReceiveVoice(const std::shared_ptr &sender) { - shared_lock read_voice_bridge_lock(this->voice_bridge_lock); - if(!this->voice_bridge || !this->voice_bridge->voice_channel()) return false; - return SpeakingClient::shouldReceiveVoice(sender); + shared_lock read_voice_bridge_lock(this->voice_bridge_lock); + if(!this->voice_bridge || !this->voice_bridge->voice_channel()) return false; + return SpeakingClient::shouldReceiveVoice(sender); } void WebClient::handlePacketVoiceWhisper(const pipes::buffer_view &string, bool flag) { - shared_lock read_voice_bridge_lock(this->voice_bridge_lock); - if(!this->voice_bridge || !this->voice_bridge->voice_channel()) return; - SpeakingClient::handlePacketVoiceWhisper(string, flag); + shared_lock read_voice_bridge_lock(this->voice_bridge_lock); + if(!this->voice_bridge || !this->voice_bridge->voice_channel()) return; + SpeakingClient::handlePacketVoiceWhisper(string, flag); } void WebClient::send_voice_packet(const pipes::buffer_view &view, const SpeakingClient::VoicePacketFlags &flags) { - shared_lock read_voice_bridge_lock(this->voice_bridge_lock); - if(this->voice_bridge) { - auto channel = this->voice_bridge->voice_channel(); - if(channel) { - channel->send(view); - /* may somehow get the "real" packet size? */ - this->connectionStatistics->logOutgoingPacket(stats::ConnectionStatistics::category::VOICE, view.length()); - } - } + shared_lock read_voice_bridge_lock(this->voice_bridge_lock); + if(this->voice_bridge) { + auto channel = this->voice_bridge->voice_channel(); + if(channel) { + channel->send(view); + /* may somehow get the "real" packet size? */ + this->connectionStatistics->logOutgoingPacket(stats::ConnectionStatistics::category::VOICE, view.length()); + } + } } void WebClient::send_voice_whisper_packet(const pipes::buffer_view &view, const SpeakingClient::VoicePacketFlags &flags) { - logError(this->server->getServerId(), "Web client got whisper packet"); - //As well log the data! + logError(this->server->getServerId(), "Web client got whisper packet"); + //As well log the data! } diff --git a/server/src/client/web/WebClient.h b/server/src/client/web/WebClient.h index 8f62146..40cb61c 100644 --- a/server/src/client/web/WebClient.h +++ b/server/src/client/web/WebClient.h @@ -30,15 +30,15 @@ namespace ts { bool disconnect(const std::string &reason) override; bool closeConnection(const std::chrono::system_clock::time_point& timeout = std::chrono::system_clock::time_point()) override; - bool shouldReceiveVoice(const std::shared_ptr &sender) override; + bool shouldReceiveVoice(const std::shared_ptr &sender) override; - inline std::chrono::nanoseconds client_ping() { return this->client_ping_layer_7(); } - inline std::chrono::nanoseconds client_ping_layer_5() { return this->ping.value; } - inline std::chrono::nanoseconds client_ping_layer_7() { return this->js_ping.value; } - protected: - void handlePacketVoiceWhisper(const pipes::buffer_view &string, bool) override; + inline std::chrono::nanoseconds client_ping() { return this->client_ping_layer_7(); } + inline std::chrono::nanoseconds client_ping_layer_5() { return this->ping.value; } + inline std::chrono::nanoseconds client_ping_layer_7() { return this->js_ping.value; } + protected: + void handlePacketVoiceWhisper(const pipes::buffer_view &string, bool) override; - protected: + protected: void tick(const std::chrono::system_clock::time_point&) override; /* Every 500ms */ void applySelfLock(const std::shared_ptr &cl){ _this = cl; } @@ -55,37 +55,37 @@ namespace ts { ::event* writeEvent; struct { - uint8_t current_id = 0; - std::chrono::system_clock::time_point last_request; - std::chrono::system_clock::time_point last_response; + uint8_t current_id = 0; + std::chrono::system_clock::time_point last_request; + std::chrono::system_clock::time_point last_response; - std::chrono::nanoseconds value{}; - std::chrono::nanoseconds timeout{2000}; + std::chrono::nanoseconds value{}; + std::chrono::nanoseconds timeout{2000}; } ping; - struct { - uint8_t current_id = 0; - std::chrono::system_clock::time_point last_request; - std::chrono::system_clock::time_point last_response; + struct { + uint8_t current_id = 0; + std::chrono::system_clock::time_point last_request; + std::chrono::system_clock::time_point last_response; - std::chrono::nanoseconds value{}; - std::chrono::nanoseconds timeout{2000}; - } js_ping; + std::chrono::nanoseconds value{}; + std::chrono::nanoseconds timeout{2000}; + } js_ping; std::mutex queue_lock; std::deque queue_read; - std::deque queue_write; + std::deque queue_write; threads::Mutex execute_lock; /* needs to be recursive! */ - std::thread flush_thread; - std::recursive_mutex close_lock; + std::thread flush_thread; + std::recursive_mutex close_lock; private: - void initialize(); + void initialize(); - bool ssl_detected = false; - bool ssl_encrypted = true; - pipes::SSL ssl_handler; - pipes::WebSocket ws_handler; + bool ssl_detected = false; + bool ssl_encrypted = true; + pipes::SSL ssl_handler; + pipes::WebSocket ws_handler; void handleMessageRead(int, short, void*); void handleMessageWrite(int, short, void*); void enqueue_raw_packet(const pipes::buffer_view& /* buffer */); @@ -93,7 +93,7 @@ namespace ts { void processNextMessage(const std::chrono::system_clock::time_point& /* scheduled */); void registerMessageProcess(); - std::shared_ptr> event_handle_packet; + std::shared_ptr> event_handle_packet; //WS events void onWSConnected(); @@ -103,14 +103,14 @@ namespace ts { void disconnectFinal(); void handleMessage(const std::string &); - public: - void send_voice_packet(const pipes::buffer_view &view, const VoicePacketFlags &flags) override; - void send_voice_whisper_packet(const pipes::buffer_view &view, const VoicePacketFlags &flags) override; + public: + void send_voice_packet(const pipes::buffer_view &view, const VoicePacketFlags &flags) override; + void send_voice_whisper_packet(const pipes::buffer_view &view, const VoicePacketFlags &flags) override; - protected: + protected: - CommandResult handleCommand(Command &command) override; - CommandResult handleCommandClientInit(Command &command) override; + CommandResult handleCommand(Command &command) override; + CommandResult handleCommandClientInit(Command &command) override; }; } } diff --git a/server/src/geo/GeoLocation.cpp b/server/src/geo/GeoLocation.cpp index 88319ce..c6b32cf 100644 --- a/server/src/geo/GeoLocation.cpp +++ b/server/src/geo/GeoLocation.cpp @@ -23,7 +23,7 @@ std::shared_ptr RangedIPProviderBase::_resolveInfo(IpAddress_t address, bo if(entry.startAddress > beAddr) continue; if(entry.endAddress >= entry.startAddress && enforce_range) { - if(entry.endAddress < beAddr) continue; + if(entry.endAddress < beAddr) continue; } if(!closest || closest->startAddress < entry.startAddress) closest = &entry; @@ -37,9 +37,9 @@ std::shared_ptr RangedIPProviderBase::_resolveInfo(IpAddress_t address, bo } void RangedIPProviderBase::_registerRange(const RangeEntryMapping& entry) { - this->mapping[this->index(entry.startAddress)].push_back(entry); - if(entry.endAddress > 0 && this->index(entry.endAddress) != this->index(entry.startAddress)) - this->mapping[this->index(entry.endAddress)].push_back(entry); + this->mapping[this->index(entry.startAddress)].push_back(entry); + if(entry.endAddress > 0 && this->index(entry.endAddress) != this->index(entry.startAddress)) + this->mapping[this->index(entry.endAddress)].push_back(entry); } @@ -64,32 +64,32 @@ bool geoloc::CVSFileBasedProviderBase::loadCVS(std::string &err) { } std::deque CVSFileBasedProviderBase::parseCVSLine(const std::string& line, char sep) { - std::deque result; - std::stringstream buffer; + std::deque result; + std::stringstream buffer; - bool quoted = false; - for(const char c : line) { - if(quoted) { - if(c == '"') { - quoted = false; - continue; - } - buffer << c; - continue; - } - if(c == sep) { - result.push_back(buffer.str()); - buffer = std::stringstream(); - continue; - } - if(c == '"') { - quoted = true; - continue; - } - if(c == ' ' && buffer.str().empty()) continue; + bool quoted = false; + for(const char c : line) { + if(quoted) { + if(c == '"') { + quoted = false; + continue; + } + buffer << c; + continue; + } + if(c == sep) { + result.push_back(buffer.str()); + buffer = std::stringstream(); + continue; + } + if(c == '"') { + quoted = true; + continue; + } + if(c == ' ' && buffer.str().empty()) continue; - buffer << c; - } - result.push_back(buffer.str()); - return result; + buffer << c; + } + result.push_back(buffer.str()); + return result; } \ No newline at end of file diff --git a/server/src/geo/GeoLocation.h b/server/src/geo/GeoLocation.h index 1ac0eea..3fd8544 100644 --- a/server/src/geo/GeoLocation.h +++ b/server/src/geo/GeoLocation.h @@ -44,7 +44,7 @@ namespace geoloc { private: }; - template + template class RangeEntryMapping { public: IpAddress_t startAddress; @@ -52,99 +52,99 @@ namespace geoloc { std::shared_ptr data; }; - class RangedIPProviderBase { - public: + class RangedIPProviderBase { + public: - protected: - inline uint8_t index(IpAddress_t address) { return (address >> (8 * 3)) & 0xFF; } + protected: + inline uint8_t index(IpAddress_t address) { return (address >> (8 * 3)) & 0xFF; } - std::shared_ptr _resolveInfo(IpAddress_t address, bool enforce_range); - void _registerRange(const RangeEntryMapping&); + std::shared_ptr _resolveInfo(IpAddress_t address, bool enforce_range); + void _registerRange(const RangeEntryMapping&); - std::array>, 256> mapping; - }; + std::array>, 256> mapping; + }; - template + template class RangedIPProvider : public InfoProvider, public RangedIPProviderBase { public: std::shared_ptr resolveInfo(IpAddress_t address, bool enforce_range) override { return std::static_pointer_cast(this->_resolveInfo(address, enforce_range)); } void unload() override { - for(auto& elm : this->mapping) - elm.clear(); + for(auto& elm : this->mapping) + elm.clear(); } protected: void registerRange(const RangeEntryMapping& mapping) { - this->_registerRange(*(RangeEntryMapping*) &mapping); + this->_registerRange(*(RangeEntryMapping*) &mapping); } }; - class CVSFileBasedProviderBase { - public: - explicit CVSFileBasedProviderBase(const std::string& file); - virtual ~CVSFileBasedProviderBase() = default; + class CVSFileBasedProviderBase { + public: + explicit CVSFileBasedProviderBase(const std::string& file); + virtual ~CVSFileBasedProviderBase() = default; - bool loadCVS(std::string &); - std::string getFileName(){ return this->fileName; } - protected: - std::string fileName; - virtual void invoke_single_line(const std::string& line) = 0; - virtual void emit_line_parse_failed(const std::string& line) = 0; - std::deque parseCVSLine(const std::string& line, char sep); - }; + bool loadCVS(std::string &); + std::string getFileName(){ return this->fileName; } + protected: + std::string fileName; + virtual void invoke_single_line(const std::string& line) = 0; + virtual void emit_line_parse_failed(const std::string& line) = 0; + std::deque parseCVSLine(const std::string& line, char sep); + }; - template + template class CVSFileBasedProvider : public RangedIPProvider, public CVSFileBasedProviderBase { public: - explicit CVSFileBasedProvider(const std::string& file) : CVSFileBasedProviderBase(file) {} - ~CVSFileBasedProvider() = default; + explicit CVSFileBasedProvider(const std::string& file) : CVSFileBasedProviderBase(file) {} + ~CVSFileBasedProvider() = default; - bool load(std::string &error) override { - return this->loadCVS(error); - } - protected: + bool load(std::string &error) override { + return this->loadCVS(error); + } + protected: virtual bool parseSingleLine(const std::string&, RangeEntryMapping&) = 0; private: - void invoke_single_line(const std::string& line) override { - RangeEntryMapping entry; - if(!this->parseSingleLine(line, entry)) { - this->emit_line_parse_failed(line); - } else this->registerRange(entry); + void invoke_single_line(const std::string& line) override { + RangeEntryMapping entry; + if(!this->parseSingleLine(line, entry)) { + this->emit_line_parse_failed(line); + } else this->registerRange(entry); } }; - /** IP to location */ - struct CountryInfo { - CountryInfo(std::string identifier, std::string name) : identifier(std::move(identifier)), name(std::move(name)) {} + /** IP to location */ + struct CountryInfo { + CountryInfo(std::string identifier, std::string name) : identifier(std::move(identifier)), name(std::move(name)) {} - std::string identifier; - std::string name; - }; + std::string identifier; + std::string name; + }; - enum ProviderType { - PROVIDER_MIN, - PROVIDER_IP2LOCATION = PROVIDER_MIN, - PROVIDER_SOFTWARE77, - PROVIDER_MAX - }; + enum ProviderType { + PROVIDER_MIN, + PROVIDER_IP2LOCATION = PROVIDER_MIN, + PROVIDER_SOFTWARE77, + PROVIDER_MAX + }; - class LocationIPProvider : public CVSFileBasedProvider { - public: - LocationIPProvider(const std::string& file) : CVSFileBasedProvider(file) {}; - virtual ~LocationIPProvider() = default; + class LocationIPProvider : public CVSFileBasedProvider { + public: + LocationIPProvider(const std::string& file) : CVSFileBasedProvider(file) {}; + virtual ~LocationIPProvider() = default; - void unload() override { - for(auto& elm : this->countryMapping) - elm.clear(); - } + void unload() override { + for(auto& elm : this->countryMapping) + elm.clear(); + } - std::shared_ptr resolveCountryInfo(std::string code); - std::shared_ptr createCountryInfo(std::string code, const std::string& name); - private: - std::array>, 26> countryMapping; //A-Z - }; + std::shared_ptr resolveCountryInfo(std::string code); + std::shared_ptr createCountryInfo(std::string code, const std::string& name); + private: + std::array>, 26> countryMapping; //A-Z + }; class Software77Provider : public LocationIPProvider { @@ -153,42 +153,42 @@ namespace geoloc { ~Software77Provider() override; protected: bool parseSingleLine(const std::string &string, RangeEntryMapping &mapping) override; - void emit_line_parse_failed(const std::string &line) override; + void emit_line_parse_failed(const std::string &line) override; }; class IP2LocationProvider : public LocationIPProvider { public: - explicit IP2LocationProvider(const std::string& file); + explicit IP2LocationProvider(const std::string& file); ~IP2LocationProvider() = default; protected: bool parseSingleLine(const std::string &string, RangeEntryMapping &mapping) override; - void emit_line_parse_failed(const std::string &line) override; + void emit_line_parse_failed(const std::string &line) override; }; /** VPN Blocker **/ struct VPNInfo { - VPNInfo(std::string name, std::string side) : name(std::move(name)), side(std::move(side)) {} + VPNInfo(std::string name, std::string side) : name(std::move(name)), side(std::move(side)) {} - std::string name; - std::string side; + std::string name; + std::string side; }; class IPCatBlocker : public CVSFileBasedProvider { - public: - explicit IPCatBlocker(const std::string& file); - ~IPCatBlocker() = default; + public: + explicit IPCatBlocker(const std::string& file); + ~IPCatBlocker() = default; - protected: - bool parseSingleLine(const std::string &string, RangeEntryMapping &mapping) override; - void emit_line_parse_failed(const std::string &line) override; - private: - std::shared_ptr get_or_create_info(const std::string &hoster, const std::string &webside); + protected: + bool parseSingleLine(const std::string &string, RangeEntryMapping &mapping) override; + void emit_line_parse_failed(const std::string &line) override; + private: + std::shared_ptr get_or_create_info(const std::string &hoster, const std::string &webside); - std::deque> hoster_info; + std::deque> hoster_info; }; - extern InfoProvider* provider; - extern InfoProvider* provider_vpn; + extern InfoProvider* provider; + extern InfoProvider* provider_vpn; } \ No newline at end of file diff --git a/server/src/geo/IP2Location.cpp b/server/src/geo/IP2Location.cpp index fb2092e..f4502d1 100644 --- a/server/src/geo/IP2Location.cpp +++ b/server/src/geo/IP2Location.cpp @@ -6,22 +6,22 @@ using namespace geoloc; /** LocationIPProvider **/ std::shared_ptr LocationIPProvider::resolveCountryInfo(std::string code) { - if(code[0] == '-') code = "UNKNOWN"; - auto &list = countryMapping[code[0] - 'A']; - for (const auto &entry : list) - if (entry->identifier == code) - return entry; - return nullptr; + if(code[0] == '-') code = "UNKNOWN"; + auto &list = countryMapping[code[0] - 'A']; + for (const auto &entry : list) + if (entry->identifier == code) + return entry; + return nullptr; } std::shared_ptr LocationIPProvider::createCountryInfo(std::string code, const std::string &name) { - if(code[0] == '-') code = "UNKNOWN"; - auto info = resolveCountryInfo(code); - if(info) return info; - info = std::make_shared(code, name); - this->countryMapping[code[0] - 'A'].push_back(info); + if(code[0] == '-') code = "UNKNOWN"; + auto info = resolveCountryInfo(code); + if(info) return info; + info = std::make_shared(code, name); + this->countryMapping[code[0] - 'A'].push_back(info); - return info; + return info; } /** Software77Provider **/ @@ -29,37 +29,37 @@ geoloc::Software77Provider::Software77Provider(const std::string &file) : Locati geoloc::Software77Provider::~Software77Provider() = default; bool Software77Provider::parseSingleLine(const std::string &line, RangeEntryMapping &mapping) { - auto tokens = this->parseCVSLine(line, ','); + auto tokens = this->parseCVSLine(line, ','); - auto code = tokens[4]; - auto cinfo = this->resolveCountryInfo(code); - if(!cinfo) - cinfo = this->createCountryInfo(code, tokens[6]); - mapping.data = cinfo; - mapping.startAddress = static_cast(std::stoul(tokens[0])); - mapping.endAddress = -1; - return true; + auto code = tokens[4]; + auto cinfo = this->resolveCountryInfo(code); + if(!cinfo) + cinfo = this->createCountryInfo(code, tokens[6]); + mapping.data = cinfo; + mapping.startAddress = static_cast(std::stoul(tokens[0])); + mapping.endAddress = -1; + return true; } void Software77Provider::emit_line_parse_failed(const std::string &line) { - logError(LOG_GENERAL, "Software77Provider: Failed to parse line \"{}\". Skipping this line", line); + logError(LOG_GENERAL, "Software77Provider: Failed to parse line \"{}\". Skipping this line", line); } /** IP2LocationProvider **/ IP2LocationProvider::IP2LocationProvider(const std::string &file) : LocationIPProvider(file){} bool IP2LocationProvider::parseSingleLine(const std::string &line, RangeEntryMapping &mapping) { - auto tokens = this->parseCVSLine(line, ','); + auto tokens = this->parseCVSLine(line, ','); - auto code = tokens[2]; - auto cinfo = this->resolveCountryInfo(code); - if(!cinfo) - cinfo = this->createCountryInfo(code, tokens[3]); - mapping.data = cinfo; - mapping.startAddress = static_cast(std::stoul(tokens[0])); - mapping.endAddress = static_cast(std::stoul(tokens[1])); - return true; + auto code = tokens[2]; + auto cinfo = this->resolveCountryInfo(code); + if(!cinfo) + cinfo = this->createCountryInfo(code, tokens[3]); + mapping.data = cinfo; + mapping.startAddress = static_cast(std::stoul(tokens[0])); + mapping.endAddress = static_cast(std::stoul(tokens[1])); + return true; } void IP2LocationProvider::emit_line_parse_failed(const std::string &line) { - logError(LOG_GENERAL, "IP2LocationProvider: Failed to parse line \"{}\". Skipping this line", line); + logError(LOG_GENERAL, "IP2LocationProvider: Failed to parse line \"{}\". Skipping this line", line); } diff --git a/server/src/geo/VPNBlocker.cpp b/server/src/geo/VPNBlocker.cpp index ad7b7d8..afbdb65 100644 --- a/server/src/geo/VPNBlocker.cpp +++ b/server/src/geo/VPNBlocker.cpp @@ -8,26 +8,26 @@ using namespace geoloc; IPCatBlocker::IPCatBlocker(const std::string &file) : CVSFileBasedProvider(file) {} bool IPCatBlocker::parseSingleLine(const std::string &line, geoloc::RangeEntryMapping &mapping) { - auto tokens = this->parseCVSLine(line, ','); - if(tokens.size() != 4) return false; + auto tokens = this->parseCVSLine(line, ','); + if(tokens.size() != 4) return false; - mapping.data = this->get_or_create_info(tokens[2], tokens[3]); - mapping.startAddress = ip_swap_order(inet_addr(tokens[0].c_str())); - mapping.endAddress = ip_swap_order(inet_addr(tokens[1].c_str())); + mapping.data = this->get_or_create_info(tokens[2], tokens[3]); + mapping.startAddress = ip_swap_order(inet_addr(tokens[0].c_str())); + mapping.endAddress = ip_swap_order(inet_addr(tokens[1].c_str())); - return true; + return true; } std::shared_ptr IPCatBlocker::get_or_create_info(const std::string &hoster, const std::string &webside) { - for(const auto& info : this->hoster_info) - if(info->name == hoster && info->side == webside) - return info; - auto result = make_shared(hoster, webside); - this->hoster_info.push_back(result); - return result; + for(const auto& info : this->hoster_info) + if(info->name == hoster && info->side == webside) + return info; + auto result = make_shared(hoster, webside); + this->hoster_info.push_back(result); + return result; } void IPCatBlocker::emit_line_parse_failed(const std::string &line) { - logError(LOG_GENERAL, "IPCatBlocker: Failed to parse line \"{}\". Skipping this line", line); + logError(LOG_GENERAL, "IPCatBlocker: Failed to parse line \"{}\". Skipping this line", line); } diff --git a/server/src/lincense/LicenseHelper.cpp b/server/src/lincense/LicenseHelper.cpp index 24b60ff..1e77b00 100644 --- a/server/src/lincense/LicenseHelper.cpp +++ b/server/src/lincense/LicenseHelper.cpp @@ -28,7 +28,7 @@ LicenseHelper::~LicenseHelper() { lock.unlock(); request->abortRequest(); - request->callback_update_certificate = nullptr; + request->callback_update_certificate = nullptr; } } } @@ -50,7 +50,7 @@ void LicenseHelper::tick() { if(this->request.current) { auto promise = this->request.current->requestInfo(); if(promise.state() != threads::FutureState::WORKING){ - auto exception = this->request.current->exception(); + auto exception = this->request.current->exception(); if(promise.state() == threads::FutureState::FAILED) { this->handle_request_failed(verbose, exception ? exception->what() : strobf("unknown").c_str()); this->request.current = nullptr; /* connection should be already closed */ @@ -166,7 +166,7 @@ void LicenseHelper::do_request(bool verbose) { auto license_data = serverInstance->generateLicenseData(); auto request = make_shared(license_data, server_addr); request->verbose = false; - request->callback_update_certificate = [&](const auto& update) { this->callback_certificate_update(update); }; + request->callback_update_certificate = [&](const auto& update) { this->callback_certificate_update(update); }; { lock_guard lock(this->request.current_lock); this->request.current = request; @@ -200,5 +200,5 @@ void LicenseHelper::handle_request_failed(bool verbose, const std::string& error } void LicenseHelper::callback_certificate_update(const license::WebCertificate &certificate) { - serverInstance->setWebCertRoot(certificate.key, certificate.certificate, certificate.revision); + serverInstance->setWebCertRoot(certificate.key, certificate.certificate, certificate.revision); } \ No newline at end of file diff --git a/server/src/lincense/TeamSpeakLicense.cpp b/server/src/lincense/TeamSpeakLicense.cpp index ce2aa48..8bb53e4 100644 --- a/server/src/lincense/TeamSpeakLicense.cpp +++ b/server/src/lincense/TeamSpeakLicense.cpp @@ -20,134 +20,134 @@ TeamSpeakLicense::TeamSpeakLicense(const std::string& file) : file_name(file) {} extern bool file_exists(const std::string& name); 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; + 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; } #define TEST_KEY(key) \ if(properties.count(key) == 0) { \ - error = string("missing key ") + key; \ - return false; \ + error = string("missing key ") + key; \ + return false; \ } #define ERR(message) \ do { \ - this->data = nullptr; \ - error = message; \ - return false; \ + this->data = nullptr; \ + error = message; \ + return false; \ } while(0) bool TeamSpeakLicense::load(std::string& error) { - if(!file_exists(this->file_name)) { - ofstream os(this->file_name); - if(!os) { - error = "Could not create default file!"; - return false; - } - os << "# This is your auto generated TeaSpeak keychain." << endl; - os << "# DO NOT SHARE THESE DATA!" << endl; + if(!file_exists(this->file_name)) { + ofstream os(this->file_name); + if(!os) { + error = "Could not create default file!"; + return false; + } + os << "# This is your auto generated TeaSpeak keychain." << endl; + os << "# DO NOT SHARE THESE DATA!" << endl; - auto chain = make_shared(); - auto entry = chain->addServerEntry(teamspeak::SERVER_LICENSE_OFFLINE, "TeaSpeak", 1024); - if(!entry) { - error = "Could not create chain default entry!"; - return false; - } - entry->lifetime(hours(24 * 360), system_clock::now() - hours(12)); + auto chain = make_shared(); + auto entry = chain->addServerEntry(teamspeak::SERVER_LICENSE_OFFLINE, "TeaSpeak", 1024); + if(!entry) { + error = "Could not create chain default entry!"; + return false; + } + entry->lifetime(hours(24 * 360), system_clock::now() - hours(12)); - uint8_t root_private[32], root_public[32], root_seed[64]; - for(auto& e : root_seed) e = (uint8_t) rand(); - ed25519_create_keypair(root_public, root_private, root_seed); + uint8_t root_private[32], root_public[32], root_seed[64]; + for(auto& e : root_seed) e = (uint8_t) rand(); + ed25519_create_keypair(root_public, root_private, root_seed); - auto prv_root = chain->generatePrivateKey(root_private); + auto prv_root = chain->generatePrivateKey(root_private); - os << "version:0" << endl; - os << "chain:" << base64::encode(chain->exportChain()) << endl; - os << "root_key_prv:" << base64::encode(prv_root) << endl; - os << "root_key_pbl:" << base64::encode((const char*) root_public, 32) << endl; - os << "root_prv_index:" << to_string(1) << endl; - os.flush(); - os.close(); - } + os << "version:0" << endl; + os << "chain:" << base64::encode(chain->exportChain()) << endl; + os << "root_key_prv:" << base64::encode(prv_root) << endl; + os << "root_key_pbl:" << base64::encode((const char*) root_public, 32) << endl; + os << "root_prv_index:" << to_string(1) << endl; + os.flush(); + os.close(); + } - ifstream in(this->file_name); - if(!in) { - error = "could not open file"; - return false; - } + ifstream in(this->file_name); + if(!in) { + error = "could not open file"; + return false; + } - string line; - map properties; - while(getline(in, line)) { - line = strip(line); - if(line.empty() || line[0] == '#') continue; + string line; + map properties; + while(getline(in, line)) { + line = strip(line); + if(line.empty() || line[0] == '#') continue; - auto index = line.find(':'); - if(index == string::npos) continue; + auto index = line.find(':'); + if(index == string::npos) continue; - properties[line.substr(0, index)] = strip(line.substr(index + 1)); - } + properties[line.substr(0, index)] = strip(line.substr(index + 1)); + } - TEST_KEY("version"); - TEST_KEY("chain"); - TEST_KEY("root_key_prv"); - TEST_KEY("root_key_pbl"); - TEST_KEY("root_prv_index"); + TEST_KEY("version"); + TEST_KEY("chain"); + TEST_KEY("root_key_prv"); + TEST_KEY("root_key_pbl"); + TEST_KEY("root_prv_index"); - if(properties["version"] != "0") ERR("Invalid file version (" + properties["version"] + ")"); + if(properties["version"] != "0") ERR("Invalid file version (" + properties["version"] + ")"); - auto stream = istringstream(base64::decode(properties["chain"])); - auto chain = teamspeak::LicenseChain::parse(stream, error); - if(!chain) ERR("Could not parse chain (" + error + ")"); + auto stream = istringstream(base64::decode(properties["chain"])); + auto chain = teamspeak::LicenseChain::parse(stream, error); + if(!chain) ERR("Could not parse chain (" + error + ")"); - this->data.reset(new LicenseChainData{}); - this->data->chain = chain; + this->data.reset(new LicenseChainData{}); + this->data->chain = chain; - { - auto buffer = base64::decode(properties["root_key_prv"]); - if(buffer.length() != 32) ERR("Invalid root prv key length!"); - memcpy(this->data->root_key, buffer.data(), 32); - } - { - auto buffer = base64::decode(properties["root_key_pbl"]); - if(buffer.length() != 32) ERR("Invalid root pbl key length!"); - memcpy(this->data->public_key, buffer.data(), 32); - } + { + auto buffer = base64::decode(properties["root_key_prv"]); + if(buffer.length() != 32) ERR("Invalid root prv key length!"); + memcpy(this->data->root_key, buffer.data(), 32); + } + { + auto buffer = base64::decode(properties["root_key_pbl"]); + if(buffer.length() != 32) ERR("Invalid root pbl key length!"); + memcpy(this->data->public_key, buffer.data(), 32); + } - try { - this->data->root_index = stoi(properties["root_prv_index"]); - } catch (std::exception&) { - ERR("Could not parse prv root index"); - } + try { + this->data->root_index = stoi(properties["root_prv_index"]); + } catch (std::exception&) { + ERR("Could not parse prv root index"); + } - { - string message = "This is a test message for singing (WolverinDEV)"; - u_char sign[128]; - auto key_public = this->data->chain->generatePublicKey(this->data->public_key); - auto key_private = this->data->chain->generatePrivateKey(this->data->root_key, this->data->root_index); - key_private = key_private + key_private; //ed25519_sign requires 64 bytes (may it expects a public key in front?) + { + string message = "This is a test message for singing (WolverinDEV)"; + u_char sign[128]; + auto key_public = this->data->chain->generatePublicKey(this->data->public_key); + auto key_private = this->data->chain->generatePrivateKey(this->data->root_key, this->data->root_index); + key_private = key_private + key_private; //ed25519_sign requires 64 bytes (may it expects a public key in front?) - ed25519_sign(sign, (u_char*) message.data(), message.length(), (u_char*) key_public.data(), (u_char*) key_private.data()); - if(!ed25519_verify(sign, (u_char*) message.data(), message.length(), (u_char*) key_public.data())) ERR("Failed to sign and verify a simple test message. Keypair probably broken!"); - } + ed25519_sign(sign, (u_char*) message.data(), message.length(), (u_char*) key_public.data(), (u_char*) key_private.data()); + if(!ed25519_verify(sign, (u_char*) message.data(), message.length(), (u_char*) key_public.data())) ERR("Failed to sign and verify a simple test message. Keypair probably broken!"); + } - return true; + return true; } std::shared_ptr TeamSpeakLicense::license(bool copy) { - if(!copy || !this->data) return this->data; + if(!copy || !this->data) return this->data; - auto result = make_shared(); - result->chain = this->data->chain->copy(); - result->root_index = this->data->root_index; + auto result = make_shared(); + result->chain = this->data->chain->copy(); + result->root_index = this->data->root_index; - memcpy(result->root_key, data->root_key, 32); - memcpy(result->public_key, data->public_key, 32); - return result; + memcpy(result->root_key, data->root_key, 32); + memcpy(result->public_key, data->public_key, 32); + return result; } \ No newline at end of file diff --git a/server/src/lincense/TeamSpeakLicense.h b/server/src/lincense/TeamSpeakLicense.h index ac34191..16bf8a7 100644 --- a/server/src/lincense/TeamSpeakLicense.h +++ b/server/src/lincense/TeamSpeakLicense.h @@ -4,22 +4,22 @@ #include namespace ts { - struct LicenseChainData { - std::shared_ptr chain; - license::teamspeak::LicensePublicKey public_key; - license::teamspeak::LicensePublicKey root_key; - int root_index; - }; + struct LicenseChainData { + std::shared_ptr chain; + license::teamspeak::LicensePublicKey public_key; + license::teamspeak::LicensePublicKey root_key; + int root_index; + }; - class TeamSpeakLicense { - public: - TeamSpeakLicense(const std::string&); - bool load(std::string&); + class TeamSpeakLicense { + public: + TeamSpeakLicense(const std::string&); + bool load(std::string&); - inline std::string file() { return this->file_name; } - std::shared_ptr license(bool copy = true); - private: - std::string file_name; - std::shared_ptr data; - }; + inline std::string file() { return this->file_name; } + std::shared_ptr license(bool copy = true); + private: + std::string file_name; + std::shared_ptr data; + }; } \ No newline at end of file diff --git a/server/src/manager/BanManager.cpp b/server/src/manager/BanManager.cpp index be74d10..a6689a1 100644 --- a/server/src/manager/BanManager.cpp +++ b/server/src/manager/BanManager.cpp @@ -15,18 +15,18 @@ BanManager::BanManager(sql::SqlManager* handle) : sql(handle) {} BanManager::~BanManager() {} bool BanManager::loadBans() { - this->current_ban_index.store(0); + this->current_ban_index.store(0); try { - if(config::server::delete_old_bans) { - debugMessage(LOG_INSTANCE, "Deleting old bans"); - LOG_SQL_CMD(sql::command(this->sql, "DELETE FROM `bannedClients` WHERE `until` < :now AND `until` > 0", variable{":now", duration_cast(system_clock::now().time_since_epoch()).count()}).execute()); - } - LOG_SQL_CMD(sql::command(this->sql, "SELECT `banId` FROM `bannedClients` ORDER BY `banId` DESC LIMIT 1").query([](atomic& counter, int, string* values, string*) { - counter.store(stoll(values[0])); - }, this->current_ban_index)); + if(config::server::delete_old_bans) { + debugMessage(LOG_INSTANCE, "Deleting old bans"); + LOG_SQL_CMD(sql::command(this->sql, "DELETE FROM `bannedClients` WHERE `until` < :now AND `until` > 0", variable{":now", duration_cast(system_clock::now().time_since_epoch()).count()}).execute()); + } + LOG_SQL_CMD(sql::command(this->sql, "SELECT `banId` FROM `bannedClients` ORDER BY `banId` DESC LIMIT 1").query([](atomic& counter, int, string* values, string*) { + counter.store(stoll(values[0])); + }, this->current_ban_index)); } catch(std::exception& ex) { - logCritical(LOG_INSTANCE, "Failed to setup ban manager!"); - return false; + logCritical(LOG_INSTANCE, "Failed to setup ban manager!"); + return false; } return true; } @@ -44,48 +44,48 @@ inline deque> resolveBansByQuery(sql::command& comman res->reason = "undefined"; res->created = time_point(); res->until = time_point(); - res->triggered = 0; + res->triggered = 0; - int index; + int index; try { - for(index = 0; index < count; index++){ - string key = columnNames[index]; - if(key == "invokerDbId") - res->invokerDbId = stoull(values[index]); - else if(key == "hwid") - res->hwid = values[index]; - else if(key == "uid") - res->uid = values[index]; - else if(key == "name") - res->name = values[index]; - else if(key == "ip") - res->ip = values[index]; - else if(key == "strType") - res->strType = static_cast(stol(values[index])); - else if(key == "created") - res->created = time_point() + milliseconds(stol(values[index])); - else if(key == "until") - res->until = time_point() + milliseconds(stol(values[index])); - else if(key == "banId") - res->banId = stoull(values[index]); - else if(key == "reason") - res->reason = values[index]; - else if(key == "invName") - res->invokerName = values[index]; - else if(key == "invUid") - res->invokerUid = values[index]; - else if(key == "serverId") - res->serverId = stoll(values[index]); - else if(key == "triggered") - res->triggered = stoull(values[index]); - else cerr << "Invalid ban column " << key << endl; - } + for(index = 0; index < count; index++){ + string key = columnNames[index]; + if(key == "invokerDbId") + res->invokerDbId = stoull(values[index]); + else if(key == "hwid") + res->hwid = values[index]; + else if(key == "uid") + res->uid = values[index]; + else if(key == "name") + res->name = values[index]; + else if(key == "ip") + res->ip = values[index]; + else if(key == "strType") + res->strType = static_cast(stol(values[index])); + else if(key == "created") + res->created = time_point() + milliseconds(stol(values[index])); + else if(key == "until") + res->until = time_point() + milliseconds(stol(values[index])); + else if(key == "banId") + res->banId = stoull(values[index]); + else if(key == "reason") + res->reason = values[index]; + else if(key == "invName") + res->invokerName = values[index]; + else if(key == "invUid") + res->invokerUid = values[index]; + else if(key == "serverId") + res->serverId = stoll(values[index]); + else if(key == "triggered") + res->triggered = stoull(values[index]); + else cerr << "Invalid ban column " << key << endl; + } } catch(std::exception& ex) { - logError(LOG_GENERAL, "Failed to parse ban {}. (Exception while parsing row {} ({}): {})", res->banId, columnNames[index], values[index], ex.what()); - return 0; + logError(LOG_GENERAL, "Failed to parse ban {}. (Exception while parsing row {} ({}): {})", res->banId, columnNames[index], values[index], ex.what()); + return 0; } - list.push_back(std::move(res)); + list.push_back(std::move(res)); return 0; }, result); @@ -106,48 +106,48 @@ inline deque> resolveBanByQuery(sql::command& command){ #define SERVER_ID "(`serverId` = 0 OR `serverId` = :sid)" std::shared_ptr BanManager::findBanById(ServerId sid, uint64_t id) { auto cmd = sql::command(this->sql, "SELECT * FROM `bannedClients` WHERE " SERVER_ID " AND `banId` = :id" UNTIL_SQL, - variable{":time", duration_cast(system_clock::now().time_since_epoch()).count()}, - variable{":sid", sid}, - variable{":id", id}); + variable{":time", duration_cast(system_clock::now().time_since_epoch()).count()}, + variable{":sid", sid}, + variable{":id", id}); return resolveBanByQuery(cmd).back(); } std::shared_ptr BanManager::findBanByHwid(ServerId sid, std::string hwid) { - if(hwid.empty()) - hwid = "empty"; - auto cmd = sql::command(this->sql, "SELECT * FROM `bannedClients` WHERE " SERVER_ID " AND `hwid` = :hwid" UNTIL_SQL, - variable{":time", duration_cast(system_clock::now().time_since_epoch()).count()}, - variable{":sid", sid}, - variable{":hwid", hwid}); + if(hwid.empty()) + hwid = "empty"; + auto cmd = sql::command(this->sql, "SELECT * FROM `bannedClients` WHERE " SERVER_ID " AND `hwid` = :hwid" UNTIL_SQL, + variable{":time", duration_cast(system_clock::now().time_since_epoch()).count()}, + variable{":sid", sid}, + variable{":hwid", hwid}); return resolveBanByQuery(cmd).back(); } std::shared_ptr BanManager::findBanByUid(ServerId sid, std::string uid) { - if(uid.empty()) - uid = "empty"; - auto cmd = sql::command(this->sql, "SELECT * FROM `bannedClients` WHERE " SERVER_ID " AND `uid` = :uid" UNTIL_SQL, - variable{":time", duration_cast(system_clock::now().time_since_epoch()).count()}, - variable{":sid", sid}, - variable{":uid", uid}); + if(uid.empty()) + uid = "empty"; + auto cmd = sql::command(this->sql, "SELECT * FROM `bannedClients` WHERE " SERVER_ID " AND `uid` = :uid" UNTIL_SQL, + variable{":time", duration_cast(system_clock::now().time_since_epoch()).count()}, + variable{":sid", sid}, + variable{":uid", uid}); return resolveBanByQuery(cmd).back(); } std::shared_ptr BanManager::findBanByIp(ServerId sid, std::string ip) { - if(ip.empty()) - ip = "empty"; - auto cmd = sql::command(this->sql, "SELECT * FROM `bannedClients` WHERE " SERVER_ID " AND `ip` = :ip" UNTIL_SQL, - variable{":time", duration_cast(system_clock::now().time_since_epoch()).count()}, - variable{":sid", sid}, - variable{":ip", ip}); + if(ip.empty()) + ip = "empty"; + auto cmd = sql::command(this->sql, "SELECT * FROM `bannedClients` WHERE " SERVER_ID " AND `ip` = :ip" UNTIL_SQL, + variable{":time", duration_cast(system_clock::now().time_since_epoch()).count()}, + variable{":sid", sid}, + variable{":ip", ip}); return resolveBanByQuery(cmd).back(); } std::shared_ptr BanManager::findBanByName(ServerId sid, std::string nickName) { - auto cmd = sql::command(this->sql, "SELECT * FROM `bannedClients` WHERE " SERVER_ID " AND (`name` = :name OR `strType` = :type)" UNTIL_SQL, - variable{":time", duration_cast(system_clock::now().time_since_epoch()).count()}, - variable{":sid", sid}, - variable{":name", nickName}, - variable{":type", BanStringType::BST_REGEX}); + auto cmd = sql::command(this->sql, "SELECT * FROM `bannedClients` WHERE " SERVER_ID " AND (`name` = :name OR `strType` = :type)" UNTIL_SQL, + variable{":time", duration_cast(system_clock::now().time_since_epoch()).count()}, + variable{":sid", sid}, + variable{":name", nickName}, + variable{":type", BanStringType::BST_REGEX}); auto av = resolveBanByQuery(cmd); while(!av.empty()){ @@ -165,23 +165,23 @@ std::shared_ptr BanManager::findBanByName(ServerId sid, std::string n } std::shared_ptr BanManager::findBanExact(ts::ServerId server_id, const std::string &reason, const std::string &uid, const std::string &ip, const std::string &name, const std::string &hardware_id) { - auto cmd = sql::command(this->sql, "SELECT * FROM `bannedClients` WHERE " SERVER_ID " AND `ip` = :ip AND `strType` = :name_mode AND `name` = :name AND `uid` = :uid AND `hwid` = :hwid AND `reason` = :reason" UNTIL_SQL, - variable{":time", duration_cast(system_clock::now().time_since_epoch()).count()}, - variable{":sid", server_id}, - variable{":hwid", hardware_id}, - variable{":uid", uid}, - variable{":name", name}, - variable{":name_mode", BanStringType::BST_FIXED}, - variable{":ip", ip}, - variable{":reason", reason} + auto cmd = sql::command(this->sql, "SELECT * FROM `bannedClients` WHERE " SERVER_ID " AND `ip` = :ip AND `strType` = :name_mode AND `name` = :name AND `uid` = :uid AND `hwid` = :hwid AND `reason` = :reason" UNTIL_SQL, + variable{":time", duration_cast(system_clock::now().time_since_epoch()).count()}, + variable{":sid", server_id}, + variable{":hwid", hardware_id}, + variable{":uid", uid}, + variable{":name", name}, + variable{":name_mode", BanStringType::BST_FIXED}, + variable{":ip", ip}, + variable{":reason", reason} ); - return resolveBanByQuery(cmd).back(); + return resolveBanByQuery(cmd).back(); } std::deque> BanManager::listBans(ServerId sid) { - auto command = sql::command(this->sql, "SELECT `bannedClients`.*, clients.`clientUid` AS `invUid`, clients.`lastName` AS `invName` FROM `bannedClients` INNER JOIN clients ON clients.cldbid = bannedClients.invokerDbId AND clients.serverId = bannedClients.serverId WHERE bannedClients.`serverId` = :sid" UNTIL_SQL, - variable{":time", duration_cast(system_clock::now().time_since_epoch()).count()}, - variable{":sid", sid}); + auto command = sql::command(this->sql, "SELECT `bannedClients`.*, clients.`clientUid` AS `invUid`, clients.`lastName` AS `invName` FROM `bannedClients` INNER JOIN clients ON clients.cldbid = bannedClients.invokerDbId AND clients.serverId = bannedClients.serverId WHERE bannedClients.`serverId` = :sid" UNTIL_SQL, + variable{":time", duration_cast(system_clock::now().time_since_epoch()).count()}, + variable{":sid", sid}); return resolveBansByQuery(command); } @@ -192,11 +192,11 @@ void BanManager::deleteAllBans(ServerId sid) { } BanId BanManager::registerBan(ServerId id, uint64_t invoker, string reason, std::string uid, std::string ip, std::string nickName, std::string hwid, std::chrono::time_point until) { - if(ip.empty() && uid.empty() && hwid.empty() && nickName.empty()) { - logError(0, "Tried to register an empty ban. Reject ban."); - return 0; - } - const BanId ban_id = ++this->current_ban_index; + if(ip.empty() && uid.empty() && hwid.empty() && nickName.empty()) { + logError(0, "Tried to register an empty ban. Reject ban."); + return 0; + } + const BanId ban_id = ++this->current_ban_index; sql::command(this->sql, "INSERT INTO `bannedClients` (`banId`, `serverId`, `invokerDbId`, `reason`, `hwid`, `uid`, `name`, `ip`, `strType`, `created`, `until`) VALUES (:bid, :sid, :invoker, :reason, :hwid, :uid, :name, :ip, :strType, :create, :until)", variable{":sid", id}, variable{":bid", ban_id}, @@ -256,60 +256,60 @@ void BanManager::trigger_ban(const std::shared_ptr& record, const std::string& hardware_id, const std::string& nickname, const std::string& ip) { - sql::command(this->sql, "INSERT INTO `ban_trigger` (`server_id`, `ban_id`, `unique_id`, `hardware_id`, `name`, `ip`, `timestamp`) VALUES (:sid, :bid, :uid, :hid, :name, :ip, :timestamp)", - variable{":sid", server}, - variable{":bid", record->banId}, - variable{":uid", unique_id}, - variable{":hid", hardware_id}, - variable{":name", nickname}, - variable{":ip", ip}, - variable{":timestamp", duration_cast(chrono::system_clock::now().time_since_epoch()).count()} - ).executeLater().waitAndGetLater(LOG_SQL_CMD, {1, "future failed"}); + sql::command(this->sql, "INSERT INTO `ban_trigger` (`server_id`, `ban_id`, `unique_id`, `hardware_id`, `name`, `ip`, `timestamp`) VALUES (:sid, :bid, :uid, :hid, :name, :ip, :timestamp)", + variable{":sid", server}, + variable{":bid", record->banId}, + variable{":uid", unique_id}, + variable{":hid", hardware_id}, + variable{":name", nickname}, + variable{":ip", ip}, + variable{":timestamp", duration_cast(chrono::system_clock::now().time_since_epoch()).count()} + ).executeLater().waitAndGetLater(LOG_SQL_CMD, {1, "future failed"}); - record->triggered++; - sql::command(this->sql, "UPDATE `bannedClients` SET `triggered` = :triggered WHERE `banId` = :banId AND `serverId` = :sid", - variable{":sid", record->serverId}, - variable{":banId", record->banId}, - variable{":triggered", record->triggered} - ).executeLater().waitAndGetLater(LOG_SQL_CMD, {1, "future failed"}); + record->triggered++; + sql::command(this->sql, "UPDATE `bannedClients` SET `triggered` = :triggered WHERE `banId` = :banId AND `serverId` = :sid", + variable{":sid", record->serverId}, + variable{":banId", record->banId}, + variable{":triggered", record->triggered} + ).executeLater().waitAndGetLater(LOG_SQL_CMD, {1, "future failed"}); } std::deque> BanManager::trigger_list(const std::shared_ptr &record, ServerId server_id, ssize_t offset, ssize_t length) { - std::deque> result; + std::deque> result; - if(offset < 0) offset = 0; - if(length <= 0) length = -1; - LOG_SQL_CMD(sql::command(this->sql, "SELECT `unique_id`, `hardware_id`, `name`, `ip`, `timestamp` FROM `ban_trigger` WHERE `server_id` = :sid AND `ban_id` = :bid LIMIT :offset, :limit", - variable{":sid", server_id}, - variable{":bid", record->banId}, - variable{":offset", offset}, - variable{":limit", length} - ).query([server_id, &record](std::deque>& list, int length, string* values, string* names){ - auto entry = make_shared(); - entry->server_id = server_id; - entry->ban_id = record->banId; - int index; - try { - for(index = 0; index < length; index++) { - if(names[index] == "unique_id") - entry->unique_id = values[index]; - else if(names[index] == "hardware_id") - entry->hardware_id = values[index]; - else if(names[index] == "name") - entry->name = values[index]; - else if(names[index] == "ip") - entry->ip = values[index]; - else if(names[index] == "timestamp") - entry->timestamp = system_clock::time_point() + milliseconds(stoll(values[index])); - } - } catch(std::exception& ex) { - logError(LOG_GENERAL, "Failed to parse ban trigger {}. (Exception while parsing row {} ({}): {})", entry->ban_id, names[index], values[index], ex.what()); - return 0; - } + if(offset < 0) offset = 0; + if(length <= 0) length = -1; + LOG_SQL_CMD(sql::command(this->sql, "SELECT `unique_id`, `hardware_id`, `name`, `ip`, `timestamp` FROM `ban_trigger` WHERE `server_id` = :sid AND `ban_id` = :bid LIMIT :offset, :limit", + variable{":sid", server_id}, + variable{":bid", record->banId}, + variable{":offset", offset}, + variable{":limit", length} + ).query([server_id, &record](std::deque>& list, int length, string* values, string* names){ + auto entry = make_shared(); + entry->server_id = server_id; + entry->ban_id = record->banId; + int index; + try { + for(index = 0; index < length; index++) { + if(names[index] == "unique_id") + entry->unique_id = values[index]; + else if(names[index] == "hardware_id") + entry->hardware_id = values[index]; + else if(names[index] == "name") + entry->name = values[index]; + else if(names[index] == "ip") + entry->ip = values[index]; + else if(names[index] == "timestamp") + entry->timestamp = system_clock::time_point() + milliseconds(stoll(values[index])); + } + } catch(std::exception& ex) { + logError(LOG_GENERAL, "Failed to parse ban trigger {}. (Exception while parsing row {} ({}): {})", entry->ban_id, names[index], values[index], ex.what()); + return 0; + } - list.push_back(entry); - return 0; + list.push_back(entry); + return 0; }, result)); - return result; + return result; } \ No newline at end of file diff --git a/server/src/manager/BanManager.h b/server/src/manager/BanManager.h index 0685c36..a1e76cf 100644 --- a/server/src/manager/BanManager.h +++ b/server/src/manager/BanManager.h @@ -51,14 +51,14 @@ namespace ts { }; struct BanTrigger { - ServerId server_id; - BanId ban_id; - std::chrono::system_clock::time_point timestamp; + ServerId server_id; + BanId ban_id; + std::chrono::system_clock::time_point timestamp; - std::string unique_id; - std::string hardware_id; - std::string name; - std::string ip; + std::string unique_id; + std::string hardware_id; + std::string name; + std::string ip; }; class BanManager { @@ -84,7 +84,7 @@ namespace ts { void deleteAllBans(ServerId sid); - BanId registerBan(ServerId, ClientDbId invoker, std::string reason, std::string uid, std::string ip, std::string nickName, std::string hwid, std::chrono::time_point until); + BanId registerBan(ServerId, ClientDbId invoker, std::string reason, std::string uid, std::string ip, std::string nickName, std::string hwid, std::chrono::time_point until); void unban(ServerId, BanId); void unban(std::shared_ptr); void updateBan(std::shared_ptr); diff --git a/server/src/manager/ConversationManager.cpp b/server/src/manager/ConversationManager.cpp index eba2000..c02227c 100644 --- a/server/src/manager/ConversationManager.cpp +++ b/server/src/manager/ConversationManager.cpp @@ -193,1068 +193,1068 @@ namespace fs = std::experimental::filesystem; 0x555555c41ed7 ret #endif __attribute__((optimize("-O3"), always_inline)) void apply_crypt(void* source, void* target, size_t length, uint64_t base_key) { - uint64_t crypt_key = base_key; - size_t length_left = length; - auto source_ptr = (uint8_t*) source; - auto dest_ptr = (uint8_t*) target; + uint64_t crypt_key = base_key; + size_t length_left = length; + auto source_ptr = (uint8_t*) source; + auto dest_ptr = (uint8_t*) target; - while(length_left >= 8) { - crypt_key ^= (crypt_key << (length_left & 0x7U)) ^ length_left; - *(uint64_t*) dest_ptr = *(uint64_t*) source_ptr ^ crypt_key; + while(length_left >= 8) { + crypt_key ^= (crypt_key << (length_left & 0x7U)) ^ length_left; + *(uint64_t*) dest_ptr = *(uint64_t*) source_ptr ^ crypt_key; - dest_ptr += 8; - source_ptr += 8; - length_left -= 8; - } - while(length_left > 0) { - crypt_key ^= (crypt_key << (length_left & 0x7U)) ^ length_left; - *dest_ptr = *source_ptr ^ (uint8_t) crypt_key; + dest_ptr += 8; + source_ptr += 8; + length_left -= 8; + } + while(length_left > 0) { + crypt_key ^= (crypt_key << (length_left & 0x7U)) ^ length_left; + *dest_ptr = *source_ptr ^ (uint8_t) crypt_key; - length_left--; - source_ptr++; - dest_ptr++; - } + length_left--; + source_ptr++; + dest_ptr++; + } } Conversation::Conversation(const std::shared_ptr &handle, ts::ChannelId channel_id, std::string file) : _ref_handle(handle), _channel_id(channel_id), file_name(std::move(file)) { } Conversation::~Conversation() { - this->finalize(); + this->finalize(); } bool Conversation::initialize(std::string& error) { - auto ref_self = this->_ref_self.lock(); - assert(ref_self); + auto ref_self = this->_ref_self.lock(); + assert(ref_self); - auto handle = this->_ref_handle.lock(); - assert(handle); + auto handle = this->_ref_handle.lock(); + assert(handle); - auto ref_server = handle->ref_server(); - if(!ref_server) { - error = "invalid server ref"; - return false; - } + auto ref_server = handle->ref_server(); + if(!ref_server) { + error = "invalid server ref"; + return false; + } - auto file = fs::u8path(this->file_name); - if(!fs::is_directory(file.parent_path())) { - debugMessage(ref_server->getServerId(), "[Conversations] Creating conversation containing directory {}", file.parent_path().string()); - try { - fs::create_directories(file.parent_path()); - } catch(fs::filesystem_error& ex) { - error = "failed to create data directories (" + to_string(ex.code().value()) + "|" + string(ex.what()) + ")"; - return false; - } - } + auto file = fs::u8path(this->file_name); + if(!fs::is_directory(file.parent_path())) { + debugMessage(ref_server->getServerId(), "[Conversations] Creating conversation containing directory {}", file.parent_path().string()); + try { + fs::create_directories(file.parent_path()); + } catch(fs::filesystem_error& ex) { + error = "failed to create data directories (" + to_string(ex.code().value()) + "|" + string(ex.what()) + ")"; + return false; + } + } - this->file_handle = fopen(this->file_name.c_str(), fs::exists(file) ? "r+" : "w+"); - if(!this->file_handle) { - this->_volatile = true; - error = "failed to open file"; - return false; - } - setbuf(this->file_handle, nullptr); /* we're doing random access (a buffer is useless here) */ + this->file_handle = fopen(this->file_name.c_str(), fs::exists(file) ? "r+" : "w+"); + if(!this->file_handle) { + this->_volatile = true; + error = "failed to open file"; + return false; + } + setbuf(this->file_handle, nullptr); /* we're doing random access (a buffer is useless here) */ - auto sql = ref_server->getSql(); + auto sql = ref_server->getSql(); - auto result = sql::command(sql, "SELECT `begin_timestamp`, `end_timestamp`, `block_offset`, `flags` FROM `conversation_blocks` WHERE `server_id` = :sid AND `conversation_id` = :cid", - variable{":sid", ref_server->getServerId()}, variable{":cid", this->_channel_id}).query([&](int count, std::string* values, std::string* names) { - std::chrono::system_clock::time_point begin_timestamp{}, end_timestamp{}; - uint64_t block_offset = 0; - uint16_t flags = 0; + auto result = sql::command(sql, "SELECT `begin_timestamp`, `end_timestamp`, `block_offset`, `flags` FROM `conversation_blocks` WHERE `server_id` = :sid AND `conversation_id` = :cid", + variable{":sid", ref_server->getServerId()}, variable{":cid", this->_channel_id}).query([&](int count, std::string* values, std::string* names) { + std::chrono::system_clock::time_point begin_timestamp{}, end_timestamp{}; + uint64_t block_offset = 0; + uint16_t flags = 0; - try { - for(int index = 0; index < count; index++) { - if(names[index] == "begin_timestamp") - begin_timestamp += milliseconds(stoll(values[index])); - else if(names[index] == "end_timestamp") - end_timestamp += milliseconds(stoll(values[index])); - else if(names[index] == "block_offset") - block_offset = stoull(values[index]); - else if(names[index] == "flags") - flags = (uint16_t) stoll(values[index]); - } - } catch(std::exception& ex) { - logError(ref_server->getServerId(), "[Conversations] Failed to parse conversation block entry! Exception: {}", ex.what()); - return 0; - } + try { + for(int index = 0; index < count; index++) { + if(names[index] == "begin_timestamp") + begin_timestamp += milliseconds(stoll(values[index])); + else if(names[index] == "end_timestamp") + end_timestamp += milliseconds(stoll(values[index])); + else if(names[index] == "block_offset") + block_offset = stoull(values[index]); + else if(names[index] == "flags") + flags = (uint16_t) stoll(values[index]); + } + } catch(std::exception& ex) { + logError(ref_server->getServerId(), "[Conversations] Failed to parse conversation block entry! Exception: {}", ex.what()); + return 0; + } - auto block = make_shared(db::MessageBlock{ - begin_timestamp, - end_timestamp, + auto block = make_shared(db::MessageBlock{ + begin_timestamp, + end_timestamp, - block_offset, - {flags}, + block_offset, + {flags}, - nullptr, - nullptr - }); + nullptr, + nullptr + }); - /* we dont load invalid blocks */ - if(block->flag_invalid) - return 0; + /* we dont load invalid blocks */ + if(block->flag_invalid) + return 0; - this->message_blocks.push_back(block); - return 0; - }); - LOG_SQL_CMD(result); + this->message_blocks.push_back(block); + return 0; + }); + LOG_SQL_CMD(result); - /* find duplicates and remove them */ - { - map> blocks; - for(auto& block : this->message_blocks) { - auto& entry = blocks[block->block_offset]; - if(entry) { - debugMessage(ref_server->getServerId(), "[Conversations][{}] Found duplicated block at index {}. Using newest block and dropping old one.", this->_channel_id, block->block_offset); - if(entry->begin_timestamp < block->begin_timestamp) { - entry->flag_invalid = true; - this->db_save_block(entry); - entry = block; - } else { - block->flag_invalid = true; - this->db_save_block(block); - } - } else - entry = block; - } + /* find duplicates and remove them */ + { + map> blocks; + for(auto& block : this->message_blocks) { + auto& entry = blocks[block->block_offset]; + if(entry) { + debugMessage(ref_server->getServerId(), "[Conversations][{}] Found duplicated block at index {}. Using newest block and dropping old one.", this->_channel_id, block->block_offset); + if(entry->begin_timestamp < block->begin_timestamp) { + entry->flag_invalid = true; + this->db_save_block(entry); + entry = block; + } else { + block->flag_invalid = true; + this->db_save_block(block); + } + } else + entry = block; + } - /* lets remove the invalid blocks */ - this->message_blocks.erase(std::find_if(this->message_blocks.begin(), this->message_blocks.end(), [](const shared_ptr& block) { return block->flag_invalid; }), this->message_blocks.end()); - } + /* lets remove the invalid blocks */ + this->message_blocks.erase(std::find_if(this->message_blocks.begin(), this->message_blocks.end(), [](const shared_ptr& block) { return block->flag_invalid; }), this->message_blocks.end()); + } - /* lets find the last block */ - if(!this->message_blocks.empty()) { - debugMessage(ref_server->getServerId(), "[Conversations][{}] Loaded {} blocks. Trying to find last block.", this->_channel_id, this->message_blocks.size()); - deque> open_blocks; - for(auto& block : this->message_blocks) - if(!block->flag_finished) - open_blocks.push_back(block); + /* lets find the last block */ + if(!this->message_blocks.empty()) { + debugMessage(ref_server->getServerId(), "[Conversations][{}] Loaded {} blocks. Trying to find last block.", this->_channel_id, this->message_blocks.size()); + deque> open_blocks; + for(auto& block : this->message_blocks) + if(!block->flag_finished) + open_blocks.push_back(block); - logTrace(ref_server->getServerId(), "[Conversations][{}] Found {} unfinished blocks. Searching for the \"latest\" and closing all previous blocks.", this->_channel_id, open_blocks.size()); - shared_ptr latest_block; - const auto calculate_latest_block = [&](bool open_only) { - latest_block = nullptr; - for(auto& block : open_blocks) { - if(block->flag_invalid) - continue; + logTrace(ref_server->getServerId(), "[Conversations][{}] Found {} unfinished blocks. Searching for the \"latest\" and closing all previous blocks.", this->_channel_id, open_blocks.size()); + shared_ptr latest_block; + const auto calculate_latest_block = [&](bool open_only) { + latest_block = nullptr; + for(auto& block : open_blocks) { + if(block->flag_invalid) + continue; - if(!latest_block || latest_block->begin_timestamp < block->begin_timestamp) { - if(latest_block) { - latest_block->flag_finished_later = true; - latest_block->flag_finished = true; - this->db_save_block(latest_block); - } + if(!latest_block || latest_block->begin_timestamp < block->begin_timestamp) { + if(latest_block) { + latest_block->flag_finished_later = true; + latest_block->flag_finished = true; + this->db_save_block(latest_block); + } - latest_block = block; - } - } - }; - calculate_latest_block(true); + latest_block = block; + } + } + }; + calculate_latest_block(true); - if(latest_block) { - logTrace(ref_server->getServerId(), "[Conversations][{}] Found a latest block at index {}. Verify block with file.", this->_channel_id, latest_block->block_offset); + if(latest_block) { + logTrace(ref_server->getServerId(), "[Conversations][{}] Found a latest block at index {}. Verify block with file.", this->_channel_id, latest_block->block_offset); - const auto verify_block = [&] { - auto result = fseek(this->file_handle, 0, SEEK_END); - if(result != 0) { - error = "failed to seek to the end (" + to_string(result) + " | " + to_string(errno) + ")"; - return; - } + const auto verify_block = [&] { + auto result = fseek(this->file_handle, 0, SEEK_END); + if(result != 0) { + error = "failed to seek to the end (" + to_string(result) + " | " + to_string(errno) + ")"; + return; + } - auto file_size = ftell(this->file_handle); - if(file_size < 0) { - error = "failed to tell the end position (" + to_string(file_size) + " | " + to_string(errno) + ")"; - return; - } - logTrace(ref_server->getServerId(), "[Conversations][{}] File total size {}, last block index {}", this->_channel_id, file_size, latest_block->block_offset); - if(file_size < (latest_block->block_offset + sizeof(fio::BlockHeader))) { - latest_block->flag_finished_later = true; - latest_block->flag_invalid = true; - this->finish_block(latest_block, false); + auto file_size = ftell(this->file_handle); + if(file_size < 0) { + error = "failed to tell the end position (" + to_string(file_size) + " | " + to_string(errno) + ")"; + return; + } + logTrace(ref_server->getServerId(), "[Conversations][{}] File total size {}, last block index {}", this->_channel_id, file_size, latest_block->block_offset); + if(file_size < (latest_block->block_offset + sizeof(fio::BlockHeader))) { + latest_block->flag_finished_later = true; + latest_block->flag_invalid = true; + this->finish_block(latest_block, false); - logTrace(ref_server->getServerId(), "[Conversations][{}] File total size is less than block requires. Appending a new block at the end of the file.", this->_channel_id, latest_block->block_offset); - latest_block = nullptr; - return; - } + logTrace(ref_server->getServerId(), "[Conversations][{}] File total size is less than block requires. Appending a new block at the end of the file.", this->_channel_id, latest_block->block_offset); + latest_block = nullptr; + return; + } - if(!this->load_message_block_header(latest_block, error)) { - latest_block->flag_finished_later = true; - latest_block->flag_invalid = true; - this->finish_block(latest_block, false); + if(!this->load_message_block_header(latest_block, error)) { + latest_block->flag_finished_later = true; + latest_block->flag_invalid = true; + this->finish_block(latest_block, false); - logTrace(ref_server->getServerId(), "[Conversations][{}] Failed to load latest block at file index {}: {}. Appending an new one.", this->_channel_id, latest_block->block_offset, error); - error = ""; - latest_block = nullptr; - return; - } + logTrace(ref_server->getServerId(), "[Conversations][{}] Failed to load latest block at file index {}: {}. Appending an new one.", this->_channel_id, latest_block->block_offset, error); + error = ""; + latest_block = nullptr; + return; + } - /* We've a valid last block. Now the general write function could decide if we want a new block. */ - this->last_message_block = latest_block; - logTrace(ref_server->getServerId(), "[Conversations][{}] Last db saved block valid. Reusing it.", this->_channel_id, latest_block->block_offset, error); - }; - verify_block(); - if(!error.empty()) { - latest_block->flag_finished_later = true; - latest_block->flag_invalid = true; - this->finish_block(latest_block, false); + /* We've a valid last block. Now the general write function could decide if we want a new block. */ + this->last_message_block = latest_block; + logTrace(ref_server->getServerId(), "[Conversations][{}] Last db saved block valid. Reusing it.", this->_channel_id, latest_block->block_offset, error); + }; + verify_block(); + if(!error.empty()) { + latest_block->flag_finished_later = true; + latest_block->flag_invalid = true; + this->finish_block(latest_block, false); - logError(ref_server->getServerId(), "[Conversations][{}] Could not verify last block. Appending a new one at the end of the file.", this->_channel_id); - latest_block = nullptr; - } - error = ""; - } else { - logTrace(ref_server->getServerId(), "[Conversations][{}] Found no open last block. Using a new one.", this->_channel_id); - } - } else { - debugMessage(ref_server->getServerId(), "[Conversations][{}] Found no blocks. Creating new at the end of the file.", this->_channel_id, this->message_blocks.size()); - } + logError(ref_server->getServerId(), "[Conversations][{}] Could not verify last block. Appending a new one at the end of the file.", this->_channel_id); + latest_block = nullptr; + } + error = ""; + } else { + logTrace(ref_server->getServerId(), "[Conversations][{}] Found no open last block. Using a new one.", this->_channel_id); + } + } else { + debugMessage(ref_server->getServerId(), "[Conversations][{}] Found no blocks. Creating new at the end of the file.", this->_channel_id, this->message_blocks.size()); + } - std::stable_sort(this->message_blocks.begin(), this->message_blocks.end(), [](const shared_ptr& a, const shared_ptr& b) { return a->begin_timestamp < b->begin_timestamp; }); - this->_write_event = make_shared>(ts::event::ProxiedEventEntry(ref_self, &Conversation::process_write_queue)); + std::stable_sort(this->message_blocks.begin(), this->message_blocks.end(), [](const shared_ptr& a, const shared_ptr& b) { return a->begin_timestamp < b->begin_timestamp; }); + this->_write_event = make_shared>(ts::event::ProxiedEventEntry(ref_self, &Conversation::process_write_queue)); - /* set the last message timestamp property */ - { - auto last_message = this->message_history(1); - if(!last_message.empty()) - this->_last_message_timestamp = last_message.back()->message_timestamp; - else - this->_last_message_timestamp = system_clock::time_point{}; - } - /* close the file handle because we've passed our checks */ - { - fclose(this->file_handle); - this->file_handle = nullptr; - } - return true; + /* set the last message timestamp property */ + { + auto last_message = this->message_history(1); + if(!last_message.empty()) + this->_last_message_timestamp = last_message.back()->message_timestamp; + else + this->_last_message_timestamp = system_clock::time_point{}; + } + /* close the file handle because we've passed our checks */ + { + fclose(this->file_handle); + this->file_handle = nullptr; + } + return true; } void Conversation::finalize() { - this->_write_event = nullptr; /* we dont want to schedule/execute new events! */ - this->_write_loop_lock.lock(); /* wait until current write proceed */ - this->_write_loop_lock.unlock(); + this->_write_event = nullptr; /* we dont want to schedule/execute new events! */ + this->_write_loop_lock.lock(); /* wait until current write proceed */ + this->_write_loop_lock.unlock(); - //TODO: May flush? + //TODO: May flush? - { - lock_guard lock(this->message_block_lock); - this->message_blocks.clear(); - } + { + lock_guard lock(this->message_block_lock); + this->message_blocks.clear(); + } } void Conversation::cleanup_cache() { - auto ref_handle = this->ref_handle(); - if(!ref_handle) - return; - auto ref_server = ref_handle->ref_server(); - if(!ref_server) - return; + auto ref_handle = this->ref_handle(); + if(!ref_handle) + return; + auto ref_server = ref_handle->ref_server(); + if(!ref_server) + return; - { - lock_guard block(this->message_block_lock); - for(auto& block : this->message_blocks) { - block->block_header = nullptr; - block->indexed_block = nullptr; - } - } - { - lock_guard file_lock(this->file_handle_lock); - if(this->last_access + minutes(5) < system_clock::now()) { - if(this->file_handle) { - fclose(this->file_handle); - this->file_handle = nullptr; - debugMessage(ref_server->getServerId(), "[Conversations][{}] Closing file handle due to inactivity.", this->_channel_id); - } - } - } + { + lock_guard block(this->message_block_lock); + for(auto& block : this->message_blocks) { + block->block_header = nullptr; + block->indexed_block = nullptr; + } + } + { + lock_guard file_lock(this->file_handle_lock); + if(this->last_access + minutes(5) < system_clock::now()) { + if(this->file_handle) { + fclose(this->file_handle); + this->file_handle = nullptr; + debugMessage(ref_server->getServerId(), "[Conversations][{}] Closing file handle due to inactivity.", this->_channel_id); + } + } + } } bool Conversation::setup_file() { - this->file_handle = fopen(this->file_name.c_str(), fs::exists(this->file_name) ? "r+" : "w+"); - if(!this->file_handle) { - auto ref_handle = this->ref_handle(); - if(!ref_handle) - return false; - auto ref_server = ref_handle->ref_server(); - if(!ref_server) - return false; + this->file_handle = fopen(this->file_name.c_str(), fs::exists(this->file_name) ? "r+" : "w+"); + if(!this->file_handle) { + auto ref_handle = this->ref_handle(); + if(!ref_handle) + return false; + auto ref_server = ref_handle->ref_server(); + if(!ref_server) + return false; - logError(ref_server->getServerId(), "[Conversations][{}] Failed to open closed file handle. ({} | {})", errno, strerror(errno)); - return false; - } - setbuf(this->file_handle, nullptr); /* we're doing random access (a buffer is useless here) */ - return true; + logError(ref_server->getServerId(), "[Conversations][{}] Failed to open closed file handle. ({} | {})", errno, strerror(errno)); + return false; + } + setbuf(this->file_handle, nullptr); /* we're doing random access (a buffer is useless here) */ + return true; } ssize_t Conversation::fread(void *target, size_t length, ssize_t index, bool acquire_lock) { - if(length == 0) - return 0; + if(length == 0) + return 0; - unique_lock file_lock(this->file_handle_lock, defer_lock); - if(acquire_lock) - file_lock.lock(); - this->last_access = system_clock::now(); - if(!this->file_handle && !this->setup_file()) - return -3; - if(index >= 0) { - auto result = fseek(this->file_handle, index, SEEK_SET); - if(result < 0) - return -2; - } + unique_lock file_lock(this->file_handle_lock, defer_lock); + if(acquire_lock) + file_lock.lock(); + this->last_access = system_clock::now(); + if(!this->file_handle && !this->setup_file()) + return -3; + if(index >= 0) { + auto result = fseek(this->file_handle, index, SEEK_SET); + if(result < 0) + return -2; + } - size_t total_read = 0; - while(total_read < length) { - auto read = ::fread_unlocked((char*) target + total_read, 1, length - total_read, this->file_handle); - if(read <= 0) - return -1; - total_read += read; - } - return total_read; + size_t total_read = 0; + while(total_read < length) { + auto read = ::fread_unlocked((char*) target + total_read, 1, length - total_read, this->file_handle); + if(read <= 0) + return -1; + total_read += read; + } + return total_read; } ssize_t Conversation::fwrite(void *target, size_t length, ssize_t index, bool extend_file, bool acquire_lock) { - if(length == 0) - return 0; + if(length == 0) + return 0; - unique_lock file_lock(this->file_handle_lock, defer_lock); - if(acquire_lock) - file_lock.lock(); - extend_file = false; /* fseek does the job good ad well */ - if(!this->file_handle && !this->setup_file()) - return -3; - this->last_access = system_clock::now(); - if(index >= 0) { - auto result = extend_file ? lseek(fileno(this->file_handle), index, SEEK_SET) : fseek(this->file_handle, index, SEEK_SET); - if(result < 0) - return -2; - } + unique_lock file_lock(this->file_handle_lock, defer_lock); + if(acquire_lock) + file_lock.lock(); + extend_file = false; /* fseek does the job good ad well */ + if(!this->file_handle && !this->setup_file()) + return -3; + this->last_access = system_clock::now(); + if(index >= 0) { + auto result = extend_file ? lseek(fileno(this->file_handle), index, SEEK_SET) : fseek(this->file_handle, index, SEEK_SET); + if(result < 0) + return -2; + } - size_t total_written = 0; - while(total_written < length) { - auto written = ::fwrite_unlocked((char*) target + total_written, 1, length - total_written, this->file_handle); - if(written <= 0) - return -1; - total_written += written; - } - return total_written; + size_t total_written = 0; + while(total_written < length) { + auto written = ::fwrite_unlocked((char*) target + total_written, 1, length - total_written, this->file_handle); + if(written <= 0) + return -1; + total_written += written; + } + return total_written; } bool Conversation::load_message_block_header(const std::shared_ptr &block, std::string &error) { - if(block->block_header) - return true; + if(block->block_header) + return true; - auto block_header = make_unique(); - auto read = this->fread(&*block_header, sizeof(*block_header), block->block_offset, true); - if(read != sizeof(*block_header)) { - error = "failed to read block header (read " + to_string(read) + " out of " + to_string(sizeof(*block_header)) + ": " + to_string(errno) + ")"; - return false; - } - if(block_header->version != 1) { - error = "block version missmatch (block version: " + to_string(block_header->version) + ", current version: 1)"; - return false; - } + auto block_header = make_unique(); + auto read = this->fread(&*block_header, sizeof(*block_header), block->block_offset, true); + if(read != sizeof(*block_header)) { + error = "failed to read block header (read " + to_string(read) + " out of " + to_string(sizeof(*block_header)) + ": " + to_string(errno) + ")"; + return false; + } + if(block_header->version != 1) { + error = "block version missmatch (block version: " + to_string(block_header->version) + ", current version: 1)"; + return false; + } - if(block_header->cookie != fio::BlockHeader::HEADER_COOKIE) { - error = "block cookie missmatch"; - return false; - } + if(block_header->cookie != fio::BlockHeader::HEADER_COOKIE) { + error = "block cookie missmatch"; + return false; + } - block->block_header = move(block_header); - return true; + block->block_header = move(block_header); + return true; } bool Conversation::load_message_block_index(const std::shared_ptr &block, std::string& error) { - if(block->indexed_block) - return true; + if(block->indexed_block) + return true; - auto index = make_shared(); - index->index_successful = false; - { - if(!this->load_message_block_header(block, error)) { - error = "failed to load block header: " + error; - return false; - } - auto block_header = block->block_header; - if(!block_header) { - error = "failed to reference block header "; - return false; - } + auto index = make_shared(); + index->index_successful = false; + { + if(!this->load_message_block_header(block, error)) { + error = "failed to load block header: " + error; + return false; + } + auto block_header = block->block_header; + if(!block_header) { + error = "failed to reference block header "; + return false; + } - if(block_header->block_size > fio::BlockHeader::MAX_BLOCK_SIZE) { - error = "block contains too many messages (" + to_string(block_header->block_size) + ")"; - return false; - } + if(block_header->block_size > fio::BlockHeader::MAX_BLOCK_SIZE) { + error = "block contains too many messages (" + to_string(block_header->block_size) + ")"; + return false; + } - size_t offset = block->block_offset; - offset += sizeof(fio::BlockHeader); - size_t max_offset = block->block_offset + block_header->block_size; /* block_size := Written size, must be smaller or equal to the max size, except max size is 0 */ + size_t offset = block->block_offset; + offset += sizeof(fio::BlockHeader); + size_t max_offset = block->block_offset + block_header->block_size; /* block_size := Written size, must be smaller or equal to the max size, except max size is 0 */ - fio::MessageHeader header{}; - while(offset < max_offset) { - if(this->fread(&header, sizeof(header), offset, true) != sizeof(header)) { - error = "failed to read message header at index" + to_string(offset); - return false; - } - if(header.cookie != fio::MessageHeader::HEADER_COOKIE) { - error = "failed to verify message header cookie at index " + to_string(offset); - return false; - } - index->message_index.emplace_back(fio::IndexedBlockMessage{(uint32_t) (offset - block->block_offset), {header}, nullptr}); - offset += header.total_length; - } - } - block->indexed_block = index; - return true; + fio::MessageHeader header{}; + while(offset < max_offset) { + if(this->fread(&header, sizeof(header), offset, true) != sizeof(header)) { + error = "failed to read message header at index" + to_string(offset); + return false; + } + if(header.cookie != fio::MessageHeader::HEADER_COOKIE) { + error = "failed to verify message header cookie at index " + to_string(offset); + return false; + } + index->message_index.emplace_back(fio::IndexedBlockMessage{(uint32_t) (offset - block->block_offset), {header}, nullptr}); + offset += header.total_length; + } + } + block->indexed_block = index; + return true; } bool Conversation::load_messages(const std::shared_ptr &block, size_t index, size_t end_index, std::string &error) { - if(!this->load_message_block_index(block, error)) { - error = "failed to index block: " + error; - return false; - } + if(!this->load_message_block_index(block, error)) { + error = "failed to index block: " + error; + return false; + } - auto indexed_block = block->indexed_block; - auto header = block->block_header; - if(!indexed_block || !header) { - error = "failed to reference required data"; - return false; - } + auto indexed_block = block->indexed_block; + auto header = block->block_header; + if(!indexed_block || !header) { + error = "failed to reference required data"; + return false; + } - /* Note: We dont lock message_index_lock here because the write thread only increases the list and dont take stuff away where we could pointing at! */ - if(index >= indexed_block->message_index.size()) - return true; + /* Note: We dont lock message_index_lock here because the write thread only increases the list and dont take stuff away where we could pointing at! */ + if(index >= indexed_block->message_index.size()) + return true; - unique_lock file_lock(this->file_handle_lock); - if(!this->file_handle && !this->setup_file()) { - error = "failed to open file handle"; - return false; - } - auto result = fseek(this->file_handle, block->block_offset + indexed_block->message_index[index].message_offset, SEEK_SET); - if(result == EINVAL) { - error = "failed to seek to begin of an indexed block read"; - return false; - } + unique_lock file_lock(this->file_handle_lock); + if(!this->file_handle && !this->setup_file()) { + error = "failed to open file handle"; + return false; + } + auto result = fseek(this->file_handle, block->block_offset + indexed_block->message_index[index].message_offset, SEEK_SET); + if(result == EINVAL) { + error = "failed to seek to begin of an indexed block read"; + return false; + } - /* - * We dont need to lock the message_index_lock here because we never delete the messages and we just iterate with index - */ - while(index < end_index && index < indexed_block->message_index.size()) { - auto& message_data = indexed_block->message_index[index]; - if(message_data.message_data) { - index++; - continue; - } + /* + * We dont need to lock the message_index_lock here because we never delete the messages and we just iterate with index + */ + while(index < end_index && index < indexed_block->message_index.size()) { + auto& message_data = indexed_block->message_index[index]; + if(message_data.message_data) { + index++; + continue; + } - auto result = fseek(this->file_handle, sizeof(fio::MessageHeader), SEEK_CUR); - if(result == EINVAL) { - error = "failed to seek to begin of the message data"; - return false; - } + auto result = fseek(this->file_handle, sizeof(fio::MessageHeader), SEEK_CUR); + if(result == EINVAL) { + error = "failed to seek to begin of the message data"; + return false; + } - auto data = make_shared(); - if(header->meta_encrypted) { - auto meta_size = message_data.header.sender_unique_id_length + message_data.header.sender_name_length; - auto meta_buffer = malloc(meta_size); + auto data = make_shared(); + if(header->meta_encrypted) { + auto meta_size = message_data.header.sender_unique_id_length + message_data.header.sender_name_length; + auto meta_buffer = malloc(meta_size); - if(this->fread(meta_buffer, meta_size, -1, false) != meta_size) { - error = "failed to read message metadata at " + to_string(index); - free(meta_buffer); - return false; - } + if(this->fread(meta_buffer, meta_size, -1, false) != meta_size) { + error = "failed to read message metadata at " + to_string(index); + free(meta_buffer); + return false; + } - apply_crypt(meta_buffer, meta_buffer, meta_size, (block->block_offset ^ message_data.header.message_timestamp) ^ 0x6675636b20796f75ULL); /* 0x6675636b20796f75 := 'fuck you' */ + apply_crypt(meta_buffer, meta_buffer, meta_size, (block->block_offset ^ message_data.header.message_timestamp) ^ 0x6675636b20796f75ULL); /* 0x6675636b20796f75 := 'fuck you' */ - data->sender_unique_id.assign((char*) meta_buffer, message_data.header.sender_unique_id_length); - data->sender_name.assign((char*) meta_buffer + message_data.header.sender_unique_id_length, message_data.header.sender_name_length); - free(meta_buffer); - } else { - data->sender_unique_id.resize(message_data.header.sender_unique_id_length); - data->sender_name.resize(message_data.header.sender_name_length); + data->sender_unique_id.assign((char*) meta_buffer, message_data.header.sender_unique_id_length); + data->sender_name.assign((char*) meta_buffer + message_data.header.sender_unique_id_length, message_data.header.sender_name_length); + free(meta_buffer); + } else { + data->sender_unique_id.resize(message_data.header.sender_unique_id_length); + data->sender_name.resize(message_data.header.sender_name_length); - if(this->fread(data->sender_unique_id.data(), data->sender_unique_id.length(), -1, false) != data->sender_unique_id.length()) { - error = "failed to read message sender unique id at " + to_string(index); - return false; - } + if(this->fread(data->sender_unique_id.data(), data->sender_unique_id.length(), -1, false) != data->sender_unique_id.length()) { + error = "failed to read message sender unique id at " + to_string(index); + return false; + } - if(this->fread(data->sender_name.data(), data->sender_name.length(), -1, false) != data->sender_name.length()) { - error = "failed to read message sender name id at " + to_string(index); - return false; - } - } + if(this->fread(data->sender_name.data(), data->sender_name.length(), -1, false) != data->sender_name.length()) { + error = "failed to read message sender name id at " + to_string(index); + return false; + } + } - data->message.resize(message_data.header.message_length); - if(this->fread(data->message.data(), data->message.length(), -1, false) != data->message.length()) { - error = "failed to read message id at " + to_string(index); - return false; - } + data->message.resize(message_data.header.message_length); + if(this->fread(data->message.data(), data->message.length(), -1, false) != data->message.length()) { + error = "failed to read message id at " + to_string(index); + return false; + } - if(header->message_encrypted) - apply_crypt(data->message.data(), data->message.data(), data->message.size(), block->block_offset ^ message_data.header.message_timestamp); + if(header->message_encrypted) + apply_crypt(data->message.data(), data->message.data(), data->message.size(), block->block_offset ^ message_data.header.message_timestamp); - message_data.message_data = data; - index++; - } + message_data.message_data = data; + index++; + } - return true; + return true; } void Conversation::finish_block(const std::shared_ptr &block, bool write_file) { - if(block->flag_finished) - return; + if(block->flag_finished) + return; - auto handle = this->_ref_handle.lock(); - sassert(handle); - if(!handle) - return; + auto handle = this->_ref_handle.lock(); + sassert(handle); + if(!handle) + return; - auto ref_server = handle->ref_server(); - if(!ref_server) - return; + auto ref_server = handle->ref_server(); + if(!ref_server) + return; - block->flag_finished = true; + block->flag_finished = true; - if(write_file) { - string error; - auto result = this->load_message_block_header(block, error); - auto header = block->block_header; - result &= !!header; /* only success if we really have a header */ - if(result) { - if(header->block_max_size == 0) { - header->block_max_size = header->block_size; - header->finished = true; - if(!this->write_block_header(header, block->block_offset, error)) { - logError(ref_server->getServerId(), "Failed to finish block because block header could not be written: {}", error); - block->flag_invalid = true; /* because we cant set the block size we've to declare that block as invalid */ - } - } - } else { - logError(ref_server->getServerId(), "Failed to finish block because block header could not be set: {}", error); - block->flag_invalid = true; /* because we cant set the block size we've to declare that block as invalid */ - } - } else { - block->flag_invalid = true; /* because we dont write the block we cant ensure a valid block */ - } + if(write_file) { + string error; + auto result = this->load_message_block_header(block, error); + auto header = block->block_header; + result &= !!header; /* only success if we really have a header */ + if(result) { + if(header->block_max_size == 0) { + header->block_max_size = header->block_size; + header->finished = true; + if(!this->write_block_header(header, block->block_offset, error)) { + logError(ref_server->getServerId(), "Failed to finish block because block header could not be written: {}", error); + block->flag_invalid = true; /* because we cant set the block size we've to declare that block as invalid */ + } + } + } else { + logError(ref_server->getServerId(), "Failed to finish block because block header could not be set: {}", error); + block->flag_invalid = true; /* because we cant set the block size we've to declare that block as invalid */ + } + } else { + block->flag_invalid = true; /* because we dont write the block we cant ensure a valid block */ + } - this->db_save_block(block); + this->db_save_block(block); } bool Conversation::write_block_header(const std::shared_ptr &header, size_t index, std::string &error) { - auto code = this->fwrite(&*header, sizeof(fio::BlockHeader), index, false, true); - if(code == sizeof(fio::BlockHeader)) - return true; - error = "write returned " + to_string(code); - return false; + auto code = this->fwrite(&*header, sizeof(fio::BlockHeader), index, false, true); + if(code == sizeof(fio::BlockHeader)) + return true; + error = "write returned " + to_string(code); + return false; } void Conversation::process_write_queue(const std::chrono::system_clock::time_point &scheduled_time) { - unique_lock write_lock(this->_write_loop_lock, try_to_lock); - if(!write_lock.owns_lock()) /* we're already writing if this lock fails */ - return; + unique_lock write_lock(this->_write_loop_lock, try_to_lock); + if(!write_lock.owns_lock()) /* we're already writing if this lock fails */ + return; - unique_lock write_queue_lock(this->_write_queue_lock, defer_lock); - std::shared_ptr write_entry; - fio::MessageHeader write_header{}; - std::shared_ptr block_header; - auto handle = this->_ref_handle.lock(); - sassert(handle); - if(!handle) - return; + unique_lock write_queue_lock(this->_write_queue_lock, defer_lock); + std::shared_ptr write_entry; + fio::MessageHeader write_header{}; + std::shared_ptr block_header; + auto handle = this->_ref_handle.lock(); + sassert(handle); + if(!handle) + return; - auto ref_server = handle->ref_server(); - if(!ref_server) - return; + auto ref_server = handle->ref_server(); + if(!ref_server) + return; - write_header.cookie = fio::MessageHeader::HEADER_COOKIE; - write_header.message_flags = 0; + write_header.cookie = fio::MessageHeader::HEADER_COOKIE; + write_header.message_flags = 0; - while(true) { - write_queue_lock.lock(); - if(this->_write_queue.empty()) - break; - write_entry = this->_write_queue.front(); - this->_write_queue.pop_front(); - write_queue_lock.unlock(); + while(true) { + write_queue_lock.lock(); + if(this->_write_queue.empty()) + break; + write_entry = this->_write_queue.front(); + this->_write_queue.pop_front(); + write_queue_lock.unlock(); - /* calculate the write message total length */ - write_header.message_length = (uint16_t) min(write_entry->message.size(), (size_t) (65536 - 1)); - write_header.sender_name_length = (uint8_t) min(write_entry->sender_name.size(), (size_t) 255); - write_header.sender_unique_id_length = (uint8_t) min(write_entry->sender_unique_id.size(), (size_t) 255); - write_header.total_length = sizeof(write_header) + write_header.sender_unique_id_length + write_header.sender_name_length + write_header.message_length; + /* calculate the write message total length */ + write_header.message_length = (uint16_t) min(write_entry->message.size(), (size_t) (65536 - 1)); + write_header.sender_name_length = (uint8_t) min(write_entry->sender_name.size(), (size_t) 255); + write_header.sender_unique_id_length = (uint8_t) min(write_entry->sender_unique_id.size(), (size_t) 255); + write_header.total_length = sizeof(write_header) + write_header.sender_unique_id_length + write_header.sender_name_length + write_header.message_length; - /* verify last block */ - if(this->last_message_block) { - block_header = this->last_message_block->block_header; + /* verify last block */ + if(this->last_message_block) { + block_header = this->last_message_block->block_header; - if(!block_header) { - logError(ref_server->getServerId(), "[Conversations][{}] Current last block contains no header! Try to finish it and creating a new one.", this->_channel_id); - this->finish_block(this->last_message_block, true); - this->last_message_block = nullptr; - } else if(this->last_message_block->flag_finished) - this->last_message_block = nullptr; - else { - if(this->last_message_block->begin_timestamp + hours(24) < scheduled_time) { - debugMessage(ref_server->getServerId(), "[Conversations][{}] Beginning new block. Old block is older than 24hrs. ({})", this->_channel_id, this->last_message_block->begin_timestamp.time_since_epoch().count()); - this->finish_block(this->last_message_block, true); - this->last_message_block = nullptr; - } else if((block_header->block_max_size != 0 && block_header->block_size + write_header.total_length >= block_header->block_max_size) || block_header->block_size > fio::BlockHeader::MAX_BLOCK_SIZE){ - debugMessage(ref_server->getServerId(), "[Conversations][{}] Beginning new block. Old block would exceed his space (Current index: {}, Max index: {}, Soft cap: {}, Message size: {}).", - this->_channel_id, block_header->block_size, block_header->block_max_size, fio::BlockHeader::MAX_BLOCK_SIZE, write_header.total_length); - this->finish_block(this->last_message_block, true); - this->last_message_block = nullptr; - } else if(block_header->message_version != 1){ - debugMessage(ref_server->getServerId(), "[Conversations][{}] Beginning new block. Old block had another message version (Current version: {}, Block version: {}).", this->_channel_id, 1, block_header->message_version); - this->finish_block(this->last_message_block, true); - this->last_message_block = nullptr; - } - } - } - /* test if we have a block or create a new one at the begin of the file */ - if(!this->last_message_block) { - //Note: If we reuse blocks we've to reorder them within message_blocks (newest blocks are at the end) - //TODO: Find "free" blocks and use them! (But do not use indirectly finished blocks, their max size could be invalid) + if(!block_header) { + logError(ref_server->getServerId(), "[Conversations][{}] Current last block contains no header! Try to finish it and creating a new one.", this->_channel_id); + this->finish_block(this->last_message_block, true); + this->last_message_block = nullptr; + } else if(this->last_message_block->flag_finished) + this->last_message_block = nullptr; + else { + if(this->last_message_block->begin_timestamp + hours(24) < scheduled_time) { + debugMessage(ref_server->getServerId(), "[Conversations][{}] Beginning new block. Old block is older than 24hrs. ({})", this->_channel_id, this->last_message_block->begin_timestamp.time_since_epoch().count()); + this->finish_block(this->last_message_block, true); + this->last_message_block = nullptr; + } else if((block_header->block_max_size != 0 && block_header->block_size + write_header.total_length >= block_header->block_max_size) || block_header->block_size > fio::BlockHeader::MAX_BLOCK_SIZE){ + debugMessage(ref_server->getServerId(), "[Conversations][{}] Beginning new block. Old block would exceed his space (Current index: {}, Max index: {}, Soft cap: {}, Message size: {}).", + this->_channel_id, block_header->block_size, block_header->block_max_size, fio::BlockHeader::MAX_BLOCK_SIZE, write_header.total_length); + this->finish_block(this->last_message_block, true); + this->last_message_block = nullptr; + } else if(block_header->message_version != 1){ + debugMessage(ref_server->getServerId(), "[Conversations][{}] Beginning new block. Old block had another message version (Current version: {}, Block version: {}).", this->_channel_id, 1, block_header->message_version); + this->finish_block(this->last_message_block, true); + this->last_message_block = nullptr; + } + } + } + /* test if we have a block or create a new one at the begin of the file */ + if(!this->last_message_block) { + //Note: If we reuse blocks we've to reorder them within message_blocks (newest blocks are at the end) + //TODO: Find "free" blocks and use them! (But do not use indirectly finished blocks, their max size could be invalid) - unique_lock file_lock(this->file_handle_lock); - if(!this->file_handle && !this->setup_file()) { - logError(ref_server->getServerId(), "[Conversations][{}] Failed to reopen log file. Dropping message!", this->_channel_id); - return; - } - auto result = fseek(this->file_handle, 0, SEEK_END); - if(result != 0) { - logError(ref_server->getServerId(), "[Conversations][{}] failed to seek to the end (" + to_string(result) + " " + to_string(errno) + "). Could not create new block. Dropping message!", this->_channel_id); - return; - } + unique_lock file_lock(this->file_handle_lock); + if(!this->file_handle && !this->setup_file()) { + logError(ref_server->getServerId(), "[Conversations][{}] Failed to reopen log file. Dropping message!", this->_channel_id); + return; + } + auto result = fseek(this->file_handle, 0, SEEK_END); + if(result != 0) { + logError(ref_server->getServerId(), "[Conversations][{}] failed to seek to the end (" + to_string(result) + " " + to_string(errno) + "). Could not create new block. Dropping message!", this->_channel_id); + return; + } - auto file_size = ftell(this->file_handle); - if(file_size < 0) { - logError(ref_server->getServerId(), "[Conversations][{}] failed to tell the end position (" + to_string(result) + " " + to_string(errno) + "). Could not create new block. Dropping message!", this->_channel_id); - return; - } - file_lock.unlock(); - this->last_message_block = this->db_create_block((uint64_t) file_size); - if(!this->last_message_block) { - logError(ref_server->getServerId(), "[Conversations][{}] Failed to create a new block within database. Dropping message!", this->_channel_id); - return; - } - block_header = make_shared(); - memset(&*block_header, 0, sizeof(fio::BlockHeader)); + auto file_size = ftell(this->file_handle); + if(file_size < 0) { + logError(ref_server->getServerId(), "[Conversations][{}] failed to tell the end position (" + to_string(result) + " " + to_string(errno) + "). Could not create new block. Dropping message!", this->_channel_id); + return; + } + file_lock.unlock(); + this->last_message_block = this->db_create_block((uint64_t) file_size); + if(!this->last_message_block) { + logError(ref_server->getServerId(), "[Conversations][{}] Failed to create a new block within database. Dropping message!", this->_channel_id); + return; + } + block_header = make_shared(); + memset(&*block_header, 0, sizeof(fio::BlockHeader)); - block_header->version = 1; - block_header->message_version = 1; - block_header->cookie = fio::BlockHeader::HEADER_COOKIE; - block_header->first_message_timestamp = (uint64_t) duration_cast(write_entry->message_timestamp.time_since_epoch()).count(); - block_header->block_size = sizeof(fio::BlockHeader); + block_header->version = 1; + block_header->message_version = 1; + block_header->cookie = fio::BlockHeader::HEADER_COOKIE; + block_header->first_message_timestamp = (uint64_t) duration_cast(write_entry->message_timestamp.time_since_epoch()).count(); + block_header->block_size = sizeof(fio::BlockHeader); - block_header->message_encrypted = true; /* May add some kind of hidden debug option? */ - block_header->meta_encrypted = true; /* May add some kind of hidden debug option? */ - this->last_message_block->block_header = block_header; - } + block_header->message_encrypted = true; /* May add some kind of hidden debug option? */ + block_header->meta_encrypted = true; /* May add some kind of hidden debug option? */ + this->last_message_block->block_header = block_header; + } - auto entry_offset = this->last_message_block->block_offset + sizeof(fio::BlockHeader) + block_header->last_message_offset; - write_header.sender_database_id = write_entry->sender_database_id; - write_header.message_timestamp = (uint64_t) duration_cast(write_entry->message_timestamp.time_since_epoch()).count(); - block_header->last_message_timestamp = write_header.message_timestamp; + auto entry_offset = this->last_message_block->block_offset + sizeof(fio::BlockHeader) + block_header->last_message_offset; + write_header.sender_database_id = write_entry->sender_database_id; + write_header.message_timestamp = (uint64_t) duration_cast(write_entry->message_timestamp.time_since_epoch()).count(); + block_header->last_message_timestamp = write_header.message_timestamp; - /* first write the header */ - if(this->fwrite(&write_header, sizeof(write_header), entry_offset, true, true) != sizeof(write_header)) { - logError(ref_server->getServerId(), "[Conversations][{}] Failed to write message header. Dropping message!", this->_channel_id); - return; - } - entry_offset += sizeof(write_header); + /* first write the header */ + if(this->fwrite(&write_header, sizeof(write_header), entry_offset, true, true) != sizeof(write_header)) { + logError(ref_server->getServerId(), "[Conversations][{}] Failed to write message header. Dropping message!", this->_channel_id); + return; + } + entry_offset += sizeof(write_header); - /* write the metadata */ - { - auto write_buffer_size = write_header.sender_unique_id_length + write_header.sender_name_length; - auto write_buffer = malloc(write_buffer_size); + /* write the metadata */ + { + auto write_buffer_size = write_header.sender_unique_id_length + write_header.sender_name_length; + auto write_buffer = malloc(write_buffer_size); - memcpy(write_buffer, write_entry->sender_unique_id.data(), write_header.sender_unique_id_length); - memcpy((char*) write_buffer + write_header.sender_unique_id_length, write_entry->sender_name.data(), write_header.sender_name_length); + memcpy(write_buffer, write_entry->sender_unique_id.data(), write_header.sender_unique_id_length); + memcpy((char*) write_buffer + write_header.sender_unique_id_length, write_entry->sender_name.data(), write_header.sender_name_length); - if(block_header->meta_encrypted) - apply_crypt(write_buffer, write_buffer, write_buffer_size, (this->last_message_block->block_offset ^ write_header.message_timestamp) ^ 0x6675636b20796f75ULL); /* 0x6675636b20796f75 := 'fuck you' */ + if(block_header->meta_encrypted) + apply_crypt(write_buffer, write_buffer, write_buffer_size, (this->last_message_block->block_offset ^ write_header.message_timestamp) ^ 0x6675636b20796f75ULL); /* 0x6675636b20796f75 := 'fuck you' */ - /* then write the sender unique id */ - if(this->fwrite(write_buffer, write_buffer_size, entry_offset, true, true) != write_buffer_size) { - logError(ref_server->getServerId(), "[Conversations][{}] Failed to write message header. Dropping message!", this->_channel_id); - free(write_buffer); - return; - } - free(write_buffer); - entry_offset += write_buffer_size; - } + /* then write the sender unique id */ + if(this->fwrite(write_buffer, write_buffer_size, entry_offset, true, true) != write_buffer_size) { + logError(ref_server->getServerId(), "[Conversations][{}] Failed to write message header. Dropping message!", this->_channel_id); + free(write_buffer); + return; + } + free(write_buffer); + entry_offset += write_buffer_size; + } - /* then write the message */ - { - bool message_result; - if(block_header->message_encrypted) { - size_t length = write_entry->message.size(); - char* target_buffer = (char*) malloc(length); - apply_crypt(write_entry->message.data(), target_buffer, length, this->last_message_block->block_offset ^ write_header.message_timestamp); + /* then write the message */ + { + bool message_result; + if(block_header->message_encrypted) { + size_t length = write_entry->message.size(); + char* target_buffer = (char*) malloc(length); + apply_crypt(write_entry->message.data(), target_buffer, length, this->last_message_block->block_offset ^ write_header.message_timestamp); - message_result = this->fwrite(target_buffer, write_header.message_length, entry_offset, true, true) == write_header.message_length; - free(target_buffer); - } else { - message_result = this->fwrite(write_entry->message.data(), write_header.message_length, entry_offset, true, true) == write_header.message_length; - } - if(!message_result) { - logError(ref_server->getServerId(), "[Conversations][{}] Failed to write message. Dropping message!", this->_channel_id); - return; - } - entry_offset += write_header.message_length; - } + message_result = this->fwrite(target_buffer, write_header.message_length, entry_offset, true, true) == write_header.message_length; + free(target_buffer); + } else { + message_result = this->fwrite(write_entry->message.data(), write_header.message_length, entry_offset, true, true) == write_header.message_length; + } + if(!message_result) { + logError(ref_server->getServerId(), "[Conversations][{}] Failed to write message. Dropping message!", this->_channel_id); + return; + } + entry_offset += write_header.message_length; + } - block_header->last_message_offset = (uint32_t) (entry_offset - this->last_message_block->block_offset - sizeof(fio::BlockHeader)); - block_header->block_size += write_header.total_length; - block_header->message_count += 1; + block_header->last_message_offset = (uint32_t) (entry_offset - this->last_message_block->block_offset - sizeof(fio::BlockHeader)); + block_header->block_size += write_header.total_length; + block_header->message_count += 1; - auto indexed_block = this->last_message_block->indexed_block; - if(indexed_block) { - lock_guard lock(indexed_block->message_index_lock); - indexed_block->message_index.push_back(fio::IndexedBlockMessage{(uint32_t) (entry_offset - this->last_message_block->block_offset), {write_header}, nullptr}); - } - } + auto indexed_block = this->last_message_block->indexed_block; + if(indexed_block) { + lock_guard lock(indexed_block->message_index_lock); + indexed_block->message_index.push_back(fio::IndexedBlockMessage{(uint32_t) (entry_offset - this->last_message_block->block_offset), {write_header}, nullptr}); + } + } - if(write_header.total_length != 0) {/* will be set when at least one message has been written */ - this->db_save_block(this->last_message_block); + if(write_header.total_length != 0) {/* will be set when at least one message has been written */ + this->db_save_block(this->last_message_block); - string error; - if(!this->write_block_header(block_header, this->last_message_block->block_offset, error)) { - logWarning(ref_server->getServerId(), "[Conversations][{}] Failed to write block header after message write ({}). This could cause data loss!", this->_channel_id, error); - } - } + string error; + if(!this->write_block_header(block_header, this->last_message_block->block_offset, error)) { + logWarning(ref_server->getServerId(), "[Conversations][{}] Failed to write block header after message write ({}). This could cause data loss!", this->_channel_id, error); + } + } } std::shared_ptr Conversation::db_create_block(uint64_t offset) { - auto result = make_shared(); - result->block_offset = offset; - result->begin_timestamp = system_clock::now(); - result->end_timestamp = system_clock::now(); - result->flags = 0; - result->flag_used = true; - result->flag_invalid = true; /* first set it to invalid for the database so it becomes active as soon somebody uses it */ + auto result = make_shared(); + result->block_offset = offset; + result->begin_timestamp = system_clock::now(); + result->end_timestamp = system_clock::now(); + result->flags = 0; + result->flag_used = true; + result->flag_invalid = true; /* first set it to invalid for the database so it becomes active as soon somebody uses it */ - auto handle = this->_ref_handle.lock(); - assert(handle); + auto handle = this->_ref_handle.lock(); + assert(handle); - auto ref_server = handle->ref_server(); - if(!ref_server) - return nullptr; + auto ref_server = handle->ref_server(); + if(!ref_server) + return nullptr; - auto sql = ref_server->getSql(); - if(!sql) - return nullptr; + auto sql = ref_server->getSql(); + if(!sql) + return nullptr; - //`server_id` INT, `conversation_id` INT, `begin_timestamp` INT, `end_timestamp` INT, `block_offset` INT, `flags` INT - auto sql_result = sql::command(sql, "INSERT INTO `conversation_blocks` (`server_id`, `conversation_id`, `begin_timestamp`, `end_timestamp`, `block_offset`, `flags`) VALUES (:sid, :cid, :bt, :et, :bo, :f);", - variable{":sid", ref_server->getServerId()}, - variable{":cid", this->_channel_id}, - variable{":bt", duration_cast(result->begin_timestamp.time_since_epoch()).count()}, + //`server_id` INT, `conversation_id` INT, `begin_timestamp` INT, `end_timestamp` INT, `block_offset` INT, `flags` INT + auto sql_result = sql::command(sql, "INSERT INTO `conversation_blocks` (`server_id`, `conversation_id`, `begin_timestamp`, `end_timestamp`, `block_offset`, `flags`) VALUES (:sid, :cid, :bt, :et, :bo, :f);", + variable{":sid", ref_server->getServerId()}, + variable{":cid", this->_channel_id}, + variable{":bt", duration_cast(result->begin_timestamp.time_since_epoch()).count()}, variable{":et", duration_cast(result->end_timestamp.time_since_epoch()).count()}, variable{":bo", offset}, variable{":f", result->flags} - ).executeLater(); - sql_result.waitAndGetLater(LOG_SQL_CMD, {-1, "future error"}); + ).executeLater(); + sql_result.waitAndGetLater(LOG_SQL_CMD, {-1, "future error"}); - { - lock_guard lock(this->message_block_lock); - this->message_blocks.push_back(result); - } + { + lock_guard lock(this->message_block_lock); + this->message_blocks.push_back(result); + } - result->flag_invalid = false; - return result; + result->flag_invalid = false; + return result; } void Conversation::db_save_block(const std::shared_ptr &block) { - auto handle = this->_ref_handle.lock(); - assert(handle); + auto handle = this->_ref_handle.lock(); + assert(handle); - auto ref_server = handle->ref_server(); - if(!ref_server) { - logWarning(ref_server->getServerId(), "[Conversations][{}] Failed to update block db info (server expired)", this->_channel_id); - return; - } + auto ref_server = handle->ref_server(); + if(!ref_server) { + logWarning(ref_server->getServerId(), "[Conversations][{}] Failed to update block db info (server expired)", this->_channel_id); + return; + } - auto sql = ref_server->getSql(); - if(!sql) { - logWarning(ref_server->getServerId(), "[Conversations][{}] Failed to update block db info (sql expired)", this->_channel_id); - return; - } + auto sql = ref_server->getSql(); + if(!sql) { + logWarning(ref_server->getServerId(), "[Conversations][{}] Failed to update block db info (sql expired)", this->_channel_id); + return; + } - auto sql_result = sql::command(sql, "UPDATE `conversation_blocks` SET `end_timestamp` = :et, `flags` = :f WHERE `server_id` = :sid AND `conversation_id` = :cid AND `begin_timestamp` = :bt AND `block_offset` = :bo", - variable{":sid", ref_server->getServerId()}, - variable{":cid", this->_channel_id}, - variable{":bt", duration_cast(block->begin_timestamp.time_since_epoch()).count()}, - variable{":et", duration_cast(block->end_timestamp.time_since_epoch()).count()}, - variable{":bo", block->block_offset}, - variable{":f", block->flags} - ).executeLater(); - sql_result.waitAndGetLater(LOG_SQL_CMD, {-1, "future error"}); + auto sql_result = sql::command(sql, "UPDATE `conversation_blocks` SET `end_timestamp` = :et, `flags` = :f WHERE `server_id` = :sid AND `conversation_id` = :cid AND `begin_timestamp` = :bt AND `block_offset` = :bo", + variable{":sid", ref_server->getServerId()}, + variable{":cid", this->_channel_id}, + variable{":bt", duration_cast(block->begin_timestamp.time_since_epoch()).count()}, + variable{":et", duration_cast(block->end_timestamp.time_since_epoch()).count()}, + variable{":bo", block->block_offset}, + variable{":f", block->flags} + ).executeLater(); + sql_result.waitAndGetLater(LOG_SQL_CMD, {-1, "future error"}); } void Conversation::register_message(ts::ClientDbId sender_database_id, const std::string &sender_unique_id, const std::string &sender_name, const std::chrono::system_clock::time_point &ts, const std::string &message) { - auto entry = make_shared(); - entry->message_timestamp = ts; - this->_last_message_timestamp = entry->message_timestamp; - entry->message = message; - entry->sender_name = sender_name; - entry->sender_unique_id = sender_unique_id; - entry->sender_database_id = sender_database_id; + auto entry = make_shared(); + entry->message_timestamp = ts; + this->_last_message_timestamp = entry->message_timestamp; + entry->message = message; + entry->sender_name = sender_name; + entry->sender_unique_id = sender_unique_id; + entry->sender_database_id = sender_database_id; - { - lock_guard lock(this->_last_messages_lock); - this->_last_messages.push_back(entry); - while(this->_last_messages.size() > this->_last_messages_limit) - this->_last_messages.pop_front(); /* TODO: Use a iterator for delete to improve performance */ - } - if(!this->volatile_only()) { - { - lock_guard lock(this->_write_queue_lock); - this->_write_queue.push_back(entry); - } - auto executor = serverInstance->getConversationIo(); - executor->schedule(this->_write_event); - } + { + lock_guard lock(this->_last_messages_lock); + this->_last_messages.push_back(entry); + while(this->_last_messages.size() > this->_last_messages_limit) + this->_last_messages.pop_front(); /* TODO: Use a iterator for delete to improve performance */ + } + if(!this->volatile_only()) { + { + lock_guard lock(this->_write_queue_lock); + this->_write_queue.push_back(entry); + } + auto executor = serverInstance->getConversationIo(); + executor->schedule(this->_write_event); + } } std::deque> Conversation::message_history(const std::chrono::system_clock::time_point &end_timestamp, size_t message_count, const std::chrono::system_clock::time_point &begin_timestamp) { - std::deque> result; - if(message_count == 0) - return result; + std::deque> result; + if(message_count == 0) + return result; - bool count_deleted = false; - /* first try to fillout the result with the cached messages */ - { - lock_guard lock(this->_last_messages_lock); - //TODO: May just insert the rest of the iterator instead of looping? - for(auto it = this->_last_messages.rbegin(); it != this->_last_messages.rend(); it++) { - if((*it)->message_timestamp > end_timestamp) /* message has been send after the search timestamp */ - continue; - if(begin_timestamp.time_since_epoch().count() != 0 && (*it)->message_timestamp < begin_timestamp) - return result; - if((*it)->flag_message_deleted && !count_deleted) - continue; + bool count_deleted = false; + /* first try to fillout the result with the cached messages */ + { + lock_guard lock(this->_last_messages_lock); + //TODO: May just insert the rest of the iterator instead of looping? + for(auto it = this->_last_messages.rbegin(); it != this->_last_messages.rend(); it++) { + if((*it)->message_timestamp > end_timestamp) /* message has been send after the search timestamp */ + continue; + if(begin_timestamp.time_since_epoch().count() != 0 && (*it)->message_timestamp < begin_timestamp) + return result; + if((*it)->flag_message_deleted && !count_deleted) + continue; - result.push_back(*it); - if(--message_count == 0) - return result; - } - } + result.push_back(*it); + if(--message_count == 0) + return result; + } + } - if(!this->volatile_only()) { - auto handle = this->_ref_handle.lock(); - if(!handle) - return result; + if(!this->volatile_only()) { + auto handle = this->_ref_handle.lock(); + if(!handle) + return result; - auto ref_server = handle->ref_server(); - if(!ref_server) - return result; + auto ref_server = handle->ref_server(); + if(!ref_server) + return result; - auto begin_timestamp_ms = chrono::floor(begin_timestamp.time_since_epoch()).count(); - auto timestamp_ms = result.empty() ? chrono::floor(end_timestamp.time_since_epoch()).count() : chrono::floor(result.back()->message_timestamp.time_since_epoch()).count(); + auto begin_timestamp_ms = chrono::floor(begin_timestamp.time_since_epoch()).count(); + auto timestamp_ms = result.empty() ? chrono::floor(end_timestamp.time_since_epoch()).count() : chrono::floor(result.back()->message_timestamp.time_since_epoch()).count(); - unique_lock lock(this->message_block_lock); - auto rit = this->message_blocks.end(); - if(rit != this->message_blocks.begin()) { - bool found = false; - do { - rit--; - if(chrono::floor((*rit)->begin_timestamp.time_since_epoch()).count() < timestamp_ms) { - found = true; - break; /* we found the first block which is created before the point we're searching from */ - } - } while(rit != this->message_blocks.begin()); + unique_lock lock(this->message_block_lock); + auto rit = this->message_blocks.end(); + if(rit != this->message_blocks.begin()) { + bool found = false; + do { + rit--; + if(chrono::floor((*rit)->begin_timestamp.time_since_epoch()).count() < timestamp_ms) { + found = true; + break; /* we found the first block which is created before the point we're searching from */ + } + } while(rit != this->message_blocks.begin()); - string error; - if(found) { - vector> relevant_entries{this->message_blocks.begin(), ++rit}; - lock.unlock(); + string error; + if(found) { + vector> relevant_entries{this->message_blocks.begin(), ++rit}; + lock.unlock(); - auto _rit = --relevant_entries.end(); - do { - auto block = *_rit; - /* lets search for messages */ - if(!this->load_message_block_index(block, error)) { - logWarning(ref_server->getServerId(), "[Conversations][{}] Failed to load message block {} for message lookup: {}", this->_channel_id, block->block_offset, error); - continue; - } - auto index = (*_rit)->indexed_block; - if(!index) { - logWarning(ref_server->getServerId(), "[Conversations][{}] Failed to reference indexed block within message block.", this->_channel_id); - continue; - } + auto _rit = --relevant_entries.end(); + do { + auto block = *_rit; + /* lets search for messages */ + if(!this->load_message_block_index(block, error)) { + logWarning(ref_server->getServerId(), "[Conversations][{}] Failed to load message block {} for message lookup: {}", this->_channel_id, block->block_offset, error); + continue; + } + auto index = (*_rit)->indexed_block; + if(!index) { + logWarning(ref_server->getServerId(), "[Conversations][{}] Failed to reference indexed block within message block.", this->_channel_id); + continue; + } - lock_guard index_lock{index->message_index_lock}; - auto rmid = index->message_index.end(); - if(rmid == index->message_index.begin()) - continue; /* Empty block? Funny */ + lock_guard index_lock{index->message_index_lock}; + auto rmid = index->message_index.end(); + if(rmid == index->message_index.begin()) + continue; /* Empty block? Funny */ - auto block_found = false; - do { - rmid--; - if(rmid->header.message_timestamp < timestamp_ms) { - block_found = true; - break; /* we found the first block which is created before the point we're searching from */ - } - } while(rmid != index->message_index.begin()); - if(!block_found) - continue; + auto block_found = false; + do { + rmid--; + if(rmid->header.message_timestamp < timestamp_ms) { + block_found = true; + break; /* we found the first block which is created before the point we're searching from */ + } + } while(rmid != index->message_index.begin()); + if(!block_found) + continue; - if(!this->load_messages(block, 0, std::distance(index->message_index.begin(), rmid) + 1, error)) { - logWarning(ref_server->getServerId(), "[Conversations][{}] Failed to load messages within block {} for message lookup: {}", this->_channel_id, block->block_offset, error); - continue; - } - do { - if(rmid->header.flag_deleted && !count_deleted) - continue; + if(!this->load_messages(block, 0, std::distance(index->message_index.begin(), rmid) + 1, error)) { + logWarning(ref_server->getServerId(), "[Conversations][{}] Failed to load messages within block {} for message lookup: {}", this->_channel_id, block->block_offset, error); + continue; + } + do { + if(rmid->header.flag_deleted && !count_deleted) + continue; - if(rmid->header.message_timestamp >= timestamp_ms) - continue; /* for some reason we got a message from the index of before where we are. This could happen for "orphaned" blocks which point to a valid block within the future block */ + if(rmid->header.message_timestamp >= timestamp_ms) + continue; /* for some reason we got a message from the index of before where we are. This could happen for "orphaned" blocks which point to a valid block within the future block */ - if(begin_timestamp.time_since_epoch().count() != 0 && rmid->header.message_timestamp < begin_timestamp_ms) - return result; + if(begin_timestamp.time_since_epoch().count() != 0 && rmid->header.message_timestamp < begin_timestamp_ms) + return result; - auto data = rmid->message_data; - if(!data) - continue; + auto data = rmid->message_data; + if(!data) + continue; - result.push_back(make_shared(ConversationEntry{ - system_clock::time_point{} + milliseconds{rmid->header.message_timestamp}, + result.push_back(make_shared(ConversationEntry{ + system_clock::time_point{} + milliseconds{rmid->header.message_timestamp}, - (ClientDbId) rmid->header.sender_database_id, - data->sender_unique_id, - data->sender_name, + (ClientDbId) rmid->header.sender_database_id, + data->sender_unique_id, + data->sender_name, - data->message, + data->message, - rmid->header.flag_deleted - })); - timestamp_ms = rmid->header.message_timestamp; - if(--message_count == 0) - return result; - } while(rmid-- != index->message_index.begin()); - } while(_rit-- != relevant_entries.begin()); - } - } - } + rmid->header.flag_deleted + })); + timestamp_ms = rmid->header.message_timestamp; + if(--message_count == 0) + return result; + } while(rmid-- != index->message_index.begin()); + } while(_rit-- != relevant_entries.begin()); + } + } + } - return result; + return result; } //TODO: May move the IO write part to the write queue? size_t Conversation::delete_messages(const std::chrono::system_clock::time_point &end_timestamp, size_t message_count, const std::chrono::system_clock::time_point &begin_timestamp, ts::ClientDbId cldbid) { - size_t delete_count_volatile = 0, delete_count = 0; + size_t delete_count_volatile = 0, delete_count = 0; - if(message_count == 0) - return 0; + if(message_count == 0) + return 0; - /* first try to fillout the result with the cached messages */ - { - lock_guard lock(this->_last_messages_lock); - for(auto it = this->_last_messages.rbegin(); it != this->_last_messages.rend(); it++) { - if((*it)->message_timestamp > end_timestamp) /* message has been send after the search timestamp */ - continue; + /* first try to fillout the result with the cached messages */ + { + lock_guard lock(this->_last_messages_lock); + for(auto it = this->_last_messages.rbegin(); it != this->_last_messages.rend(); it++) { + if((*it)->message_timestamp > end_timestamp) /* message has been send after the search timestamp */ + continue; - if(begin_timestamp.time_since_epoch().count() != 0 && (*it)->message_timestamp < begin_timestamp) - break; + if(begin_timestamp.time_since_epoch().count() != 0 && (*it)->message_timestamp < begin_timestamp) + break; - if(cldbid != 0 && (*it)->sender_database_id != cldbid) - continue; + if(cldbid != 0 && (*it)->sender_database_id != cldbid) + continue; - (*it)->flag_message_deleted = true; - if(++delete_count_volatile >= message_count) - break; - } - } + (*it)->flag_message_deleted = true; + if(++delete_count_volatile >= message_count) + break; + } + } - /* TODO: Remove from write queue */ + /* TODO: Remove from write queue */ - if(!this->volatile_only()) { - auto handle = this->_ref_handle.lock(); - if(!handle) - return delete_count_volatile; + if(!this->volatile_only()) { + auto handle = this->_ref_handle.lock(); + if(!handle) + return delete_count_volatile; - auto ref_server = handle->ref_server(); - if(!ref_server) - return delete_count_volatile; + auto ref_server = handle->ref_server(); + if(!ref_server) + return delete_count_volatile; - auto begin_timestamp_ms = chrono::floor(begin_timestamp.time_since_epoch()).count(); - auto timestamp_ms = chrono::floor(end_timestamp.time_since_epoch()).count(); + auto begin_timestamp_ms = chrono::floor(begin_timestamp.time_since_epoch()).count(); + auto timestamp_ms = chrono::floor(end_timestamp.time_since_epoch()).count(); - unique_lock lock(this->message_block_lock); - auto rit = this->message_blocks.end(); - if(rit != this->message_blocks.begin()) { - bool found = false; - do { - rit--; - if(chrono::floor((*rit)->begin_timestamp.time_since_epoch()).count() < timestamp_ms) { - found = true; - break; /* we found the first block which is created before the point we're searching from */ - } - } while(rit != this->message_blocks.begin()); + unique_lock lock(this->message_block_lock); + auto rit = this->message_blocks.end(); + if(rit != this->message_blocks.begin()) { + bool found = false; + do { + rit--; + if(chrono::floor((*rit)->begin_timestamp.time_since_epoch()).count() < timestamp_ms) { + found = true; + break; /* we found the first block which is created before the point we're searching from */ + } + } while(rit != this->message_blocks.begin()); - string error; - if(found) { - vector> relevant_entries{this->message_blocks.begin(), ++rit}; - lock.unlock(); + string error; + if(found) { + vector> relevant_entries{this->message_blocks.begin(), ++rit}; + lock.unlock(); - auto _rit = --relevant_entries.end(); - do { - auto block = *_rit; - /* lets search for messages */ - if(!this->load_message_block_index(block, error)) { - logWarning(ref_server->getServerId(), "[Conversations][{}] Failed to load message block {} for message delete: {}", this->_channel_id, block->block_offset, error); - continue; - } - auto index = (*_rit)->indexed_block; - if(!index) { - logWarning(ref_server->getServerId(), "[Conversations][{}] Failed to reference indexed block within message block.", this->_channel_id); - continue; - } + auto _rit = --relevant_entries.end(); + do { + auto block = *_rit; + /* lets search for messages */ + if(!this->load_message_block_index(block, error)) { + logWarning(ref_server->getServerId(), "[Conversations][{}] Failed to load message block {} for message delete: {}", this->_channel_id, block->block_offset, error); + continue; + } + auto index = (*_rit)->indexed_block; + if(!index) { + logWarning(ref_server->getServerId(), "[Conversations][{}] Failed to reference indexed block within message block.", this->_channel_id); + continue; + } - lock_guard index_lock{index->message_index_lock}; - auto rmid = index->message_index.end(); - if(rmid == index->message_index.begin()) - continue; /* Empty block? Funny */ + lock_guard index_lock{index->message_index_lock}; + auto rmid = index->message_index.end(); + if(rmid == index->message_index.begin()) + continue; /* Empty block? Funny */ - auto block_found = false; - do { - rmid--; - if(rmid->header.message_timestamp < timestamp_ms) { - block_found = true; - break; /* we found the first block which is created before the point we're searching from */ - } - } while(rmid != index->message_index.begin()); - if(!block_found) - continue; + auto block_found = false; + do { + rmid--; + if(rmid->header.message_timestamp < timestamp_ms) { + block_found = true; + break; /* we found the first block which is created before the point we're searching from */ + } + } while(rmid != index->message_index.begin()); + if(!block_found) + continue; - do { - if(rmid->header.message_timestamp >= timestamp_ms) - continue; /* for some reason we got a message from the index of before where we are. This could happen for "orphaned" blocks which point to a valid block within the future block */ + do { + if(rmid->header.message_timestamp >= timestamp_ms) + continue; /* for some reason we got a message from the index of before where we are. This could happen for "orphaned" blocks which point to a valid block within the future block */ - if(begin_timestamp.time_since_epoch().count() != 0 && rmid->header.message_timestamp < begin_timestamp_ms) - return max(delete_count, delete_count_volatile); + if(begin_timestamp.time_since_epoch().count() != 0 && rmid->header.message_timestamp < begin_timestamp_ms) + return max(delete_count, delete_count_volatile); - if(cldbid != 0 && rmid->header.sender_database_id != cldbid) - continue; + if(cldbid != 0 && rmid->header.sender_database_id != cldbid) + continue; - if(!rmid->header.flag_deleted) { - rmid->header.flag_deleted = true; + if(!rmid->header.flag_deleted) { + rmid->header.flag_deleted = true; - auto offset = block->block_offset + rmid->message_offset; - if(this->fwrite(&rmid->header, sizeof(rmid->header), offset, false, true) != sizeof(rmid->header)) - logWarning(ref_server->getServerId(), "[Conversations][{}] Failed to save message flags.", this->_channel_id); - } + auto offset = block->block_offset + rmid->message_offset; + if(this->fwrite(&rmid->header, sizeof(rmid->header), offset, false, true) != sizeof(rmid->header)) + logWarning(ref_server->getServerId(), "[Conversations][{}] Failed to save message flags.", this->_channel_id); + } - timestamp_ms = rmid->header.message_timestamp; - if(++delete_count >= message_count) - return max(delete_count, delete_count_volatile); - } while(rmid-- != index->message_index.begin()); - } while(_rit-- != relevant_entries.begin()); - } - } - } + timestamp_ms = rmid->header.message_timestamp; + if(++delete_count >= message_count) + return max(delete_count, delete_count_volatile); + } while(rmid-- != index->message_index.begin()); + } while(_rit-- != relevant_entries.begin()); + } + } + } - return max(delete_count, delete_count_volatile); + return max(delete_count, delete_count_volatile); } ConversationManager::ConversationManager(const std::shared_ptr &server) : _ref_server(server) { } @@ -1262,147 +1262,147 @@ ConversationManager::ConversationManager(const std::shared_ptr &_this) { - assert(&*_this == this); - this->_ref_this = _this; + assert(&*_this == this); + this->_ref_this = _this; - auto ref_server = this->ref_server(); - assert(ref_server); - auto sql = ref_server->getSql(); - assert(sql); + auto ref_server = this->ref_server(); + assert(ref_server); + auto sql = ref_server->getSql(); + assert(sql); - auto result = sql::command(sql, "SELECT `conversation_id`, `file_path` FROM `conversations` WHERE `server_id` = :sid", variable{":sid", ref_server->getServerId()}).query([&](int count, std::string* values, std::string* names) { - ChannelId conversation_id = 0; - std::string file_path; + auto result = sql::command(sql, "SELECT `conversation_id`, `file_path` FROM `conversations` WHERE `server_id` = :sid", variable{":sid", ref_server->getServerId()}).query([&](int count, std::string* values, std::string* names) { + ChannelId conversation_id = 0; + std::string file_path; - try { - for(int index = 0; index < count; index++) { - if(names[index] == "conversation_id") - conversation_id += stoll(values[index]); - else if(names[index] == "file_path") - file_path += values[index]; - } - } catch(std::exception& ex) { - logError(ref_server->getServerId(), "[Conversations] Failed to parse conversation entry! Exception: {}", ex.what()); - return 0; - } + try { + for(int index = 0; index < count; index++) { + if(names[index] == "conversation_id") + conversation_id += stoll(values[index]); + else if(names[index] == "file_path") + file_path += values[index]; + } + } catch(std::exception& ex) { + logError(ref_server->getServerId(), "[Conversations] Failed to parse conversation entry! Exception: {}", ex.what()); + return 0; + } - auto conversation = make_shared(_this, conversation_id, file_path); - conversation->set_ref_self(conversation); - string error; - if(!conversation->initialize(error)) { - logError(ref_server->getServerId(), "[Conversations] Failed to load conversation for channel {}: {}. Conversation is in volatile mode", conversation_id, error); - } - this->_conversations.push_back(conversation); - return 0; - }); - LOG_SQL_CMD(result); - debugMessage(ref_server->getServerId(), "[Conversations] Loaded {} conversations", this->_conversations.size()); + auto conversation = make_shared(_this, conversation_id, file_path); + conversation->set_ref_self(conversation); + string error; + if(!conversation->initialize(error)) { + logError(ref_server->getServerId(), "[Conversations] Failed to load conversation for channel {}: {}. Conversation is in volatile mode", conversation_id, error); + } + this->_conversations.push_back(conversation); + return 0; + }); + LOG_SQL_CMD(result); + debugMessage(ref_server->getServerId(), "[Conversations] Loaded {} conversations", this->_conversations.size()); } bool ConversationManager::conversation_exists(ts::ChannelId channel_id) { - lock_guard lock(this->_conversations_lock); - return find_if(this->_conversations.begin(), this->_conversations.end(), [&](const shared_ptr& conv){ return conv->channel_id() == channel_id; })!= this->_conversations.end(); + lock_guard lock(this->_conversations_lock); + return find_if(this->_conversations.begin(), this->_conversations.end(), [&](const shared_ptr& conv){ return conv->channel_id() == channel_id; })!= this->_conversations.end(); } std::shared_ptr ConversationManager::get(ts::ChannelId channel_id) { - unique_lock lock(this->_conversations_lock); - auto found = find_if(this->_conversations.begin(), this->_conversations.end(), [&](const shared_ptr& conv){ return conv->channel_id() == channel_id; }); - if(found != this->_conversations.end()) - return *found; - return nullptr; + unique_lock lock(this->_conversations_lock); + auto found = find_if(this->_conversations.begin(), this->_conversations.end(), [&](const shared_ptr& conv){ return conv->channel_id() == channel_id; }); + if(found != this->_conversations.end()) + return *found; + return nullptr; } std::shared_ptr ConversationManager::get_or_create(ts::ChannelId channel_id) { - unique_lock lock(this->_conversations_lock); - auto found = find_if(this->_conversations.begin(), this->_conversations.end(), [&](const shared_ptr& conv){ return conv->channel_id() == channel_id; }); - if(found != this->_conversations.end()) - return *found; + unique_lock lock(this->_conversations_lock); + auto found = find_if(this->_conversations.begin(), this->_conversations.end(), [&](const shared_ptr& conv){ return conv->channel_id() == channel_id; }); + if(found != this->_conversations.end()) + return *found; - auto ref_server = this->ref_server(); - assert(ref_server); + auto ref_server = this->ref_server(); + assert(ref_server); - //TODO: More configurable! - auto file_path = "files/server_" + to_string(ref_server->getServerId()) + "/conversations/conversation_" + to_string(channel_id) + ".cvs"; - auto conversation = make_shared(this->_ref_this.lock(), channel_id, file_path); - conversation->set_ref_self(conversation); - this->_conversations.push_back(conversation); - lock.unlock(); + //TODO: More configurable! + auto file_path = "files/server_" + to_string(ref_server->getServerId()) + "/conversations/conversation_" + to_string(channel_id) + ".cvs"; + auto conversation = make_shared(this->_ref_this.lock(), channel_id, file_path); + conversation->set_ref_self(conversation); + this->_conversations.push_back(conversation); + lock.unlock(); - { - auto sql_result = sql::command(ref_server->getSql(), "INSERT INTO `conversations` (`server_id`, `channel_id`, `conversation_id`, `file_path`) VALUES (:sid, :cid, :cid, :fpath)", - variable{":sid", ref_server->getServerId()}, variable{":cid", channel_id}, variable{":fpath", file_path}).executeLater(); - sql_result.waitAndGetLater(LOG_SQL_CMD, {-1, "future error"}); - } + { + auto sql_result = sql::command(ref_server->getSql(), "INSERT INTO `conversations` (`server_id`, `channel_id`, `conversation_id`, `file_path`) VALUES (:sid, :cid, :cid, :fpath)", + variable{":sid", ref_server->getServerId()}, variable{":cid", channel_id}, variable{":fpath", file_path}).executeLater(); + sql_result.waitAndGetLater(LOG_SQL_CMD, {-1, "future error"}); + } - string error; - if(!conversation->initialize(error)) - logError(ref_server->getServerId(), "[Conversations] Failed to load conversation for channel {}: {}. Conversation is in volatile mode", channel_id, error); - return conversation; + string error; + if(!conversation->initialize(error)) + logError(ref_server->getServerId(), "[Conversations] Failed to load conversation for channel {}: {}. Conversation is in volatile mode", channel_id, error); + return conversation; } void ConversationManager::delete_conversation(ts::ChannelId channel_id) { - { - lock_guard lock(this->_conversations_lock); - this->_conversations.erase(remove_if(this->_conversations.begin(), this->_conversations.end(), [&](const shared_ptr& conv){ return conv->channel_id() == channel_id; }), this->_conversations.end()); - } + { + lock_guard lock(this->_conversations_lock); + this->_conversations.erase(remove_if(this->_conversations.begin(), this->_conversations.end(), [&](const shared_ptr& conv){ return conv->channel_id() == channel_id; }), this->_conversations.end()); + } - auto ref_server = this->ref_server(); - if(!ref_server) { - logWarning(ref_server->getServerId(), "[Conversations][{}] Failed to delete conversation (server expired)", channel_id); - return; - } + auto ref_server = this->ref_server(); + if(!ref_server) { + logWarning(ref_server->getServerId(), "[Conversations][{}] Failed to delete conversation (server expired)", channel_id); + return; + } - auto sql = ref_server->getSql(); - if(!sql) { - logWarning(ref_server->getServerId(), "[Conversations][{}] Failed to delete conversation (sql expired)", channel_id); - return; - } + auto sql = ref_server->getSql(); + if(!sql) { + logWarning(ref_server->getServerId(), "[Conversations][{}] Failed to delete conversation (sql expired)", channel_id); + return; + } - { - auto sql_result = sql::command(sql, "DELETE FROM `conversations` WHERE `server_id` = :sid AND `channel_id` = :cid AND `conversation_id` = :cid", variable{":sid", ref_server->getServerId()}, variable{":cid", channel_id}).executeLater(); - sql_result.waitAndGetLater(LOG_SQL_CMD, {-1, "future error"}); - } - { - auto sql_result = sql::command(sql, "DELETE FROM `conversation_blocks` WHERE `server_id` = :sid AND `conversation_id` = :cid", variable{":sid", ref_server->getServerId()}, variable{":cid", channel_id}).executeLater(); - sql_result.waitAndGetLater(LOG_SQL_CMD, {-1, "future error"}); - } + { + auto sql_result = sql::command(sql, "DELETE FROM `conversations` WHERE `server_id` = :sid AND `channel_id` = :cid AND `conversation_id` = :cid", variable{":sid", ref_server->getServerId()}, variable{":cid", channel_id}).executeLater(); + sql_result.waitAndGetLater(LOG_SQL_CMD, {-1, "future error"}); + } + { + auto sql_result = sql::command(sql, "DELETE FROM `conversation_blocks` WHERE `server_id` = :sid AND `conversation_id` = :cid", variable{":sid", ref_server->getServerId()}, variable{":cid", channel_id}).executeLater(); + sql_result.waitAndGetLater(LOG_SQL_CMD, {-1, "future error"}); + } - //TODO: More configurable! - auto file_path = "files/server_" + to_string(ref_server->getServerId()) + "/conversations/conversation_" + to_string(channel_id) + ".cvs"; - try { - fs::remove(fs::u8path(file_path)); - } catch(fs::filesystem_error& error) { - logWarning(ref_server->getServerId(), "[Conversations][{}] Failed to delete data file ({}): {}|{}", channel_id, file_path, error.code().value(), error.what()); - } + //TODO: More configurable! + auto file_path = "files/server_" + to_string(ref_server->getServerId()) + "/conversations/conversation_" + to_string(channel_id) + ".cvs"; + try { + fs::remove(fs::u8path(file_path)); + } catch(fs::filesystem_error& error) { + logWarning(ref_server->getServerId(), "[Conversations][{}] Failed to delete data file ({}): {}|{}", channel_id, file_path, error.code().value(), error.what()); + } } void ConversationManager::synchronize_channels() { - auto ref_server = this->ref_server(); - if(!ref_server) { - logWarning(ref_server->getServerId(), "[Conversations][{}] Failed to synchronize conversations (server expired)"); - return; - } + auto ref_server = this->ref_server(); + if(!ref_server) { + logWarning(ref_server->getServerId(), "[Conversations][{}] Failed to synchronize conversations (server expired)"); + return; + } - auto chan_tree = ref_server->getChannelTree(); - deque> channels{this->_conversations}; - for(auto& channel : channels) { - auto schannel = chan_tree->findChannel(channel->channel_id()); - if(!schannel) { - logMessage(ref_server->getServerId(), "[Conversations][{}] Deleting conversation because channel does not exists anymore.", channel->channel_id()); - this->delete_conversation(channel->channel_id()); - continue; - } + auto chan_tree = ref_server->getChannelTree(); + deque> channels{this->_conversations}; + for(auto& channel : channels) { + auto schannel = chan_tree->findChannel(channel->channel_id()); + if(!schannel) { + logMessage(ref_server->getServerId(), "[Conversations][{}] Deleting conversation because channel does not exists anymore.", channel->channel_id()); + this->delete_conversation(channel->channel_id()); + continue; + } - auto history_size = schannel->properties()[property::CHANNEL_CONVERSATION_HISTORY_LENGTH].as(); - channel->set_history_length(history_size); - } + auto history_size = schannel->properties()[property::CHANNEL_CONVERSATION_HISTORY_LENGTH].as(); + channel->set_history_length(history_size); + } } void ConversationManager::cleanup_cache() { - unique_lock lock(this->_conversations_lock); - std::vector> conversations{this->_conversations.begin(), this->_conversations.end()}; - lock.unlock(); + unique_lock lock(this->_conversations_lock); + std::vector> conversations{this->_conversations.begin(), this->_conversations.end()}; + lock.unlock(); - for(auto& conversation : conversations) - conversation->cleanup_cache(); + for(auto& conversation : conversations) + conversation->cleanup_cache(); } \ No newline at end of file diff --git a/server/src/manager/ConversationManager.h b/server/src/manager/ConversationManager.h index b29c6eb..b212a57 100644 --- a/server/src/manager/ConversationManager.h +++ b/server/src/manager/ConversationManager.h @@ -10,247 +10,247 @@ #include namespace ts { - namespace server { - class TSServer; - namespace conversation { - struct ConversationEntry { - std::chrono::system_clock::time_point message_timestamp; + namespace server { + class TSServer; + namespace conversation { + struct ConversationEntry { + std::chrono::system_clock::time_point message_timestamp; - ClientDbId sender_database_id; - std::string sender_unique_id; - std::string sender_name; + ClientDbId sender_database_id; + std::string sender_unique_id; + std::string sender_name; - std::string message; - bool flag_message_deleted; - }; + std::string message; + bool flag_message_deleted; + }; - namespace fio { - #pragma pack(push, 1) - struct BlockHeader { - static constexpr uint64_t HEADER_COOKIE = 0xC0FFEEBABE; - static constexpr uint64_t MAX_BLOCK_SIZE = 0xFF00; + namespace fio { + #pragma pack(push, 1) + struct BlockHeader { + static constexpr uint64_t HEADER_COOKIE = 0xC0FFEEBABE; + static constexpr uint64_t MAX_BLOCK_SIZE = 0xFF00; - uint8_t version; /* every time 1 */ - uint64_t cookie /* const 0xC0FFEEBABE */; - uint8_t message_version; /* every time 1; Version for the containing messages */ + uint8_t version; /* every time 1 */ + uint64_t cookie /* const 0xC0FFEEBABE */; + uint8_t message_version; /* every time 1; Version for the containing messages */ - uint32_t block_size; /* size of the full block (with data) incl. header! */ - uint32_t block_max_size; /* size of the full block incl. header! 0 if the block is located at the end and could be extended */ + uint32_t block_size; /* size of the full block (with data) incl. header! */ + uint32_t block_max_size; /* size of the full block incl. header! 0 if the block is located at the end and could be extended */ - uint32_t message_count; /* message count */ - uint32_t last_message_offset; /* offset to the last message. Offset begins after header (first message has offset of 0) */ - union { - uint8_t flags; - struct { - uint8_t _padding : 5; + uint32_t message_count; /* message count */ + uint32_t last_message_offset; /* offset to the last message. Offset begins after header (first message has offset of 0) */ + union { + uint8_t flags; + struct { + uint8_t _padding : 5; - bool message_encrypted: 1; /* 0x04 */ - bool meta_encrypted: 1; /* 0x02 */ /* Not implemented */ - bool finished: 1; /* 0x01 */ /* if this block is finally finished; Most the time a next block follows directly */ - }; - }; + bool message_encrypted: 1; /* 0x04 */ + bool meta_encrypted: 1; /* 0x02 */ /* Not implemented */ + bool finished: 1; /* 0x01 */ /* if this block is finally finished; Most the time a next block follows directly */ + }; + }; - uint64_t first_message_timestamp; - uint64_t last_message_timestamp; - }; - static_assert(__BYTE_ORDER == __LITTLE_ENDIAN); - static_assert(sizeof(BlockHeader) == 43); + uint64_t first_message_timestamp; + uint64_t last_message_timestamp; + }; + static_assert(__BYTE_ORDER == __LITTLE_ENDIAN); + static_assert(sizeof(BlockHeader) == 43); - struct MessageHeader { - static constexpr uint16_t HEADER_COOKIE = 0xAFFE; - uint16_t cookie; /* const 0xAFFE */ - uint16_t total_length; /* Total length of the full message data. Includes this header! */ - uint64_t message_timestamp; /* milliseconds since epoch */ - uint64_t sender_database_id; - uint8_t sender_unique_id_length; /* directly followed by this header */ - uint8_t sender_name_length; /* directly followed after the unique id */ - uint16_t message_length; /* directly followed after the name */ - union { - uint16_t message_flags; /* could be later something like deleted etc.... */ - struct { - uint16_t _flags_padding: 15; - bool flag_deleted: 1; - }; - }; - }; - static_assert(sizeof(MessageHeader) == 26); - #pragma pack(pop) + struct MessageHeader { + static constexpr uint16_t HEADER_COOKIE = 0xAFFE; + uint16_t cookie; /* const 0xAFFE */ + uint16_t total_length; /* Total length of the full message data. Includes this header! */ + uint64_t message_timestamp; /* milliseconds since epoch */ + uint64_t sender_database_id; + uint8_t sender_unique_id_length; /* directly followed by this header */ + uint8_t sender_name_length; /* directly followed after the unique id */ + uint16_t message_length; /* directly followed after the name */ + union { + uint16_t message_flags; /* could be later something like deleted etc.... */ + struct { + uint16_t _flags_padding: 15; + bool flag_deleted: 1; + }; + }; + }; + static_assert(sizeof(MessageHeader) == 26); + #pragma pack(pop) - struct IndexedMessageData { - std::string sender_unique_id; - std::string sender_name; - std::string message; - }; + struct IndexedMessageData { + std::string sender_unique_id; + std::string sender_name; + std::string message; + }; - struct IndexedBlockMessage { - uint32_t message_offset; - MessageHeader header; + struct IndexedBlockMessage { + uint32_t message_offset; + MessageHeader header; - std::shared_ptr message_data; - }; + std::shared_ptr message_data; + }; - struct IndexedBlock { - bool index_successful; - std::deque message_index; - std::mutex message_index_lock; - }; - } + struct IndexedBlock { + bool index_successful; + std::deque message_index; + std::mutex message_index_lock; + }; + } - namespace db { - struct MessageBlock { - std::chrono::system_clock::time_point begin_timestamp; - std::chrono::system_clock::time_point end_timestamp; + namespace db { + struct MessageBlock { + std::chrono::system_clock::time_point begin_timestamp; + std::chrono::system_clock::time_point end_timestamp; - uint64_t block_offset; + uint64_t block_offset; - union { - uint16_t flags; - struct { - //Attention: Order matters! - bool flag__unused_0 : 1; - bool flag__unused_1 : 1; - bool flag__unused_2 : 1; - bool flag__unused_3 : 1; - bool flag__unused_4 : 1; - bool flag__unused_5 : 1; - bool flag__unused_6 : 1; - bool flag__unused_7 : 1; - bool flag__unused_8 : 1; - bool flag__unused_9 : 1; - bool flag__unused_10 : 1; - bool flag__unused_11 : 1; + union { + uint16_t flags; + struct { + //Attention: Order matters! + bool flag__unused_0 : 1; + bool flag__unused_1 : 1; + bool flag__unused_2 : 1; + bool flag__unused_3 : 1; + bool flag__unused_4 : 1; + bool flag__unused_5 : 1; + bool flag__unused_6 : 1; + bool flag__unused_7 : 1; + bool flag__unused_8 : 1; + bool flag__unused_9 : 1; + bool flag__unused_10 : 1; + bool flag__unused_11 : 1; - bool flag_finished : 1; - bool flag_finished_later : 1; /* if true the block has been closed because we've a newer block. */ + bool flag_finished : 1; + bool flag_finished_later : 1; /* if true the block has been closed because we've a newer block. */ - bool flag_invalid : 1; /* this block is considered as invalid and will be ignored */ - bool flag_used : 1; - }; - }; + bool flag_invalid : 1; /* this block is considered as invalid and will be ignored */ + bool flag_used : 1; + }; + }; - std::shared_ptr block_header; - std::shared_ptr indexed_block; - }; - static_assert(__BYTE_ORDER == __LITTLE_ENDIAN); - } + std::shared_ptr block_header; + std::shared_ptr indexed_block; + }; + static_assert(__BYTE_ORDER == __LITTLE_ENDIAN); + } - class ConversationManager; - class Conversation { - public: - Conversation(const std::shared_ptr& /* handle */, ChannelId /* channel id */, std::string /* file name */); - ~Conversation(); + class ConversationManager; + class Conversation { + public: + Conversation(const std::shared_ptr& /* handle */, ChannelId /* channel id */, std::string /* file name */); + ~Conversation(); - bool initialize(std::string& error); - void finalize(); + bool initialize(std::string& error); + void finalize(); - inline ChannelId channel_id() { return this->_channel_id; } - /* if for some reason we're not able to open the file then we're in volatile mode */ - inline bool volatile_only() { return this->_volatile || this->_history_length < 0; } - void cleanup_cache(); + inline ChannelId channel_id() { return this->_channel_id; } + /* if for some reason we're not able to open the file then we're in volatile mode */ + inline bool volatile_only() { return this->_volatile || this->_history_length < 0; } + void cleanup_cache(); - void set_history_length(ssize_t length) { this->_history_length = length; } - ssize_t history_length() { return this->_history_length; } + void set_history_length(ssize_t length) { this->_history_length = length; } + ssize_t history_length() { return this->_history_length; } - inline std::chrono::system_clock::time_point last_message() { return this->_last_message_timestamp; } - void register_message(ClientDbId sender_database_id, const std::string& sender_unique_id, const std::string& sender_name, const std::chrono::system_clock::time_point& /* timestamp */, const std::string& message); - /* Lookup n messages since end timestamp. Upper time limit is begin timestamp */ - std::deque> message_history(const std::chrono::system_clock::time_point& /* end timestamp */, size_t /* limit */, const std::chrono::system_clock::time_point& /* begin timestamp */); + inline std::chrono::system_clock::time_point last_message() { return this->_last_message_timestamp; } + void register_message(ClientDbId sender_database_id, const std::string& sender_unique_id, const std::string& sender_name, const std::chrono::system_clock::time_point& /* timestamp */, const std::string& message); + /* Lookup n messages since end timestamp. Upper time limit is begin timestamp */ + std::deque> message_history(const std::chrono::system_clock::time_point& /* end timestamp */, size_t /* limit */, const std::chrono::system_clock::time_point& /* begin timestamp */); - std::deque> message_history(size_t limit) { - return this->message_history(std::chrono::system_clock::now(), limit, std::chrono::system_clock::time_point{}); - } + std::deque> message_history(size_t limit) { + return this->message_history(std::chrono::system_clock::now(), limit, std::chrono::system_clock::time_point{}); + } - size_t delete_messages(const std::chrono::system_clock::time_point& /* end timestamp */, size_t /* limit */, const std::chrono::system_clock::time_point& /* begin timestamp */, ClientDbId /* database id */); + size_t delete_messages(const std::chrono::system_clock::time_point& /* end timestamp */, size_t /* limit */, const std::chrono::system_clock::time_point& /* begin timestamp */, ClientDbId /* database id */); - ts_always_inline void set_ref_self(const std::shared_ptr& pointer) { - this->_ref_self = pointer; - } - private: - std::weak_ptr _ref_self; - std::weak_ptr _ref_handle; - ts_always_inline std::shared_ptr ref_handle() { - return this->_ref_handle.lock(); - } + ts_always_inline void set_ref_self(const std::shared_ptr& pointer) { + this->_ref_self = pointer; + } + private: + std::weak_ptr _ref_self; + std::weak_ptr _ref_handle; + ts_always_inline std::shared_ptr ref_handle() { + return this->_ref_handle.lock(); + } - inline bool setup_file(); - inline ssize_t fread(void* target, size_t length, ssize_t index, bool acquire_handle); - inline ssize_t fwrite(void* target, size_t length, ssize_t index, bool extend_file, bool acquire_handle); + inline bool setup_file(); + inline ssize_t fread(void* target, size_t length, ssize_t index, bool acquire_handle); + inline ssize_t fwrite(void* target, size_t length, ssize_t index, bool extend_file, bool acquire_handle); - /* block db functions */ - void db_save_block(const std::shared_ptr& /* block */); - std::shared_ptr db_create_block(uint64_t /* block offset */); + /* block db functions */ + void db_save_block(const std::shared_ptr& /* block */); + std::shared_ptr db_create_block(uint64_t /* block offset */); - /* message blocks */ - std::mutex message_block_lock; - /* blocks sorted desc (newest blocks last in list (push_back)) */ - std::deque> message_blocks; - /* Access last_message_block only within the write queue or while initializing! */ - std::shared_ptr last_message_block; /* is registered within message_blocks,but musnt be the last! */ - bool load_message_block_header(const std::shared_ptr& /* block */, std::string& /* error */); - bool load_message_block_index(const std::shared_ptr& /* block */, std::string& /* error */); - bool load_messages(const std::shared_ptr& /* block */, size_t /* begin index */, size_t /* end index */, std::string& /* error */); + /* message blocks */ + std::mutex message_block_lock; + /* blocks sorted desc (newest blocks last in list (push_back)) */ + std::deque> message_blocks; + /* Access last_message_block only within the write queue or while initializing! */ + std::shared_ptr last_message_block; /* is registered within message_blocks,but musnt be the last! */ + bool load_message_block_header(const std::shared_ptr& /* block */, std::string& /* error */); + bool load_message_block_index(const std::shared_ptr& /* block */, std::string& /* error */); + bool load_messages(const std::shared_ptr& /* block */, size_t /* begin index */, size_t /* end index */, std::string& /* error */); - /* message blocks write stuff */ - std::shared_ptr create_new_block(std::string& /* error */); - void finish_block(const std::shared_ptr& /* block */, bool write_file); - bool write_block_header(const std::shared_ptr& /* header */, size_t /* header index */, std::string& /* error */); + /* message blocks write stuff */ + std::shared_ptr create_new_block(std::string& /* error */); + void finish_block(const std::shared_ptr& /* block */, bool write_file); + bool write_block_header(const std::shared_ptr& /* header */, size_t /* header index */, std::string& /* error */); - /* cached messages */ - std::mutex _last_messages_lock; - std::deque> _last_messages; - size_t _last_messages_limit = 100; /* cache max 100 messages */ + /* cached messages */ + std::mutex _last_messages_lock; + std::deque> _last_messages; + size_t _last_messages_limit = 100; /* cache max 100 messages */ - /* write handler */ - std::mutex _write_loop_lock; - std::mutex _write_queue_lock; - std::deque> _write_queue; - std::shared_ptr> _write_event; - void process_write_queue(const std::chrono::system_clock::time_point&); + /* write handler */ + std::mutex _write_loop_lock; + std::mutex _write_queue_lock; + std::deque> _write_queue; + std::shared_ptr> _write_event; + void process_write_queue(const std::chrono::system_clock::time_point&); - /* basic file stuff */ - std::string file_name; - std::mutex file_handle_lock; - std::chrono::system_clock::time_point last_access; - FILE* file_handle = nullptr; - ChannelId _channel_id; + /* basic file stuff */ + std::string file_name; + std::mutex file_handle_lock; + std::chrono::system_clock::time_point last_access; + FILE* file_handle = nullptr; + ChannelId _channel_id; - ssize_t _history_length = 0; - bool _volatile = false; + ssize_t _history_length = 0; + bool _volatile = false; - std::chrono::system_clock::time_point _last_message_timestamp; - }; + std::chrono::system_clock::time_point _last_message_timestamp; + }; - class ConversationManager { - public: - ConversationManager(const std::shared_ptr& /* server */); - virtual ~ConversationManager(); + class ConversationManager { + public: + ConversationManager(const std::shared_ptr& /* server */); + virtual ~ConversationManager(); - void initialize(const std::shared_ptr& _this); - void synchronize_channels(); - void cleanup_cache(); + void initialize(const std::shared_ptr& _this); + void synchronize_channels(); + void cleanup_cache(); - bool conversation_exists(ChannelId /* channel */); - std::shared_ptr get(ChannelId /* channel */); - std::shared_ptr get_or_create(ChannelId /* channel */); - void delete_conversation(ChannelId /* channel */); + bool conversation_exists(ChannelId /* channel */); + std::shared_ptr get(ChannelId /* channel */); + std::shared_ptr get_or_create(ChannelId /* channel */); + void delete_conversation(ChannelId /* channel */); - inline const std::deque> conversations() { - std::lock_guard lock(this->_conversations_lock); - return this->_conversations; - } + inline const std::deque> conversations() { + std::lock_guard lock(this->_conversations_lock); + return this->_conversations; + } - ts_always_inline std::shared_ptr ref_server() { - return this->_ref_server.lock(); - } - private: - std::weak_ptr _ref_this; - std::weak_ptr _ref_server; + ts_always_inline std::shared_ptr ref_server() { + return this->_ref_server.lock(); + } + private: + std::weak_ptr _ref_this; + std::weak_ptr _ref_server; - std::mutex _conversations_lock; - std::deque> _conversations; + std::mutex _conversations_lock; + std::deque> _conversations; - std::string file_path; - }; - } - } + std::string file_path; + }; + } + } } \ No newline at end of file diff --git a/server/src/manager/IpListManager.cpp b/server/src/manager/IpListManager.cpp index 816f654..da6db1f 100644 --- a/server/src/manager/IpListManager.cpp +++ b/server/src/manager/IpListManager.cpp @@ -14,74 +14,74 @@ using namespace ts; IpListManager::IpListManager(const std::string& file, const std::deque& def) : file(file), default_entries(def) { } bool file_exists(const std::string& name) { - struct stat buffer{}; - return (stat (name.c_str(), &buffer) == 0); + struct stat buffer{}; + return (stat (name.c_str(), &buffer) == 0); } 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; + 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; } bool IpListManager::reload(std::string& error) { - if(!file_exists(this->file)) { - ofstream os(this->file); - if(!os) { - error = "Could not create default file!"; - return false; - } - for(const auto& entry : this->default_entries) - os << entry << endl; - os.flush(); - os.close(); - } - ifstream stream(this->file); - if(!stream) { - error = "Failed to read file!"; - return false; - } + if(!file_exists(this->file)) { + ofstream os(this->file); + if(!os) { + error = "Could not create default file!"; + return false; + } + for(const auto& entry : this->default_entries) + os << entry << endl; + os.flush(); + os.close(); + } + ifstream stream(this->file); + if(!stream) { + error = "Failed to read file!"; + return false; + } - string line; - int line_number = 0; - while(getline(stream, line)) { - line_number++; - line = strip(line); - if(line.empty() || line[0] == '#') continue; + string line; + int line_number = 0; + while(getline(stream, line)) { + line_number++; + line = strip(line); + if(line.empty() || line[0] == '#') continue; - IPEntry result{}; - if(!this->parse_entry(result, line)) - logError(0, "Failed to parse ip entry at line {} of file {}. Line: '{}'", line_number, this->file, line); - else - this->entries.push_back(result); - } + IPEntry result{}; + if(!this->parse_entry(result, line)) + logError(0, "Failed to parse ip entry at line {} of file {}. Line: '{}'", line_number, this->file, line); + else + this->entries.push_back(result); + } - return true; + return true; } bool IpListManager::contains(const sockaddr_storage &address) { - for(const auto& entry : this->entries) - if(net::address_equal_ranged(address, entry.address, entry.range)) - return true; - return false; + for(const auto& entry : this->entries) + if(net::address_equal_ranged(address, entry.address, entry.range)) + return true; + return false; } bool IpListManager::parse_entry(ts::IpListManager::IPEntry &result, const std::string &line) { - auto seperator = line.find('/'); - auto address = line.substr(0, seperator); - auto mask = seperator == string::npos ? "255" : line.substr(seperator + 1); + auto seperator = line.find('/'); + auto address = line.substr(0, seperator); + auto mask = seperator == string::npos ? "255" : line.substr(seperator + 1); - if(!net::resolve_address(address, result.address)) - return false; + if(!net::resolve_address(address, result.address)) + return false; - try { - result.range = static_cast(stoll(mask)); - } catch(std::exception& ex) { - return false; - } - return true; + try { + result.range = static_cast(stoll(mask)); + } catch(std::exception& ex) { + return false; + } + return true; } \ No newline at end of file diff --git a/server/src/manager/IpListManager.h b/server/src/manager/IpListManager.h index d9450c1..95e6301 100644 --- a/server/src/manager/IpListManager.h +++ b/server/src/manager/IpListManager.h @@ -5,25 +5,25 @@ #include namespace ts { - class IpListManager { - struct IPEntry { - sockaddr_storage address; - uint8_t range; /* [0;32] or [0;128] */ - }; - public: - IpListManager(const std::string&, const std::deque&); + class IpListManager { + struct IPEntry { + sockaddr_storage address; + uint8_t range; /* [0;32] or [0;128] */ + }; + public: + IpListManager(const std::string&, const std::deque&); - bool reload(std::string&); + bool reload(std::string&); - std::string file_name() { return this->file; } - std::deque addresses() { return this->entries; } + std::string file_name() { return this->file; } + std::deque addresses() { return this->entries; } - bool contains(const sockaddr_storage& address); - private: - std::string file; - std::deque entries; - std::deque default_entries; + bool contains(const sockaddr_storage& address); + private: + std::string file; + std::deque entries; + std::deque default_entries; - bool parse_entry(IPEntry& result, const std::string& /* line */); - }; + bool parse_entry(IPEntry& result, const std::string& /* line */); + }; } \ No newline at end of file diff --git a/server/src/manager/LetterManager.cpp b/server/src/manager/LetterManager.cpp index 4bdf93d..600be6b 100644 --- a/server/src/manager/LetterManager.cpp +++ b/server/src/manager/LetterManager.cpp @@ -15,25 +15,25 @@ LetterManager::LetterManager(server::TSServer* server) : server(server) {} LetterManager::~LetterManager() {} size_t LetterManager::unread_letter_count(const ts::ClientUid &client_unique_id) { - size_t result = 0; - auto res = sql::command(this->server->getSql(), "SELECT COUNT(*) FROM `letters` WHERE `serverId` = :sid AND `receiver` = :uid AND `read` = :false", - variable{":sid", this->server ? this->server->getServerId() : 0}, - variable{":uid", client_unique_id}, - variable{":false", 0} - ).query([&](int length, std::string* values, std::string* columns) { - if(length != 1) - return 1; + size_t result = 0; + auto res = sql::command(this->server->getSql(), "SELECT COUNT(*) FROM `letters` WHERE `serverId` = :sid AND `receiver` = :uid AND `read` = :false", + variable{":sid", this->server ? this->server->getServerId() : 0}, + variable{":uid", client_unique_id}, + variable{":false", 0} + ).query([&](int length, std::string* values, std::string* columns) { + if(length != 1) + return 1; - try { - result = stoll(values[0]); - } catch(std::exception& ex) { - logError(this->server ? this->server->getServerId() : 0, "Failed to parse unread letter count: {}", ex.what()); - return 1; - } - return 0; - }); - (LOG_SQL_CMD)(res); - return result; + try { + result = stoll(values[0]); + } catch(std::exception& ex) { + logError(this->server ? this->server->getServerId() : 0, "Failed to parse unread letter count: {}", ex.what()); + return 1; + } + return 0; + }); + (LOG_SQL_CMD)(res); + return result; } std::vector> LetterManager::avariableLetters(ClientUid cluid) { diff --git a/server/src/manager/PermissionNameManager.cpp b/server/src/manager/PermissionNameManager.cpp index b87c2dd..188bba3 100644 --- a/server/src/manager/PermissionNameManager.cpp +++ b/server/src/manager/PermissionNameManager.cpp @@ -12,111 +12,111 @@ PermissionNameMapper::~PermissionNameMapper() {} std::string PermissionNameMapper::permission_name(const Type::value& type, const ts::permission::PermissionType &permission) const { - const static std::string unknown = "unknown"; + const static std::string unknown = "unknown"; - if(type < 0 || type > Type::MAX) - return unknown; + if(type < 0 || type > Type::MAX) + return unknown; - if(permission < 0 || permission >= PermissionType::permission_id_max) - return unknown; + if(permission < 0 || permission >= PermissionType::permission_id_max) + return unknown; - return this->mapping[type][permission].mapped_name; + return this->mapping[type][permission].mapped_name; } std::string PermissionNameMapper::permission_name_grant(const Type::value& type, const ts::permission::PermissionType &permission) const { - const static std::string unknown = "unknown"; + const static std::string unknown = "unknown"; - if(type < 0 || type > Type::MAX) - return unknown; + if(type < 0 || type > Type::MAX) + return unknown; - if(permission < 0 || permission >= PermissionType::permission_id_max) - return unknown; + if(permission < 0 || permission >= PermissionType::permission_id_max) + return unknown; - return this->mapping[type][permission].grant_name; + return this->mapping[type][permission].grant_name; } bool PermissionNameMapper::initialize(const std::string &file, std::string &error) { - auto file_path = fs::u8path(file); - if(!fs::exists(file)) { - error = "file does not exists"; - return false; - } + auto file_path = fs::u8path(file); + if(!fs::exists(file)) { + error = "file does not exists"; + return false; + } - ifstream istream(file_path); - if(!istream.good()) { - error = "failed to open file"; - return false; - } + ifstream istream(file_path); + if(!istream.good()) { + error = "failed to open file"; + return false; + } - array, PermissionType::permission_id_max> mapping; + array, PermissionType::permission_id_max> mapping; - string line; - auto current_type = Type::MAX; - size_t line_index = 0; + string line; + auto current_type = Type::MAX; + size_t line_index = 0; - std::string key, value; - while(istream) { - line.clear(); - getline(istream, line); - line_index++; - if(line.empty() || line[0] == '#') - continue; + std::string key, value; + while(istream) { + line.clear(); + getline(istream, line); + line_index++; + if(line.empty() || line[0] == '#') + continue; - auto seperator = line.find(':'); - if(seperator == -1) { - logWarning(LOG_INSTANCE, "Invalid permission mapping line {}: {}", line_index, line); - continue; - } + auto seperator = line.find(':'); + if(seperator == -1) { + logWarning(LOG_INSTANCE, "Invalid permission mapping line {}: {}", line_index, line); + continue; + } - key = line.substr(0, seperator); - value = line.substr(seperator + 1); + key = line.substr(0, seperator); + value = line.substr(seperator + 1); - if(key == "group") { - try { - auto index = stol(value); - if(index < 0 || index >= Type::MAX) { - logWarning(LOG_INSTANCE, "Invalid permission group at {}. Value is not in bounds: {}", line_index, line); - continue; - } + if(key == "group") { + try { + auto index = stol(value); + if(index < 0 || index >= Type::MAX) { + logWarning(LOG_INSTANCE, "Invalid permission group at {}. Value is not in bounds: {}", line_index, line); + continue; + } - current_type = (Type::value) index; - continue; - } catch(std::exception& ex) { - logWarning(LOG_INSTANCE, "Invalid permission group at {}. Value is not a number: {}", line_index, line); - continue; - } - } else if(key == "mapping") { - if(current_type == Type::MAX) { - logWarning(LOG_INSTANCE, "Invalid permission mapping entry at line {} (No group set): {}", line_index, line); - continue; - } - seperator = value.find(':'); - if(seperator == -1) { - logWarning(LOG_INSTANCE, "Invalid permission mapping entry at line {} (Missing colon): {}", line_index, line); - continue; - } - key = value.substr(0, seperator); - value = value.substr(seperator + 1); + current_type = (Type::value) index; + continue; + } catch(std::exception& ex) { + logWarning(LOG_INSTANCE, "Invalid permission group at {}. Value is not a number: {}", line_index, line); + continue; + } + } else if(key == "mapping") { + if(current_type == Type::MAX) { + logWarning(LOG_INSTANCE, "Invalid permission mapping entry at line {} (No group set): {}", line_index, line); + continue; + } + seperator = value.find(':'); + if(seperator == -1) { + logWarning(LOG_INSTANCE, "Invalid permission mapping entry at line {} (Missing colon): {}", line_index, line); + continue; + } + key = value.substr(0, seperator); + value = value.substr(seperator + 1); - mapping[current_type][key] = value; - } else { - logWarning(LOG_INSTANCE, "Invalid permission mapping line at {}. Key is unknown: {}", line_index, line); - continue; - } - } + mapping[current_type][key] = value; + } else { + logWarning(LOG_INSTANCE, "Invalid permission mapping line at {}. Key is unknown: {}", line_index, line); + continue; + } + } - /* lets build the index */ - for(Type::value type = Type::MIN; type < Type::MAX; (*(int*) &type)++) { - auto& array = this->mapping[type]; - auto& map = mapping[type]; + /* lets build the index */ + for(Type::value type = Type::MIN; type < Type::MAX; (*(int*) &type)++) { + auto& array = this->mapping[type]; + auto& map = mapping[type]; - for(PermissionType permission = PermissionType::permission_id_min; permission < PermissionType::permission_id_max; (*(uint16_t *)&permission)++) { - auto data = permission::resolvePermissionData(permission); + for(PermissionType permission = PermissionType::permission_id_min; permission < PermissionType::permission_id_max; (*(uint16_t *)&permission)++) { + auto data = permission::resolvePermissionData(permission); - array[permission].mapped_name = map.count(data->name) > 0 ? map[data->name] : data->name; - array[permission].grant_name = "i_needed_modify_power_" + array[permission].mapped_name.substr(2); - } - } + array[permission].mapped_name = map.count(data->name) > 0 ? map[data->name] : data->name; + array[permission].grant_name = "i_needed_modify_power_" + array[permission].mapped_name.substr(2); + } + } - return true; + return true; } \ No newline at end of file diff --git a/server/src/manager/PermissionNameMapper.h b/server/src/manager/PermissionNameMapper.h index d8a5dd2..6f778df 100644 --- a/server/src/manager/PermissionNameMapper.h +++ b/server/src/manager/PermissionNameMapper.h @@ -5,56 +5,56 @@ #include "Definitions.h" namespace ts { - namespace permission { - class PermissionNameMapper { - public: - struct Type { - enum value { - MIN, - TS3 = MIN, - TEAWEB, - TEACLIENT, - QUERY, - MAX - }; + namespace permission { + class PermissionNameMapper { + public: + struct Type { + enum value { + MIN, + TS3 = MIN, + TEAWEB, + TEACLIENT, + QUERY, + MAX + }; - inline static value from_client_type(const server::ClientType& client) { - if(client == server::ClientType::CLIENT_TEAMSPEAK) - return value::TS3; + inline static value from_client_type(const server::ClientType& client) { + if(client == server::ClientType::CLIENT_TEAMSPEAK) + return value::TS3; - if(client == server::ClientType::CLIENT_TEASPEAK) - return value::TEACLIENT; + if(client == server::ClientType::CLIENT_TEASPEAK) + return value::TEACLIENT; - if(client == server::ClientType::CLIENT_WEB) - return value::TEAWEB; + if(client == server::ClientType::CLIENT_WEB) + return value::TEAWEB; - return value::QUERY; - } - }; + return value::QUERY; + } + }; - PermissionNameMapper(); - virtual ~PermissionNameMapper(); + PermissionNameMapper(); + virtual ~PermissionNameMapper(); - bool initialize(const std::string& file, std::string& error); + bool initialize(const std::string& file, std::string& error); - std::string permission_name(const Type::value& /* type */, const permission::PermissionType& /* permission */) const; - inline std::string permission_name(const server::ClientType& type, const permission::PermissionType& permission) const { - return this->permission_name(Type::from_client_type(type), permission); - } + std::string permission_name(const Type::value& /* type */, const permission::PermissionType& /* permission */) const; + inline std::string permission_name(const server::ClientType& type, const permission::PermissionType& permission) const { + return this->permission_name(Type::from_client_type(type), permission); + } - std::string permission_name_grant(const Type::value& /* type */, const permission::PermissionType& /* permission */) const; - inline std::string permission_name_grant(const server::ClientType& type, const permission::PermissionType& permission) const { - return this->permission_name_grant(Type::from_client_type(type), permission); - } - private: - struct PermissionMap { - std::string mapped_name; - std::string grant_name; - }; - std::array< - std::array, - Type::value::MAX - > mapping; - }; - } + std::string permission_name_grant(const Type::value& /* type */, const permission::PermissionType& /* permission */) const; + inline std::string permission_name_grant(const server::ClientType& type, const permission::PermissionType& permission) const { + return this->permission_name_grant(Type::from_client_type(type), permission); + } + private: + struct PermissionMap { + std::string mapped_name; + std::string grant_name; + }; + std::array< + std::array, + Type::value::MAX + > mapping; + }; + } } \ No newline at end of file diff --git a/server/src/manager/SqlDataManager.cpp b/server/src/manager/SqlDataManager.cpp index 09851ca..ef4bb70 100644 --- a/server/src/manager/SqlDataManager.cpp +++ b/server/src/manager/SqlDataManager.cpp @@ -21,14 +21,14 @@ SqlDataManager::~SqlDataManager() {} #define EXECUTE(msg, cmd) \ result = sql::command(this->manager, cmd).execute(); \ if(!result){ \ - error = string(msg) + " Command '" + std::string(cmd) + "' returns " + result.fmtStr(); \ - return false; \ + error = string(msg) + " Command '" + std::string(cmd) + "' returns " + result.fmtStr(); \ + return false; \ } #define EXECUTE_(error_message, cmd, ignore) \ result = sql::command(this->manager, cmd).execute(); \ if(!result && result.msg().find(ignore) == string::npos){ \ - error = string(error_message) + " Command '" + std::string(cmd) + "' returns " + result.fmtStr(); \ - return false; \ + error = string(error_message) + " Command '" + std::string(cmd) + "' returns " + result.fmtStr(); \ + return false; \ } #define BUILD_CREATE_TABLE(tblName, types) "CREATE TABLE IF NOT EXISTS `" tblName "` (" types ")" @@ -53,231 +53,231 @@ if(!result && result.msg().find(ignore) == string::npos){ template struct alive_watch { - bool do_notify; - T notify; + bool do_notify; + T notify; - alive_watch(T&& function) : notify(std::forward(function)), do_notify(true) { } - ~alive_watch() { - if(do_notify) - notify(); - } + alive_watch(T&& function) : notify(std::forward(function)), do_notify(true) { } + ~alive_watch() { + if(do_notify) + notify(); + } }; #define db_version(new_version) \ do { \ - if(!this->change_database_version(new_version)) { \ - error = "failed to update database version"; \ - return false; \ - } \ + if(!this->change_database_version(new_version)) { \ + error = "failed to update database version"; \ + return false; \ + } \ } while(0) #define perm_version(new_version) \ do { \ - if(!this->change_permission_version(new_version)) { \ - error = "failed to update permission version"; \ - return false; \ - } \ + if(!this->change_permission_version(new_version)) { \ + error = "failed to update permission version"; \ + return false; \ + } \ } while(0) bool SqlDataManager::initialize(std::string& error) { - if(ts::config::database::url.find("sqlite://") == 0) - this->manager = new sql::sqlite::SqliteManager(); - else if(ts::config::database::url.find("mysql://") == 0) - this->manager = new sql::mysql::MySQLManager(); - else { - error = "Invalid database type!"; - return false; - } - auto result = manager->connect(ts::config::database::url); - if(!result) { - error = "Could not connect to " + ts::config::database::url + " (" + result.fmtStr() + ")."; - return false; - } + if(ts::config::database::url.find("sqlite://") == 0) + this->manager = new sql::sqlite::SqliteManager(); + else if(ts::config::database::url.find("mysql://") == 0) + this->manager = new sql::mysql::MySQLManager(); + else { + error = "Invalid database type!"; + return false; + } + auto result = manager->connect(ts::config::database::url); + if(!result) { + error = "Could not connect to " + ts::config::database::url + " (" + result.fmtStr() + ")."; + return false; + } - if(manager->getType() == sql::TYPE_MYSQL) { - sql::command(this->manager, "SET NAMES utf8").execute(); - //sql::command(this->manager, "DEFAULT CHARSET=utf8").execute(); - } else if(manager->getType() == sql::TYPE_SQLITE) { - if(!config::database::sqlite::journal_mode.empty()) - sql::command(this->manager, "PRAGMA journal_mode=" + config::database::sqlite::journal_mode + ";").execute(); + if(manager->getType() == sql::TYPE_MYSQL) { + sql::command(this->manager, "SET NAMES utf8").execute(); + //sql::command(this->manager, "DEFAULT CHARSET=utf8").execute(); + } else if(manager->getType() == sql::TYPE_SQLITE) { + if(!config::database::sqlite::journal_mode.empty()) + sql::command(this->manager, "PRAGMA journal_mode=" + config::database::sqlite::journal_mode + ";").execute(); - if(!config::database::sqlite::sync_mode.empty()) - sql::command(this->manager, "PRAGMA synchronous=" + config::database::sqlite::sync_mode + ";").execute(); + if(!config::database::sqlite::sync_mode.empty()) + sql::command(this->manager, "PRAGMA synchronous=" + config::database::sqlite::sync_mode + ";").execute(); - if(!config::database::sqlite::locking_mode.empty()) - sql::command(this->manager, "PRAGMA locking_mode=" + config::database::sqlite::locking_mode + ";").execute(); + if(!config::database::sqlite::locking_mode.empty()) + sql::command(this->manager, "PRAGMA locking_mode=" + config::database::sqlite::locking_mode + ";").execute(); - sql::command(this->manager, "PRAGMA encoding = \"UTF-8\";").execute(); - } + sql::command(this->manager, "PRAGMA encoding = \"UTF-8\";").execute(); + } - /* begin transaction, if available */ - if(manager->getType() == sql::TYPE_SQLITE) { - result = sql::command(this->sql(),"BEGIN TRANSACTION;").execute(); - if(!result) { - error = "failed to begin transaction (" + result.fmtStr() + ")"; - return false; - } - } + /* begin transaction, if available */ + if(manager->getType() == sql::TYPE_SQLITE) { + result = sql::command(this->sql(),"BEGIN TRANSACTION;").execute(); + if(!result) { + error = "failed to begin transaction (" + result.fmtStr() + ")"; + return false; + } + } - alive_watch rollback_watch([&]{ - if(manager->getType() == sql::TYPE_SQLITE) { - auto result = sql::command(this->sql(), "ROLLBACK;").execute(); - if (!result) { - logCritical(LOG_GENERAL, "Failed to rollback database after transaction."); - return; - } - debugMessage(LOG_GENERAL, "Rollbacked database successfully."); - } - }); + alive_watch rollback_watch([&]{ + if(manager->getType() == sql::TYPE_SQLITE) { + auto result = sql::command(this->sql(), "ROLLBACK;").execute(); + if (!result) { + logCritical(LOG_GENERAL, "Failed to rollback database after transaction."); + return; + } + debugMessage(LOG_GENERAL, "Rollbacked database successfully."); + } + }); - if(!this->detect_versions()) { - error = "failed to detect database/permission version"; - return false; - } + if(!this->detect_versions()) { + error = "failed to detect database/permission version"; + return false; + } - if(!this->update_database(error)) { - error = "failed to upgrade database: " + error; - return false; - } + if(!this->update_database(error)) { + error = "failed to upgrade database: " + error; + return false; + } - //Advanced locked test - { - bool property_exists = false; - sql::command(this->sql(), "SELECT * FORM `general` WHERE `key` = :key", variable{":key", "lock_test"}).query([](bool& flag, int, string*, string*) { flag = true; }, property_exists); - sql::result res; - if(!property_exists) { - res = sql::command(this->sql(), "INSERT INTO `general` (`key`, `value`) VALUES (:key, :value);", variable{":key", "lock_test"}, variable{":value", "UPDATE ME!"}).execute(); - } else { - res = sql::command(this->sql(), "UPDATE `general` SET `value`= :value WHERE `key`= :key;", variable{":key", "lock_test"}, variable{":value", "TeaSpeak created by WolverinDEV <3"}).execute(); - } - if(!res) { - if(res.msg().find("database is locked") != string::npos) error = "database is locked"; - else error = "Failed to execute lock test! Command result: " + res.fmtStr(); - return false; - } - } + //Advanced locked test + { + bool property_exists = false; + sql::command(this->sql(), "SELECT * FORM `general` WHERE `key` = :key", variable{":key", "lock_test"}).query([](bool& flag, int, string*, string*) { flag = true; }, property_exists); + sql::result res; + if(!property_exists) { + res = sql::command(this->sql(), "INSERT INTO `general` (`key`, `value`) VALUES (:key, :value);", variable{":key", "lock_test"}, variable{":value", "UPDATE ME!"}).execute(); + } else { + res = sql::command(this->sql(), "UPDATE `general` SET `value`= :value WHERE `key`= :key;", variable{":key", "lock_test"}, variable{":value", "TeaSpeak created by WolverinDEV <3"}).execute(); + } + if(!res) { + if(res.msg().find("database is locked") != string::npos) error = "database is locked"; + else error = "Failed to execute lock test! Command result: " + res.fmtStr(); + return false; + } + } - if(!this->update_permissions(error)) { - error = "failed to upgrade permissions: " + error; - return false; - } - rollback_watch.do_notify = false; /* transaction was successful */ - if(manager->getType() == sql::TYPE_SQLITE) { - result = sql::command(this->sql(), "COMMIT;").execute(); - if(!result) { - error = "failed to commit changes"; - return false; - } - } + if(!this->update_permissions(error)) { + error = "failed to upgrade permissions: " + error; + return false; + } + rollback_watch.do_notify = false; /* transaction was successful */ + if(manager->getType() == sql::TYPE_SQLITE) { + result = sql::command(this->sql(), "COMMIT;").execute(); + if(!result) { + error = "failed to commit changes"; + return false; + } + } - return true; + return true; } bool SqlDataManager::update_database(std::string &error) { - if(this->_database_version != CURRENT_DATABASE_VERSION) { - string command_append_utf8 = manager->getType() == sql::TYPE_MYSQL ? " CHARACTER SET=utf8" : ""; + if(this->_database_version != CURRENT_DATABASE_VERSION) { + string command_append_utf8 = manager->getType() == sql::TYPE_MYSQL ? " CHARACTER SET=utf8" : ""; - sql::result result; - auto timestamp_start = system_clock::now(); - logMessage(LOG_GENERAL, "Upgrading database from version " + to_string(this->_database_version) + " to " + to_string(CURRENT_DATABASE_VERSION) + ". This could take a moment!"); + sql::result result; + auto timestamp_start = system_clock::now(); + logMessage(LOG_GENERAL, "Upgrading database from version " + to_string(this->_database_version) + " to " + to_string(CURRENT_DATABASE_VERSION) + ". This could take a moment!"); - switch (this->_database_version) { - case -1: - CREATE_TABLE("general", "`key` VARCHAR(" UNKNOWN_KEY_LENGTH "), `value` TEXT", command_append_utf8); - CREATE_TABLE("servers", "`serverId` INT NOT NULL, `host` TEXT NOT NULL, `port` INT", command_append_utf8); + switch (this->_database_version) { + case -1: + CREATE_TABLE("general", "`key` VARCHAR(" UNKNOWN_KEY_LENGTH "), `value` TEXT", command_append_utf8); + CREATE_TABLE("servers", "`serverId` INT NOT NULL, `host` TEXT NOT NULL, `port` INT", command_append_utf8); - CREATE_TABLE("assignedGroups", "`serverId` INT NOT NULL, `cldbid` INT NOT NULL, `groupId` INT, `channelId` INT DEFAULT -1, `until` BIGINT DEFAULT -1", command_append_utf8); - CREATE_TABLE("groups", "`serverId` INT NOT NULL, `groupId` INTEGER, `target` INT, `type` INT, `displayName` VARCHAR(" CLIENT_NAME_LENGTH ")", command_append_utf8); + CREATE_TABLE("assignedGroups", "`serverId` INT NOT NULL, `cldbid` INT NOT NULL, `groupId` INT, `channelId` INT DEFAULT -1, `until` BIGINT DEFAULT -1", command_append_utf8); + CREATE_TABLE("groups", "`serverId` INT NOT NULL, `groupId` INTEGER, `target` INT, `type` INT, `displayName` VARCHAR(" CLIENT_NAME_LENGTH ")", command_append_utf8); - CREATE_TABLE("queries", "`username` VARCHAR(" CLIENT_NAME_LENGTH "), `password` TEXT, `uniqueId` VARCHAR(" CLIENT_UID_LENGTH ")", command_append_utf8); - CREATE_TABLE("clients", "`serverId` INT NOT NULL, `cldbid` INTEGER, `clientUid` VARCHAR(" CLIENT_UID_LENGTH "), `firstConnect` BIGINT, `lastConnect` BIGINT, `connections` INT, `lastName` VARCHAR(" CLIENT_NAME_LENGTH ")", command_append_utf8); - CREATE_TABLE("channels", "`serverId` INT NOT NULL, `channelId` INT, `type` INT, `parentId` INT", command_append_utf8); + CREATE_TABLE("queries", "`username` VARCHAR(" CLIENT_NAME_LENGTH "), `password` TEXT, `uniqueId` VARCHAR(" CLIENT_UID_LENGTH ")", command_append_utf8); + CREATE_TABLE("clients", "`serverId` INT NOT NULL, `cldbid` INTEGER, `clientUid` VARCHAR(" CLIENT_UID_LENGTH "), `firstConnect` BIGINT, `lastConnect` BIGINT, `connections` INT, `lastName` VARCHAR(" CLIENT_NAME_LENGTH ")", command_append_utf8); + CREATE_TABLE("channels", "`serverId` INT NOT NULL, `channelId` INT, `type` INT, `parentId` INT", command_append_utf8); - CREATE_TABLE("properties", "`serverId` INTEGER DEFAULT -1, `type` INTEGER, `id` INTEGER, `key` VARCHAR(" UNKNOWN_KEY_LENGTH "), `value` TEXT", command_append_utf8); - CREATE_TABLE("permissions", "`serverId` INT NOT NULL, `type` INT, `id` INT, `channelId` INT, `permId` VARCHAR(" UNKNOWN_KEY_LENGTH "), `value` INT, `grant` INT", command_append_utf8); + CREATE_TABLE("properties", "`serverId` INTEGER DEFAULT -1, `type` INTEGER, `id` INTEGER, `key` VARCHAR(" UNKNOWN_KEY_LENGTH "), `value` TEXT", command_append_utf8); + CREATE_TABLE("permissions", "`serverId` INT NOT NULL, `type` INT, `id` INT, `channelId` INT, `permId` VARCHAR(" UNKNOWN_KEY_LENGTH "), `value` INT, `grant` INT", command_append_utf8); - CREATE_TABLE("bannedClients", "`banId` INTEGER NOT NULL PRIMARY KEY, `serverId` INT NOT NULL, `invokerDbId` INT NOT NULL, `reason` TEXT, `hwid` VARCHAR(" CLIENT_UID_LENGTH "), `uid` VARCHAR(" CLIENT_UID_LENGTH "), `name` VARCHAR(" CLIENT_NAME_LENGTH "), `ip` VARCHAR(128), `strType` VARCHAR(32), `created` BIGINT DEFAULT -1, `until` BIGINT DEFAULT -1", command_append_utf8); - CREATE_TABLE("tokens", "`serverId` INT NOT NULL, `type` INT NOT NULL, `token` VARCHAR(128), `targetGroup` INT NOT NULL, `targetChannel` INT, `description` TEXT , `created` INT", command_append_utf8); + CREATE_TABLE("bannedClients", "`banId` INTEGER NOT NULL PRIMARY KEY, `serverId` INT NOT NULL, `invokerDbId` INT NOT NULL, `reason` TEXT, `hwid` VARCHAR(" CLIENT_UID_LENGTH "), `uid` VARCHAR(" CLIENT_UID_LENGTH "), `name` VARCHAR(" CLIENT_NAME_LENGTH "), `ip` VARCHAR(128), `strType` VARCHAR(32), `created` BIGINT DEFAULT -1, `until` BIGINT DEFAULT -1", command_append_utf8); + CREATE_TABLE("tokens", "`serverId` INT NOT NULL, `type` INT NOT NULL, `token` VARCHAR(128), `targetGroup` INT NOT NULL, `targetChannel` INT, `description` TEXT , `created` INT", command_append_utf8); - CREATE_TABLE("complains", "`serverId` INT NOT NULL, `targetId` INT NOT NULL, `reporterId` INT NOT NULL, `reason` TEXT, `created` INT", command_append_utf8); - CREATE_TABLE("letters", "`serverId` INT NOT NULL, `letterId` INTEGER NOT NULL PRIMARY KEY, `sender` VARCHAR(" CLIENT_UID_LENGTH "), `receiver` VARCHAR(" CLIENT_UID_LENGTH "), `created` INT, `subject` TEXT, `message` TEXT, `read` INT", command_append_utf8); - CREATE_TABLE("musicbots", "`serverId` INT, `botId` INT, `uniqueId` VARCHAR(" CLIENT_UID_LENGTH "), `owner` INT", command_append_utf8); + CREATE_TABLE("complains", "`serverId` INT NOT NULL, `targetId` INT NOT NULL, `reporterId` INT NOT NULL, `reason` TEXT, `created` INT", command_append_utf8); + CREATE_TABLE("letters", "`serverId` INT NOT NULL, `letterId` INTEGER NOT NULL PRIMARY KEY, `sender` VARCHAR(" CLIENT_UID_LENGTH "), `receiver` VARCHAR(" CLIENT_UID_LENGTH "), `created` INT, `subject` TEXT, `message` TEXT, `read` INT", command_append_utf8); + CREATE_TABLE("musicbots", "`serverId` INT, `botId` INT, `uniqueId` VARCHAR(" CLIENT_UID_LENGTH "), `owner` INT", command_append_utf8); - db_version(0); - case 0: - CREATE_INDEX("general", "key"); - CREATE_INDEX("servers", "serverId"); + db_version(0); + case 0: + CREATE_INDEX("general", "key"); + CREATE_INDEX("servers", "serverId"); - CREATE_INDEX2R("assignedGroups", "serverId", "cldbid"); - CREATE_INDEX2R("groups", "serverId", "groupId"); - CREATE_INDEX("groups", "serverId"); + CREATE_INDEX2R("assignedGroups", "serverId", "cldbid"); + CREATE_INDEX2R("groups", "serverId", "groupId"); + CREATE_INDEX("groups", "serverId"); - CREATE_INDEX("queries", "username"); - CREATE_INDEX("clients", "serverId"); - CREATE_INDEX2R("clients", "serverId", "cldbid"); - CREATE_INDEX("channels", "serverId"); + CREATE_INDEX("queries", "username"); + CREATE_INDEX("clients", "serverId"); + CREATE_INDEX2R("clients", "serverId", "cldbid"); + CREATE_INDEX("channels", "serverId"); - CREATE_INDEX("properties", "serverId"); - CREATE_INDEX("permissions", "serverId"); - CREATE_INDEX2R("properties", "serverId", "id"); - CREATE_INDEX2R("permissions", "serverId", "channelId"); + CREATE_INDEX("properties", "serverId"); + CREATE_INDEX("permissions", "serverId"); + CREATE_INDEX2R("properties", "serverId", "id"); + CREATE_INDEX2R("permissions", "serverId", "channelId"); - CREATE_INDEX("bannedClients", "serverId"); - CREATE_INDEX("bannedClients", "banId"); - CREATE_INDEX2R("bannedClients", "serverId", "hwid"); - CREATE_INDEX2R("bannedClients", "serverId", "name"); - CREATE_INDEX2R("bannedClients", "serverId", "uid"); - CREATE_INDEX2R("bannedClients", "serverId", "ip"); + CREATE_INDEX("bannedClients", "serverId"); + CREATE_INDEX("bannedClients", "banId"); + CREATE_INDEX2R("bannedClients", "serverId", "hwid"); + CREATE_INDEX2R("bannedClients", "serverId", "name"); + CREATE_INDEX2R("bannedClients", "serverId", "uid"); + CREATE_INDEX2R("bannedClients", "serverId", "ip"); - CREATE_INDEX("tokens", "serverId"); - CREATE_INDEX("complains", "serverId"); - CREATE_INDEX("letters", "serverId"); - CREATE_INDEX("letters", "letterId"); - CREATE_INDEX("musicbots", "serverId"); - db_version(1); + CREATE_INDEX("tokens", "serverId"); + CREATE_INDEX("complains", "serverId"); + CREATE_INDEX("letters", "serverId"); + CREATE_INDEX("letters", "letterId"); + CREATE_INDEX("musicbots", "serverId"); + db_version(1); - case 1: - sql::command(this->sql(), "UPDATE `properties` SET `type` = :type WHERE `serverId` = 0 AND `id` = 0", variable{":type", property::PropertyType::PROP_TYPE_INSTANCE}).execute(); - db_version(2); + case 1: + sql::command(this->sql(), "UPDATE `properties` SET `type` = :type WHERE `serverId` = 0 AND `id` = 0", variable{":type", property::PropertyType::PROP_TYPE_INSTANCE}).execute(); + db_version(2); - case 2: - sql::command(this->sql(), "ALTER TABLE permissions ADD flag_skip BOOL;").execute(); - sql::command(this->sql(), "ALTER TABLE permissions ADD flag_negate BOOL;").execute(); - db_version(3); + case 2: + sql::command(this->sql(), "ALTER TABLE permissions ADD flag_skip BOOL;").execute(); + sql::command(this->sql(), "ALTER TABLE permissions ADD flag_negate BOOL;").execute(); + db_version(3); - case 3: - EXECUTE("Failed to update ban table", "ALTER TABLE `bannedClients` ADD COLUMN `triggered` INT DEFAULT 0;"); - CREATE_TABLE("ban_trigger", "`server_id` INT, `ban_id` INT, `unique_id` VARCHAR(" CLIENT_UID_LENGTH "), `hardware_id` VARCHAR(" CLIENT_UID_LENGTH "), `name` VARCHAR(" CLIENT_NAME_LENGTH "), `ip` VARCHAR(128), `timestamp` BIGINT", command_append_utf8); - CREATE_INDEX2R("ban_trigger", "server_id", "ban_id"); - db_version(4); - case 4: - sql::command(this->sql(), "ALTER TABLE queries ADD server INT;").execute(); - db_version(5); + case 3: + EXECUTE("Failed to update ban table", "ALTER TABLE `bannedClients` ADD COLUMN `triggered` INT DEFAULT 0;"); + CREATE_TABLE("ban_trigger", "`server_id` INT, `ban_id` INT, `unique_id` VARCHAR(" CLIENT_UID_LENGTH "), `hardware_id` VARCHAR(" CLIENT_UID_LENGTH "), `name` VARCHAR(" CLIENT_NAME_LENGTH "), `ip` VARCHAR(128), `timestamp` BIGINT", command_append_utf8); + CREATE_INDEX2R("ban_trigger", "server_id", "ban_id"); + db_version(4); + case 4: + sql::command(this->sql(), "ALTER TABLE queries ADD server INT;").execute(); + db_version(5); - case 5: - CREATE_TABLE("playlists", "`serverId` INT NOT NULL, `playlist_id` INT", command_append_utf8); - CREATE_INDEX("playlists", "serverId"); + case 5: + CREATE_TABLE("playlists", "`serverId` INT NOT NULL, `playlist_id` INT", command_append_utf8); + CREATE_INDEX("playlists", "serverId"); - CREATE_TABLE("playlist_songs", "`serverId` INT NOT NULL, `playlist_id` INT, `song_id` INT, `order_id` INT, `invoker_dbid` INT, `url` TEXT, `url_loader` TEXT", command_append_utf8); - CREATE_INDEX2R("playlist_songs", "serverId", "playlist_id"); - db_version(6); + CREATE_TABLE("playlist_songs", "`serverId` INT NOT NULL, `playlist_id` INT, `song_id` INT, `order_id` INT, `invoker_dbid` INT, `url` TEXT, `url_loader` TEXT", command_append_utf8); + CREATE_INDEX2R("playlist_songs", "serverId", "playlist_id"); + db_version(6); - sql::command(this->sql(), "UPDATE `permissions ` SET `permId` = `b_client_music_create_temporary` WHERE `permId` = `b_client_music_create`").execute(); + sql::command(this->sql(), "UPDATE `permissions ` SET `permId` = `b_client_music_create_temporary` WHERE `permId` = `b_client_music_create`").execute(); - case 6: - sql::command(this->sql(), "ALTER TABLE playlist_songs ADD loaded BOOL;").execute(); - sql::command(this->sql(), "ALTER TABLE playlist_songs ADD metadata TEXT;").execute(); + case 6: + sql::command(this->sql(), "ALTER TABLE playlist_songs ADD loaded BOOL;").execute(); + sql::command(this->sql(), "ALTER TABLE playlist_songs ADD metadata TEXT;").execute(); - case 7: - /* recreate permission table */ + case 7: + /* recreate permission table */ /* DROP TABLE `permissions`, `properties`; ALTER TABLE permissions_v6 RENAME permissions; ALTER TABLE properties_v6 RENAME properties; */ - /* MySQL command + /* MySQL command START TRANSACTION; SET SESSION sql_mode='STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION'; @@ -303,296 +303,296 @@ DELETE FROM `permissions` WHERE `type` = 2 AND NOT (`serverId`, `id`) IN (SELECT DELETE FROM `properties` WHERE `type` = 1 AND NOT (`serverId` IN (SELECT `serverId` FROM servers) OR `serverId` = 0); ROLLBACK; - */ - if(manager->getType() == sql::TYPE_MYSQL) { - /* - * FIXME implement update for MySQL as well! - auto mysql = (sql::mysql::MySQLManager*) this->sql(); + */ + if(manager->getType() == sql::TYPE_MYSQL) { + /* + * FIXME implement update for MySQL as well! + auto mysql = (sql::mysql::MySQLManager*) this->sql(); - if(!result) { - error = "failed to update database (" + result.fmtStr() + ")"; - return false; - } - */ - } else { - result = sql::command(this->sql(), "ALTER TABLE permissions RENAME TO permissions_v6;").execute(); - if(!result) { - error = "Failed to rename permission to permission v6 (" + result.fmtStr() + ")"; - return false; - } + if(!result) { + error = "failed to update database (" + result.fmtStr() + ")"; + return false; + } + */ + } else { + result = sql::command(this->sql(), "ALTER TABLE permissions RENAME TO permissions_v6;").execute(); + if(!result) { + error = "Failed to rename permission to permission v6 (" + result.fmtStr() + ")"; + return false; + } - result = sql::command(this->sql(), "CREATE TABLE `permissions`(`serverId` INT NOT NULL, `type` INT, `id` INT, `channelId` INT, `permId` VARCHAR(256), `value` INT, `grant` INT, `flag_skip` BOOL, `flag_negate` BOOL, CONSTRAINT PK PRIMARY KEY(`serverId`, `type`, `id`, `channelId`, `permId`));").execute(); - if(!result) { - error = "Failed to create new permission table (" + result.fmtStr() + ")"; - return false; - } + result = sql::command(this->sql(), "CREATE TABLE `permissions`(`serverId` INT NOT NULL, `type` INT, `id` INT, `channelId` INT, `permId` VARCHAR(256), `value` INT, `grant` INT, `flag_skip` BOOL, `flag_negate` BOOL, CONSTRAINT PK PRIMARY KEY(`serverId`, `type`, `id`, `channelId`, `permId`));").execute(); + if(!result) { + error = "Failed to create new permission table (" + result.fmtStr() + ")"; + return false; + } - result = sql::command(this->sql(), "INSERT INTO `permissions` SELECT * FROM `permissions_v6` GROUP BY `serverId`, `type`, `id`, `channelId`, `permId`;").execute(); - if(!result) { - error = "Failed to insert unique permissions (" + result.fmtStr() + ")"; - return false; - } + result = sql::command(this->sql(), "INSERT INTO `permissions` SELECT * FROM `permissions_v6` GROUP BY `serverId`, `type`, `id`, `channelId`, `permId`;").execute(); + if(!result) { + error = "Failed to insert unique permissions (" + result.fmtStr() + ")"; + return false; + } - if(manager->getType() == sql::TYPE_SQLITE) { - DROP_INDEX("permissions", "serverId"); - DROP_INDEX2R("permissions", "serverId", "channelId"); - } - CREATE_INDEX("permissions", "serverId"); - CREATE_INDEX2R("permissions", "serverId", "channelId"); + if(manager->getType() == sql::TYPE_SQLITE) { + DROP_INDEX("permissions", "serverId"); + DROP_INDEX2R("permissions", "serverId", "channelId"); + } + CREATE_INDEX("permissions", "serverId"); + CREATE_INDEX2R("permissions", "serverId", "channelId"); - /* recreate property table */ - result = sql::command(this->sql(), "ALTER TABLE properties RENAME TO properties_v6;").execute(); - if(!result) { - error = "Failed to rename properties to properties v6 (" + result.fmtStr() + ")"; - return false; - } + /* recreate property table */ + result = sql::command(this->sql(), "ALTER TABLE properties RENAME TO properties_v6;").execute(); + if(!result) { + error = "Failed to rename properties to properties v6 (" + result.fmtStr() + ")"; + return false; + } - result = sql::command(this->sql(), "CREATE TABLE properties(`serverId` INTEGER DEFAULT -1, `type` INTEGER, `id` INTEGER, `key` VARCHAR(256), `value` TEXT, CONSTRAINT PK PRIMARY KEY (`serverId`, `type`, `id`, `key`));").execute(); - if(!result) { - error = "Failed to create new properties table (" + result.fmtStr() + ")"; - return false; - } + result = sql::command(this->sql(), "CREATE TABLE properties(`serverId` INTEGER DEFAULT -1, `type` INTEGER, `id` INTEGER, `key` VARCHAR(256), `value` TEXT, CONSTRAINT PK PRIMARY KEY (`serverId`, `type`, `id`, `key`));").execute(); + if(!result) { + error = "Failed to create new properties table (" + result.fmtStr() + ")"; + return false; + } - /* - SET SESSION sql_mode='STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION'; - INSERT INTO `properties` SELECT * FROM `properties_v6` GROUP BY `serverId`, `type`, `id`, `key`; - */ - result = sql::command(this->sql(), "INSERT INTO `properties` SELECT * FROM `properties_v6` GROUP BY `serverId`, `type`, `id`, `key`;").execute(); - if(!result) { - error = "Failed to insert unique properties (" + result.fmtStr() + ")"; - return false; - } - if(manager->getType() == sql::TYPE_SQLITE) { - DROP_INDEX("properties", "serverId"); - DROP_INDEX2R("properties", "serverId", "id"); - } - CREATE_INDEX("properties", "serverId"); - CREATE_INDEX2R("properties", "serverId", "id"); + /* + SET SESSION sql_mode='STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION'; + INSERT INTO `properties` SELECT * FROM `properties_v6` GROUP BY `serverId`, `type`, `id`, `key`; + */ + result = sql::command(this->sql(), "INSERT INTO `properties` SELECT * FROM `properties_v6` GROUP BY `serverId`, `type`, `id`, `key`;").execute(); + if(!result) { + error = "Failed to insert unique properties (" + result.fmtStr() + ")"; + return false; + } + if(manager->getType() == sql::TYPE_SQLITE) { + DROP_INDEX("properties", "serverId"); + DROP_INDEX2R("properties", "serverId", "id"); + } + CREATE_INDEX("properties", "serverId"); + CREATE_INDEX2R("properties", "serverId", "id"); - /* SELECT * FROM `permissions` WHERE `type` = 1 AND (`serverId`, `channelId`) IN (SELECT `serverId`, `channelId` FROM `channels`) */ - /* Channel permission cleanup: DELETE FROM `permissions` WHERE `type` = 1 AND NOT (`serverId`, `channelId`) IN (SELECT `serverId`, `channelId` FROM `channels`) */ + /* SELECT * FROM `permissions` WHERE `type` = 1 AND (`serverId`, `channelId`) IN (SELECT `serverId`, `channelId` FROM `channels`) */ + /* Channel permission cleanup: DELETE FROM `permissions` WHERE `type` = 1 AND NOT (`serverId`, `channelId`) IN (SELECT `serverId`, `channelId` FROM `channels`) */ - sql::command(this->sql(), "DELETE FROM `permissions` WHERE (`type` = 1 OR `type` = 2) AND NOT ((`serverId`, `channelId`) IN (SELECT `serverId`, `channelId` FROM `channels`) OR `channelId` = 0);").execute(); - sql::command(this->sql(), "DELETE FROM `permissions` WHERE `type` = 0 AND NOT (`serverId`, `id`) IN (SELECT `serverId`, `groupId` FROM `groups`);").execute(); - sql::command(this->sql(), "DELETE FROM `permissions` WHERE `type` = 2 AND NOT (`serverId`, `id`) IN (SELECT `serverId`, `cldbid` FROM `clients`);").execute(); + sql::command(this->sql(), "DELETE FROM `permissions` WHERE (`type` = 1 OR `type` = 2) AND NOT ((`serverId`, `channelId`) IN (SELECT `serverId`, `channelId` FROM `channels`) OR `channelId` = 0);").execute(); + sql::command(this->sql(), "DELETE FROM `permissions` WHERE `type` = 0 AND NOT (`serverId`, `id`) IN (SELECT `serverId`, `groupId` FROM `groups`);").execute(); + sql::command(this->sql(), "DELETE FROM `permissions` WHERE `type` = 2 AND NOT (`serverId`, `id`) IN (SELECT `serverId`, `cldbid` FROM `clients`);").execute(); - sql::command(this->sql(), "DELETE FROM `properties` WHERE `type` = 1 AND NOT (`serverId` IN (SELECT `serverId` FROM servers) OR `serverId` = 0);").execute(); - } - db_version(8); - case 8: - result = sql::command(this->sql(), "UPDATE `queries` SET `server` = 0 WHERE `server` IS NULL").execute(); - if(!result) { - error = "Failed to drop null query entries (" + result.fmtStr() + ")"; - return false; - } - db_version(9); - case 9: - // - //"UPDATE `permissions` SET `id` = :id WHERE `type` = :channel_type" ;permission::SQL_PERM_CHANNEL - result = sql::command(this->sql(), "UPDATE `permissions` SET `id` = :id WHERE `type` = :channel_type;", variable{":channel_type", permission::SQL_PERM_CHANNEL}, variable{":id", 0}).execute(); - if(!result) { - if(result.code() == 1) { //constraint failed => duplicated ids for example - size_t count = 0; - result = sql::command(this->sql(), "SELECT COUNT(*) FROM `permissions` WHERE NOT `id` = :id AND `type` = :type", variable{":type", permission::SQL_PERM_CHANNEL}, variable{":id", 0}).query([&](int, std::string* values, std::string*) { - count = stoll(values[0]); - }); - logError(LOG_GENERAL, "Database contains invalid channel permissions. Deleting permissions ({}).", count); - result = sql::command(this->sql(), "DELETE FROM `permissions` WHERE NOT `id` = :id AND `type` = :type", variable{":channel_type", permission::SQL_PERM_CHANNEL}, variable{":id", 0}).execute(); - } - if(!result) { - error = "Failed to fix channel properties (" + result.fmtStr() + ")"; - return false; - } - } - db_version(10); - case 10: - CREATE_TABLE("conversations", "`server_id` INT, `channel_id` INT, `conversation_id` INT, `file_path` TEXT", command_append_utf8); - CREATE_TABLE("conversation_blocks", "`server_id` INT, `conversation_id` INT, `begin_timestamp` INT, `end_timestamp` INT, `block_offset` INT, `flags` INT", command_append_utf8); - CREATE_INDEX("conversations", "server_id"); - CREATE_INDEX2R("conversation_blocks", "server_id", "conversation_id"); - db_version(11); - default: - break; - } + sql::command(this->sql(), "DELETE FROM `properties` WHERE `type` = 1 AND NOT (`serverId` IN (SELECT `serverId` FROM servers) OR `serverId` = 0);").execute(); + } + db_version(8); + case 8: + result = sql::command(this->sql(), "UPDATE `queries` SET `server` = 0 WHERE `server` IS NULL").execute(); + if(!result) { + error = "Failed to drop null query entries (" + result.fmtStr() + ")"; + return false; + } + db_version(9); + case 9: + // + //"UPDATE `permissions` SET `id` = :id WHERE `type` = :channel_type" ;permission::SQL_PERM_CHANNEL + result = sql::command(this->sql(), "UPDATE `permissions` SET `id` = :id WHERE `type` = :channel_type;", variable{":channel_type", permission::SQL_PERM_CHANNEL}, variable{":id", 0}).execute(); + if(!result) { + if(result.code() == 1) { //constraint failed => duplicated ids for example + size_t count = 0; + result = sql::command(this->sql(), "SELECT COUNT(*) FROM `permissions` WHERE NOT `id` = :id AND `type` = :type", variable{":type", permission::SQL_PERM_CHANNEL}, variable{":id", 0}).query([&](int, std::string* values, std::string*) { + count = stoll(values[0]); + }); + logError(LOG_GENERAL, "Database contains invalid channel permissions. Deleting permissions ({}).", count); + result = sql::command(this->sql(), "DELETE FROM `permissions` WHERE NOT `id` = :id AND `type` = :type", variable{":channel_type", permission::SQL_PERM_CHANNEL}, variable{":id", 0}).execute(); + } + if(!result) { + error = "Failed to fix channel properties (" + result.fmtStr() + ")"; + return false; + } + } + db_version(10); + case 10: + CREATE_TABLE("conversations", "`server_id` INT, `channel_id` INT, `conversation_id` INT, `file_path` TEXT", command_append_utf8); + CREATE_TABLE("conversation_blocks", "`server_id` INT, `conversation_id` INT, `begin_timestamp` INT, `end_timestamp` INT, `block_offset` INT, `flags` INT", command_append_utf8); + CREATE_INDEX("conversations", "server_id"); + CREATE_INDEX2R("conversation_blocks", "server_id", "conversation_id"); + db_version(11); + default: + break; + } - auto timestamp_end = system_clock::now(); - logMessage(LOG_GENERAL, "Database upgrade took {}ms", duration_cast(timestamp_end - timestamp_start).count()); - } + auto timestamp_end = system_clock::now(); + logMessage(LOG_GENERAL, "Database upgrade took {}ms", duration_cast(timestamp_end - timestamp_start).count()); + } - return true; + return true; } bool SqlDataManager::update_permissions(std::string &error) { - if(this->_permissions_version != CURRENT_PERMISSION_VERSION) { - sql::result result; - auto timestamp_start = system_clock::now(); + if(this->_permissions_version != CURRENT_PERMISSION_VERSION) { + sql::result result; + auto timestamp_start = system_clock::now(); - logMessage(LOG_GENERAL, "Upgrading permissions from version " + to_string(this->_permissions_version) + " to " + to_string(CURRENT_PERMISSION_VERSION) + ". This could take a moment!"); + logMessage(LOG_GENERAL, "Upgrading permissions from version " + to_string(this->_permissions_version) + " to " + to_string(CURRENT_PERMISSION_VERSION) + ". This could take a moment!"); - const auto auto_update = [&](permission::update::GroupUpdateType update_type, const std::string& permission, permission::v2::PermissionFlaggedValue value, bool skip, bool negate, permission::v2::PermissionFlaggedValue granted) { - /* - INSERT [OR IGNORE | IGNORE] INTO `perms` (serverId, type, id, channelId, permId, value, grant, flag_skip, flag_negate) - SELECT DISTINCT `permissions`.`serverId`, 0, `groupId`, 0, :name, :value, :grant, :skip, :negate FROM groups - INNER JOIN `permissions` - ON permissions.permId = 'i_group_auto_update_type' AND permissions.channelId = 0 AND permissions.id = groups.groupId AND permissions.serverId = groups.serverId AND permissions.value = :update_type; - */ + const auto auto_update = [&](permission::update::GroupUpdateType update_type, const std::string& permission, permission::v2::PermissionFlaggedValue value, bool skip, bool negate, permission::v2::PermissionFlaggedValue granted) { + /* + INSERT [OR IGNORE | IGNORE] INTO `perms` (serverId, type, id, channelId, permId, value, grant, flag_skip, flag_negate) + SELECT DISTINCT `permissions`.`serverId`, 0, `groupId`, 0, :name, :value, :grant, :skip, :negate FROM groups + INNER JOIN `permissions` + ON permissions.permId = 'i_group_auto_update_type' AND permissions.channelId = 0 AND permissions.id = groups.groupId AND permissions.serverId = groups.serverId AND permissions.value = :update_type; + */ - std::string query = "INSERT "; - if(this->sql()->getType() == sql::TYPE_MYSQL) - query += "IGNORE "; - else - query += "OR IGNORE "; - query += "INTO `permissions` (`serverId`, `type`, `id`, `channelId`, `permId`, `value`, `grant`, `flag_skip`, `flag_negate`) "; - query += string() + "SELECT DISTINCT `permissions`.`serverId`, 0, `groupId`, 0, " - + "'" + permission + "', " - + to_string(value.has_value ? value.value : -2) + ", " - + to_string(granted.has_value ? granted.value : -2) + ", " - + to_string(skip) + ", " - + to_string(negate) + " FROM groups "; - query += "INNER JOIN `permissions` "; - query += "ON permissions.permId = 'i_group_auto_update_type' AND permissions.channelId = 0 AND permissions.id = groups.groupId AND permissions.serverId = groups.serverId AND permissions.value = " + to_string(update_type); + std::string query = "INSERT "; + if(this->sql()->getType() == sql::TYPE_MYSQL) + query += "IGNORE "; + else + query += "OR IGNORE "; + query += "INTO `permissions` (`serverId`, `type`, `id`, `channelId`, `permId`, `value`, `grant`, `flag_skip`, `flag_negate`) "; + query += string() + "SELECT DISTINCT `permissions`.`serverId`, 0, `groupId`, 0, " + + "'" + permission + "', " + + to_string(value.has_value ? value.value : -2) + ", " + + to_string(granted.has_value ? granted.value : -2) + ", " + + to_string(skip) + ", " + + to_string(negate) + " FROM groups "; + query += "INNER JOIN `permissions` "; + query += "ON permissions.permId = 'i_group_auto_update_type' AND permissions.channelId = 0 AND permissions.id = groups.groupId AND permissions.serverId = groups.serverId AND permissions.value = " + to_string(update_type); - logTrace(LOG_GENERAL, "Executing sql update: {}", query); - auto result = sql::command(this->sql(), query).execute(); - if(!result) { - error = "failed to auto update permission " + permission + " for type " + to_string(update_type) + ": " + result.fmtStr(); - return false; - } + logTrace(LOG_GENERAL, "Executing sql update: {}", query); + auto result = sql::command(this->sql(), query).execute(); + if(!result) { + error = "failed to auto update permission " + permission + " for type " + to_string(update_type) + ": " + result.fmtStr(); + return false; + } - return true; - }; + return true; + }; - switch (this->_permissions_version) { - case -1: - /* initial setup, or first introduce of 1.4.0. Default stuff will be loaded from the template file so we only run updates here */ - if(!auto_update(permission::update::QUERY_ADMIN, "b_client_is_priority_speaker", {-2, false}, false, false, {100, true})) - return false; - if(!auto_update(permission::update::QUERY_ADMIN, "b_virtualserver_modify_country_code", {1, true}, false, false, {100, true})) - return false; - if(!auto_update(permission::update::QUERY_ADMIN, "b_channel_ignore_subscribe_power", {1, true}, false, false, {100, true})) - return false; - if(!auto_update(permission::update::QUERY_ADMIN, "b_channel_ignore_description_view_power", {1, true}, false, false, {100, true})) - return false; - if(!auto_update(permission::update::QUERY_ADMIN, "i_max_playlist_size", {1, false}, false, false, {100, true})) - return false; - if(!auto_update(permission::update::QUERY_ADMIN, "i_max_playlists", {1, false}, false, false, {100, true})) - return false; - if(!auto_update(permission::update::QUERY_ADMIN, "i_channel_create_modify_conversation_history_length", {1, false}, false, false, {100, true})) - return false; - if(!auto_update(permission::update::QUERY_ADMIN, "b_channel_create_modify_conversation_history_unlimited", {1, true}, false, false, {100, true})) - return false; - if(!auto_update(permission::update::QUERY_ADMIN, "b_channel_create_modify_conversation_private", {1, true}, false, false, {100, true})) - return false; - if(!auto_update(permission::update::QUERY_ADMIN, "b_channel_conversation_message_delete", {1, true}, false, false, {100, true})) - return false; + switch (this->_permissions_version) { + case -1: + /* initial setup, or first introduce of 1.4.0. Default stuff will be loaded from the template file so we only run updates here */ + if(!auto_update(permission::update::QUERY_ADMIN, "b_client_is_priority_speaker", {-2, false}, false, false, {100, true})) + return false; + if(!auto_update(permission::update::QUERY_ADMIN, "b_virtualserver_modify_country_code", {1, true}, false, false, {100, true})) + return false; + if(!auto_update(permission::update::QUERY_ADMIN, "b_channel_ignore_subscribe_power", {1, true}, false, false, {100, true})) + return false; + if(!auto_update(permission::update::QUERY_ADMIN, "b_channel_ignore_description_view_power", {1, true}, false, false, {100, true})) + return false; + if(!auto_update(permission::update::QUERY_ADMIN, "i_max_playlist_size", {1, false}, false, false, {100, true})) + return false; + if(!auto_update(permission::update::QUERY_ADMIN, "i_max_playlists", {1, false}, false, false, {100, true})) + return false; + if(!auto_update(permission::update::QUERY_ADMIN, "i_channel_create_modify_conversation_history_length", {1, false}, false, false, {100, true})) + return false; + if(!auto_update(permission::update::QUERY_ADMIN, "b_channel_create_modify_conversation_history_unlimited", {1, true}, false, false, {100, true})) + return false; + if(!auto_update(permission::update::QUERY_ADMIN, "b_channel_create_modify_conversation_private", {1, true}, false, false, {100, true})) + return false; + if(!auto_update(permission::update::QUERY_ADMIN, "b_channel_conversation_message_delete", {1, true}, false, false, {100, true})) + return false; - if(!auto_update(permission::update::SERVER_ADMIN, "b_client_is_priority_speaker", {-2, false}, false, false, {75, true})) - return false; - if(!auto_update(permission::update::SERVER_ADMIN, "b_virtualserver_modify_country_code", {1, true}, false, false, {75, true})) - return false; - if(!auto_update(permission::update::SERVER_ADMIN, "b_channel_ignore_subscribe_power", {1, true}, false, false, {75, true})) - return false; - if(!auto_update(permission::update::SERVER_ADMIN, "b_channel_ignore_description_view_power", {1, true}, false, false, {75, true})) - return false; - if(!auto_update(permission::update::SERVER_ADMIN, "i_max_playlist_size", {1, false}, false, false, {75, true})) - return false; - if(!auto_update(permission::update::SERVER_ADMIN, "i_max_playlists", {1, false}, false, false, {75, true})) - return false; - if(!auto_update(permission::update::SERVER_ADMIN, "i_channel_create_modify_conversation_history_length", {1, false}, false, false, {75, true})) - return false; - if(!auto_update(permission::update::SERVER_ADMIN, "b_channel_create_modify_conversation_history_unlimited", {1, true}, false, false, {75, true})) - return false; - if(!auto_update(permission::update::SERVER_ADMIN, "b_channel_create_modify_conversation_private", {1, true}, false, false, {75, true})) - return false; - if(!auto_update(permission::update::SERVER_ADMIN, "b_channel_conversation_message_delete", {1, true}, false, false, {75, true})) - return false; + if(!auto_update(permission::update::SERVER_ADMIN, "b_client_is_priority_speaker", {-2, false}, false, false, {75, true})) + return false; + if(!auto_update(permission::update::SERVER_ADMIN, "b_virtualserver_modify_country_code", {1, true}, false, false, {75, true})) + return false; + if(!auto_update(permission::update::SERVER_ADMIN, "b_channel_ignore_subscribe_power", {1, true}, false, false, {75, true})) + return false; + if(!auto_update(permission::update::SERVER_ADMIN, "b_channel_ignore_description_view_power", {1, true}, false, false, {75, true})) + return false; + if(!auto_update(permission::update::SERVER_ADMIN, "i_max_playlist_size", {1, false}, false, false, {75, true})) + return false; + if(!auto_update(permission::update::SERVER_ADMIN, "i_max_playlists", {1, false}, false, false, {75, true})) + return false; + if(!auto_update(permission::update::SERVER_ADMIN, "i_channel_create_modify_conversation_history_length", {1, false}, false, false, {75, true})) + return false; + if(!auto_update(permission::update::SERVER_ADMIN, "b_channel_create_modify_conversation_history_unlimited", {1, true}, false, false, {75, true})) + return false; + if(!auto_update(permission::update::SERVER_ADMIN, "b_channel_create_modify_conversation_private", {1, true}, false, false, {75, true})) + return false; + if(!auto_update(permission::update::SERVER_ADMIN, "b_channel_conversation_message_delete", {1, true}, false, false, {75, true})) + return false; - if(!auto_update(permission::update::SERVER_NORMAL, "i_max_playlist_size", {50, true}, false, false, {-2, false})) - return false; - if(!auto_update(permission::update::SERVER_NORMAL, "i_channel_create_modify_conversation_history_length", {15000, true}, false, false, {-2, false})) - return false; + if(!auto_update(permission::update::SERVER_NORMAL, "i_max_playlist_size", {50, true}, false, false, {-2, false})) + return false; + if(!auto_update(permission::update::SERVER_NORMAL, "i_channel_create_modify_conversation_history_length", {15000, true}, false, false, {-2, false})) + return false; - if(!auto_update(permission::update::SERVER_GUEST, "i_max_playlist_size", {10, true}, false, false, {-2, false})) - return false; + if(!auto_update(permission::update::SERVER_GUEST, "i_max_playlist_size", {10, true}, false, false, {-2, false})) + return false; - perm_version(0); + perm_version(0); - case 0: - result = sql::command(this->sql(), "DELETE FROM `permissions` WHERE `permId` = :permid", variable{":permid", "b_client_music_create"}).execute(); - if(!result) { - LOG_SQL_CMD(result); - return false; - } - result = sql::command(this->sql(), "DELETE FROM `permissions` WHERE `permId` = :permid", variable{":permid", "b_client_music_delete_own"}).execute(); - if(!result) { - LOG_SQL_CMD(result); - return false; - } - perm_version(1); - default: - break; - } + case 0: + result = sql::command(this->sql(), "DELETE FROM `permissions` WHERE `permId` = :permid", variable{":permid", "b_client_music_create"}).execute(); + if(!result) { + LOG_SQL_CMD(result); + return false; + } + result = sql::command(this->sql(), "DELETE FROM `permissions` WHERE `permId` = :permid", variable{":permid", "b_client_music_delete_own"}).execute(); + if(!result) { + LOG_SQL_CMD(result); + return false; + } + perm_version(1); + default: + break; + } - auto timestamp_end = system_clock::now(); - logMessage(LOG_GENERAL, "Permission upgrade took {}ms", duration_cast(timestamp_end - timestamp_start).count()); - } - return true; + auto timestamp_end = system_clock::now(); + logMessage(LOG_GENERAL, "Permission upgrade took {}ms", duration_cast(timestamp_end - timestamp_start).count()); + } + return true; } void SqlDataManager::finalize() { - if(this->manager) this->manager->disconnect(); - delete this->manager; - this->manager = nullptr; + if(this->manager) this->manager->disconnect(); + delete this->manager; + this->manager = nullptr; } bool SqlDataManager::detect_versions() { - auto result = sql::command(this->manager, "SELECT `value` FROM `general` WHERE `key`= :key", variable{":key", "data_version"}).query([&](int length, char** values, char**){ - this->_database_version = atoi(values[0]); - this->database_version_present = true; - return 0; - }); + auto result = sql::command(this->manager, "SELECT `value` FROM `general` WHERE `key`= :key", variable{":key", "data_version"}).query([&](int length, char** values, char**){ + this->_database_version = atoi(values[0]); + this->database_version_present = true; + return 0; + }); - result = sql::command(this->manager, "SELECT `value` FROM `general` WHERE `key`= :key", variable{":key", "permissions_version"}).query([&](int length, char** values, char**){ - this->_permissions_version = atoi(values[0]); - this->permissions_version_present = true; - return 0; - }); + result = sql::command(this->manager, "SELECT `value` FROM `general` WHERE `key`= :key", variable{":key", "permissions_version"}).query([&](int length, char** values, char**){ + this->_permissions_version = atoi(values[0]); + this->permissions_version_present = true; + return 0; + }); - return true; + return true; } bool SqlDataManager::change_database_version(int version) { - string command; - if(this->database_version_present) - command = "UPDATE `general` SET `value`= :version WHERE `key`= :key;"; - else - command = "INSERT INTO `general` (`key`, `value`) VALUES (:key, :version);"; + string command; + if(this->database_version_present) + command = "UPDATE `general` SET `value`= :version WHERE `key`= :key;"; + else + command = "INSERT INTO `general` (`key`, `value`) VALUES (:key, :version);"; - auto result = sql::command(this->manager, command, variable{":version", version}, variable{":key", "data_version"}).execute(); - if(!result) { - logError(LOG_INSTANCE, "Could not update SQL database version. (" + result.fmtStr() + ")"); - return false; - } - this->database_version_present = true; - this->_database_version = version; + auto result = sql::command(this->manager, command, variable{":version", version}, variable{":key", "data_version"}).execute(); + if(!result) { + logError(LOG_INSTANCE, "Could not update SQL database version. (" + result.fmtStr() + ")"); + return false; + } + this->database_version_present = true; + this->_database_version = version; - return true; + return true; } bool SqlDataManager::change_permission_version(int version) { - string command; - if(this->permissions_version_present) - command = "UPDATE `general` SET `value`= :version WHERE `key`= :key;"; - else - command = "INSERT INTO `general` (`key`, `value`) VALUES (:key, :version);"; + string command; + if(this->permissions_version_present) + command = "UPDATE `general` SET `value`= :version WHERE `key`= :key;"; + else + command = "INSERT INTO `general` (`key`, `value`) VALUES (:key, :version);"; - auto result = sql::command(this->manager, command, variable{":version", version}, variable{":key", "permissions_version"}).execute(); - if(!result) { - logError(LOG_INSTANCE, "Could not update SQL permissions version. (" + result.fmtStr() + ")"); - return false; - } - this->permissions_version_present = true; - this->_permissions_version = version; + auto result = sql::command(this->manager, command, variable{":version", version}, variable{":key", "permissions_version"}).execute(); + if(!result) { + logError(LOG_INSTANCE, "Could not update SQL permissions version. (" + result.fmtStr() + ")"); + return false; + } + this->permissions_version_present = true; + this->_permissions_version = version; - return true; + return true; } \ No newline at end of file diff --git a/server/src/manager/SqlDataManager.h b/server/src/manager/SqlDataManager.h index bb217cf..6ff625e 100644 --- a/server/src/manager/SqlDataManager.h +++ b/server/src/manager/SqlDataManager.h @@ -3,31 +3,31 @@ #include namespace ts { - namespace server { - class SqlDataManager { - public: - SqlDataManager(); - virtual ~SqlDataManager(); + namespace server { + class SqlDataManager { + public: + SqlDataManager(); + virtual ~SqlDataManager(); - [[nodiscard]] inline int get_database_version() const { return this->_database_version; } - [[nodiscard]] inline int get_permissions_version() const { return this->_database_version; } - bool initialize(std::string&); - void finalize(); + [[nodiscard]] inline int get_database_version() const { return this->_database_version; } + [[nodiscard]] inline int get_permissions_version() const { return this->_database_version; } + bool initialize(std::string&); + void finalize(); - sql::SqlManager* sql() { return this->manager; } - private: - sql::SqlManager* manager = nullptr; - int _database_version = -1; - int _permissions_version = -1; - bool database_version_present = false; - bool permissions_version_present = false; + sql::SqlManager* sql() { return this->manager; } + private: + sql::SqlManager* manager = nullptr; + int _database_version = -1; + int _permissions_version = -1; + bool database_version_present = false; + bool permissions_version_present = false; - bool detect_versions(); - bool change_database_version(int); - bool change_permission_version(int); + bool detect_versions(); + bool change_database_version(int); + bool change_permission_version(int); - bool update_database(std::string& /* errror */); - bool update_permissions(std::string& /* errror */); - }; - } + bool update_database(std::string& /* errror */); + bool update_permissions(std::string& /* errror */); + }; + } } \ No newline at end of file diff --git a/server/src/music/MusicBotManager.cpp b/server/src/music/MusicBotManager.cpp index 276fa34..590d56c 100644 --- a/server/src/music/MusicBotManager.cpp +++ b/server/src/music/MusicBotManager.cpp @@ -27,8 +27,8 @@ void MusicBotManager::adjustTickPool() { } void MusicBotManager::shutdown() { - tick_music.shutdown(); - load_music.shutdown(); + tick_music.shutdown(); + load_music.shutdown(); } MusicBotManager::MusicBotManager(const shared_ptr& server) : handle(server) { } @@ -91,9 +91,9 @@ std::shared_ptr MusicBotManager::createBot(ClientDbId owner handle->groups->enableCache(musicBot->getClientDatabaseId()); musicBot->setDisplayName("Im a music bot!"); musicBot->properties()[property::CLIENT_LASTCONNECTED] = duration_cast(system_clock::now().time_since_epoch()).count(); - musicBot->properties()[property::CLIENT_CREATED] = duration_cast(system_clock::now().time_since_epoch()).count(); - musicBot->properties()[property::CLIENT_VERSION] = "TeaMusic"; - musicBot->properties()[property::CLIENT_PLATFORM] = "internal"; + musicBot->properties()[property::CLIENT_CREATED] = duration_cast(system_clock::now().time_since_epoch()).count(); + musicBot->properties()[property::CLIENT_VERSION] = "TeaMusic"; + musicBot->properties()[property::CLIENT_PLATFORM] = "internal"; handle->registerClient(musicBot); { @@ -115,8 +115,8 @@ bool MusicBotManager::assign_playlist(const std::shared_ptrplaylist(); - bot->set_playlist(playlist); + auto old_playlist = bot->playlist(); + bot->set_playlist(playlist); if(old_playlist && old_playlist->playlist_type() == Playlist::Type::BOT_BOUND) { string error; @@ -188,8 +188,8 @@ std::shared_ptr MusicBotManager::findBotById(ClientDbId id) void MusicBotManager::load_bots() { if(!config::music::enabled) return; - auto handle = this->handle.lock(); - assert(handle); + auto handle = this->handle.lock(); + assert(handle); auto res = sql::command(handle->getSql(), "SELECT `botId`, `uniqueId`, `owner` FROM `musicbots` WHERE `serverId` = :sid", variable{":sid", handle->getServerId()}).query(&MusicBotManager::sqlCreateMusicBot, this); (LOG_SQL_CMD)(res); @@ -215,8 +215,8 @@ void MusicBotManager::load_bots() { } int MusicBotManager::sqlCreateMusicBot(int length, std::string* values, std::string* names) { - auto handle = this->handle.lock(); - assert(handle); + auto handle = this->handle.lock(); + assert(handle); ClientDbId botId = 0, owner = 0; std::string uid; @@ -238,7 +238,7 @@ int MusicBotManager::sqlCreateMusicBot(int length, std::string* values, std::str musicBot->properties()[property::CLIENT_LASTCONNECTED] = duration_cast(system_clock::now().time_since_epoch()).count(); } - handle->groups->enableCache(musicBot->getClientDatabaseId()); + handle->groups->enableCache(musicBot->getClientDatabaseId()); if(musicBot->getClientDatabaseId() != botId) logCritical(handle->getServerId(),"Invalid music bot id mapping!"); { @@ -248,7 +248,7 @@ int MusicBotManager::sqlCreateMusicBot(int length, std::string* values, std::str playlist = this->create_playlist(0, "Music Manager"); playlist->properties()[property::PLAYLIST_TYPE] = Playlist::Type::BOT_BOUND; } - playlist->load_songs(); + playlist->load_songs(); musicBot->set_playlist(playlist); } @@ -262,9 +262,9 @@ int MusicBotManager::sqlCreateMusicBot(int length, std::string* values, std::str void MusicBotManager::connectBots() { for(const auto& bot : this->music_bots) { - bot->server = this->handle.lock(); - if(!bot->currentChannel || bot->getClientId() == 0) - bot->initialize_bot(); + bot->server = this->handle.lock(); + if(!bot->currentChannel || bot->getClientId() == 0) + bot->initialize_bot(); } } @@ -277,7 +277,7 @@ void MusicBotManager::disconnectBots() { unique_lock server_channel_lock(handle->channel_tree_lock); handle->client_move(bot, nullptr, nullptr, "Music bot deleted", ViewReasonId::VREASON_SERVER_LEFT, true, server_channel_lock); } - bot->server = nullptr; + bot->server = nullptr; } } diff --git a/server/src/music/MusicBotManager.h b/server/src/music/MusicBotManager.h index 965fc1d..1ffb3ed 100644 --- a/server/src/music/MusicBotManager.h +++ b/server/src/music/MusicBotManager.h @@ -53,7 +53,7 @@ namespace ts { return this->playlists_list; } std::shared_ptr find_playlist(PlaylistId /* id */); - std::deque> find_playlists_by_client(ClientDbId /* owner */); + std::deque> find_playlists_by_client(ClientDbId /* owner */); std::shared_ptr create_playlist(ClientDbId /* owner */, const std::string& /* owner name */); bool delete_playlist(PlaylistId /* id */, std::string& /* error */); diff --git a/server/src/music/MusicPlaylist.cpp b/server/src/music/MusicPlaylist.cpp index 28b6b72..cca9612 100644 --- a/server/src/music/MusicPlaylist.cpp +++ b/server/src/music/MusicPlaylist.cpp @@ -15,160 +15,160 @@ using namespace std::chrono; Playlist::Playlist(const std::shared_ptr &manager, const std::shared_ptr &properties, const std::shared_ptr& permissions) : _properties(properties), _permissions(permissions), manager(manager) { } Playlist::~Playlist() { - this->destory_tree(); + this->destory_tree(); } void Playlist::set_self_ref(const std::shared_ptr &ref) { - assert(&*ref == this); - this->_self = ref; + assert(&*ref == this); + this->_self = ref; } std::shared_ptr Playlist::get_server() { - auto handle = this->ref_handle(); - if(!handle) return nullptr; - return handle->ref_server(); + auto handle = this->ref_handle(); + if(!handle) return nullptr; + return handle->ref_server(); } ServerId Playlist::get_server_id() { - auto server = this->get_server(); - if(!server) return 0; + auto server = this->get_server(); + if(!server) return 0; - return server->getServerId(); + return server->getServerId(); } sql::SqlManager* Playlist::get_sql() { - auto server = this->get_server(); - if(!server) return nullptr; + auto server = this->get_server(); + if(!server) return nullptr; - return server->getSql(); + return server->getSql(); } std::shared_ptr Playlist::playlist_end(const std::unique_lock& lock) { - assert(lock.owns_lock()); + assert(lock.owns_lock()); - auto current = this->playlist_head; - while(current && current->next_song) - current = current->next_song; - return current; + auto current = this->playlist_head; + while(current && current->next_song) + current = current->next_song; + return current; } std::shared_ptr Playlist::playlist_find(const std::unique_lock &lock, ts::SongId id) { - assert(lock.owns_lock()); + assert(lock.owns_lock()); - auto current = this->playlist_head; - while(current) { - assert(current->entry); + auto current = this->playlist_head; + while(current) { + assert(current->entry); - if(current->entry->id == id) - return current; + if(current->entry->id == id) + return current; - current = current->next_song; - } + current = current->next_song; + } - return nullptr; + return nullptr; } bool Playlist::playlist_insert(const std::unique_lock &lock, const shared_ptr &entry, shared_ptr previous, bool link_only) { - assert(lock.owns_lock()); + assert(lock.owns_lock()); - entry->next_song = nullptr; - if(!this->playlist_head) { - this->playlist_head = entry; - previous = nullptr; - } else { - if(previous) { - if(previous->next_song) { - assert(previous->next_song->previous_song == previous); - entry->next_song = previous->next_song; - if(link_only) - previous->next_song->previous_song = entry; - else - previous->next_song->set_previous_song(entry); + entry->next_song = nullptr; + if(!this->playlist_head) { + this->playlist_head = entry; + previous = nullptr; + } else { + if(previous) { + if(previous->next_song) { + assert(previous->next_song->previous_song == previous); + entry->next_song = previous->next_song; + if(link_only) + previous->next_song->previous_song = entry; + else + previous->next_song->set_previous_song(entry); - } - previous->next_song = entry; - } else { - if(link_only) - this->playlist_head->previous_song = entry; - else - this->playlist_head->set_previous_song(entry); - entry->next_song = this->playlist_head; - this->playlist_head = entry; - } - } - if(link_only) - entry->previous_song = previous; - else - entry->set_previous_song(previous); + } + previous->next_song = entry; + } else { + if(link_only) + this->playlist_head->previous_song = entry; + else + this->playlist_head->set_previous_song(entry); + entry->next_song = this->playlist_head; + this->playlist_head = entry; + } + } + if(link_only) + entry->previous_song = previous; + else + entry->set_previous_song(previous); - return true; + return true; } bool Playlist::playlist_reorder(const std::unique_lock& lock, const shared_ptr &entry, std::shared_ptr previous) { - assert(lock.owns_lock()); + assert(lock.owns_lock()); - if(!this->playlist_remove(lock, entry)) return false; - return this->playlist_insert(lock, entry, std::move(previous)); + if(!this->playlist_remove(lock, entry)) return false; + return this->playlist_insert(lock, entry, std::move(previous)); } bool Playlist::playlist_remove(const std::unique_lock& lock, const std::shared_ptr &entry) { - assert(lock.owns_lock()); + assert(lock.owns_lock()); - if(entry == this->playlist_head) { - this->playlist_head = entry->next_song; - if(this->playlist_head) - this->playlist_head->set_previous_song(nullptr); - } else { - assert(entry->previous_song); /* if its not head it must have a previous song */ - entry->previous_song->next_song = entry->next_song; - if(entry->next_song) - entry->next_song->set_previous_song(entry->previous_song); - } + if(entry == this->playlist_head) { + this->playlist_head = entry->next_song; + if(this->playlist_head) + this->playlist_head->set_previous_song(nullptr); + } else { + assert(entry->previous_song); /* if its not head it must have a previous song */ + entry->previous_song->next_song = entry->next_song; + if(entry->next_song) + entry->next_song->set_previous_song(entry->previous_song); + } - /* release references */ - entry->previous_song = nullptr; - entry->next_song = nullptr; + /* release references */ + entry->previous_song = nullptr; + entry->next_song = nullptr; - return true; + return true; } void Playlist::load_songs() { - if(!this->_songs_loaded) - this->_songs_loaded = true; - else return; + if(!this->_songs_loaded) + this->_songs_loaded = true; + else return; - auto entries = this->load_entries(); - this->build_tree(std::move(entries)); + auto entries = this->load_entries(); + this->build_tree(std::move(entries)); } bool Playlist::sql_add(const std::shared_ptr &entry) { - auto sql_handle = this->get_sql(); - if(!sql_handle) return false; + auto sql_handle = this->get_sql(); + if(!sql_handle) return false; - entry->id = ++current_id; + entry->id = ++current_id; - //`serverId` INT NOT NULL, `playlist_id` INT, `song_id` INT, `order_id` INT, `invoker_dbid` INT, `url` TEXT - auto sql_result = sql::command(sql_handle, "INSERT INTO `playlist_songs` (`serverId`, `playlist_id`, `song_id`, `order_id`, `invoker_dbid`, `url`, `url_loader`, `loaded`, `metadata`) VALUES (:server_id, :playlist_id, :song_id, :order_id, :invoker_dbid, :url, :url_loader, :loaded, :metadata)", - variable{":server_id", this->get_server_id()}, - variable{":playlist_id", this->playlist_id()}, - variable{":song_id", entry->id}, - variable{":order_id", entry->previous_song_id}, - variable{":invoker_dbid", entry->invoker}, - variable{":url", entry->url}, + //`serverId` INT NOT NULL, `playlist_id` INT, `song_id` INT, `order_id` INT, `invoker_dbid` INT, `url` TEXT + auto sql_result = sql::command(sql_handle, "INSERT INTO `playlist_songs` (`serverId`, `playlist_id`, `song_id`, `order_id`, `invoker_dbid`, `url`, `url_loader`, `loaded`, `metadata`) VALUES (:server_id, :playlist_id, :song_id, :order_id, :invoker_dbid, :url, :url_loader, :loaded, :metadata)", + variable{":server_id", this->get_server_id()}, + variable{":playlist_id", this->playlist_id()}, + variable{":song_id", entry->id}, + variable{":order_id", entry->previous_song_id}, + variable{":invoker_dbid", entry->invoker}, + variable{":url", entry->url}, variable{":url_loader", entry->url_loader}, variable{":loaded", entry->loaded}, variable{":metadata", entry->metadata} - ).execute(); + ).execute(); - LOG_SQL_CMD(sql_result); + LOG_SQL_CMD(sql_result); - return !!sql_result; + return !!sql_result; } bool Playlist::sql_apply_changes(const std::shared_ptr &entry) { - auto sql_handle = this->get_sql(); - if(!sql_handle) return false; - sql::command(sql_handle, "UPDATE `playlist_songs` SET `order_id` = :order_id, `invoker_dbid` = :invoker_dbid, `url` = :url , `url_loader` = :url_loader, `loaded` = :loaded, `metadata` = :metadata WHERE `serverId` = :server_id AND `playlist_id` = :playlist_id AND `song_id` = :song_id", + auto sql_handle = this->get_sql(); + if(!sql_handle) return false; + sql::command(sql_handle, "UPDATE `playlist_songs` SET `order_id` = :order_id, `invoker_dbid` = :invoker_dbid, `url` = :url , `url_loader` = :url_loader, `loaded` = :loaded, `metadata` = :metadata WHERE `serverId` = :server_id AND `playlist_id` = :playlist_id AND `song_id` = :song_id", variable{":server_id", this->get_server_id()}, variable{":playlist_id", this->playlist_id()}, variable{":song_id", entry->id}, @@ -178,283 +178,283 @@ bool Playlist::sql_apply_changes(const std::shared_ptrurl_loader}, variable{":loaded", entry->loaded}, variable{":metadata", entry->metadata} - ).executeLater().waitAndGetLater(LOG_SQL_CMD, {-1, "failed future"}); + ).executeLater().waitAndGetLater(LOG_SQL_CMD, {-1, "failed future"}); - return true; + return true; } bool Playlist::sql_flush_all_changes() { - unique_lock list_lock(this->playlist_lock); - deque> changed_entries; - auto head = this->playlist_head; - while(head) { - if(head->modified) { - changed_entries.push_back(head->entry); - head->modified = false; - continue; - } + unique_lock list_lock(this->playlist_lock); + deque> changed_entries; + auto head = this->playlist_head; + while(head) { + if(head->modified) { + changed_entries.push_back(head->entry); + head->modified = false; + continue; + } - head = head->next_song; - } + head = head->next_song; + } - list_lock.unlock(); + list_lock.unlock(); - for(const auto& entry : changed_entries) - this->sql_apply_changes(entry); //TODO Whats when we encounter an error? + for(const auto& entry : changed_entries) + this->sql_apply_changes(entry); //TODO Whats when we encounter an error? - return true; + return true; } bool Playlist::sql_remove(const std::shared_ptr &entry) { - auto sql_handle = this->get_sql(); - if(!sql_handle) return false; - sql::command(sql_handle, "DELETE FROM `playlist_songs` WHERE `serverId` = :server_id AND `playlist_id` = :playlist_id AND `song_id` = :song_id", - variable{":server_id", this->get_server_id()}, - variable{":playlist_id", this->playlist_id()}, - variable{":song_id", entry->id} - ).executeLater().waitAndGetLater(LOG_SQL_CMD, {-1, "failed future"}); + auto sql_handle = this->get_sql(); + if(!sql_handle) return false; + sql::command(sql_handle, "DELETE FROM `playlist_songs` WHERE `serverId` = :server_id AND `playlist_id` = :playlist_id AND `song_id` = :song_id", + variable{":server_id", this->get_server_id()}, + variable{":playlist_id", this->playlist_id()}, + variable{":song_id", entry->id} + ).executeLater().waitAndGetLater(LOG_SQL_CMD, {-1, "failed future"}); - return true; + return true; } std::deque> Playlist::load_entries() { - std::deque> result; + std::deque> result; - this->current_id = 0; - auto sql_handle = this->get_sql(); - assert(sql_handle); - auto sql_result = sql::command(sql_handle, "SELECT `song_id`, `order_id`, `invoker_dbid`, `url`, `url_loader`, `loaded`, `metadata` FROM `playlist_songs` WHERE `serverId` = :server_id AND `playlist_id` = :playlist_id", - variable{":server_id", this->get_server_id()}, - variable{":playlist_id", this->playlist_id()}) - .query([&](int length, string* values, string* columns) { - auto entry = make_shared(); - for(int index = 0; index < length; index++) { - try { - if(columns[index] == "song_id") - entry->id = (SongId) stoll(values[index]); - else if(columns[index] == "order_id") - entry->previous_song_id = (SongId) stoll(values[index]); - else if(columns[index] == "invoker_dbid") - entry->invoker = (ClientDbId) stoll(values[index]); - else if(columns[index] == "url") - entry->url = values[index]; - else if(columns[index] == "url_loader") - entry->url_loader = values[index]; - else if(columns[index] == "loaded") - entry->loaded = values[index].length() > 0 && stoll(values[index]) == 1; - else if(columns[index] == "metadata") - entry->metadata = values[index]; - } catch(const std::exception& ex) { - logError(this->get_server_id(), "[PlayList] Failed to parse song entry property in playlist {}. Key: {}, Value: {}, Error: {}", this->playlist_id(), columns[index], values[index], ex.what()); - return; - } - } + this->current_id = 0; + auto sql_handle = this->get_sql(); + assert(sql_handle); + auto sql_result = sql::command(sql_handle, "SELECT `song_id`, `order_id`, `invoker_dbid`, `url`, `url_loader`, `loaded`, `metadata` FROM `playlist_songs` WHERE `serverId` = :server_id AND `playlist_id` = :playlist_id", + variable{":server_id", this->get_server_id()}, + variable{":playlist_id", this->playlist_id()}) + .query([&](int length, string* values, string* columns) { + auto entry = make_shared(); + for(int index = 0; index < length; index++) { + try { + if(columns[index] == "song_id") + entry->id = (SongId) stoll(values[index]); + else if(columns[index] == "order_id") + entry->previous_song_id = (SongId) stoll(values[index]); + else if(columns[index] == "invoker_dbid") + entry->invoker = (ClientDbId) stoll(values[index]); + else if(columns[index] == "url") + entry->url = values[index]; + else if(columns[index] == "url_loader") + entry->url_loader = values[index]; + else if(columns[index] == "loaded") + entry->loaded = values[index].length() > 0 && stoll(values[index]) == 1; + else if(columns[index] == "metadata") + entry->metadata = values[index]; + } catch(const std::exception& ex) { + logError(this->get_server_id(), "[PlayList] Failed to parse song entry property in playlist {}. Key: {}, Value: {}, Error: {}", this->playlist_id(), columns[index], values[index], ex.what()); + return; + } + } - if(entry->id > this->current_id) - this->current_id = entry->id; - result.push_back(move(entry)); - }); - LOG_SQL_CMD(sql_result); + if(entry->id > this->current_id) + this->current_id = entry->id; + result.push_back(move(entry)); + }); + LOG_SQL_CMD(sql_result); - map count; - for(const auto& entry : result) - ++count[entry->id]; + map count; + for(const auto& entry : result) + ++count[entry->id]; - for(const auto& entry : count) { - if(entry.second <= 1) continue; + for(const auto& entry : count) { + if(entry.second <= 1) continue; - logError(this->get_server_id(), "[PlayList] Playlist {} contains {} times a song with ID {}. removing all!", this->playlist_id(), entry.second, entry.first); - auto sql_command = sql::command(sql_handle, "DELETE FROM `playlist_songs` WHERE `serverId` = :server_id AND `playlist_id` = :playlist_id AND `song_id` = :song_id", - variable{":server_id", this->get_server_id()}, - variable{":playlist_id", this->playlist_id()}, - variable{":song_id", entry.first} - ).execute(); - LOG_SQL_CMD(sql_command); - } + logError(this->get_server_id(), "[PlayList] Playlist {} contains {} times a song with ID {}. removing all!", this->playlist_id(), entry.second, entry.first); + auto sql_command = sql::command(sql_handle, "DELETE FROM `playlist_songs` WHERE `serverId` = :server_id AND `playlist_id` = :playlist_id AND `song_id` = :song_id", + variable{":server_id", this->get_server_id()}, + variable{":playlist_id", this->playlist_id()}, + variable{":song_id", entry.first} + ).execute(); + LOG_SQL_CMD(sql_command); + } - { + { - std::deque> _result; - for(const auto& entry : result) - if(count[entry->id] <= 1) { - this->enqueue_load(entry); - _result.push_back(entry); - } + std::deque> _result; + for(const auto& entry : result) + if(count[entry->id] <= 1) { + this->enqueue_load(entry); + _result.push_back(entry); + } - return _result; - } + return _result; + } } bool Playlist::build_tree(deque> entries) { - this->playlist_head = nullptr; + this->playlist_head = nullptr; - if(entries.empty()) - return true; + if(entries.empty()) + return true; - unique_lock list_lock(this->playlist_lock); - auto find_entry = [&](SongId id) -> shared_ptr { - for(const auto& entry : entries) - if(entry->id == id) - return entry; - return nullptr; - }; + unique_lock list_lock(this->playlist_lock); + auto find_entry = [&](SongId id) -> shared_ptr { + for(const auto& entry : entries) + if(entry->id == id) + return entry; + return nullptr; + }; - deque> l_entries; - for(const auto& entry : entries) - l_entries.push_back(move(linked::create_entry(0, entry->id, entry->previous_song_id))); + deque> l_entries; + for(const auto& entry : entries) + l_entries.push_back(move(linked::create_entry(0, entry->id, entry->previous_song_id))); - deque errors; - auto head = linked::build_chain(l_entries, errors); + deque errors; + auto head = linked::build_chain(l_entries, errors); - shared_ptr current_tail = nullptr; - while(head) { - auto entry = make_shared(); - entry->entry = find_entry(head->entry_id); + shared_ptr current_tail = nullptr; + while(head) { + auto entry = make_shared(); + entry->entry = find_entry(head->entry_id); - if(!current_tail) { - /* initialization */ - if(head->modified) - entry->set_previous_song(nullptr); - else - entry->previous_song = nullptr; + if(!current_tail) { + /* initialization */ + if(head->modified) + entry->set_previous_song(nullptr); + else + entry->previous_song = nullptr; - this->playlist_head = entry; - } else { - if(head->modified) - entry->set_previous_song(current_tail); - else - entry->previous_song = current_tail; - } + this->playlist_head = entry; + } else { + if(head->modified) + entry->set_previous_song(current_tail); + else + entry->previous_song = current_tail; + } - if(current_tail) - current_tail->next_song = entry; - current_tail = entry; + if(current_tail) + current_tail->next_song = entry; + current_tail = entry; - head = head->next; - } + head = head->next; + } - return true; + return true; } void Playlist::destory_tree() { - unique_lock list_lock(this->playlist_lock); - auto element = this->playlist_head; - while(element) { - element->entry = nullptr; - if(element->previous_song) { - element->previous_song->next_song = nullptr; - element->previous_song = nullptr; - } - element = element->next_song; - } + unique_lock list_lock(this->playlist_lock); + auto element = this->playlist_head; + while(element) { + element->entry = nullptr; + if(element->previous_song) { + element->previous_song->next_song = nullptr; + element->previous_song = nullptr; + } + element = element->next_song; + } } std::deque> Playlist::list_songs() { - deque> result; + deque> result; - unique_lock list_lock(this->playlist_lock); - auto head = this->playlist_head; - while(head) { - result.push_back(head->entry); - head = head->next_song; - } + unique_lock list_lock(this->playlist_lock); + auto head = this->playlist_head; + while(head) { + result.push_back(head->entry); + head = head->next_song; + } - return result; + return result; } std::shared_ptr Playlist::find_song(ts::SongId id) { - unique_lock list_lock(this->playlist_lock); - auto head = this->playlist_head; - while(head) { - if(head->entry->id == id) - return head->entry; + unique_lock list_lock(this->playlist_lock); + auto head = this->playlist_head; + while(head) { + if(head->entry->id == id) + return head->entry; - head = head->next_song; - } + head = head->next_song; + } - return nullptr; + return nullptr; } std::shared_ptr Playlist::add_song(const std::shared_ptr &client, const std::string &url, const std::string& url_loader, ts::SongId order) { - return this->add_song(client ? client->getClientDatabaseId() : 0, url, url_loader, order); + return this->add_song(client ? client->getClientDatabaseId() : 0, url, url_loader, order); } std::shared_ptr Playlist::add_song(ClientDbId client, const std::string &url, const std::string& url_loader, ts::SongId order) { - auto entry = make_shared(); + auto entry = make_shared(); - entry->previous_song_id = order; - entry->invoker = client; - entry->url = url; - entry->url_loader = url_loader; + entry->previous_song_id = order; + entry->invoker = client; + entry->url = url; + entry->url_loader = url_loader; - if(!this->sql_add(entry)) return nullptr; + if(!this->sql_add(entry)) return nullptr; - auto list_entry = make_shared(); - list_entry->entry = entry; + auto list_entry = make_shared(); + list_entry->entry = entry; - unique_lock list_lock(this->playlist_lock); - if(order == 0) { - auto end = playlist_end(list_lock); - entry->previous_song_id = end ? end->entry->id : 0; - list_entry->modified = true; - } - auto order_entry = this->playlist_find(list_lock, entry->previous_song_id); - this->playlist_insert(list_lock, list_entry, order_entry); - if(order_entry ? order_entry->entry->id : 0 != order || list_entry->modified) { - list_entry->modified = false; - entry->previous_song_id = list_entry->previous_song ? list_entry->previous_song->entry->id : 0; - this->sql_apply_changes(entry); - } + unique_lock list_lock(this->playlist_lock); + if(order == 0) { + auto end = playlist_end(list_lock); + entry->previous_song_id = end ? end->entry->id : 0; + list_entry->modified = true; + } + auto order_entry = this->playlist_find(list_lock, entry->previous_song_id); + this->playlist_insert(list_lock, list_entry, order_entry); + if(order_entry ? order_entry->entry->id : 0 != order || list_entry->modified) { + list_entry->modified = false; + entry->previous_song_id = list_entry->previous_song ? list_entry->previous_song->entry->id : 0; + this->sql_apply_changes(entry); + } - this->enqueue_load(entry); - this->properties()[property::PLAYLIST_FLAG_FINISHED] = false; - return entry; + this->enqueue_load(entry); + this->properties()[property::PLAYLIST_FLAG_FINISHED] = false; + return entry; } bool Playlist::delete_song(ts::SongId id) { - unique_lock list_lock(this->playlist_lock); - auto song = this->playlist_find(list_lock, id); - if(!song) return false; + unique_lock list_lock(this->playlist_lock); + auto song = this->playlist_find(list_lock, id); + if(!song) return false; - this->playlist_remove(list_lock, song); - list_lock.unlock(); + this->playlist_remove(list_lock, song); + list_lock.unlock(); - this->sql_remove(song->entry); - return true; + this->sql_remove(song->entry); + return true; } bool Playlist::reorder_song(ts::SongId song_id, ts::SongId order_id) { - unique_lock list_lock(this->playlist_lock); - auto song = this->playlist_find(list_lock, song_id); - auto order = this->playlist_find(list_lock, order_id); - if(!song) return false; - if(!order && order_id != 0) return false; + unique_lock list_lock(this->playlist_lock); + auto song = this->playlist_find(list_lock, song_id); + auto order = this->playlist_find(list_lock, order_id); + if(!song) return false; + if(!order && order_id != 0) return false; - if(!this->playlist_reorder(list_lock, song, order)) return false; - list_lock.unlock(); + if(!this->playlist_reorder(list_lock, song, order)) return false; + list_lock.unlock(); - this->sql_flush_all_changes(); - return true; + this->sql_flush_all_changes(); + return true; } void Playlist::enqueue_load(const std::shared_ptr &entry) { - if(entry->load_future) return; /* song has been already loaded or parsed */ + if(entry->load_future) return; /* song has been already loaded or parsed */ - entry->load_start = system_clock::now(); - entry->load_future = make_unique(); - weak_ptr weak_self = this->ref(); - weak_ptr weak_entry = entry; + entry->load_start = system_clock::now(); + entry->load_future = make_unique(); + weak_ptr weak_self = this->ref(); + weak_ptr weak_entry = entry; - logTrace(this->get_server_id(), "[PlayList] Enqueueing song {} for loading", entry->id); + logTrace(this->get_server_id(), "[PlayList] Enqueueing song {} for loading", entry->id); - MusicBotManager::load_music.execute([weak_self, weak_entry] { - shared_ptr self = weak_self.lock(); - auto entry = weak_entry.lock(); - if(!self || !entry) - return; /* we're old */ - self->execute_async_load(entry); - }); + MusicBotManager::load_music.execute([weak_self, weak_entry] { + shared_ptr self = weak_self.lock(); + auto entry = weak_entry.lock(); + if(!self || !entry) + return; /* we're old */ + self->execute_async_load(entry); + }); } #define FORCE_LOAD \ @@ -462,16 +462,16 @@ entry->loaded = false; \ goto load_entry /* - struct UrlSongInfo : public UrlInfo { - std::string title; - std::string description; + struct UrlSongInfo : public UrlInfo { + std::string title; + std::string description; - std::map metadata; - }; + std::map metadata; + }; - struct UrlPlaylistInfo : public UrlInfo { - std::deque> entries; - }; + struct UrlPlaylistInfo : public UrlInfo { + std::deque> entries; + }; */ using UrlType = ::music::UrlType; @@ -479,111 +479,111 @@ using UrlSongInfo = ::music::UrlSongInfo; using UrlPlaylistInfo = ::music::UrlPlaylistInfo; void Playlist::execute_async_load(const std::shared_ptr &entry) { - if(entry->loaded) { /* we need to parse the metadata */ - Json::Value data; - Json::CharReaderBuilder rbuilder; - std::string errs; + if(entry->loaded) { /* we need to parse the metadata */ + Json::Value data; + Json::CharReaderBuilder rbuilder; + std::string errs; - istringstream jsonStream(entry->metadata); - bool parsingSuccessful = Json::parseFromStream(rbuilder, jsonStream, &data, &errs); - if (!parsingSuccessful) { - logError(this->get_server_id(), "[PlayList] Failed to parse loaded metadata for song {}. Metadata data!", entry->id); - FORCE_LOAD; - } + istringstream jsonStream(entry->metadata); + bool parsingSuccessful = Json::parseFromStream(rbuilder, jsonStream, &data, &errs); + if (!parsingSuccessful) { + logError(this->get_server_id(), "[PlayList] Failed to parse loaded metadata for song {}. Metadata data!", entry->id); + FORCE_LOAD; + } - if(!data["type"].isUInt()) { - logError(this->get_server_id(), "[PlayList] Failed to parse loaded metadata for song {}. Metadata has an invalid key 'type'. Query data again!", entry->id); - FORCE_LOAD; - } - if(!data["url"].isString()) { - logError(this->get_server_id(), "[PlayList] Failed to parse loaded metadata for song {}. Metadata has an invalid key 'url'. Query data again!", entry->id); - FORCE_LOAD; - } + if(!data["type"].isUInt()) { + logError(this->get_server_id(), "[PlayList] Failed to parse loaded metadata for song {}. Metadata has an invalid key 'type'. Query data again!", entry->id); + FORCE_LOAD; + } + if(!data["url"].isString()) { + logError(this->get_server_id(), "[PlayList] Failed to parse loaded metadata for song {}. Metadata has an invalid key 'url'. Query data again!", entry->id); + FORCE_LOAD; + } - auto type = (UrlType) data["type"].asUInt(); - if(type != UrlType::TYPE_VIDEO && type != UrlType::TYPE_STREAM) { - /* we're currently not able to parse playlists */ - FORCE_LOAD; - } else { - auto info = make_shared(); - if(data["metadata"].isObject()) { - for(const auto& key : data["metadata"]) - info->metadata[key.asString()] = data["metadata"][key.asString()].asString(); - } - info->url = data["url"].asString(); - info->title = data["title"].asString(); - info->type = type; - info->description = data["description"].asString(); + auto type = (UrlType) data["type"].asUInt(); + if(type != UrlType::TYPE_VIDEO && type != UrlType::TYPE_STREAM) { + /* we're currently not able to parse playlists */ + FORCE_LOAD; + } else { + auto info = make_shared(); + if(data["metadata"].isObject()) { + for(const auto& key : data["metadata"]) + info->metadata[key.asString()] = data["metadata"][key.asString()].asString(); + } + info->url = data["url"].asString(); + info->title = data["title"].asString(); + info->type = type; + info->description = data["description"].asString(); - entry->load_future->executionSucceed(info); - } - } + entry->load_future->executionSucceed(info); + } + } - load_entry: - if(!entry->loaded) { - auto provider = ::music::manager::resolveProvider(entry->url_loader, entry->url); - if(!provider) { - entry->load_future->executionFailed("failed to load info provider"); - return; - } + load_entry: + if(!entry->loaded) { + auto provider = ::music::manager::resolveProvider(entry->url_loader, entry->url); + if(!provider) { + entry->load_future->executionFailed("failed to load info provider"); + return; + } - auto info_future = provider->query_info(entry->url, nullptr, &*this->get_server()); - info_future.waitAndGet(system_clock::now() + seconds(30)); /* TODO load timeout configurable? */ + auto info_future = provider->query_info(entry->url, nullptr, &*this->get_server()); + info_future.waitAndGet(system_clock::now() + seconds(30)); /* TODO load timeout configurable? */ - if(info_future.succeeded() && info_future.get()) { - auto result = *info_future.get(); + if(info_future.succeeded() && info_future.get()) { + auto result = *info_future.get(); - { - Json::Value root; - root["type"] = result->type; - root["url"] = result->url; - if(result->type == UrlType::TYPE_PLAYLIST) { - auto casted = static_pointer_cast(result); - } else if(result->type == UrlType::TYPE_STREAM || result->type == UrlType::TYPE_VIDEO) { - auto casted = static_pointer_cast(result); - root["title"] = casted->title; - root["description"] = casted->description; - for(const auto& meta : casted->metadata) { - root["metadata"][meta.first] = meta.second; - } - } + { + Json::Value root; + root["type"] = result->type; + root["url"] = result->url; + if(result->type == UrlType::TYPE_PLAYLIST) { + auto casted = static_pointer_cast(result); + } else if(result->type == UrlType::TYPE_STREAM || result->type == UrlType::TYPE_VIDEO) { + auto casted = static_pointer_cast(result); + root["title"] = casted->title; + root["description"] = casted->description; + for(const auto& meta : casted->metadata) { + root["metadata"][meta.first] = meta.second; + } + } - entry->metadata = root.toStyledString(); + entry->metadata = root.toStyledString(); - Json::StreamWriterBuilder builder; - builder["indentation"] = ""; // If you want whitespace-less output - entry->metadata = Json::writeString(builder, root); - } + Json::StreamWriterBuilder builder; + builder["indentation"] = ""; // If you want whitespace-less output + entry->metadata = Json::writeString(builder, root); + } - if(result->type == UrlType::TYPE_PLAYLIST) { - auto playlist = static_pointer_cast(result); - debugMessage(this->get_server_id(), "[PlayList] Song {} shows up as a playlist. Inserting entries {}", entry->id, playlist->entries.size()); + if(result->type == UrlType::TYPE_PLAYLIST) { + auto playlist = static_pointer_cast(result); + debugMessage(this->get_server_id(), "[PlayList] Song {} shows up as a playlist. Inserting entries {}", entry->id, playlist->entries.size()); - SongId previous_id = entry->id; - size_t current_songs = this->list_songs().size(); - auto max_songs = this->max_songs(); + SongId previous_id = entry->id; + size_t current_songs = this->list_songs().size(); + auto max_songs = this->max_songs(); - for(const auto& element : playlist->entries) { - if(max_songs != -1 && max_songs < /* = (+1 because we delete the main playlist) */ current_songs) { + for(const auto& element : playlist->entries) { + if(max_songs != -1 && max_songs < /* = (+1 because we delete the main playlist) */ current_songs) { logMessage(this->get_server_id(), "[PlayList][{}] Added playlist {} contains more songs than allowed by setting. Dropping the rest.", this->playlist_id(), element->url); - break; - } - auto queued_element = this->add_song(entry->invoker, element->url, "", previous_id); - if(!queued_element) continue; //TODO log insert fail? + break; + } + auto queued_element = this->add_song(entry->invoker, element->url, "", previous_id); + if(!queued_element) continue; //TODO log insert fail? - previous_id = queued_element->id; - } + previous_id = queued_element->id; + } - this->delete_song(entry->id); /* delete playlist song entry because it has been resolved compleatly */ - } - entry->load_future->executionSucceed(result); - entry->loaded = true; + this->delete_song(entry->id); /* delete playlist song entry because it has been resolved compleatly */ + } + entry->load_future->executionSucceed(result); + entry->loaded = true; - if(result->type != UrlType::TYPE_PLAYLIST) /* we've already deleted this entry if this is a playlist entry*/ - this->sql_apply_changes(entry); - } else { - entry->load_future->executionFailed("failed to load info: " + info_future.errorMegssage()); - return; - } - } + if(result->type != UrlType::TYPE_PLAYLIST) /* we've already deleted this entry if this is a playlist entry*/ + this->sql_apply_changes(entry); + } else { + entry->load_future->executionFailed("failed to load info: " + info_future.errorMegssage()); + return; + } + } } \ No newline at end of file diff --git a/server/src/music/MusicPlaylist.h b/server/src/music/MusicPlaylist.h index d13d209..c1a7888 100644 --- a/server/src/music/MusicPlaylist.h +++ b/server/src/music/MusicPlaylist.h @@ -8,148 +8,148 @@ #include "MusicBotManager.h" namespace ts { - namespace server { - class ConnectedClient; - } + namespace server { + class ConnectedClient; + } - namespace permission { - class PermissionManager; - } + namespace permission { + class PermissionManager; + } - namespace music { - class Playlist; + namespace music { + class Playlist; - struct PlaylistEntryInfo { - typedef threads::Future> load_future_t; - SongId previous_song_id = 0; + struct PlaylistEntryInfo { + typedef threads::Future> load_future_t; + SongId previous_song_id = 0; - ClientDbId invoker = 0; - SongId id = 0; + ClientDbId invoker = 0; + SongId id = 0; - std::string url; - std::string url_loader; - std::string metadata; /* only present when loaded */ - bool loaded = false; + std::string url; + std::string url_loader; + std::string metadata; /* only present when loaded */ + bool loaded = false; - std::chrono::system_clock::time_point load_start; - std::unique_ptr load_future; + std::chrono::system_clock::time_point load_start; + std::unique_ptr load_future; struct { bool successfully_loaded; } data; - }; + }; - class PlaylistEntry { - friend class Playlist; - public: - /* linking for the list */ - std::shared_ptr previous_song; - std::shared_ptr next_song; + class PlaylistEntry { + friend class Playlist; + public: + /* linking for the list */ + std::shared_ptr previous_song; + std::shared_ptr next_song; - /* the entry */ - std::shared_ptr entry; + /* the entry */ + std::shared_ptr entry; - bool modified = false; - private: - inline void set_previous_song(const std::shared_ptr& song) { - assert(this->entry); + bool modified = false; + private: + inline void set_previous_song(const std::shared_ptr& song) { + assert(this->entry); - this->previous_song = song; - this->entry->previous_song_id = 0; - this->modified = true; - } - }; + this->previous_song = song; + this->entry->previous_song_id = 0; + this->modified = true; + } + }; - //TODO add some kind of play history? - class Playlist { - friend class MusicBotManager; - public: - struct Type { - enum value { - BOT_BOUND, - GENERAL - }; - }; + //TODO add some kind of play history? + class Playlist { + friend class MusicBotManager; + public: + struct Type { + enum value { + BOT_BOUND, + GENERAL + }; + }; - Playlist(const std::shared_ptr& /* manager */, const std::shared_ptr& /* properties */, const std::shared_ptr& /* permissions */); - virtual ~Playlist(); + Playlist(const std::shared_ptr& /* manager */, const std::shared_ptr& /* properties */, const std::shared_ptr& /* permissions */); + virtual ~Playlist(); - void load_songs(); - inline bool loaded() { return this->_songs_loaded; }; + void load_songs(); + inline bool loaded() { return this->_songs_loaded; }; - virtual std::deque> list_songs(); - virtual std::shared_ptr find_song(SongId /* song */); - virtual std::shared_ptr add_song(const std::shared_ptr& /* invoker */, const std::string& /* url */, const std::string& /* url loader */, SongId /* previous */ = 0); - virtual std::shared_ptr add_song(ClientDbId /* invoker */, const std::string& /* url */, const std::string& /* url loader */, SongId /* previous */ = 0); - virtual bool delete_song(SongId /* song */); - virtual bool reorder_song(SongId /* song */, SongId /* new id */); + virtual std::deque> list_songs(); + virtual std::shared_ptr find_song(SongId /* song */); + virtual std::shared_ptr add_song(const std::shared_ptr& /* invoker */, const std::string& /* url */, const std::string& /* url loader */, SongId /* previous */ = 0); + virtual std::shared_ptr add_song(ClientDbId /* invoker */, const std::string& /* url */, const std::string& /* url loader */, SongId /* previous */ = 0); + virtual bool delete_song(SongId /* song */); + virtual bool reorder_song(SongId /* song */, SongId /* new id */); - inline Properties& properties() { - return *this->_properties; - } + inline Properties& properties() { + return *this->_properties; + } - inline std::shared_ptr permissions() { - return this->_permissions; - } + inline std::shared_ptr permissions() { + return this->_permissions; + } - inline PlaylistId playlist_id() { - return this->properties()[property::PLAYLIST_ID].as(); - } + inline PlaylistId playlist_id() { + return this->properties()[property::PLAYLIST_ID].as(); + } - inline Type::value playlist_type() { - return this->properties()[property::PLAYLIST_TYPE].as(); - } + inline Type::value playlist_type() { + return this->properties()[property::PLAYLIST_TYPE].as(); + } - inline int32_t max_songs() { - return this->properties()[property::PLAYLIST_MAX_SONGS]; - } + inline int32_t max_songs() { + return this->properties()[property::PLAYLIST_MAX_SONGS]; + } - inline std::shared_ptr ref_handle() { return this->manager.lock(); } + inline std::shared_ptr ref_handle() { return this->manager.lock(); } - template ::value, int>::type = 0> - inline std::shared_ptr ref() { return std::dynamic_pointer_cast(this->_self.lock()); } - protected: - virtual void set_self_ref(const std::shared_ptr& /* playlist */); + template ::value, int>::type = 0> + inline std::shared_ptr ref() { return std::dynamic_pointer_cast(this->_self.lock()); } + protected: + virtual void set_self_ref(const std::shared_ptr& /* playlist */); - std::atomic current_id; - std::shared_ptr _permissions; - std::shared_ptr _properties; - std::weak_ptr manager; - std::weak_ptr _self; - bool _songs_loaded = false; + std::atomic current_id; + std::shared_ptr _permissions; + std::shared_ptr _properties; + std::weak_ptr manager; + std::weak_ptr _self; + bool _songs_loaded = false; - sql::SqlManager* get_sql(); - std::shared_ptr get_server(); - ServerId get_server_id(); + sql::SqlManager* get_sql(); + std::shared_ptr get_server(); + ServerId get_server_id(); - std::shared_mutex playlist_lock; - std::shared_ptr playlist_head; + std::shared_mutex playlist_lock; + std::shared_ptr playlist_head; - /* playlist functions are threadsave */ - std::shared_ptr playlist_find(const std::unique_lock& /* playlist lock */, SongId /* song */); - std::shared_ptr playlist_end(const std::unique_lock &); - bool playlist_insert( - const std::unique_lock& /* playlist lock */, - const std::shared_ptr& /* song */, - std::shared_ptr /* previous */ = nullptr, + /* playlist functions are threadsave */ + std::shared_ptr playlist_find(const std::unique_lock& /* playlist lock */, SongId /* song */); + std::shared_ptr playlist_end(const std::unique_lock &); + bool playlist_insert( + const std::unique_lock& /* playlist lock */, + const std::shared_ptr& /* song */, + std::shared_ptr /* previous */ = nullptr, bool link_only = false ); - bool playlist_remove(const std::unique_lock& /* playlist lock */, const std::shared_ptr& /* song */); - bool playlist_reorder(const std::unique_lock& /* playlist lock */, const std::shared_ptr& /* song */, std::shared_ptr /* previous */ = nullptr); + bool playlist_remove(const std::unique_lock& /* playlist lock */, const std::shared_ptr& /* song */); + bool playlist_reorder(const std::unique_lock& /* playlist lock */, const std::shared_ptr& /* song */, std::shared_ptr /* previous */ = nullptr); - std::deque> load_entries(); - bool build_tree(std::deque> /* entries */); - void destory_tree(); + std::deque> load_entries(); + bool build_tree(std::deque> /* entries */); + void destory_tree(); - bool sql_remove(const std::shared_ptr& /* entry */); - bool sql_add(const std::shared_ptr& /* entry */); /* also assigns an ID */ - bool sql_apply_changes(const std::shared_ptr& /* entry */); - bool sql_flush_all_changes(); + bool sql_remove(const std::shared_ptr& /* entry */); + bool sql_add(const std::shared_ptr& /* entry */); /* also assigns an ID */ + bool sql_apply_changes(const std::shared_ptr& /* entry */); + bool sql_flush_all_changes(); - void enqueue_load(const std::shared_ptr& /* entry */); - void execute_async_load(const std::shared_ptr &/* entry */); - }; - } + void enqueue_load(const std::shared_ptr& /* entry */); + void execute_async_load(const std::shared_ptr &/* entry */); + }; + } } DEFINE_TRANSFORMS(ts::music::Playlist::Type::value, uint8_t); \ No newline at end of file diff --git a/server/src/music/PlayablePlaylist.cpp b/server/src/music/PlayablePlaylist.cpp index 5b20a84..c3e8e4d 100644 --- a/server/src/music/PlayablePlaylist.cpp +++ b/server/src/music/PlayablePlaylist.cpp @@ -15,141 +15,141 @@ PlayablePlaylist::PlayablePlaylist(const std::shared_ptr &handl PlayablePlaylist::~PlayablePlaylist() {} void PlayablePlaylist::set_self_ref(const std::shared_ptr &ptr) { - Playlist::set_self_ref(ptr); + Playlist::set_self_ref(ptr); } std::shared_ptr PlayablePlaylist::pop_next() { - unique_lock lock(this->currently_playing_lock); + unique_lock lock(this->currently_playing_lock); - auto entry = this->loading_entry.lock(); - if(!entry) { - lock.unlock(); - entry = this->pop_next_entry(); - lock.lock(); - this->loading_entry = entry; + auto entry = this->loading_entry.lock(); + if(!entry) { + lock.unlock(); + entry = this->pop_next_entry(); + lock.lock(); + this->loading_entry = entry; - auto id = entry ? entry->id : 0; - if(this->properties()[property::PLAYLIST_CURRENT_SONG_ID].as() != id) - this->properties()[property::PLAYLIST_CURRENT_SONG_ID] = id; - } - if(!entry) return nullptr; + auto id = entry ? entry->id : 0; + if(this->properties()[property::PLAYLIST_CURRENT_SONG_ID].as() != id) + this->properties()[property::PLAYLIST_CURRENT_SONG_ID] = id; + } + if(!entry) return nullptr; - if(!entry->load_future) { - this->loading_entry.reset(); - return nullptr; /* skip this entry */ - } + if(!entry->load_future) { + this->loading_entry.reset(); + return nullptr; /* skip this entry */ + } - if(entry->load_future->state() == threads::FutureState::WORKING) { - if(entry->load_start + seconds(30) < system_clock::now()) { - this->loading_entry.reset(); - logError(this->get_server_id(), "[PlayList] Failed to load song info. Error: {}. Timeout. Removing song", entry->load_future->errorMegssage()); - this->delete_song(entry->id); /* acquired playlist */ + if(entry->load_future->state() == threads::FutureState::WORKING) { + if(entry->load_start + seconds(30) < system_clock::now()) { + this->loading_entry.reset(); + logError(this->get_server_id(), "[PlayList] Failed to load song info. Error: {}. Timeout. Removing song", entry->load_future->errorMegssage()); + this->delete_song(entry->id); /* acquired playlist */ - return PlayableSong::create({ + return PlayableSong::create({ entry->id, entry->url, entry->invoker }, MusicClient::failedLoader("Failed to parse song info. Info loader timeouted!")); - } + } - return nullptr; - } + return nullptr; + } - this->loading_entry.reset(); /* reset the loading entry because we're returning it when succeeded, else we skip*/ - if(!entry->load_future->succeeded() || !entry->load_future->getValue(nullptr)) { - //TODO log error within channel? - logError(this->get_server_id(), "[PlayList] Failed to load song info. Error: {}. Removing song", entry->load_future->errorMegssage()); - this->delete_song(entry->id); /* acquired playlist */ + this->loading_entry.reset(); /* reset the loading entry because we're returning it when succeeded, else we skip*/ + if(!entry->load_future->succeeded() || !entry->load_future->getValue(nullptr)) { + //TODO log error within channel? + logError(this->get_server_id(), "[PlayList] Failed to load song info. Error: {}. Removing song", entry->load_future->errorMegssage()); + this->delete_song(entry->id); /* acquired playlist */ - /* return promise */ - return PlayableSong::create({ + /* return promise */ + return PlayableSong::create({ entry->id, entry->url, entry->invoker }, MusicClient::failedLoader("Failed to parse song info: " + entry->load_future->errorMegssage())); - } + } - auto result = entry->load_future->getValue(nullptr); - assert(result); /* previous tested */ + auto result = entry->load_future->getValue(nullptr); + assert(result); /* previous tested */ - if(result->type != ::music::UrlType::TYPE_VIDEO && result->type != ::music::UrlType::TYPE_STREAM) { - /* we cant replay that kind of entry. Skipping it */ - debugMessage(this->get_server_id(), "[PlayList] Tried to replay an invalid entry type (SongID: {}, Type: {})! Skipping song", entry->id, result->type); - return nullptr; - } + if(result->type != ::music::UrlType::TYPE_VIDEO && result->type != ::music::UrlType::TYPE_STREAM) { + /* we cant replay that kind of entry. Skipping it */ + debugMessage(this->get_server_id(), "[PlayList] Tried to replay an invalid entry type (SongID: {}, Type: {})! Skipping song", entry->id, result->type); + return nullptr; + } - return PlayableSong::create({ + return PlayableSong::create({ entry->id, result->url, entry->invoker - }, MusicClient::providerLoader(entry->url_loader)); + }, MusicClient::providerLoader(entry->url_loader)); } std::shared_ptr PlayablePlaylist::pop_next_entry() { - if(!this->playlist_head) return nullptr; /* fuzzy check if we're not maybe empty */ + if(!this->playlist_head) return nullptr; /* fuzzy check if we're not maybe empty */ - auto replay_mode = this->properties()[property::PLAYLIST_REPLAY_MODE].as(); - auto songs = this->list_songs(); - if(songs.empty()) return nullptr; /* we've nothing to play */ + auto replay_mode = this->properties()[property::PLAYLIST_REPLAY_MODE].as(); + auto songs = this->list_songs(); + if(songs.empty()) return nullptr; /* we've nothing to play */ - unique_lock playlist_lock(this->playlist_lock); - auto current_song = this->playlist_find(playlist_lock, this->currently_playing()); + unique_lock playlist_lock(this->playlist_lock); + auto current_song = this->playlist_find(playlist_lock, this->currently_playing()); - if(replay_mode == ReplayMode::SINGLE_LOOPED) { - if(current_song) return current_song->entry; - return songs.front(); - } + if(replay_mode == ReplayMode::SINGLE_LOOPED) { + if(current_song) return current_song->entry; + return songs.front(); + } - std::shared_ptr result; - if(replay_mode == ReplayMode::LINEAR || replay_mode == ReplayMode::LINEAR_LOOPED) { - if(current_song) { - if(current_song->next_song) - result = current_song->next_song->entry; - else if(replay_mode == ReplayMode::LINEAR_LOOPED && this->playlist_head) - result = this->playlist_head->entry; - else { - this->properties()[property::PLAYLIST_FLAG_FINISHED] = true; - result = nullptr; - } - } else if(replay_mode == ReplayMode::LINEAR_LOOPED || !this->properties()[property::PLAYLIST_FLAG_FINISHED]) { - result = songs.front(); - } - } else if(replay_mode == ReplayMode::SHUFFLE) { - //TODO may add a already played list? - if(songs.size() == 1) { - this->properties()[property::PLAYLIST_FLAG_FINISHED] = true; - result = nullptr; - } else { - size_t index; - while(songs[index = (rand() % songs.size())] == (!current_song ? nullptr : current_song->entry)) { } + std::shared_ptr result; + if(replay_mode == ReplayMode::LINEAR || replay_mode == ReplayMode::LINEAR_LOOPED) { + if(current_song) { + if(current_song->next_song) + result = current_song->next_song->entry; + else if(replay_mode == ReplayMode::LINEAR_LOOPED && this->playlist_head) + result = this->playlist_head->entry; + else { + this->properties()[property::PLAYLIST_FLAG_FINISHED] = true; + result = nullptr; + } + } else if(replay_mode == ReplayMode::LINEAR_LOOPED || !this->properties()[property::PLAYLIST_FLAG_FINISHED]) { + result = songs.front(); + } + } else if(replay_mode == ReplayMode::SHUFFLE) { + //TODO may add a already played list? + if(songs.size() == 1) { + this->properties()[property::PLAYLIST_FLAG_FINISHED] = true; + result = nullptr; + } else { + size_t index; + while(songs[index = (rand() % songs.size())] == (!current_song ? nullptr : current_song->entry)) { } - result = songs[index]; - } - } - playlist_lock.unlock(); + result = songs[index]; + } + } + playlist_lock.unlock(); - if(current_song && this->properties()[property::PLAYLIST_FLAG_DELETE_PLAYED].as()) - this->delete_song(current_song->entry->id); - return result; + if(current_song && this->properties()[property::PLAYLIST_FLAG_DELETE_PLAYED].as()) + this->delete_song(current_song->entry->id); + return result; } bool PlayablePlaylist::set_current_song(SongId song_id) { - { - unique_lock playlist_lock(this->playlist_lock); - auto current_song = this->playlist_find(playlist_lock, song_id); - this->loading_entry = current_song->entry; + { + unique_lock playlist_lock(this->playlist_lock); + auto current_song = this->playlist_find(playlist_lock, song_id); + this->loading_entry = current_song->entry; - if(current_song) { - this->enqueue_load(current_song->entry); - auto id = current_song ? current_song->entry->id : 0; - if(this->properties()[property::PLAYLIST_CURRENT_SONG_ID].as() != id) - this->properties()[property::PLAYLIST_CURRENT_SONG_ID] = id; - } - } - { - auto bot = this->current_bot(); - bot->forwardSong(); /* skip to the song */ - } + if(current_song) { + this->enqueue_load(current_song->entry); + auto id = current_song ? current_song->entry->id : 0; + if(this->properties()[property::PLAYLIST_CURRENT_SONG_ID].as() != id) + this->properties()[property::PLAYLIST_CURRENT_SONG_ID] = id; + } + } + { + auto bot = this->current_bot(); + bot->forwardSong(); /* skip to the song */ + } - return true; + return true; } \ No newline at end of file diff --git a/server/src/music/PlayablePlaylist.h b/server/src/music/PlayablePlaylist.h index 8d78fd7..0ac3d76 100644 --- a/server/src/music/PlayablePlaylist.h +++ b/server/src/music/PlayablePlaylist.h @@ -4,46 +4,46 @@ #include "MusicPlaylist.h" namespace ts { - namespace server { - class ConnectedClient; - class MusicClient; - } + namespace server { + class ConnectedClient; + class MusicClient; + } - namespace music { - class MusicBotManager; + namespace music { + class MusicBotManager; - class PlayablePlaylist : public Playlist { - friend class server::MusicClient; - public: - struct ReplayMode { - enum value { - /* 0 = normal | 1 = loop list | 2 = loop entry | 3 = shuffle */ - LINEAR = 0, - LINEAR_LOOPED, - SINGLE_LOOPED, - SHUFFLE - }; - }; - PlayablePlaylist(const std::shared_ptr& /* manager */, const std::shared_ptr& /* properties */, const std::shared_ptr& /* permissions */); - virtual ~PlayablePlaylist(); + class PlayablePlaylist : public Playlist { + friend class server::MusicClient; + public: + struct ReplayMode { + enum value { + /* 0 = normal | 1 = loop list | 2 = loop entry | 3 = shuffle */ + LINEAR = 0, + LINEAR_LOOPED, + SINGLE_LOOPED, + SHUFFLE + }; + }; + PlayablePlaylist(const std::shared_ptr& /* manager */, const std::shared_ptr& /* properties */, const std::shared_ptr& /* permissions */); + virtual ~PlayablePlaylist(); - inline ReplayMode::value replay_mode() { return this->properties()[property::PLAYLIST_REPLAY_MODE].as(); } - inline void set_replay_mode(ReplayMode::value mode) { this->properties()[property::PLAYLIST_REPLAY_MODE] = mode; } + inline ReplayMode::value replay_mode() { return this->properties()[property::PLAYLIST_REPLAY_MODE].as(); } + inline void set_replay_mode(ReplayMode::value mode) { this->properties()[property::PLAYLIST_REPLAY_MODE] = mode; } - inline SongId currently_playing() { return this->properties()[property::PLAYLIST_CURRENT_SONG_ID]; } - bool set_current_song(SongId /* song */); + inline SongId currently_playing() { return this->properties()[property::PLAYLIST_CURRENT_SONG_ID]; } + bool set_current_song(SongId /* song */); - std::shared_ptr pop_next(); - inline std::shared_ptr current_bot() { return this->_current_bot.lock(); } - protected: - std::mutex currently_playing_lock; - std::weak_ptr loading_entry; - std::weak_ptr _current_bot; + std::shared_ptr pop_next(); + inline std::shared_ptr current_bot() { return this->_current_bot.lock(); } + protected: + std::mutex currently_playing_lock; + std::weak_ptr loading_entry; + std::weak_ptr _current_bot; - void set_self_ref(const std::shared_ptr &ptr) override; + void set_self_ref(const std::shared_ptr &ptr) override; - std::shared_ptr pop_next_entry(); - }; - } + std::shared_ptr pop_next_entry(); + }; + } } DEFINE_TRANSFORMS(ts::music::PlayablePlaylist::ReplayMode::value, uint8_t); \ No newline at end of file diff --git a/server/src/pinteraction/ApplicationInteraction.cpp b/server/src/pinteraction/ApplicationInteraction.cpp index dc9a4df..269a199 100644 --- a/server/src/pinteraction/ApplicationInteraction.cpp +++ b/server/src/pinteraction/ApplicationInteraction.cpp @@ -47,13 +47,13 @@ namespace interaction { info = (DefaultMemoryInfo*) SHARED_MEMORY_PTR; /* initialize the mutex */ - { - pthread_mutexattr_t attr{}; - pthread_mutexattr_init(&attr); - pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_NORMAL); - pthread_mutexattr_setpshared(&attr, true); - pthread_mutex_init(&info->memoryLock, &attr); - } + { + pthread_mutexattr_t attr{}; + pthread_mutexattr_init(&attr); + pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_NORMAL); + pthread_mutexattr_setpshared(&attr, true); + pthread_mutex_init(&info->memoryLock, &attr); + } pthread_mutex_lock(&info->memoryLock); memset(info->infoUsed, 0, sizeof(info->infoUsed) / sizeof(info->infoUsed[0])); @@ -101,7 +101,7 @@ namespace interaction { if(info.shm_nattch == 0){ shmctl(SHARED_MEMORY_ID, IPC_RMID, nullptr); - logMessage(LOG_INSTANCE, "Deleting shared memory"); + logMessage(LOG_INSTANCE, "Deleting shared memory"); } } diff --git a/server/src/server/POWHandler.cpp b/server/src/server/POWHandler.cpp index 8ac6454..1a17ab1 100644 --- a/server/src/server/POWHandler.cpp +++ b/server/src/server/POWHandler.cpp @@ -17,323 +17,323 @@ using namespace ts::server; POWHandler::POWHandler(ts::server::VoiceServer *server) : server(server) { } void POWHandler::execute_tick() { - auto now = system_clock::now(); + auto now = system_clock::now(); - lock_guard lock(this->pending_clients_lock); - this->pending_clients.erase(remove_if(this->pending_clients.begin(), this->pending_clients.end(), [&, now](const shared_ptr& client) { - if(now - client->last_packet > seconds(5)) { - #ifdef POW_ERROR - if(client->state != LowHandshakeState::COMPLETED) { /* handshake succeeded */ - debugMessage(this->get_server_id(), "[POW] Dropping connection from {} (Timeout)", net::to_string(client->address)); - } - #endif - return true; - } - return false; - }), this->pending_clients.end()); + lock_guard lock(this->pending_clients_lock); + this->pending_clients.erase(remove_if(this->pending_clients.begin(), this->pending_clients.end(), [&, now](const shared_ptr& client) { + if(now - client->last_packet > seconds(5)) { + #ifdef POW_ERROR + if(client->state != LowHandshakeState::COMPLETED) { /* handshake succeeded */ + debugMessage(this->get_server_id(), "[POW] Dropping connection from {} (Timeout)", net::to_string(client->address)); + } + #endif + return true; + } + return false; + }), this->pending_clients.end()); } void POWHandler::delete_client(const std::shared_ptr &client) { - lock_guard lock(this->pending_clients_lock); - auto it = find(this->pending_clients.begin(), this->pending_clients.end(), client); - if(it != this->pending_clients.end()) - this->pending_clients.erase(it); + lock_guard lock(this->pending_clients_lock); + auto it = find(this->pending_clients.begin(), this->pending_clients.end(), client); + if(it != this->pending_clients.end()) + this->pending_clients.erase(it); } void POWHandler::handle_datagram(int socket, const sockaddr_storage &address,msghdr &info, const pipes::buffer_view &buffer) { - if(buffer.length() < MAC_SIZE + CLIENT_HEADER_SIZE + 5) - return; /* too short packet! */ + if(buffer.length() < MAC_SIZE + CLIENT_HEADER_SIZE + 5) + return; /* too short packet! */ - std::shared_ptr client; - { - lock_guard lock(this->pending_clients_lock); - for(const auto& c : this->pending_clients) - if(c->socket == socket && memcmp(&address, &c->address, sizeof(sockaddr_storage)) == 0) { - client = c; - break; - } + std::shared_ptr client; + { + lock_guard lock(this->pending_clients_lock); + for(const auto& c : this->pending_clients) + if(c->socket == socket && memcmp(&address, &c->address, sizeof(sockaddr_storage)) == 0) { + client = c; + break; + } - if(!client) { - #ifdef POW_DEBUG - debugMessage(this->get_server_id(), "[POW] Got a new connection from {}", net::to_string(address)); - #endif - //We've got a new client - client = make_shared(); - client->state = LowHandshakeState::UNSET; - this->pending_clients.push_back(client); - } - } + if(!client) { + #ifdef POW_DEBUG + debugMessage(this->get_server_id(), "[POW] Got a new connection from {}", net::to_string(address)); + #endif + //We've got a new client + client = make_shared(); + client->state = LowHandshakeState::UNSET; + this->pending_clients.push_back(client); + } + } - unique_lock lock(client->handle_lock, defer_lock_t{}); - if(!lock.try_lock_for(nanoseconds(15 * 1000))) { - //Failed to acquire handle lock - return; - } + unique_lock lock(client->handle_lock, defer_lock_t{}); + if(!lock.try_lock_for(nanoseconds(15 * 1000))) { + //Failed to acquire handle lock + return; + } - if(client->state == LowHandshakeState::UNSET) { /* initialize client */ - client->socket = socket; - client->client_version = be2le32(&buffer[MAC_SIZE + CLIENT_HEADER_SIZE]); - memcpy(&client->address, &address, sizeof(client->address)); - io::DatagramPacket::extract_info(info, client->address_info); + if(client->state == LowHandshakeState::UNSET) { /* initialize client */ + client->socket = socket; + client->client_version = be2le32(&buffer[MAC_SIZE + CLIENT_HEADER_SIZE]); + memcpy(&client->address, &address, sizeof(client->address)); + io::DatagramPacket::extract_info(info, client->address_info); - client->state = LowHandshakeState::COOKIE_GET; - } - client->last_packet = system_clock::now(); + client->state = LowHandshakeState::COOKIE_GET; + } + client->last_packet = system_clock::now(); - /* buffer is in client packet format, but we dont need to parse because we dont need the header */ - auto data = buffer.view(MAC_SIZE + CLIENT_HEADER_SIZE); - //this->crypto.client_time = be2le32((char*) packet->data().data_ptr(), 0); /* client timestamp */ - auto packet_state = static_cast(data[4]); - #ifdef POW_DEBUG - debugMessage(this->get_server_id(), "[POW][{}] Received packet with state {}. length: {}", net::to_string(address), packet_state, data.length()); - #endif + /* buffer is in client packet format, but we dont need to parse because we dont need the header */ + auto data = buffer.view(MAC_SIZE + CLIENT_HEADER_SIZE); + //this->crypto.client_time = be2le32((char*) packet->data().data_ptr(), 0); /* client timestamp */ + auto packet_state = static_cast(data[4]); + #ifdef POW_DEBUG + debugMessage(this->get_server_id(), "[POW][{}] Received packet with state {}. length: {}", net::to_string(address), packet_state, data.length()); + #endif - if(packet_state < client->state) { - #ifdef POW_ERROR - debugMessage(this->get_server_id(), "[POW][{}] Received packet with lower state then the current state. Jumping back. Current state: {}, New state: {}", net::to_string(address), client->state, packet_state); - #endif - client->state = packet_state; - } else if(packet_state != client->state) { - if(packet_state == LowHandshakeState::PUZZLE_GET && client->state == LowHandshakeState::COOKIE_GET) { - if(!config::voice::enforce_coocie_handshake) { - #ifdef POW_DEBUG - debugMessage(this->get_server_id(), "[POW][{}] Client wants a puzzle, but we expected a cookie. Ignoring it and jump ahead to the puzzle.", net::to_string(address)); - #endif - client->state = LowHandshakeState::PUZZLE_GET; - client->server_control_data[0] = 0; /* disable cookie check */ - goto handle_packet; - } - } - #ifdef POW_ERROR - debugMessage(this->get_server_id(), "[POW][{}] Received packet an unexpected state. Expected: {}, Received: {}. Resetting client", net::to_string(address), client->state, packet_state); - #endif - this->reset_client(client); - return; - } + if(packet_state < client->state) { + #ifdef POW_ERROR + debugMessage(this->get_server_id(), "[POW][{}] Received packet with lower state then the current state. Jumping back. Current state: {}, New state: {}", net::to_string(address), client->state, packet_state); + #endif + client->state = packet_state; + } else if(packet_state != client->state) { + if(packet_state == LowHandshakeState::PUZZLE_GET && client->state == LowHandshakeState::COOKIE_GET) { + if(!config::voice::enforce_coocie_handshake) { + #ifdef POW_DEBUG + debugMessage(this->get_server_id(), "[POW][{}] Client wants a puzzle, but we expected a cookie. Ignoring it and jump ahead to the puzzle.", net::to_string(address)); + #endif + client->state = LowHandshakeState::PUZZLE_GET; + client->server_control_data[0] = 0; /* disable cookie check */ + goto handle_packet; + } + } + #ifdef POW_ERROR + debugMessage(this->get_server_id(), "[POW][{}] Received packet an unexpected state. Expected: {}, Received: {}. Resetting client", net::to_string(address), client->state, packet_state); + #endif + this->reset_client(client); + return; + } - handle_packet: - if(packet_state == LowHandshakeState::COOKIE_GET) - this->handle_cookie_get(client, data); - else if(packet_state == LowHandshakeState::PUZZLE_GET) - this->handle_puzzle_get(client, data); - else if(packet_state == LowHandshakeState::PUZZLE_SOLVE) - this->handle_puzzle_solve(client, data); - else { - #ifdef POW_ERROR - debugMessage(this->get_server_id(), "[POW][{}] Got an invalid packet state of type {}. Resetting client.", net::to_string(address), packet_state); - #endif - this->reset_client(client); - return; - } + handle_packet: + if(packet_state == LowHandshakeState::COOKIE_GET) + this->handle_cookie_get(client, data); + else if(packet_state == LowHandshakeState::PUZZLE_GET) + this->handle_puzzle_get(client, data); + else if(packet_state == LowHandshakeState::PUZZLE_SOLVE) + this->handle_puzzle_solve(client, data); + else { + #ifdef POW_ERROR + debugMessage(this->get_server_id(), "[POW][{}] Got an invalid packet state of type {}. Resetting client.", net::to_string(address), packet_state); + #endif + this->reset_client(client); + return; + } } void POWHandler::send_data(const std::shared_ptr &client, const pipes::buffer_view &buffer) { - auto datagram = io::DatagramPacket::create(client->address, client->address_info, buffer.length() + MAC_SIZE + SERVER_HEADER_SIZE, nullptr); - if(!datagram) return; //Should never happen + auto datagram = io::DatagramPacket::create(client->address, client->address_info, buffer.length() + MAC_SIZE + SERVER_HEADER_SIZE, nullptr); + if(!datagram) return; //Should never happen - /* first 8 bytes mac */ - memcpy(&datagram->data[0], "TS3INIT1", 8); + /* first 8 bytes mac */ + memcpy(&datagram->data[0], "TS3INIT1", 8); - /* 2 bytes packet id (const 101) */ - le2be16(101, &datagram->data[8]); + /* 2 bytes packet id (const 101) */ + le2be16(101, &datagram->data[8]); - /* 1 byte flags and type */ - datagram->data[10] = (uint8_t) (0x08 | 0x80); + /* 1 byte flags and type */ + datagram->data[10] = (uint8_t) (0x08 | 0x80); - memcpy(&datagram->data[11], buffer.data_ptr(), buffer.length()); - this->server->send_datagram(client->socket, datagram); + memcpy(&datagram->data[11], buffer.data_ptr(), buffer.length()); + this->server->send_datagram(client->socket, datagram); } void POWHandler::reset_client(const std::shared_ptr &client) { - uint8_t buffer[2] = {COMMAND_RESET, 0}; - this->send_data(client, pipes::buffer_view{buffer, 2}); - client->state = LowHandshakeState::COOKIE_GET; + uint8_t buffer[2] = {COMMAND_RESET, 0}; + this->send_data(client, pipes::buffer_view{buffer, 2}); + client->state = LowHandshakeState::COOKIE_GET; } inline void generate_random(uint8_t *destination, size_t length) { - while(length-- > 0) - *(destination++) = (uint8_t) rand(); + while(length-- > 0) + *(destination++) = (uint8_t) rand(); } inline void write_reversed(uint8_t* destination, uint8_t* source, size_t length) { - destination += length; - while(length-- > 0) - *(--destination) = *(source++); + destination += length; + while(length-- > 0) + *(--destination) = *(source++); } void POWHandler::handle_cookie_get(const std::shared_ptr &client, const pipes::buffer_view &buffer) { - if(buffer.length() != 21) { - #ifdef POW_ERROR - debugMessage(this->get_server_id(), "[POW][{}][Cookie] Received an invalid packet with an invalid length. Expected {} bytes, but got {} bytes", net::to_string(client->address), 21, buffer.length()); - #endif - return; - } + if(buffer.length() != 21) { + #ifdef POW_ERROR + debugMessage(this->get_server_id(), "[POW][{}][Cookie] Received an invalid packet with an invalid length. Expected {} bytes, but got {} bytes", net::to_string(client->address), 21, buffer.length()); + #endif + return; + } - /* initialize data */ - if(client->server_control_data[0] == 0) { - generate_random(client->server_control_data, 16); - client->server_control_data[0] |= 1; - } + /* initialize data */ + if(client->server_control_data[0] == 0) { + generate_random(client->server_control_data, 16); + client->server_control_data[0] |= 1; + } - /* parse values */ - memcpy(client->client_control_data, &buffer[9], 4); + /* parse values */ + memcpy(client->client_control_data, &buffer[9], 4); - /* send response */ - { - uint8_t response_buffer[21]; - response_buffer[0] = LowHandshakeState::COOKIE_SET; - memcpy(&response_buffer[1], client->server_control_data, 16); - write_reversed(&response_buffer[17], client->client_control_data, 4); + /* send response */ + { + uint8_t response_buffer[21]; + response_buffer[0] = LowHandshakeState::COOKIE_SET; + memcpy(&response_buffer[1], client->server_control_data, 16); + write_reversed(&response_buffer[17], client->client_control_data, 4); - this->send_data(client, pipes::buffer_view{response_buffer, 21}); - } + this->send_data(client, pipes::buffer_view{response_buffer, 21}); + } - client->state = LowHandshakeState::PUZZLE_GET; + client->state = LowHandshakeState::PUZZLE_GET; } void POWHandler::handle_puzzle_get(const std::shared_ptr &client, const pipes::buffer_view &buffer) { - if(buffer.length() != 25) { - #ifdef POW_ERROR - debugMessage(this->get_server_id(), "[POW][{}][Puzzle] Received an invalid puzzle request with an invalid length. Expected {} bytes, but got {} bytes", net::to_string(client->address), 25, buffer.length()); - #endif - return; - } + if(buffer.length() != 25) { + #ifdef POW_ERROR + debugMessage(this->get_server_id(), "[POW][{}][Puzzle] Received an invalid puzzle request with an invalid length. Expected {} bytes, but got {} bytes", net::to_string(client->address), 25, buffer.length()); + #endif + return; + } - /* verify the server cookie */ - if(client->server_control_data[0] != 0) { - if(memcmp(client->server_control_data, &buffer[5], 16) != 0) { - #ifdef POW_ERROR - debugMessage(this->get_server_id(), "[POW][{}][Puzzle] Received an invalid puzzle request. Returned server cookie dosnt match! Resetting client", net::to_string(client->address)); - #endif - this->reset_client(client); - return; - } - } + /* verify the server cookie */ + if(client->server_control_data[0] != 0) { + if(memcmp(client->server_control_data, &buffer[5], 16) != 0) { + #ifdef POW_ERROR + debugMessage(this->get_server_id(), "[POW][{}][Puzzle] Received an invalid puzzle request. Returned server cookie dosnt match! Resetting client", net::to_string(client->address)); + #endif + this->reset_client(client); + return; + } + } - if(!client->rsa_challenge) - client->rsa_challenge = serverInstance->getVoiceServerManager()->rsaPuzzles()->nextPuzzle(); + if(!client->rsa_challenge) + client->rsa_challenge = serverInstance->getVoiceServerManager()->rsaPuzzles()->nextPuzzle(); - /* send response */ - { - size_t response_length = 1 + 64 * 2 + 4 + 100; - uint8_t response_buffer[response_length]; + /* send response */ + { + size_t response_length = 1 + 64 * 2 + 4 + 100; + uint8_t response_buffer[response_length]; - /* first byte step */ - response_buffer[0] = LowHandshakeState::PUZZLE_SET; + /* first byte step */ + response_buffer[0] = LowHandshakeState::PUZZLE_SET; - /* 64 bytes x */ - memcpy(&response_buffer[1], client->rsa_challenge->data_x, 64); + /* 64 bytes x */ + memcpy(&response_buffer[1], client->rsa_challenge->data_x, 64); - /* 64 bytes n */ - memcpy(&response_buffer[1 + 64], client->rsa_challenge->data_n, 64); + /* 64 bytes n */ + memcpy(&response_buffer[1 + 64], client->rsa_challenge->data_n, 64); - /* 2 bytes exponent (level) */ - le2be32(client->rsa_challenge->level, &response_buffer[1 + 64 + 64]); + /* 2 bytes exponent (level) */ + le2be32(client->rsa_challenge->level, &response_buffer[1 + 64 + 64]); - /* custom server data (empty) */ - memset(&response_buffer[1 + 64 * 2 + 4], 0, 100); + /* custom server data (empty) */ + memset(&response_buffer[1 + 64 * 2 + 4], 0, 100); - this->send_data(client, pipes::buffer_view{response_buffer, response_length}); - } + this->send_data(client, pipes::buffer_view{response_buffer, response_length}); + } - client->state = LowHandshakeState::PUZZLE_SOLVE; + client->state = LowHandshakeState::PUZZLE_SOLVE; } void POWHandler::handle_puzzle_solve(const std::shared_ptr &client, const pipes::buffer_view &buffer) { - if(buffer.length() < 301) { - #ifdef POW_ERROR - debugMessage(this->get_server_id(), "[POW][{}][Puzzle] Received an invalid puzzle solution with an invalid length. Expected at least {} bytes, but got {} bytes", net::to_string(client->address), 301, buffer.length()); - #endif - return; - } + if(buffer.length() < 301) { + #ifdef POW_ERROR + debugMessage(this->get_server_id(), "[POW][{}][Puzzle] Received an invalid puzzle solution with an invalid length. Expected at least {} bytes, but got {} bytes", net::to_string(client->address), 301, buffer.length()); + #endif + return; + } - if(!client->rsa_challenge) { - #ifdef POW_ERROR - debugMessage(this->get_server_id(), "[POW][{}][Puzzle] Received a puzzle solution for a puzzle which hasnt yet been decided! Resetting client", net::to_string(client->address)); - #endif - this->reset_client(client); - return; - } + if(!client->rsa_challenge) { + #ifdef POW_ERROR + debugMessage(this->get_server_id(), "[POW][{}][Puzzle] Received a puzzle solution for a puzzle which hasnt yet been decided! Resetting client", net::to_string(client->address)); + #endif + this->reset_client(client); + return; + } - /* validate data */ - { - /* should we validate x,n and level as well? */ + /* validate data */ + { + /* should we validate x,n and level as well? */ - if(memcmp(client->rsa_challenge->data_result, &buffer[4 + 1 + 2 * 64 + 04 + 100], 64) != 0) { - #ifdef POW_ERROR - debugMessage(this->get_server_id(), "[POW][{}][Puzzle] Received an invalid puzzle solution! Resetting client", net::to_string(client->address)); - #endif - this->reset_client(client); - return; - } - } + if(memcmp(client->rsa_challenge->data_result, &buffer[4 + 1 + 2 * 64 + 04 + 100], 64) != 0) { + #ifdef POW_ERROR + debugMessage(this->get_server_id(), "[POW][{}][Puzzle] Received an invalid puzzle solution! Resetting client", net::to_string(client->address)); + #endif + this->reset_client(client); + return; + } + } - auto command = buffer.view(301); - #ifdef POW_DEBUG - debugMessage(this->get_server_id(), "[POW][{}][Puzzle] Puzzle solved, received command {}", net::to_string(client->address), command.string()); - #endif + auto command = buffer.view(301); + #ifdef POW_DEBUG + debugMessage(this->get_server_id(), "[POW][{}][Puzzle] Puzzle solved, received command {}", net::to_string(client->address), command.string()); + #endif - auto voice_client = this->register_verified_client(client); - if(voice_client) { - auto& read_queue = voice_client->connection->packet_buffers()[protocol::COMMAND]; + auto voice_client = this->register_verified_client(client); + if(voice_client) { + auto& read_queue = voice_client->connection->packet_buffers()[protocol::COMMAND]; - auto packet = make_unique(protocol::PacketTypeInfo::Command, command); - packet->memory_state.id_branded = false; - packet->applyPacketId(0, 0); /* first packet */ + auto packet = make_unique(protocol::PacketTypeInfo::Command, command); + packet->memory_state.id_branded = false; + packet->applyPacketId(0, 0); /* first packet */ - { - unique_lock buffer_lock(read_queue.buffer_lock); - if(read_queue.current_index() == 0) { - if(!read_queue.insert_index(0, move(packet))) { - #ifdef POW_ERROR - debugMessage(this->get_server_id(), "[POW][{}][Puzzle] Failed to insert command packet into buffer!", net::to_string(client->address)); - #endif - return; - } - } else { - read_queue.push_front(move(packet)); - } - } + { + unique_lock buffer_lock(read_queue.buffer_lock); + if(read_queue.current_index() == 0) { + if(!read_queue.insert_index(0, move(packet))) { + #ifdef POW_ERROR + debugMessage(this->get_server_id(), "[POW][{}][Puzzle] Failed to insert command packet into buffer!", net::to_string(client->address)); + #endif + return; + } + } else { + read_queue.push_front(move(packet)); + } + } - this->server->schedule_execute(&*voice_client); - client->state = LowHandshakeState::COMPLETED; - } else { - #ifdef POW_ERROR - debugMessage(this->get_server_id(), "[POW][{}][Puzzle] Failed to initialize client. Doing nothing, until a new packet.", net::to_string(client->address)); - #endif - } + this->server->schedule_execute(&*voice_client); + client->state = LowHandshakeState::COMPLETED; + } else { + #ifdef POW_ERROR + debugMessage(this->get_server_id(), "[POW][{}][Puzzle] Failed to initialize client. Doing nothing, until a new packet.", net::to_string(client->address)); + #endif + } } shared_ptr POWHandler::register_verified_client(const std::shared_ptr &client) { - shared_ptr voice_client; - { - lock_guard lock(this->server->connectionLock); - for(const auto& connection : this->server->activeConnections) { - if(memcmp(&connection->remote_address, &client->address, sizeof(client->address)) == 0){ - voice_client = connection; - break; - } - } - } + shared_ptr voice_client; + { + lock_guard lock(this->server->connectionLock); + for(const auto& connection : this->server->activeConnections) { + if(memcmp(&connection->remote_address, &client->address, sizeof(client->address)) == 0){ + voice_client = connection; + break; + } + } + } - if(!voice_client) { - voice_client = std::make_shared(this->server->get_server()->getVoiceServer(), &client->address); - voice_client->_this = voice_client; - voice_client->initialize(); + if(!voice_client) { + voice_client = std::make_shared(this->server->get_server()->getVoiceServer(), &client->address); + voice_client->_this = voice_client; + voice_client->initialize(); - voice_client->socket = client->socket; - voice_client->state = ConnectionState::INIT_HIGH; - memcpy(&voice_client->address_info, &client->address_info, sizeof(client->address_info)); + voice_client->socket = client->socket; + voice_client->state = ConnectionState::INIT_HIGH; + memcpy(&voice_client->address_info, &client->address_info, sizeof(client->address_info)); - { - lock_guard lock(this->server->connectionLock); - server->activeConnections.push_back(voice_client); - } + { + lock_guard lock(this->server->connectionLock); + server->activeConnections.push_back(voice_client); + } - debugMessage(this->get_server_id(), "Having new voice client. Remote address: {}", voice_client->getLoggingPeerIp() +":" + to_string(voice_client->getPeerPort())); - } - voice_client->crypto.client_time = client->client_version; - voice_client->last_packet_handshake = system_clock::now(); + debugMessage(this->get_server_id(), "Having new voice client. Remote address: {}", voice_client->getLoggingPeerIp() +":" + to_string(voice_client->getPeerPort())); + } + voice_client->crypto.client_time = client->client_version; + voice_client->last_packet_handshake = system_clock::now(); - return voice_client; + return voice_client; } \ No newline at end of file diff --git a/server/src/server/POWHandler.h b/server/src/server/POWHandler.h index 5f6ac97..bac5570 100644 --- a/server/src/server/POWHandler.h +++ b/server/src/server/POWHandler.h @@ -9,61 +9,61 @@ #include "src/TSServer.h" namespace ts { - namespace server { - class POWHandler { - public: - enum LowHandshakeState : uint8_t { - COOKIE_GET, - COOKIE_SET, - PUZZLE_GET, - PUZZLE_SET, - PUZZLE_SOLVE, - PUZZLE_RESET, - COMPLETED, - COMMAND_RESET = 127, - UNSET = 0xFB - }; + namespace server { + class POWHandler { + public: + enum LowHandshakeState : uint8_t { + COOKIE_GET, + COOKIE_SET, + PUZZLE_GET, + PUZZLE_SET, + PUZZLE_SOLVE, + PUZZLE_RESET, + COMPLETED, + COMMAND_RESET = 127, + UNSET = 0xFB + }; - struct Client { - int socket; - sockaddr_storage address; - io::pktinfo_storage address_info; + struct Client { + int socket; + sockaddr_storage address; + io::pktinfo_storage address_info; - std::timed_mutex handle_lock; - std::chrono::system_clock::time_point last_packet; - LowHandshakeState state = LowHandshakeState::COOKIE_GET; + std::timed_mutex handle_lock; + std::chrono::system_clock::time_point last_packet; + LowHandshakeState state = LowHandshakeState::COOKIE_GET; - uint8_t client_control_data[4] = {0,0,0,0}; - uint8_t server_control_data[16] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}; - uint8_t server_data[100]; + uint8_t client_control_data[4] = {0,0,0,0}; + uint8_t server_control_data[16] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}; + uint8_t server_data[100]; - uint32_t client_version; + uint32_t client_version; - std::shared_ptr rsa_challenge; - }; + std::shared_ptr rsa_challenge; + }; - explicit POWHandler(VoiceServer* /* server */); + explicit POWHandler(VoiceServer* /* server */); - void handle_datagram(int /* socket */, const sockaddr_storage& /* address */, msghdr& /* info */, const pipes::buffer_view& /* buffer */); - void execute_tick(); - private: - inline ServerId get_server_id() { - return this->server->get_server()->getServerId(); - } - VoiceServer* server; + void handle_datagram(int /* socket */, const sockaddr_storage& /* address */, msghdr& /* info */, const pipes::buffer_view& /* buffer */); + void execute_tick(); + private: + inline ServerId get_server_id() { + return this->server->get_server()->getServerId(); + } + VoiceServer* server; - std::mutex pending_clients_lock; - std::deque> pending_clients; + std::mutex pending_clients_lock; + std::deque> pending_clients; - void delete_client(const std::shared_ptr& /* client */); + void delete_client(const std::shared_ptr& /* client */); - void handle_cookie_get(const std::shared_ptr& /* client */, const pipes::buffer_view& /* buffer */); - void handle_puzzle_get(const std::shared_ptr& /* client */, const pipes::buffer_view& /* buffer */); - void handle_puzzle_solve(const std::shared_ptr& /* client */, const pipes::buffer_view& /* buffer */); - std::shared_ptr register_verified_client(const std::shared_ptr& /* client */); + void handle_cookie_get(const std::shared_ptr& /* client */, const pipes::buffer_view& /* buffer */); + void handle_puzzle_get(const std::shared_ptr& /* client */, const pipes::buffer_view& /* buffer */); + void handle_puzzle_solve(const std::shared_ptr& /* client */, const pipes::buffer_view& /* buffer */); + std::shared_ptr register_verified_client(const std::shared_ptr& /* client */); - void send_data(const std::shared_ptr &client /* client */, const pipes::buffer_view &buffer /* buffer */); - void reset_client(const std::shared_ptr &client /* client */); - }; - } + void send_data(const std::shared_ptr &client /* client */, const pipes::buffer_view &buffer /* buffer */); + void reset_client(const std::shared_ptr &client /* client */); + }; + } } \ No newline at end of file diff --git a/server/src/server/QueryServer.cpp b/server/src/server/QueryServer.cpp index 4c07513..2b35d38 100644 --- a/server/src/server/QueryServer.cpp +++ b/server/src/server/QueryServer.cpp @@ -24,130 +24,130 @@ using namespace ts::server; #endif QueryServer::QueryServer(sql::SqlManager* db) : sql(db) { - this->_executePool = new threads::ThreadPool(4, "EXEC Query"); + this->_executePool = new threads::ThreadPool(4, "EXEC Query"); } QueryServer::~QueryServer() { stop(); - this->_executePool->shutdown(); - delete this->_executePool; + this->_executePool->shutdown(); + delete this->_executePool; } void QueryServer::unregisterConnection(const shared_ptr &client) { - { - lock_guard lock(this->connected_clients_lock); - auto found = std::find(this->connectedClients.begin(), this->connectedClients.end(), client); - if(found != this->connectedClients.end()) - this->connectedClients.erase(found); - else - logError(LOG_QUERY, "Attempted to unregister an invalid connection!"); - } + { + lock_guard lock(this->connected_clients_lock); + auto found = std::find(this->connectedClients.begin(), this->connectedClients.end(), client); + if(found != this->connectedClients.end()) + this->connectedClients.erase(found); + else + logError(LOG_QUERY, "Attempted to unregister an invalid connection!"); + } if(client->server) { - client->server->getGroupManager()->disableCache(client->getClientDatabaseId()); + client->server->getGroupManager()->disableCache(client->getClientDatabaseId()); } else { - serverInstance->getGroupManager()->disableCache(client->getClientDatabaseId()); + serverInstance->getGroupManager()->disableCache(client->getClientDatabaseId()); } /* client->handle = nullptr; */ } bool QueryServer::start(const deque> &bindings, std::string &error) { - if(this->active) { - error = "already started"; - return false; - } - this->active = true; + if(this->active) { + error = "already started"; + return false; + } + this->active = true; - /* load ip black/whitelist */ - { - ip_blacklist.reset(new IpListManager("query_ip_blacklist.txt", {"#A new line separated address blacklist", "#", "#For example if we dont want google:", "8.8.8.8"})); - ip_whitelist.reset(new IpListManager("query_ip_whitelist.txt", {"#A new line separated address whitelist", "#Every ip have no flood and login attempt limit!", "127.0.0.1/8", "::1"})); - string error; - if(!this->ip_blacklist->reload(error)) logError(LOG_QUERY, "Failed to load query blacklist: {}", error); - if(!this->ip_whitelist->reload(error)) logError(LOG_QUERY, "Failed to load query whitelist: {}", error); - } + /* load ip black/whitelist */ + { + ip_blacklist.reset(new IpListManager("query_ip_blacklist.txt", {"#A new line separated address blacklist", "#", "#For example if we dont want google:", "8.8.8.8"})); + ip_whitelist.reset(new IpListManager("query_ip_whitelist.txt", {"#A new line separated address whitelist", "#Every ip have no flood and login attempt limit!", "127.0.0.1/8", "::1"})); + string error; + if(!this->ip_blacklist->reload(error)) logError(LOG_QUERY, "Failed to load query blacklist: {}", error); + if(!this->ip_whitelist->reload(error)) logError(LOG_QUERY, "Failed to load query whitelist: {}", error); + } - /* reserve backup file descriptor in case that the max file descriptors have been reached */ - { - this->server_reserve_fd = dup(1); - if(this->server_reserve_fd < 0) - logWarning(LOG_QUERY, "Failed to reserve a backup accept file descriptor. ({} | {})", errno, strerror(errno)); - } + /* reserve backup file descriptor in case that the max file descriptors have been reached */ + { + this->server_reserve_fd = dup(1); + if(this->server_reserve_fd < 0) + logWarning(LOG_QUERY, "Failed to reserve a backup accept file descriptor. ({} | {})", errno, strerror(errno)); + } - /* setup event bases */ - { - this->eventLoop = event_base_new(); - this->ioThread = new threads::Thread(THREAD_SAVE_OPERATIONS | THREAD_EXECUTE_LATER, [&]{ - while(this->active) { - debugMessage(LOG_QUERY, "Entering event loop ({})", (void*) this->eventLoop); - event_base_loop(this->eventLoop, EVLOOP_NO_EXIT_ON_EMPTY); - if(this->active) { - debugMessage(LOG_QUERY, "Event loop exited ({}). No active events. Sleeping 1 seconds", (void*) this->eventLoop); - this_thread::sleep_for(seconds(1)); - } else { - debugMessage(LOG_QUERY, "Event loop exited ({})", (void*) this->eventLoop); - } - } - }); - this->ioThread->name("EVENT Query").execute(); - } + /* setup event bases */ + { + this->eventLoop = event_base_new(); + this->ioThread = new threads::Thread(THREAD_SAVE_OPERATIONS | THREAD_EXECUTE_LATER, [&]{ + while(this->active) { + debugMessage(LOG_QUERY, "Entering event loop ({})", (void*) this->eventLoop); + event_base_loop(this->eventLoop, EVLOOP_NO_EXIT_ON_EMPTY); + if(this->active) { + debugMessage(LOG_QUERY, "Event loop exited ({}). No active events. Sleeping 1 seconds", (void*) this->eventLoop); + this_thread::sleep_for(seconds(1)); + } else { + debugMessage(LOG_QUERY, "Event loop exited ({})", (void*) this->eventLoop); + } + } + }); + this->ioThread->name("EVENT Query").execute(); + } - for(auto& binding : bindings) { - binding->file_descriptor = socket(binding->address.ss_family, SOCK_STREAM | SOCK_NONBLOCK, 0); - if(binding->file_descriptor < 0) { - logError(LOG_QUERY, "Failed to bind server to {}. (Failed to create socket: {} | {})", binding->as_string(), errno, strerror(errno)); - continue; - } + for(auto& binding : bindings) { + binding->file_descriptor = socket(binding->address.ss_family, SOCK_STREAM | SOCK_NONBLOCK, 0); + if(binding->file_descriptor < 0) { + logError(LOG_QUERY, "Failed to bind server to {}. (Failed to create socket: {} | {})", binding->as_string(), errno, strerror(errno)); + continue; + } - int enable = 1, disabled = 0; + int enable = 1, disabled = 0; - if (setsockopt(binding->file_descriptor, SOL_SOCKET, SO_REUSEADDR, &enable, sizeof(int)) < 0) - logWarning(LOG_QUERY, "Failed to activate SO_REUSEADDR for binding {} ({} | {})", binding->as_string(), errno, strerror(errno)); - if(setsockopt(binding->file_descriptor, IPPROTO_TCP, TCP_NOPUSH, &disabled, sizeof disabled) < 0) - logWarning(LOG_QUERY, "Failed to deactivate TCP_NOPUSH for binding {} ({} | {})", binding->as_string(), errno, strerror(errno)); - if(binding->address.ss_family == AF_INET6) { - if(setsockopt(binding->file_descriptor, IPPROTO_IPV6, IPV6_V6ONLY, &enable, sizeof(int)) < 0) - logWarning(LOG_QUERY, "Failed to activate IPV6_V6ONLY for IPv6 binding {} ({} | {})", binding->as_string(), errno, strerror(errno)); - } - if(fcntl(binding->file_descriptor, F_SETFD, FD_CLOEXEC) < 0) - logWarning(LOG_QUERY, "Failed to set flag FD_CLOEXEC for binding {} ({} | {})", binding->as_string(), errno, strerror(errno)); + if (setsockopt(binding->file_descriptor, SOL_SOCKET, SO_REUSEADDR, &enable, sizeof(int)) < 0) + logWarning(LOG_QUERY, "Failed to activate SO_REUSEADDR for binding {} ({} | {})", binding->as_string(), errno, strerror(errno)); + if(setsockopt(binding->file_descriptor, IPPROTO_TCP, TCP_NOPUSH, &disabled, sizeof disabled) < 0) + logWarning(LOG_QUERY, "Failed to deactivate TCP_NOPUSH for binding {} ({} | {})", binding->as_string(), errno, strerror(errno)); + if(binding->address.ss_family == AF_INET6) { + if(setsockopt(binding->file_descriptor, IPPROTO_IPV6, IPV6_V6ONLY, &enable, sizeof(int)) < 0) + logWarning(LOG_QUERY, "Failed to activate IPV6_V6ONLY for IPv6 binding {} ({} | {})", binding->as_string(), errno, strerror(errno)); + } + if(fcntl(binding->file_descriptor, F_SETFD, FD_CLOEXEC) < 0) + logWarning(LOG_QUERY, "Failed to set flag FD_CLOEXEC for binding {} ({} | {})", binding->as_string(), errno, strerror(errno)); - if (bind(binding->file_descriptor, (struct sockaddr *) &binding->address, sizeof(binding->address)) < 0) { - logError(LOG_QUERY, "Failed to bind server to {}. (Failed to bind socket: {} | {})", binding->as_string(), errno, strerror(errno)); - close(binding->file_descriptor); - continue; - } + if (bind(binding->file_descriptor, (struct sockaddr *) &binding->address, sizeof(binding->address)) < 0) { + logError(LOG_QUERY, "Failed to bind server to {}. (Failed to bind socket: {} | {})", binding->as_string(), errno, strerror(errno)); + close(binding->file_descriptor); + continue; + } - if (listen(binding->file_descriptor, SOMAXCONN) < 0) { - logError(LOG_QUERY, "Failed to bind server to {}. (Failed to listen: {} | {})", binding->as_string(), errno, strerror(errno)); - close(binding->file_descriptor); - continue; - } + if (listen(binding->file_descriptor, SOMAXCONN) < 0) { + logError(LOG_QUERY, "Failed to bind server to {}. (Failed to listen: {} | {})", binding->as_string(), errno, strerror(errno)); + close(binding->file_descriptor); + continue; + } - binding->event_accept = event_new(this->eventLoop, binding->file_descriptor, EV_READ | EV_PERSIST, [](int a, short b, void* c){ ((QueryServer *) c)->on_client_receive(a, b, c); }, this); - event_add(binding->event_accept, nullptr); - this->bindings.push_back(binding); - } + binding->event_accept = event_new(this->eventLoop, binding->file_descriptor, EV_READ | EV_PERSIST, [](int a, short b, void* c){ ((QueryServer *) c)->on_client_receive(a, b, c); }, this); + event_add(binding->event_accept, nullptr); + this->bindings.push_back(binding); + } - if(this->bindings.empty()) { - this->stop(); - error = "failed to bind to any address"; - return false; - } + if(this->bindings.empty()) { + this->stop(); + error = "failed to bind to any address"; + return false; + } - this->tickingId = serverInstance->scheduler()->schedule("query", bind(&QueryServer::tick, this), seconds(1)); - return true; + this->tickingId = serverInstance->scheduler()->schedule("query", bind(&QueryServer::tick, this), seconds(1)); + return true; } void QueryServer::stop() { if(!this->running()) - return; + return; active = false; - serverInstance->scheduler()->cancelTask("query"); + serverInstance->scheduler()->cancelTask("query"); - this->connected_clients_lock.lock(); - auto connected_clients = this->connectedClients; + this->connected_clients_lock.lock(); + auto connected_clients = this->connectedClients; this->connected_clients_lock.unlock(); Command cmd("serverstop"); @@ -175,117 +175,117 @@ void QueryServer::stop() { } this->threads.clear(); - for(auto& binding : this->bindings) { - if(binding->event_accept) { - event_del_block(binding->event_accept); - event_free(binding->event_accept); - binding->event_accept = nullptr; - } - if(binding->file_descriptor > 0) { - if(shutdown(binding->file_descriptor, SHUT_RDWR) < 0) - logWarning(LOG_QUERY, "Failed to shutdown socket for binding {} ({} | {}).", binding->as_string(), errno, strerror(errno)); - if(close(binding->file_descriptor) < 0) - logError(LOG_QUERY, "Failed to close socket for binding {} ({} | {}).", binding->as_string(), errno, strerror(errno)); - binding->file_descriptor = -1; - } - } - this->bindings.clear(); + for(auto& binding : this->bindings) { + if(binding->event_accept) { + event_del_block(binding->event_accept); + event_free(binding->event_accept); + binding->event_accept = nullptr; + } + if(binding->file_descriptor > 0) { + if(shutdown(binding->file_descriptor, SHUT_RDWR) < 0) + logWarning(LOG_QUERY, "Failed to shutdown socket for binding {} ({} | {}).", binding->as_string(), errno, strerror(errno)); + if(close(binding->file_descriptor) < 0) + logError(LOG_QUERY, "Failed to close socket for binding {} ({} | {}).", binding->as_string(), errno, strerror(errno)); + binding->file_descriptor = -1; + } + } + this->bindings.clear(); - if(this->eventLoop) - event_base_loopexit(this->eventLoop, nullptr); + if(this->eventLoop) + event_base_loopexit(this->eventLoop, nullptr); if(this->ioThread) { - if(this->ioThread->join(seconds(3)) != 0) { - logCritical(LOG_QUERY, "Failed to terminate event loop!"); - this->ioThread->detach(); - } + if(this->ioThread->join(seconds(3)) != 0) { + logCritical(LOG_QUERY, "Failed to terminate event loop!"); + this->ioThread->detach(); + } } delete this->ioThread; this->ioThread = nullptr; - if(this->eventLoop) { - event_base_free(this->eventLoop); - this->eventLoop = nullptr; - } + if(this->eventLoop) { + event_base_free(this->eventLoop); + this->eventLoop = nullptr; + } if(this->server_reserve_fd > 0) { - if(close(this->server_reserve_fd) < 0) - logError(LOG_QUERY, "Failed to close backup file descriptor ({} | {})", errno, strerror(errno)); + if(close(this->server_reserve_fd) < 0) + logError(LOG_QUERY, "Failed to close backup file descriptor ({} | {})", errno, strerror(errno)); } - this->server_reserve_fd = -1; + this->server_reserve_fd = -1; } inline std::string logging_address(const sockaddr_storage& address) { - if(config::server::disable_ip_saving) - return "X.X.X.X" + to_string(net::port(address)); - return net::to_string(address, true); + if(config::server::disable_ip_saving) + return "X.X.X.X" + to_string(net::port(address)); + return net::to_string(address, true); } inline void send_direct_disconnect(const sockaddr_storage& address, int file_descriptor, const char* message, size_t message_length) { - auto _non_block = [&]{ - int flags = fcntl(file_descriptor, F_GETFL, 0); - if (flags == -1) { - debugMessage(LOG_QUERY, "[{}] Failed to set socket to nonblock. Flag query failed ({} | {})", logging_address(address), errno, strerror(errno)); - return; - } + auto _non_block = [&]{ + int flags = fcntl(file_descriptor, F_GETFL, 0); + if (flags == -1) { + debugMessage(LOG_QUERY, "[{}] Failed to set socket to nonblock. Flag query failed ({} | {})", logging_address(address), errno, strerror(errno)); + return; + } - flags &= ~O_NONBLOCK; - if(fcntl(file_descriptor, F_SETFL, flags) == -1) { - debugMessage(LOG_QUERY, "[{}] Failed to set socket to nonblock. Flag apply failed ({} | {})", logging_address(address), errno, strerror(errno)); - return; - } - }; - _non_block(); + flags &= ~O_NONBLOCK; + if(fcntl(file_descriptor, F_SETFL, flags) == -1) { + debugMessage(LOG_QUERY, "[{}] Failed to set socket to nonblock. Flag apply failed ({} | {})", logging_address(address), errno, strerror(errno)); + return; + } + }; + _non_block(); - { - struct timeval timeout{}; - timeout.tv_sec = 5; - timeout.tv_usec = 0; - if (setsockopt (file_descriptor, SOL_SOCKET, SO_SNDTIMEO, (char *)&timeout, sizeof(timeout)) < 0) - debugMessage(LOG_QUERY, "[{}] Failed to set the send timeout on socket", logging_address(address)); - } + { + struct timeval timeout{}; + timeout.tv_sec = 5; + timeout.tv_usec = 0; + if (setsockopt (file_descriptor, SOL_SOCKET, SO_SNDTIMEO, (char *)&timeout, sizeof(timeout)) < 0) + debugMessage(LOG_QUERY, "[{}] Failed to set the send timeout on socket", logging_address(address)); + } - bool broken_pipe = false; - auto _send = [&](const char* data, size_t length) { - if(broken_pipe) - return; + bool broken_pipe = false; + auto _send = [&](const char* data, size_t length) { + if(broken_pipe) + return; - size_t written_bytes = 0; - while(written_bytes < length) { - auto result = send(file_descriptor, data + written_bytes, length - written_bytes, MSG_NOSIGNAL); - if(result <= 0) { - broken_pipe |= errno == EPIPE; - debugMessage(LOG_QUERY, "[{}] Failed to send a message of length {}. Bytes written: {}, error: {} | {}", logging_address(address), length, written_bytes, errno, strerror(errno)); - return; - } else { - written_bytes += result; - } - } - }; + size_t written_bytes = 0; + while(written_bytes < length) { + auto result = send(file_descriptor, data + written_bytes, length - written_bytes, MSG_NOSIGNAL); + if(result <= 0) { + broken_pipe |= errno == EPIPE; + debugMessage(LOG_QUERY, "[{}] Failed to send a message of length {}. Bytes written: {}, error: {} | {}", logging_address(address), length, written_bytes, errno, strerror(errno)); + return; + } else { + written_bytes += result; + } + } + }; - /* we could ignore errors here */ - _send(config::query::motd.data(), config::query::motd.size()); - _send(message, message_length); + /* we could ignore errors here */ + _send(config::query::motd.data(), config::query::motd.size()); + _send(message, message_length); - /* "flush" with the last new line and then close */ - int flag = 1; - if(setsockopt(file_descriptor, IPPROTO_TCP, TCP_NODELAY, (char *) &flag, sizeof(int)) < 0) { - debugMessage(LOG_QUERY, "[{}] Failed to enabled TCP no delay to flush the direct query disconnect socket ({} | {}).", logging_address(address), errno, strerror(errno)); - } - _send(config::query::newlineCharacter.data(), config::query::newlineCharacter.size()); + /* "flush" with the last new line and then close */ + int flag = 1; + if(setsockopt(file_descriptor, IPPROTO_TCP, TCP_NODELAY, (char *) &flag, sizeof(int)) < 0) { + debugMessage(LOG_QUERY, "[{}] Failed to enabled TCP no delay to flush the direct query disconnect socket ({} | {}).", logging_address(address), errno, strerror(errno)); + } + _send(config::query::newlineCharacter.data(), config::query::newlineCharacter.size()); - if(shutdown(file_descriptor, SHUT_RDWR) < 0) { - debugMessage(LOG_QUERY, "[{}] Failed to shutdown socket ({} | {}).", logging_address(address), errno, strerror(errno)); - } - if(close(file_descriptor) < 0) { - debugMessage(LOG_QUERY, "[{}] Failed to close socket ({} | {}).", logging_address(address), errno, strerror(errno)); - } + if(shutdown(file_descriptor, SHUT_RDWR) < 0) { + debugMessage(LOG_QUERY, "[{}] Failed to shutdown socket ({} | {}).", logging_address(address), errno, strerror(errno)); + } + if(close(file_descriptor) < 0) { + debugMessage(LOG_QUERY, "[{}] Failed to close socket ({} | {}).", logging_address(address), errno, strerror(errno)); + } } //dummyfdflood //dummyfdflood clear void QueryServer::on_client_receive(int _server_file_descriptor, short ev, void *arg) { - sockaddr_storage remote_address{}; + sockaddr_storage remote_address{}; memset(&remote_address, 0, sizeof(sockaddr_in)); socklen_t address_length = sizeof(remote_address); @@ -295,110 +295,110 @@ void QueryServer::on_client_receive(int _server_file_descriptor, short ev, void return; if(errno == EMFILE || errno == ENFILE) { - if(errno == EMFILE) - logError(LOG_QUERY, "Server ran out file descriptors. Please increase the process file descriptor limit or decrease the instance variable 'serverinstance_query_max_connections'"); - else - logError(LOG_QUERY, "Server ran out file descriptors. Please increase the process and system-wide file descriptor limit or decrease the instance variable 'serverinstance_query_max_connections'"); + if(errno == EMFILE) + logError(LOG_QUERY, "Server ran out file descriptors. Please increase the process file descriptor limit or decrease the instance variable 'serverinstance_query_max_connections'"); + else + logError(LOG_QUERY, "Server ran out file descriptors. Please increase the process and system-wide file descriptor limit or decrease the instance variable 'serverinstance_query_max_connections'"); - bool tmp_close_success = false; - { - lock_guard reserve_fd_lock(server_reserve_fd_lock); - if(this->server_reserve_fd > 0) { - debugMessage(LOG_QUERY, "Trying to accept client with the reserved file descriptor to send him a protocol limit reached exception."); - auto _ = [&]{ - if(close(this->server_reserve_fd) < 0) { - debugMessage(LOG_QUERY, "Failed to close reserved file descriptor"); - tmp_close_success = false; - return; - } - this->server_reserve_fd = 0; + bool tmp_close_success = false; + { + lock_guard reserve_fd_lock(server_reserve_fd_lock); + if(this->server_reserve_fd > 0) { + debugMessage(LOG_QUERY, "Trying to accept client with the reserved file descriptor to send him a protocol limit reached exception."); + auto _ = [&]{ + if(close(this->server_reserve_fd) < 0) { + debugMessage(LOG_QUERY, "Failed to close reserved file descriptor"); + tmp_close_success = false; + return; + } + this->server_reserve_fd = 0; - errno = 0; - file_descriptor = accept(_server_file_descriptor, (struct sockaddr *) &remote_address, &address_length); - if(file_descriptor < 0) { - if(errno == EMFILE || errno == ENFILE) - debugMessage(LOG_QUERY, "[{}] Even with freeing the reserved descriptor accept failed. Attempting to reclaim reserved file descriptor", logging_address(remote_address)); - else if(errno == EAGAIN); - else { - debugMessage(LOG_QUERY, "[{}] Failed to accept client with reserved file descriptor. ({} | {})", logging_address(remote_address), errno, strerror(errno)); - } - this->server_reserve_fd = dup(1); - if(this->server_reserve_fd < 0) - debugMessage(LOG_QUERY, "[{}] Failed to reclaim reserved file descriptor. Future clients cant be accepted!", logging_address(remote_address)); - else - tmp_close_success = true; - return; - } - debugMessage(LOG_QUERY, "[{}] Successfully accepted client via reserved descriptor (fd: {}). Initializing socket and sending MOTD and disconnect.", logging_address(remote_address), file_descriptor); + errno = 0; + file_descriptor = accept(_server_file_descriptor, (struct sockaddr *) &remote_address, &address_length); + if(file_descriptor < 0) { + if(errno == EMFILE || errno == ENFILE) + debugMessage(LOG_QUERY, "[{}] Even with freeing the reserved descriptor accept failed. Attempting to reclaim reserved file descriptor", logging_address(remote_address)); + else if(errno == EAGAIN); + else { + debugMessage(LOG_QUERY, "[{}] Failed to accept client with reserved file descriptor. ({} | {})", logging_address(remote_address), errno, strerror(errno)); + } + this->server_reserve_fd = dup(1); + if(this->server_reserve_fd < 0) + debugMessage(LOG_QUERY, "[{}] Failed to reclaim reserved file descriptor. Future clients cant be accepted!", logging_address(remote_address)); + else + tmp_close_success = true; + return; + } + debugMessage(LOG_QUERY, "[{}] Successfully accepted client via reserved descriptor (fd: {}). Initializing socket and sending MOTD and disconnect.", logging_address(remote_address), file_descriptor); - static auto resource_limit_error = R"(error id=57344 msg=query\sserver\sresource\slimit\sreached extra_msg=file\sdescriptor\slimit\sexceeded)"; - send_direct_disconnect(remote_address, file_descriptor, resource_limit_error, strlen(resource_limit_error)); + static auto resource_limit_error = R"(error id=57344 msg=query\sserver\sresource\slimit\sreached extra_msg=file\sdescriptor\slimit\sexceeded)"; + send_direct_disconnect(remote_address, file_descriptor, resource_limit_error, strlen(resource_limit_error)); - this->server_reserve_fd = dup(1); - if(this->server_reserve_fd < 0) - debugMessage(LOG_QUERY, "Failed to reclaim reserved file descriptor. Future clients cant be accepted!"); - else - tmp_close_success = true; - logMessage(LOG_QUERY, "[{}] Dropping new query connection attempt because of too many open file descriptors.", logging_address(remote_address)); - }; - _(); - } - } + this->server_reserve_fd = dup(1); + if(this->server_reserve_fd < 0) + debugMessage(LOG_QUERY, "Failed to reclaim reserved file descriptor. Future clients cant be accepted!"); + else + tmp_close_success = true; + logMessage(LOG_QUERY, "[{}] Dropping new query connection attempt because of too many open file descriptors.", logging_address(remote_address)); + }; + _(); + } + } - if(!tmp_close_success) { - debugMessage(LOG_QUERY, "Sleeping two seconds because we're currently having no resources for this user. (Removing the accept event)"); - for(auto& binding : this->bindings) - event_del_noblock(binding->event_accept); - accept_event_deleted = system_clock::now(); - return; - } - return; + if(!tmp_close_success) { + debugMessage(LOG_QUERY, "Sleeping two seconds because we're currently having no resources for this user. (Removing the accept event)"); + for(auto& binding : this->bindings) + event_del_noblock(binding->event_accept); + accept_event_deleted = system_clock::now(); + return; + } + return; } - logMessage(LOG_QUERY, "Got an error while accepting a new client. (errno: {}, message: {})", errno, strerror(errno)); + logMessage(LOG_QUERY, "Got an error while accepting a new client. (errno: {}, message: {})", errno, strerror(errno)); return; } - { - unique_lock lock(this->connected_clients_lock); - auto max_connections = serverInstance->properties()[property::SERVERINSTANCE_QUERY_MAX_CONNECTIONS].as(); - if(max_connections > 0 && max_connections <= this->connectedClients.size()) { - lock.unlock(); - logMessage(LOG_QUERY, "[{}] Dropping new query connection attempt because of too many connected query clients.", logging_address(remote_address)); - static auto query_server_full = R"(error id=4611 msg=max\sclients\sreached)"; - send_direct_disconnect(remote_address, file_descriptor, query_server_full, strlen(query_server_full)); - return; - } + { + unique_lock lock(this->connected_clients_lock); + auto max_connections = serverInstance->properties()[property::SERVERINSTANCE_QUERY_MAX_CONNECTIONS].as(); + if(max_connections > 0 && max_connections <= this->connectedClients.size()) { + lock.unlock(); + logMessage(LOG_QUERY, "[{}] Dropping new query connection attempt because of too many connected query clients.", logging_address(remote_address)); + static auto query_server_full = R"(error id=4611 msg=max\sclients\sreached)"; + send_direct_disconnect(remote_address, file_descriptor, query_server_full, strlen(query_server_full)); + return; + } - auto max_ip_connections = serverInstance->properties()[property::SERVERINSTANCE_QUERY_MAX_CONNECTIONS_PER_IP].as(); - if(max_ip_connections > 0) { - size_t connection_count = 0; - for(auto& client : this->connectedClients) { - if(net::address_equal(client->remote_address, remote_address)) - connection_count++; - } + auto max_ip_connections = serverInstance->properties()[property::SERVERINSTANCE_QUERY_MAX_CONNECTIONS_PER_IP].as(); + if(max_ip_connections > 0) { + size_t connection_count = 0; + for(auto& client : this->connectedClients) { + if(net::address_equal(client->remote_address, remote_address)) + connection_count++; + } - if(connection_count >= max_ip_connections) { - lock.unlock(); - logMessage(LOG_QUERY, "[{}] Dropping new query connection attempt because of too many simultaneously connected session from this ip.", logging_address(remote_address)); - static auto query_server_full = R"(error id=4610 msg=too\smany\ssimultaneously\sconnected\ssessions)";// - send_direct_disconnect(remote_address, file_descriptor, query_server_full, strlen(query_server_full)); - return; - } - } - } + if(connection_count >= max_ip_connections) { + lock.unlock(); + logMessage(LOG_QUERY, "[{}] Dropping new query connection attempt because of too many simultaneously connected session from this ip.", logging_address(remote_address)); + static auto query_server_full = R"(error id=4610 msg=too\smany\ssimultaneously\sconnected\ssessions)";// + send_direct_disconnect(remote_address, file_descriptor, query_server_full, strlen(query_server_full)); + return; + } + } + } shared_ptr client = std::make_shared(this, file_descriptor); client->applySelfLock(client); memcpy(&client->remote_address, &remote_address, sizeof(remote_address)); - { - lock_guard lock(this->connected_clients_lock); - this->connectedClients.push_back(client); - } + { + lock_guard lock(this->connected_clients_lock); + this->connectedClients.push_back(client); + } client->preInitialize(); if(client->readEvent) { - event_add(client->readEvent, nullptr); + event_add(client->readEvent, nullptr); } - logMessage(LOG_QUERY, "Got new client from {}", client->getLoggingPeerIp() + ":" + to_string(client->getPeerPort())); + logMessage(LOG_QUERY, "Got new client from {}", client->getLoggingPeerIp() + ":" + to_string(client->getPeerPort())); } /* @@ -488,7 +488,7 @@ inline deque> query_accounts(sql::command& command) { else if(columns[index] == "uniqueId") entry->unique_id = value[index]; else if(columns[index] == "server") { - entry->bound_server = value[index].empty() ? 0 : stoll(value[index]); + entry->bound_server = value[index].empty() ? 0 : stoll(value[index]); } } catch (std::exception& ex) { logError(LOG_QUERY, "Failed to parse query account data for row {} ({})", columns[index], value[index]); @@ -504,7 +504,7 @@ inline deque> query_accounts(sql::command& command) { } std::shared_ptr QueryServer::create_query_account(const std::string &username, ts::ServerId server, const std::string &owner, const std::string &password) { - LOG_SQL_CMD(sql::command(this->sql, "INSERT INTO `queries` (`username`, `password`, `uniqueId`, `server`) VALUES (:name, :password, :uid, :server)", variable{":name", username}, variable{":uid", owner}, variable{":password", password}, variable{":server", server}).execute()); + LOG_SQL_CMD(sql::command(this->sql, "INSERT INTO `queries` (`username`, `password`, `uniqueId`, `server`) VALUES (:name, :password, :uid, :server)", variable{":name", username}, variable{":uid", owner}, variable{":password", password}, variable{":server", server}).execute()); return this->find_query_account_by_name(username); } @@ -542,12 +542,12 @@ deque> QueryServer::find_query_accounts_by_unique_id(co } bool QueryServer::rename_query_account(const std::shared_ptr &account, const std::string &new_name) { - LOG_SQL_CMD(sql::command(this->sql, "UPDATE `queries` SET `username` = :new_name WHERE `username` = :old_name AND `server` = :server", variable{":new_name",new_name}, variable{":old_name", account->username}, variable{":server", account->bound_server}).execute()); + LOG_SQL_CMD(sql::command(this->sql, "UPDATE `queries` SET `username` = :new_name WHERE `username` = :old_name AND `server` = :server", variable{":new_name",new_name}, variable{":old_name", account->username}, variable{":server", account->bound_server}).execute()); return true; } bool QueryServer::change_query_password(const std::shared_ptr &account, const std::string &password) { - LOG_SQL_CMD(sql::command(this->sql, "UPDATE `queries` SET `password` = :password WHERE `username` = :name AND `server` = :server", variable{":password", password}, variable{":name", account->username}, variable{":server", account->bound_server}).execute()); + LOG_SQL_CMD(sql::command(this->sql, "UPDATE `queries` SET `password` = :password WHERE `username` = :name AND `server` = :server", variable{":password", password}, variable{":name", account->username}, variable{":server", account->bound_server}).execute()); return true; } @@ -585,9 +585,9 @@ void QueryServer::tick() { } } if(this->accept_event_deleted.time_since_epoch().count() != 0 && accept_event_deleted + seconds(5) < system_clock::now()) { - debugMessage(LOG_QUERY, "Readding accept event and try again if we have enough resources again."); - for(auto& binding : this->bindings) - event_add(binding->event_accept, nullptr); - accept_event_deleted = system_clock::time_point{}; + debugMessage(LOG_QUERY, "Readding accept event and try again if we have enough resources again."); + for(auto& binding : this->bindings) + event_add(binding->event_accept, nullptr); + accept_event_deleted = system_clock::time_point{}; } } \ No newline at end of file diff --git a/server/src/server/QueryServer.h b/server/src/server/QueryServer.h index 92cf9c4..7b5fdf8 100644 --- a/server/src/server/QueryServer.h +++ b/server/src/server/QueryServer.h @@ -29,35 +29,35 @@ namespace ts { ClientUid uniqueId; }; - class QueryAccount { - public: - std::string username; - std::string unique_id; + class QueryAccount { + public: + std::string username; + std::string unique_id; - ServerId bound_server; + ServerId bound_server; - QueryAccount() = default; - virtual ~QueryAccount() = default; + QueryAccount() = default; + virtual ~QueryAccount() = default; }; - class PasswortedQueryAccount : public QueryAccount { - public: - std::string password; + class PasswortedQueryAccount : public QueryAccount { + public: + std::string password; - PasswortedQueryAccount() = default; - virtual ~PasswortedQueryAccount() = default; + PasswortedQueryAccount() = default; + virtual ~PasswortedQueryAccount() = default; }; class QueryServer { friend class QueryClient; public: - struct Binding { - sockaddr_storage address{}; - int file_descriptor = 0; - ::event* event_accept = nullptr; + struct Binding { + sockaddr_storage address{}; + int file_descriptor = 0; + ::event* event_accept = nullptr; - inline std::string as_string() { return net::to_string(address, true); } - }; + inline std::string as_string() { return net::to_string(address, true); } + }; explicit QueryServer(sql::SqlManager*); ~QueryServer(); @@ -71,36 +71,36 @@ namespace ts { /* std::string createQueryLogin(const std::string &name, ClientUid uid, std::string = ""); bool renameQueryLogin(ClientUid uid, const std::string &targetName); - std::string resetQueryPassword(const std::shared_ptr&, std::string = ""); + std::string resetQueryPassword(const std::shared_ptr&, std::string = ""); std::shared_ptr findQueryLoginByName(const std::string &name); std::shared_ptr findQueryLoginByUid(const std::string &uid); - */ + */ - std::deque> list_query_accounts(OptionalServerId /* server */); - std::shared_ptr create_query_account(const std::string& /* name */, ServerId /* server */, const std::string& /* owner unique id */, const std::string& /* password */); - std::shared_ptr load_password(const std::shared_ptr& /* account */); - bool delete_query_account(const std::shared_ptr& /* account */); + std::deque> list_query_accounts(OptionalServerId /* server */); + std::shared_ptr create_query_account(const std::string& /* name */, ServerId /* server */, const std::string& /* owner unique id */, const std::string& /* password */); + std::shared_ptr load_password(const std::shared_ptr& /* account */); + bool delete_query_account(const std::shared_ptr& /* account */); - std::shared_ptr find_query_account_by_name(const std::string& /* name */); - std::deque> find_query_accounts_by_unique_id(const std::string &unique_id /* unique id */); + std::shared_ptr find_query_account_by_name(const std::string& /* name */); + std::deque> find_query_accounts_by_unique_id(const std::string &unique_id /* unique id */); - bool rename_query_account(const std::shared_ptr& /* account */, const std::string& /* new name */); - bool change_query_password(const std::shared_ptr& /* account */, const std::string& /* new password */); + bool rename_query_account(const std::shared_ptr& /* account */, const std::string& /* new name */); + bool change_query_password(const std::shared_ptr& /* account */, const std::string& /* new password */); - threads::ThreadPool* executePool() { return this->_executePool; } + threads::ThreadPool* executePool() { return this->_executePool; } private: sql::SqlManager* sql; bool active = false; - std::deque> bindings; + std::deque> bindings; std::vector threads; std::mutex server_reserve_fd_lock; - int server_reserve_fd = -1; /* -1 = unset | 0 = in use | > 0 ready to use */ + int server_reserve_fd = -1; /* -1 = unset | 0 = in use | > 0 ready to use */ std::unique_ptr ip_whitelist; - std::unique_ptr ip_blacklist; + std::unique_ptr ip_blacklist; //IO stuff event_base* eventLoop = nullptr; diff --git a/server/src/server/VoiceIOManager.cpp b/server/src/server/VoiceIOManager.cpp index c6d9d8c..7619cfb 100644 --- a/server/src/server/VoiceIOManager.cpp +++ b/server/src/server/VoiceIOManager.cpp @@ -23,9 +23,9 @@ std::shared_ptr VoiceIOManager::enableIo(server::TSServer *serv this->adjustExecutors(this->servers.size() + 1); std::vector> use_list; - use_list.reserve(config::threads::voice::events_per_server); + use_list.reserve(config::threads::voice::events_per_server); - lock_guard executor_lock(this->executorLock); + lock_guard executor_lock(this->executorLock); for(size_t i = 0; i < config::threads::voice::events_per_server; i++){ auto loop = this->less_used_io_loop(use_list); if(!loop) break; //No more loops open @@ -38,7 +38,7 @@ std::shared_ptr VoiceIOManager::enableIo(server::TSServer *serv threads::MutexLock l(this->serverLock); this->servers.push_back(server_io); } - this->ioExecutorNotify.notify_all(); + this->ioExecutorNotify.notify_all(); return server_io; } @@ -48,10 +48,10 @@ void VoiceIOManager::disableIo(server::TSServer* server) { { threads::MutexLock l(this->serverLock); for(const auto& sio : this->servers) { - if(sio->server == server) { - server_io = sio; - break; - } + if(sio->server == server) { + server_io = sio; + break; + } } if(!server_io) return; this->servers.erase(std::find(this->servers.begin(), this->servers.end(), server_io),this->servers.end()); @@ -62,18 +62,18 @@ void VoiceIOManager::disableIo(server::TSServer* server) { entry->despawn(); } - server_io->event_loop_events.clear(); + server_io->event_loop_events.clear(); this->adjustExecutors(this->servers.size()); } void VoiceIOManager::shutdownGlobally() { /* Unregister all servers */ - { + { lock_guard server_lock(this->serverLock); for(const auto& server : this->servers) for(const auto& loop : server->event_loop_events){ - loop->disable(); - loop->despawn(); + loop->disable(); + loop->despawn(); } } @@ -88,7 +88,7 @@ void VoiceIOManager::shutdownGlobally() { } /* keep a ref to all event loops so they dont despawn in their event thread */ - unique_lock executor_lock{this->executorLock}; + unique_lock executor_lock{this->executorLock}; auto event_loops = this->event_loops; auto wait_end = system_clock::now() + chrono::seconds{5}; @@ -99,15 +99,15 @@ void VoiceIOManager::shutdownGlobally() { auto status = this->ioExecutorNotify.wait_until(executor_lock, wait_end); if(status == std::cv_status::timeout) { logCritical(LOG_GENERAL, - "Failed to shutdown all event loops successfully. After timeout {} loops are left.", - this->event_loops.size() + "Failed to shutdown all event loops successfully. After timeout {} loops are left.", + this->event_loops.size() ); break; } } /* now delete all loops */ - event_loops.clear(); + event_loops.clear(); } //TODO also reduce thread pool! @@ -125,8 +125,8 @@ void VoiceIOManager::adjustExecutors(size_t size) { } IOEventLoop::~IOEventLoop() { - assert(this_thread::get_id() != this->executor.get_id()); - assert(!this->executor.joinable()); + assert(this_thread::get_id() != this->executor.get_id()); + assert(!this->executor.joinable()); } /** @@ -226,20 +226,20 @@ int IOServerHandler::resolve_file_descriptor(const std::shared_ptr &client) { - if(this->event_loop_events.empty()) - return; /* TODO any kind of error or warning? */ + if(this->event_loop_events.empty()) + return; /* TODO any kind of error or warning? */ auto socket = client->socket; - auto event_loop = this->event_loop_events[this->event_loop_index++ % this->event_loop_events.size()]; - if(socket < 0 || socket > event_loop->events.size()) - return; /* TODO any kind of error or warning? */ + auto event_loop = this->event_loop_events[this->event_loop_index++ % this->event_loop_events.size()]; + if(socket < 0 || socket > event_loop->events.size()) + return; /* TODO any kind of error or warning? */ - auto event = event_loop->events[socket]; - if(!event->event_write) - return; /* TODO any kind of error or warning? */ + auto event = event_loop->events[socket]; + if(!event->event_write) + return; /* TODO any kind of error or warning? */ event->push_voice_write_queue(client); - event_add(event->event_write, nullptr); + event_add(event->event_write, nullptr); } void IOServerHandler::send_datagram(ts::io::datagram_packet_t datagram, int socket) { @@ -255,31 +255,31 @@ void IOServerHandler::send_datagram(ts::io::datagram_packet_t datagram, int sock return; /* TODO any kind of error or warning? */ if(datagram) - event->push_dg_write_queue(datagram); + event->push_dg_write_queue(datagram); - event_add(event->event_write, nullptr); + event_add(event->event_write, nullptr); } void IOEventLoopEvents::spawn() { - for(const auto& binding : this->owner->server->getVoiceServer()->activeBindings()) { + for(const auto& binding : this->owner->server->getVoiceServer()->activeBindings()) { auto entry = make_shared(); entry->file_descriptor = binding->file_descriptor; entry->handle = this; - entry->server = this->owner->server; + entry->server = this->owner->server; entry->family = binding->address.ss_family; - entry->voice_server = &*this->owner->server->getVoiceServer(); + entry->voice_server = &*this->owner->server->getVoiceServer(); entry->event_read = event_new(this->event_loop->base, binding->file_descriptor, EV_READ | EV_PERSIST, VoiceServer::handleMessageRead, &*entry); entry->event_write = event_new(this->event_loop->base, binding->file_descriptor, EV_WRITE, VoiceServer::handleMessageWrite, &*entry); this->events.push_back(entry); - entry->socket_id = (int) this->events.size() - 1; + entry->socket_id = (int) this->events.size() - 1; { lock_guard lock(event_loop->events_lock); this->event_loop->assigned_events.push_back(entry); } - } + } } void IOEventLoopEvents::despawn() { @@ -297,14 +297,14 @@ void IOEventLoopEvents::despawn() { } void VoiceIOManager::dispatchBase(shared_ptr self) { - debugMessage(LOG_INSTANCE, "Dispatching io base {}", (void*) self->base); + debugMessage(LOG_INSTANCE, "Dispatching io base {}", (void*) self->base); while(true) { - if(event_base_got_exit(self->base) || self->shutdown) - break; + if(event_base_got_exit(self->base) || self->shutdown) + break; event_base_loop(self->base, 0); { - /* wait until reschedule */ + /* wait until reschedule */ unique_lock execute_lock(this->executorLock); if(event_base_get_num_events(self->base, EVENT_BASE_COUNT_ACTIVE | EVENT_BASE_COUNT_ADDED | EVENT_BASE_COUNT_VIRTUAL) == 0) this->ioExecutorNotify.wait(execute_lock); @@ -316,9 +316,9 @@ void VoiceIOManager::dispatchBase(shared_ptr self) { lock_guard executor_lock(this->executorLock); auto found = std::find(this->event_loops.begin(), this->event_loops.end(), self); if(found != this->event_loops.end()) { - this->event_loops.erase(found); + this->event_loops.erase(found); } else { - logCritical(LOG_INSTANCE, "Could not find executor in executor registry ({})!", (void*) self->base); + logCritical(LOG_INSTANCE, "Could not find executor in executor registry ({})!", (void*) self->base); } if(!self->assigned_events.empty()) { @@ -339,7 +339,7 @@ DatagramPacket* DatagramPacket::create(const sockaddr_storage &address, const pk new (&instance->self_buffer) pipes::buffer; instance->next_packet = nullptr; - instance->self_buffer = membuf; + instance->self_buffer = membuf; memcpy(&instance->address, &address, sizeof(address)); if(address.ss_family == AF_INET6) { memcpy(&instance->address_info, &address_info, sizeof(in6_pktinfo)); @@ -355,5 +355,5 @@ DatagramPacket* DatagramPacket::create(const sockaddr_storage &address, const pk } void DatagramPacket::destory(ts::io::datagram_packet_t packet) { - packet->self_buffer.~buffer(); + packet->self_buffer.~buffer(); } \ No newline at end of file diff --git a/server/src/server/VoiceIOManager.h b/server/src/server/VoiceIOManager.h index eaadb12..c970438 100644 --- a/server/src/server/VoiceIOManager.h +++ b/server/src/server/VoiceIOManager.h @@ -10,46 +10,46 @@ namespace ts { namespace server { class TSServer; - class VoiceServer; + class VoiceServer; class VoiceClient; } namespace io { - union pktinfo_storage { - in_pktinfo v4; - in6_pktinfo v6; - }; + union pktinfo_storage { + in_pktinfo v4; + in6_pktinfo v6; + }; - struct DatagramPacket; + struct DatagramPacket; - typedef DatagramPacket* datagram_packet_t; - struct DatagramPacket { - private: - pipes::buffer self_buffer; + typedef DatagramPacket* datagram_packet_t; + struct DatagramPacket { + private: + pipes::buffer self_buffer; - public: - datagram_packet_t next_packet; + public: + datagram_packet_t next_packet; - sockaddr_storage address; - pktinfo_storage address_info; + sockaddr_storage address; + pktinfo_storage address_info; - size_t data_length; - uint8_t data[0]; + size_t data_length; + uint8_t data[0]; - static void destory(datagram_packet_t); - static datagram_packet_t create(const sockaddr_storage& address, const pktinfo_storage& address_info, size_t length, const uint8_t* data); - static int extract_info(msghdr& /* header */, pktinfo_storage& /* info */); + static void destory(datagram_packet_t); + static datagram_packet_t create(const sockaddr_storage& address, const pktinfo_storage& address_info, size_t length, const uint8_t* data); + static int extract_info(msghdr& /* header */, pktinfo_storage& /* info */); - DatagramPacket() = delete; - DatagramPacket(const DatagramPacket&) = delete; - DatagramPacket(DatagramPacket&&) = delete; + DatagramPacket() = delete; + DatagramPacket(const DatagramPacket&) = delete; + DatagramPacket(DatagramPacket&&) = delete; - ~DatagramPacket() = delete; - }; + ~DatagramPacket() = delete; + }; class VoiceIOManager; class IOServerHandler; - struct IOEventLoopEntry; - struct IOEventLoopEvents; + struct IOEventLoopEntry; + struct IOEventLoopEvents; struct IOEventLoop { IOEventLoop() = default; @@ -66,83 +66,83 @@ namespace ts { }; struct IOEventLoopEntry { - IOEventLoopEvents* handle; + IOEventLoopEvents* handle; - int socket_id = 0; - sa_family_t family; + int socket_id = 0; + sa_family_t family; - /* keeps these addresses in "hot" memory instead resolving three ptr */ - server::TSServer* server; - server::VoiceServer* voice_server; + /* keeps these addresses in "hot" memory instead resolving three ptr */ + server::TSServer* server; + server::VoiceServer* voice_server; int file_descriptor = 0; /* actual socket */ ::event* event_read = nullptr; ::event* event_write = nullptr; - spin_lock write_queue_lock; - datagram_packet_t dg_write_queue_head = nullptr; - datagram_packet_t dg_write_queue_tail = nullptr; + spin_lock write_queue_lock; + datagram_packet_t dg_write_queue_head = nullptr; + datagram_packet_t dg_write_queue_tail = nullptr; - std::deque> voice_write_queue; + std::deque> voice_write_queue; - inline datagram_packet_t pop_dg_write_queue() { - std::lock_guard lock(this->write_queue_lock); - if(!this->dg_write_queue_head) - return nullptr; + inline datagram_packet_t pop_dg_write_queue() { + std::lock_guard lock(this->write_queue_lock); + if(!this->dg_write_queue_head) + return nullptr; - auto packet = this->dg_write_queue_head; - if(packet == this->dg_write_queue_tail) { - this->dg_write_queue_tail = nullptr; - this->dg_write_queue_head = nullptr; - } else { - this->dg_write_queue_head = packet->next_packet; - } + auto packet = this->dg_write_queue_head; + if(packet == this->dg_write_queue_tail) { + this->dg_write_queue_tail = nullptr; + this->dg_write_queue_head = nullptr; + } else { + this->dg_write_queue_head = packet->next_packet; + } - return packet; - } + return packet; + } - inline void push_dg_write_queue(datagram_packet_t packet) { - std::lock_guard lock(this->write_queue_lock); - if(this->dg_write_queue_tail) { - this->dg_write_queue_tail->next_packet = packet; - } else { - this->dg_write_queue_head = packet; - } - this->dg_write_queue_tail = packet; - } + inline void push_dg_write_queue(datagram_packet_t packet) { + std::lock_guard lock(this->write_queue_lock); + if(this->dg_write_queue_tail) { + this->dg_write_queue_tail->next_packet = packet; + } else { + this->dg_write_queue_head = packet; + } + this->dg_write_queue_tail = packet; + } - inline void push_voice_write_queue(const std::shared_ptr& client) { - std::lock_guard lock(this->write_queue_lock); - this->voice_write_queue.push_back(client); - } + inline void push_voice_write_queue(const std::shared_ptr& client) { + std::lock_guard lock(this->write_queue_lock); + this->voice_write_queue.push_back(client); + } - /* return 0 on success | 1 on there is more, but success | 2 on empty */ - inline int pop_voice_write_queue(std::shared_ptr& result) { - std::lock_guard lock(this->write_queue_lock); + /* return 0 on success | 1 on there is more, but success | 2 on empty */ + inline int pop_voice_write_queue(std::shared_ptr& result) { + std::lock_guard lock(this->write_queue_lock); - auto it_begin = this->voice_write_queue.begin(); - auto it_end = this->voice_write_queue.end(); - auto it = it_begin; + auto it_begin = this->voice_write_queue.begin(); + auto it_end = this->voice_write_queue.end(); + auto it = it_begin; - while(it != it_end) { - result = it->lock(); - if(result) { - this->voice_write_queue.erase(it_begin, ++it); - return (int) (it != it_end); - } - it++; - } - if(it_begin != it_end) - this->voice_write_queue.erase(it_begin, it_end); - return 2; - } + while(it != it_end) { + result = it->lock(); + if(result) { + this->voice_write_queue.erase(it_begin, ++it); + return (int) (it != it_end); + } + it++; + } + if(it_begin != it_end) + this->voice_write_queue.erase(it_begin, it_end); + return 2; + } ~IOEventLoopEntry() { - if(this->event_read) - event_free(this->event_read); + if(this->event_read) + event_free(this->event_read); - if(this->event_write) - event_free(this->event_write); + if(this->event_write) + event_free(this->event_write); } }; @@ -155,31 +155,31 @@ namespace ts { void despawn(); inline void activate(){ - for(const auto& event : events) { - if(event->event_read) { - event_add(event->event_read, nullptr); - } - } + for(const auto& event : events) { + if(event->event_read) { + event_add(event->event_read, nullptr); + } + } } inline void disable(bool blocking = true){ - for(const auto& event : events) { - if(event->event_read) { - event_del(event->event_read); - event_del_block(event->event_read); - } + for(const auto& event : events) { + if(event->event_read) { + event_del(event->event_read); + event_del_block(event->event_read); + } - if(event->event_write) { - event_del(event->event_write); - event_del_block(event->event_write); - } - } + if(event->event_write) { + event_del(event->event_write); + event_del_block(event->event_write); + } + } } inline IOEventLoopEntry* event(int fd) { - for(const auto& entry : this->events) - if(entry->file_descriptor == fd) return entry.get(); - return nullptr; + for(const auto& entry : this->events) + if(entry->file_descriptor == fd) return entry.get(); + return nullptr; } }; @@ -190,9 +190,9 @@ namespace ts { explicit IOServerHandler(server::TSServer*); ~IOServerHandler(); - void invoke_write(const std::shared_ptr& /* client */); - int resolve_file_descriptor(const std::shared_ptr& /* client */); - void send_datagram(datagram_packet_t /* packet */, int /* socket */); + void invoke_write(const std::shared_ptr& /* client */); + int resolve_file_descriptor(const std::shared_ptr& /* client */); + void send_datagram(datagram_packet_t /* packet */, int /* socket */); private: std::shared_ptr create_event_loop_events(const std::shared_ptr &); @@ -217,7 +217,7 @@ namespace ts { std::deque> servers; std::mutex executorLock; - /* will be called as soon servers have been added or an event loop has been finished */ + /* will be called as soon servers have been added or an event loop has been finished */ std::condition_variable ioExecutorNotify; std::deque> event_loops; diff --git a/server/src/server/VoiceServer.cpp b/server/src/server/VoiceServer.cpp index f99f615..4ad9cb3 100644 --- a/server/src/server/VoiceServer.cpp +++ b/server/src/server/VoiceServer.cpp @@ -35,76 +35,76 @@ VoiceServer::~VoiceServer() { } #define SET_OPTION(type, option, flag, error) \ if(setsockopt(bind->file_descriptor, type, option, &flag, sizeof(flag)) < 0) { \ - error; \ - ::close(bind->file_descriptor); \ - bind->file_descriptor = 0; \ - continue; \ + error; \ + ::close(bind->file_descriptor); \ + bind->file_descriptor = 0; \ + continue; \ } bool VoiceServer::start(const std::deque>& binding, std::string& error) { - if(this->running) return false; + if(this->running) return false; if(binding.empty()) { - error = "Missing bindings!"; - return false; + error = "Missing bindings!"; + return false; } - this->running = true; + this->running = true; this->bindings = binding; - int enable = 1, disable = 0; - for (auto &bind : binding) { - bind->file_descriptor = socket(bind->address.ss_family, SOCK_DGRAM, 0); - if(!bind->file_descriptor) { - logError(this->server->getServerId(), "Failed to create socket for {}", bind->address_string()); - continue; - } + int enable = 1, disable = 0; + for (auto &bind : binding) { + bind->file_descriptor = socket(bind->address.ss_family, SOCK_DGRAM, 0); + if(!bind->file_descriptor) { + logError(this->server->getServerId(), "Failed to create socket for {}", bind->address_string()); + continue; + } - if(setsockopt(bind->file_descriptor, SOL_SOCKET, SO_REUSEADDR, &disable, sizeof(int)) < 0) logError(this->server->getServerId(), "Could not disable flag reuse address for bound {}!", bind->address_string()); - if(setsockopt(bind->file_descriptor, SOL_SOCKET, SO_REUSEPORT, &disable, sizeof(int)) < 0) logError(this->server->getServerId(), "Could not disable flag reuse port for bound {}!", bind->address_string()); - //if(setsockopt(bind->file_descriptor, SOL_SOCKET, SO_REUSEPORT, &disable, sizeof(int)) < 0) logError(this->server->getServerId(), "Could not disable flag reuse port for bound {}!", bind->address_string()); + if(setsockopt(bind->file_descriptor, SOL_SOCKET, SO_REUSEADDR, &disable, sizeof(int)) < 0) logError(this->server->getServerId(), "Could not disable flag reuse address for bound {}!", bind->address_string()); + if(setsockopt(bind->file_descriptor, SOL_SOCKET, SO_REUSEPORT, &disable, sizeof(int)) < 0) logError(this->server->getServerId(), "Could not disable flag reuse port for bound {}!", bind->address_string()); + //if(setsockopt(bind->file_descriptor, SOL_SOCKET, SO_REUSEPORT, &disable, sizeof(int)) < 0) logError(this->server->getServerId(), "Could not disable flag reuse port for bound {}!", bind->address_string()); - /* We're never sending over MTU size packets! */ - int pmtu = IP_PMTUDISC_DO; - setsockopt(bind->file_descriptor, IPPROTO_IP, IP_MTU_DISCOVER, &pmtu, sizeof(pmtu)); + /* We're never sending over MTU size packets! */ + int pmtu = IP_PMTUDISC_DO; + setsockopt(bind->file_descriptor, IPPROTO_IP, IP_MTU_DISCOVER, &pmtu, sizeof(pmtu)); - if(fcntl(bind->file_descriptor, F_SETFD, FD_CLOEXEC) < 0) - logError(this->server->getServerId(), "Failed to enable FD_CLOEXEC for {} ({}) (VoiceServer)", bind->file_descriptor, bind->address_string()); + if(fcntl(bind->file_descriptor, F_SETFD, FD_CLOEXEC) < 0) + logError(this->server->getServerId(), "Failed to enable FD_CLOEXEC for {} ({}) (VoiceServer)", bind->file_descriptor, bind->address_string()); - if(bind->address.ss_family == AF_INET6) { - SET_OPTION(IPPROTO_IPV6, IPV6_RECVPKTINFO, enable, { - logError(this->server->getServerId(), "Failed to enable packet info (v6) for {}", bind->address_string()); - }); - SET_OPTION(IPPROTO_IPV6, IPV6_V6ONLY, enable, { - logError(this->server->getServerId(), "Failed to enable ip v6 only for {}", bind->address_string()); - }); - } else { - SET_OPTION(IPPROTO_IP, IP_PKTINFO, enable, { - logError(this->server->getServerId(), "Failed to enable packet info for {}", bind->address_string()); - }); - } + if(bind->address.ss_family == AF_INET6) { + SET_OPTION(IPPROTO_IPV6, IPV6_RECVPKTINFO, enable, { + logError(this->server->getServerId(), "Failed to enable packet info (v6) for {}", bind->address_string()); + }); + SET_OPTION(IPPROTO_IPV6, IPV6_V6ONLY, enable, { + logError(this->server->getServerId(), "Failed to enable ip v6 only for {}", bind->address_string()); + }); + } else { + SET_OPTION(IPPROTO_IP, IP_PKTINFO, enable, { + logError(this->server->getServerId(), "Failed to enable packet info for {}", bind->address_string()); + }); + } - if(::bind(bind->file_descriptor, (const sockaddr*) &bind->address, net::address_size(bind->address)) < 0) { - logError(this->server->getServerId(), "Failed to bind to {} ({} => {})", bind->address_string(), errno, strerror(errno)); - ::close(bind->file_descriptor); - bind->file_descriptor = 0; - continue; - } - fcntl(bind->file_descriptor, F_SETFL, fcntl(bind->file_descriptor, F_GETFL, 0) | O_NONBLOCK); - } - { - auto bindings = this->activeBindings(); - if(bindings.empty()) { - error = "Failed to bind any address!"; - this->running = false; - this->bindings.empty(); - return false; - } + if(::bind(bind->file_descriptor, (const sockaddr*) &bind->address, net::address_size(bind->address)) < 0) { + logError(this->server->getServerId(), "Failed to bind to {} ({} => {})", bind->address_string(), errno, strerror(errno)); + ::close(bind->file_descriptor); + bind->file_descriptor = 0; + continue; + } + fcntl(bind->file_descriptor, F_SETFL, fcntl(bind->file_descriptor, F_GETFL, 0) | O_NONBLOCK); + } + { + auto bindings = this->activeBindings(); + if(bindings.empty()) { + error = "Failed to bind any address!"; + this->running = false; + this->bindings.empty(); + return false; + } - string str; - for(auto it = bindings.begin(); it != bindings.end(); it++) { - str += net::to_string((*it)->address) + (it + 1 == bindings.end() ? "" : " | "); - } - logMessage(this->server->getServerId(), "Started server on {}.", str); - } + string str; + for(auto it = bindings.begin(); it != bindings.end(); it++) { + str += net::to_string((*it)->address) + (it + 1 == bindings.end() ? "" : " | "); + } + logMessage(this->server->getServerId(), "Started server on {}.", str); + } this->io = serverInstance->getVoiceServerManager()->ioManager()->enableIo(this->server.get()); @@ -112,138 +112,138 @@ bool VoiceServer::start(const std::deque>& b } void VoiceServer::triggerWrite(const std::shared_ptr& client) { - if(!client) { - logError(this->server->getServerId(), "Invalid client for triggerWrite()"); - return; - } + if(!client) { + logError(this->server->getServerId(), "Invalid client for triggerWrite()"); + return; + } - this->io->invoke_write(client); + this->io->invoke_write(client); } void VoiceServer::schedule_execute(const ts::server::VoiceClient *client) { - auto vmanager = serverInstance->getVoiceServerManager(); - if(!vmanager) - return; - auto evloop = vmanager->get_executor_loop(); - if(!evloop) - return; + auto vmanager = serverInstance->getVoiceServerManager(); + if(!vmanager) + return; + auto evloop = vmanager->get_executor_loop(); + if(!evloop) + return; - evloop->schedule(client->event_handle_packet); + evloop->schedule(client->event_handle_packet); } void VoiceServer::tickHandshakingClients() { - this->pow_handler->execute_tick(); + this->pow_handler->execute_tick(); - decltype(this->activeConnections) connections; - { - lock_guard lock(this->connectionLock); - connections = this->activeConnections; - } - for(const auto& client : connections) - if(client->state == ConnectionState::INIT_HIGH || client->state == ConnectionState::INIT_LOW) + decltype(this->activeConnections) connections; + { + lock_guard lock(this->connectionLock); + connections = this->activeConnections; + } + for(const auto& client : connections) + if(client->state == ConnectionState::INIT_HIGH || client->state == ConnectionState::INIT_LOW) client->tick(system_clock::now()); } void VoiceServer::execute_resend(const std::chrono::system_clock::time_point &now, std::chrono::system_clock::time_point &next) { - decltype(this->activeConnections) connections; - { - lock_guard lock(this->connectionLock); - connections = this->activeConnections; - } - deque buffers; - string error; - for(const auto& client : connections) { - auto connection = client->getConnection(); - sassert(connection); /* its not possible that a client hasn't a connection! */ + decltype(this->activeConnections) connections; + { + lock_guard lock(this->connectionLock); + connections = this->activeConnections; + } + deque buffers; + string error; + for(const auto& client : connections) { + auto connection = client->getConnection(); + sassert(connection); /* its not possible that a client hasn't a connection! */ - if (connection->acknowledge_handler.execute_resend(now, next, buffers, error) < 0) { - debugMessage(client->getServerId(), "{} Failed to execute packet resend: {}", CLIENT_STR_LOG_PREFIX_(client), error); + if (connection->acknowledge_handler.execute_resend(now, next, buffers, error) < 0) { + debugMessage(client->getServerId(), "{} Failed to execute packet resend: {}", CLIENT_STR_LOG_PREFIX_(client), error); - if(client->state == ConnectionState::CONNECTED) { - client->disconnect(ViewReasonId::VREASON_TIMEOUT, config::messages::timeout::packet_resend_failed, nullptr, true); - } else - client->closeConnection(system_clock::now() + seconds(1)); - } else if(!buffers.empty()) { - { - lock_guard client_write_lock(connection->write_queue_lock); - connection->write_queue.insert(connection->write_queue.end(), buffers.begin(), buffers.end()); - } - buffers.clear(); - connection->triggerWrite(); - } - } + if(client->state == ConnectionState::CONNECTED) { + client->disconnect(ViewReasonId::VREASON_TIMEOUT, config::messages::timeout::packet_resend_failed, nullptr, true); + } else + client->closeConnection(system_clock::now() + seconds(1)); + } else if(!buffers.empty()) { + { + lock_guard client_write_lock(connection->write_queue_lock); + connection->write_queue.insert(connection->write_queue.end(), buffers.begin(), buffers.end()); + } + buffers.clear(); + connection->triggerWrite(); + } + } } bool VoiceServer::stop(const std::chrono::milliseconds& flushTimeout) { - if(!this->running) return false; + if(!this->running) return false; this->running = false; this->connectionLock.lock(); auto list = this->activeConnections; this->connectionLock.unlock(); for(const auto &e : list) - e->closeConnection(system_clock::now() + seconds(1)); + e->closeConnection(system_clock::now() + seconds(1)); - auto beg = system_clock::now(); - while(!this->activeConnections.empty() && flushTimeout.count() != 0 && system_clock::now() - beg < flushTimeout) - threads::self::sleep_for(milliseconds(10)); + auto beg = system_clock::now(); + while(!this->activeConnections.empty() && flushTimeout.count() != 0 && system_clock::now() - beg < flushTimeout) + threads::self::sleep_for(milliseconds(10)); - for(const auto& connection : this->activeConnections) - connection->voice_server = nullptr; + for(const auto& connection : this->activeConnections) + connection->voice_server = nullptr; this->activeConnections.clear(); - serverInstance->getVoiceServerManager()->ioManager()->disableIo(this->server.get()); - this->io = nullptr; - for(const auto& bind : this->bindings) { - if(bind->file_descriptor > 0){ - if(!shutdown(bind->file_descriptor, SHUT_RDWR)) logError(this->server->getServerId(), "Failed to shutdown socket {} ({}) Reason: {}/{}", bind->file_descriptor, bind->address_string(), errno, strerror(errno)); - if(!close(bind->file_descriptor)) { - if(errno != ENOTCONN) - logError(this->server->getServerId(), "Failed to close socket {} ({}) Reason: {}/{}", bind->file_descriptor, bind->address_string(), errno, strerror(errno)); - } - bind->file_descriptor = 0; - } - } - this->bindings.clear(); + serverInstance->getVoiceServerManager()->ioManager()->disableIo(this->server.get()); + this->io = nullptr; + for(const auto& bind : this->bindings) { + if(bind->file_descriptor > 0){ + if(!shutdown(bind->file_descriptor, SHUT_RDWR)) logError(this->server->getServerId(), "Failed to shutdown socket {} ({}) Reason: {}/{}", bind->file_descriptor, bind->address_string(), errno, strerror(errno)); + if(!close(bind->file_descriptor)) { + if(errno != ENOTCONN) + logError(this->server->getServerId(), "Failed to close socket {} ({}) Reason: {}/{}", bind->file_descriptor, bind->address_string(), errno, strerror(errno)); + } + bind->file_descriptor = 0; + } + } + this->bindings.clear(); return true; } std::shared_ptr VoiceServer::findClient(ts::ClientId client) { - lock_guard lock(this->connectionLock); + lock_guard lock(this->connectionLock); - for(const auto& elm : this->activeConnections) { - if(elm->getClientId() == client) - return elm; - } - return nullptr; + for(const auto& elm : this->activeConnections) { + if(elm->getClientId() == client) + return elm; + } + return nullptr; } std::shared_ptr VoiceServer::findClient(sockaddr_in *addr, bool) { - lock_guard lock(this->connectionLock); + lock_guard lock(this->connectionLock); for(const auto& elm : this->activeConnections) { - if(elm->isAddressV4()) - if(elm->getAddressV4()->sin_addr.s_addr == addr->sin_addr.s_addr) - if(elm->getAddressV4()->sin_port == addr->sin_port) - return elm; + if(elm->isAddressV4()) + if(elm->getAddressV4()->sin_addr.s_addr == addr->sin_addr.s_addr) + if(elm->getAddressV4()->sin_port == addr->sin_port) + return elm; } return nullptr; } std::shared_ptr VoiceServer::findClient(sockaddr_in6 *addr, bool) { - lock_guard lock(this->connectionLock); + lock_guard lock(this->connectionLock); - for(const auto& elm : this->activeConnections) { - if(elm->isAddressV6()) - if(memcmp(elm->getAddressV6()->sin6_addr.__in6_u.__u6_addr8, addr->sin6_addr.__in6_u.__u6_addr8, 16) == 0) - if(elm->getAddressV6()->sin6_port == addr->sin6_port) - return elm; - } - return nullptr; + for(const auto& elm : this->activeConnections) { + if(elm->isAddressV6()) + if(memcmp(elm->getAddressV6()->sin6_addr.__in6_u.__u6_addr8, addr->sin6_addr.__in6_u.__u6_addr8, 16) == 0) + if(elm->getAddressV6()->sin6_port == addr->sin6_port) + return elm; + } + return nullptr; } bool VoiceServer::unregisterConnection(std::shared_ptr connection) { - lock_guard lock(this->connectionLock); + lock_guard lock(this->connectionLock); auto found = std::find(this->activeConnections.begin(), this->activeConnections.end(), connection); if(found != activeConnections.end()) @@ -253,128 +253,128 @@ bool VoiceServer::unregisterConnection(std::shared_ptr connection) } static union { - char literal[8] = {'T', 'S', '3', 'I', 'N', 'I', 'T', '1'}; - uint64_t integral; + char literal[8] = {'T', 'S', '3', 'I', 'N', 'I', 'T', '1'}; + uint64_t integral; } TS3INIT; int io::DatagramPacket::extract_info(msghdr &message, pktinfo_storage &info) { - for (cmsghdr* cmsg = CMSG_FIRSTHDR(&message); cmsg != nullptr; cmsg = CMSG_NXTHDR(&message, cmsg)) { // iterate through all the control headers - if(cmsg->cmsg_type != IP_PKTINFO && cmsg->cmsg_type != IPV6_PKTINFO) continue; + for (cmsghdr* cmsg = CMSG_FIRSTHDR(&message); cmsg != nullptr; cmsg = CMSG_NXTHDR(&message, cmsg)) { // iterate through all the control headers + if(cmsg->cmsg_type != IP_PKTINFO && cmsg->cmsg_type != IPV6_PKTINFO) continue; - if(cmsg->cmsg_level == IPPROTO_IP) { - memcpy(&info, (void*) CMSG_DATA(cmsg), sizeof(in_pktinfo)); - return 4; - } else if(cmsg->cmsg_level == IPPROTO_IPV6) { - memcpy(&info, (void*) CMSG_DATA(cmsg), sizeof(in6_pktinfo)); - return 6; - } - } - return 0; + if(cmsg->cmsg_level == IPPROTO_IP) { + memcpy(&info, (void*) CMSG_DATA(cmsg), sizeof(in_pktinfo)); + return 4; + } else if(cmsg->cmsg_level == IPPROTO_IPV6) { + memcpy(&info, (void*) CMSG_DATA(cmsg), sizeof(in6_pktinfo)); + return 6; + } + } + return 0; } void VoiceServer::handleMessageRead(int fd, short events, void *_event_handle) { - auto event_handle = (io::IOEventLoopEntry*) _event_handle; - auto voice_server = event_handle->voice_server; - auto ts_server = event_handle->server; + auto event_handle = (io::IOEventLoopEntry*) _event_handle; + auto voice_server = event_handle->voice_server; + auto ts_server = event_handle->server; - size_t raw_read_buffer_length = event_handle->family == AF_INET ? 600 : 1600; //IPv6 MTU: 1500 | IPv4 MTU: 576 - uint8_t raw_read_buffer[raw_read_buffer_length]; //Allocate on stack, so we dont need heap here + size_t raw_read_buffer_length = event_handle->family == AF_INET ? 600 : 1600; //IPv6 MTU: 1500 | IPv4 MTU: 576 + uint8_t raw_read_buffer[raw_read_buffer_length]; //Allocate on stack, so we dont need heap here ssize_t bytes_read = 0; - pipes::buffer_view read_buffer{raw_read_buffer, raw_read_buffer_length}; /* will not allocate anything, just sets its mode to ptr and thats it :) */ + pipes::buffer_view read_buffer{raw_read_buffer, raw_read_buffer_length}; /* will not allocate anything, just sets its mode to ptr and thats it :) */ - sockaddr_storage remote_address{}; - iovec io_vector{}; - io_vector.iov_base = (void*) raw_read_buffer; - io_vector.iov_len = raw_read_buffer_length; + sockaddr_storage remote_address{}; + iovec io_vector{}; + io_vector.iov_base = (void*) raw_read_buffer; + io_vector.iov_len = raw_read_buffer_length; - char message_headers[0x100]; + char message_headers[0x100]; - msghdr message{}; + msghdr message{}; message.msg_name = &remote_address; message.msg_namelen = sizeof(remote_address); - message.msg_iov = &io_vector; - message.msg_iovlen = 1; - message.msg_control = message_headers; - message.msg_controllen = 0x100; + message.msg_iov = &io_vector; + message.msg_iovlen = 1; + message.msg_control = message_headers; + message.msg_controllen = 0x100; - auto read_timeout = system_clock::now() + microseconds(2500); /* read 2.5ms long at a time or 'till nothing more is there */ + auto read_timeout = system_clock::now() + microseconds(2500); /* read 2.5ms long at a time or 'till nothing more is there */ while(system_clock::now() <= read_timeout){ - message.msg_flags = 0; - bytes_read = recvmsg(fd, &message, 0); + message.msg_flags = 0; + bytes_read = recvmsg(fd, &message, 0); - if((message.msg_flags & MSG_TRUNC) > 0) - logError(ts_server->getServerId(), "Received truncated message from {}", net::to_string(remote_address)); + if((message.msg_flags & MSG_TRUNC) > 0) + logError(ts_server->getServerId(), "Received truncated message from {}", net::to_string(remote_address)); if(bytes_read < 0){ if(errno == EAGAIN) break; //Nothing more to read - logCritical(ts_server->getServerId(), "Could not receive datagram packet! Code: {} Reason: {}", errno, strerror(errno)); + logCritical(ts_server->getServerId(), "Could not receive datagram packet! Code: {} Reason: {}", errno, strerror(errno)); break; } else if(bytes_read == 0){ - //This should never happen + //This should never happen break; } if(bytes_read < MAC_SIZE + CLIENT_HEADER_SIZE) { - /* reenable for debug. else short packages could be a dos attach */ - //logError(ts_server->getServerId(), "Received an too short packet!"); + /* reenable for debug. else short packages could be a dos attach */ + //logError(ts_server->getServerId(), "Received an too short packet!"); continue; } shared_ptr client; { - if(*(uint64_t*) raw_read_buffer == TS3INIT.integral) { - //Handle ddos protection... - voice_server->pow_handler->handle_datagram(event_handle->socket_id, remote_address, message, read_buffer.view(0, bytes_read)); - } else { - auto client_id = (ClientId) be2le16(&raw_read_buffer[10]); - if(client_id > 0) { - client = dynamic_pointer_cast(voice_server->server->findClient(client_id)); - } else { - client = voice_server->findClient(&remote_address, true); - } - } + if(*(uint64_t*) raw_read_buffer == TS3INIT.integral) { + //Handle ddos protection... + voice_server->pow_handler->handle_datagram(event_handle->socket_id, remote_address, message, read_buffer.view(0, bytes_read)); + } else { + auto client_id = (ClientId) be2le16(&raw_read_buffer[10]); + if(client_id > 0) { + client = dynamic_pointer_cast(voice_server->server->findClient(client_id)); + } else { + client = voice_server->findClient(&remote_address, true); + } + } } if(!client) - continue; + continue; if(memcmp(&client->remote_address, &remote_address, sizeof(sockaddr_storage)) != 0) { /* verify the remote address */ - if((read_buffer[12] & 0x80) == 0 && client->state == ConnectionState::CONNECTED) { /* only encrypted packets are allowed */ - if(client->connection->verify_encryption(read_buffer.view(0, bytes_read))) { /* the ip had changed */ - auto old_address = net::to_string(client->remote_address); - auto new_address = net::to_string(remote_address); + if((read_buffer[12] & 0x80) == 0 && client->state == ConnectionState::CONNECTED) { /* only encrypted packets are allowed */ + if(client->connection->verify_encryption(read_buffer.view(0, bytes_read))) { /* the ip had changed */ + auto old_address = net::to_string(client->remote_address); + auto new_address = net::to_string(remote_address); - auto command = "dummy_ipchange old_ip=" + old_address + " new_ip=" + new_address; - auto packet = make_unique(protocol::PacketTypeInfo::Command, pipes::buffer_view{command.data(), command.length()}); - packet->memory_state.id_branded = false; - packet->applyPacketId(0, 0); + auto command = "dummy_ipchange old_ip=" + old_address + " new_ip=" + new_address; + auto packet = make_unique(protocol::PacketTypeInfo::Command, pipes::buffer_view{command.data(), command.length()}); + packet->memory_state.id_branded = false; + packet->applyPacketId(0, 0); - { - auto& buffer = client->connection->packet_buffers()[protocol::COMMAND]; - unique_lock buffer_lock(buffer.buffer_lock); - buffer.push_front(move(packet)); - } + { + auto& buffer = client->connection->packet_buffers()[protocol::COMMAND]; + unique_lock buffer_lock(buffer.buffer_lock); + buffer.push_front(move(packet)); + } - memcpy(&client->remote_address, &remote_address, sizeof(remote_address)); - io::DatagramPacket::extract_info(message, client->address_info); - } - } else { - continue; /* we've no clue */ - } + memcpy(&client->remote_address, &remote_address, sizeof(remote_address)); + io::DatagramPacket::extract_info(message, client->address_info); + } + } else { + continue; /* we've no clue */ + } } if(client->state != ConnectionState::DISCONNECTED){ #ifdef VC_USE_READ_QUEUE { - lock_guard lock(client->connection->queueLock); - client->connection->readQueue.push_back(read_buffer.view(0, readBytes).dup(buffer::allocate_buffer(readBytes))); + lock_guard lock(client->connection->queueLock); + client->connection->readQueue.push_back(read_buffer.view(0, readBytes).dup(buffer::allocate_buffer(readBytes))); } while(client->state != ConnectionState::DISCONNECTED && client->connection->handleNextDatagram()); #else - client->connection->handleDatagramReceived(read_buffer.view(0, bytes_read)); + client->connection->handleDatagramReceived(read_buffer.view(0, bytes_read)); #endif client = nullptr; } @@ -382,218 +382,218 @@ void VoiceServer::handleMessageRead(int fd, short events, void *_event_handle) { } #ifndef USE_TIMER - #ifdef ALARM_TIMER - #undef ALARM_TIMER - #endif - #define ALARM_TIMER(...) + #ifdef ALARM_TIMER + #undef ALARM_TIMER + #endif + #define ALARM_TIMER(...) #endif template struct IOData { - int file_descriptor = 0; - iovec vector{}; - struct msghdr message{}; - char message_headers[MHS]; + int file_descriptor = 0; + iovec vector{}; + struct msghdr message{}; + char message_headers[MHS]; - IOData() { - /* Speed is key here, we dont need to zero paddings! - memset(&this->vector, 0, sizeof(this->vector)); - memset(&this->message, 0, sizeof(this->message)); - memset(this->message_headers, 0, sizeof(this->message_headers)); - */ + IOData() { + /* Speed is key here, we dont need to zero paddings! + memset(&this->vector, 0, sizeof(this->vector)); + memset(&this->message, 0, sizeof(this->message)); + memset(this->message_headers, 0, sizeof(this->message_headers)); + */ - this->vector.iov_base = nullptr; - this->vector.iov_len = 0; + this->vector.iov_base = nullptr; + this->vector.iov_len = 0; - this->message.msg_name = nullptr; - this->message.msg_namelen = 0; + this->message.msg_name = nullptr; + this->message.msg_namelen = 0; - this->message.msg_iov = &vector; - this->message.msg_iovlen = 1; + this->message.msg_iov = &vector; + this->message.msg_iovlen = 1; - this->message.msg_control = this->message_headers; - this->message.msg_controllen = sizeof(this->message_headers); - } + this->message.msg_control = this->message_headers; + this->message.msg_controllen = sizeof(this->message_headers); + } }; template inline ssize_t write_datagram(IOData& io, const sockaddr_storage& address, io::pktinfo_storage* info, size_t length, void* buffer) { - io.message.msg_flags = 0; - io.message.msg_name = (void*) &address; - io.message.msg_namelen = address.ss_family == AF_INET ? sizeof(sockaddr_in) : sizeof(sockaddr_in6); + io.message.msg_flags = 0; + io.message.msg_name = (void*) &address; + io.message.msg_namelen = address.ss_family == AF_INET ? sizeof(sockaddr_in) : sizeof(sockaddr_in6); - io.vector.iov_len = length; - io.vector.iov_base = buffer; + io.vector.iov_len = length; + io.vector.iov_base = buffer; - if(info) { - auto cmsg = CMSG_FIRSTHDR(&io.message); - if(address.ss_family == AF_INET) { - cmsg->cmsg_level = IPPROTO_IP; - cmsg->cmsg_type = IP_PKTINFO; - cmsg->cmsg_len = CMSG_LEN(sizeof(in_pktinfo)); + if(info) { + auto cmsg = CMSG_FIRSTHDR(&io.message); + if(address.ss_family == AF_INET) { + cmsg->cmsg_level = IPPROTO_IP; + cmsg->cmsg_type = IP_PKTINFO; + cmsg->cmsg_len = CMSG_LEN(sizeof(in_pktinfo)); - memcpy(CMSG_DATA(cmsg), info, sizeof(in_pktinfo)); + memcpy(CMSG_DATA(cmsg), info, sizeof(in_pktinfo)); - io.message.msg_controllen = CMSG_SPACE(sizeof(in_pktinfo)); - } else if(address.ss_family == AF_INET6) { - cmsg->cmsg_level = IPPROTO_IPV6; - cmsg->cmsg_type = IPV6_PKTINFO; - cmsg->cmsg_len = CMSG_LEN(sizeof(in6_pktinfo)); + io.message.msg_controllen = CMSG_SPACE(sizeof(in_pktinfo)); + } else if(address.ss_family == AF_INET6) { + cmsg->cmsg_level = IPPROTO_IPV6; + cmsg->cmsg_type = IPV6_PKTINFO; + cmsg->cmsg_len = CMSG_LEN(sizeof(in6_pktinfo)); - memcpy(CMSG_DATA(cmsg), info, sizeof(in6_pktinfo)); + memcpy(CMSG_DATA(cmsg), info, sizeof(in6_pktinfo)); - io.message.msg_controllen = CMSG_SPACE(sizeof(in6_pktinfo)); - } else if(address.ss_family == 0) - return length; /* address is unset (testing ip loss i guess) */ - } else { - io.message.msg_controllen = 0; - } + io.message.msg_controllen = CMSG_SPACE(sizeof(in6_pktinfo)); + } else if(address.ss_family == 0) + return length; /* address is unset (testing ip loss i guess) */ + } else { + io.message.msg_controllen = 0; + } - auto status = sendmsg(io.file_descriptor, &io.message, 0); - if(status< 0 && errno == EINVAL) { - /* may something is wrong here */ - status = send(io.file_descriptor, buffer, length, 0); - if(status < 0) - return -0xFEB; - } - return status; + auto status = sendmsg(io.file_descriptor, &io.message, 0); + if(status< 0 && errno == EINVAL) { + /* may something is wrong here */ + status = send(io.file_descriptor, buffer, length, 0); + if(status < 0) + return -0xFEB; + } + return status; } void VoiceServer::handleMessageWrite(int fd, short events, void *_event_handle) { - auto event_handle = (io::IOEventLoopEntry*) _event_handle; - auto voice_server = event_handle->voice_server; + auto event_handle = (io::IOEventLoopEntry*) _event_handle; + auto voice_server = event_handle->voice_server; - bool retrigger = false; - int buffer_state; + bool retrigger = false; + int buffer_state; - IOData<0x100> io{}; - io.file_descriptor = fd; + IOData<0x100> io{}; + io.file_descriptor = fd; - TIMING_START(timings); - TIMING_STEP(timings, "client"); - { /* write and process clients */ - shared_ptr client; - pipes::buffer buffer; - bool more_clients, more_to_prepare = false, more_to_write = false; + TIMING_START(timings); + TIMING_STEP(timings, "client"); + { /* write and process clients */ + shared_ptr client; + pipes::buffer buffer; + bool more_clients, more_to_prepare = false, more_to_write = false; - auto write_timeout = system_clock::now() + microseconds(2500); /* read 2.5ms long at a time or 'till nothing more is there */ - while(system_clock::now() <= write_timeout){ - if(!client) { - auto client_queue_state = event_handle->pop_voice_write_queue(client); /* we need a new client, the old client has nothing more to do */ - if(client_queue_state == 2) - break; - more_clients = (bool) client_queue_state; - } + auto write_timeout = system_clock::now() + microseconds(2500); /* read 2.5ms long at a time or 'till nothing more is there */ + while(system_clock::now() <= write_timeout){ + if(!client) { + auto client_queue_state = event_handle->pop_voice_write_queue(client); /* we need a new client, the old client has nothing more to do */ + if(client_queue_state == 2) + break; + more_clients = (bool) client_queue_state; + } - auto connection = client->connection; - auto client_ptr = &*client; + auto connection = client->connection; + auto client_ptr = &*client; - TIMING_STEP(timings, "client get"); - more_to_prepare = connection->preprocess_write_packets(); - TIMING_STEP(timings, "client prepare"); + TIMING_STEP(timings, "client get"); + more_to_prepare = connection->preprocess_write_packets(); + TIMING_STEP(timings, "client prepare"); - while(system_clock::now() <= write_timeout) { - buffer_state = connection->pop_write_buffer(buffer); - more_to_write = buffer_state == 1; + while(system_clock::now() <= write_timeout) { + buffer_state = connection->pop_write_buffer(buffer); + more_to_write = buffer_state == 1; - TIMING_STEP(timings, "buffer pop"); - if(buffer_state != 2) { - ssize_t res = write_datagram(io, client_ptr->remote_address, &client_ptr->address_info, buffer.length(), buffer.data_ptr()); - TIMING_STEP(timings, "buffer write"); - if(res != buffer.length()){ - if(errno == EAGAIN) { - logCritical(voice_server->server->getServerId(), "Failed to write datagram packet for client {} (EAGAIN).", client->getLoggingPeerIp() + ":" + to_string(client->getPeerPort())); - return; - } else if(errno == EINVAL || res == -0xFEB) { - /* needs more debug */ - auto voice_client = dynamic_pointer_cast(client); - logCritical( - voice_server->server->getServerId(), - "Failed to write datagram packet ({} @ {}) for client {} ({}) {}. Dropping packet! Extra data: [fd: {}/{}, supposed socket: {}/{} => {}, client family: {}, socket family: {}]", - buffer.length(), buffer.data_ptr(), - client->getLoggingPeerIp() + ":" + to_string(client->getPeerPort()), - strerror(errno), - res, - fd, - event_handle->file_descriptor, - voice_client->socket, - event_handle->socket_id, - voice_server->io->resolve_file_descriptor(voice_client), - voice_client->isAddressV4() ? "v4" : voice_client->isAddressV6() ? "v6" : "v?", - event_handle->family == AF_INET ? "v4" : "v6" - ); - } else { - logCritical( - voice_server->server->getServerId(), - "Failed to write datagram packet for client {} (errno: {} message: {}). Dropping packet!", - client->getLoggingPeerIp() + ":" + to_string(client->getPeerPort()), - errno, - strerror(errno) - ); - } - break; - } - } - if(!more_to_write) - break; /* client has no more datagram packets to write */ - } + TIMING_STEP(timings, "buffer pop"); + if(buffer_state != 2) { + ssize_t res = write_datagram(io, client_ptr->remote_address, &client_ptr->address_info, buffer.length(), buffer.data_ptr()); + TIMING_STEP(timings, "buffer write"); + if(res != buffer.length()){ + if(errno == EAGAIN) { + logCritical(voice_server->server->getServerId(), "Failed to write datagram packet for client {} (EAGAIN).", client->getLoggingPeerIp() + ":" + to_string(client->getPeerPort())); + return; + } else if(errno == EINVAL || res == -0xFEB) { + /* needs more debug */ + auto voice_client = dynamic_pointer_cast(client); + logCritical( + voice_server->server->getServerId(), + "Failed to write datagram packet ({} @ {}) for client {} ({}) {}. Dropping packet! Extra data: [fd: {}/{}, supposed socket: {}/{} => {}, client family: {}, socket family: {}]", + buffer.length(), buffer.data_ptr(), + client->getLoggingPeerIp() + ":" + to_string(client->getPeerPort()), + strerror(errno), + res, + fd, + event_handle->file_descriptor, + voice_client->socket, + event_handle->socket_id, + voice_server->io->resolve_file_descriptor(voice_client), + voice_client->isAddressV4() ? "v4" : voice_client->isAddressV6() ? "v6" : "v?", + event_handle->family == AF_INET ? "v4" : "v6" + ); + } else { + logCritical( + voice_server->server->getServerId(), + "Failed to write datagram packet for client {} (errno: {} message: {}). Dropping packet!", + client->getLoggingPeerIp() + ":" + to_string(client->getPeerPort()), + errno, + strerror(errno) + ); + } + break; + } + } + if(!more_to_write) + break; /* client has no more datagram packets to write */ + } - if(more_to_write) { - /* we exceeded the max write time, rescheduling write */ - more_to_prepare = false; /* we'll call this with the next write */ - voice_server->triggerWrite(client); - client.reset(); - TIMING_STEP(timings, "retrigger client"); - } + if(more_to_write) { + /* we exceeded the max write time, rescheduling write */ + more_to_prepare = false; /* we'll call this with the next write */ + voice_server->triggerWrite(client); + client.reset(); + TIMING_STEP(timings, "retrigger client"); + } - if(more_clients) { - /* allow other clients to write as well */ - if(more_to_write) - event_handle->push_voice_write_queue(client); - client.reset(); - continue; - } + if(more_clients) { + /* allow other clients to write as well */ + if(more_to_write) + event_handle->push_voice_write_queue(client); + client.reset(); + continue; + } - if(!more_to_prepare) { - /* we're done with this client. Nothing more to prepare */ - client.reset(); - } - } + if(!more_to_prepare) { + /* we're done with this client. Nothing more to prepare */ + client.reset(); + } + } - retrigger |= more_to_prepare || more_to_write; + retrigger |= more_to_prepare || more_to_write; - } - TIMING_STEP(timings, "client-full-end"); - /* write all manually specified datagram packets */ - { - auto write_timeout = system_clock::now() + microseconds(2500); /* read 2.5ms long at a time or 'till nothing more is there */ - io::datagram_packet_t packet; + } + TIMING_STEP(timings, "client-full-end"); + /* write all manually specified datagram packets */ + { + auto write_timeout = system_clock::now() + microseconds(2500); /* read 2.5ms long at a time or 'till nothing more is there */ + io::datagram_packet_t packet; - while(system_clock::now() <= write_timeout && (packet = event_handle->pop_dg_write_queue())) { - ssize_t res = write_datagram(io, packet->address, &packet->address_info, packet->data_length, packet->data); - if(res != packet->data_length) { - if(errno == EAGAIN) { - event_handle->push_dg_write_queue(packet); - } else - io::DatagramPacket::destory(packet); + while(system_clock::now() <= write_timeout && (packet = event_handle->pop_dg_write_queue())) { + ssize_t res = write_datagram(io, packet->address, &packet->address_info, packet->data_length, packet->data); + if(res != packet->data_length) { + if(errno == EAGAIN) { + event_handle->push_dg_write_queue(packet); + } else + io::DatagramPacket::destory(packet); - logError(voice_server->server->getServerId(), "Failed to send datagram. Wrote {} out of {}. {}/{}", res, packet->data_length, errno, strerror(errno)); - retrigger = false; - break; - } - io::DatagramPacket::destory(packet); - } + logError(voice_server->server->getServerId(), "Failed to send datagram. Wrote {} out of {}. {}/{}", res, packet->data_length, errno, strerror(errno)); + retrigger = false; + break; + } + io::DatagramPacket::destory(packet); + } - retrigger |= packet != nullptr; /* memory stored at packet is not accessible anymore. But anyways pop_dg_write_queue returns 0 if there is nothing more */ - } - TIMING_STEP(timings, "dgram-full-end"); - if(retrigger) - event_add(event_handle->event_write, nullptr); - TIMING_REPORT({ - debugMessage(0, "Write timings: {}", TIMING_FINISH_U(timings, microseconds, "mu")); - }); + retrigger |= packet != nullptr; /* memory stored at packet is not accessible anymore. But anyways pop_dg_write_queue returns 0 if there is nothing more */ + } + TIMING_STEP(timings, "dgram-full-end"); + if(retrigger) + event_add(event_handle->event_write, nullptr); + TIMING_REPORT({ + debugMessage(0, "Write timings: {}", TIMING_FINISH_U(timings, microseconds, "mu")); + }); } void VoiceServer::send_datagram(int socket, io::datagram_packet_t packet) { - this->io->send_datagram(packet, socket); + this->io->send_datagram(packet, socket); } \ No newline at end of file diff --git a/server/src/server/VoiceServer.h b/server/src/server/VoiceServer.h index 3e8a07e..af9018d 100644 --- a/server/src/server/VoiceServer.h +++ b/server/src/server/VoiceServer.h @@ -22,11 +22,11 @@ namespace ts { class POWHandler; struct VoiceServerBinding { - sockaddr_storage address{}; - int file_descriptor = 0; + sockaddr_storage address{}; + int file_descriptor = 0; - inline std::string address_string() { return net::to_string(address); } - inline uint16_t address_port() { return net::port(address); } + inline std::string address_string() { return net::to_string(address); } + inline uint16_t address_port() { return net::port(address); } }; class VoiceServer { @@ -41,9 +41,9 @@ namespace ts { bool start(const std::deque>&, std::string&); bool stop(const std::chrono::milliseconds& flushTimeout = std::chrono::milliseconds(1000)); - std::shared_ptr findClient(ClientId); + std::shared_ptr findClient(ClientId); std::shared_ptr findClient(sockaddr_in* addr, bool lock = true); - std::shared_ptr findClient(sockaddr_in6* addr, bool lock = true); + std::shared_ptr findClient(sockaddr_in6* addr, bool lock = true); inline std::shared_ptr findClient(sockaddr_storage* address, bool lock = true) { return address->ss_family == AF_INET ? this->findClient((sockaddr_in*) address, lock) : @@ -53,20 +53,20 @@ namespace ts { } bool unregisterConnection(std::shared_ptr); - inline std::deque> activeBindings() { - std::deque> result; - for(const auto& entry : this->bindings) - if(entry->file_descriptor > 0) result.push_back(entry); - return result; - } + inline std::deque> activeBindings() { + std::deque> result; + for(const auto& entry : this->bindings) + if(entry->file_descriptor > 0) result.push_back(entry); + return result; + } - inline std::shared_ptr get_server() { return this->server; } + inline std::shared_ptr get_server() { return this->server; } private: - std::unique_ptr pow_handler; - std::shared_ptr server = nullptr; + std::unique_ptr pow_handler; + std::shared_ptr server = nullptr; bool running = false; - std::deque> bindings; + std::deque> bindings; std::recursive_mutex connectionLock; std::deque> activeConnections; @@ -75,8 +75,8 @@ namespace ts { void schedule_execute(VoiceClient const *); void tickHandshakingClients(); - void execute_resend(const std::chrono::system_clock::time_point& /* now */, std::chrono::system_clock::time_point& /* next resend */); - void send_datagram(int /* socket */, io::datagram_packet_t /* packet */); + void execute_resend(const std::chrono::system_clock::time_point& /* now */, std::chrono::system_clock::time_point& /* next resend */); + void send_datagram(int /* socket */, io::datagram_packet_t /* packet */); std::shared_ptr io; private: @@ -85,9 +85,9 @@ namespace ts { /* execute loop */ /* TODO - std::mutex execute_list_lock; - protocol::RingBuffer execute_list; - void run_execute_clients(); + std::mutex execute_list_lock; + protocol::RingBuffer execute_list; + void run_execute_clients(); */ }; } diff --git a/server/src/server/WebIoManager.cpp b/server/src/server/WebIoManager.cpp index ac94c7a..fa7ff20 100644 --- a/server/src/server/WebIoManager.cpp +++ b/server/src/server/WebIoManager.cpp @@ -6,48 +6,48 @@ using namespace std; using namespace ts::webio; void EventLoop::invoker_loop() { - const timeval tick_timeout{10, 0}; - const auto original_base = this->loop; - while(this->loop == original_base) { - event_base_loopexit(original_base, &tick_timeout); - event_base_dispatch(original_base); - } - debugMessage(LOG_GENERAL, "Event base dispatch of {} ended.", (void*) original_base); + const timeval tick_timeout{10, 0}; + const auto original_base = this->loop; + while(this->loop == original_base) { + event_base_loopexit(original_base, &tick_timeout); + event_base_dispatch(original_base); + } + debugMessage(LOG_GENERAL, "Event base dispatch of {} ended.", (void*) original_base); } LoopManager::LoopManager() { - lock_guard lock(this->loop_lock); - for(int i = 0; i < config::threads::web::io_loops; i++) { - auto loop = make_shared(); - loop->loop = event_base_new(); - loop->invoker = make_unique(&EventLoop::invoker_loop, loop.get()); + lock_guard lock(this->loop_lock); + for(int i = 0; i < config::threads::web::io_loops; i++) { + auto loop = make_shared(); + loop->loop = event_base_new(); + loop->invoker = make_unique(&EventLoop::invoker_loop, loop.get()); - pthread_setname_np(loop->invoker->native_handle(), ("Web IO #" + to_string(i)).c_str()); + pthread_setname_np(loop->invoker->native_handle(), ("Web IO #" + to_string(i)).c_str()); - this->loops.push_back(move(loop)); - } + this->loops.push_back(move(loop)); + } } LoopManager::~LoopManager() { - lock_guard lock(this->loop_lock); - for(const auto& loop : this->loops) { - const auto base = loop->loop; - loop->loop = nullptr; + lock_guard lock(this->loop_lock); + for(const auto& loop : this->loops) { + const auto base = loop->loop; + loop->loop = nullptr; - const timeval timeout_now{0, 1}; - event_base_loopexit(base, &timeout_now); + const timeval timeout_now{0, 1}; + event_base_loopexit(base, &timeout_now); - if(loop->invoker && loop->invoker->joinable()) - loop->invoker->join(); + if(loop->invoker && loop->invoker->joinable()) + loop->invoker->join(); - event_base_free(base); - } + event_base_free(base); + } } std::shared_ptr LoopManager::next_loop() { - lock_guard lock(this->loop_lock); - if(this->loops.empty()) - return nullptr; + lock_guard lock(this->loop_lock); + if(this->loops.empty()) + return nullptr; - return this->loops[this->loop_index++ % this->loops.size()]; + return this->loops[this->loop_index++ % this->loops.size()]; } \ No newline at end of file diff --git a/server/src/server/WebIoManager.h b/server/src/server/WebIoManager.h index 8162dd9..2e8ec54 100644 --- a/server/src/server/WebIoManager.h +++ b/server/src/server/WebIoManager.h @@ -5,27 +5,27 @@ #include namespace ts { - namespace webio { - class LoopManager; - struct EventLoop { - friend class LoopManager; - public: - event_base* loop; - std::unique_ptr invoker; + namespace webio { + class LoopManager; + struct EventLoop { + friend class LoopManager; + public: + event_base* loop; + std::unique_ptr invoker; - private: - void invoker_loop(); - }; - class LoopManager { - public: - LoopManager(); - ~LoopManager(); + private: + void invoker_loop(); + }; + class LoopManager { + public: + LoopManager(); + ~LoopManager(); - std::shared_ptr next_loop(); - private: - std::atomic loop_index{0}; - std::mutex loop_lock; - std::deque> loops; - }; - } + std::shared_ptr next_loop(); + private: + std::atomic loop_index{0}; + std::mutex loop_lock; + std::deque> loops; + }; + } } \ No newline at end of file diff --git a/server/src/server/WebServer.cpp b/server/src/server/WebServer.cpp index 524ee52..ce347b4 100644 --- a/server/src/server/WebServer.cpp +++ b/server/src/server/WebServer.cpp @@ -19,167 +19,167 @@ WebControlServer::WebControlServer(const std::shared_ptr& handle) : ha WebControlServer::~WebControlServer() = default; bool WebControlServer::start(const std::deque>& bindings, std::string& error) { - if(this->running()) { - error = "server already running"; - return false; - } - this->_running = true; + if(this->running()) { + error = "server already running"; + return false; + } + this->_running = true; - /* reserve backup file descriptor in case that the max file descriptors have been reached */ - { - this->server_reserve_fd = dup(1); - if(this->server_reserve_fd < 0) - logWarning(this->handle->getServerId(), "Failed to reserve a backup accept file descriptor. ({} | {})", errno, strerror(errno)); - } + /* reserve backup file descriptor in case that the max file descriptors have been reached */ + { + this->server_reserve_fd = dup(1); + if(this->server_reserve_fd < 0) + logWarning(this->handle->getServerId(), "Failed to reserve a backup accept file descriptor. ({} | {})", errno, strerror(errno)); + } - { - for(auto& binding : bindings) { - binding->file_descriptor = socket(binding->address.ss_family, SOCK_STREAM | SOCK_NONBLOCK, 0); - if(binding->file_descriptor < 0) { - logError(this->handle->getServerId(), "[Web] Failed to bind server to {}. (Failed to create socket: {} | {})", binding->as_string(), errno, strerror(errno)); - continue; - } + { + for(auto& binding : bindings) { + binding->file_descriptor = socket(binding->address.ss_family, SOCK_STREAM | SOCK_NONBLOCK, 0); + if(binding->file_descriptor < 0) { + logError(this->handle->getServerId(), "[Web] Failed to bind server to {}. (Failed to create socket: {} | {})", binding->as_string(), errno, strerror(errno)); + continue; + } - int enable = 1, disabled = 0; + int enable = 1, disabled = 0; - if (setsockopt(binding->file_descriptor, SOL_SOCKET, SO_REUSEADDR, &enable, sizeof(int)) < 0) - logWarning(this->handle->getServerId(), "[Web] Failed to activate SO_REUSEADDR for binding {} ({} | {})", binding->as_string(), errno, strerror(errno)); - if(setsockopt(binding->file_descriptor, IPPROTO_TCP, TCP_NOPUSH, &disabled, sizeof disabled) < 0) - logWarning(this->handle->getServerId(), "[Web] Failed to deactivate TCP_NOPUSH for binding {} ({} | {})", binding->as_string(), errno, strerror(errno)); - if(binding->address.ss_family == AF_INET6) { - if(setsockopt(binding->file_descriptor, IPPROTO_IPV6, IPV6_V6ONLY, &enable, sizeof(int)) < 0) - logWarning(this->handle->getServerId(), "[Web] Failed to activate IPV6_V6ONLY for IPv6 binding {} ({} | {})", binding->as_string(), errno, strerror(errno)); - } - if(fcntl(binding->file_descriptor, F_SETFD, FD_CLOEXEC) < 0) - logWarning(this->handle->getServerId(), "[Web] Failed to set flag FD_CLOEXEC for binding {} ({} | {})", binding->as_string(), errno, strerror(errno)); + if (setsockopt(binding->file_descriptor, SOL_SOCKET, SO_REUSEADDR, &enable, sizeof(int)) < 0) + logWarning(this->handle->getServerId(), "[Web] Failed to activate SO_REUSEADDR for binding {} ({} | {})", binding->as_string(), errno, strerror(errno)); + if(setsockopt(binding->file_descriptor, IPPROTO_TCP, TCP_NOPUSH, &disabled, sizeof disabled) < 0) + logWarning(this->handle->getServerId(), "[Web] Failed to deactivate TCP_NOPUSH for binding {} ({} | {})", binding->as_string(), errno, strerror(errno)); + if(binding->address.ss_family == AF_INET6) { + if(setsockopt(binding->file_descriptor, IPPROTO_IPV6, IPV6_V6ONLY, &enable, sizeof(int)) < 0) + logWarning(this->handle->getServerId(), "[Web] Failed to activate IPV6_V6ONLY for IPv6 binding {} ({} | {})", binding->as_string(), errno, strerror(errno)); + } + if(fcntl(binding->file_descriptor, F_SETFD, FD_CLOEXEC) < 0) + logWarning(this->handle->getServerId(), "[Web] Failed to set flag FD_CLOEXEC for binding {} ({} | {})", binding->as_string(), errno, strerror(errno)); - if (bind(binding->file_descriptor, (struct sockaddr *) &binding->address, sizeof(binding->address)) < 0) { - logError(this->handle->getServerId(), "[Web] Failed to bind server to {}. (Failed to bind socket: {} | {})", binding->as_string(), errno, strerror(errno)); - close(binding->file_descriptor); - continue; - } + if (bind(binding->file_descriptor, (struct sockaddr *) &binding->address, sizeof(binding->address)) < 0) { + logError(this->handle->getServerId(), "[Web] Failed to bind server to {}. (Failed to bind socket: {} | {})", binding->as_string(), errno, strerror(errno)); + close(binding->file_descriptor); + continue; + } - if (listen(binding->file_descriptor, SOMAXCONN) < 0) { - logError(this->handle->getServerId(), "[Web] Failed to bind server to {}. (Failed to listen: {} | {})", binding->as_string(), errno, strerror(errno)); - close(binding->file_descriptor); - continue; - } + if (listen(binding->file_descriptor, SOMAXCONN) < 0) { + logError(this->handle->getServerId(), "[Web] Failed to bind server to {}. (Failed to listen: {} | {})", binding->as_string(), errno, strerror(errno)); + close(binding->file_descriptor); + continue; + } - auto io_base = serverInstance->getWebIoLoop()->next_loop(); - assert(io_base); - binding->event_accept = event_new(io_base->loop, binding->file_descriptor, EV_READ | EV_PERSIST, [](int a, short b, void* c){ ((WebControlServer *) c)->on_client_receive(a, b, c); }, this); - event_add(binding->event_accept, nullptr); - this->bindings.push_back(binding); - } + auto io_base = serverInstance->getWebIoLoop()->next_loop(); + assert(io_base); + binding->event_accept = event_new(io_base->loop, binding->file_descriptor, EV_READ | EV_PERSIST, [](int a, short b, void* c){ ((WebControlServer *) c)->on_client_receive(a, b, c); }, this); + event_add(binding->event_accept, nullptr); + this->bindings.push_back(binding); + } - if(this->bindings.empty()) { - this->stop(); - error = "failed to bind to any address"; - return false; - } - } - return true; + if(this->bindings.empty()) { + this->stop(); + error = "failed to bind to any address"; + return false; + } + } + return true; } #define CLOSE_CONNECTION \ if(shutdown(file_descriptor, SHUT_RDWR) < 0) { \ - debugMessage(LOG_FT, "[{}] Failed to shutdown socket ({} | {}).", logging_address(remote_address), errno, strerror(errno)); \ + debugMessage(LOG_FT, "[{}] Failed to shutdown socket ({} | {}).", logging_address(remote_address), errno, strerror(errno)); \ } \ if(close(file_descriptor) < 0) { \ - debugMessage(LOG_FT, "[{}] Failed to close socket ({} | {}).", logging_address(remote_address), errno, strerror(errno)); \ + debugMessage(LOG_FT, "[{}] Failed to close socket ({} | {}).", logging_address(remote_address), errno, strerror(errno)); \ } inline std::string logging_address(const sockaddr_storage& address) { - if(config::server::disable_ip_saving) - return "X.X.X.X" + to_string(net::port(address)); - return net::to_string(address, true); + if(config::server::disable_ip_saving) + return "X.X.X.X" + to_string(net::port(address)); + return net::to_string(address, true); } void WebControlServer::on_client_receive(int _server_file_descriptor, short ev, void *arg) { - sockaddr_storage remote_address{}; - memset(&remote_address, 0, sizeof(remote_address)); - socklen_t address_length = sizeof(remote_address); + sockaddr_storage remote_address{}; + memset(&remote_address, 0, sizeof(remote_address)); + socklen_t address_length = sizeof(remote_address); - int file_descriptor = accept(_server_file_descriptor, (struct sockaddr *) &remote_address, &address_length); - if (file_descriptor < 0) { - if(errno == EAGAIN) - return; + int file_descriptor = accept(_server_file_descriptor, (struct sockaddr *) &remote_address, &address_length); + if (file_descriptor < 0) { + if(errno == EAGAIN) + return; - if(errno == EMFILE || errno == ENFILE) { - if(errno == EMFILE) - logError(this->handle->getServerId(), "[Web] Server ran out file descriptors. Please increase the process file descriptor limit."); - else - logError(this->handle->getServerId(), "[Web] Server ran out file descriptors. Please increase the process and system-wide file descriptor limit."); + if(errno == EMFILE || errno == ENFILE) { + if(errno == EMFILE) + logError(this->handle->getServerId(), "[Web] Server ran out file descriptors. Please increase the process file descriptor limit."); + else + logError(this->handle->getServerId(), "[Web] Server ran out file descriptors. Please increase the process and system-wide file descriptor limit."); - bool tmp_close_success = false; - { - lock_guard reserve_fd_lock(server_reserve_fd_lock); - if(this->server_reserve_fd > 0) { - debugMessage(this->handle->getServerId(), "[Web] Trying to accept client with the reserved file descriptor to close the incomming connection."); - auto _ = [&]{ - if(close(this->server_reserve_fd) < 0) { - debugMessage(this->handle->getServerId(), "[Web] Failed to close reserved file descriptor"); - tmp_close_success = false; - return; - } - this->server_reserve_fd = 0; + bool tmp_close_success = false; + { + lock_guard reserve_fd_lock(server_reserve_fd_lock); + if(this->server_reserve_fd > 0) { + debugMessage(this->handle->getServerId(), "[Web] Trying to accept client with the reserved file descriptor to close the incomming connection."); + auto _ = [&]{ + if(close(this->server_reserve_fd) < 0) { + debugMessage(this->handle->getServerId(), "[Web] Failed to close reserved file descriptor"); + tmp_close_success = false; + return; + } + this->server_reserve_fd = 0; - errno = 0; - file_descriptor = accept(_server_file_descriptor, (struct sockaddr *) &remote_address, &address_length); - if(file_descriptor < 0) { - if(errno == EMFILE || errno == ENFILE) - debugMessage(this->handle->getServerId(), "[Web] [{}] Even with freeing the reserved descriptor accept failed. Attempting to reclaim reserved file descriptor", logging_address(remote_address)); - else if(errno == EAGAIN); - else { - debugMessage(this->handle->getServerId(), "[Web] [{}] Failed to accept client with reserved file descriptor. ({} | {})", logging_address(remote_address), errno, strerror(errno)); - } - this->server_reserve_fd = dup(1); - if(this->server_reserve_fd < 0) - debugMessage(this->handle->getServerId(), "[Web] [{}] Failed to reclaim reserved file descriptor. Future clients cant be accepted!", logging_address(remote_address)); - else - tmp_close_success = true; - return; - } - debugMessage(this->handle->getServerId(), "[Web] [{}] Successfully accepted client via reserved descriptor (fd: {}). Disconnecting client.", logging_address(remote_address), file_descriptor); + errno = 0; + file_descriptor = accept(_server_file_descriptor, (struct sockaddr *) &remote_address, &address_length); + if(file_descriptor < 0) { + if(errno == EMFILE || errno == ENFILE) + debugMessage(this->handle->getServerId(), "[Web] [{}] Even with freeing the reserved descriptor accept failed. Attempting to reclaim reserved file descriptor", logging_address(remote_address)); + else if(errno == EAGAIN); + else { + debugMessage(this->handle->getServerId(), "[Web] [{}] Failed to accept client with reserved file descriptor. ({} | {})", logging_address(remote_address), errno, strerror(errno)); + } + this->server_reserve_fd = dup(1); + if(this->server_reserve_fd < 0) + debugMessage(this->handle->getServerId(), "[Web] [{}] Failed to reclaim reserved file descriptor. Future clients cant be accepted!", logging_address(remote_address)); + else + tmp_close_success = true; + return; + } + debugMessage(this->handle->getServerId(), "[Web] [{}] Successfully accepted client via reserved descriptor (fd: {}). Disconnecting client.", logging_address(remote_address), file_descriptor); - CLOSE_CONNECTION - this->server_reserve_fd = dup(1); - if(this->server_reserve_fd < 0) - debugMessage(this->handle->getServerId(), "[Web] Failed to reclaim reserved file descriptor. Future clients cant be accepted!"); - else - tmp_close_success = true; - logMessage(this->handle->getServerId(), "[Web] [{}] Dropping file transfer connection attempt because of too many open file descriptors.", logging_address(remote_address)); - }; - _(); - } - } + CLOSE_CONNECTION + this->server_reserve_fd = dup(1); + if(this->server_reserve_fd < 0) + debugMessage(this->handle->getServerId(), "[Web] Failed to reclaim reserved file descriptor. Future clients cant be accepted!"); + else + tmp_close_success = true; + logMessage(this->handle->getServerId(), "[Web] [{}] Dropping file transfer connection attempt because of too many open file descriptors.", logging_address(remote_address)); + }; + _(); + } + } - if(!tmp_close_success) { - debugMessage(this->handle->getServerId(), "[Web] Sleeping two seconds because we're currently having no resources for this user. (Removing the accept event)"); - for(auto& binding : this->bindings) - event_del_noblock(binding->event_accept); - accept_event_deleted = system_clock::now(); - return; - } - return; - } - logMessage(this->handle->getServerId(), "[Web] Got an error while accepting a new client. (errno: {}, message: {})", errno, strerror(errno)); - return; - } + if(!tmp_close_success) { + debugMessage(this->handle->getServerId(), "[Web] Sleeping two seconds because we're currently having no resources for this user. (Removing the accept event)"); + for(auto& binding : this->bindings) + event_del_noblock(binding->event_accept); + accept_event_deleted = system_clock::now(); + return; + } + return; + } + logMessage(this->handle->getServerId(), "[Web] Got an error while accepting a new client. (errno: {}, message: {})", errno, strerror(errno)); + return; + } - shared_ptr client = std::make_shared(this, file_descriptor); - memcpy(&client->remote_address, &remote_address, sizeof(remote_address)); - client->applySelfLock(client); - client->initialize(); + shared_ptr client = std::make_shared(this, file_descriptor); + memcpy(&client->remote_address, &remote_address, sizeof(remote_address)); + client->applySelfLock(client); + client->initialize(); - this->clientLock.lock(); - this->clients.push_back(client); - this->clientLock.unlock(); - event_add(client->readEvent, nullptr); - logMessage(this->handle->getServerId(), "[Web] Got new client from {}:{}", client->getLoggingPeerIp(), client->getPeerPort()); + this->clientLock.lock(); + this->clients.push_back(client); + this->clientLock.unlock(); + event_add(client->readEvent, nullptr); + logMessage(this->handle->getServerId(), "[Web] Got new client from {}:{}", client->getLoggingPeerIp(), client->getPeerPort()); } void WebControlServer::stop() { @@ -189,31 +189,31 @@ void WebControlServer::stop() { this->clientLock.unlock(); for(const auto& e : clList) { - e->closeConnection(system_clock::now()); + e->closeConnection(system_clock::now()); } - for(auto& binding : this->bindings) { - if(binding->event_accept) { - event_del_block(binding->event_accept); - event_free(binding->event_accept); - binding->event_accept = nullptr; - } - if(binding->file_descriptor > 0) { - if(shutdown(binding->file_descriptor, SHUT_RDWR) < 0) - logWarning(this->handle->getServerId(), "[Web] Failed to shutdown socket for binding {} ({} | {}).", binding->as_string(), errno, strerror(errno)); - if(close(binding->file_descriptor) < 0) - logError(this->handle->getServerId(), "[Web] Failed to close socket for binding {} ({} | {}).", binding->as_string(), errno, strerror(errno)); - binding->file_descriptor = -1; - } - } - this->bindings.clear(); + for(auto& binding : this->bindings) { + if(binding->event_accept) { + event_del_block(binding->event_accept); + event_free(binding->event_accept); + binding->event_accept = nullptr; + } + if(binding->file_descriptor > 0) { + if(shutdown(binding->file_descriptor, SHUT_RDWR) < 0) + logWarning(this->handle->getServerId(), "[Web] Failed to shutdown socket for binding {} ({} | {}).", binding->as_string(), errno, strerror(errno)); + if(close(binding->file_descriptor) < 0) + logError(this->handle->getServerId(), "[Web] Failed to close socket for binding {} ({} | {}).", binding->as_string(), errno, strerror(errno)); + binding->file_descriptor = -1; + } + } + this->bindings.clear(); - if(this->server_reserve_fd > 0) { - if(close(this->server_reserve_fd) < 0) - logError(this->handle->getServerId(), "[Web] Failed to close backup file descriptor ({} | {})", errno, strerror(errno)); - } - this->server_reserve_fd = -1; + if(this->server_reserve_fd > 0) { + if(close(this->server_reserve_fd) < 0) + logError(this->handle->getServerId(), "[Web] Failed to close backup file descriptor ({} | {})", errno, strerror(errno)); + } + this->server_reserve_fd = -1; } void WebControlServer::unregisterConnection(const std::shared_ptr& connection) { diff --git a/server/src/server/WebServer.h b/server/src/server/WebServer.h index 92f5fae..6ecb629 100644 --- a/server/src/server/WebServer.h +++ b/server/src/server/WebServer.h @@ -13,19 +13,19 @@ namespace ts { class WebControlServer { friend class WebClient; public: - struct Binding { - sockaddr_storage address{}; - int file_descriptor = 0; - ::event* event_accept = nullptr; + struct Binding { + sockaddr_storage address{}; + int file_descriptor = 0; + ::event* event_accept = nullptr; - inline std::string as_string() { return net::to_string(address, true); } - }; + inline std::string as_string() { return net::to_string(address, true); } + }; explicit WebControlServer(const std::shared_ptr&); ~WebControlServer(); - bool start(const std::deque>& /* bindings */, std::string& /* error */); - inline bool running(){ return _running; } + bool start(const std::deque>& /* bindings */, std::string& /* error */); + inline bool running(){ return _running; } void stop(); std::shared_ptr getTS(){ return this->handle; } @@ -42,15 +42,15 @@ namespace ts { std::deque> clients; bool _running = false; - std::deque> bindings; + std::deque> bindings; - std::mutex server_reserve_fd_lock; - int server_reserve_fd = -1; /* -1 = unset | 0 = in use | > 0 ready to use */ + std::mutex server_reserve_fd_lock; + int server_reserve_fd = -1; /* -1 = unset | 0 = in use | > 0 ready to use */ - //IO stuff - std::chrono::system_clock::time_point accept_event_deleted; + //IO stuff + std::chrono::system_clock::time_point accept_event_deleted; private: - void on_client_receive(int fd, short ev, void *arg); + void on_client_receive(int fd, short ev, void *arg); void unregisterConnection(const std::shared_ptr&); }; } diff --git a/server/src/server/file/FileServer.cpp b/server/src/server/file/FileServer.cpp index f2ec735..d288d50 100644 --- a/server/src/server/file/FileServer.cpp +++ b/server/src/server/file/FileServer.cpp @@ -52,18 +52,18 @@ bool FileServer::fileExists(std::shared_ptr file) { } std::shared_ptr FileServer::findFile(std::string path, std::shared_ptr parent) { - if(path.find(this->rootPath) != 0) { - string strPath = (parent ? parent->path + "/" + parent->name : this->rootPath) + "/"; - if(strPath.find(this->rootPath) != 0 && rootPath != strPath) - path = this->rootPath + strPath; - if(path.find(strPath) == 0 && !strPath.empty()) - ; - else path = strPath + path; - } + if(path.find(this->rootPath) != 0) { + string strPath = (parent ? parent->path + "/" + parent->name : this->rootPath) + "/"; + if(strPath.find(this->rootPath) != 0 && rootPath != strPath) + path = this->rootPath + strPath; + if(path.find(strPath) == 0 && !strPath.empty()) + ; + else path = strPath + path; + } fs::path absPath = fs::u8path(path); if(!fs::is_regular_file(absPath) && !fs::is_directory(absPath)){ - debugMessage(LOG_FT, "Could not find requested file. Abs path: {} | {}. (path={}, parent={})", absPath.string(), path, path, (parent ? parent->path + "/" + parent->name : "./")); + debugMessage(LOG_FT, "Could not find requested file. Abs path: {} | {}. (path={}, parent={})", absPath.string(), path, path, (parent ? parent->path + "/" + parent->name : "./")); return nullptr; } @@ -89,13 +89,13 @@ std::vector> FileServer::listFiles(std::shared_ if(!fs::is_directory(directory)) return {}; std::vector> result; - deque files; + deque files; for(const auto& elm : fs::directory_iterator(directory)) { files.push_back(elm); } - std::sort(files.begin(), files.end(), [](const fs::directory_entry& a, const fs::directory_entry& b) { - return fs::last_write_time(a.path()) > fs::last_write_time(b.path()); - }); + std::sort(files.begin(), files.end(), [](const fs::directory_entry& a, const fs::directory_entry& b) { + return fs::last_write_time(a.path()) > fs::last_write_time(b.path()); + }); for(const auto& elm : files) { if(fs::is_regular_file(elm.path())){ auto entry = make_shared(); @@ -144,10 +144,10 @@ std::shared_ptr FileServer::generateDownloadTransferKey( } auto result = make_shared(); - result->owner = client; - result->server = client->getServerId(); + result->owner = client; + result->server = client->getServerId(); - result->key_id = (uint16_t) (rand() & 0xFFFF); + result->key_id = (uint16_t) (rand() & 0xFFFF); result->createTimestamp = std::chrono::system_clock::now(); result->key = randomString(16); result->offset = offset; @@ -155,21 +155,21 @@ std::shared_ptr FileServer::generateDownloadTransferKey( result->upload = false; result->targetFile = targetFile; - { //Test for server? - auto bandwidth_server = client->getServer()->properties()[property::VIRTUALSERVER_MAX_UPLOAD_TOTAL_BANDWIDTH].as(); - auto bandwidth_instance = serverInstance->properties()[property::SERVERINSTANCE_MAX_UPLOAD_TOTAL_BANDWIDTH].as(); + { //Test for server? + auto bandwidth_server = client->getServer()->properties()[property::VIRTUALSERVER_MAX_UPLOAD_TOTAL_BANDWIDTH].as(); + auto bandwidth_instance = serverInstance->properties()[property::SERVERINSTANCE_MAX_UPLOAD_TOTAL_BANDWIDTH].as(); - result->max_server_bandwidth = bandwidth_server; - result->max_bandwhidth = bandwidth_server; + result->max_server_bandwidth = bandwidth_server; + result->max_bandwhidth = bandwidth_server; - if(bandwidth_instance != -1) { - if(bandwidth_instance < bandwidth_server) - result->max_bandwhidth = bandwidth_instance; //We want to limit that already until exact calculations was made - } - } + if(bandwidth_instance != -1) { + if(bandwidth_instance < bandwidth_server) + result->max_bandwhidth = bandwidth_instance; //We want to limit that already until exact calculations was made + } + } - if(client->getServer()) - result->max_server_bandwidth = client->getServer()->properties()[property::VIRTUALSERVER_MAX_DOWNLOAD_TOTAL_BANDWIDTH]; + if(client->getServer()) + result->max_server_bandwidth = client->getServer()->properties()[property::VIRTUALSERVER_MAX_DOWNLOAD_TOTAL_BANDWIDTH]; { threads::MutexLock lock(this->keylock); @@ -180,10 +180,10 @@ std::shared_ptr FileServer::generateDownloadTransferKey( std::shared_ptr FileServer::generateUploadTransferKey(std::string &errorMessage, std::string targetFile, size_t size, size_t offset, const shared_ptr& client) { shared_ptr result = make_shared(); - result->owner = client; - result->server = client->getServerId(); + result->owner = client; + result->server = client->getServerId(); - result->key_id = (uint16_t) (rand() & 0xFFFF); + result->key_id = (uint16_t) (rand() & 0xFFFF); result->createTimestamp = std::chrono::system_clock::now(); result->key = randomString(16); result->offset = offset; @@ -191,18 +191,18 @@ std::shared_ptr FileServer::generateUploadTransferKey(st result->upload = true; result->targetFile = targetFile; - { //Test for server? - auto bandwidth_server = client->getServer()->properties()[property::VIRTUALSERVER_MAX_DOWNLOAD_TOTAL_BANDWIDTH].as(); - auto bandwidth_instance = serverInstance->properties()[property::SERVERINSTANCE_MAX_DOWNLOAD_TOTAL_BANDWIDTH].as(); + { //Test for server? + auto bandwidth_server = client->getServer()->properties()[property::VIRTUALSERVER_MAX_DOWNLOAD_TOTAL_BANDWIDTH].as(); + auto bandwidth_instance = serverInstance->properties()[property::SERVERINSTANCE_MAX_DOWNLOAD_TOTAL_BANDWIDTH].as(); - result->max_server_bandwidth = bandwidth_server; - result->max_bandwhidth = bandwidth_server; + result->max_server_bandwidth = bandwidth_server; + result->max_bandwhidth = bandwidth_server; - if(bandwidth_instance != -1) { - if(bandwidth_instance < bandwidth_server) - result->max_bandwhidth = bandwidth_instance; //We want to limit that already until exact calculations was made - } - } + if(bandwidth_instance != -1) { + if(bandwidth_instance < bandwidth_server) + result->max_bandwhidth = bandwidth_instance; //We want to limit that already until exact calculations was made + } + } { threads::MutexLock lock(this->keylock); pendingKeys.push_back(result); @@ -223,19 +223,19 @@ std::shared_ptr FileServer::resolveDirectory(const shared_ptr FileServer::iconDirectory(const shared_ptr &server) { - fs::path root = fs::u8path(this->rootPath); + fs::path root = fs::u8path(this->rootPath); fs::path path = fs::u8path("server_" + to_string(server ? server->getServerId() : 0) + "/icons"); if(!fs::exists(root / path)) { - if(!fs::create_directories(root / path)) - return nullptr; + if(!fs::create_directories(root / path)) + return nullptr; } return static_pointer_cast(findFile(path.string())); } bool FileServer::iconExists(const shared_ptr &server, IconId icon) { - if(icon == 0) return false; - if(icon < 1000) return true; - return this->findFile("icon_" + to_string(icon), this->iconDirectory(server)) != nullptr; + if(icon == 0) return false; + if(icon < 1000) return true; + return this->findFile("icon_" + to_string(icon), this->iconDirectory(server)) != nullptr; } std::shared_ptr FileServer::avatarDirectory(const shared_ptr &server) { @@ -249,111 +249,111 @@ std::shared_ptr FileServer::avatarDirectory(const shared_ptr &server) { auto dir = iconDirectory(server); if(!dir) { - logError(LOG_FT,"Failed to find icon directory for server {}", server ? server->getServerId() : 0); + logError(LOG_FT,"Failed to find icon directory for server {}", server ? server->getServerId() : 0); } else { - if(!fs::exists(fs::u8path(dir->path + "/" + dir->name + "/" TS3_ICON_HASH))) { - try { - fs::copy(fs::u8path("resources/teaspeak16px.png"), fs::u8path(dir->path + "/" + dir->name + "/" + TS3_ICON_HASH)); - } catch(std::exception& ex) { - logError(LOG_FT, "Failed to copy default path: {}", ex.what()); - } - } + if(!fs::exists(fs::u8path(dir->path + "/" + dir->name + "/" TS3_ICON_HASH))) { + try { + fs::copy(fs::u8path("resources/teaspeak16px.png"), fs::u8path(dir->path + "/" + dir->name + "/" + TS3_ICON_HASH)); + } catch(std::exception& ex) { + logError(LOG_FT, "Failed to copy default path: {}", ex.what()); + } + } } } std::string FileServer::server_file_base(const std::shared_ptr &server) { - return rootPath + "/server_" + to_string(server->getServerId()); + return rootPath + "/server_" + to_string(server->getServerId()); } void FileServer::deleteServer(const shared_ptr &server) { - fs::path path = fs::u8path(rootPath + "/server_" + to_string(server ? server->getServerId() : 0)); - if(fs::exists(path)) { - error_code error; - if(fs::remove_all(path, error) == 0) - logError(LOG_FT, "Could not delete server directory {} ({} | {})", path.string(), error.value(), error.message()); - } else { - logError(LOG_FT, "Could not delete missing server directory (" + path.string() + ")"); - } + fs::path path = fs::u8path(rootPath + "/server_" + to_string(server ? server->getServerId() : 0)); + if(fs::exists(path)) { + error_code error; + if(fs::remove_all(path, error) == 0) + logError(LOG_FT, "Could not delete server directory {} ({} | {})", path.string(), error.value(), error.message()); + } else { + logError(LOG_FT, "Could not delete missing server directory (" + path.string() + ")"); + } } //The actual server! bool FileServer::start(const std::deque>& bindings, std::string& error) { if(this->running()) { - error = "server already running"; - return false; + error = "server already running"; + return false; } this->active = true; - /* reserve backup file descriptor in case that the max file descriptors have been reached */ - { - this->server_reserve_fd = dup(1); - if(this->server_reserve_fd < 0) - logWarning(LOG_FT, "Failed to reserve a backup accept file descriptor. ({} | {})", errno, strerror(errno)); - } + /* reserve backup file descriptor in case that the max file descriptors have been reached */ + { + this->server_reserve_fd = dup(1); + if(this->server_reserve_fd < 0) + logWarning(LOG_FT, "Failed to reserve a backup accept file descriptor. ({} | {})", errno, strerror(errno)); + } - /* setup event bases */ - { - this->ioLoop = event_base_new(); - this->ioThread = new threads::Thread(THREAD_SAVE_OPERATIONS | THREAD_EXECUTE_LATER, [&]{ - while(this->active) { - debugMessage(LOG_FT, "Entering event loop ({})", (void*) this->ioLoop); - event_base_loop(this->ioLoop, EVLOOP_NO_EXIT_ON_EMPTY); - if(this->active) { - debugMessage(LOG_FT, "Event loop exited ({}). No active events. Sleeping 1 seconds", (void*) this->ioLoop); - this_thread::sleep_for(seconds(1)); - } else { - debugMessage(LOG_FT, "Event loop exited ({})", (void*) this->ioLoop); - } - } - }); - this->ioThread->name("File IO #1").execute(); - } + /* setup event bases */ + { + this->ioLoop = event_base_new(); + this->ioThread = new threads::Thread(THREAD_SAVE_OPERATIONS | THREAD_EXECUTE_LATER, [&]{ + while(this->active) { + debugMessage(LOG_FT, "Entering event loop ({})", (void*) this->ioLoop); + event_base_loop(this->ioLoop, EVLOOP_NO_EXIT_ON_EMPTY); + if(this->active) { + debugMessage(LOG_FT, "Event loop exited ({}). No active events. Sleeping 1 seconds", (void*) this->ioLoop); + this_thread::sleep_for(seconds(1)); + } else { + debugMessage(LOG_FT, "Event loop exited ({})", (void*) this->ioLoop); + } + } + }); + this->ioThread->name("File IO #1").execute(); + } - { - for(auto& binding : bindings) { - binding->file_descriptor = socket(binding->address.ss_family, SOCK_STREAM | SOCK_NONBLOCK, 0); - if(binding->file_descriptor < 0) { - logError(LOG_FT, "Failed to bind server to {}. (Failed to create socket: {} | {})", binding->as_string(), errno, strerror(errno)); - continue; - } + { + for(auto& binding : bindings) { + binding->file_descriptor = socket(binding->address.ss_family, SOCK_STREAM | SOCK_NONBLOCK, 0); + if(binding->file_descriptor < 0) { + logError(LOG_FT, "Failed to bind server to {}. (Failed to create socket: {} | {})", binding->as_string(), errno, strerror(errno)); + continue; + } - int enable = 1, disabled = 0; + int enable = 1, disabled = 0; - if (setsockopt(binding->file_descriptor, SOL_SOCKET, SO_REUSEADDR, &enable, sizeof(int)) < 0) - logWarning(LOG_FT, "Failed to activate SO_REUSEADDR for binding {} ({} | {})", binding->as_string(), errno, strerror(errno)); - if(setsockopt(binding->file_descriptor, IPPROTO_TCP, TCP_NOPUSH, &disabled, sizeof disabled) < 0) - logWarning(LOG_FT, "Failed to deactivate TCP_NOPUSH for binding {} ({} | {})", binding->as_string(), errno, strerror(errno)); - if(binding->address.ss_family == AF_INET6) { - if(setsockopt(binding->file_descriptor, IPPROTO_IPV6, IPV6_V6ONLY, &enable, sizeof(int)) < 0) - logWarning(LOG_FT, "Failed to activate IPV6_V6ONLY for IPv6 binding {} ({} | {})", binding->as_string(), errno, strerror(errno)); - } - if(fcntl(binding->file_descriptor, F_SETFD, FD_CLOEXEC) < 0) - logWarning(LOG_FT, "Failed to set flag FD_CLOEXEC for binding {} ({} | {})", binding->as_string(), errno, strerror(errno)); + if (setsockopt(binding->file_descriptor, SOL_SOCKET, SO_REUSEADDR, &enable, sizeof(int)) < 0) + logWarning(LOG_FT, "Failed to activate SO_REUSEADDR for binding {} ({} | {})", binding->as_string(), errno, strerror(errno)); + if(setsockopt(binding->file_descriptor, IPPROTO_TCP, TCP_NOPUSH, &disabled, sizeof disabled) < 0) + logWarning(LOG_FT, "Failed to deactivate TCP_NOPUSH for binding {} ({} | {})", binding->as_string(), errno, strerror(errno)); + if(binding->address.ss_family == AF_INET6) { + if(setsockopt(binding->file_descriptor, IPPROTO_IPV6, IPV6_V6ONLY, &enable, sizeof(int)) < 0) + logWarning(LOG_FT, "Failed to activate IPV6_V6ONLY for IPv6 binding {} ({} | {})", binding->as_string(), errno, strerror(errno)); + } + if(fcntl(binding->file_descriptor, F_SETFD, FD_CLOEXEC) < 0) + logWarning(LOG_FT, "Failed to set flag FD_CLOEXEC for binding {} ({} | {})", binding->as_string(), errno, strerror(errno)); - if (bind(binding->file_descriptor, (struct sockaddr *) &binding->address, sizeof(binding->address)) < 0) { - logError(LOG_FT, "Failed to bind server to {}. (Failed to bind socket: {} | {})", binding->as_string(), errno, strerror(errno)); - close(binding->file_descriptor); - continue; - } + if (bind(binding->file_descriptor, (struct sockaddr *) &binding->address, sizeof(binding->address)) < 0) { + logError(LOG_FT, "Failed to bind server to {}. (Failed to bind socket: {} | {})", binding->as_string(), errno, strerror(errno)); + close(binding->file_descriptor); + continue; + } - if (listen(binding->file_descriptor, SOMAXCONN) < 0) { - logError(LOG_FT, "Failed to bind server to {}. (Failed to listen: {} | {})", binding->as_string(), errno, strerror(errno)); - close(binding->file_descriptor); - continue; - } + if (listen(binding->file_descriptor, SOMAXCONN) < 0) { + logError(LOG_FT, "Failed to bind server to {}. (Failed to listen: {} | {})", binding->as_string(), errno, strerror(errno)); + close(binding->file_descriptor); + continue; + } - binding->event_accept = event_new(this->ioLoop, binding->file_descriptor, EV_READ | EV_PERSIST, [](int a, short b, void* c){ ((FileServer *) c)->on_client_accept(a, b, c); }, this); - event_add(binding->event_accept, nullptr); - this->bindings.push_back(binding); - } + binding->event_accept = event_new(this->ioLoop, binding->file_descriptor, EV_READ | EV_PERSIST, [](int a, short b, void* c){ ((FileServer *) c)->on_client_accept(a, b, c); }, this); + event_add(binding->event_accept, nullptr); + this->bindings.push_back(binding); + } - if(this->bindings.empty()) { - this->stop(); - error = "failed to bind to any address"; - return false; - } - } + if(this->bindings.empty()) { + this->stop(); + error = "failed to bind to any address"; + return false; + } + } for(int index = 0; index < 2; index++){ auto th = new threads::Thread(THREAD_SAVE_OPERATIONS | THREAD_EXECUTE_LATER, &FileServer::clientTickingExecutor, this); @@ -366,177 +366,177 @@ bool FileServer::start(const std::deque>& b void FileServer::stop() { if(!this->running()) return; active = false; - { - lock_guard lock(this->tickingLock); - this->tickingCon.notify_all(); - } + { + lock_guard lock(this->tickingLock); + this->tickingCon.notify_all(); + } - for(auto& binding : this->bindings) { - if(binding->event_accept) { - event_del_block(binding->event_accept); - event_free(binding->event_accept); - binding->event_accept = nullptr; - } - if(binding->file_descriptor > 0) { - if(shutdown(binding->file_descriptor, SHUT_RDWR) < 0) - logWarning(LOG_FT, "Failed to shutdown socket for binding {} ({} | {}).", binding->as_string(), errno, strerror(errno)); - if(close(binding->file_descriptor) < 0) - logError(LOG_FT, "Failed to close socket for binding {} ({} | {}).", binding->as_string(), errno, strerror(errno)); - binding->file_descriptor = -1; - } - } - this->bindings.clear(); + for(auto& binding : this->bindings) { + if(binding->event_accept) { + event_del_block(binding->event_accept); + event_free(binding->event_accept); + binding->event_accept = nullptr; + } + if(binding->file_descriptor > 0) { + if(shutdown(binding->file_descriptor, SHUT_RDWR) < 0) + logWarning(LOG_FT, "Failed to shutdown socket for binding {} ({} | {}).", binding->as_string(), errno, strerror(errno)); + if(close(binding->file_descriptor) < 0) + logError(LOG_FT, "Failed to close socket for binding {} ({} | {}).", binding->as_string(), errno, strerror(errno)); + binding->file_descriptor = -1; + } + } + this->bindings.clear(); - auto clClone = this->connectedClients; - for(const auto& cl : clClone) { - cl->disconnect(chrono::milliseconds(1)); - } + auto clClone = this->connectedClients; + for(const auto& cl : clClone) { + cl->disconnect(chrono::milliseconds(1)); + } - if(this->ioLoop) { - event_base_loopbreak(this->ioLoop); - event_base_loopexit(this->ioLoop, nullptr); - } + if(this->ioLoop) { + event_base_loopbreak(this->ioLoop); + event_base_loopexit(this->ioLoop, nullptr); + } - for(const auto& thread : this->tickingThreads){ - if(thread->join(chrono::seconds(1)) != 0) logCritical(LOG_FT, "Failed to terminal file server tick thread!"); - delete thread; - } - this->tickingThreads.clear(); + for(const auto& thread : this->tickingThreads){ + if(thread->join(chrono::seconds(1)) != 0) logCritical(LOG_FT, "Failed to terminal file server tick thread!"); + delete thread; + } + this->tickingThreads.clear(); if(this->ioThread) - if(this->ioThread->join(chrono::system_clock::now() + chrono::seconds(3)) != 0) { - logCritical(LOG_FT, "Could not terminate the event base dispatch thread of the file server"); - this->ioThread->detach(); - } + if(this->ioThread->join(chrono::system_clock::now() + chrono::seconds(3)) != 0) { + logCritical(LOG_FT, "Could not terminate the event base dispatch thread of the file server"); + this->ioThread->detach(); + } delete this->ioThread; this->ioThread = nullptr; - if(this->ioLoop) { - event_base_free(this->ioLoop); - this->ioLoop = nullptr; - } + if(this->ioLoop) { + event_base_free(this->ioLoop); + this->ioLoop = nullptr; + } - if(this->server_reserve_fd > 0) { - if(close(this->server_reserve_fd) < 0) - logError(LOG_FT, "Failed to close backup file descriptor ({} | {})", errno, strerror(errno)); - } - this->server_reserve_fd = -1; + if(this->server_reserve_fd > 0) { + if(close(this->server_reserve_fd) < 0) + logError(LOG_FT, "Failed to close backup file descriptor ({} | {})", errno, strerror(errno)); + } + this->server_reserve_fd = -1; } inline std::string logging_address(const sockaddr_storage& address) { - if(config::server::disable_ip_saving) - return "[0|X.X.X.X" + to_string(net::port(address)) + "|unconnected]"; - return "[0|X.X.X.X" + net::to_string(address, true) + "|unconnected]"; + if(config::server::disable_ip_saving) + return "[0|X.X.X.X" + to_string(net::port(address)) + "|unconnected]"; + return "[0|X.X.X.X" + net::to_string(address, true) + "|unconnected]"; } #define CLOSE_CONNECTION \ if(shutdown(file_descriptor, SHUT_RDWR) < 0) { \ - debugMessage(LOG_FT, "[{}] Failed to shutdown socket ({} | {}).", logging_address(remote_address), errno, strerror(errno)); \ + debugMessage(LOG_FT, "[{}] Failed to shutdown socket ({} | {}).", logging_address(remote_address), errno, strerror(errno)); \ } \ if(close(file_descriptor) < 0) { \ - debugMessage(LOG_FT, "[{}] Failed to close socket ({} | {}).", logging_address(remote_address), errno, strerror(errno)); \ + debugMessage(LOG_FT, "[{}] Failed to close socket ({} | {}).", logging_address(remote_address), errno, strerror(errno)); \ } void FileServer::on_client_accept(int _server_file_descriptor, short ev, void *arg) { - sockaddr_storage remote_address{}; - memset(&remote_address, 0, sizeof(remote_address)); + sockaddr_storage remote_address{}; + memset(&remote_address, 0, sizeof(remote_address)); socklen_t address_length = sizeof(remote_address); - int file_descriptor = accept(_server_file_descriptor, (struct sockaddr *) &remote_address, &address_length); - if (file_descriptor < 0) { - if(errno == EAGAIN) - return; + int file_descriptor = accept(_server_file_descriptor, (struct sockaddr *) &remote_address, &address_length); + if (file_descriptor < 0) { + if(errno == EAGAIN) + return; - if(errno == EMFILE || errno == ENFILE) { - if(errno == EMFILE) - logError(LOG_FT, "Server ran out file descriptors. Please increase the process file descriptor limit or decrease the instance variable 'serverinstance_filetransfer_max_connections'"); - else - logError(LOG_FT, "Server ran out file descriptors. Please increase the process and system-wide file descriptor limit or decrease the instance variable 'serverinstance_filetransfer_max_connections'"); + if(errno == EMFILE || errno == ENFILE) { + if(errno == EMFILE) + logError(LOG_FT, "Server ran out file descriptors. Please increase the process file descriptor limit or decrease the instance variable 'serverinstance_filetransfer_max_connections'"); + else + logError(LOG_FT, "Server ran out file descriptors. Please increase the process and system-wide file descriptor limit or decrease the instance variable 'serverinstance_filetransfer_max_connections'"); - bool tmp_close_success = false; - { - lock_guard reserve_fd_lock(server_reserve_fd_lock); - if(this->server_reserve_fd > 0) { - debugMessage(LOG_FT, "Trying to accept client with the reserved file descriptor to close the incomming connection."); - auto _ = [&]{ - if(close(this->server_reserve_fd) < 0) { - debugMessage(LOG_FT, "Failed to close reserved file descriptor"); - tmp_close_success = false; - return; - } - this->server_reserve_fd = 0; + bool tmp_close_success = false; + { + lock_guard reserve_fd_lock(server_reserve_fd_lock); + if(this->server_reserve_fd > 0) { + debugMessage(LOG_FT, "Trying to accept client with the reserved file descriptor to close the incomming connection."); + auto _ = [&]{ + if(close(this->server_reserve_fd) < 0) { + debugMessage(LOG_FT, "Failed to close reserved file descriptor"); + tmp_close_success = false; + return; + } + this->server_reserve_fd = 0; - errno = 0; - file_descriptor = accept(_server_file_descriptor, (struct sockaddr *) &remote_address, &address_length); - if(file_descriptor < 0) { - if(errno == EMFILE || errno == ENFILE) - debugMessage(LOG_FT, "[{}] Even with freeing the reserved descriptor accept failed. Attempting to reclaim reserved file descriptor", logging_address(remote_address)); - else if(errno == EAGAIN); - else { - debugMessage(LOG_FT, "[{}] Failed to accept client with reserved file descriptor. ({} | {})", logging_address(remote_address), errno, strerror(errno)); - } - this->server_reserve_fd = dup(1); - if(this->server_reserve_fd < 0) - debugMessage(LOG_FT, "[{}] Failed to reclaim reserved file descriptor. Future clients cant be accepted!", logging_address(remote_address)); - else - tmp_close_success = true; - return; - } - debugMessage(LOG_FT, "[{}] Successfully accepted client via reserved descriptor (fd: {}). Disconnecting client.", logging_address(remote_address), file_descriptor); + errno = 0; + file_descriptor = accept(_server_file_descriptor, (struct sockaddr *) &remote_address, &address_length); + if(file_descriptor < 0) { + if(errno == EMFILE || errno == ENFILE) + debugMessage(LOG_FT, "[{}] Even with freeing the reserved descriptor accept failed. Attempting to reclaim reserved file descriptor", logging_address(remote_address)); + else if(errno == EAGAIN); + else { + debugMessage(LOG_FT, "[{}] Failed to accept client with reserved file descriptor. ({} | {})", logging_address(remote_address), errno, strerror(errno)); + } + this->server_reserve_fd = dup(1); + if(this->server_reserve_fd < 0) + debugMessage(LOG_FT, "[{}] Failed to reclaim reserved file descriptor. Future clients cant be accepted!", logging_address(remote_address)); + else + tmp_close_success = true; + return; + } + debugMessage(LOG_FT, "[{}] Successfully accepted client via reserved descriptor (fd: {}). Disconnecting client.", logging_address(remote_address), file_descriptor); - CLOSE_CONNECTION - this->server_reserve_fd = dup(1); - if(this->server_reserve_fd < 0) - debugMessage(LOG_FT, "Failed to reclaim reserved file descriptor. Future clients cant be accepted!"); - else - tmp_close_success = true; - logMessage(LOG_FT, "[{}] Dropping file transfer connection attempt because of too many open file descriptors.", logging_address(remote_address)); - }; - _(); - } - } + CLOSE_CONNECTION + this->server_reserve_fd = dup(1); + if(this->server_reserve_fd < 0) + debugMessage(LOG_FT, "Failed to reclaim reserved file descriptor. Future clients cant be accepted!"); + else + tmp_close_success = true; + logMessage(LOG_FT, "[{}] Dropping file transfer connection attempt because of too many open file descriptors.", logging_address(remote_address)); + }; + _(); + } + } - if(!tmp_close_success) { - debugMessage(LOG_FT, "Sleeping two seconds because we're currently having no resources for this user. (Removing the accept event)"); - for(auto& binding : this->bindings) - event_del_noblock(binding->event_accept); - accept_event_deleted = system_clock::now(); - return; - } - return; - } - logMessage(LOG_FT, "Got an error while accepting a new client. (errno: {}, message: {})", errno, strerror(errno)); - return; - } - { - unique_lock lock(this->clientLock); - auto max_connections = serverInstance->properties()[property::SERVERINSTANCE_FILETRANSFER_MAX_CONNECTIONS].as(); - if(max_connections > 0 && max_connections <= this->connectedClients.size()) { - lock.unlock(); - logMessage(LOG_FT, "[{}] Dropping new connection attempt because of too many connected clients.", logging_address(remote_address)); - CLOSE_CONNECTION - return; - } + if(!tmp_close_success) { + debugMessage(LOG_FT, "Sleeping two seconds because we're currently having no resources for this user. (Removing the accept event)"); + for(auto& binding : this->bindings) + event_del_noblock(binding->event_accept); + accept_event_deleted = system_clock::now(); + return; + } + return; + } + logMessage(LOG_FT, "Got an error while accepting a new client. (errno: {}, message: {})", errno, strerror(errno)); + return; + } + { + unique_lock lock(this->clientLock); + auto max_connections = serverInstance->properties()[property::SERVERINSTANCE_FILETRANSFER_MAX_CONNECTIONS].as(); + if(max_connections > 0 && max_connections <= this->connectedClients.size()) { + lock.unlock(); + logMessage(LOG_FT, "[{}] Dropping new connection attempt because of too many connected clients.", logging_address(remote_address)); + CLOSE_CONNECTION + return; + } - auto max_ip_connections = serverInstance->properties()[property::SERVERINSTANCE_FILETRANSFER_MAX_CONNECTIONS_PER_IP].as(); - if(max_ip_connections > 0) { - size_t connection_count = 0; - for(auto& client : this->connectedClients) { - if(net::address_equal(client->remote_address, remote_address)) - connection_count++; - } + auto max_ip_connections = serverInstance->properties()[property::SERVERINSTANCE_FILETRANSFER_MAX_CONNECTIONS_PER_IP].as(); + if(max_ip_connections > 0) { + size_t connection_count = 0; + for(auto& client : this->connectedClients) { + if(net::address_equal(client->remote_address, remote_address)) + connection_count++; + } - if(connection_count >= max_ip_connections) { - lock.unlock(); - logMessage(LOG_FT, "[{}] Dropping new connection attempt because of too many simultaneously connected session from this ip.", logging_address(remote_address)); - CLOSE_CONNECTION - return; - } - } - } + if(connection_count >= max_ip_connections) { + lock.unlock(); + logMessage(LOG_FT, "[{}] Dropping new connection attempt because of too many simultaneously connected session from this ip.", logging_address(remote_address)); + CLOSE_CONNECTION + return; + } + } + } - shared_ptr client = std::make_shared(this, file_descriptor); + shared_ptr client = std::make_shared(this, file_descriptor); client->_this = client; memcpy(&client->remote_address, &remote_address, sizeof(remote_address)); @@ -544,7 +544,7 @@ void FileServer::on_client_accept(int _server_file_descriptor, short ev, void *a this->connectedClients.push_back(client); this->clientLock.unlock(); event_add(client->readEvent, nullptr); - logMessage(LOG_FT, "[{}] Remote peer connected. Initializing session.", logging_address(remote_address)); + logMessage(LOG_FT, "[{}] Remote peer connected. Initializing session.", logging_address(remote_address)); } void FileServer::clientTickingExecutor() { @@ -564,24 +564,24 @@ void FileServer::clientTickingExecutor() { } std::deque> FileServer::running_file_transfers(const std::shared_ptr &client) { - std::deque> result; + std::deque> result; - { - threads::MutexLock lock(this->clientLock); - for(const auto& c : this->connectedClients) { - if(!client || c->client == client) result.push_back(c); - } - } + { + threads::MutexLock lock(this->clientLock); + for(const auto& c : this->connectedClients) { + if(!client || c->client == client) result.push_back(c); + } + } - return result; + return result; } std::deque> FileServer::pending_file_transfers(const std::shared_ptr &client) { - std::deque> result; - for(const auto& key : this->pending_keys()) - if(!client || key->owner.lock() == client) - result.push_back(key); - return result; + std::deque> result; + for(const auto& key : this->pending_keys()) + if(!client || key->owner.lock() == client) + result.push_back(key); + return result; } void FileServer::tickFileClient(std::shared_ptr cl) { @@ -591,175 +591,175 @@ void FileServer::tickFileClient(std::shared_ptr cl) { } struct TransfareGroup { - shared_ptr server; - deque> clients; - enum { - upload, - download - } direction = upload; - size_t used_bandwidth = 0; - ssize_t max_bandwidth = -1; + shared_ptr server; + deque> clients; + enum { + upload, + download + } direction = upload; + size_t used_bandwidth = 0; + ssize_t max_bandwidth = -1; }; void FileServer::instanceTick() { - { - //tickQueue - auto client = this->connected_clients(); - lock_guard tick_lock(this->tickingLock); - this->tickQueue.insert(this->tickQueue.end(), client.begin(), client.end()); //Tick all clients :) - this->tickingCon.notify_all(); - } - if(this->accept_event_deleted.time_since_epoch().count() != 0 && accept_event_deleted + seconds(5) < system_clock::now()) { - debugMessage(LOG_FT, "Readding accept event and try again if we have enough resources again."); - for(auto& binding : this->bindings) - event_add(binding->event_accept, nullptr); - accept_event_deleted = system_clock::time_point{}; - } + { + //tickQueue + auto client = this->connected_clients(); + lock_guard tick_lock(this->tickingLock); + this->tickQueue.insert(this->tickQueue.end(), client.begin(), client.end()); //Tick all clients :) + this->tickingCon.notify_all(); + } + if(this->accept_event_deleted.time_since_epoch().count() != 0 && accept_event_deleted + seconds(5) < system_clock::now()) { + debugMessage(LOG_FT, "Readding accept event and try again if we have enough resources again."); + for(auto& binding : this->bindings) + event_add(binding->event_accept, nullptr); + accept_event_deleted = system_clock::time_point{}; + } - auto now = system_clock::now(); - if(timestamp_bandwidth_update + seconds(1) < now) { - timestamp_bandwidth_update = now; + auto now = system_clock::now(); + if(timestamp_bandwidth_update + seconds(1) < now) { + timestamp_bandwidth_update = now; - for(const auto& entry : this->pending_keys()) { - if(!entry) continue; - if(entry->createTimestamp + minutes(1) < now) { - logMessage(entry->server, "[FILE] Timeout file transfer for file " + entry->targetFile + "! Dropping key"); + for(const auto& entry : this->pending_keys()) { + if(!entry) continue; + if(entry->createTimestamp + minutes(1) < now) { + logMessage(entry->server, "[FILE] Timeout file transfer for file " + entry->targetFile + "! Dropping key"); - { - threads::MutexLock lock(this->keylock); - auto index = find(this->pendingKeys.begin(), this->pendingKeys.end(), entry); - if(index != this->pendingKeys.end()) - this->pendingKeys.erase(index); - } - } - } + { + threads::MutexLock lock(this->keylock); + auto index = find(this->pendingKeys.begin(), this->pendingKeys.end(), entry); + if(index != this->pendingKeys.end()) + this->pendingKeys.erase(index); + } + } + } - deque> groups; - { - for(const auto& client : this->connected_clients()) { - if(!client || !client->client) continue; + deque> groups; + { + for(const auto& client : this->connected_clients()) { + if(!client || !client->client) continue; - lock_guard l(client->tickLock); //Instance hangup here - if(!client->pendingKey) continue; + lock_guard l(client->tickLock); //Instance hangup here + if(!client->pendingKey) continue; - auto used_band = client->used_bandwidth(); - if(client->pendingKey && client->state_connection != FileClient::C_DISCONNECTED && (client->pendingKey->max_bandwhidth < 0 || used_band < client->pendingKey->max_bandwhidth)) { + auto used_band = client->used_bandwidth(); + if(client->pendingKey && client->state_connection != FileClient::C_DISCONNECTED && (client->pendingKey->max_bandwhidth < 0 || used_band < client->pendingKey->max_bandwhidth)) { - if(client->event_read_hold) { //Reactivate read! - event_add(client->readEvent, nullptr); - client->event_read_hold = false; - debugMessage(LOG_FT, "{} Reattaching client read event! (Current bandwidth {})", client->client_prefix(), used_band); - } - if(client->event_write_hold) { //Reactivate read! - event_add(client->writeEvent, nullptr); - client->event_write_hold = false; - debugMessage(LOG_FT, "{} Reattaching client write event! (Current bandwidth {})", client->client_prefix(), used_band); - } - } - if(!client->client->getServer()) continue; + if(client->event_read_hold) { //Reactivate read! + event_add(client->readEvent, nullptr); + client->event_read_hold = false; + debugMessage(LOG_FT, "{} Reattaching client read event! (Current bandwidth {})", client->client_prefix(), used_band); + } + if(client->event_write_hold) { //Reactivate read! + event_add(client->writeEvent, nullptr); + client->event_write_hold = false; + debugMessage(LOG_FT, "{} Reattaching client write event! (Current bandwidth {})", client->client_prefix(), used_band); + } + } + if(!client->client->getServer()) continue; - bool found = false; - for(auto& group : groups) { - if (group->server == client->client->getServer() && (group->direction == TransfareGroup::upload) == client->pendingKey->upload) { - group->clients.push_back(client); - group->used_bandwidth += client->used_bandwidth(); - found = true; - break; - } - } - if(found) continue; + bool found = false; + for(auto& group : groups) { + if (group->server == client->client->getServer() && (group->direction == TransfareGroup::upload) == client->pendingKey->upload) { + group->clients.push_back(client); + group->used_bandwidth += client->used_bandwidth(); + found = true; + break; + } + } + if(found) continue; - auto entry = make_shared(); - entry->direction = client->pendingKey->upload ? TransfareGroup::upload : TransfareGroup::download; - entry->used_bandwidth = client->used_bandwidth(); - entry->clients.push_back(client); - entry->server = client->client->getServer(); - groups.push_back(entry); - } - } + auto entry = make_shared(); + entry->direction = client->pendingKey->upload ? TransfareGroup::upload : TransfareGroup::download; + entry->used_bandwidth = client->used_bandwidth(); + entry->clients.push_back(client); + entry->server = client->client->getServer(); + groups.push_back(entry); + } + } - { //Instance limit upload - auto limit_upload = serverInstance->properties()[property::SERVERINSTANCE_MAX_UPLOAD_TOTAL_BANDWIDTH].as(); - if(limit_upload >= 0) { - deque> elements; - size_t used_bandwidth = 0; - for(const auto& entry : groups) { - if (entry->direction == TransfareGroup::upload) { - elements.push_back(entry); - used_bandwidth += entry->used_bandwidth; - } - } - if(!elements.empty() && used_bandwidth > limit_upload) { - for(auto& entry : elements) { - entry->max_bandwidth = ssize_t(ceil(double(entry->used_bandwidth) * double(limit_upload) / double(used_bandwidth))); //Adjust the total - } - } - } - } + { //Instance limit upload + auto limit_upload = serverInstance->properties()[property::SERVERINSTANCE_MAX_UPLOAD_TOTAL_BANDWIDTH].as(); + if(limit_upload >= 0) { + deque> elements; + size_t used_bandwidth = 0; + for(const auto& entry : groups) { + if (entry->direction == TransfareGroup::upload) { + elements.push_back(entry); + used_bandwidth += entry->used_bandwidth; + } + } + if(!elements.empty() && used_bandwidth > limit_upload) { + for(auto& entry : elements) { + entry->max_bandwidth = ssize_t(ceil(double(entry->used_bandwidth) * double(limit_upload) / double(used_bandwidth))); //Adjust the total + } + } + } + } - { //Instance limit download - auto limit_download = serverInstance->properties()[property::SERVERINSTANCE_MAX_DOWNLOAD_TOTAL_BANDWIDTH].as(); - if(limit_download >= 0) { - deque> elements; - size_t used_bandwidth = 0; - for(const auto& entry : groups) { - if (entry->direction == TransfareGroup::download) { - elements.push_back(entry); - used_bandwidth += entry->used_bandwidth; - } - } - if(!elements.empty() && used_bandwidth > limit_download) { - for(auto& entry : elements) { - entry->max_bandwidth = ssize_t(ceil(double(entry->used_bandwidth) * double(limit_download) / double(used_bandwidth))); //Adjust the total - } - } - } - } + { //Instance limit download + auto limit_download = serverInstance->properties()[property::SERVERINSTANCE_MAX_DOWNLOAD_TOTAL_BANDWIDTH].as(); + if(limit_download >= 0) { + deque> elements; + size_t used_bandwidth = 0; + for(const auto& entry : groups) { + if (entry->direction == TransfareGroup::download) { + elements.push_back(entry); + used_bandwidth += entry->used_bandwidth; + } + } + if(!elements.empty() && used_bandwidth > limit_download) { + for(auto& entry : elements) { + entry->max_bandwidth = ssize_t(ceil(double(entry->used_bandwidth) * double(limit_download) / double(used_bandwidth))); //Adjust the total + } + } + } + } - map>> servers; - for(const auto& entry : groups) { - servers[entry->server->getServerId()].push_back(entry); - } + map>> servers; + for(const auto& entry : groups) { + servers[entry->server->getServerId()].push_back(entry); + } - for(const auto& server : servers) { - if(server.second.empty()) continue; + for(const auto& server : servers) { + if(server.second.empty()) continue; - { //Adjust server upload - auto max_bandwidth = server.second.front()->server->properties()[property::VIRTUALSERVER_MAX_UPLOAD_TOTAL_BANDWIDTH].as(); - if(max_bandwidth >= 0) { - deque> tranfares; - size_t bandwidth = 0; - for(const auto& trans : server.second) { - if(trans->direction == TransfareGroup::upload) { - tranfares.push_back(trans); - bandwidth += trans->max_bandwidth; - } - } + { //Adjust server upload + auto max_bandwidth = server.second.front()->server->properties()[property::VIRTUALSERVER_MAX_UPLOAD_TOTAL_BANDWIDTH].as(); + if(max_bandwidth >= 0) { + deque> tranfares; + size_t bandwidth = 0; + for(const auto& trans : server.second) { + if(trans->direction == TransfareGroup::upload) { + tranfares.push_back(trans); + bandwidth += trans->max_bandwidth; + } + } - if(bandwidth > max_bandwidth) { - for(auto& trans : tranfares) - trans->max_bandwidth = ssize_t(ceil(double(trans->max_bandwidth) * double(max_bandwidth) / double(bandwidth))); //Adjust the total - } - } - } + if(bandwidth > max_bandwidth) { + for(auto& trans : tranfares) + trans->max_bandwidth = ssize_t(ceil(double(trans->max_bandwidth) * double(max_bandwidth) / double(bandwidth))); //Adjust the total + } + } + } - { //Adjust server download - auto max_bandwidth = server.second.front()->server->properties()[property::VIRTUALSERVER_MAX_DOWNLOAD_TOTAL_BANDWIDTH].as(); - if(max_bandwidth >= 0) { - deque> tranfares; - size_t bandwidth = 0; - for(const auto& trans : server.second) - if(trans->direction == TransfareGroup::download) { - tranfares.push_back(trans); - bandwidth += trans->max_bandwidth; - } + { //Adjust server download + auto max_bandwidth = server.second.front()->server->properties()[property::VIRTUALSERVER_MAX_DOWNLOAD_TOTAL_BANDWIDTH].as(); + if(max_bandwidth >= 0) { + deque> tranfares; + size_t bandwidth = 0; + for(const auto& trans : server.second) + if(trans->direction == TransfareGroup::download) { + tranfares.push_back(trans); + bandwidth += trans->max_bandwidth; + } - if(bandwidth > max_bandwidth) { - for(auto& trans : tranfares) - trans->max_bandwidth = ssize_t(ceil(double(trans->max_bandwidth) * double(max_bandwidth) / double(bandwidth))); //Adjust the total - } - } - } - } - } + if(bandwidth > max_bandwidth) { + for(auto& trans : tranfares) + trans->max_bandwidth = ssize_t(ceil(double(trans->max_bandwidth) * double(max_bandwidth) / double(bandwidth))); //Adjust the total + } + } + } + } + } } \ No newline at end of file diff --git a/server/src/server/file/FileServer.h b/server/src/server/file/FileServer.h index e864c6a..f48bf07 100644 --- a/server/src/server/file/FileServer.h +++ b/server/src/server/file/FileServer.h @@ -28,9 +28,9 @@ DEFINE_VARIABLE_TRANSFORM(ts::file::FileType::FileType, VARTYPE_INT, std::to_str namespace ts { class BasicChannel; - namespace server { - class ConnectedClient; - } + namespace server { + class ConnectedClient; + } namespace file { struct FileEntry { @@ -53,9 +53,9 @@ namespace ts { }; struct FileTransfereKey { - uint16_t key_id = 0; - ServerId server; - std::weak_ptr owner; + uint16_t key_id = 0; + ServerId server; + std::weak_ptr owner; std::chrono::time_point createTimestamp; std::string key; @@ -65,8 +65,8 @@ namespace ts { bool upload = false; - int64_t max_bandwhidth = -1; //Get calculated each time - int64_t max_server_bandwidth = -1; + int64_t max_bandwhidth = -1; //Get calculated each time + int64_t max_server_bandwidth = -1; }; } @@ -79,23 +79,23 @@ namespace ts { class FileServer { friend class FileClient; public: - struct Binding { - sockaddr_storage address{}; - int file_descriptor = 0; - ::event* event_accept = nullptr; + struct Binding { + sockaddr_storage address{}; + int file_descriptor = 0; + ::event* event_accept = nullptr; - inline std::string as_string() { return net::to_string(address, true); } - }; + inline std::string as_string() { return net::to_string(address, true); } + }; FileServer(); ~FileServer(); - bool start(const std::deque>& /* bindings */, std::string& /* error */); + bool start(const std::deque>& /* bindings */, std::string& /* error */); void stop(); - ts_always_inline bool running(){ return active; } - ts_always_inline std::deque> list_bindings() { return this->bindings; } + ts_always_inline bool running(){ return active; } + ts_always_inline std::deque> list_bindings() { return this->bindings; } - std::shared_ptr createDirectory(std::string name, std::shared_ptr parent); + std::shared_ptr createDirectory(std::string name, std::shared_ptr parent); bool fileExists(std::shared_ptr); bool fileExists(std::shared_ptr); std::shared_ptr findFile(std::string, std::shared_ptr = nullptr); @@ -108,36 +108,36 @@ namespace ts { std::shared_ptr resolveDirectory(const std::shared_ptr &, std::shared_ptr, std::string = ""); std::shared_ptr iconDirectory(const std::shared_ptr &); - bool iconExists(const std::shared_ptr &, IconId); + bool iconExists(const std::shared_ptr &, IconId); std::shared_ptr avatarDirectory(const std::shared_ptr &); std::string server_file_base(const std::shared_ptr &); void setupServer(const std::shared_ptr &); - void deleteServer(const std::shared_ptr &); + void deleteServer(const std::shared_ptr &); void tickFileClient(std::shared_ptr); void instanceTick(); - std::deque> running_file_transfers(const std::shared_ptr & /* client */ = nullptr); - std::deque> pending_file_transfers(const std::shared_ptr & /* client */ = nullptr); + std::deque> running_file_transfers(const std::shared_ptr & /* client */ = nullptr); + std::deque> pending_file_transfers(const std::shared_ptr & /* client */ = nullptr); private: bool active = false; - std::deque> bindings; + std::deque> bindings; std::string rootPath = "./files/"; //IO stuff event_base* ioLoop = nullptr; - std::chrono::system_clock::time_point accept_event_deleted; + std::chrono::system_clock::time_point accept_event_deleted; - std::mutex server_reserve_fd_lock; - int server_reserve_fd = -1; /* -1 = unset | 0 = in use | > 0 ready to use */ + std::mutex server_reserve_fd_lock; + int server_reserve_fd = -1; /* -1 = unset | 0 = in use | > 0 ready to use */ threads::Mutex clientLock; std::deque> connectedClients; - inline std::deque> connected_clients(){ - threads::MutexLock lock(clientLock); - return connectedClients; - } + inline std::deque> connected_clients(){ + threads::MutexLock lock(clientLock); + return connectedClients; + } threads::Thread* ioThread = nullptr; void on_client_accept(int fd, short ev, void *arg); @@ -153,8 +153,8 @@ namespace ts { threads::Mutex keylock; std::deque> pendingKeys; inline std::deque> pending_keys(){ - threads::MutexLock lock(keylock); - return pendingKeys; + threads::MutexLock lock(keylock); + return pendingKeys; } void clientTickingExecutor(); diff --git a/server/src/terminal/CommandHandler.cpp b/server/src/terminal/CommandHandler.cpp index cdb823b..acb6730 100644 --- a/server/src/terminal/CommandHandler.cpp +++ b/server/src/terminal/CommandHandler.cpp @@ -14,7 +14,7 @@ #include "src/server/QueryServer.h" #ifdef HAVE_JEMALLOC - #include + #include #endif using namespace std; @@ -64,109 +64,109 @@ namespace terminal { else if(cmd.lcommand == "permgrant") handleCommandPermGrant(cmd); else if(cmd.lcommand == "dummycrash" || cmd.lcommand == "dummy_crash") - handleCommandDummyCrash(cmd); + handleCommandDummyCrash(cmd); else if(cmd.lcommand == "dummyfdflood" || cmd.lcommand == "dummy_fdflood") - handleCommandDummyFdFlood(cmd); + handleCommandDummyFdFlood(cmd); else if(cmd.lcommand == "meminfo") - handleCommandMemInfo(cmd); + handleCommandMemInfo(cmd); else if(cmd.lcommand == "spoken") - handleCommandSpoken(cmd); + handleCommandSpoken(cmd); else if(cmd.lcommand == "passwd") - handleCommandPasswd(cmd); + handleCommandPasswd(cmd); else if(cmd.lcommand == "memflush") handleCommandMemFlush(cmd); else if(cmd.lcommand == "statsreset") - handleCommandStatsReset(cmd); + handleCommandStatsReset(cmd); else if(cmd.lcommand == "reload") - handleCommandReload(cmd); + handleCommandReload(cmd); else logError("Missing command " + cmd.command + "/" + cmd.line); } - bool handleCommandDummyCrash(TerminalCommand& arguments) { - if(!arguments.arguments.empty()) { - if(arguments.larguments[0] == "raise") { - raise(SIGABRT); - return true; - } else if(arguments.larguments[0] == "assert") { //dummycrash assert - assert(false); - return true; - } else if(arguments.larguments[0] == "exception") { - throw std::bad_exception(); - } - } - *(int*)(nullptr) = 0; - return true; - } + bool handleCommandDummyCrash(TerminalCommand& arguments) { + if(!arguments.arguments.empty()) { + if(arguments.larguments[0] == "raise") { + raise(SIGABRT); + return true; + } else if(arguments.larguments[0] == "assert") { //dummycrash assert + assert(false); + return true; + } else if(arguments.larguments[0] == "exception") { + throw std::bad_exception(); + } + } + *(int*)(nullptr) = 0; + return true; + } bool handleCommandHelp(TerminalCommand& args){ logMessage("§aAvariable commands:"); logMessage(" §7- §eend §7| §eshutdown"); - logMessage(" §7- §ereload config"); + logMessage(" §7- §ereload config"); logMessage(" §7- §echat"); logMessage(" §7- §einfo"); logMessage(" §7- §epermgrant"); - logMessage(" §7- §epasswd"); - logMessage(" §7- §4dummy_crash"); + logMessage(" §7- §epasswd"); + logMessage(" §7- §4dummy_crash"); logMessage(" §7- §4memflush"); - logMessage(" §7- §4meminfo"); + logMessage(" §7- §4meminfo"); return true; } bool handleCommandEnd(TerminalCommand& arguments){ if(arguments.arguments.size() < 1) { - logMessage("Invalid argument count!"); - logMessage("Usage: shutdown [h|m|s]:...> "); - logMessage("Example: shutdown info | Displays info about the current scheduled shutdown"); - logMessage("Example: shutdown cancel | Cancels the currently scheduled shutdown"); - logMessage("Example: shutdown now Server shutdown | The server instance will shutdown instantly"); - logMessage("Example: shutdown 1h:30m Server shutdown | The server instance will shutdown in 1h and 30 min"); - logMessage("Example: shutdown 1h:1m:1s Server shutdown | The server instance will shutdown in 1h and 1 min and 1 second"); - return false; + logMessage("Invalid argument count!"); + logMessage("Usage: shutdown [h|m|s]:...> "); + logMessage("Example: shutdown info | Displays info about the current scheduled shutdown"); + logMessage("Example: shutdown cancel | Cancels the currently scheduled shutdown"); + logMessage("Example: shutdown now Server shutdown | The server instance will shutdown instantly"); + logMessage("Example: shutdown 1h:30m Server shutdown | The server instance will shutdown in 1h and 30 min"); + logMessage("Example: shutdown 1h:1m:1s Server shutdown | The server instance will shutdown in 1h and 1 min and 1 second"); + return false; } - nanoseconds period{}; - if(arguments.larguments[0] == "info") { - auto task = ts::server::scheduledShutdown(); - if(!task) { - logMessage("It isn't a shutdown scheduled!"); - } else { - auto time = system_clock::to_time_t(task->time_point); - logMessage("You scheduled a shutdown task at " + string(ctime(&time))); - } - return true; - } else if(arguments.larguments[0] == "cancel") { - auto task = ts::server::scheduledShutdown(); - if(!task) { - logMessage("The isnt a shutdown scheduled!"); - } else { - ts::server::cancelShutdown(true); - logMessage("Shutdown task canceled!"); - } - return true; - } else if(arguments.larguments[0] != "now") { - string error; - period = period::parse(arguments.larguments[0], error); - if(!error.empty()) { - logError("Invalid period: " + error); - return false; - } - } + nanoseconds period{}; + if(arguments.larguments[0] == "info") { + auto task = ts::server::scheduledShutdown(); + if(!task) { + logMessage("It isn't a shutdown scheduled!"); + } else { + auto time = system_clock::to_time_t(task->time_point); + logMessage("You scheduled a shutdown task at " + string(ctime(&time))); + } + return true; + } else if(arguments.larguments[0] == "cancel") { + auto task = ts::server::scheduledShutdown(); + if(!task) { + logMessage("The isnt a shutdown scheduled!"); + } else { + ts::server::cancelShutdown(true); + logMessage("Shutdown task canceled!"); + } + return true; + } else if(arguments.larguments[0] != "now") { + string error; + period = period::parse(arguments.larguments[0], error); + if(!error.empty()) { + logError("Invalid period: " + error); + return false; + } + } - std::string reason = ts::config::messages::applicationStopped; - if(arguments.arguments.size() > 1) { - reason = ""; - for(auto it = arguments.arguments.begin() + 1; it != arguments.arguments.end(); it++) - reason += it->string() + (it + 1 != arguments.arguments.end() ? " " : ""); - } + std::string reason = ts::config::messages::applicationStopped; + if(arguments.arguments.size() > 1) { + reason = ""; + for(auto it = arguments.arguments.begin() + 1; it != arguments.arguments.end(); it++) + reason += it->string() + (it + 1 != arguments.arguments.end() ? " " : ""); + } - if(period.count() == 0) { - logMessage("Stopping instance"); - ts::server::shutdownInstance(reason); - } else { - auto time = system_clock::to_time_t(system_clock::now() + period); - logMessage("Scheduled shutdown at " + string(ctime(&time)) + ""); - ts::server::scheduleShutdown(system_clock::now() + period, reason); - } + if(period.count() == 0) { + logMessage("Stopping instance"); + ts::server::shutdownInstance(reason); + } else { + auto time = system_clock::to_time_t(system_clock::now() + period); + logMessage("Scheduled shutdown at " + string(ctime(&time)) + ""); + ts::server::scheduleShutdown(system_clock::now() + period, reason); + } return true; } @@ -306,217 +306,217 @@ namespace terminal { //memflush buffer //memflush alloc bool handleCommandMemFlush(TerminalCommand& cmd) { - if(cmd.arguments.size() > 0) { - if(cmd.larguments[0] == "db") { - if(serverInstance->getSql()->getType() != sql::TYPE_SQLITE) { - logMessage("This command just works when you use sqlite!"); - return false; - } - logMessage("Memory used by SQLite:"); - logMessage(" Currently used: {0:>6}kb ({0:>9} bytes)", sqlite3_memory_used() / 1024); - logMessage(" Max used: {0:>6}kb ({0:>9} bytes)", sqlite3_memory_highwater(true) / 1024); - logMessage(" Freed: {0:>6}kb ({0:>9} bytes)", sqlite3_db_release_memory(((sql::sqlite::SqliteManager*) serverInstance->getSql())->getDatabase()) / 1024); - logMessage(" Used after free: {0:>6}kb ({0:>9} bytes)", sqlite3_memory_used() / 1024); - sqlite3_memory_highwater(true); //Reset the watermark - return true; - } else if(cmd.larguments[0] == "buffer") { - auto info = buffer::cleanup_buffers(buffer::cleanmode::CHUNKS_BLOCKS); - logMessage("Cleaned up {} bytes ({} bytes internal)", info.bytes_freed_internal + info.bytes_freed_buffer,info.bytes_freed_internal); - return true; - } else if(cmd.larguments[0] == "alloc") { + if(cmd.arguments.size() > 0) { + if(cmd.larguments[0] == "db") { + if(serverInstance->getSql()->getType() != sql::TYPE_SQLITE) { + logMessage("This command just works when you use sqlite!"); + return false; + } + logMessage("Memory used by SQLite:"); + logMessage(" Currently used: {0:>6}kb ({0:>9} bytes)", sqlite3_memory_used() / 1024); + logMessage(" Max used: {0:>6}kb ({0:>9} bytes)", sqlite3_memory_highwater(true) / 1024); + logMessage(" Freed: {0:>6}kb ({0:>9} bytes)", sqlite3_db_release_memory(((sql::sqlite::SqliteManager*) serverInstance->getSql())->getDatabase()) / 1024); + logMessage(" Used after free: {0:>6}kb ({0:>9} bytes)", sqlite3_memory_used() / 1024); + sqlite3_memory_highwater(true); //Reset the watermark + return true; + } else if(cmd.larguments[0] == "buffer") { + auto info = buffer::cleanup_buffers(buffer::cleanmode::CHUNKS_BLOCKS); + logMessage("Cleaned up {} bytes ({} bytes internal)", info.bytes_freed_internal + info.bytes_freed_buffer,info.bytes_freed_internal); + return true; + } else if(cmd.larguments[0] == "alloc") { #ifdef HAVE_JEMALLOC - size_t - old_retained, old_active, old_allocated, - new_retained, new_active, new_allocated, - size_size_t; - mallctl("stats.retained", &old_retained, &(size_size_t = sizeof(size_t)), nullptr, 0); - mallctl("stats.allocated", &old_allocated, &(size_size_t = sizeof(size_t)), nullptr, 0); - mallctl("stats.active", &old_active, &(size_size_t = sizeof(size_t)), nullptr, 0); + size_t + old_retained, old_active, old_allocated, + new_retained, new_active, new_allocated, + size_size_t; + mallctl("stats.retained", &old_retained, &(size_size_t = sizeof(size_t)), nullptr, 0); + mallctl("stats.allocated", &old_allocated, &(size_size_t = sizeof(size_t)), nullptr, 0); + mallctl("stats.active", &old_active, &(size_size_t = sizeof(size_t)), nullptr, 0); - auto begin = system_clock::now(); - mallctl("arena." STRINGIFY(MALLCTL_ARENAS_ALL) ".decay", nullptr, nullptr, nullptr, 0); - mallctl("arena." STRINGIFY(MALLCTL_ARENAS_ALL) ".purge", nullptr, nullptr, nullptr, 0); - auto end = system_clock::now(); + auto begin = system_clock::now(); + mallctl("arena." STRINGIFY(MALLCTL_ARENAS_ALL) ".decay", nullptr, nullptr, nullptr, 0); + mallctl("arena." STRINGIFY(MALLCTL_ARENAS_ALL) ".purge", nullptr, nullptr, nullptr, 0); + auto end = system_clock::now(); - { /* refresh everything */ - uint64_t epoch = static_cast(system_clock::now().time_since_epoch().count()); - mallctl("epoch", nullptr, nullptr, &epoch, sizeof(int16_t)); - } - mallctl("stats.retained", &new_retained, &(size_size_t = sizeof(size_t)), nullptr, 0); - mallctl("stats.allocated", &new_allocated, &(size_size_t = sizeof(size_t)), nullptr, 0); - mallctl("stats.active", &new_active, &(size_size_t = sizeof(size_t)), nullptr, 0); + { /* refresh everything */ + uint64_t epoch = static_cast(system_clock::now().time_since_epoch().count()); + mallctl("epoch", nullptr, nullptr, &epoch, sizeof(int16_t)); + } + mallctl("stats.retained", &new_retained, &(size_size_t = sizeof(size_t)), nullptr, 0); + mallctl("stats.allocated", &new_allocated, &(size_size_t = sizeof(size_t)), nullptr, 0); + mallctl("stats.active", &new_active, &(size_size_t = sizeof(size_t)), nullptr, 0); - logMessage("Cleaned up allocated internals successfully within {}us", duration_cast(end - begin).count()); - logMessage(" Allocated: {0:>9} => {0:>9} bytes", old_allocated, new_allocated); - logMessage(" Retained : {0:>9} => {0:>9} bytes", old_retained, new_retained); - logMessage(" Active : {0:>9} => {0:>9} bytes", old_active, new_active); + logMessage("Cleaned up allocated internals successfully within {}us", duration_cast(end - begin).count()); + logMessage(" Allocated: {0:>9} => {0:>9} bytes", old_allocated, new_allocated); + logMessage(" Retained : {0:>9} => {0:>9} bytes", old_retained, new_retained); + logMessage(" Active : {0:>9} => {0:>9} bytes", old_active, new_active); #else - logError("Jemalloc extension has not been compiled!"); + logError("Jemalloc extension has not been compiled!"); #endif - return true; - } - } - logMessage("Invalid argument count. Possible: [db|buffer|alloc]"); + return true; + } + } + logMessage("Invalid argument count. Possible: [db|buffer|alloc]"); return true; } - void process_mem_usage(double& vm_usage, double& resident_set) - { - vm_usage = 0.0; - resident_set = 0.0; + void process_mem_usage(double& vm_usage, double& resident_set) + { + vm_usage = 0.0; + resident_set = 0.0; - // the two fields we want - unsigned long vsize; - long rss; - { - std::string ignore; - std::ifstream ifs("/proc/self/stat", std::ios_base::in); - ifs >> ignore >> ignore >> ignore >> ignore >> ignore >> ignore >> ignore >> ignore >> ignore >> ignore - >> ignore >> ignore >> ignore >> ignore >> ignore >> ignore >> ignore >> ignore >> ignore >> ignore - >> ignore >> ignore >> vsize >> rss; - } + // the two fields we want + unsigned long vsize; + long rss; + { + std::string ignore; + std::ifstream ifs("/proc/self/stat", std::ios_base::in); + ifs >> ignore >> ignore >> ignore >> ignore >> ignore >> ignore >> ignore >> ignore >> ignore >> ignore + >> ignore >> ignore >> ignore >> ignore >> ignore >> ignore >> ignore >> ignore >> ignore >> ignore + >> ignore >> ignore >> vsize >> rss; + } - long page_size_kb = sysconf(_SC_PAGE_SIZE) / 1024; // in case x86-64 is configured to use 2MB pages - vm_usage = vsize / 1024.0; - resident_set = rss * page_size_kb; - } + long page_size_kb = sysconf(_SC_PAGE_SIZE) / 1024; // in case x86-64 is configured to use 2MB pages + vm_usage = vsize / 1024.0; + resident_set = rss * page_size_kb; + } - bool handleCommandMemInfo(TerminalCommand& cmd){ - bool flag_base = false, flag_malloc = false, flag_track = false, flag_buffer = false; + bool handleCommandMemInfo(TerminalCommand& cmd){ + bool flag_base = false, flag_malloc = false, flag_track = false, flag_buffer = false; - if(cmd.arguments.size() > 0) { - if(cmd.larguments[0] == "basic") - flag_base = true; - else if(cmd.larguments[0] == "malloc") - flag_malloc = true; - else if(cmd.larguments[0] == "track") - flag_track = true; - else if(cmd.larguments[0] == "buffers") - flag_buffer = true; - } else { - flag_base = flag_malloc = flag_track = flag_buffer = true; - } + if(cmd.arguments.size() > 0) { + if(cmd.larguments[0] == "basic") + flag_base = true; + else if(cmd.larguments[0] == "malloc") + flag_malloc = true; + else if(cmd.larguments[0] == "track") + flag_track = true; + else if(cmd.larguments[0] == "buffers") + flag_buffer = true; + } else { + flag_base = flag_malloc = flag_track = flag_buffer = true; + } - if(flag_base) { - double vm, rss; - process_mem_usage(vm, rss); - logMessage("Used memory: {} (VM: {})", rss, vm); - } - if(flag_malloc) { - stringstream ss; + if(flag_base) { + double vm, rss; + process_mem_usage(vm, rss); + logMessage("Used memory: {} (VM: {})", rss, vm); + } + if(flag_malloc) { + stringstream ss; #ifdef HAVE_JEMALLOC - malloc_stats_print([](void* data, const char* buffer) { - auto _ss = (stringstream*) data; - *_ss << buffer; - }, &ss, nullptr); + malloc_stats_print([](void* data, const char* buffer) { + auto _ss = (stringstream*) data; + *_ss << buffer; + }, &ss, nullptr); #else - ss << "Jemalloc is not present!"; + ss << "Jemalloc is not present!"; #endif - logMessage(ss.str()); - } - if(flag_track) - memtrack::statistics(); - if(flag_buffer) { - auto info = buffer::buffer_memory(); - logMessage("Allocated memory: {}kb", ceil((info.bytes_internal + info.bytes_buffer) / 1024)); - logMessage(" Internal: {}kb", ceil((info.bytes_internal) / 1024)); - logMessage(" Buffers : {}kb", ceil((info.bytes_buffer) / 1024)); - logMessage(" Buffers Used: {}kb", ceil((info.bytes_buffer_used) / 1024)); - } - return true; - } + logMessage(ss.str()); + } + if(flag_track) + memtrack::statistics(); + if(flag_buffer) { + auto info = buffer::buffer_memory(); + logMessage("Allocated memory: {}kb", ceil((info.bytes_internal + info.bytes_buffer) / 1024)); + logMessage(" Internal: {}kb", ceil((info.bytes_internal) / 1024)); + logMessage(" Buffers : {}kb", ceil((info.bytes_buffer) / 1024)); + logMessage(" Buffers Used: {}kb", ceil((info.bytes_buffer_used) / 1024)); + } + return true; + } - bool handleCommandSpoken(TerminalCommand& cmd) { - //TODO print spoken statistics - return false; - } + bool handleCommandSpoken(TerminalCommand& cmd) { + //TODO print spoken statistics + return false; + } - bool handleCommandPasswd(TerminalCommand& cmd) { - if(cmd.arguments.size() != 2) { - logError("Invalid usage: passwd "); - return false; - } - if(cmd.arguments[0].string() != cmd.arguments[1].string()) { - logError("Passwords does not match!"); - return false; - } + bool handleCommandPasswd(TerminalCommand& cmd) { + if(cmd.arguments.size() != 2) { + logError("Invalid usage: passwd "); + return false; + } + if(cmd.arguments[0].string() != cmd.arguments[1].string()) { + logError("Passwords does not match!"); + return false; + } - auto serveradmin = serverInstance->getQueryServer()->find_query_account_by_name("serveradmin"); - if(!serveradmin) { - auto password = ""; - logErrorFmt(true, 0, "Creating a new serveradmin query login!"); - if(!(serveradmin = serverInstance->getQueryServer()->create_query_account("serveradmin", 0, "serveradmin", password))) { - logError("Could not create serveradmin account!"); - return false; - } - } + auto serveradmin = serverInstance->getQueryServer()->find_query_account_by_name("serveradmin"); + if(!serveradmin) { + auto password = ""; + logErrorFmt(true, 0, "Creating a new serveradmin query login!"); + if(!(serveradmin = serverInstance->getQueryServer()->create_query_account("serveradmin", 0, "serveradmin", password))) { + logError("Could not create serveradmin account!"); + return false; + } + } - serverInstance->getQueryServer()->change_query_password(serveradmin, cmd.arguments[0]); - logMessage("Server admin successfully changed!"); - return false; - } + serverInstance->getQueryServer()->change_query_password(serveradmin, cmd.arguments[0]); + logMessage("Server admin successfully changed!"); + return false; + } - extern bool handleCommandStatsReset(TerminalCommand& cmd) { - serverInstance->properties()[property::SERVERINSTANCE_MONTHLY_TIMESTAMP] = 0; - logMessage("Monthly statistics will be reset"); - return true; + extern bool handleCommandStatsReset(TerminalCommand& cmd) { + serverInstance->properties()[property::SERVERINSTANCE_MONTHLY_TIMESTAMP] = 0; + logMessage("Monthly statistics will be reset"); + return true; } deque fd_leaks; bool handleCommandDummyFdFlood(TerminalCommand& cmd) { - size_t value; - if(cmd.arguments.size() < 1) { - value = 1024; + size_t value; + if(cmd.arguments.size() < 1) { + value = 1024; - rlimit limit{1024, 10000}; - setrlimit(7, &limit); - } else if(cmd.larguments[0] == "clear") { - logMessage("Clearup leaks"); - for(auto& fd : fd_leaks) - close(fd); - fd_leaks.clear(); - return true; - } else { - value = cmd.arguments[0].as(); - } + rlimit limit{1024, 10000}; + setrlimit(7, &limit); + } else if(cmd.larguments[0] == "clear") { + logMessage("Clearup leaks"); + for(auto& fd : fd_leaks) + close(fd); + fd_leaks.clear(); + return true; + } else { + value = cmd.arguments[0].as(); + } - logMessage("Leaking {} file descriptors", value); - size_t index = 0; - while(index < value) { - auto fd = dup(1); - if(fd < 0) - logMessage("Failed to create a file descriptor {} | {}", errno, strerror(errno)); - else - fd_leaks.push_back(fd); + logMessage("Leaking {} file descriptors", value); + size_t index = 0; + while(index < value) { + auto fd = dup(1); + if(fd < 0) + logMessage("Failed to create a file descriptor {} | {}", errno, strerror(errno)); + else + fd_leaks.push_back(fd); - index++; - } - return true; + index++; + } + return true; } - bool handleCommandReload(TerminalCommand& cmd) { - if(cmd.larguments.size() < 1 || cmd.larguments[0] != "config") { - logMessage("Invalid target. Available: config"); - return true; - } + bool handleCommandReload(TerminalCommand& cmd) { + if(cmd.larguments.size() < 1 || cmd.larguments[0] != "config") { + logMessage("Invalid target. Available: config"); + return true; + } - vector error; - if(!serverInstance->reloadConfig(error, true)) { - logError("Failed to reload instance ({}):", error.size()); - for(auto& msg : error) - logError(" - {}", msg); - } else if(!error.empty()) { - logMessage("Reloaded successfully. Messages:"); - for(auto& msg : error) - logMessage(" - {}", msg); - } else { - logMessage("Reloaded successfully."); - } + vector error; + if(!serverInstance->reloadConfig(error, true)) { + logError("Failed to reload instance ({}):", error.size()); + for(auto& msg : error) + logError(" - {}", msg); + } else if(!error.empty()) { + logMessage("Reloaded successfully. Messages:"); + for(auto& msg : error) + logMessage(" - {}", msg); + } else { + logMessage("Reloaded successfully."); + } - return true; + return true; } } } \ No newline at end of file diff --git a/server/src/terminal/CommandHandler.h b/server/src/terminal/CommandHandler.h index c004273..efd616b 100644 --- a/server/src/terminal/CommandHandler.h +++ b/server/src/terminal/CommandHandler.h @@ -18,7 +18,7 @@ namespace terminal { extern void handleCommand(std::string); extern bool handleCommandDummyCrash(TerminalCommand&); - extern bool handleCommandDummyFdFlood(TerminalCommand&); + extern bool handleCommandDummyFdFlood(TerminalCommand&); extern bool handleCommandHelp(TerminalCommand&); extern bool handleCommandEnd(TerminalCommand&); @@ -35,6 +35,6 @@ namespace terminal { extern bool handleCommandStatsReset(TerminalCommand&); - extern bool handleCommandReload(TerminalCommand&); + extern bool handleCommandReload(TerminalCommand&); } } \ No newline at end of file diff --git a/server/src/weblist/TeamSpeakWebClient.cpp b/server/src/weblist/TeamSpeakWebClient.cpp index 25047cd..c792cbc 100644 --- a/server/src/weblist/TeamSpeakWebClient.cpp +++ b/server/src/weblist/TeamSpeakWebClient.cpp @@ -10,292 +10,292 @@ using namespace ts::server; TSWebClient::TSWebClient(const std::shared_ptr &server, struct event_base *event_base, uint16_t session_index, bool resend_name) : server(server), event_base(event_base), session_index(session_index), send_name(resend_name) { } TSWebClient::~TSWebClient() { - this->unregister_events(true); - try { - if(this->close_thread.get_id() == std::this_thread::get_id()) - this->close_thread.detach(); - else if(this->close_thread.joinable()) - this->close_thread.join(); - } catch(std::exception& ex) { - logCritical(this->server ? this->server->getServerId() : 0, "[WebList] Failed to join or detach close thread! message: {}", ex.what()); - } + this->unregister_events(true); + try { + if(this->close_thread.get_id() == std::this_thread::get_id()) + this->close_thread.detach(); + else if(this->close_thread.joinable()) + this->close_thread.join(); + } catch(std::exception& ex) { + logCritical(this->server ? this->server->getServerId() : 0, "[WebList] Failed to join or detach close thread! message: {}", ex.what()); + } } void TSWebClient::_handle_message_read(int file_descriptor, short, void *ptr_client) { - const auto client = (TSWebClient*) ptr_client; + const auto client = (TSWebClient*) ptr_client; - pipes::buffer buffer{512}; - const auto read = recv(file_descriptor, buffer.data_ptr(), buffer.length(), MSG_NOSIGNAL | MSG_DONTWAIT); - if(read <= 0) { - logError(client->server->getServerId(), "[WebList] Failed to read weblist response. ({} | {} => {})", read, errno, strerror(errno)); - event_del_noblock(client->event_read); - client->trigger_fail_later("failed to read!", true); - return; - } + pipes::buffer buffer{512}; + const auto read = recv(file_descriptor, buffer.data_ptr(), buffer.length(), MSG_NOSIGNAL | MSG_DONTWAIT); + if(read <= 0) { + logError(client->server->getServerId(), "[WebList] Failed to read weblist response. ({} | {} => {})", read, errno, strerror(errno)); + event_del_noblock(client->event_read); + client->trigger_fail_later("failed to read!", true); + return; + } - client->handle_message_read(buffer.view(0, read)); + client->handle_message_read(buffer.view(0, read)); } void TSWebClient::_handle_message_write(int file_descriptor, short, void *ptr_client) { - const auto client = (TSWebClient*) ptr_client; - pipes::buffer buffer; + const auto client = (TSWebClient*) ptr_client; + pipes::buffer buffer; - { - std::lock_guard lock(client->write_lock); - if(client->write_buffer.empty()) return; - buffer = std::move(client->write_buffer[0]); - client->write_buffer.pop_front(); + { + std::lock_guard lock(client->write_lock); + if(client->write_buffer.empty()) return; + buffer = std::move(client->write_buffer[0]); + client->write_buffer.pop_front(); - if(!client->write_buffer.empty() && client->event_write) - event_add(client->event_write, nullptr); - } + if(!client->write_buffer.empty() && client->event_write) + event_add(client->event_write, nullptr); + } - const auto write = sendto(file_descriptor, buffer.data_ptr(), buffer.length(), 0, (const sockaddr *) &client->remote_address, sizeof(sockaddr_in)); - if(write != buffer.length()){ - logError(client->server->getServerId(), "[WebList] Failed to send weblist response. ({} | {} => {})", write, errno, strerror(errno)); - client->trigger_fail_later("failed to write!", true); - return; - } + const auto write = sendto(file_descriptor, buffer.data_ptr(), buffer.length(), 0, (const sockaddr *) &client->remote_address, sizeof(sockaddr_in)); + if(write != buffer.length()){ + logError(client->server->getServerId(), "[WebList] Failed to send weblist response. ({} | {} => {})", write, errno, strerror(errno)); + client->trigger_fail_later("failed to write!", true); + return; + } } void TSWebClient::_handle_timeout(int, short, void *ptr_client) { - const auto client = (TSWebClient*) ptr_client; - if(client->event_timeout) - event_del_noblock(client->event_timeout); - client->trigger_fail_later("timeout", true); + const auto client = (TSWebClient*) ptr_client; + if(client->event_timeout) + event_del_noblock(client->event_timeout); + client->trigger_fail_later("timeout", true); } void TSWebClient::unregister_events(bool blocking) { - const auto unregister = [blocking](event*& event) { - if(blocking) { - event_del_block(event); - event_free(event); - event = nullptr; - } else { - event_del_noblock(event); - } - }; + const auto unregister = [blocking](event*& event) { + if(blocking) { + event_del_block(event); + event_free(event); + event = nullptr; + } else { + event_del_noblock(event); + } + }; - if(this->event_timeout) - unregister(this->event_timeout); - if(this->event_read) - unregister(this->event_read); - if(this->event_write) - unregister(this->event_write); + if(this->event_timeout) + unregister(this->event_timeout); + if(this->event_read) + unregister(this->event_read); + if(this->event_write) + unregister(this->event_write); - if(this->file_descriptor > 0) { - close(this->file_descriptor); - this->file_descriptor = 0; - } + if(this->file_descriptor > 0) { + close(this->file_descriptor); + this->file_descriptor = 0; + } } void TSWebClient::reset_timeout(bool reschedule) { - if(this->event_timeout) { - event_del(this->event_timeout); - if(reschedule) { - timeval timeout{0}; - timeout.tv_sec = 5; - event_add(this->event_timeout, &timeout); - } - } + if(this->event_timeout) { + event_del(this->event_timeout); + if(reschedule) { + timeval timeout{0}; + timeout.tv_sec = 5; + event_add(this->event_timeout, &timeout); + } + } } void TSWebClient::trigger_fail_later(const std::string &error, bool retry) { - if(this->state >= S_FINALIZING || this->close_thread.joinable()) return; - this->state = S_FINALIZING; - this->close_thread = std::thread([&, error, retry](){ - this->unregister_events(true); - if(this->callback_error) - this->callback_error(error,retry); - }); + if(this->state >= S_FINALIZING || this->close_thread.joinable()) return; + this->state = S_FINALIZING; + this->close_thread = std::thread([&, error, retry](){ + this->unregister_events(true); + if(this->callback_error) + this->callback_error(error,retry); + }); } void TSWebClient::trigger_success_later() { - if(this->state >= S_FINALIZING || this->close_thread.joinable()) return; - this->state = S_FINALIZING; - this->close_thread = std::thread([&](){ - this->unregister_events(true); - if(this->callback_success) - this->callback_success(); - }); + if(this->state >= S_FINALIZING || this->close_thread.joinable()) return; + this->state = S_FINALIZING; + this->close_thread = std::thread([&](){ + this->unregister_events(true); + if(this->callback_success) + this->callback_success(); + }); } void TSWebClient::write_message(const pipes::buffer_view &buffer) { - if(this->state >= S_FINALIZING) return; + if(this->state >= S_FINALIZING) return; - { - std::lock_guard lock(this->write_lock); - this->write_buffer.push_back(buffer.own_buffer()); - } + { + std::lock_guard lock(this->write_lock); + this->write_buffer.push_back(buffer.own_buffer()); + } - if(this->event_write) - event_add(this->event_write, nullptr); + if(this->event_write) + event_add(this->event_write, nullptr); } #define BASE_PORT 30566 void TSWebClient::report() { - this->state = S_INITIALIZING; + this->state = S_INITIALIZING; - memset(&this->remote_address, 0, sizeof(sockaddr_in)); - this->remote_address.sin_family = AF_INET; - this->remote_address.sin_port = htons(2010); - { - auto record = gethostbyname("weblist.teamspeak.com"); - if(!record) { - this->trigger_fail_later("Failed to resolve weblist.teamspeak.com", true); - return; - } - this->remote_address.sin_addr.s_addr = ((in_addr*) record->h_addr)->s_addr; - } + memset(&this->remote_address, 0, sizeof(sockaddr_in)); + this->remote_address.sin_family = AF_INET; + this->remote_address.sin_port = htons(2010); + { + auto record = gethostbyname("weblist.teamspeak.com"); + if(!record) { + this->trigger_fail_later("Failed to resolve weblist.teamspeak.com", true); + return; + } + this->remote_address.sin_addr.s_addr = ((in_addr*) record->h_addr)->s_addr; + } - this->file_descriptor = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); - if(this->file_descriptor <= 0) { - this->trigger_fail_later("Failed to create file descriptor", true); - return; - } + this->file_descriptor = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); + if(this->file_descriptor <= 0) { + this->trigger_fail_later("Failed to create file descriptor", true); + return; + } - if(fcntl(this->file_descriptor, F_SETFD, FD_CLOEXEC) < 0) { - this->trigger_fail_later("Failed to enable FD_CLOEXEC for {} (WebReport)", true); - return; - } + if(fcntl(this->file_descriptor, F_SETFD, FD_CLOEXEC) < 0) { + this->trigger_fail_later("Failed to enable FD_CLOEXEC for {} (WebReport)", true); + return; + } - int allow = 1; - setsockopt(this->file_descriptor, SOL_SOCKET, SO_REUSEADDR, &allow, sizeof(int)); + int allow = 1; + setsockopt(this->file_descriptor, SOL_SOCKET, SO_REUSEADDR, &allow, sizeof(int)); - { - sockaddr_in address{}; - address.sin_family = AF_INET; - address.sin_port = htons(BASE_PORT + this->server->getServerId()); - address.sin_addr.s_addr = INADDR_ANY; + { + sockaddr_in address{}; + address.sin_family = AF_INET; + address.sin_port = htons(BASE_PORT + this->server->getServerId()); + address.sin_addr.s_addr = INADDR_ANY; - if(this->server->getVoiceServer()) { - for(const auto& binding : this->server->getVoiceServer()->activeBindings()) { - if(binding->address.ss_family != AF_INET) continue; + if(this->server->getVoiceServer()) { + for(const auto& binding : this->server->getVoiceServer()->activeBindings()) { + if(binding->address.ss_family != AF_INET) continue; - address.sin_addr.s_addr = ((sockaddr_in*) &binding->address)->sin_addr.s_addr; - debugMessage(this->server->getServerId(), "[WebList] Requesting from local address {}:{}", binding->address_string(), ntohs(address.sin_port)); - break; - } - } + address.sin_addr.s_addr = ((sockaddr_in*) &binding->address)->sin_addr.s_addr; + debugMessage(this->server->getServerId(), "[WebList] Requesting from local address {}:{}", binding->address_string(), ntohs(address.sin_port)); + break; + } + } - if(bind(this->file_descriptor, (const sockaddr *) &address, sizeof(address)) < 0) { - this->trigger_fail_later("Failed to bind socket", true); - return; - } - } + if(bind(this->file_descriptor, (const sockaddr *) &address, sizeof(address)) < 0) { + this->trigger_fail_later("Failed to bind socket", true); + return; + } + } - this->event_read = event_new(this->event_base, this->file_descriptor, EV_READ | EV_PERSIST, TSWebClient::_handle_message_read, this); - this->event_write = event_new(this->event_base, this->file_descriptor, EV_WRITE, TSWebClient::_handle_message_write, this); - this->event_timeout = event_new(this->event_base, -1, 0, TSWebClient::_handle_timeout, this); - event_add(this->event_read, nullptr); + this->event_read = event_new(this->event_base, this->file_descriptor, EV_READ | EV_PERSIST, TSWebClient::_handle_message_read, this); + this->event_write = event_new(this->event_base, this->file_descriptor, EV_WRITE, TSWebClient::_handle_message_write, this); + this->event_timeout = event_new(this->event_base, -1, 0, TSWebClient::_handle_timeout, this); + event_add(this->event_read, nullptr); - this->reset_timeout(true); - this->request_session(); + this->reset_timeout(true); + this->request_session(); } void TSWebClient::abort() { - this->trigger_fail_later("aborted", false); + this->trigger_fail_later("aborted", false); } void TSWebClient::abort_sync() { - this->unregister_events(true); - if(this->callback_error) - this->callback_error("aborted", false); + this->unregister_events(true); + if(this->callback_error) + this->callback_error("aborted", false); } inline char nibble(int c) { - assert(c < 16); + assert(c < 16); - if(c >= 0 && c <= 9) - return (char) ('0' + c); - else - return (char) ('A' + (c - 10)); + if(c >= 0 && c <= 9) + return (char) ('0' + c); + else + return (char) ('A' + (c - 10)); } inline std::string hex(const pipes::buffer_view &buffer) { - std::string result; - for(size_t index = 0; index < buffer.length(); index++) - result += std::string() + "0x" + nibble(buffer[index] >> 4) + nibble(buffer[index] & 0x0F) + " "; + std::string result; + for(size_t index = 0; index < buffer.length(); index++) + result += std::string() + "0x" + nibble(buffer[index] >> 4) + nibble(buffer[index] & 0x0F) + " "; - if(result.empty()) return result; - return result.substr(0, result.length() - 1); + if(result.empty()) return result; + return result.substr(0, result.length() - 1); } void TSWebClient::handle_message_read(const pipes::buffer_view &buffer) { - this->reset_timeout(true); - if(this->state == S_INITIALIZING) { - if(buffer.length() != 8) { - this->trigger_fail_later("Failed to receive key. (Invalid packet length of " + std::to_string(buffer.length()) + ". Expected 8. Buffer: " + hex(buffer) + ")", true); - return; - } + this->reset_timeout(true); + if(this->state == S_INITIALIZING) { + if(buffer.length() != 8) { + this->trigger_fail_later("Failed to receive key. (Invalid packet length of " + std::to_string(buffer.length()) + ". Expected 8. Buffer: " + hex(buffer) + ")", true); + return; + } - this->session_id = *(uint32_t*) &buffer[4]; - this->send_status(this->send_name); - } else if(this->state == S_DATA_EXCHANGE) { - if(buffer.length() != 5) { - this->trigger_fail_later("Failed to receive exchange stats. (Invalid packet length of " + std::to_string(buffer.length()) + ". Expected 5. Buffer: " + hex(buffer) + ")", true); - return; - } + this->session_id = *(uint32_t*) &buffer[4]; + this->send_status(this->send_name); + } else if(this->state == S_DATA_EXCHANGE) { + if(buffer.length() != 5) { + this->trigger_fail_later("Failed to receive exchange stats. (Invalid packet length of " + std::to_string(buffer.length()) + ". Expected 5. Buffer: " + hex(buffer) + ")", true); + return; + } - int status = buffer[4]; - if(status == 7) { - this->send_name = true; - debugMessage(this->server->getServerId(), "[WebList] Received exchange status 7. Resending with server name."); - this->send_status(true); - return; - } else if(status == 0) { - this->trigger_success_later(); - return; - } else { - debugMessage(this->server->getServerId(), "[WebList] Weblist request failed. Status code {}. Buffer: {}", status, hex(buffer)); - this->trigger_fail_later("Invalid exchange status. Status: " + std::to_string(status), status != 5); - return; - } - } + int status = buffer[4]; + if(status == 7) { + this->send_name = true; + debugMessage(this->server->getServerId(), "[WebList] Received exchange status 7. Resending with server name."); + this->send_status(true); + return; + } else if(status == 0) { + this->trigger_success_later(); + return; + } else { + debugMessage(this->server->getServerId(), "[WebList] Weblist request failed. Status code {}. Buffer: {}", status, hex(buffer)); + this->trigger_fail_later("Invalid exchange status. Status: " + std::to_string(status), status != 5); + return; + } + } } void TSWebClient::request_session() { - this->state = S_INITIALIZING; + this->state = S_INITIALIZING; - pipes::buffer buffer{4}; - buffer[0] = 1; //Version - buffer[1] = (uint8_t) (session_index >> 8); //Packet number - buffer[2] = (uint8_t) (session_index & 0xFF); - buffer[3] = 1; //Request session + pipes::buffer buffer{4}; + buffer[0] = 1; //Version + buffer[1] = (uint8_t) (session_index >> 8); //Packet number + buffer[2] = (uint8_t) (session_index & 0xFF); + buffer[3] = 1; //Request session - this->write_message(buffer); + this->write_message(buffer); } void TSWebClient::send_status(bool flag_name) { - this->state = S_DATA_EXCHANGE; + this->state = S_DATA_EXCHANGE; - auto name = this->server->getDisplayName(); - pipes::buffer buffer{4 + 4 + 3 * 2 + 1 + 1 + (flag_name ? name.length() : 0)}; + auto name = this->server->getDisplayName(); + pipes::buffer buffer{4 + 4 + 3 * 2 + 1 + 1 + (flag_name ? name.length() : 0)}; - buffer[0] = 1; //Version - buffer[1] = (uint8_t) (session_index >> 8); //Packet number - buffer[2] = (uint8_t) (session_index & 0xFF); - buffer[3] = 2; //Status update + buffer[0] = 1; //Version + buffer[1] = (uint8_t) (session_index >> 8); //Packet number + buffer[2] = (uint8_t) (session_index & 0xFF); + buffer[3] = 2; //Status update - *(uint32_t*) &buffer[4] = this->session_id; - *(uint16_t*) &buffer[8] = this->server->properties()[property::VIRTUALSERVER_PORT].as(); - *(uint16_t*) &buffer[10] = this->server->properties()[property::VIRTUALSERVER_MAXCLIENTS].as(); - *(uint16_t*) &buffer[12] = this->server->properties()[property::VIRTUALSERVER_CLIENTS_ONLINE].as(); + *(uint32_t*) &buffer[4] = this->session_id; + *(uint16_t*) &buffer[8] = this->server->properties()[property::VIRTUALSERVER_PORT].as(); + *(uint16_t*) &buffer[10] = this->server->properties()[property::VIRTUALSERVER_MAXCLIENTS].as(); + *(uint16_t*) &buffer[12] = this->server->properties()[property::VIRTUALSERVER_CLIENTS_ONLINE].as(); - bool flag_channel = this->server->could_default_create_channel(); - bool flag_password = this->server->properties()[property::VIRTUALSERVER_FLAG_PASSWORD].as(); - debugMessage(this->server->getServerId(), "[WebList] Sending properties: flag_channel: {}, flag_password: {}", flag_channel, flag_password); - buffer[14] = (flag_password ? 0x01 : 0) | (flag_channel ? 0x02 : 0); //Create channels (0x01 = password) + bool flag_channel = this->server->could_default_create_channel(); + bool flag_password = this->server->properties()[property::VIRTUALSERVER_FLAG_PASSWORD].as(); + debugMessage(this->server->getServerId(), "[WebList] Sending properties: flag_channel: {}, flag_password: {}", flag_channel, flag_password); + buffer[14] = (flag_password ? 0x01 : 0) | (flag_channel ? 0x02 : 0); //Create channels (0x01 = password) - if(flag_name) { - buffer[15] = (uint8_t) name.length(); - if(name.length() > 0) { - memcpy(&buffer[16], name.c_str(), name.length()); - } - } else { - buffer[15] = 0; //We dont want to send a server name - } + if(flag_name) { + buffer[15] = (uint8_t) name.length(); + if(name.length() > 0) { + memcpy(&buffer[16], name.c_str(), name.length()); + } + } else { + buffer[15] = 0; //We dont want to send a server name + } - this->write_message(buffer); + this->write_message(buffer); } \ No newline at end of file diff --git a/server/src/weblist/TeamSpeakWebClient.h b/server/src/weblist/TeamSpeakWebClient.h index 23634d2..a5d8319 100644 --- a/server/src/weblist/TeamSpeakWebClient.h +++ b/server/src/weblist/TeamSpeakWebClient.h @@ -6,67 +6,67 @@ #include namespace ts { - namespace server { - class TSServer; - } + namespace server { + class TSServer; + } - namespace weblist { - class TSWebClient { - public: - enum State { - S_UNINITIALIZED, - S_INITIALIZING, - S_DATA_EXCHANGE, - S_FINALIZING, - S_FINALIZED - }; + namespace weblist { + class TSWebClient { + public: + enum State { + S_UNINITIALIZED, + S_INITIALIZING, + S_DATA_EXCHANGE, + S_FINALIZING, + S_FINALIZED + }; - TSWebClient(const std::shared_ptr &server, struct event_base *event_base, uint16_t session_index, bool /* resend name */); - virtual ~TSWebClient(); + TSWebClient(const std::shared_ptr &server, struct event_base *event_base, uint16_t session_index, bool /* resend name */); + virtual ~TSWebClient(); - void report(); - void abort(); - void abort_sync(); - inline bool running() { return this->state != S_FINALIZED && this->state != S_UNINITIALIZED; } + void report(); + void abort(); + void abort_sync(); + inline bool running() { return this->state != S_FINALIZED && this->state != S_UNINITIALIZED; } - std::function callback_error; - std::function callback_success; - private: - std::shared_ptr server; - struct event_base* event_base = nullptr; - std::thread close_thread; - State state; + std::function callback_error; + std::function callback_success; + private: + std::shared_ptr server; + struct event_base* event_base = nullptr; + std::thread close_thread; + State state; - sockaddr_in remote_address{}; - std::mutex write_lock; - std::deque write_buffer{}; - int file_descriptor{0}; + sockaddr_in remote_address{}; + std::mutex write_lock; + std::deque write_buffer{}; + int file_descriptor{0}; - struct event* event_read = nullptr; - struct event* event_write = nullptr; - struct event* event_timeout = nullptr; + struct event* event_read = nullptr; + struct event* event_write = nullptr; + struct event* event_timeout = nullptr; - void unregister_events(bool /* blocking */); - void reset_timeout(bool /* reschedule */); + void unregister_events(bool /* blocking */); + void reset_timeout(bool /* reschedule */); - static void _handle_message_read(int, short, void*); - static void _handle_message_write(int, short, void*); - static void _handle_timeout(int, short,void*); + static void _handle_message_read(int, short, void*); + static void _handle_message_write(int, short, void*); + static void _handle_timeout(int, short,void*); - void trigger_fail_later(const std::string& /* message */, bool /* retry */); - void trigger_success_later(); + void trigger_fail_later(const std::string& /* message */, bool /* retry */); + void trigger_success_later(); - void write_message(const pipes::buffer_view& /* buffer */); + void write_message(const pipes::buffer_view& /* buffer */); - private: - void handle_message_read(const pipes::buffer_view& /* buffer */); + private: + void handle_message_read(const pipes::buffer_view& /* buffer */); - uint16_t session_index = 1; - uint32_t session_id = 0; - bool send_name = false; + uint16_t session_index = 1; + uint32_t session_id = 0; + bool send_name = false; - void request_session(); - void send_status(bool /* name */); - }; - } + void request_session(); + void send_status(bool /* name */); + }; + } } \ No newline at end of file diff --git a/server/src/weblist/WebListManager.cpp b/server/src/weblist/WebListManager.cpp index 7094819..0ddec06 100644 --- a/server/src/weblist/WebListManager.cpp +++ b/server/src/weblist/WebListManager.cpp @@ -12,127 +12,127 @@ using namespace ts::server; using namespace ts::weblist; WebListManager::WebListManager() { - this->event_base = event_base_new(); + this->event_base = event_base_new(); - this->event_base_dispatch = std::thread([&](){ - system_clock::time_point start; - while(this->event_base) { - start = system_clock::now(); - ::event_base_dispatch(this->event_base); - if(system_clock::now() - start < seconds(1)) - this_thread::sleep_for(seconds(1)); - } - }); + this->event_base_dispatch = std::thread([&](){ + system_clock::time_point start; + while(this->event_base) { + start = system_clock::now(); + ::event_base_dispatch(this->event_base); + if(system_clock::now() - start < seconds(1)) + this_thread::sleep_for(seconds(1)); + } + }); } WebListManager::~WebListManager() { - auto base = this->event_base; - this->event_base = nullptr; - event_base_loopbreak(base); - this->event_base_dispatch.join(); + auto base = this->event_base; + this->event_base = nullptr; + event_base_loopbreak(base); + this->event_base_dispatch.join(); - event_base_free(base); + event_base_free(base); } void WebListManager::enable_report(const std::shared_ptr &server) { - { - unique_lock lock(this->entry_lock); - for(const auto& entry : this->entries) { - if(entry->server == server) - return; - } + { + unique_lock lock(this->entry_lock); + for(const auto& entry : this->entries) { + if(entry->server == server) + return; + } - auto entry = make_shared(); - entry->server = server; - entry->scheduled_request = system_clock::now(); - entry->fail_count = 0; - this->entries.push_back(entry); - } + auto entry = make_shared(); + entry->server = server; + entry->scheduled_request = system_clock::now(); + entry->fail_count = 0; + this->entries.push_back(entry); + } } bool WebListManager::reports_enabled(const std::shared_ptr &server) { - { - lock_guard lock(this->entry_lock); - for(const auto& entry : this->entries) { - if(entry->server == server) return true; - } - } - return false; + { + lock_guard lock(this->entry_lock); + for(const auto& entry : this->entries) { + if(entry->server == server) return true; + } + } + return false; } void WebListManager::disable_report(const std::shared_ptr &server) { - if(!server) return; + if(!server) return; - unique_lock lock(this->entry_lock); - for(const auto& entry : this->entries) { - if(entry->server == server) { - shared_ptr copied_entry = entry; //Copy it before erasing + unique_lock lock(this->entry_lock); + for(const auto& entry : this->entries) { + if(entry->server == server) { + shared_ptr copied_entry = entry; //Copy it before erasing - auto it = find(this->entries.begin(), this->entries.end(), copied_entry); - if(it != this->entries.end()) - this->entries.erase(it); - if(copied_entry->current_request) { - lock.unlock(); - copied_entry->current_request->abort_sync(); - } - return; - } - } + auto it = find(this->entries.begin(), this->entries.end(), copied_entry); + if(it != this->entries.end()) + this->entries.erase(it); + if(copied_entry->current_request) { + lock.unlock(); + copied_entry->current_request->abort_sync(); + } + return; + } + } } void WebListManager::tick() { - if(!this->enabled) return; + if(!this->enabled) return; - unique_lock lock(this->entry_lock); - auto entries = this->entries; - lock.unlock(); + unique_lock lock(this->entry_lock); + auto entries = this->entries; + lock.unlock(); - auto now = system_clock::now(); - for(const auto& entry : entries) { - if(entry->scheduled_request < now && (!entry->current_request || entry->scheduled_request + minutes(5) < now)) { - entry->current_request = make_shared(entry->server, this->event_base, entry->session_count, entry->last_name != entry->server->getDisplayName()); + auto now = system_clock::now(); + for(const auto& entry : entries) { + if(entry->scheduled_request < now && (!entry->current_request || entry->scheduled_request + minutes(5) < now)) { + entry->current_request = make_shared(entry->server, this->event_base, entry->session_count, entry->last_name != entry->server->getDisplayName()); - weak_ptr weak_entry = entry; - entry->current_request->callback_success = [weak_entry, now](){ - auto _entry = weak_entry.lock(); - if(!_entry) return; + weak_ptr weak_entry = entry; + entry->current_request->callback_success = [weak_entry, now](){ + auto _entry = weak_entry.lock(); + if(!_entry) return; - _entry->fail_count = 0; - _entry->scheduled_request = now + minutes(10); - _entry->session_count++; - _entry->last_name = _entry->server->getDisplayName(); - logMessage(_entry->server->getServerId(), "[WebList] Status update succeeded! Scheduling next update in ten minutes."); + _entry->fail_count = 0; + _entry->scheduled_request = now + minutes(10); + _entry->session_count++; + _entry->last_name = _entry->server->getDisplayName(); + logMessage(_entry->server->getServerId(), "[WebList] Status update succeeded! Scheduling next update in ten minutes."); - _entry->current_request.reset(); - }; - entry->current_request->callback_error = [weak_entry, now](auto error, bool retry) { - auto _entry = weak_entry.lock(); - if(!_entry) return; + _entry->current_request.reset(); + }; + entry->current_request->callback_error = [weak_entry, now](auto error, bool retry) { + auto _entry = weak_entry.lock(); + if(!_entry) return; - _entry->fail_count++; - logError(_entry->server->getServerId(), "[WebList] Status update failed. Error: " + error); + _entry->fail_count++; + logError(_entry->server->getServerId(), "[WebList] Status update failed. Error: " + error); - if(_entry->fail_count == 1 && retry) { - logMessage(_entry->server->getServerId(), "[WebList] Scheduling next update attempt in 5 seconds."); - _entry->scheduled_request = now + seconds(5); - } else if(_entry->fail_count == 2 && retry) { - logMessage(_entry->server->getServerId(), "[WebList] Scheduling next update attempt in 30 seconds."); - _entry->scheduled_request = now + seconds(30); - } else if(_entry->fail_count == 3 && retry) { - logMessage(_entry->server->getServerId(), "[WebList] Scheduling next update attempt in 1 minute."); - _entry->scheduled_request = now + seconds(60); - } else if(_entry->fail_count >= 4) { - logMessage(_entry->server->getServerId(), "[WebList] Scheduling next update attempt in 10 minutes."); - _entry->scheduled_request = now + minutes(10); - } + if(_entry->fail_count == 1 && retry) { + logMessage(_entry->server->getServerId(), "[WebList] Scheduling next update attempt in 5 seconds."); + _entry->scheduled_request = now + seconds(5); + } else if(_entry->fail_count == 2 && retry) { + logMessage(_entry->server->getServerId(), "[WebList] Scheduling next update attempt in 30 seconds."); + _entry->scheduled_request = now + seconds(30); + } else if(_entry->fail_count == 3 && retry) { + logMessage(_entry->server->getServerId(), "[WebList] Scheduling next update attempt in 1 minute."); + _entry->scheduled_request = now + seconds(60); + } else if(_entry->fail_count >= 4) { + logMessage(_entry->server->getServerId(), "[WebList] Scheduling next update attempt in 10 minutes."); + _entry->scheduled_request = now + minutes(10); + } - _entry->current_request.reset(); - }; + _entry->current_request.reset(); + }; - { - /* could be blocking due to gethostbyname. We dont want to have the request deallocated */ - auto ref_request = entry->current_request; - ref_request->report(); - } - } - } + { + /* could be blocking due to gethostbyname. We dont want to have the request deallocated */ + auto ref_request = entry->current_request; + ref_request->report(); + } + } + } } \ No newline at end of file diff --git a/server/src/weblist/WebListManager.h b/server/src/weblist/WebListManager.h index f43bb20..c2f84b2 100644 --- a/server/src/weblist/WebListManager.h +++ b/server/src/weblist/WebListManager.h @@ -8,41 +8,41 @@ struct event_base; namespace ts { - namespace server { - class TSServer; - } - namespace weblist { - class TSWebClient; - class WebListManager { - private: - struct Entry { - std::shared_ptr server; - std::shared_ptr current_request; + namespace server { + class TSServer; + } + namespace weblist { + class TSWebClient; + class WebListManager { + private: + struct Entry { + std::shared_ptr server; + std::shared_ptr current_request; - std::chrono::system_clock::time_point last_success; - std::chrono::system_clock::time_point scheduled_request; - int fail_count = 0; + std::chrono::system_clock::time_point last_success; + std::chrono::system_clock::time_point scheduled_request; + int fail_count = 0; - uint16_t session_count = 1; - std::string last_name; - }; - public: - WebListManager(); - ~WebListManager(); + uint16_t session_count = 1; + std::string last_name; + }; + public: + WebListManager(); + ~WebListManager(); - void enable_report(const std::shared_ptr& /* server */); - bool reports_enabled(const std::shared_ptr& /* server */); - void disable_report(const std::shared_ptr& /* server */); + void enable_report(const std::shared_ptr& /* server */); + bool reports_enabled(const std::shared_ptr& /* server */); + void disable_report(const std::shared_ptr& /* server */); - void tick(); + void tick(); - bool enabled = false; - private: - struct event_base* event_base = nullptr; - std::thread event_base_dispatch; + bool enabled = false; + private: + struct event_base* event_base = nullptr; + std::thread event_base_dispatch; - std::mutex entry_lock; - std::deque> entries; - }; - } + std::mutex entry_lock; + std::deque> entries; + }; + } } \ No newline at end of file