From 97fd71b69a88a189d3d24a527bea1c8442810e07 Mon Sep 17 00:00:00 2001 From: WolverinDEV Date: Sat, 15 Feb 2020 14:03:32 +0100 Subject: [PATCH] Some updates --- CMakeLists.txt | 572 +++---- src/Error.cpp | 412 ++--- src/Error.h | 790 +++++----- src/EventLoop.h | 178 +-- src/PermissionManager.cpp | 3040 ++++++++++++++++++------------------- src/PermissionManager.h | 1900 +++++++++++------------ src/protocol/Packet.cpp | 482 +++--- src/protocol/Packet.h | 740 ++++----- src/query/Command.h | 438 +++--- src/sql/mysql/MySQL.cpp | 1714 ++++++++++----------- src/sql/mysql/MySQL.h | 146 +- 11 files changed, 5206 insertions(+), 5206 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 6c27c5b..10c51b3 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,286 +1,286 @@ -cmake_minimum_required(VERSION 3.6) -project(TeaSpeak-Shared) -set(CMAKE_CXX_STANDARD 20) - -if(NOT WIN32) - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fPIC -Wall -Wno-reorder -Wno-sign-compare -fpermissive -ftemplate-depth=1000 ${MEMORY_DEBUG_FLAGS}") - set(CMAKE_INCLUDE_CURRENT_DIR ON) -else() - #For Windows - add_definitions(-D_SILENCE_CXX17_OLD_ALLOCATOR_MEMBERS_DEPRECATION_WARNING) - - add_compile_options(/wd4996) #'std::result_of_t': warning STL4014: std::result_of and std::result_of_t are deprecated in C++17. -endif() - -if(CMAKE_PLATFORM_INCLUDE AND NOT CMAKE_PLATFORM_INCLUDE STREQUAL "") - include(${CMAKE_PLATFORM_INCLUDE}) -endif() - -find_package(TomMath REQUIRED) -include_directories(${TomMath_INCLUDE_DIR}) - -find_package(TomCrypt REQUIRED) -include_directories(${TomCrypt_INCLUDE_DIR}) - -find_package(DataPipes REQUIRED) -include_directories(${DataPipes_INCLUDE_DIR}) - -# LibEvent fucks up the CMAKE_FIND_LIBRARY_SUFFIXES variable -if (NOT find_event) - function(find_event static) - if(static) - set(LIBEVENT_STATIC_LINK TRUE) - endif() - find_package(Libevent REQUIRED) - include_directories(${LIBEVENT_INCLUDE_DIRS}) - endfunction() -endif () -find_event(ON) - -find_package(StringVariable REQUIRED) -include_directories(${StringVariable_INCLUDE_DIR}) - -find_package(Ed25519 REQUIRED) -include_directories(${ed25519_INCLUDE_DIR}) - -find_package(ThreadPool REQUIRED) -include_directories(${ThreadPool_INCLUDE_DIR}) -if(WIN32) - add_definitions(-DWINDOWS) #Required for ThreadPool -endif() -find_package(spdlog REQUIRED) -link_libraries(spdlog::spdlog_header_only) #Its a header only lib so we should be fine :) - -if(NOT TEASPEAK_SERVER) - add_definitions(-DNO_OPEN_SSL) - add_definitions(-D_HAS_STD_BYTE) - #FML -else() - find_package(CXXTerminal REQUIRED) - - add_definitions(-DHAVE_CXX_TERMINAL) - add_definitions(-DHAVE_JSON) - set(HAVE_SQLITE3 ON) - set(HAVE_OPEN_SSL ON) - message("HAVE JSON!") -endif() - -if (MSVC) - set(CompilerFlags - CMAKE_CXX_FLAGS - CMAKE_CXX_FLAGS_DEBUG - CMAKE_CXX_FLAGS_RELEASE - CMAKE_CXX_FLAGS_RELWITHDEBINFO - CMAKE_C_FLAGS_RELWITHDEBINFO - CMAKE_C_FLAGS - CMAKE_C_FLAGS_DEBUG - CMAKE_C_FLAGS_RELEASE - ) - foreach(CompilerFlag ${CompilerFlags}) - string(REPLACE "/MD" "/MT" ${CompilerFlag} "${${CompilerFlag}}") - endforeach() - add_compile_options("/EHsc") #We require exception handling -else() - set(CMAKE_CXX_FLAGS_RELEASE "-O3") #-DNDEBUG We want assert! -endif() -add_definitions(-DUSE_BORINGSSL) -include_directories(${LIBRARY_PATH}/boringssl/include/) -set(SOURCE_FILES - src/misc/rnd.cpp - src/misc/time.cpp - src/misc/memtracker.cpp - src/misc/digest.cpp - src/misc/base64.cpp - - #Logger - src/log/LogUtils.cpp - src/log/LogSinks.cpp - - - src/qlz/QuickLZ.cpp - src/qlz/QuickLZ_L3.cpp - src/qlz/QuickLZ_L1.cpp - - src/converters/converter.cpp - - src/query/command3.cpp - src/query/command2.cpp - src/query/Command.cpp - src/query/escape.cpp - - src/protocol/generation.cpp - src/protocol/Packet.cpp - src/protocol/buffers.cpp - src/protocol/buffers_allocator_c.cpp - src/PermissionManager.cpp - src/Properties.cpp - src/BasicChannel.cpp - src/Error.cpp - src/protocol/CryptHandler.cpp - src/protocol/CompressionHandler.cpp - src/Variable.cpp - src/linked_helper.cpp - src/EventLoop.cpp - - src/License.cpp - - src/bbcode/bbcodes.cpp - - src/channel/TreeView.cpp - src/protocol/ringbuffer.cpp - src/protocol/AcknowledgeManager.cpp -) - -set(HEADER_FILES - src/misc/base64.h - src/misc/endianness.h - src/misc/cast.h - src/misc/rnd.h - src/misc/time.h - src/misc/std_unique_ptr.h - src/misc/net.h - src/misc/lambda.h - src/misc/hex.h - src/misc/advanced_mutex.h - src/misc/memtracker.h - src/misc/strobf.h - - src/log/translation.h - src/log/LogUtils.h - - src/PermissionManager.h - src/protocol/buffers.h - src/protocol/Packet.h - src/Properties.h - src/BasicChannel.h - src/Definitions.h - src/Error.h - src/protocol/CryptHandler.h - src/Variable.h - src/misc/queue.h - - src/misc/digest.h - - src/bbcode/bbcodes.h - - src/channel/TreeView.h -) - -if(HAVE_SQLITE3) - set(SOURCE_FILES ${SOURCE_FILES} - src/sql/SqlQuery.cpp - src/sql/sqlite/SqliteSQL.cpp - src/sql/mysql/MySQL.cpp - ) - set(HEADER_FILES ${HEADER_FILES} - src/sql/SqlQuery.h - src/sql/sqlite/SqliteSQL.h - src/sql/mysql/MySQL.h - ) -endif() - -if(HAVE_OPEN_SSL) - set(SOURCE_FILES ${SOURCE_FILES} - src/ssl/SSLManager.cpp - ) - set(HEADER_FILES ${HEADER_FILES} - src/ssl/SSLManager.h - ) -endif() - -add_library(TeaSpeak STATIC ${SOURCE_FILES} ${HEADER_FILES}) -target_link_libraries(TeaSpeak PUBLIC threadpool::static jsoncpp_lib) -target_compile_options(TeaSpeak PRIVATE "-Wall") - -if (TEASPEAK_SERVER) - target_link_libraries(TeaSpeak PUBLIC CXXTerminal::static) -endif () -target_include_directories(TeaSpeak PUBLIC src/) -install(TARGETS TeaSpeak - ARCHIVE DESTINATION lib -) -INSTALL ( - DIRECTORY ${CMAKE_SOURCE_DIR}/src/ - DESTINATION include - FILES_MATCHING PATTERN "*.h*" -) - -set(TEST_LIBRARIES - threadpool::static #Static - TeaSpeak #Static - TeaLicenseHelper #Static - TeaMusic #Static - CXXTerminal::static #Static - ${StringVariable_LIBRARIES_STATIC} - ${YAML_CPP_LIBRARIES} - pthread - stdc++fs - libevent::core libevent::pthreads - opus::static - yaml-cpp - ${LIBRARY_PATH_PROTOBUF} - - #We're forsed to use boringssl caused by the fact that boringssl is already within webrtc! - - #Require a so - sqlite3 - - breakpad::static - protobuf::libprotobuf - jemalloc::shared - - tomcrypt::static - tommath::static - - mysqlclient.a - jsoncpp_lib - ${ed25519_LIBRARIES_STATIC} - ${DataPipes_LIBRARIES_SHARED} # Also includes glib2.0 - - openssl::ssl::shared - openssl::crypto::shared - dl - z -) -include_directories(src/) -option(BUILD_TESTS "Enable/disable test building" ON) -if(BUILD_TESTS) - add_executable(RingTest test/RingTest.cpp ${SOURCE_FILES}) - target_link_libraries(RingTest ${TEST_LIBRARIES}) - - if(NOT WIN32) - add_executable(CommandTest ${SOURCE_FILES} ${HEADER_FILES} test/CommandTest.cpp src/log/LogSinks.cpp src/log/LogSinks.h ../server/MySQLLibSSLFix.c) - target_link_libraries(CommandTest ${TEST_LIBRARIES}) - - add_executable(WebsocketTest ${SOURCE_FILES} ${HEADER_FILES} test/WSSTest.cpp src/log/LogSinks.cpp src/log/LogSinks.h) - target_link_libraries(WebsocketTest ${TEST_LIBRARIES}) - - add_executable(SQLTest ${SOURCE_FILES} ${HEADER_FILES} test/SQLTest.cpp src/log/LogSinks.cpp src/log/LogSinks.h) - target_link_libraries(SQLTest ${TEST_LIBRARIES}) - - add_executable(ChannelTest ${SOURCE_FILES} ${HEADER_FILES} test/ChannelTest.cpp src/log/LogSinks.cpp src/log/LogSinks.h) - target_link_libraries(ChannelTest ${TEST_LIBRARIES}) - - add_executable(EndianessTest ${SOURCE_FILES} ${HEADER_FILES} test/EndianessTest.cpp src/log/LogSinks.cpp src/log/LogSinks.h) - target_link_libraries(EndianessTest ${TEST_LIBRARIES}) - - include_directories(/usr/local/include/breakpad) - add_executable(CrashTest test/CrashTest.cpp ${SOURCE_FILES}) - target_link_libraries(CrashTest ${TEST_LIBRARIES}) - - add_executable(PorpertyTest test/PropertyTest.cpp ${SOURCE_FILES}) - target_link_libraries(PorpertyTest ${TEST_LIBRARIES}) - - add_executable(BBTest test/BBTest.cpp ${SOURCE_FILES} src/query/command_unused.h) - target_link_libraries(BBTest ${TEST_LIBRARIES}) - - add_executable(LinkedTest test/LinkedTest.cpp ${SOURCE_FILES}) - target_link_libraries(LinkedTest ${TEST_LIBRARIES}) - - add_executable(PermissionTest test/PermissionTest.cpp ${SOURCE_FILES}) - target_link_libraries(PermissionTest ${TEST_LIBRARIES}) - - add_executable(GenerationTest test/generationTest.cpp ${SOURCE_FILES} ../server/MySQLLibSSLFix.c) - target_link_libraries(GenerationTest ${TEST_LIBRARIES}) - endif() -endif() +cmake_minimum_required(VERSION 3.6) +project(TeaSpeak-Shared) +set(CMAKE_CXX_STANDARD 20) + +if(NOT WIN32) + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fPIC -Wall -Wno-reorder -Wno-sign-compare -fpermissive -ftemplate-depth=1000 ${MEMORY_DEBUG_FLAGS}") + set(CMAKE_INCLUDE_CURRENT_DIR ON) +else() + #For Windows + add_definitions(-D_SILENCE_CXX17_OLD_ALLOCATOR_MEMBERS_DEPRECATION_WARNING) + + add_compile_options(/wd4996) #'std::result_of_t': warning STL4014: std::result_of and std::result_of_t are deprecated in C++17. +endif() + +if(CMAKE_PLATFORM_INCLUDE AND NOT CMAKE_PLATFORM_INCLUDE STREQUAL "") + include(${CMAKE_PLATFORM_INCLUDE}) +endif() + +find_package(TomMath REQUIRED) +include_directories(${TomMath_INCLUDE_DIR}) + +find_package(TomCrypt REQUIRED) +include_directories(${TomCrypt_INCLUDE_DIR}) + +find_package(DataPipes REQUIRED) +include_directories(${DataPipes_INCLUDE_DIR}) + +# LibEvent fucks up the CMAKE_FIND_LIBRARY_SUFFIXES variable +if (NOT find_event) + function(find_event static) + if(static) + set(LIBEVENT_STATIC_LINK TRUE) + endif() + find_package(Libevent REQUIRED) + include_directories(${LIBEVENT_INCLUDE_DIRS}) + endfunction() +endif () +find_event(ON) + +find_package(StringVariable REQUIRED) +include_directories(${StringVariable_INCLUDE_DIR}) + +find_package(Ed25519 REQUIRED) +include_directories(${ed25519_INCLUDE_DIR}) + +find_package(ThreadPool REQUIRED) +include_directories(${ThreadPool_INCLUDE_DIR}) +if(WIN32) + add_definitions(-DWINDOWS) #Required for ThreadPool +endif() +find_package(spdlog REQUIRED) +link_libraries(spdlog::spdlog_header_only) #Its a header only lib so we should be fine :) + +if(NOT TEASPEAK_SERVER) + add_definitions(-DNO_OPEN_SSL) + add_definitions(-D_HAS_STD_BYTE) + #FML +else() + find_package(CXXTerminal REQUIRED) + + add_definitions(-DHAVE_CXX_TERMINAL) + add_definitions(-DHAVE_JSON) + set(HAVE_SQLITE3 ON) + set(HAVE_OPEN_SSL ON) + message("HAVE JSON!") +endif() + +if (MSVC) + set(CompilerFlags + CMAKE_CXX_FLAGS + CMAKE_CXX_FLAGS_DEBUG + CMAKE_CXX_FLAGS_RELEASE + CMAKE_CXX_FLAGS_RELWITHDEBINFO + CMAKE_C_FLAGS_RELWITHDEBINFO + CMAKE_C_FLAGS + CMAKE_C_FLAGS_DEBUG + CMAKE_C_FLAGS_RELEASE + ) + foreach(CompilerFlag ${CompilerFlags}) + string(REPLACE "/MD" "/MT" ${CompilerFlag} "${${CompilerFlag}}") + endforeach() + add_compile_options("/EHsc") #We require exception handling +else() + set(CMAKE_CXX_FLAGS_RELEASE "-O3") #-DNDEBUG We want assert! +endif() +add_definitions(-DUSE_BORINGSSL) +include_directories(${LIBRARY_PATH}/boringssl/include/) +set(SOURCE_FILES + src/misc/rnd.cpp + src/misc/time.cpp + src/misc/memtracker.cpp + src/misc/digest.cpp + src/misc/base64.cpp + + #Logger + src/log/LogUtils.cpp + src/log/LogSinks.cpp + + + src/qlz/QuickLZ.cpp + src/qlz/QuickLZ_L3.cpp + src/qlz/QuickLZ_L1.cpp + + src/converters/converter.cpp + + src/query/command3.cpp + src/query/command2.cpp + src/query/Command.cpp + src/query/escape.cpp + + src/protocol/generation.cpp + src/protocol/Packet.cpp + src/protocol/buffers.cpp + src/protocol/buffers_allocator_c.cpp + src/PermissionManager.cpp + src/Properties.cpp + src/BasicChannel.cpp + src/Error.cpp + src/protocol/CryptHandler.cpp + src/protocol/CompressionHandler.cpp + src/Variable.cpp + src/linked_helper.cpp + src/EventLoop.cpp + + src/License.cpp + + src/bbcode/bbcodes.cpp + + src/channel/TreeView.cpp + src/protocol/ringbuffer.cpp + src/protocol/AcknowledgeManager.cpp +) + +set(HEADER_FILES + src/misc/base64.h + src/misc/endianness.h + src/misc/cast.h + src/misc/rnd.h + src/misc/time.h + src/misc/std_unique_ptr.h + src/misc/net.h + src/misc/lambda.h + src/misc/hex.h + src/misc/advanced_mutex.h + src/misc/memtracker.h + src/misc/strobf.h + + src/log/translation.h + src/log/LogUtils.h + + src/PermissionManager.h + src/protocol/buffers.h + src/protocol/Packet.h + src/Properties.h + src/BasicChannel.h + src/Definitions.h + src/Error.h + src/protocol/CryptHandler.h + src/Variable.h + src/misc/queue.h + + src/misc/digest.h + + src/bbcode/bbcodes.h + + src/channel/TreeView.h +) + +if(HAVE_SQLITE3) + set(SOURCE_FILES ${SOURCE_FILES} + src/sql/SqlQuery.cpp + src/sql/sqlite/SqliteSQL.cpp + src/sql/mysql/MySQL.cpp + ) + set(HEADER_FILES ${HEADER_FILES} + src/sql/SqlQuery.h + src/sql/sqlite/SqliteSQL.h + src/sql/mysql/MySQL.h + ) +endif() + +if(HAVE_OPEN_SSL) + set(SOURCE_FILES ${SOURCE_FILES} + src/ssl/SSLManager.cpp + ) + set(HEADER_FILES ${HEADER_FILES} + src/ssl/SSLManager.h + ) +endif() + +add_library(TeaSpeak STATIC ${SOURCE_FILES} ${HEADER_FILES}) +target_link_libraries(TeaSpeak PUBLIC threadpool::static jsoncpp_lib) +target_compile_options(TeaSpeak PRIVATE "-Wall") + +if (TEASPEAK_SERVER) + target_link_libraries(TeaSpeak PUBLIC CXXTerminal::static) +endif () +target_include_directories(TeaSpeak PUBLIC src/) +install(TARGETS TeaSpeak + ARCHIVE DESTINATION lib +) +INSTALL ( + DIRECTORY ${CMAKE_SOURCE_DIR}/src/ + DESTINATION include + FILES_MATCHING PATTERN "*.h*" +) + +set(TEST_LIBRARIES + threadpool::static #Static + TeaSpeak #Static + TeaLicenseHelper #Static + TeaMusic #Static + CXXTerminal::static #Static + ${StringVariable_LIBRARIES_STATIC} + ${YAML_CPP_LIBRARIES} + pthread + stdc++fs + libevent::core libevent::pthreads + opus::static + yaml-cpp + ${LIBRARY_PATH_PROTOBUF} + + #We're forsed to use boringssl caused by the fact that boringssl is already within webrtc! + + #Require a so + sqlite3 + + breakpad::static + protobuf::libprotobuf + jemalloc::shared + + tomcrypt::static + tommath::static + + mysqlclient.a + jsoncpp_lib + ${ed25519_LIBRARIES_STATIC} + ${DataPipes_LIBRARIES_SHARED} # Also includes glib2.0 + + openssl::ssl::shared + openssl::crypto::shared + dl + z +) +include_directories(src/) +option(BUILD_TESTS "Enable/disable test building" ON) +if(BUILD_TESTS) + add_executable(RingTest test/RingTest.cpp ${SOURCE_FILES}) + target_link_libraries(RingTest ${TEST_LIBRARIES}) + + if(NOT WIN32) + add_executable(CommandTest ${SOURCE_FILES} ${HEADER_FILES} test/CommandTest.cpp src/log/LogSinks.cpp src/log/LogSinks.h ../server/MySQLLibSSLFix.c) + target_link_libraries(CommandTest ${TEST_LIBRARIES}) + + add_executable(WebsocketTest ${SOURCE_FILES} ${HEADER_FILES} test/WSSTest.cpp src/log/LogSinks.cpp src/log/LogSinks.h) + target_link_libraries(WebsocketTest ${TEST_LIBRARIES}) + + add_executable(SQLTest ${SOURCE_FILES} ${HEADER_FILES} test/SQLTest.cpp src/log/LogSinks.cpp src/log/LogSinks.h) + target_link_libraries(SQLTest ${TEST_LIBRARIES}) + + add_executable(ChannelTest ${SOURCE_FILES} ${HEADER_FILES} test/ChannelTest.cpp src/log/LogSinks.cpp src/log/LogSinks.h) + target_link_libraries(ChannelTest ${TEST_LIBRARIES}) + + add_executable(EndianessTest ${SOURCE_FILES} ${HEADER_FILES} test/EndianessTest.cpp src/log/LogSinks.cpp src/log/LogSinks.h) + target_link_libraries(EndianessTest ${TEST_LIBRARIES}) + + include_directories(/usr/local/include/breakpad) + add_executable(CrashTest test/CrashTest.cpp ${SOURCE_FILES}) + target_link_libraries(CrashTest ${TEST_LIBRARIES}) + + add_executable(PorpertyTest test/PropertyTest.cpp ${SOURCE_FILES}) + target_link_libraries(PorpertyTest ${TEST_LIBRARIES}) + + add_executable(BBTest test/BBTest.cpp ${SOURCE_FILES} src/query/command_unused.h) + target_link_libraries(BBTest ${TEST_LIBRARIES}) + + add_executable(LinkedTest test/LinkedTest.cpp ${SOURCE_FILES}) + target_link_libraries(LinkedTest ${TEST_LIBRARIES}) + + add_executable(PermissionTest test/PermissionTest.cpp ${SOURCE_FILES}) + target_link_libraries(PermissionTest ${TEST_LIBRARIES}) + + add_executable(GenerationTest test/generationTest.cpp ${SOURCE_FILES} ../server/MySQLLibSSLFix.c) + target_link_libraries(GenerationTest ${TEST_LIBRARIES}) + endif() +endif() diff --git a/src/Error.cpp b/src/Error.cpp index cc3710b..f630e9e 100644 --- a/src/Error.cpp +++ b/src/Error.cpp @@ -1,207 +1,207 @@ -// -// Created by wolverindev on 17.10.17. -// - -#include "Error.h" - -using namespace ts; - -const std::vector ts::avariableErrors = { - {0x0000, "ok" , "ok" }, - {0x0001, "undefined" , "undefined error" }, - {0x0002, "not_implemented" , "not implemented" }, - {0x0005, "lib_time_limit_reached" , "library time limit reached" }, - - {0x0100, "command_not_found" , "command not found" }, - {0x0101, "unable_to_bind_network_port" , "unable to bind network port" }, - {0x0102, "no_network_port_available" , "no network port available" }, - - {0x0200, "client_invalid_id" , "invalid clientID" }, - {0x0201, "client_nickname_inuse" , "nickname is already in use" }, - {0x0202, "" , "invalid error code" }, - {0x0203, "client_protocol_limit_reached" , "max clients protocol limit reached" }, - {0x0204, "client_invalid_type" , "invalid client type" }, - {0x0205, "client_already_subscribed" , "already subscribed" }, - {0x0206, "client_not_logged_in" , "not logged in" }, - {0x0207, "client_could_not_validate_identity" , "could not validate client identity" }, - {0x0208, "client_invalid_password" , "invalid loginname or password" }, - {0x0209, "client_too_many_clones_connected" , "too many clones already connected" }, - {0x020A, "client_version_outdated" , "client version outdated, please update" }, - {0x020B, "client_is_online" , "client is online" }, - {0x020C, "client_is_flooding" , "client is flooding" }, - {0x020D, "client_hacked" , "client is modified" }, - {0x020E, "client_cannot_verify_now" , "can not verify client at this moment" }, - {0x020F, "client_login_not_permitted" , "client is not permitted to log in" }, - {0x0210, "client_not_subscribed" , "client is not subscribed to the channel" }, - {0x0211, "client_unknown" , "client is not known" }, - {0x0212, "client_join_rate_limit_reached" , "client has reached his join attempt limit" }, - {0x0213, "client_is_already_member_of_group" , "client is already a member of the group" }, - {0x0214, "client_is_not_member_of_group" , "client is not a member of the group" }, - {0x0215, "client_type_is_not_allowed" , "client type is not allowed to join the server" }, - - {0x0300, "channel_invalid_id" , "invalid channelID" }, - {0x0301, "channel_protocol_limit_reached" , "max channels protocol limit reached" }, - {0x0302, "channel_already_in" , "already member of channel" }, - {0x0303, "channel_name_inuse" , "channel name is already in use" }, - {0x0304, "channel_not_empty" , "channel not empty" }, - {0x0305, "channel_can_not_delete_default" , "can not delete default channel" }, - {0x0306, "channel_default_require_permanent" , "default channel requires permanent" }, - {0x0307, "channel_invalid_flags" , "invalid channel flags" }, - {0x0308, "channel_parent_not_permanent" , "permanent channel can not be child of non permanent channel"}, - {0x0309, "channel_maxclients_reached" , "channel maxclient reached" }, - {0x030A, "channel_maxfamily_reached" , "channel maxfamily reached" }, - {0x030B, "channel_invalid_order" , "invalid channel order" }, - {0x030C, "channel_no_filetransfer_supported" , "channel does not support filetransfers" }, - {0x030D, "channel_invalid_password" , "invalid channel password" }, - {0x030E, "channel_is_private_channel" , "channel is private channel" }, - {0x030F, "channel_invalid_security_hash" , "invalid security hash supplied by client" }, - {0x0310, "channel_is_deleted" , "target channel is deleted" }, - {0x0311, "channel_name_invalid" , "channel name is invalid" }, - {0x0312, "channel_limit_reached" , "the virtualserver channel limit has been reached" }, - - {0x0400, "server_invalid_id" , "invalid serverID" }, - {0x0401, "server_running" , "server is running" }, - {0x0402, "server_is_shutting_down" , "server is shutting down" }, - {0x0403, "server_maxclients_reached" , "server maxclient reached" }, - {0x0404, "server_invalid_password" , "invalid server password" }, - {0x0405, "server_deployment_active" , "deployment active" }, - {0x0406, "server_unable_to_stop_own_server" , "unable to stop own server in your connection class" }, - {0x0407, "server_is_virtual" , "server is virtual" }, - {0x0408, "server_wrong_machineid" , "server wrong machineID" }, - {0x0409, "server_is_not_running" , "server is not running" }, - {0x040A, "server_is_booting" , "server is booting up" }, - {0x040B, "server_status_invalid" , "server got an invalid status for this operation" }, - {0x040C, "server_modal_quit" , "server modal quit" }, - {0x040D, "server_version_outdated" , "server version is too old for command" }, - {0x040D, "server_already_joined" , "query client already joined to the server" }, - {0x040E, "server_is_not_shutting_down" , "server isn't shutting down" }, - {0x040F, "server_max_vs_reached" , "You reached the maximal virtual server limit" }, - {0x0410, "server_unbound" , "you are not bound to any server" }, - {0x0411, "server_join_rate_limit_reached" , "the server reached his join attempt limit" }, - - - {0x0500, "sql" , "sql error" }, - {0x0501, "database_empty_result" , "sql empty result set" }, - {0x0502, "database_duplicate_entry" , "sql duplicate entry" }, - {0x0503, "database_no_modifications" , "sql no modifications" }, - {0x0504, "database_constraint" , "sql invalid constraint" }, - {0x0505, "database_reinvoke" , "sql reinvoke command" }, - - {0x0600, "parameter_quote" , "invalid quote" }, - {0x0601, "parameter_invalid_count" , "invalid parameter count" }, - {0x0602, "parameter_invalid" , "invalid parameter" }, - {0x0603, "parameter_not_found" , "parameter not found" }, - {0x0604, "parameter_convert" , "convert error" }, - {0x0605, "parameter_invalid_size" , "invalid parameter size" }, - {0x0606, "parameter_missing" , "missing required parameter" }, - {0x0607, "parameter_checksum" , "invalid checksum" }, - - {0x0700, "vs_critical" , "virtual server got a critical error" }, - {0x0701, "connection_lost" , "Connection lost" }, - {0x0702, "not_connected" , "not connected" }, - {0x0703, "no_cached_connection_info" , "no cached connection info" }, - {0x0704, "currently_not_possible" , "currently not possible" }, - {0x0705, "failed_connection_initialisation" , "failed connection initialization" }, - {0x0706, "could_not_resolve_hostname" , "could not resolve hostname" }, - {0x0707, "invalid_server_connection_handler_id" , "invalid server connection handler ID" }, - {0x0708, "could_not_initialise_input_client" , "could not initialize Input client" }, - {0x0709, "clientlibrary_not_initialised" , "client library not initialized" }, - {0x070A, "serverlibrary_not_initialised" , "server library not initialized" }, - {0x070B, "whisper_too_many_targets" , "too many whisper targets" }, - {0x070C, "whisper_no_targets" , "no whisper targets found" }, - - {0x0800, "file_invalid_name" , "invalid file name" }, - {0x0801, "file_invalid_permissions" , "invalid file permissions" }, - {0x0802, "file_already_exists" , "file already exists" }, - {0x0803, "file_not_found" , "file not found" }, - {0x0804, "file_io_error" , "file input/output error" }, - {0x0805, "file_invalid_transfer_id" , "invalid file transfer ID" }, - {0x0806, "file_invalid_path" , "invalid file path" }, - {0x0807, "file_no_files_available" , "no files available" }, - {0x0808, "file_overwrite_excludes_resume" , "overwrite excludes resume" }, - {0x0809, "file_invalid_size" , "invalid file size" }, - {0x080A, "file_already_in_use" , "file already in use" }, - {0x080B, "file_could_not_open_connection" , "could not open file transfer connection" }, - {0x080C, "file_no_space_left_on_device" , "no space left on device (disk full?)" }, - {0x080D, "file_exceeds_file_system_maximum_size", "file exceeds file system's maximum file size" }, - {0x080E, "file_transfer_connection_timeout" , "file transfer connection timeout" }, - {0x080F, "file_connection_lost" , "lost file transfer connection" }, - {0x0810, "file_exceeds_supplied_size" , "file exceeds supplied file size" }, - {0x0811, "file_transfer_complete" , "file transfer complete" }, - {0x0812, "file_transfer_canceled" , "file transfer canceled" }, - {0x0813, "file_transfer_interrupted" , "file transfer interrupted" }, - {0x0814, "file_transfer_server_quota_exceeded" , "file transfer server quota exceeded" }, - {0x0815, "file_transfer_client_quota_exceeded" , "file transfer client quota exceeded" }, - {0x0816, "file_transfer_reset" , "file transfer reset" }, - {0x0817, "file_transfer_limit_reached" , "file transfer limit reached" }, - - - {0x0A08, "server_insufficeient_permissions" , "insufficient client permissions" }, - - {0x0B01, "accounting_slot_limit_reached" , "max slot limit reached" }, - - {0x0D01, "server_connect_banned" , "connection failed, you are banned" }, - {0x0D03, "ban_flooding" , "flood ban" }, - - {0x0F00, "token_invalid_id" , "invalid privilege key" }, - - {0x1001, "web_handshake_invalid" , "Invalid handshake" }, - {0x1001, "web_handshake_unsupported" , "Handshake intention unsupported" }, - {0x1002, "web_handshake_identity_unsupported" , "Handshake identity unsupported" }, - {0x1003, "web_handshake_identity_proof_failed" , "Identity proof failed" }, - {0x1004, "web_handshake_identity_outdated" , "data seems to be outdated" }, - - {0x1100, "music_invalid_id" , "invalid botID" }, - {0x1101, "music_limit_reached" , "Server music bot limit is reached" }, - {0x1102, "music_client_limit_reached" , "Client music bot limit is reached" }, - {0x1103, "music_invalid_player_state" , "Invalid player state" }, - {0x1104, "music_invalid_action" , "Invalid action" }, - {0x1105, "music_no_player" , "Missing player instance" }, - {0x1105, "music_disabled" , "Music bots have been disabled" }, - - {0x2100, "playlist_invalid_id" , "invalid playlist id" }, - {0x2101, "playlist_invalid_song_id" , "invalid playlist song id" }, - {0x2102, "playlist_already_in_use" , "playlist is already used by another bot" }, - {0x2103, "playlist_is_in_use" , "playlist is used by another bot" }, - - {0x2200, "conversation_invalid_id" , "invalid conversation id" }, - {0x2201, "conversation_more_data" , "there are more messages to send" }, - {0x2202, "conversation_is_private" , "the target conversation is private" }, - - {0x1200, "query_not_exists" , "query account does not exists" }, - {0x1201, "query_already_exists" , "query account already exists" }, - {0x1202, "query_too_many_simultaneously_sessions", "too many simultaneously connected sessions" }, - {0x1203, "query_maxclients_reached" , "query server reached its limit" }, - - {0x1300, "group_invalid_id" , "Invalid group id" }, - {0x1301, "group_name_inuse" , "Group name is already in use" }, - {0x1302, "group_not_assigned_over_this_server" , "the group hasn't been assigned over this server" }, - - {0xE000, "resource_limit_reached" , "resource limit reached" }, - - {0xFFFF, "custom_error" , "costume" }, -}; -ErrorType ErrorType::Success = avariableErrors[0]; -ErrorType ErrorType::Costume = findError("custom_error"); -ErrorType ErrorType::VSError = findError("vs_critical"); -ErrorType ErrorType::DBEmpty = findError("database_empty_result"); - -ErrorType ts::findError(uint16_t errorId){ - for(auto elm : avariableErrors) - if(elm.errorId == errorId) return elm; - return ErrorType{errorId, "undefined", "undefined"}; -} - -ErrorType ts::findError(std::string key){ - for(auto elm : avariableErrors) - if(elm.name == key) return elm; - return ErrorType{1, key, "undefined"}; -} - -CommandResult CommandResult::Success = {avariableErrors[0], ""}; -CommandResult CommandResult::NotImplemented = {avariableErrors[2], ""}; - -CommandResultPermissionError::CommandResultPermissionError(permission::PermissionType error, const std::string &extraMsg) : CommandResult(findError(0x0A08), "") { - this->extraProperties["failed_permid"] = std::to_string((int16_t) error); - this->_type = PERM_ERROR; +// +// Created by wolverindev on 17.10.17. +// + +#include "Error.h" + +using namespace ts; + +const std::vector ts::avariableErrors = { + {0x0000, "ok" , "ok" }, + {0x0001, "undefined" , "undefined error" }, + {0x0002, "not_implemented" , "not implemented" }, + {0x0005, "lib_time_limit_reached" , "library time limit reached" }, + + {0x0100, "command_not_found" , "command not found" }, + {0x0101, "unable_to_bind_network_port" , "unable to bind network port" }, + {0x0102, "no_network_port_available" , "no network port available" }, + + {0x0200, "client_invalid_id" , "invalid clientID" }, + {0x0201, "client_nickname_inuse" , "nickname is already in use" }, + {0x0202, "" , "invalid error code" }, + {0x0203, "client_protocol_limit_reached" , "max clients protocol limit reached" }, + {0x0204, "client_invalid_type" , "invalid client type" }, + {0x0205, "client_already_subscribed" , "already subscribed" }, + {0x0206, "client_not_logged_in" , "not logged in" }, + {0x0207, "client_could_not_validate_identity" , "could not validate client identity" }, + {0x0208, "client_invalid_password" , "invalid loginname or password" }, + {0x0209, "client_too_many_clones_connected" , "too many clones already connected" }, + {0x020A, "client_version_outdated" , "client version outdated, please update" }, + {0x020B, "client_is_online" , "client is online" }, + {0x020C, "client_is_flooding" , "client is flooding" }, + {0x020D, "client_hacked" , "client is modified" }, + {0x020E, "client_cannot_verify_now" , "can not verify client at this moment" }, + {0x020F, "client_login_not_permitted" , "client is not permitted to log in" }, + {0x0210, "client_not_subscribed" , "client is not subscribed to the channel" }, + {0x0211, "client_unknown" , "client is not known" }, + {0x0212, "client_join_rate_limit_reached" , "client has reached his join attempt limit" }, + {0x0213, "client_is_already_member_of_group" , "client is already a member of the group" }, + {0x0214, "client_is_not_member_of_group" , "client is not a member of the group" }, + {0x0215, "client_type_is_not_allowed" , "client type is not allowed to join the server" }, + + {0x0300, "channel_invalid_id" , "invalid channelID" }, + {0x0301, "channel_protocol_limit_reached" , "max channels protocol limit reached" }, + {0x0302, "channel_already_in" , "already member of channel" }, + {0x0303, "channel_name_inuse" , "channel name is already in use" }, + {0x0304, "channel_not_empty" , "channel not empty" }, + {0x0305, "channel_can_not_delete_default" , "can not delete default channel" }, + {0x0306, "channel_default_require_permanent" , "default channel requires permanent" }, + {0x0307, "channel_invalid_flags" , "invalid channel flags" }, + {0x0308, "channel_parent_not_permanent" , "permanent channel can not be child of non permanent channel"}, + {0x0309, "channel_maxclients_reached" , "channel maxclient reached" }, + {0x030A, "channel_maxfamily_reached" , "channel maxfamily reached" }, + {0x030B, "channel_invalid_order" , "invalid channel order" }, + {0x030C, "channel_no_filetransfer_supported" , "channel does not support filetransfers" }, + {0x030D, "channel_invalid_password" , "invalid channel password" }, + {0x030E, "channel_is_private_channel" , "channel is private channel" }, + {0x030F, "channel_invalid_security_hash" , "invalid security hash supplied by client" }, + {0x0310, "channel_is_deleted" , "target channel is deleted" }, + {0x0311, "channel_name_invalid" , "channel name is invalid" }, + {0x0312, "channel_limit_reached" , "the virtualserver channel limit has been reached" }, + + {0x0400, "server_invalid_id" , "invalid serverID" }, + {0x0401, "server_running" , "server is running" }, + {0x0402, "server_is_shutting_down" , "server is shutting down" }, + {0x0403, "server_maxclients_reached" , "server maxclient reached" }, + {0x0404, "server_invalid_password" , "invalid server password" }, + {0x0405, "server_deployment_active" , "deployment active" }, + {0x0406, "server_unable_to_stop_own_server" , "unable to stop own server in your connection class" }, + {0x0407, "server_is_virtual" , "server is virtual" }, + {0x0408, "server_wrong_machineid" , "server wrong machineID" }, + {0x0409, "server_is_not_running" , "server is not running" }, + {0x040A, "server_is_booting" , "server is booting up" }, + {0x040B, "server_status_invalid" , "server got an invalid status for this operation" }, + {0x040C, "server_modal_quit" , "server modal quit" }, + {0x040D, "server_version_outdated" , "server version is too old for command" }, + {0x040D, "server_already_joined" , "query client already joined to the server" }, + {0x040E, "server_is_not_shutting_down" , "server isn't shutting down" }, + {0x040F, "server_max_vs_reached" , "You reached the maximal virtual server limit" }, + {0x0410, "server_unbound" , "you are not bound to any server" }, + {0x0411, "server_join_rate_limit_reached" , "the server reached his join attempt limit" }, + + + {0x0500, "sql" , "sql error" }, + {0x0501, "database_empty_result" , "sql empty result set" }, + {0x0502, "database_duplicate_entry" , "sql duplicate entry" }, + {0x0503, "database_no_modifications" , "sql no modifications" }, + {0x0504, "database_constraint" , "sql invalid constraint" }, + {0x0505, "database_reinvoke" , "sql reinvoke command" }, + + {0x0600, "parameter_quote" , "invalid quote" }, + {0x0601, "parameter_invalid_count" , "invalid parameter count" }, + {0x0602, "parameter_invalid" , "invalid parameter" }, + {0x0603, "parameter_not_found" , "parameter not found" }, + {0x0604, "parameter_convert" , "convert error" }, + {0x0605, "parameter_invalid_size" , "invalid parameter size" }, + {0x0606, "parameter_missing" , "missing required parameter" }, + {0x0607, "parameter_checksum" , "invalid checksum" }, + + {0x0700, "vs_critical" , "virtual server got a critical error" }, + {0x0701, "connection_lost" , "Connection lost" }, + {0x0702, "not_connected" , "not connected" }, + {0x0703, "no_cached_connection_info" , "no cached connection info" }, + {0x0704, "currently_not_possible" , "currently not possible" }, + {0x0705, "failed_connection_initialisation" , "failed connection initialization" }, + {0x0706, "could_not_resolve_hostname" , "could not resolve hostname" }, + {0x0707, "invalid_server_connection_handler_id" , "invalid server connection handler ID" }, + {0x0708, "could_not_initialise_input_client" , "could not initialize Input client" }, + {0x0709, "clientlibrary_not_initialised" , "client library not initialized" }, + {0x070A, "serverlibrary_not_initialised" , "server library not initialized" }, + {0x070B, "whisper_too_many_targets" , "too many whisper targets" }, + {0x070C, "whisper_no_targets" , "no whisper targets found" }, + + {0x0800, "file_invalid_name" , "invalid file name" }, + {0x0801, "file_invalid_permissions" , "invalid file permissions" }, + {0x0802, "file_already_exists" , "file already exists" }, + {0x0803, "file_not_found" , "file not found" }, + {0x0804, "file_io_error" , "file input/output error" }, + {0x0805, "file_invalid_transfer_id" , "invalid file transfer ID" }, + {0x0806, "file_invalid_path" , "invalid file path" }, + {0x0807, "file_no_files_available" , "no files available" }, + {0x0808, "file_overwrite_excludes_resume" , "overwrite excludes resume" }, + {0x0809, "file_invalid_size" , "invalid file size" }, + {0x080A, "file_already_in_use" , "file already in use" }, + {0x080B, "file_could_not_open_connection" , "could not open file transfer connection" }, + {0x080C, "file_no_space_left_on_device" , "no space left on device (disk full?)" }, + {0x080D, "file_exceeds_file_system_maximum_size", "file exceeds file system's maximum file size" }, + {0x080E, "file_transfer_connection_timeout" , "file transfer connection timeout" }, + {0x080F, "file_connection_lost" , "lost file transfer connection" }, + {0x0810, "file_exceeds_supplied_size" , "file exceeds supplied file size" }, + {0x0811, "file_transfer_complete" , "file transfer complete" }, + {0x0812, "file_transfer_canceled" , "file transfer canceled" }, + {0x0813, "file_transfer_interrupted" , "file transfer interrupted" }, + {0x0814, "file_transfer_server_quota_exceeded" , "file transfer server quota exceeded" }, + {0x0815, "file_transfer_client_quota_exceeded" , "file transfer client quota exceeded" }, + {0x0816, "file_transfer_reset" , "file transfer reset" }, + {0x0817, "file_transfer_limit_reached" , "file transfer limit reached" }, + + + {0x0A08, "server_insufficeient_permissions" , "insufficient client permissions" }, + + {0x0B01, "accounting_slot_limit_reached" , "max slot limit reached" }, + + {0x0D01, "server_connect_banned" , "connection failed, you are banned" }, + {0x0D03, "ban_flooding" , "flood ban" }, + + {0x0F00, "token_invalid_id" , "invalid privilege key" }, + + {0x1001, "web_handshake_invalid" , "Invalid handshake" }, + {0x1001, "web_handshake_unsupported" , "Handshake intention unsupported" }, + {0x1002, "web_handshake_identity_unsupported" , "Handshake identity unsupported" }, + {0x1003, "web_handshake_identity_proof_failed" , "Identity proof failed" }, + {0x1004, "web_handshake_identity_outdated" , "data seems to be outdated" }, + + {0x1100, "music_invalid_id" , "invalid botID" }, + {0x1101, "music_limit_reached" , "Server music bot limit is reached" }, + {0x1102, "music_client_limit_reached" , "Client music bot limit is reached" }, + {0x1103, "music_invalid_player_state" , "Invalid player state" }, + {0x1104, "music_invalid_action" , "Invalid action" }, + {0x1105, "music_no_player" , "Missing player instance" }, + {0x1105, "music_disabled" , "Music bots have been disabled" }, + + {0x2100, "playlist_invalid_id" , "invalid playlist id" }, + {0x2101, "playlist_invalid_song_id" , "invalid playlist song id" }, + {0x2102, "playlist_already_in_use" , "playlist is already used by another bot" }, + {0x2103, "playlist_is_in_use" , "playlist is used by another bot" }, + + {0x2200, "conversation_invalid_id" , "invalid conversation id" }, + {0x2201, "conversation_more_data" , "there are more messages to send" }, + {0x2202, "conversation_is_private" , "the target conversation is private" }, + + {0x1200, "query_not_exists" , "query account does not exists" }, + {0x1201, "query_already_exists" , "query account already exists" }, + {0x1202, "query_too_many_simultaneously_sessions", "too many simultaneously connected sessions" }, + {0x1203, "query_maxclients_reached" , "query server reached its limit" }, + + {0x1300, "group_invalid_id" , "Invalid group id" }, + {0x1301, "group_name_inuse" , "Group name is already in use" }, + {0x1302, "group_not_assigned_over_this_server" , "the group hasn't been assigned over this server" }, + + {0xE000, "resource_limit_reached" , "resource limit reached" }, + + {0xFFFF, "custom_error" , "costume" }, +}; +ErrorType ErrorType::Success = avariableErrors[0]; +ErrorType ErrorType::Costume = findError("custom_error"); +ErrorType ErrorType::VSError = findError("vs_critical"); +ErrorType ErrorType::DBEmpty = findError("database_empty_result"); + +ErrorType ts::findError(uint16_t errorId){ + for(auto elm : avariableErrors) + if(elm.errorId == errorId) return elm; + return ErrorType{errorId, "undefined", "undefined"}; +} + +ErrorType ts::findError(std::string key){ + for(auto elm : avariableErrors) + if(elm.name == key) return elm; + return ErrorType{1, key, "undefined"}; +} + +CommandResult CommandResult::Success = {avariableErrors[0], ""}; +CommandResult CommandResult::NotImplemented = {avariableErrors[2], ""}; + +CommandResultPermissionError::CommandResultPermissionError(permission::PermissionType error, const std::string &extraMsg) : CommandResult(findError(0x0A08), "") { + this->extraProperties["failed_permid"] = std::to_string((int16_t) error); + this->_type = PERM_ERROR; } \ No newline at end of file diff --git a/src/Error.h b/src/Error.h index c4b8af7..723b649 100644 --- a/src/Error.h +++ b/src/Error.h @@ -1,396 +1,396 @@ -#pragma once - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#define _NDEBUG - -namespace ts { - struct CommandResult; - namespace permission { - enum PermissionType : uint16_t; - } - - struct error { - enum type : uint16_t { - ok = 0x0, - undefined = 0x1, - not_implemented = 0x2, - lib_time_limit_reached = 0x5, - command_not_found = 0x100, - unable_to_bind_network_port = 0x101, - no_network_port_available = 0x102, - client_invalid_id = 0x200, - client_nickname_inuse = 0x201, - invalid_error_code = 0x202, - - client_protocol_limit_reached = 0x203, - client_invalid_type = 0x204, - client_already_subscribed = 0x205, - client_not_logged_in = 0x206, - client_could_not_validate_identity = 0x207, - client_invalid_password = 0x208, - client_too_many_clones_connected = 0x209, - client_version_outdated = 0x20a, - client_is_online = 0x20b, - client_is_flooding = 0x20c, - client_hacked = 0x20d, - client_cannot_verify_now = 0x20e, - client_login_not_permitted = 0x20f, - client_not_subscribed = 0x210, - client_unknown = 0x0211, - client_join_rate_limit_reached = 0x0212, - client_is_already_member_of_group = 0x0213, - client_is_not_member_of_group = 0x0214, - client_type_is_not_allowed = 0x0215, - - channel_invalid_id = 0x300, - channel_protocol_limit_reached = 0x301, - channel_already_in = 0x302, - channel_name_inuse = 0x303, - channel_not_empty = 0x304, - channel_can_not_delete_default = 0x305, - channel_default_require_permanent = 0x306, - channel_invalid_flags = 0x307, - channel_parent_not_permanent = 0x308, - channel_maxclients_reached = 0x309, - channel_maxfamily_reached = 0x30a, - channel_invalid_order = 0x30b, - channel_no_filetransfer_supported = 0x30c, - channel_invalid_password = 0x30d, - channel_is_private_channel = 0x30e, - channel_invalid_security_hash = 0x30f, - channel_is_deleted = 0x310, - channel_name_invalid = 0x311, - channel_limit_reached = 0x312, - - server_invalid_id = 0x400, - server_running = 0x401, - server_is_shutting_down = 0x402, - server_maxclients_reached = 0x403, - server_invalid_password = 0x404, - server_deployment_active = 0x405, - server_unable_to_stop_own_server = 0x406, - server_is_virtual = 0x407, - server_wrong_machineid = 0x408, - server_is_not_running = 0x409, - server_is_booting = 0x40a, - server_status_invalid = 0x40b, - server_modal_quit = 0x40c, - server_version_outdated = 0x40d, - server_already_joined = 0x40d, - server_is_not_shutting_down = 0x40e, - server_max_vs_reached = 0x40f, - server_unbound = 0x410, - server_join_rate_limit_reached = 0x411, - - sql = 0x500, - database_empty_result = 0x501, - database_duplicate_entry = 0x502, - database_no_modifications = 0x503, - database_constraint = 0x504, - database_reinvoke = 0x505, - parameter_quote = 0x600, - parameter_invalid_count = 0x601, - parameter_invalid = 0x602, - parameter_not_found = 0x603, - parameter_convert = 0x604, - parameter_invalid_size = 0x605, - parameter_missing = 0x606, - parameter_checksum = 0x607, - vs_critical = 0x700, - connection_lost = 0x701, - not_connected = 0x702, - no_cached_connection_info = 0x703, - currently_not_possible = 0x704, - failed_connection_initialisation = 0x705, - could_not_resolve_hostname = 0x706, - invalid_server_connection_handler_id = 0x707, - could_not_initialise_input_client = 0x708, - clientlibrary_not_initialised = 0x709, - serverlibrary_not_initialised = 0x70a, - whisper_too_many_targets = 0x70b, - whisper_no_targets = 0x70c, - file_invalid_name = 0x800, - file_invalid_permissions = 0x801, - file_already_exists = 0x802, - file_not_found = 0x803, - file_io_error = 0x804, - file_invalid_transfer_id = 0x805, - file_invalid_path = 0x806, - file_no_files_available = 0x807, - file_overwrite_excludes_resume = 0x808, - file_invalid_size = 0x809, - file_already_in_use = 0x80a, - file_could_not_open_connection = 0x80b, - file_no_space_left_on_device = 0x80c, - file_exceeds_file_system_maximum_size = 0x80d, - file_transfer_connection_timeout = 0x80e, - file_connection_lost = 0x80f, - file_exceeds_supplied_size = 0x810, - file_transfer_complete = 0x811, - file_transfer_canceled = 0x812, - file_transfer_interrupted = 0x813, - file_transfer_server_quota_exceeded = 0x814, - file_transfer_client_quota_exceeded = 0x815, - file_transfer_reset = 0x816, - file_transfer_limit_reached = 0x817, - server_insufficeient_permissions = 0xa08, - accounting_slot_limit_reached = 0xb01, - server_connect_banned = 0xd01, - ban_flooding = 0xd03, - token_invalid_id = 0xf00, - web_handshake_invalid = 0x1001, - web_handshake_unsupported = 0x1001, - web_handshake_identity_unsupported = 0x1002, - web_handshake_identity_proof_failed = 0x1003, - web_handshake_identity_outdated = 0x1004, - - music_invalid_id = 0x1100, - music_limit_reached = 0x1101, - music_client_limit_reached = 0x1102, - music_invalid_player_state = 0x1103, - music_invalid_action = 0x1104, - music_no_player = 0x1105, - music_disabled = 0x1105, - playlist_invalid_id = 0x2100, - playlist_invalid_song_id = 0x2101, - playlist_already_in_use = 0x2102, - playlist_is_in_use = 0x2103, - query_not_exists = 0x1200, - query_already_exists = 0x1201, - - group_invalid_id = 0x1300, - group_name_inuse = 0x1301, - group_not_assigned_over_this_server = 0x1302, - - conversation_invalid_id = 0x2200, - conversation_more_data = 0x2201, - conversation_is_private = 0x2202, - - custom_error = 0xffff - }; - }; - - struct detailed_command_result { - error::type error_id; - std::map extra_properties; - }; - - - /* - * return command_result{permission::b_virtualserver_select_godmode}; => movabs rax,0xa08001700000001; ret; (Only if there is no destructor!) - * return command_result{permission::b_virtualserver_select_godmode}; => movabs rax,0xa08001700000001; ret; (Only if there is no destructor!) - * return command_result{error::vs_critical, "unknown error"}; => To a lot of code - */ - struct command_result { /* fixed size of 8 (64 bits) */ - static constexpr uint64_t MASK_ERROR = ~((uint64_t) 1 << (sizeof(error::type) * 8)); - static constexpr uint64_t MASK_PERMISSION = ~((uint64_t) 1 << (sizeof(permission::PermissionType) * 8)); - - static constexpr uint8_t OFFSET_ERROR = (8 - sizeof(error::type)) * 8; - static constexpr uint8_t OFFSET_PERMISSION = (8 - sizeof(permission::PermissionType) - sizeof(error::type)) * 8; - /* - * First bit is a flag bit which switches between detailed and code mode. - * 0 means detailed (Because then we could interpret data as a ptr (all ptr are 8 bit aligned => 3 zero bits)) - * bits [0;64] => data ptr (needs to be zero at destruction to avoid memory leaks) - * - * 1 means code - * bits [64 - sizeof(error::type);64] => error type | Usually evaluates to [48;64] - * bits [64 - sizeof(error::type) - sizeof(permission::PermissionType);64 - sizeof(error::type)] => permission id | Usually evaluates to [32;48] - */ - uint64_t data = 0; - - /* Test for mode 1 as described before */ - static_assert(sizeof(permission::PermissionType) * 8 + sizeof(error::type) * 8 <= 62); - - [[nodiscard]] inline error::type error_code() const { - if(this->is_detailed()) return this->details()->error_id; - - return (error::type) ((this->data >> OFFSET_ERROR) & MASK_ERROR); - } - - [[nodiscard]] inline bool is_permission_error() const { - return this->error_code() == error::server_insufficeient_permissions; - } - - [[nodiscard]] inline permission::PermissionType permission_id() const { - if(this->is_detailed()) return (permission::PermissionType) -1; /* not supported */ - - return (permission::PermissionType) ((this->data >> OFFSET_PERMISSION) & MASK_PERMISSION); - } - - [[nodiscard]] inline const detailed_command_result* details() const { return (detailed_command_result*) this->data; } - [[nodiscard]] inline detailed_command_result* details() { return (detailed_command_result*) this->data; } - - [[nodiscard]] inline bool is_detailed() const { - return (this->data & 0x1UL) == 0; - } - - inline std::unique_ptr release_details() { - if(!this->is_detailed()) return nullptr; - - auto result = this->details(); - this->data = 0; - return std::unique_ptr{result}; - } - - /* Attention: Releases the internal detailed pointer! */ - inline CommandResult as_command_result(); - -#ifndef _NDEBUG /* We dont need to secure that because gcc deduct us to an uint64_t and the only advantage is the mem leak test which is deactivated anyways */ - command_result(command_result&) = delete; - command_result(const command_result&) = delete; - command_result(command_result&& other) : data(other.data) { - other.data = 0; - } -#endif - - command_result() = default; - - explicit command_result(permission::PermissionType permission) { - this->data = 0x01; /* the the type to 1 */ - - this->data |= (uint64_t) error::server_insufficeient_permissions << OFFSET_ERROR; - this->data |= (uint64_t) permission << OFFSET_PERMISSION; - } - - - explicit command_result(error::type error) { - this->data = 0x01; /* the the type to 1 */ - - this->data |= (uint64_t) error << (8 - sizeof(error::type)) * 8; - } - - command_result(error::type error, const std::string& message) { - auto details_ptr = new detailed_command_result{}; - assert(((uintptr_t) details_ptr & 0x03U) == 0); // must be aligned! - - this->data = (uintptr_t) details_ptr; - - details_ptr->error_id = error; - details_ptr->extra_properties["extra_msg"] = message; - } - - command_result(error::type error, const std::map& properties) : command_result{error, std::string{""}} { - assert(this->is_detailed()); - this->details()->extra_properties = properties; - } - -#ifndef _NDEBUG - /* if we're not using any debug we dont have to use a deconstructor. A deconstructor prevent a direct uint64_t return as described above */ - ~command_result() { - if((this->data & 0x01) == 0x00) { - // this->details needs to be removed 'till this gets destructed - assert(this->data == 0); - } - } -#endif - }; - static_assert(sizeof(command_result) == 8); - - struct ErrorType { - public: - static ErrorType Success; - static ErrorType Costume; - static ErrorType VSError; - static ErrorType DBEmpty; - - ErrorType(const uint16_t errorId, std::string name, std::string message) : errorId(errorId), name(std::move(name)), message(std::move(message)) {} - ErrorType(const ErrorType& ref) = default; - ErrorType(ErrorType&& ref) : errorId(ref.errorId), name(std::move(ref.name)), message(std::move(ref.message)) {} - - uint16_t errorId; - std::string name; - std::string message; - - bool operator==(const ErrorType& ref) const { - return errorId == ref.errorId; - } - - bool operator!=(const ErrorType& ref) const { return !operator==(ref); } - - ErrorType& operator=(const ErrorType& ref) { - errorId = ref.errorId; - name = ref.name; - message = ref.message; - return *this; - } - - /** - * @return true if fail - */ - bool operator!() const { - return errorId != 0; - } - }; - - extern const std::vector avariableErrors; - extern ErrorType findError(uint16_t errorId); - extern ErrorType findError(std::string key); - - enum CommandResultType { - GENERAL, - PERM_ERROR - }; - - struct CommandResult { - public: - static CommandResult Success; - static CommandResult NotImplemented; - - CommandResult(const CommandResult& ref) : _type(ref._type), error(ref.error), extraProperties(ref.extraProperties) {} - CommandResult(CommandResult&& ref) : _type(ref._type), error(ref.error), extraProperties(ref.extraProperties) {} - CommandResult(ErrorType error, const std::string &extraMsg = "") : error(std::move(error)) { if(extraMsg.empty()) return; /*extraProperties["extramsg"] = extraMsg; */extraProperties["extra_msg"] = extraMsg; } - CommandResult(std::string error, const std::string &extraMsg = "") : error(findError(std::move(error))) { if(extraMsg.empty()) return; /*extraProperties["extramsg"] = extraMsg; */extraProperties["extra_msg"] = extraMsg; } - CommandResult() : CommandResult(ErrorType::Success, ""){} - CommandResult(ErrorType error, std::map details) : error(error), extraProperties(std::move(details)) {} - - bool operator==(const CommandResult& ref){ - return this->error == ref.error && ref.extraProperties == this->extraProperties; - } - - CommandResult& operator=(const CommandResult& ref)= default; - - /** - * @return true if fail - */ - bool operator!() const { - return this->error != ErrorType::Success; - } - virtual CommandResultType type(){ return _type; } - - ErrorType error; - std::map extraProperties; - CommandResultType _type = CommandResultType::GENERAL; - }; - - struct CommandResultPermissionError : public CommandResult { - public: - CommandResultPermissionError(permission::PermissionType error, const std::string &extraMsg = ""); - }; - - CommandResult command_result::as_command_result() { - if(this->is_detailed()) { - const auto details = this->details(); - auto result = CommandResult{findError(details->error_id), details->extra_properties}; - this->release_details(); - return result; - } else { - const auto code = this->error_code(); - auto error = findError(this->error_code()); - if(code == error::server_insufficeient_permissions) - return CommandResultPermissionError{(permission::PermissionType) this->permission_id()}; - else - return CommandResult{error}; - } - } -} - +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define _NDEBUG + +namespace ts { + struct CommandResult; + namespace permission { + enum PermissionType : uint16_t; + } + + struct error { + enum type : uint16_t { + ok = 0x0, + undefined = 0x1, + not_implemented = 0x2, + lib_time_limit_reached = 0x5, + command_not_found = 0x100, + unable_to_bind_network_port = 0x101, + no_network_port_available = 0x102, + client_invalid_id = 0x200, + client_nickname_inuse = 0x201, + invalid_error_code = 0x202, + + client_protocol_limit_reached = 0x203, + client_invalid_type = 0x204, + client_already_subscribed = 0x205, + client_not_logged_in = 0x206, + client_could_not_validate_identity = 0x207, + client_invalid_password = 0x208, + client_too_many_clones_connected = 0x209, + client_version_outdated = 0x20a, + client_is_online = 0x20b, + client_is_flooding = 0x20c, + client_hacked = 0x20d, + client_cannot_verify_now = 0x20e, + client_login_not_permitted = 0x20f, + client_not_subscribed = 0x210, + client_unknown = 0x0211, + client_join_rate_limit_reached = 0x0212, + client_is_already_member_of_group = 0x0213, + client_is_not_member_of_group = 0x0214, + client_type_is_not_allowed = 0x0215, + + channel_invalid_id = 0x300, + channel_protocol_limit_reached = 0x301, + channel_already_in = 0x302, + channel_name_inuse = 0x303, + channel_not_empty = 0x304, + channel_can_not_delete_default = 0x305, + channel_default_require_permanent = 0x306, + channel_invalid_flags = 0x307, + channel_parent_not_permanent = 0x308, + channel_maxclients_reached = 0x309, + channel_maxfamily_reached = 0x30a, + channel_invalid_order = 0x30b, + channel_no_filetransfer_supported = 0x30c, + channel_invalid_password = 0x30d, + channel_is_private_channel = 0x30e, + channel_invalid_security_hash = 0x30f, + channel_is_deleted = 0x310, + channel_name_invalid = 0x311, + channel_limit_reached = 0x312, + + server_invalid_id = 0x400, + server_running = 0x401, + server_is_shutting_down = 0x402, + server_maxclients_reached = 0x403, + server_invalid_password = 0x404, + server_deployment_active = 0x405, + server_unable_to_stop_own_server = 0x406, + server_is_virtual = 0x407, + server_wrong_machineid = 0x408, + server_is_not_running = 0x409, + server_is_booting = 0x40a, + server_status_invalid = 0x40b, + server_modal_quit = 0x40c, + server_version_outdated = 0x40d, + server_already_joined = 0x40d, + server_is_not_shutting_down = 0x40e, + server_max_vs_reached = 0x40f, + server_unbound = 0x410, + server_join_rate_limit_reached = 0x411, + + sql = 0x500, + database_empty_result = 0x501, + database_duplicate_entry = 0x502, + database_no_modifications = 0x503, + database_constraint = 0x504, + database_reinvoke = 0x505, + parameter_quote = 0x600, + parameter_invalid_count = 0x601, + parameter_invalid = 0x602, + parameter_not_found = 0x603, + parameter_convert = 0x604, + parameter_invalid_size = 0x605, + parameter_missing = 0x606, + parameter_checksum = 0x607, + vs_critical = 0x700, + connection_lost = 0x701, + not_connected = 0x702, + no_cached_connection_info = 0x703, + currently_not_possible = 0x704, + failed_connection_initialisation = 0x705, + could_not_resolve_hostname = 0x706, + invalid_server_connection_handler_id = 0x707, + could_not_initialise_input_client = 0x708, + clientlibrary_not_initialised = 0x709, + serverlibrary_not_initialised = 0x70a, + whisper_too_many_targets = 0x70b, + whisper_no_targets = 0x70c, + file_invalid_name = 0x800, + file_invalid_permissions = 0x801, + file_already_exists = 0x802, + file_not_found = 0x803, + file_io_error = 0x804, + file_invalid_transfer_id = 0x805, + file_invalid_path = 0x806, + file_no_files_available = 0x807, + file_overwrite_excludes_resume = 0x808, + file_invalid_size = 0x809, + file_already_in_use = 0x80a, + file_could_not_open_connection = 0x80b, + file_no_space_left_on_device = 0x80c, + file_exceeds_file_system_maximum_size = 0x80d, + file_transfer_connection_timeout = 0x80e, + file_connection_lost = 0x80f, + file_exceeds_supplied_size = 0x810, + file_transfer_complete = 0x811, + file_transfer_canceled = 0x812, + file_transfer_interrupted = 0x813, + file_transfer_server_quota_exceeded = 0x814, + file_transfer_client_quota_exceeded = 0x815, + file_transfer_reset = 0x816, + file_transfer_limit_reached = 0x817, + server_insufficeient_permissions = 0xa08, + accounting_slot_limit_reached = 0xb01, + server_connect_banned = 0xd01, + ban_flooding = 0xd03, + token_invalid_id = 0xf00, + web_handshake_invalid = 0x1001, + web_handshake_unsupported = 0x1001, + web_handshake_identity_unsupported = 0x1002, + web_handshake_identity_proof_failed = 0x1003, + web_handshake_identity_outdated = 0x1004, + + music_invalid_id = 0x1100, + music_limit_reached = 0x1101, + music_client_limit_reached = 0x1102, + music_invalid_player_state = 0x1103, + music_invalid_action = 0x1104, + music_no_player = 0x1105, + music_disabled = 0x1105, + playlist_invalid_id = 0x2100, + playlist_invalid_song_id = 0x2101, + playlist_already_in_use = 0x2102, + playlist_is_in_use = 0x2103, + query_not_exists = 0x1200, + query_already_exists = 0x1201, + + group_invalid_id = 0x1300, + group_name_inuse = 0x1301, + group_not_assigned_over_this_server = 0x1302, + + conversation_invalid_id = 0x2200, + conversation_more_data = 0x2201, + conversation_is_private = 0x2202, + + custom_error = 0xffff + }; + }; + + struct detailed_command_result { + error::type error_id; + std::map extra_properties; + }; + + + /* + * return command_result{permission::b_virtualserver_select_godmode}; => movabs rax,0xa08001700000001; ret; (Only if there is no destructor!) + * return command_result{permission::b_virtualserver_select_godmode}; => movabs rax,0xa08001700000001; ret; (Only if there is no destructor!) + * return command_result{error::vs_critical, "unknown error"}; => To a lot of code + */ + struct command_result { /* fixed size of 8 (64 bits) */ + static constexpr uint64_t MASK_ERROR = ~((uint64_t) 1 << (sizeof(error::type) * 8)); + static constexpr uint64_t MASK_PERMISSION = ~((uint64_t) 1 << (sizeof(permission::PermissionType) * 8)); + + static constexpr uint8_t OFFSET_ERROR = (8 - sizeof(error::type)) * 8; + static constexpr uint8_t OFFSET_PERMISSION = (8 - sizeof(permission::PermissionType) - sizeof(error::type)) * 8; + /* + * First bit is a flag bit which switches between detailed and code mode. + * 0 means detailed (Because then we could interpret data as a ptr (all ptr are 8 bit aligned => 3 zero bits)) + * bits [0;64] => data ptr (needs to be zero at destruction to avoid memory leaks) + * + * 1 means code + * bits [64 - sizeof(error::type);64] => error type | Usually evaluates to [48;64] + * bits [64 - sizeof(error::type) - sizeof(permission::PermissionType);64 - sizeof(error::type)] => permission id | Usually evaluates to [32;48] + */ + uint64_t data = 0; + + /* Test for mode 1 as described before */ + static_assert(sizeof(permission::PermissionType) * 8 + sizeof(error::type) * 8 <= 62); + + [[nodiscard]] inline error::type error_code() const { + if(this->is_detailed()) return this->details()->error_id; + + return (error::type) ((this->data >> OFFSET_ERROR) & MASK_ERROR); + } + + [[nodiscard]] inline bool is_permission_error() const { + return this->error_code() == error::server_insufficeient_permissions; + } + + [[nodiscard]] inline permission::PermissionType permission_id() const { + if(this->is_detailed()) return (permission::PermissionType) -1; /* not supported */ + + return (permission::PermissionType) ((this->data >> OFFSET_PERMISSION) & MASK_PERMISSION); + } + + [[nodiscard]] inline const detailed_command_result* details() const { return (detailed_command_result*) this->data; } + [[nodiscard]] inline detailed_command_result* details() { return (detailed_command_result*) this->data; } + + [[nodiscard]] inline bool is_detailed() const { + return (this->data & 0x1UL) == 0; + } + + inline std::unique_ptr release_details() { + if(!this->is_detailed()) return nullptr; + + auto result = this->details(); + this->data = 0; + return std::unique_ptr{result}; + } + + /* Attention: Releases the internal detailed pointer! */ + inline CommandResult as_command_result(); + +#ifndef _NDEBUG /* We dont need to secure that because gcc deduct us to an uint64_t and the only advantage is the mem leak test which is deactivated anyways */ + command_result(command_result&) = delete; + command_result(const command_result&) = delete; + command_result(command_result&& other) : data(other.data) { + other.data = 0; + } +#endif + + command_result() = default; + + explicit command_result(permission::PermissionType permission) { + this->data = 0x01; /* the the type to 1 */ + + this->data |= (uint64_t) error::server_insufficeient_permissions << OFFSET_ERROR; + this->data |= (uint64_t) permission << OFFSET_PERMISSION; + } + + + explicit command_result(error::type error) { + this->data = 0x01; /* the the type to 1 */ + + this->data |= (uint64_t) error << (8 - sizeof(error::type)) * 8; + } + + command_result(error::type error, const std::string& message) { + auto details_ptr = new detailed_command_result{}; + assert(((uintptr_t) details_ptr & 0x03U) == 0); // must be aligned! + + this->data = (uintptr_t) details_ptr; + + details_ptr->error_id = error; + details_ptr->extra_properties["extra_msg"] = message; + } + + command_result(error::type error, const std::map& properties) : command_result{error, std::string{""}} { + assert(this->is_detailed()); + this->details()->extra_properties = properties; + } + +#ifndef _NDEBUG + /* if we're not using any debug we dont have to use a deconstructor. A deconstructor prevent a direct uint64_t return as described above */ + ~command_result() { + if((this->data & 0x01) == 0x00) { + // this->details needs to be removed 'till this gets destructed + assert(this->data == 0); + } + } +#endif + }; + static_assert(sizeof(command_result) == 8); + + struct ErrorType { + public: + static ErrorType Success; + static ErrorType Costume; + static ErrorType VSError; + static ErrorType DBEmpty; + + ErrorType(const uint16_t errorId, std::string name, std::string message) : errorId(errorId), name(std::move(name)), message(std::move(message)) {} + ErrorType(const ErrorType& ref) = default; + ErrorType(ErrorType&& ref) : errorId(ref.errorId), name(std::move(ref.name)), message(std::move(ref.message)) {} + + uint16_t errorId; + std::string name; + std::string message; + + bool operator==(const ErrorType& ref) const { + return errorId == ref.errorId; + } + + bool operator!=(const ErrorType& ref) const { return !operator==(ref); } + + ErrorType& operator=(const ErrorType& ref) { + errorId = ref.errorId; + name = ref.name; + message = ref.message; + return *this; + } + + /** + * @return true if fail + */ + bool operator!() const { + return errorId != 0; + } + }; + + extern const std::vector avariableErrors; + extern ErrorType findError(uint16_t errorId); + extern ErrorType findError(std::string key); + + enum CommandResultType { + GENERAL, + PERM_ERROR + }; + + struct CommandResult { + public: + static CommandResult Success; + static CommandResult NotImplemented; + + CommandResult(const CommandResult& ref) : _type(ref._type), error(ref.error), extraProperties(ref.extraProperties) {} + CommandResult(CommandResult&& ref) : _type(ref._type), error(ref.error), extraProperties(ref.extraProperties) {} + CommandResult(ErrorType error, const std::string &extraMsg = "") : error(std::move(error)) { if(extraMsg.empty()) return; /*extraProperties["extramsg"] = extraMsg; */extraProperties["extra_msg"] = extraMsg; } + CommandResult(std::string error, const std::string &extraMsg = "") : error(findError(std::move(error))) { if(extraMsg.empty()) return; /*extraProperties["extramsg"] = extraMsg; */extraProperties["extra_msg"] = extraMsg; } + CommandResult() : CommandResult(ErrorType::Success, ""){} + CommandResult(ErrorType error, std::map details) : error(error), extraProperties(std::move(details)) {} + + bool operator==(const CommandResult& ref){ + return this->error == ref.error && ref.extraProperties == this->extraProperties; + } + + CommandResult& operator=(const CommandResult& ref)= default; + + /** + * @return true if fail + */ + bool operator!() const { + return this->error != ErrorType::Success; + } + virtual CommandResultType type(){ return _type; } + + ErrorType error; + std::map extraProperties; + CommandResultType _type = CommandResultType::GENERAL; + }; + + struct CommandResultPermissionError : public CommandResult { + public: + CommandResultPermissionError(permission::PermissionType error, const std::string &extraMsg = ""); + }; + + CommandResult command_result::as_command_result() { + if(this->is_detailed()) { + const auto details = this->details(); + auto result = CommandResult{findError(details->error_id), details->extra_properties}; + this->release_details(); + return result; + } else { + const auto code = this->error_code(); + auto error = findError(this->error_code()); + if(code == error::server_insufficeient_permissions) + return CommandResultPermissionError{(permission::PermissionType) this->permission_id()}; + else + return CommandResult{error}; + } + } +} + #undef _NDEBUG \ No newline at end of file diff --git a/src/EventLoop.h b/src/EventLoop.h index 9696f43..a0a893a 100644 --- a/src/EventLoop.h +++ b/src/EventLoop.h @@ -1,90 +1,90 @@ -#pragma once - -#include -#include -#include -#include -#include -#include - -namespace ts { - namespace event { - class EventExecutor; - - class EventEntry { - friend class EventExecutor; - public: - virtual void event_execute(const std::chrono::system_clock::time_point& /* scheduled timestamp */) = 0; - - private: - void* _event_ptr = nullptr; - }; - - template - class ProxiedEventEntry : public event::EventEntry { - public: - using callback_t = void(class_t::*)(const std::chrono::system_clock::time_point &); - using static_callback_t = void(*)(class_t *, const std::chrono::system_clock::time_point &); - - ProxiedEventEntry(const std::shared_ptr& _instance, callback_t callback) : instance(_instance), callback(callback) { } - - std::weak_ptr instance; - callback_t callback; - - void event_execute(const std::chrono::system_clock::time_point &point) override { - auto _instance = this->instance.lock(); - if(!_instance) - return; - - auto callback_ptr = (void**) &this->callback; - (*(static_callback_t*) callback_ptr)(&*_instance, point); - } - }; - - class EventExecutor { - public: - explicit EventExecutor(const std::string& /* thread prefix */); - virtual ~EventExecutor(); - - bool initialize(int /* num threads */); - bool schedule(const std::shared_ptr& /* entry */); - bool cancel(const std::shared_ptr& /* entry */); /* Note: Will not cancel already running executes */ - void shutdown(); - - inline const std::string& thread_prefix() const { return this->_thread_prefix; } - - void threads(int /* num threads */); - inline int threads() const { return this->target_threads; } - private: - struct LinkedEntry { - LinkedEntry* previous; - LinkedEntry* next; - - std::chrono::system_clock::time_point scheduled; - std::weak_ptr entry; - }; - - static void _executor(EventExecutor*); - void _spawn_executor(std::unique_lock&); - void _shutdown(std::unique_lock&); - void _reset_events(std::unique_lock&); - -#ifndef WIN32 - void _reassign_thread_names(std::unique_lock&); -#endif - - bool should_shutdown = true; - bool should_adjust = false; /* thread adjustments */ - int target_threads = 0; - - std::vector _threads; - std::mutex lock; - std::condition_variable condition; - - LinkedEntry* head = nullptr; - LinkedEntry* tail = nullptr; - - std::string _thread_prefix; - }; - } +#pragma once + +#include +#include +#include +#include +#include +#include + +namespace ts { + namespace event { + class EventExecutor; + + class EventEntry { + friend class EventExecutor; + public: + virtual void event_execute(const std::chrono::system_clock::time_point& /* scheduled timestamp */) = 0; + + private: + void* _event_ptr = nullptr; + }; + + template + class ProxiedEventEntry : public event::EventEntry { + public: + using callback_t = void(class_t::*)(const std::chrono::system_clock::time_point &); + using static_callback_t = void(*)(class_t *, const std::chrono::system_clock::time_point &); + + ProxiedEventEntry(const std::shared_ptr& _instance, callback_t callback) : instance(_instance), callback(callback) { } + + std::weak_ptr instance; + callback_t callback; + + void event_execute(const std::chrono::system_clock::time_point &point) override { + auto _instance = this->instance.lock(); + if(!_instance) + return; + + auto callback_ptr = (void**) &this->callback; + (*(static_callback_t*) callback_ptr)(&*_instance, point); + } + }; + + class EventExecutor { + public: + explicit EventExecutor(const std::string& /* thread prefix */); + virtual ~EventExecutor(); + + bool initialize(int /* num threads */); + bool schedule(const std::shared_ptr& /* entry */); + bool cancel(const std::shared_ptr& /* entry */); /* Note: Will not cancel already running executes */ + void shutdown(); + + inline const std::string& thread_prefix() const { return this->_thread_prefix; } + + void threads(int /* num threads */); + inline int threads() const { return this->target_threads; } + private: + struct LinkedEntry { + LinkedEntry* previous; + LinkedEntry* next; + + std::chrono::system_clock::time_point scheduled; + std::weak_ptr entry; + }; + + static void _executor(EventExecutor*); + void _spawn_executor(std::unique_lock&); + void _shutdown(std::unique_lock&); + void _reset_events(std::unique_lock&); + +#ifndef WIN32 + void _reassign_thread_names(std::unique_lock&); +#endif + + bool should_shutdown = true; + bool should_adjust = false; /* thread adjustments */ + int target_threads = 0; + + std::vector _threads; + std::mutex lock; + std::condition_variable condition; + + LinkedEntry* head = nullptr; + LinkedEntry* tail = nullptr; + + std::string _thread_prefix; + }; + } } \ No newline at end of file diff --git a/src/PermissionManager.cpp b/src/PermissionManager.cpp index 688383e..d75c5b4 100644 --- a/src/PermissionManager.cpp +++ b/src/PermissionManager.cpp @@ -1,1520 +1,1520 @@ -#include -#include -#include "misc/memtracker.h" -#include "BasicChannel.h" -#include "log/LogUtils.h" - -using namespace std; -using namespace ts; -using namespace ts::permission; - -deque> ts::permission::availablePermissions = deque>{ - make_shared(PermissionType::b_serverinstance_help_view, PermissionGroup::global_info, "b_serverinstance_help_view", "Retrieve information about ServerQuery commands"), - make_shared(PermissionType::b_serverinstance_version_view, PermissionGroup::global_info, "b_serverinstance_version_view", "Retrieve global server version (including platform and build number)"), - make_shared(PermissionType::b_serverinstance_info_view, PermissionGroup::global_info, "b_serverinstance_info_view", "Retrieve global server information"), - make_shared(PermissionType::b_serverinstance_virtualserver_list, PermissionGroup::global_info, "b_serverinstance_virtualserver_list", "List virtual servers stored in the sql"), - make_shared(PermissionType::b_serverinstance_binding_list, PermissionGroup::global_info, "b_serverinstance_binding_list", "List active IP bindings on multi-homed machines"), - //Removed due its useless - make_shared(PermissionType::b_serverinstance_permission_list, PermissionGroup::global_info, "b_serverinstance_permission_list", "List permissions available available on the server instance"), - make_shared(PermissionType::b_serverinstance_permission_find, PermissionGroup::global_info, "b_serverinstance_permission_find", "Search permission assignments by name or ID"), - make_shared(PermissionType::b_virtualserver_create, PermissionGroup::global_vsmanage, "b_virtualserver_create", "Create virtual servers"), - make_shared(PermissionType::b_virtualserver_delete, PermissionGroup::global_vsmanage, "b_virtualserver_delete", "Delete virtual servers"), - make_shared(PermissionType::b_virtualserver_start_any, PermissionGroup::global_vsmanage, "b_virtualserver_start_any", "Start any virtual server in the server instance"), - make_shared(PermissionType::b_virtualserver_stop_any, PermissionGroup::global_vsmanage, "b_virtualserver_stop_any", "Stop any virtual server in the server instance"), - make_shared(PermissionType::b_virtualserver_change_machine_id, PermissionGroup::global_vsmanage, "b_virtualserver_change_machine_id", "Change a virtual servers machine ID"), - make_shared(PermissionType::b_virtualserver_change_template, PermissionGroup::global_vsmanage, "b_virtualserver_change_template", "Edit virtual server default template values"), - make_shared(PermissionType::b_serverquery_login, PermissionGroup::global_admin, "b_serverquery_login", "Login to ServerQuery"), - make_shared(PermissionType::b_serverinstance_textmessage_send, PermissionGroup::global_admin, "b_serverinstance_textmessage_send", "Send text messages to all virtual servers at once"), - make_shared(PermissionType::b_serverinstance_log_view, PermissionGroup::global_admin, "b_serverinstance_log_view", "Retrieve global server log"), - make_shared(PermissionType::b_serverinstance_log_add, PermissionGroup::global_admin, "b_serverinstance_log_add", "Write to global server log"), - make_shared(PermissionType::b_serverinstance_stop, PermissionGroup::global_admin, "b_serverinstance_stop", "Shutdown the server process"), - make_shared(PermissionType::b_serverinstance_modify_settings, PermissionGroup::global_settings, "b_serverinstance_modify_settings", "Edit global settings"), - make_shared(PermissionType::b_serverinstance_modify_querygroup, PermissionGroup::global_settings, "b_serverinstance_modify_querygroup", "Edit global ServerQuery groups"), - make_shared(PermissionType::b_serverinstance_modify_templates, PermissionGroup::global_settings, "b_serverinstance_modify_templates", "Edit global template groups"), - make_shared(PermissionType::b_virtualserver_select, PermissionGroup::vs_info, "b_virtualserver_select", "Select a virtual server"), - make_shared(PermissionType::b_virtualserver_select_godmode, PermissionGroup::vs_info, "b_virtualserver_select_godmode", "Select a virtual server but be invisible"), - make_shared(PermissionType::b_virtualserver_info_view, PermissionGroup::vs_info, "b_virtualserver_info_view", "Retrieve virtual server information"), - make_shared(PermissionType::b_virtualserver_connectioninfo_view, PermissionGroup::vs_info, "b_virtualserver_connectioninfo_view", "Retrieve virtual server connection information"), - make_shared(PermissionType::b_virtualserver_channel_list, PermissionGroup::vs_info, "b_virtualserver_channel_list", "List channels on a virtual server"), - make_shared(PermissionType::b_virtualserver_channel_search, PermissionGroup::vs_info, "b_virtualserver_channel_search", "Search for channels on a virtual server"), - make_shared(PermissionType::b_virtualserver_client_list, PermissionGroup::vs_info, "b_virtualserver_client_list", "List clients online on a virtual server"), - make_shared(PermissionType::b_virtualserver_client_search, PermissionGroup::vs_info, "b_virtualserver_client_search", "Search for clients online on a virtual server"), - make_shared(PermissionType::b_virtualserver_client_dblist, PermissionGroup::vs_info, "b_virtualserver_client_dblist", "List client identities known by the virtual server"), - make_shared(PermissionType::b_virtualserver_client_dbsearch, PermissionGroup::vs_info, "b_virtualserver_client_dbsearch", "Search for client identities known by the virtual server"), - make_shared(PermissionType::b_virtualserver_client_dbinfo, PermissionGroup::vs_info, "b_virtualserver_client_dbinfo", "Retrieve client information"), - make_shared(PermissionType::b_virtualserver_permission_find, PermissionGroup::vs_info, "b_virtualserver_permission_find", "Find permissions"), - make_shared(PermissionType::b_virtualserver_custom_search, PermissionGroup::vs_info, "b_virtualserver_custom_search", "Find custom fields"), - make_shared(PermissionType::b_virtualserver_start, PermissionGroup::vs_admin, "b_virtualserver_start", "Start own virtual server"), - make_shared(PermissionType::b_virtualserver_stop, PermissionGroup::vs_admin, "b_virtualserver_stop", "Stop own virtual server"), - make_shared(PermissionType::b_virtualserver_token_list, PermissionGroup::vs_admin, "b_virtualserver_token_list", "List privilege keys available"), - make_shared(PermissionType::b_virtualserver_token_add, PermissionGroup::vs_admin, "b_virtualserver_token_add", "Create new privilege keys"), - make_shared(PermissionType::b_virtualserver_token_use, PermissionGroup::vs_admin, "b_virtualserver_token_use", "Use a privilege keys to gain access to groups"), - make_shared(PermissionType::b_virtualserver_token_delete, PermissionGroup::vs_admin, "b_virtualserver_token_delete", "Delete a privilege key"), - make_shared(PermissionType::b_virtualserver_log_view, PermissionGroup::vs_admin, "b_virtualserver_log_view", "Retrieve virtual server log"), - make_shared(PermissionType::b_virtualserver_log_add, PermissionGroup::vs_admin, "b_virtualserver_log_add", "Write to virtual server log"), - make_shared(PermissionType::b_virtualserver_join_ignore_password, PermissionGroup::vs_admin, "b_virtualserver_join_ignore_password", "Join virtual server ignoring its password"), - make_shared(PermissionType::b_virtualserver_notify_register, PermissionGroup::vs_admin, "b_virtualserver_notify_register", "Register for server notifications"), - make_shared(PermissionType::b_virtualserver_notify_unregister, PermissionGroup::vs_admin, "b_virtualserver_notify_unregister", "Unregister from server notifications"), - make_shared(PermissionType::b_virtualserver_snapshot_create, PermissionGroup::vs_admin, "b_virtualserver_snapshot_create", "Create server snapshots"), - make_shared(PermissionType::b_virtualserver_snapshot_deploy, PermissionGroup::vs_admin, "b_virtualserver_snapshot_deploy", "Deploy server snapshots"), - make_shared(PermissionType::b_virtualserver_permission_reset, PermissionGroup::vs_admin, "b_virtualserver_permission_reset", "Reset the server permission settings to default values"), - make_shared(PermissionType::b_virtualserver_modify_name, PermissionGroup::vs_settings, "b_virtualserver_modify_name", "Modify server name"), - make_shared(PermissionType::b_virtualserver_modify_welcomemessage, PermissionGroup::vs_settings, "b_virtualserver_modify_welcomemessage", "Modify welcome message"), - make_shared(PermissionType::b_virtualserver_modify_maxchannels, PermissionGroup::vs_settings, "b_virtualserver_modify_maxchannels", "Modify servers max channels"), - make_shared(PermissionType::b_virtualserver_modify_maxclients, PermissionGroup::vs_settings, "b_virtualserver_modify_maxclients", "Modify servers max clients"), - make_shared(PermissionType::b_virtualserver_modify_reserved_slots, PermissionGroup::vs_settings, "b_virtualserver_modify_reserved_slots", "Modify reserved slots"), - make_shared(PermissionType::b_virtualserver_modify_password, PermissionGroup::vs_settings, "b_virtualserver_modify_password", "Modify server password"), - make_shared(PermissionType::b_virtualserver_modify_default_servergroup, PermissionGroup::vs_settings, "b_virtualserver_modify_default_servergroup", "Modify default Server Group"), - make_shared(PermissionType::b_virtualserver_modify_default_musicgroup, PermissionGroup::vs_settings, "b_virtualserver_modify_default_musicgroup", "Modify default music Group"), - make_shared(PermissionType::b_virtualserver_modify_default_channelgroup, PermissionGroup::vs_settings, "b_virtualserver_modify_default_channelgroup", "Modify default Channel Group"), - make_shared(PermissionType::b_virtualserver_modify_default_channeladmingroup, PermissionGroup::vs_settings, "b_virtualserver_modify_default_channeladmingroup", "Modify default Channel Admin Group"), - make_shared(PermissionType::b_virtualserver_modify_channel_forced_silence, PermissionGroup::vs_settings, "b_virtualserver_modify_channel_forced_silence", "Modify channel force silence value"), - make_shared(PermissionType::b_virtualserver_modify_complain, PermissionGroup::vs_settings, "b_virtualserver_modify_complain", "Modify individual complain settings"), - make_shared(PermissionType::b_virtualserver_modify_antiflood, PermissionGroup::vs_settings, "b_virtualserver_modify_antiflood", "Modify individual antiflood settings"), - make_shared(PermissionType::b_virtualserver_modify_ft_settings, PermissionGroup::vs_settings, "b_virtualserver_modify_ft_settings", "Modify file transfer settings"), - make_shared(PermissionType::b_virtualserver_modify_ft_quotas, PermissionGroup::vs_settings, "b_virtualserver_modify_ft_quotas", "Modify file transfer quotas"), - make_shared(PermissionType::b_virtualserver_modify_hostmessage, PermissionGroup::vs_settings, "b_virtualserver_modify_hostmessage", "Modify individual hostmessage settings"), - make_shared(PermissionType::b_virtualserver_modify_hostbanner, PermissionGroup::vs_settings, "b_virtualserver_modify_hostbanner", "Modify individual hostbanner settings"), - make_shared(PermissionType::b_virtualserver_modify_hostbutton, PermissionGroup::vs_settings, "b_virtualserver_modify_hostbutton", "Modify individual hostbutton settings"), - make_shared(PermissionType::b_virtualserver_modify_port, PermissionGroup::vs_settings, "b_virtualserver_modify_port", "Modify server port"), -#ifndef LAGENCY - make_shared(PermissionType::b_virtualserver_modify_host, PermissionGroup::vs_settings, "b_virtualserver_modify_host", "Modify server host"), -#endif - make_shared(PermissionType::b_virtualserver_modify_autostart, PermissionGroup::vs_settings, "b_virtualserver_modify_autostart", "Modify server autostart"), - make_shared(PermissionType::b_virtualserver_modify_needed_identity_security_level, PermissionGroup::vs_settings, "b_virtualserver_modify_needed_identity_security_level", "Modify required identity security level"), - make_shared(PermissionType::b_virtualserver_modify_priority_speaker_dimm_modificator, PermissionGroup::vs_settings, "b_virtualserver_modify_priority_speaker_dimm_modificator", "Modify priority speaker dimm modificator"), - make_shared(PermissionType::b_virtualserver_modify_log_settings, PermissionGroup::vs_settings, "b_virtualserver_modify_log_settings", "Modify log settings"), - make_shared(PermissionType::b_virtualserver_modify_min_client_version, PermissionGroup::vs_settings, "b_virtualserver_modify_min_client_version", "Modify min client version"), - make_shared(PermissionType::b_virtualserver_modify_icon_id, PermissionGroup::vs_settings, "b_virtualserver_modify_icon_id", "Modify server icon"), - make_shared(PermissionType::b_virtualserver_modify_weblist, PermissionGroup::vs_settings, "b_virtualserver_modify_weblist", "Modify web server list reporting settings"), - make_shared(PermissionType::b_virtualserver_modify_country_code, PermissionGroup::vs_settings, "b_virtualserver_modify_country_code", "Modify servers country code property"), - make_shared(PermissionType::b_virtualserver_modify_codec_encryption_mode, PermissionGroup::vs_settings, "b_virtualserver_modify_codec_encryption_mode", "Modify codec encryption mode"), - make_shared(PermissionType::b_virtualserver_modify_temporary_passwords, PermissionGroup::vs_settings, "b_virtualserver_modify_temporary_passwords", "Modify temporary serverpasswords"), - make_shared(PermissionType::b_virtualserver_modify_temporary_passwords_own, PermissionGroup::vs_settings, "b_virtualserver_modify_temporary_passwords_own", "Modify own temporary serverpasswords"), - make_shared(PermissionType::b_virtualserver_modify_channel_temp_delete_delay_default, PermissionGroup::vs_settings, "b_virtualserver_modify_channel_temp_delete_delay_default", "Modify default temporary channel delete delay"), - make_shared(PermissionType::b_virtualserver_modify_music_bot_limit, PermissionGroup::vs_settings, "b_virtualserver_modify_music_bot_limit", "Allow client to edit the server music bot limit"), - make_shared(PermissionType::b_virtualserver_modify_default_messages, PermissionGroup::vs_settings, "b_virtualserver_modify_default_messages", "Allows the client to edit the default messages"), - make_shared(PermissionType::i_channel_min_depth, PermissionGroup::channel, "i_channel_min_depth", "Min channel creation depth in hierarchy"), - make_shared(PermissionType::i_channel_max_depth, PermissionGroup::channel, "i_channel_max_depth", "Max channel creation depth in hierarchy"), - make_shared(PermissionType::b_channel_group_inheritance_end, PermissionGroup::channel, "b_channel_group_inheritance_end", "Stop inheritance of channel group permissions"), - make_shared(PermissionType::i_channel_permission_modify_power, PermissionGroup::channel, "i_channel_permission_modify_power", "Modify channel permission power"), - make_shared(PermissionType::i_channel_needed_permission_modify_power, PermissionGroup::channel, "i_channel_needed_permission_modify_power", "Needed modify channel permission power"), - make_shared(PermissionType::b_channel_info_view, PermissionGroup::channel_info, "b_channel_info_view", "Retrieve channel information"), - make_shared(PermissionType::b_channel_create_child, PermissionGroup::channel_create, "b_channel_create_child", "Create sub-channels"), - make_shared(PermissionType::b_channel_create_permanent, PermissionGroup::channel_create, "b_channel_create_permanent", "Create permanent channels"), - make_shared(PermissionType::b_channel_create_semi_permanent, PermissionGroup::channel_create, "b_channel_create_semi_permanent", "Create semi-permanent channels"), - make_shared(PermissionType::b_channel_create_temporary, PermissionGroup::channel_create, "b_channel_create_temporary", "Create temporary channels"), - make_shared(PermissionType::b_channel_create_private, PermissionGroup::channel_create, "b_channel_create_private", "Create private channel"), - make_shared(PermissionType::b_channel_create_with_topic, PermissionGroup::channel_create, "b_channel_create_with_topic", "Create channels with a topic"), - make_shared(PermissionType::b_channel_create_with_description, PermissionGroup::channel_create, "b_channel_create_with_description", "Create channels with a description"), - make_shared(PermissionType::b_channel_create_with_password, PermissionGroup::channel_create, "b_channel_create_with_password", "Create password protected channels"), - make_shared(PermissionType::b_channel_create_modify_with_codec_speex8, PermissionGroup::channel_create, "b_channel_create_modify_with_codec_speex8", "Create channels using Speex Narrowband (8 kHz) codecs"), - make_shared(PermissionType::b_channel_create_modify_with_codec_speex16, PermissionGroup::channel_create, "b_channel_create_modify_with_codec_speex16", "Create channels using Speex Wideband (16 kHz) codecs"), - make_shared(PermissionType::b_channel_create_modify_with_codec_speex32, PermissionGroup::channel_create, "b_channel_create_modify_with_codec_speex32", "Create channels using Speex Ultra-Wideband (32 kHz) codecs"), - make_shared(PermissionType::b_channel_create_modify_with_codec_celtmono48, PermissionGroup::channel_create, "b_channel_create_modify_with_codec_celtmono48", "Create channels using the CELT Mono (48 kHz) codec"), - make_shared(PermissionType::b_channel_create_modify_with_codec_opusvoice, PermissionGroup::channel_create, "b_channel_create_modify_with_codec_opusvoice", "Create channels using OPUS (voice) codec"), - make_shared(PermissionType::b_channel_create_modify_with_codec_opusmusic, PermissionGroup::channel_create, "b_channel_create_modify_with_codec_opusmusic", "Create channels using OPUS (music) codec"), - make_shared(PermissionType::i_channel_create_modify_with_codec_maxquality, PermissionGroup::channel_create, "i_channel_create_modify_with_codec_maxquality", "Create channels with custom codec quality"), - make_shared(PermissionType::i_channel_create_modify_with_codec_latency_factor_min, PermissionGroup::channel_create, "i_channel_create_modify_with_codec_latency_factor_min", "Create channels with minimal custom codec latency factor"), - make_shared(PermissionType::b_channel_create_with_maxclients, PermissionGroup::channel_create, "b_channel_create_with_maxclients", "Create channels with custom max clients"), - make_shared(PermissionType::b_channel_create_with_maxfamilyclients, PermissionGroup::channel_create, "b_channel_create_with_maxfamilyclients", "Create channels with custom max family clients"), - make_shared(PermissionType::b_channel_create_with_sortorder, PermissionGroup::channel_create, "b_channel_create_with_sortorder", "Create channels with custom sort order"), - make_shared(PermissionType::b_channel_create_with_default, PermissionGroup::channel_create, "b_channel_create_with_default", "Create default channels"), - make_shared(PermissionType::b_channel_create_with_needed_talk_power, PermissionGroup::channel_create, "b_channel_create_with_needed_talk_power", "Create channels with needed talk power"), - make_shared(PermissionType::b_channel_create_modify_with_force_password, PermissionGroup::channel_create, "b_channel_create_modify_with_force_password", "Create new channels only with password"), - make_shared(PermissionType::i_channel_create_modify_with_temp_delete_delay, PermissionGroup::channel_create, "i_channel_create_modify_with_temp_delete_delay", "Max delete delay for temporary channels"), - make_shared(PermissionType::i_channel_create_modify_conversation_history_length, PermissionGroup::channel_create, "i_channel_create_modify_conversation_history_length", "Upper limmit for the setting of the max conversation history limit"), - make_shared(PermissionType::b_channel_create_modify_conversation_history_unlimited, PermissionGroup::channel_create, "b_channel_create_modify_conversation_history_unlimited", "Allows the user to set the channel conversation history to unlimited"), - make_shared(PermissionType::b_channel_create_modify_conversation_private, PermissionGroup::channel_create, "b_channel_create_modify_conversation_private", "Allows the user to set the channel conversation to private"), - make_shared(PermissionType::b_channel_modify_parent, PermissionGroup::channel_modify, "b_channel_modify_parent", "Move channels"), - make_shared(PermissionType::b_channel_modify_make_default, PermissionGroup::channel_modify, "b_channel_modify_make_default", "Make channel default"), - make_shared(PermissionType::b_channel_modify_make_permanent, PermissionGroup::channel_modify, "b_channel_modify_make_permanent", "Make channel permanent"), - make_shared(PermissionType::b_channel_modify_make_semi_permanent, PermissionGroup::channel_modify, "b_channel_modify_make_semi_permanent", "Make channel semi-permanent"), - make_shared(PermissionType::b_channel_modify_make_temporary, PermissionGroup::channel_modify, "b_channel_modify_make_temporary", "Make channel temporary"), - make_shared(PermissionType::b_channel_modify_name, PermissionGroup::channel_modify, "b_channel_modify_name", "Modify channel name"), - make_shared(PermissionType::b_channel_modify_topic, PermissionGroup::channel_modify, "b_channel_modify_topic", "Modify channel topic"), - make_shared(PermissionType::b_channel_modify_description, PermissionGroup::channel_modify, "b_channel_modify_description", "Modify channel description"), - make_shared(PermissionType::b_channel_modify_password, PermissionGroup::channel_modify, "b_channel_modify_password", "Modify channel password"), - make_shared(PermissionType::b_channel_modify_codec, PermissionGroup::channel_modify, "b_channel_modify_codec", "Modify channel codec"), - make_shared(PermissionType::b_channel_modify_codec_quality, PermissionGroup::channel_modify, "b_channel_modify_codec_quality", "Modify channel codec quality"), - make_shared(PermissionType::b_channel_modify_codec_latency_factor, PermissionGroup::channel_modify, "b_channel_modify_codec_latency_factor", "Modify channel codec latency factor"), - make_shared(PermissionType::b_channel_modify_maxclients, PermissionGroup::channel_modify, "b_channel_modify_maxclients", "Modify channels max clients"), - make_shared(PermissionType::b_channel_modify_maxfamilyclients, PermissionGroup::channel_modify, "b_channel_modify_maxfamilyclients", "Modify channels max family clients"), - make_shared(PermissionType::b_channel_modify_sortorder, PermissionGroup::channel_modify, "b_channel_modify_sortorder", "Modify channel sort order"), - make_shared(PermissionType::b_channel_modify_needed_talk_power, PermissionGroup::channel_modify, "b_channel_modify_needed_talk_power", "Change needed channel talk power"), - make_shared(PermissionType::i_channel_modify_power, PermissionGroup::channel_modify, "i_channel_modify_power", "Channel modify power"), - make_shared(PermissionType::i_channel_needed_modify_power, PermissionGroup::channel_modify, "i_channel_needed_modify_power", "Needed channel modify power"), - make_shared(PermissionType::b_channel_modify_make_codec_encrypted, PermissionGroup::channel_modify, "b_channel_modify_make_codec_encrypted", "Make channel codec encrypted"), - make_shared(PermissionType::b_channel_modify_temp_delete_delay, PermissionGroup::channel_modify, "b_channel_modify_temp_delete_delay", "Modify temporary channel delete delay"), - make_shared(PermissionType::b_channel_conversation_message_delete, PermissionGroup::channel_modify, "b_channel_conversation_message_delete", "If set the user is able to delete conversation messages"), - - make_shared(PermissionType::b_channel_delete_permanent, PermissionGroup::channel_delete, "b_channel_delete_permanent", "Delete permanent channels"), - make_shared(PermissionType::b_channel_delete_semi_permanent, PermissionGroup::channel_delete, "b_channel_delete_semi_permanent", "Delete semi-permanent channels"), - make_shared(PermissionType::b_channel_delete_temporary, PermissionGroup::channel_delete, "b_channel_delete_temporary", "Delete temporary channels"), - make_shared(PermissionType::b_channel_delete_flag_force, PermissionGroup::channel_delete, "b_channel_delete_flag_force", "Force channel delete"), - make_shared(PermissionType::i_channel_delete_power, PermissionGroup::channel_delete, "i_channel_delete_power", "Delete channel power"), - make_shared(PermissionType::i_channel_needed_delete_power, PermissionGroup::channel_delete, "i_channel_needed_delete_power", "Needed delete channel power"), - make_shared(PermissionType::b_channel_join_permanent, PermissionGroup::channel_access, "b_channel_join_permanent", "Join permanent channels"), - make_shared(PermissionType::b_channel_join_semi_permanent, PermissionGroup::channel_access, "b_channel_join_semi_permanent", "Join semi-permanent channels"), - make_shared(PermissionType::b_channel_join_temporary, PermissionGroup::channel_access, "b_channel_join_temporary", "Join temporary channels"), - make_shared(PermissionType::b_channel_join_ignore_password, PermissionGroup::channel_access, "b_channel_join_ignore_password", "Join channel ignoring its password"), - make_shared(PermissionType::b_channel_join_ignore_maxclients, PermissionGroup::channel_access, "b_channel_join_ignore_maxclients", "Ignore channels max clients limit"), - make_shared(PermissionType::b_channel_ignore_view_power, PermissionGroup::channel_access, "b_channel_ignore_view_power", "If set the client see's every channel"), - make_shared(PermissionType::i_channel_join_power, PermissionGroup::channel_access, "i_channel_join_power", "Channel join power"), - make_shared(PermissionType::i_channel_needed_join_power, PermissionGroup::channel_access, "i_channel_needed_join_power", "Needed channel join power"), - make_shared(PermissionType::b_channel_ignore_join_power, PermissionGroup::channel_access, "b_channel_ignore_join_power", "Allows the client to bypass the channel join power"), - make_shared(PermissionType::i_channel_view_power, PermissionGroup::channel_access, "i_channel_view_power", "Channel view power"), - make_shared(PermissionType::i_channel_needed_view_power, PermissionGroup::channel_access, "i_channel_needed_view_power", "Needed channel view power"), - make_shared(PermissionType::i_channel_subscribe_power, PermissionGroup::channel_access, "i_channel_subscribe_power", "Channel subscribe power"), - make_shared(PermissionType::i_channel_needed_subscribe_power, PermissionGroup::channel_access, "i_channel_needed_subscribe_power", "Needed channel subscribe power"), - make_shared(PermissionType::b_channel_ignore_subscribe_power, PermissionGroup::channel_access, "b_channel_ignore_subscribe_power", "Allows the client to bypass the subscribe power"), - make_shared(PermissionType::i_channel_description_view_power, PermissionGroup::channel_access, "i_channel_description_view_power", "Channel description view power"), - make_shared(PermissionType::i_channel_needed_description_view_power, PermissionGroup::channel_access, "i_channel_needed_description_view_power", "Needed channel needed description view power"), - make_shared(PermissionType::b_channel_ignore_description_view_power, PermissionGroup::channel_access, "b_channel_ignore_description_view_power", "Allows the client to bypass the channel description view power"), - make_shared(PermissionType::i_icon_id, PermissionGroup::group, "i_icon_id", "Group icon identifier"), - make_shared(PermissionType::i_max_icon_filesize, PermissionGroup::group, "i_max_icon_filesize", "Max icon filesize in bytes"), - make_shared(PermissionType::i_max_playlist_size, PermissionGroup::group, "i_max_playlist_size", "Max songs within one playlist"), - make_shared(PermissionType::i_max_playlists, PermissionGroup::group, "i_max_playlists", "Max amount of playlists a client could own"), - make_shared(PermissionType::b_icon_manage, PermissionGroup::group, "b_icon_manage", "Enables icon management"), - make_shared(PermissionType::b_group_is_permanent, PermissionGroup::group, "b_group_is_permanent", "Group is permanent"), - make_shared(PermissionType::i_group_auto_update_type, PermissionGroup::group, "i_group_auto_update_type", "Group auto-update type"), - make_shared(PermissionType::i_group_auto_update_max_value, PermissionGroup::group, "i_group_auto_update_max_value", "Group auto-update max value"), - make_shared(PermissionType::i_group_sort_id, PermissionGroup::group, "i_group_sort_id", "Group sort id"), - make_shared(PermissionType::i_group_show_name_in_tree, PermissionGroup::group, "i_group_show_name_in_tree", "Show group name in tree depending on selected mode"), - make_shared(PermissionType::b_virtualserver_servergroup_list, PermissionGroup::group_info, "b_virtualserver_servergroup_list", "List server groups"), - make_shared(PermissionType::b_virtualserver_servergroup_permission_list, PermissionGroup::group_info, "b_virtualserver_servergroup_permission_list", "Allows the client to view all server group permissions"), - make_shared(PermissionType::b_virtualserver_servergroup_client_list, PermissionGroup::group_info, "b_virtualserver_servergroup_client_list", "List clients from a server group"), - make_shared(PermissionType::b_virtualserver_channelgroup_list, PermissionGroup::group_info, "b_virtualserver_channelgroup_list", "List channel groups"), - make_shared(PermissionType::b_virtualserver_channelgroup_permission_list, PermissionGroup::group_info, "b_virtualserver_channelgroup_permission_list", "Allows the client to view all channel group permissions"), - make_shared(PermissionType::b_virtualserver_channelgroup_client_list, PermissionGroup::group_info, "b_virtualserver_channelgroup_client_list", "List clients from a channel group"), - make_shared(PermissionType::b_virtualserver_client_permission_list, PermissionGroup::group_info, "b_virtualserver_client_permission_list", "Allows the client to view all client permissions"), - make_shared(PermissionType::b_virtualserver_channel_permission_list, PermissionGroup::group_info, "b_virtualserver_channel_permission_list", "Allows the client to view all channel permissions"), - make_shared(PermissionType::b_virtualserver_channelclient_permission_list, PermissionGroup::group_info, "b_virtualserver_channelclient_permission_list", "Allows the client to view all client channel permissions"), - make_shared(PermissionType::b_virtualserver_playlist_permission_list, PermissionGroup::group_info, "b_virtualserver_playlist_permission_list", "Allows the client to view all playlist permissions"), - make_shared(PermissionType::b_virtualserver_servergroup_create, PermissionGroup::group_create, "b_virtualserver_servergroup_create", "Create server groups"), - make_shared(PermissionType::b_virtualserver_channelgroup_create, PermissionGroup::group_create, "b_virtualserver_channelgroup_create", "Create channel groups"), - -#ifdef LAGENCY - make_shared(PermissionType::i_group_modify_power, PermissionGroup::group_168, "i_group_modify_power", "Group modify power"), - make_shared(PermissionType::i_group_needed_modify_power, PermissionGroup::group_168, "i_group_needed_modify_power", "Needed group modify power"), - make_shared(PermissionType::i_group_member_add_power, PermissionGroup::group_168, "i_group_member_add_power", "Group member add power"), - make_shared(PermissionType::i_group_needed_member_add_power, PermissionGroup::group_168, "i_group_needed_member_add_power", "Needed group member add power"), - make_shared(PermissionType::i_group_member_remove_power, PermissionGroup::group_168, "i_group_member_remove_power", "Group member delete power"), - make_shared(PermissionType::i_group_needed_member_remove_power, PermissionGroup::group_168, "i_group_needed_member_remove_power", "Needed group member delete power"), -#else - make_shared(PermissionType::i_server_group_modify_power, PermissionGroup::group_modify, "i_server_group_modify_power", "Server group modify power"), - make_shared(PermissionType::i_server_group_needed_modify_power, PermissionGroup::group_modify, "i_server_group_needed_modify_power", "Needed server group modify power"), - make_shared(PermissionType::i_server_group_member_add_power, PermissionGroup::group_modify, "i_server_group_member_add_power", "Server group member add power"), - make_shared(PermissionType::i_server_group_self_add_power, PermissionGroup::group_modify, "i_server_group_self_add_power", "Server group self add power"), - make_shared(PermissionType::i_server_group_needed_member_add_power, PermissionGroup::group_modify, "i_server_group_needed_member_add_power", "Needed server group member add power"), - make_shared(PermissionType::i_server_group_member_remove_power, PermissionGroup::group_modify, "i_server_group_member_remove_power", "Server group member delete power"), - make_shared(PermissionType::i_server_group_self_remove_power, PermissionGroup::group_modify, "i_server_group_self_remove_power", "Server group self delete power"), - make_shared(PermissionType::i_server_group_needed_member_remove_power, PermissionGroup::group_modify, "i_server_group_needed_member_remove_power", "Needed server group member delete power"), - - make_shared(PermissionType::i_channel_group_modify_power, PermissionGroup::group_modify, "i_channel_group_modify_power", "Channel group modify power"), - make_shared(PermissionType::i_channel_group_needed_modify_power, PermissionGroup::group_modify, "i_channel_group_needed_modify_power", "Needed channel group modify power"), - make_shared(PermissionType::i_channel_group_member_add_power, PermissionGroup::group_modify, "i_channel_group_member_add_power", "Channel group member add power"), - make_shared(PermissionType::i_channel_group_self_add_power, PermissionGroup::group_modify, "i_channel_group_self_add_power", "Channel group self add power"), - make_shared(PermissionType::i_channel_group_needed_member_add_power, PermissionGroup::group_modify, "i_channel_group_needed_member_add_power", "Needed channel group member add power"), - make_shared(PermissionType::i_channel_group_member_remove_power, PermissionGroup::group_modify, "i_channel_group_member_remove_power", "Channel group member delete power"), - make_shared(PermissionType::i_channel_group_self_remove_power, PermissionGroup::group_modify, "i_channel_group_self_remove_power", "Channel group self delete power"), - make_shared(PermissionType::i_channel_group_needed_member_remove_power, PermissionGroup::group_modify, "i_channel_group_needed_member_remove_power", "Needed channel group member delete power"), - - //old enum mapping - make_shared(PermissionType::i_displayed_group_member_add_power, PermissionGroup::group_modify, "i_group_member_add_power", "The displayed member add power (Enables/Disabled the group in the select menu)"), - make_shared(PermissionType::i_displayed_group_needed_member_add_power, PermissionGroup::group_modify, "i_group_needed_member_add_power", "The needed displayed member add power (Enables/Disabled the group in the select menu)"), - make_shared(PermissionType::i_displayed_group_member_remove_power, PermissionGroup::group_modify, "i_group_member_remove_power", "The displayed member add power (Enables/Disabled the group in the select menu)"), - make_shared(PermissionType::i_displayed_group_needed_member_remove_power, PermissionGroup::group_modify, "i_group_needed_member_remove_power", "The needed displayed member add power (Enables/Disabled the group in the select menu)"), - make_shared(PermissionType::i_displayed_group_modify_power, PermissionGroup::group_modify, "i_group_modify_power", "The displayed member add power (Enables/Disabled the group in the select menu)"), - make_shared(PermissionType::i_displayed_group_needed_modify_power, PermissionGroup::group_modify, "i_group_needed_modify_power", "The needed displayed member add power (Enables/Disabled the group in the select menu)"), - - //new enum mapping (must come AFTER the supported permissions) - make_shared(PermissionType::i_displayed_group_member_add_power, PermissionGroup::group_modify, "i_displayed_group_member_add_power", "The displayed member add power (Enables/Disabled the group in the select menu)", false), - make_shared(PermissionType::i_displayed_group_needed_member_add_power, PermissionGroup::group_modify, "i_displayed_group_needed_member_add_power", "The needed displayed member add power (Enables/Disabled the group in the select menu)", false), - make_shared(PermissionType::i_displayed_group_member_remove_power, PermissionGroup::group_modify, "i_displayed_group_member_remove_power", "The displayed member add power (Enables/Disabled the group in the select menu)", false), - make_shared(PermissionType::i_displayed_group_needed_member_remove_power, PermissionGroup::group_modify, "i_displayed_group_needed_member_remove_power", "The needed displayed member add power (Enables/Disabled the group in the select menu)", false), - make_shared(PermissionType::i_displayed_group_modify_power, PermissionGroup::group_modify, "i_displayed_group_modify_power", "The displayed member add power (Enables/Disabled the group in the select menu)", false), - make_shared(PermissionType::i_displayed_group_needed_modify_power, PermissionGroup::group_modify, "i_displayed_group_needed_modify_power", "The needed displayed member add power (Enables/Disabled the group in the select menu)", false), -#endif - - make_shared(PermissionType::i_permission_modify_power, PermissionGroup::group_modify, "i_permission_modify_power", "Permission modify power"), - make_shared(PermissionType::b_permission_modify_power_ignore, PermissionGroup::group_modify, "b_permission_modify_power_ignore", "Ignore needed permission modify power"), - make_shared(PermissionType::b_virtualserver_servergroup_delete, PermissionGroup::group_delete, "b_virtualserver_servergroup_delete", "Delete server groups"), - make_shared(PermissionType::b_virtualserver_channelgroup_delete, PermissionGroup::group_delete, "b_virtualserver_channelgroup_delete", "Delete channel groups"), - make_shared(PermissionType::i_client_permission_modify_power, PermissionGroup::client, "i_client_permission_modify_power", "Client permission modify power"), - make_shared(PermissionType::i_client_needed_permission_modify_power, PermissionGroup::client, "i_client_needed_permission_modify_power", "Needed client permission modify power"), - make_shared(PermissionType::i_client_max_clones_uid, PermissionGroup::client, "i_client_max_clones_uid", "Max additional connections per client identity"), - make_shared(PermissionType::i_client_max_clones_ip, PermissionGroup::client, "i_client_max_clones_ip", "Max additional connections per client address"), - make_shared(PermissionType::i_client_max_clones_hwid, PermissionGroup::client, "i_client_max_clones_hwid", "Max additional connections per client hardware id"), - make_shared(PermissionType::i_client_max_idletime, PermissionGroup::client, "i_client_max_idletime", "Max idle time in seconds"), - make_shared(PermissionType::i_client_max_avatar_filesize, PermissionGroup::client, "i_client_max_avatar_filesize", "Max avatar filesize in bytes"), - make_shared(PermissionType::i_client_max_channel_subscriptions, PermissionGroup::client, "i_client_max_channel_subscriptions", "Max channel subscriptions"), - make_shared(PermissionType::i_client_max_channels, PermissionGroup::client, "i_client_max_channels", "Limit of created channels"), - make_shared(PermissionType::i_client_max_temporary_channels, PermissionGroup::client, "i_client_max_temporary_channels", "Limit of created temporary channels"), - make_shared(PermissionType::i_client_max_semi_channels, PermissionGroup::client, "i_client_max_semi_channels", "Limit of created semi-permanent channels"), - make_shared(PermissionType::i_client_max_permanent_channels, PermissionGroup::client, "i_client_max_permanent_channels", "Limit of created permanent channels"), - make_shared(PermissionType::b_client_use_priority_speaker, PermissionGroup::client, "b_client_use_priority_speaker", "Allows the client to use priority speaker"), - make_shared(PermissionType::b_client_is_priority_speaker, PermissionGroup::client, "b_client_is_priority_speaker", "Toogels the client priority speaker mode"), - make_shared(PermissionType::b_client_skip_channelgroup_permissions, PermissionGroup::client, "b_client_skip_channelgroup_permissions", "Ignore channel group permissions"), - make_shared(PermissionType::b_client_force_push_to_talk, PermissionGroup::client, "b_client_force_push_to_talk", "Force Push-To-Talk capture mode"), - make_shared(PermissionType::b_client_ignore_bans, PermissionGroup::client, "b_client_ignore_bans", "Ignore bans"), - make_shared(PermissionType::b_client_ignore_vpn, PermissionGroup::client, "b_client_ignore_vpn", "Ignore the vpn check"), - make_shared(PermissionType::b_client_ignore_antiflood, PermissionGroup::client, "b_client_ignore_antiflood", "Ignore antiflood measurements"), - make_shared(PermissionType::b_client_enforce_valid_hwid, PermissionGroup::client, "b_client_enforce_valid_hwid", "Enforces the client to have a valid hardware id"), - make_shared(PermissionType::b_client_allow_invalid_packet, PermissionGroup::client, "b_client_allow_invalid_packet", "Allow client to send invalid packets"), - make_shared(PermissionType::b_client_allow_invalid_badges, PermissionGroup::client, "b_client_allow_invalid_badges", "Allow client to have invalid badges"), - make_shared(PermissionType::b_client_issue_client_query_command, PermissionGroup::client, "b_client_issue_client_query_command", "Issue query commands from client"), - make_shared(PermissionType::b_client_use_reserved_slot, PermissionGroup::client, "b_client_use_reserved_slot", "Use an reserved slot"), - make_shared(PermissionType::b_client_use_channel_commander, PermissionGroup::client, "b_client_use_channel_commander", "Use channel commander"), - make_shared(PermissionType::b_client_request_talker, PermissionGroup::client, "b_client_request_talker", "Allow to request talk power"), - make_shared(PermissionType::b_client_avatar_delete_other, PermissionGroup::client, "b_client_avatar_delete_other", "Allow deletion of avatars from other clients"), - make_shared(PermissionType::b_client_is_sticky, PermissionGroup::client, "b_client_is_sticky", "Client will be sticked to current channel"), - make_shared(PermissionType::b_client_ignore_sticky, PermissionGroup::client, "b_client_ignore_sticky", "Client ignores sticky flag"), -#ifndef LAGACY - make_shared(PermissionType::b_client_music_channel_list, PermissionGroup::client_info, "b_client_music_channel_list", "List all music bots in the current channel"), - make_shared(PermissionType::b_client_music_server_list, PermissionGroup::client_info, "b_client_music_server_list", "List all music bots on the sderver"), - make_shared(PermissionType::i_client_music_info, PermissionGroup::client_info, "i_client_music_info", "Permission to view music bot info"), - make_shared(PermissionType::i_client_music_needed_info, PermissionGroup::client_info, "i_client_music_needed_info", "Required permission to view music bot info"), -#endif - make_shared(PermissionType::b_client_info_view, PermissionGroup::client_info, "b_client_info_view", "Retrieve client information"), - make_shared(PermissionType::b_client_permissionoverview_view, PermissionGroup::client_info, "b_client_permissionoverview_view", "Retrieve client permissions overview"), - make_shared(PermissionType::b_client_permissionoverview_own, PermissionGroup::client_info, "b_client_permissionoverview_own", "Retrieve clients own permissions overview"), - make_shared(PermissionType::b_client_remoteaddress_view, PermissionGroup::client_info, "b_client_remoteaddress_view", "View client IP address and port"), - make_shared(PermissionType::i_client_serverquery_view_power, PermissionGroup::client_info, "i_client_serverquery_view_power", "ServerQuery view power"), - make_shared(PermissionType::i_client_needed_serverquery_view_power, PermissionGroup::client_info, "i_client_needed_serverquery_view_power", "Needed ServerQuery view power"), - make_shared(PermissionType::b_client_custom_info_view, PermissionGroup::client_info, "b_client_custom_info_view", "View custom fields"), - make_shared(PermissionType::i_client_kick_from_server_power, PermissionGroup::client_admin, "i_client_kick_from_server_power", "Client kick power from server"), - make_shared(PermissionType::i_client_needed_kick_from_server_power, PermissionGroup::client_admin, "i_client_needed_kick_from_server_power", "Needed client kick power from server"), - make_shared(PermissionType::i_client_kick_from_channel_power, PermissionGroup::client_admin, "i_client_kick_from_channel_power", "Client kick power from channel"), - make_shared(PermissionType::i_client_needed_kick_from_channel_power, PermissionGroup::client_admin, "i_client_needed_kick_from_channel_power", "Needed client kick power from channel"), - make_shared(PermissionType::i_client_ban_power, PermissionGroup::client_admin, "i_client_ban_power", "Client ban power"), - make_shared(PermissionType::i_client_needed_ban_power, PermissionGroup::client_admin, "i_client_needed_ban_power", "Needed client ban power"), - make_shared(PermissionType::i_client_move_power, PermissionGroup::client_admin, "i_client_move_power", "Client move power"), - make_shared(PermissionType::i_client_needed_move_power, PermissionGroup::client_admin, "i_client_needed_move_power", "Needed client move power"), - make_shared(PermissionType::i_client_complain_power, PermissionGroup::client_admin, "i_client_complain_power", "Complain power"), - make_shared(PermissionType::i_client_needed_complain_power, PermissionGroup::client_admin, "i_client_needed_complain_power", "Needed complain power"), - make_shared(PermissionType::b_client_complain_list, PermissionGroup::client_admin, "b_client_complain_list", "Show complain list"), - make_shared(PermissionType::b_client_complain_delete_own, PermissionGroup::client_admin, "b_client_complain_delete_own", "Delete own complains"), - make_shared(PermissionType::b_client_complain_delete, PermissionGroup::client_admin, "b_client_complain_delete", "Delete complains"), - make_shared(PermissionType::b_client_ban_list, PermissionGroup::client_admin, "b_client_ban_list", "Show banlist"), - make_shared(PermissionType::b_client_ban_trigger_list, PermissionGroup::client_admin, "b_client_ban_trigger_list", "Show trigger banlist"), - make_shared(PermissionType::b_client_ban_list_global, PermissionGroup::client_admin, "b_client_ban_list_global", "Show banlist globaly"), - make_shared(PermissionType::b_client_ban_create, PermissionGroup::client_admin, "b_client_ban_create", "Add a ban"), - make_shared(PermissionType::b_client_ban_create_global, PermissionGroup::client_admin, "b_client_ban_create_global", "Allow to create global bans"), - make_shared(PermissionType::b_client_ban_name, PermissionGroup::client_admin, "b_client_ban_name", "Allows the client to ban a client by name"), - make_shared(PermissionType::b_client_ban_ip, PermissionGroup::client_admin, "b_client_ban_ip", "Allows the client to ban a client by ip"), - make_shared(PermissionType::b_client_ban_hwid, PermissionGroup::client_admin, "b_client_ban_hwid", "Allows the client to ban a client hardware id"), - make_shared(PermissionType::b_client_ban_edit, PermissionGroup::client_admin, "b_client_ban_edit", "Allow to edit bans"), - make_shared(PermissionType::b_client_ban_edit_global, PermissionGroup::client_admin, "b_client_ban_edit_global", "Allow to edit global bans"), - make_shared(PermissionType::b_client_ban_delete_own, PermissionGroup::client_admin, "b_client_ban_delete_own", "Delete own bans"), - make_shared(PermissionType::b_client_ban_delete, PermissionGroup::client_admin, "b_client_ban_delete", "Delete bans"), - make_shared(PermissionType::b_client_ban_delete_own_global, PermissionGroup::client_admin, "b_client_ban_delete_own_global", "Delete own global bans"), - make_shared(PermissionType::b_client_ban_delete_global, PermissionGroup::client_admin, "b_client_ban_delete_global", "Delete global bans"), - make_shared(PermissionType::i_client_ban_max_bantime, PermissionGroup::client_admin, "i_client_ban_max_bantime", "Max bantime"), - make_shared(PermissionType::b_client_even_textmessage_send, PermissionGroup::client_basic, "b_client_even_textmessage_send", "Allows the client to send text messages to himself"), - make_shared(PermissionType::i_client_private_textmessage_power, PermissionGroup::client_basic, "i_client_private_textmessage_power", "Client private message power"), - make_shared(PermissionType::i_client_needed_private_textmessage_power, PermissionGroup::client_basic, "i_client_needed_private_textmessage_power", "Needed client private message power"), - make_shared(PermissionType::b_client_server_textmessage_send, PermissionGroup::client_basic, "b_client_server_textmessage_send", "Send text messages to virtual server"), - make_shared(PermissionType::b_client_channel_textmessage_send, PermissionGroup::client_basic, "b_client_channel_textmessage_send", "Send text messages to channel"), - make_shared(PermissionType::b_client_offline_textmessage_send, PermissionGroup::client_basic, "b_client_offline_textmessage_send", "Send offline messages to clients"), - make_shared(PermissionType::i_client_talk_power, PermissionGroup::client_basic, "i_client_talk_power", "Client talk power"), - make_shared(PermissionType::i_client_needed_talk_power, PermissionGroup::client_basic, "i_client_needed_talk_power", "Needed client talk power"), - make_shared(PermissionType::i_client_poke_power, PermissionGroup::client_basic, "i_client_poke_power", "Client poke power"), - make_shared(PermissionType::i_client_needed_poke_power, PermissionGroup::client_basic, "i_client_needed_poke_power", "Needed client poke power"), - make_shared(PermissionType::b_client_set_flag_talker, PermissionGroup::client_basic, "b_client_set_flag_talker", "Set the talker flag for clients and allow them to speak"), - make_shared(PermissionType::i_client_whisper_power, PermissionGroup::client_basic, "i_client_whisper_power", "Client whisper power"), - make_shared(PermissionType::i_client_needed_whisper_power, PermissionGroup::client_basic, "i_client_needed_whisper_power", "Client needed whisper power"), - make_shared(PermissionType::b_client_modify_description, PermissionGroup::client_modify, "b_client_modify_description", "Edit a clients description"), - make_shared(PermissionType::b_client_modify_own_description, PermissionGroup::client_modify, "b_client_modify_own_description", "Allow client to edit own description"), - make_shared(PermissionType::b_client_modify_dbproperties, PermissionGroup::client_modify, "b_client_modify_dbproperties", "Edit a clients properties in the sql"), - make_shared(PermissionType::b_client_delete_dbproperties, PermissionGroup::client_modify, "b_client_delete_dbproperties", "Delete a clients properties in the sql"), - make_shared(PermissionType::b_client_create_modify_serverquery_login, PermissionGroup::client_modify, "b_client_create_modify_serverquery_login", "Create or modify own ServerQuery account"), - - make_shared(PermissionType::b_client_query_create, PermissionGroup::client_modify, "b_client_query_create", "Create your own ServerQuery account"), - make_shared(PermissionType::b_client_query_list, PermissionGroup::client_modify, "b_client_query_list", "List all ServerQuery accounts"), - make_shared(PermissionType::b_client_query_list_own, PermissionGroup::client_modify, "b_client_query_list_own", "List all own ServerQuery accounts"), - make_shared(PermissionType::b_client_query_rename, PermissionGroup::client_modify, "b_client_query_rename", "Rename a ServerQuery account"), - make_shared(PermissionType::b_client_query_rename_own, PermissionGroup::client_modify, "b_client_query_rename_own", "Rename the own ServerQuery account"), - make_shared(PermissionType::b_client_query_change_password, PermissionGroup::client_modify, "b_client_query_change_password", "Change a server query accounts password"), - make_shared(PermissionType::b_client_query_change_own_password, PermissionGroup::client_modify, "b_client_query_change_own_password", "Change a query accounts own password"), - make_shared(PermissionType::b_client_query_change_password_global, PermissionGroup::client_modify, "b_client_query_change_password_global", "Change a global query accounts own password"), - make_shared(PermissionType::b_client_query_delete, PermissionGroup::client_modify, "b_client_query_delete", "Delete a query accounts password"), - make_shared(PermissionType::b_client_query_delete_own, PermissionGroup::client_modify, "b_client_query_delete_own", "Delete own query accounts password"), - -#ifndef LAGENCY - make_shared(PermissionType::b_client_music_create_temporary, PermissionGroup::client, "b_client_music_create_temporary", "Permission to create a music bot"), - make_shared(PermissionType::b_client_music_create_semi_permanent, PermissionGroup::client, "b_client_music_create_semi_permanent", "Allows the client to create semi permanent music bots"), - make_shared(PermissionType::b_client_music_create_permanent, PermissionGroup::client, "b_client_music_create_permanent", "Allows the client to create permanent music bots"), - make_shared(PermissionType::b_client_music_modify_temporary, PermissionGroup::client, "b_client_music_modify_temporary", "Permission to make a music bot temporary"), - make_shared(PermissionType::b_client_music_modify_semi_permanent, PermissionGroup::client, "b_client_music_modify_semi_permanent", "Allows the client to make a bot semi permanent"), - make_shared(PermissionType::b_client_music_modify_permanent, PermissionGroup::client, "b_client_music_modify_permanent", "Allows the client to make a bot permanent"), - make_shared(PermissionType::i_client_music_create_modify_max_volume, PermissionGroup::client, "i_client_music_create_modify_max_volume", "Sets the max allowed music bot volume"), - make_shared(PermissionType::i_client_music_limit, PermissionGroup::client, "i_client_music_limit", "The limit of music bots bound to this client"), - make_shared(PermissionType::i_client_music_delete_power, PermissionGroup::client, "i_client_music_delete_power", "Power to delete the music bot"), - make_shared(PermissionType::i_client_music_needed_delete_power, PermissionGroup::client, "i_client_music_needed_delete_power", "Required power to delete the music bot"), - make_shared(PermissionType::i_client_music_play_power, PermissionGroup::client, "i_client_music_play_power", "Power to play music"), - make_shared(PermissionType::i_client_music_needed_modify_power, PermissionGroup::client, "i_client_music_needed_modify_power", "Required power to modify the bot settings"), - make_shared(PermissionType::i_client_music_modify_power, PermissionGroup::client, "i_client_music_modify_power", "Power to modify the music bot settings"), - make_shared(PermissionType::i_client_music_needed_play_power, PermissionGroup::client, "i_client_music_needed_play_power", "Required power to play music"), - make_shared(PermissionType::i_client_music_rename_power, PermissionGroup::client, "i_client_music_rename_power", "Power to rename the bot"), - make_shared(PermissionType::i_client_music_needed_rename_power, PermissionGroup::client, "i_client_music_needed_rename_power", "The required rename power for a music bot"), - - make_shared(PermissionType::b_playlist_create, PermissionGroup::client, "b_playlist_create", "Allows the client to create playlists"), - make_shared(PermissionType::i_playlist_view_power, PermissionGroup::client, "i_playlist_view_power", "Power to see a playlist, and their songs"), - make_shared(PermissionType::i_playlist_needed_view_power, PermissionGroup::client, "i_playlist_needed_view_power", "Needed power to see a playlist, and their songs"), - make_shared(PermissionType::i_playlist_modify_power, PermissionGroup::client, "i_playlist_modify_power", "Power to modify the playlist properties"), - make_shared(PermissionType::i_playlist_needed_modify_power, PermissionGroup::client, "i_playlist_needed_modify_power", "Needed power to modify the playlist properties"), - make_shared(PermissionType::i_playlist_permission_modify_power, PermissionGroup::client, "i_playlist_permission_modify_power", "Power to modify the playlist permissions"), - make_shared(PermissionType::i_playlist_needed_permission_modify_power, PermissionGroup::client, "i_playlist_needed_permission_modify_power", "Needed power to modify the playlist permissions"), - make_shared(PermissionType::i_playlist_delete_power, PermissionGroup::client, "i_playlist_delete_power", "Power to delete the playlist"), - make_shared(PermissionType::i_playlist_needed_delete_power, PermissionGroup::client, "i_playlist_needed_delete_power", "Needed power to delete the playlist"), - - make_shared(PermissionType::i_playlist_song_add_power, PermissionGroup::client, "i_playlist_song_add_power", "Power to add songs to a playlist"), - make_shared(PermissionType::i_playlist_song_needed_add_power, PermissionGroup::client, "i_playlist_song_needed_add_power", "Needed power to add songs to a playlist"), - make_shared(PermissionType::i_playlist_song_remove_power, PermissionGroup::client, "i_playlist_song_remove_power", "Power to remove songs from a playlist"), - make_shared(PermissionType::i_playlist_song_needed_remove_power, PermissionGroup::client, "i_playlist_song_needed_remove_power", "Needed power to remove songs from a playlist"), - make_shared(PermissionType::i_playlist_song_move_power, PermissionGroup::client, "i_playlist_song_move_power", "Power to move songs witin a playlist"), - make_shared(PermissionType::i_playlist_song_needed_move_power, PermissionGroup::client, "i_playlist_song_needed_move_power", "Needed power to move songs within a playlist"), - - make_shared(PermissionType::b_client_use_bbcode_any, PermissionGroup::client, "b_client_use_bbcode_any", "Allows the client to use any bbcodes"), - make_shared(PermissionType::b_client_use_bbcode_image, PermissionGroup::client, "b_client_use_bbcode_image", "Allows the client to use img bbcode"), - make_shared(PermissionType::b_client_use_bbcode_url, PermissionGroup::client, "b_client_use_bbcode_url", "Allows the client to use url bbcode"), -#endif - make_shared(PermissionType::b_ft_ignore_password, PermissionGroup::ft, "b_ft_ignore_password", "Browse files without channel password"), - make_shared(PermissionType::b_ft_transfer_list, PermissionGroup::ft, "b_ft_transfer_list", "Retrieve list of running filetransfers"), - make_shared(PermissionType::i_ft_file_upload_power, PermissionGroup::ft, "i_ft_file_upload_power", "File upload power"), - make_shared(PermissionType::i_ft_needed_file_upload_power, PermissionGroup::ft, "i_ft_needed_file_upload_power", "Needed file upload power"), - make_shared(PermissionType::i_ft_file_download_power, PermissionGroup::ft, "i_ft_file_download_power", "File download power"), - make_shared(PermissionType::i_ft_needed_file_download_power, PermissionGroup::ft, "i_ft_needed_file_download_power", "Needed file download power"), - make_shared(PermissionType::i_ft_file_delete_power, PermissionGroup::ft, "i_ft_file_delete_power", "File delete power"), - make_shared(PermissionType::i_ft_needed_file_delete_power, PermissionGroup::ft, "i_ft_needed_file_delete_power", "Needed file delete power"), - make_shared(PermissionType::i_ft_file_rename_power, PermissionGroup::ft, "i_ft_file_rename_power", "File rename power"), - make_shared(PermissionType::i_ft_needed_file_rename_power, PermissionGroup::ft, "i_ft_needed_file_rename_power", "Needed file rename power"), - make_shared(PermissionType::i_ft_file_browse_power, PermissionGroup::ft, "i_ft_file_browse_power", "File browse power"), - make_shared(PermissionType::i_ft_needed_file_browse_power, PermissionGroup::ft, "i_ft_needed_file_browse_power", "Needed file browse power"), - make_shared(PermissionType::i_ft_directory_create_power, PermissionGroup::ft, "i_ft_directory_create_power", "Create directory power"), - make_shared(PermissionType::i_ft_needed_directory_create_power, PermissionGroup::ft, "i_ft_needed_directory_create_power", "Needed create directory power"), - make_shared(PermissionType::i_ft_quota_mb_download_per_client, PermissionGroup::ft, "i_ft_quota_mb_download_per_client", "Download quota per client in MByte"), - make_shared(PermissionType::i_ft_quota_mb_upload_per_client, PermissionGroup::ft, "i_ft_quota_mb_upload_per_client", "Upload quota per client in MByte") -}; - -deque ts::permission::neededPermissions = { - b_client_force_push_to_talk, - b_channel_join_ignore_password, - b_ft_ignore_password, - i_client_max_avatar_filesize, - i_client_max_channel_subscriptions, - i_permission_modify_power, - b_virtualserver_servergroup_permission_list, - b_virtualserver_client_permission_list, - b_virtualserver_channelgroup_permission_list, - b_virtualserver_channelclient_permission_list, - b_virtualserver_playlist_permission_list, - b_virtualserver_channelgroup_client_list, - b_client_permissionoverview_view, - b_client_ban_list, - b_client_ban_trigger_list, - b_client_complain_list, - b_client_complain_delete, - b_client_complain_delete_own, - b_virtualserver_log_view, - b_client_create_modify_serverquery_login, - b_virtualserver_connectioninfo_view, - b_client_modify_description, - b_client_server_textmessage_send, - b_client_channel_textmessage_send, -#ifdef LAGENCY -i_group_modify_power, - i_group_member_add_power, - i_group_member_remove_power, -#else - i_displayed_group_modify_power, - i_displayed_group_member_add_power, - i_displayed_group_member_remove_power, - - i_server_group_modify_power, - i_server_group_member_add_power, - i_server_group_self_add_power, - i_server_group_member_remove_power, - i_server_group_self_remove_power, - - i_channel_group_modify_power, - i_channel_group_member_add_power, - i_channel_group_self_add_power, - i_channel_group_member_remove_power, - i_channel_group_self_remove_power, -#endif - i_ft_file_browse_power, - b_permission_modify_power_ignore, - b_virtualserver_modify_hostmessage, - b_virtualserver_modify_ft_settings, - b_virtualserver_modify_default_musicgroup, - b_virtualserver_modify_default_servergroup, - b_virtualserver_modify_default_channelgroup, - b_virtualserver_modify_default_channeladmingroup, - b_virtualserver_modify_hostbanner, - b_virtualserver_modify_channel_forced_silence, - b_virtualserver_modify_needed_identity_security_level, - b_virtualserver_modify_name, - b_virtualserver_modify_welcomemessage, - b_virtualserver_modify_maxclients, - b_virtualserver_modify_password, - b_virtualserver_modify_complain, - b_virtualserver_modify_antiflood, - b_virtualserver_modify_ft_quotas, - b_virtualserver_modify_hostbutton, - b_virtualserver_modify_autostart, - b_virtualserver_modify_port, - b_virtualserver_modify_host, - b_virtualserver_modify_log_settings, - b_virtualserver_modify_priority_speaker_dimm_modificator, - b_virtualserver_modify_music_bot_limit, - b_virtualserver_modify_default_messages, - i_channel_create_modify_conversation_history_length, - b_channel_create_modify_conversation_history_unlimited, - b_channel_create_modify_conversation_private, - b_channel_modify_name, - b_channel_modify_password, - b_channel_modify_topic, - b_channel_modify_description, - b_channel_modify_codec, - b_channel_modify_codec_quality, - b_channel_modify_make_permanent, - b_channel_modify_make_semi_permanent, - b_channel_modify_make_temporary, - b_channel_modify_make_default, - b_channel_modify_maxclients, - b_channel_modify_maxfamilyclients, - b_channel_modify_sortorder, - b_channel_modify_needed_talk_power, - i_channel_permission_modify_power, - b_channel_create_child, - b_channel_create_permanent, - b_channel_create_temporary, - b_channel_create_with_topic, - b_channel_create_with_description, - b_channel_create_with_password, - b_channel_create_with_maxclients, - b_channel_create_with_maxfamilyclients, - b_channel_create_with_sortorder, - b_channel_create_with_default, - b_channel_create_modify_with_codec_speex8, - b_channel_create_modify_with_codec_speex16, - b_channel_create_modify_with_codec_speex32, - b_channel_create_modify_with_codec_celtmono48, - b_channel_create_modify_with_codec_opusvoice, - b_channel_create_modify_with_codec_opusmusic, - i_channel_create_modify_with_codec_maxquality, - i_client_serverquery_view_power, - b_channel_create_semi_permanent, - b_serverinstance_modify_querygroup, - b_serverinstance_modify_templates, - b_virtualserver_channel_permission_list, - b_channel_delete_permanent, - b_channel_delete_semi_permanent, - b_channel_delete_temporary, - b_channel_delete_flag_force, - b_client_set_flag_talker, - b_channel_create_with_needed_talk_power, - b_virtualserver_token_list, - b_virtualserver_token_add, - b_virtualserver_token_use, - b_virtualserver_token_delete, - - /* ban functions */ - b_client_ban_create, - b_client_ban_create_global, - - b_client_ban_list_global, - b_client_ban_list, - - b_client_ban_edit, - b_client_ban_edit_global, - - b_client_ban_name, - b_client_ban_ip, - b_client_ban_hwid, - - b_virtualserver_servergroup_list, - b_virtualserver_servergroup_client_list, - b_virtualserver_channelgroup_list, - i_client_ban_max_bantime, - b_icon_manage, - i_max_icon_filesize, - i_max_playlist_size, - i_max_playlists, - - i_client_needed_whisper_power, - i_client_whisper_power, - - b_virtualserver_modify_icon_id, - b_client_modify_own_description, - b_client_offline_textmessage_send, - b_virtualserver_client_dblist, - b_virtualserver_modify_reserved_slots, - b_channel_conversation_message_delete, - i_channel_create_modify_with_codec_latency_factor_min, - b_channel_modify_codec_latency_factor, - b_channel_modify_make_codec_encrypted, - b_virtualserver_modify_codec_encryption_mode, - b_client_use_channel_commander, - b_virtualserver_servergroup_create, - b_virtualserver_channelgroup_create, - b_client_permissionoverview_own, - i_ft_quota_mb_upload_per_client, - i_ft_quota_mb_download_per_client, - b_virtualserver_modify_weblist, - b_virtualserver_modify_country_code, - b_virtualserver_channelgroup_delete, - b_virtualserver_servergroup_delete, - b_client_ban_delete, - b_client_ban_delete_own, - b_client_ban_delete_global, - b_client_ban_delete_own_global, - b_virtualserver_modify_temporary_passwords, - b_virtualserver_modify_temporary_passwords_own, - b_client_request_talker, - b_client_avatar_delete_other, - b_channel_create_modify_with_force_password, - b_channel_create_private, - b_channel_join_ignore_maxclients, - b_virtualserver_modify_channel_temp_delete_delay_default, - i_channel_create_modify_with_temp_delete_delay, - b_channel_modify_temp_delete_delay, - i_client_poke_power, - - b_client_remoteaddress_view, - - i_client_music_play_power, - i_client_music_modify_power, - i_client_music_rename_power, - b_client_music_create_temporary, - b_client_music_create_semi_permanent, - b_client_music_create_permanent, - b_client_music_modify_temporary, - b_client_music_modify_semi_permanent, - b_client_music_modify_permanent, - i_client_music_create_modify_max_volume, - - b_playlist_create, - i_playlist_view_power, - i_playlist_modify_power, - i_playlist_permission_modify_power, - i_playlist_delete_power, - - i_playlist_song_add_power, - i_playlist_song_remove_power, - i_playlist_song_move_power, - - b_client_query_create, - b_client_query_list, - b_client_query_list_own, - b_client_query_rename, - b_client_query_rename_own, - b_client_query_change_password, - b_client_query_change_own_password, - b_client_query_change_password_global, - b_client_query_delete, - b_client_query_delete_own, -}; - -std::deque permission::availableGroups = { - global, - global_info, - global_vsmanage, - global_admin, - global_settings, - vs, - vs_info, - vs_admin, - vs_settings, - channel, - channel_info, - channel_create, - channel_modify, - channel_delete, - channel_access, - group, - group_info, - group_create, - group_modify, - group_delete, - client, - client_info, - client_admin, - client_basic, - client_modify, - ft -}; - -std::shared_ptr PermissionTypeEntry::unknown = make_shared(PermissionType::unknown, PermissionGroup::global, "unknown", "unknown"); - -vector> permission_id_map; -void permission::setup_permission_resolve() { - permission_id_map.resize(permission::permission_id_max); - - for(auto& permission : availablePermissions) { - if(!permission->clientSupported || permission->type < 0 || permission->type > permission::permission_id_max) - continue; - permission_id_map[permission->type] = permission; - } - - /* fix "holes" as well set the permission id 0 (unknown) */ - for(auto& permission : permission_id_map) { - if(permission) - continue; - permission = PermissionTypeEntry::unknown; - } -} - -std::shared_ptr permission::resolvePermissionData(PermissionType type) { - if((type & PERM_ID_GRANT) > 0) - type &= ~PERM_ID_GRANT; - - assert(!permission_id_map.empty()); - if(type < 0 || type >= permission::permission_id_max) - return PermissionTypeEntry::unknown; - - return permission_id_map[type]; -} - -std::shared_ptr permission::resolvePermissionData(const std::string& name) { - for(auto& elm : availablePermissions) - if(elm->name == name || elm->grant_name == name) - return elm; - return PermissionTypeEntry::unknown; -} - -ChannelId Permission::channelId() { - return this->channel ? this->channel->channelId() : 0; -} - -PermissionManager::PermissionManager() { - memtrack::allocated(this); -} -PermissionManager::~PermissionManager() { - memtrack::freed(this); -} - -std::shared_ptr PermissionManager::registerPermission(ts::permission::PermissionType type, - ts::permission::PermissionValue val, - const std::shared_ptr &channel, - uint16_t flag) { - return this->registerPermission(resolvePermissionData(type), val, channel, flag); -} - -std::shared_ptr PermissionManager::registerPermission(const std::shared_ptr& type, PermissionValue value, const std::shared_ptr& channel, uint16_t flagMask) { - { - auto found = getPermission(type->type, channel, false); - if(!found.empty()) return found.front(); - } - auto permission = std::make_shared(type, value, permNotGranted, flagMask, channel); - this->permissions.push_back(permission); - for(auto& elm : this->updateHandler) - elm(permission); - return permission; -} - -bool PermissionManager::hasPermission(PermissionType type, const std::shared_ptr& channel, bool testGlobal) { - return !this->getPermission(type, channel, testGlobal).empty(); -} - -bool PermissionManager::setPermission(PermissionType type, PermissionValue value, const std::shared_ptr& channel) { - auto list = this->getPermission(type, channel, false); - if(list.empty()) - list.push_back(registerPermission(type, value, channel, PERM_FLAG_PUBLIC)); - - list.front()->value = value; - for(const auto& elm : this->updateHandler) - elm(list.front()); - - return true; -} - -bool PermissionManager::setPermission(ts::permission::PermissionType type, ts::permission::PermissionValue value, const std::shared_ptr &channel, bool negated, bool skiped) { - auto list = this->getPermission(type, channel, false); - if(list.empty()) - list.push_back(registerPermission(type, value, channel, PERM_FLAG_PUBLIC)); - - list.front()->value = value; - list.front()->flag_negate = negated; - list.front()->flag_skip = skiped; - - for(const auto& elm : this->updateHandler) - elm(list.front()); - - return true; -} - -bool PermissionManager::setPermissionGranted(PermissionType type, PermissionValue value, const std::shared_ptr& channel) { - auto list = this->getPermission(type, channel, false); - if(list.empty()) - list.push_back(registerPermission(type, permNotGranted, channel, PERM_FLAG_PUBLIC)); - list.front()->granted = value; - - for(auto& elm : this->updateHandler) - elm(list.front()); - - return true; -} - -void PermissionManager::deletePermission(PermissionType type, const std::shared_ptr& channel) { - auto list = getPermission(type, channel, false); - if(list.empty()) return; - list.front()->value = permNotGranted; - //list.front()->deleted = true; - //this->permissions.erase(std::find(this->permissions.begin(), this->permissions.end(), list.front())); - - for(auto& e : this->updateHandler) - e(list.front()); -} - -std::deque> PermissionManager::getPermission(PermissionType type, const std::shared_ptr& channel, bool testGlobal) { - std::deque> res; - - std::shared_ptr channel_permission; - std::shared_ptr global_permission; - for(const auto &perm : this->permissions) - if(perm->type->type == type) { - if(perm->channel == channel) - channel_permission = perm; - else if(!perm->channel && testGlobal) - global_permission = perm; - } - if(channel_permission) res.push_back(channel_permission); - if(global_permission) res.push_back(global_permission); - return res; -} - -std::vector> PermissionManager::listPermissions(uint16_t flags) { - vector> result; - for(const auto &perm : this->permissions) - if((perm->flagMask & flags) > 0 || true) //FIXME? - result.push_back(perm); - return result; -} - -std::vector> PermissionManager::allPermissions() { - return std::vector>(this->permissions.begin(), this->permissions.end()); -} - -std::deque> PermissionManager::all_channel_specific_permissions() { - std::deque> result; - - for(const auto& permission : this->permissions) - if(permission->channel) - result.push_back(permission); - - return result; -} - -std::deque> PermissionManager::all_channel_unspecific_permissions() { - std::deque> result; - - for(const auto& permission : this->permissions) - if(!permission->channel) - result.push_back(permission); - - return result; -} - -teamspeak::MapType teamspeak::unmapping; -teamspeak::MapType teamspeak::mapping; - -teamspeak::MapType build_mapping(){ - return { - {teamspeak::GENERAL, { - {"b_virtualserver_modify_port", {"b_virtualserver_modify_port", "b_virtualserver_modify_host"}}, - {"i_client_max_clones_uid", {"i_client_max_clones_uid", "i_client_max_clones_ip", "i_client_max_clones_hwid"}}, - {"b_client_ignore_bans", {"b_client_ignore_bans", "b_client_ignore_vpn"}}, - {"b_client_ban_list", {"b_client_ban_list", "b_client_ban_list_global"}}, - - //Permissions which TeaSpeak has but TeamSpeak not - {"", {"b_virtualserver_modify_music_bot_limit"}}, - {"", {"b_client_music_channel_list"}}, - {"", {"b_client_music_server_list"}}, - {"", {"i_client_music_info"}}, - {"", {"i_client_music_needed_info"}}, - - {"", {"b_client_ban_edit"}}, - {"", {"b_client_ban_edit_global"}}, - {"", {"b_client_ban_create_global"}}, - {"", {"b_client_ban_delete_own_global"}}, - {"", {"b_client_ban_delete_global"}}, - - {"", {"b_client_even_textmessage_send"}}, - {"", {"b_client_enforce_valid_hwid"}}, - {"", {"b_client_allow_invalid_packet"}}, - {"", {"b_client_allow_invalid_badges"}}, - - {"", {"b_client_music_create"}}, - {"", {"b_client_music_delete_own"}}, - - {"", {"i_client_music_limit"}}, - {"", {"i_client_music_needed_delete_power"}}, - {"", {"i_client_music_delete_power"}}, - {"", {"i_client_music_play_power"}}, - {"", {"i_client_music_needed_play_power"}}, - {"", {"i_client_music_rename_power"}}, - {"", {"i_client_music_needed_rename_power"}}, - - {"", {"b_client_use_bbcode_any"}}, - {"", {"b_client_use_bbcode_url"}}, - {"", {"b_client_use_bbcode_image"}}, - - {"", {"b_channel_ignore_view_power"}}, - {"", {"i_channel_view_power"}}, - {"", {"i_channel_needed_view_power"}}, - - {"", {"i_client_max_channels"}}, - {"", {"i_client_max_temporary_channels"}}, - {"", {"i_client_max_semi_channels"}}, - {"", {"i_client_max_permanent_channels"}}, - - {"", {"b_virtualserver_modify_default_messages"}}, - - {"b_client_ban_list", {"b_client_ban_list", "b_client_ban_trigger_list"}}, - {"b_virtualserver_select", {"b_virtualserver_select", "b_virtualserver_select_godmode"}}, - {"b_virtualserver_modify_default_servergroup", {"b_virtualserver_modify_default_servergroup", "b_virtualserver_modify_default_musicgroup"}}, - - {"b_client_ban_create", {"b_client_ban_create", "b_client_ban_name", "b_client_ban_ip", "b_client_ban_hwid"}} - }, - }, - {teamspeak::SERVER, { - {"i_group_modify_power", {"i_server_group_modify_power", "i_channel_group_modify_power", "i_displayed_group_modify_power"}}, - {"i_group_member_add_power", {"i_server_group_member_add_power", "i_channel_group_member_add_power", "i_displayed_group_member_add_power"}}, - {"i_group_member_remove_power", {"i_server_group_member_remove_power", "i_channel_group_member_remove_power", "i_displayed_group_member_remove_power"}}, - {"i_group_needed_modify_power", {"i_server_group_needed_modify_power", "i_channel_group_needed_modify_power", "i_displayed_group_needed_modify_power"}}, - {"i_group_needed_member_add_power", {"i_server_group_needed_member_add_power", "i_channel_group_needed_member_add_power", "i_displayed_group_needed_member_add_power"}}, - {"i_group_needed_member_remove_power", {"i_server_group_needed_member_remove_power", "i_channel_group_needed_member_remove_power", "i_displayed_group_needed_member_remove_power"}}, - }, - }, - {teamspeak::CLIENT, { - {"i_group_modify_power", {"i_server_group_modify_power", "i_channel_group_modify_power", "i_displayed_group_modify_power"}}, - {"i_group_member_add_power", {"i_server_group_member_add_power", "i_channel_group_member_add_power", "i_displayed_group_member_add_power"}}, - {"i_group_member_remove_power", {"i_server_group_member_remove_power", "i_channel_group_member_remove_power", "i_displayed_group_member_remove_power"}}, - {"i_group_needed_modify_power", {"i_server_group_needed_modify_power", "i_channel_group_needed_modify_power", "i_displayed_group_needed_modify_power"}}, - {"i_group_needed_member_add_power", {"i_server_group_needed_member_add_power", "i_channel_group_needed_member_add_power", "i_displayed_group_needed_member_add_power"}}, - {"i_group_needed_member_remove_power", {"i_server_group_needed_member_remove_power", "i_channel_group_needed_member_remove_power", "i_displayed_group_needed_member_remove_power"}}, - }, - }, - {teamspeak::CHANNEL, { - {"i_group_modify_power", {"i_channel_group_modify_power", "i_displayed_group_modify_power"}}, - {"i_group_member_add_power", {"i_channel_group_member_add_power", "i_displayed_group_member_add_power"}}, - {"i_group_member_remove_power", {"i_channel_group_member_remove_power", "i_displayed_group_member_remove_power"}}, - {"i_group_needed_modify_power", {"i_channel_group_needed_modify_power", "i_displayed_group_needed_modify_power"}}, - {"i_group_needed_member_add_power", {"i_channel_group_needed_member_add_power", "i_displayed_group_needed_member_add_power"}}, - {"i_group_needed_member_remove_power", {"i_channel_group_needed_member_remove_power", "i_displayed_group_needed_member_remove_power"}}, - }, - }, - }; -}; - -inline teamspeak::MapType build_unmapping() { - teamspeak::MapType result; - - for(const auto& map : teamspeak::mapping) { - for(const auto& entry : map.second) { - for(const auto& key : entry.second) { - auto& m = result[map.first]; - auto it = m.find(key); - if(it == m.end()) - m.insert({key, {}}); - it = m.find(key); - if(!entry.first.empty()) it->second.push_back(entry.first); - } - } - } - - return result; -}; - -inline void init_mapping() { - if(teamspeak::mapping.empty()) teamspeak::mapping = build_mapping(); - if(teamspeak::unmapping.empty()) teamspeak::unmapping = build_unmapping(); -} - -template -inline deque operator+(const deque& a, const deque& b) { - deque result; - result.insert(result.end(), a.begin(), a.end()); - result.insert(result.end(), b.begin(), b.end()); - return result; -} - -inline deque mmget(teamspeak::MapType& map, teamspeak::GroupType type, const std::string& key) { - return map[type].count(key) > 0 ? map[type].find(key)->second : deque{}; -} - -inline std::deque map_entry(std::string key, teamspeak::GroupType type, teamspeak::MapType& map_table) { - init_mapping(); - - if(key.find("_needed_modify_power_") == 1) { - key = key.substr(strlen("x_needed_modify_power_")); - - deque result; - if(map_table[type].count("i_" + key) > 0 || map_table[teamspeak::GroupType::GENERAL].count("i_" + key) > 0) result = mmget(map_table, type, "i_" + key) + mmget(map_table, teamspeak::GroupType::GENERAL, "i_" + key); - else if(map_table[type].count("b_" + key) > 0 || map_table[teamspeak::GroupType::GENERAL].count("b_" + key) > 0) result = mmget(map_table, type, "b_" + key) + mmget(map_table, teamspeak::GroupType::GENERAL, "b_" + key); - else result = {"x_" + key}; - - for(auto& entry : result) - entry = "i_needed_modify_power_" + entry.substr(2); - return result; - } - if(map_table[type].count(key) > 0 || map_table[teamspeak::GroupType::GENERAL].count(key) > 0) return mmget(map_table, type, key) + mmget(map_table, teamspeak::GroupType::GENERAL, key); - return {key}; -} - -std::deque teamspeak::map_key(std::string key, GroupType type) { - return map_entry(key, type, teamspeak::mapping); -} - -std::deque teamspeak::unmap_key(std::string key, GroupType type) { - return map_entry(key, type, teamspeak::unmapping); -} - - -#define AQB(name) \ -{update::QUERY_ADMIN, {name, 1, 100, false, false}}, \ -{update::SERVER_ADMIN, {name, 1, 75, false, false}}, \ - -#define AQBG(name) \ -{update::QUERY_ADMIN, {name, permNotGranted, 100, false, false}}, \ -{update::SERVER_ADMIN, {name, permNotGranted, 75, false, false}}, \ - -#define AQI(name) \ -{update::QUERY_ADMIN, {name, 100, 100, false, false}}, \ -{update::SERVER_ADMIN, {name, 75, 75, false, false}}, \ - -#define AQIG(name) \ -{update::QUERY_ADMIN, {name, permNotGranted, 100, false, false}}, \ -{update::SERVER_ADMIN, {name, permNotGranted, 75, false, false}}, \ - -deque update::migrate = { - AQB("b_virtualserver_modify_music_bot_limit") - {update::QUERY_ADMIN, {"b_client_music_channel_list", 1, 100, false, false}}, - {update::SERVER_ADMIN, {"b_client_music_channel_list", 1, 75, false, false}}, - {update::CHANNEL_ADMIN, {"b_client_music_channel_list", 1, 75, false, false}}, - - {update::QUERY_ADMIN, {"b_client_music_server_list", 1, 100, false, false}}, - {update::SERVER_ADMIN, {"b_client_music_server_list", 1, 75, false, false}}, - - {update::QUERY_ADMIN, {"i_client_music_info", 100, 100, false, false}}, - {update::SERVER_ADMIN, {"i_client_music_info", 75, 75, false, false}}, - - {update::QUERY_ADMIN, {"i_client_music_needed_info", permNotGranted, 100, false, false}}, - {update::SERVER_ADMIN, {"i_client_music_needed_info", permNotGranted, 75, false, false}}, - - {update::QUERY_ADMIN, {"b_client_ban_list_global", 1, 100, false, false}}, - {update::SERVER_ADMIN, {"b_client_ban_list_global", 1, 75, false, false}}, - - {update::QUERY_ADMIN, {"b_client_ban_edit", 1, 100, false, false}}, - {update::SERVER_ADMIN, {"b_client_ban_edit", 1, 75, false, false}}, - - {update::QUERY_ADMIN, {"b_client_ban_create_global", 1, 100, false, false}}, - {update::QUERY_ADMIN, {"b_client_ban_edit_global", 1, 100, false, false}}, - {update::QUERY_ADMIN, {"b_client_ban_delete_own_global", 1, 100, false, false}}, - {update::QUERY_ADMIN, {"b_client_ban_delete_global", 1, 100, false, false}}, - - AQB("b_client_even_textmessage_send") - AQBG("b_client_enforce_valid_hwid") - AQB("b_client_allow_invalid_packet") - AQB("b_client_allow_invalid_badges") - - AQB("b_client_music_create") - AQB("b_client_music_delete_own") - - AQI("i_client_music_limit") - AQIG("i_client_music_needed_delete_power") - AQI("i_client_music_delete_power") - AQI("i_client_music_play_power") - AQIG("i_client_music_needed_play_power") - AQI("i_client_music_rename_power") - AQIG("i_client_music_needed_rename_power") - - - AQB("b_client_use_bbcode_any") - AQB("b_client_use_bbcode_url") - AQB("b_client_use_bbcode_image") - - {update::QUERY_ADMIN, {"b_channel_ignore_view_power", 1, 100, false, false}}, - AQI("i_channel_view_power") - AQIG("i_channel_needed_view_power") - - AQB("b_client_ignore_vpn") - - AQIG("i_client_max_channels") - AQIG("i_client_max_temporary_channels") - AQIG("i_client_max_semi_channels") - AQIG("i_client_max_permanent_channels") - {update::SERVER_NORMAL, {"i_client_max_channels", 2, permNotGranted, false, false}}, - {update::SERVER_GUEST, {"i_client_max_channels", 1, permNotGranted, false, false}}, - - AQB("b_virtualserver_modify_default_messages") - AQB("b_virtualserver_modify_default_musicgroup") - AQB("b_channel_ignore_join_power") - AQB("b_virtualserver_select_godmode") - AQB("b_client_ban_trigger_list") -}; - -v2::PermissionManager::PermissionManager() { - memset(this->block_use_count, 0, sizeof(this->block_use_count)); - memset(this->block_containers, 0, sizeof(this->block_containers)); -} - -v2::PermissionManager::~PermissionManager() { - for(auto& block : this->block_containers) - delete block; -} - -void v2::PermissionManager::load_permission(const ts::permission::PermissionType &permission, const ts::permission::v2::PermissionValues &values, bool flag_skip, bool flag_negate, bool flag_value, bool flag_grant) { - if(permission < 0 || permission >= PermissionType::permission_id_max) - return; - - const auto block = this->calculate_block(permission); - this->ref_allocate_block(block); - - auto& data = this->block_containers[block]->permissions[this->calculate_block_index(permission)]; - data.values = values; - data.flags.database_reference = true; - data.flags.skip = flag_skip; - data.flags.negate = flag_negate; - data.flags.value_set = flag_value; - data.flags.grant_set = flag_grant; - this->unref_block(block); -} - -void v2::PermissionManager::load_permission(const ts::permission::PermissionType &permission, const ts::permission::v2::PermissionValues &values, ChannelId channel_id, bool flag_skip, bool flag_negate, bool flag_value, bool flag_grant) { - if(permission < 0 || permission >= PermissionType::permission_id_max) - return; - - unique_lock channel_perm_lock(this->channel_list_lock); - ChannelPermissionContainer* permission_container = nullptr; - for(auto& entry : this->_channel_permissions) - if(entry->permission == permission && entry->channel_id == channel_id) { - permission_container = &*entry; - break; - } - - if(!permission_container) { - auto container = make_unique(); - container->permission = permission; - container->channel_id = channel_id; - permission_container = &*container; - this->_channel_permissions.push_back(std::move(container)); - - /* now set the channel flag for that permission */ - const auto block = this->calculate_block(permission); - this->ref_allocate_block(block); - - auto& data = this->block_containers[block]->permissions[this->calculate_block_index(permission)]; - data.flags.channel_specific = true; - this->unref_block(block); - } - - permission_container->values = values; - permission_container->flags.database_reference = true; - permission_container->flags.skip = flag_skip; - permission_container->flags.negate = flag_negate; - permission_container->flags.value_set = flag_value; - permission_container->flags.grant_set = flag_grant; -} - -const v2::PermissionFlags v2::PermissionManager::permission_flags(const ts::permission::PermissionType &permission) { - if(permission < 0 || permission >= PermissionType::permission_id_max) - return empty_flags; - - const auto block = this->calculate_block(permission); - if(!this->ref_block(block)) - return empty_flags; - - PermissionFlags result{this->block_containers[block]->permissions[this->calculate_block_index(permission)].flags}; - this->unref_block(block); - return result; -} - -const v2::PermissionValues v2::PermissionManager::permission_values(const ts::permission::PermissionType &permission) { - if(permission < 0 || permission >= PermissionType::permission_id_max) - return v2::empty_permission_values; - - const auto block = this->calculate_block(permission); - if(!this->ref_block(block)) - return v2::empty_permission_values; /* TODO: may consider to throw an exception because the existence should be checked by getting the permission flags */ - - v2::PermissionValues data{this->block_containers[block]->permissions[this->calculate_block_index(permission)].values}; - this->unref_block(block); - return data; -} - -const v2::PermissionFlaggedValue v2::PermissionManager::permission_value_flagged(const ts::permission::PermissionType &permission) { - if(permission < 0 || permission >= PermissionType::permission_id_max) - return v2::empty_permission_flagged_value; - - const auto block = this->calculate_block(permission); - if(!this->ref_block(block)) - return v2::empty_permission_flagged_value; - - auto& data = this->block_containers[block]->permissions[this->calculate_block_index(permission)]; - v2::PermissionFlaggedValue result{data.values.value, data.flags.value_set}; - this->unref_block(block); - return result; -} - -const v2::PermissionFlaggedValue v2::PermissionManager::permission_granted_flagged(const ts::permission::PermissionType &permission) { - if(permission < 0 || permission >= PermissionType::permission_id_max) - return v2::empty_permission_flagged_value; - - const auto block = this->calculate_block(permission); - if(!this->ref_block(block)) - return v2::empty_permission_flagged_value; - - auto& data = this->block_containers[block]->permissions[this->calculate_block_index(permission)]; - v2::PermissionFlaggedValue result{data.values.grant, data.flags.grant_set}; - this->unref_block(block); - return result; -} - -const v2::PermissionContainer v2::PermissionManager::channel_permission(const PermissionType &permission, ts::ChannelId channel_id) { - if(permission < 0 || permission >= PermissionType::permission_id_max) - return empty_channel_permission; - - shared_lock channel_perm_lock(this->channel_list_lock); - for(auto& entry : this->_channel_permissions) - if(entry->permission == permission && entry->channel_id == channel_id) - return v2::PermissionContainer{entry->flags, entry->values}; - return empty_channel_permission; -} - -void v2::PermissionManager::set_permission(const PermissionType &permission, const v2::PermissionValues &values, const v2::PermissionUpdateType &action_value, const v2::PermissionUpdateType &action_grant, int flag_skip, int flag_negate) { - if(permission < 0 || permission >= PermissionType::permission_id_max) - return; - - const auto block = this->calculate_block(permission); - this->ref_allocate_block(block); - - auto& data = this->block_containers[block]->permissions[this->calculate_block_index(permission)]; - if(action_value == v2::PermissionUpdateType::set_value) { - data.flags.value_set = true; - data.flags.flag_value_update = true; - data.values.value = values.value; - } else if(action_value == v2::PermissionUpdateType::delete_value) { - data.flags.value_set = false; - data.flags.flag_value_update = true; - data.values.value = permNotGranted; /* required for the database else it does not "deletes" the value */ - } - - if(action_grant == v2::PermissionUpdateType::set_value) { - data.flags.grant_set = true; - data.flags.flag_grant_update = true; - data.values.grant = values.grant; - } else if(action_grant == v2::PermissionUpdateType::delete_value) { - data.flags.grant_set = false; - data.flags.flag_grant_update = true; - data.values.grant = permNotGranted; /* required for the database else it does not "deletes" the value */ - } - - if(flag_skip >= 0) { - data.flags.flag_value_update = true; - data.flags.skip = flag_skip == 1; - } - - if(flag_negate >= 0) { - data.flags.flag_value_update = true; - data.flags.negate = flag_negate == 1; - } - - this->unref_block(block); - this->trigger_db_update(); -} - -void v2::PermissionManager::set_channel_permission(const PermissionType &permission, ChannelId channel_id, const v2::PermissionValues &values, const v2::PermissionUpdateType &action_value, const v2::PermissionUpdateType &action_grant, int flag_skip, int flag_negate) { - if(permission < 0 || permission >= PermissionType::permission_id_max) - return; - - unique_lock channel_perm_lock(this->channel_list_lock); - ChannelPermissionContainer* permission_container = nullptr; - for(auto& entry : this->_channel_permissions) - if(entry->permission == permission && entry->channel_id == channel_id) { - permission_container = &*entry; - break; - } - - /* register a new permission if we have no permission already*/ - if(!permission_container || !permission_container->flags.permission_set()) { /* if the permission isn't set then we have to register it again */ - if(action_value != v2::PermissionUpdateType::set_value && action_grant == v2::PermissionUpdateType::set_value) { - return; /* we were never willing to set this permission */ - } - - if(!permission_container) { - auto container = make_unique(); - container->permission = permission; - container->channel_id = channel_id; - permission_container = &*container; - this->_channel_permissions.push_back(std::move(container)); - } - - /* now set the channel flag for that permission */ - const auto block = this->calculate_block(permission); - this->ref_allocate_block(block); - - auto& data = this->block_containers[block]->permissions[this->calculate_block_index(permission)]; - data.flags.channel_specific = true; - this->unref_block(block); - } - - if(action_value == v2::PermissionUpdateType::set_value) { - permission_container->flags.value_set = true; - permission_container->flags.flag_value_update = true; - permission_container->values.value = values.value; - } else if(action_value == v2::PermissionUpdateType::delete_value) { - permission_container->flags.value_set = false; - permission_container->flags.flag_value_update = true; - } - - if(action_grant == v2::PermissionUpdateType::set_value) { - permission_container->flags.grant_set = true; - permission_container->flags.flag_grant_update = true; - permission_container->values.grant = values.grant; - } else if(action_grant == v2::PermissionUpdateType::delete_value) { - permission_container->flags.grant_set = false; - permission_container->flags.flag_grant_update = true; - } - - if(flag_skip >= 0) { - permission_container->flags.flag_value_update = true; - permission_container->flags.skip = flag_skip == 1; - } - - if(flag_negate >= 0) { - permission_container->flags.flag_value_update = true; - permission_container->flags.negate = flag_negate == 1; - } - - if(!permission_container->flags.permission_set()) { /* unregister the permission again because its unset, we delete the channel permission as soon we've flushed the updates */ - auto other_channel_permission = std::find_if(this->_channel_permissions.begin(), this->_channel_permissions.end(), [&](unique_ptr& perm) { return perm->permission == permission && perm->flags.permission_set(); }); - if(other_channel_permission == this->_channel_permissions.end()) { /* no more channel specific permissions c*/ - const auto block = this->calculate_block(permission); - if(this->ref_block(block)) { - this->block_containers[block]->permissions[this->calculate_block_index(permission)].flags.channel_specific = false; - this->unref_block(block); - } - } - } - this->trigger_db_update(); -} - -const std::vector> v2::PermissionManager::permissions() { - std::unique_lock use_lock(this->block_use_count_lock); - decltype(this->block_containers) block_containers; /* save the states/nullptr's */ - memcpy(block_containers, this->block_containers, sizeof(this->block_containers)); - size_t block_count = 0; - for(size_t index = 0; index < BULK_COUNT; index++) { - if(block_containers[index]) { - block_count++; - this->block_use_count[index]++; - } - } - use_lock.unlock(); - - vector> result; - result.reserve(block_count * PERMISSIONS_BULK_ENTRY_COUNT); - - for(size_t block_index = 0; block_index < BULK_COUNT; block_index++) { - auto& block = block_containers[block_index]; - if(!block) - continue; - - for(size_t permission_index = 0; permission_index < PERMISSIONS_BULK_ENTRY_COUNT; permission_index++) { - auto& permission = block->permissions[permission_index]; - if(!permission.flags.permission_set()) - continue; - - result.emplace_back((PermissionType) (block_index * PERMISSIONS_BULK_ENTRY_COUNT + permission_index), permission); - } - } - result.shrink_to_fit(); - - use_lock.lock(); - for(size_t index = 0; index < BULK_COUNT; index++) { - if(block_containers[index]) - this->block_use_count[index]--; - } - use_lock.unlock(); - - return result; -} - -const vector> v2::PermissionManager::channel_permissions(ts::ChannelId channel_id) { - shared_lock channel_perm_lock(this->channel_list_lock); - - vector> result; - for(auto& entry : this->_channel_permissions) - if((channel_id == entry->channel_id) && (entry->flags.value_set || entry->flags.grant_set)) - result.emplace_back(entry->permission, v2::PermissionContainer{entry->flags, entry->values}); - return result; -} - -const vector> v2::PermissionManager::channel_permissions() { - shared_lock channel_perm_lock(this->channel_list_lock); - - vector> result; - for(auto& entry : this->_channel_permissions) - if(entry->flags.value_set || entry->flags.grant_set) - result.emplace_back(entry->permission, entry->channel_id, v2::PermissionContainer{entry->flags, entry->values}); - return result; -} - -const std::vector v2::PermissionManager::flush_db_updates() { - if(!this->requires_db_save) - return {}; - - this->requires_db_save = false; - std::vector result; - - { - lock_guard use_lock(this->block_use_count_lock); - size_t block_count = 0; - for (auto &block_container : block_containers) - if (block_container) - block_count++; - result.reserve(block_count * PERMISSIONS_BULK_ENTRY_COUNT); - - for(size_t block_index = 0; block_index < BULK_COUNT; block_index++) { - auto& block = block_containers[block_index]; - if(!block) - continue; - - for(size_t permission_index = 0; permission_index < PERMISSIONS_BULK_ENTRY_COUNT; permission_index++) { - auto& permission = block->permissions[permission_index]; - - if(!permission.flags.flag_value_update && !permission.flags.flag_grant_update) - continue; - - /* we only need an update it the permission has a DB reference or we will set the permission */ - if(permission.flags.database_reference || permission.flags.permission_set()) { - /* - PermissionType permission; - ChannelId channel_id; - - PermissionValues values; - PermissionUpdateType update_value; - PermissionUpdateType update_grant; - - bool flag_db: 1; - bool flag_delete: 1; - bool flag_skip: 1; - bool flag_negate: 1; - */ - auto& entry = result.emplace_back(v2::PermissionDBUpdateEntry{ - (PermissionType) (block_index * PERMISSIONS_BULK_ENTRY_COUNT + permission_index), - (ChannelId) 0, - - permission.values, - (PermissionUpdateType) (permission.flags.flag_value_update ? (permission.flags.value_set ? PermissionUpdateType::set_value : PermissionUpdateType::delete_value) : PermissionUpdateType::do_nothing), - (PermissionUpdateType) (permission.flags.flag_grant_update ? (permission.flags.grant_set ? PermissionUpdateType::set_value : PermissionUpdateType::delete_value) : PermissionUpdateType::do_nothing), - - (bool) permission.flags.database_reference, - (bool) !permission.flags.permission_set(), /* db delete */ - (bool) permission.flags.skip, - (bool) permission.flags.negate - }); - - /* required for the database */ - if(!permission.flags.value_set) - entry.values.value = permNotGranted; - - if(!permission.flags.grant_set) - entry.values.grant = permNotGranted; - - permission.flags.database_reference = permission.flags.permission_set(); - } - - permission.flags.flag_value_update = false; - permission.flags.flag_grant_update = false; - } - } - } - { - lock_guard chanel_lock(this->channel_list_lock); - for(size_t index = 0; index < this->_channel_permissions.size(); index++) { - auto& permission = this->_channel_permissions[index]; - if(!permission->flags.flag_value_update && !permission->flags.flag_grant_update) - continue; - - - /* we only need an update it the permission has a DB reference or we will set the permission */ - if(permission->flags.database_reference || permission->flags.permission_set()) { - - auto& entry = result.emplace_back(v2::PermissionDBUpdateEntry{ - permission->permission, - permission->channel_id, - - permission->values, - (PermissionUpdateType) (permission->flags.flag_value_update ? (permission->flags.value_set ? PermissionUpdateType::set_value : PermissionUpdateType::delete_value) : PermissionUpdateType::do_nothing), - (PermissionUpdateType) (permission->flags.flag_grant_update ? (permission->flags.grant_set ? PermissionUpdateType::set_value : PermissionUpdateType::delete_value) : PermissionUpdateType::do_nothing), - - (bool) permission->flags.database_reference, - (bool) !permission->flags.permission_set(), /* db delete */ - (bool) permission->flags.skip, - (bool) permission->flags.negate - }); - - /* required for the database */ - if(!permission->flags.value_set) - entry.values.value = permNotGranted; - - if(!permission->flags.grant_set) - entry.values.grant = permNotGranted; - - permission->flags.database_reference = permission->flags.permission_set(); - } - - permission->flags.flag_value_update = false; - permission->flags.flag_grant_update = false; - if(!permission->flags.permission_set()) { - this->_channel_permissions.erase(this->_channel_permissions.begin() + index); - index--; - } - } - } - - return result; -} - -size_t v2::PermissionManager::used_memory() { - size_t result = sizeof(*this); - - for (auto &block_container : block_containers) { - if (block_container) - result += sizeof(PermissionContainerBulk); - } - - { - - shared_lock channel_lock(this->channel_list_lock); - result += this->_channel_permissions.size() * (sizeof(ChannelPermissionContainer) + sizeof(unique_ptr)); - } - - return result; -} - -void v2::PermissionManager::cleanup() { - lock_guard use_lock(this->block_use_count_lock); - - for (auto &block_container : block_containers) { - if (!block_container) continue; - - bool used = false; - for(auto& permission : block_container->permissions) { - if(permission.flags.value_set || permission.flags.grant_set || permission.flags.channel_specific) { - used = true; - break; - } - } - if(used) - continue; - - delete block_container; - block_container = nullptr; - } -} +#include +#include +#include "misc/memtracker.h" +#include "BasicChannel.h" +#include "log/LogUtils.h" + +using namespace std; +using namespace ts; +using namespace ts::permission; + +deque> ts::permission::availablePermissions = deque>{ + make_shared(PermissionType::b_serverinstance_help_view, PermissionGroup::global_info, "b_serverinstance_help_view", "Retrieve information about ServerQuery commands"), + make_shared(PermissionType::b_serverinstance_version_view, PermissionGroup::global_info, "b_serverinstance_version_view", "Retrieve global server version (including platform and build number)"), + make_shared(PermissionType::b_serverinstance_info_view, PermissionGroup::global_info, "b_serverinstance_info_view", "Retrieve global server information"), + make_shared(PermissionType::b_serverinstance_virtualserver_list, PermissionGroup::global_info, "b_serverinstance_virtualserver_list", "List virtual servers stored in the sql"), + make_shared(PermissionType::b_serverinstance_binding_list, PermissionGroup::global_info, "b_serverinstance_binding_list", "List active IP bindings on multi-homed machines"), + //Removed due its useless + make_shared(PermissionType::b_serverinstance_permission_list, PermissionGroup::global_info, "b_serverinstance_permission_list", "List permissions available available on the server instance"), + make_shared(PermissionType::b_serverinstance_permission_find, PermissionGroup::global_info, "b_serverinstance_permission_find", "Search permission assignments by name or ID"), + make_shared(PermissionType::b_virtualserver_create, PermissionGroup::global_vsmanage, "b_virtualserver_create", "Create virtual servers"), + make_shared(PermissionType::b_virtualserver_delete, PermissionGroup::global_vsmanage, "b_virtualserver_delete", "Delete virtual servers"), + make_shared(PermissionType::b_virtualserver_start_any, PermissionGroup::global_vsmanage, "b_virtualserver_start_any", "Start any virtual server in the server instance"), + make_shared(PermissionType::b_virtualserver_stop_any, PermissionGroup::global_vsmanage, "b_virtualserver_stop_any", "Stop any virtual server in the server instance"), + make_shared(PermissionType::b_virtualserver_change_machine_id, PermissionGroup::global_vsmanage, "b_virtualserver_change_machine_id", "Change a virtual servers machine ID"), + make_shared(PermissionType::b_virtualserver_change_template, PermissionGroup::global_vsmanage, "b_virtualserver_change_template", "Edit virtual server default template values"), + make_shared(PermissionType::b_serverquery_login, PermissionGroup::global_admin, "b_serverquery_login", "Login to ServerQuery"), + make_shared(PermissionType::b_serverinstance_textmessage_send, PermissionGroup::global_admin, "b_serverinstance_textmessage_send", "Send text messages to all virtual servers at once"), + make_shared(PermissionType::b_serverinstance_log_view, PermissionGroup::global_admin, "b_serverinstance_log_view", "Retrieve global server log"), + make_shared(PermissionType::b_serverinstance_log_add, PermissionGroup::global_admin, "b_serverinstance_log_add", "Write to global server log"), + make_shared(PermissionType::b_serverinstance_stop, PermissionGroup::global_admin, "b_serverinstance_stop", "Shutdown the server process"), + make_shared(PermissionType::b_serverinstance_modify_settings, PermissionGroup::global_settings, "b_serverinstance_modify_settings", "Edit global settings"), + make_shared(PermissionType::b_serverinstance_modify_querygroup, PermissionGroup::global_settings, "b_serverinstance_modify_querygroup", "Edit global ServerQuery groups"), + make_shared(PermissionType::b_serverinstance_modify_templates, PermissionGroup::global_settings, "b_serverinstance_modify_templates", "Edit global template groups"), + make_shared(PermissionType::b_virtualserver_select, PermissionGroup::vs_info, "b_virtualserver_select", "Select a virtual server"), + make_shared(PermissionType::b_virtualserver_select_godmode, PermissionGroup::vs_info, "b_virtualserver_select_godmode", "Select a virtual server but be invisible"), + make_shared(PermissionType::b_virtualserver_info_view, PermissionGroup::vs_info, "b_virtualserver_info_view", "Retrieve virtual server information"), + make_shared(PermissionType::b_virtualserver_connectioninfo_view, PermissionGroup::vs_info, "b_virtualserver_connectioninfo_view", "Retrieve virtual server connection information"), + make_shared(PermissionType::b_virtualserver_channel_list, PermissionGroup::vs_info, "b_virtualserver_channel_list", "List channels on a virtual server"), + make_shared(PermissionType::b_virtualserver_channel_search, PermissionGroup::vs_info, "b_virtualserver_channel_search", "Search for channels on a virtual server"), + make_shared(PermissionType::b_virtualserver_client_list, PermissionGroup::vs_info, "b_virtualserver_client_list", "List clients online on a virtual server"), + make_shared(PermissionType::b_virtualserver_client_search, PermissionGroup::vs_info, "b_virtualserver_client_search", "Search for clients online on a virtual server"), + make_shared(PermissionType::b_virtualserver_client_dblist, PermissionGroup::vs_info, "b_virtualserver_client_dblist", "List client identities known by the virtual server"), + make_shared(PermissionType::b_virtualserver_client_dbsearch, PermissionGroup::vs_info, "b_virtualserver_client_dbsearch", "Search for client identities known by the virtual server"), + make_shared(PermissionType::b_virtualserver_client_dbinfo, PermissionGroup::vs_info, "b_virtualserver_client_dbinfo", "Retrieve client information"), + make_shared(PermissionType::b_virtualserver_permission_find, PermissionGroup::vs_info, "b_virtualserver_permission_find", "Find permissions"), + make_shared(PermissionType::b_virtualserver_custom_search, PermissionGroup::vs_info, "b_virtualserver_custom_search", "Find custom fields"), + make_shared(PermissionType::b_virtualserver_start, PermissionGroup::vs_admin, "b_virtualserver_start", "Start own virtual server"), + make_shared(PermissionType::b_virtualserver_stop, PermissionGroup::vs_admin, "b_virtualserver_stop", "Stop own virtual server"), + make_shared(PermissionType::b_virtualserver_token_list, PermissionGroup::vs_admin, "b_virtualserver_token_list", "List privilege keys available"), + make_shared(PermissionType::b_virtualserver_token_add, PermissionGroup::vs_admin, "b_virtualserver_token_add", "Create new privilege keys"), + make_shared(PermissionType::b_virtualserver_token_use, PermissionGroup::vs_admin, "b_virtualserver_token_use", "Use a privilege keys to gain access to groups"), + make_shared(PermissionType::b_virtualserver_token_delete, PermissionGroup::vs_admin, "b_virtualserver_token_delete", "Delete a privilege key"), + make_shared(PermissionType::b_virtualserver_log_view, PermissionGroup::vs_admin, "b_virtualserver_log_view", "Retrieve virtual server log"), + make_shared(PermissionType::b_virtualserver_log_add, PermissionGroup::vs_admin, "b_virtualserver_log_add", "Write to virtual server log"), + make_shared(PermissionType::b_virtualserver_join_ignore_password, PermissionGroup::vs_admin, "b_virtualserver_join_ignore_password", "Join virtual server ignoring its password"), + make_shared(PermissionType::b_virtualserver_notify_register, PermissionGroup::vs_admin, "b_virtualserver_notify_register", "Register for server notifications"), + make_shared(PermissionType::b_virtualserver_notify_unregister, PermissionGroup::vs_admin, "b_virtualserver_notify_unregister", "Unregister from server notifications"), + make_shared(PermissionType::b_virtualserver_snapshot_create, PermissionGroup::vs_admin, "b_virtualserver_snapshot_create", "Create server snapshots"), + make_shared(PermissionType::b_virtualserver_snapshot_deploy, PermissionGroup::vs_admin, "b_virtualserver_snapshot_deploy", "Deploy server snapshots"), + make_shared(PermissionType::b_virtualserver_permission_reset, PermissionGroup::vs_admin, "b_virtualserver_permission_reset", "Reset the server permission settings to default values"), + make_shared(PermissionType::b_virtualserver_modify_name, PermissionGroup::vs_settings, "b_virtualserver_modify_name", "Modify server name"), + make_shared(PermissionType::b_virtualserver_modify_welcomemessage, PermissionGroup::vs_settings, "b_virtualserver_modify_welcomemessage", "Modify welcome message"), + make_shared(PermissionType::b_virtualserver_modify_maxchannels, PermissionGroup::vs_settings, "b_virtualserver_modify_maxchannels", "Modify servers max channels"), + make_shared(PermissionType::b_virtualserver_modify_maxclients, PermissionGroup::vs_settings, "b_virtualserver_modify_maxclients", "Modify servers max clients"), + make_shared(PermissionType::b_virtualserver_modify_reserved_slots, PermissionGroup::vs_settings, "b_virtualserver_modify_reserved_slots", "Modify reserved slots"), + make_shared(PermissionType::b_virtualserver_modify_password, PermissionGroup::vs_settings, "b_virtualserver_modify_password", "Modify server password"), + make_shared(PermissionType::b_virtualserver_modify_default_servergroup, PermissionGroup::vs_settings, "b_virtualserver_modify_default_servergroup", "Modify default Server Group"), + make_shared(PermissionType::b_virtualserver_modify_default_musicgroup, PermissionGroup::vs_settings, "b_virtualserver_modify_default_musicgroup", "Modify default music Group"), + make_shared(PermissionType::b_virtualserver_modify_default_channelgroup, PermissionGroup::vs_settings, "b_virtualserver_modify_default_channelgroup", "Modify default Channel Group"), + make_shared(PermissionType::b_virtualserver_modify_default_channeladmingroup, PermissionGroup::vs_settings, "b_virtualserver_modify_default_channeladmingroup", "Modify default Channel Admin Group"), + make_shared(PermissionType::b_virtualserver_modify_channel_forced_silence, PermissionGroup::vs_settings, "b_virtualserver_modify_channel_forced_silence", "Modify channel force silence value"), + make_shared(PermissionType::b_virtualserver_modify_complain, PermissionGroup::vs_settings, "b_virtualserver_modify_complain", "Modify individual complain settings"), + make_shared(PermissionType::b_virtualserver_modify_antiflood, PermissionGroup::vs_settings, "b_virtualserver_modify_antiflood", "Modify individual antiflood settings"), + make_shared(PermissionType::b_virtualserver_modify_ft_settings, PermissionGroup::vs_settings, "b_virtualserver_modify_ft_settings", "Modify file transfer settings"), + make_shared(PermissionType::b_virtualserver_modify_ft_quotas, PermissionGroup::vs_settings, "b_virtualserver_modify_ft_quotas", "Modify file transfer quotas"), + make_shared(PermissionType::b_virtualserver_modify_hostmessage, PermissionGroup::vs_settings, "b_virtualserver_modify_hostmessage", "Modify individual hostmessage settings"), + make_shared(PermissionType::b_virtualserver_modify_hostbanner, PermissionGroup::vs_settings, "b_virtualserver_modify_hostbanner", "Modify individual hostbanner settings"), + make_shared(PermissionType::b_virtualserver_modify_hostbutton, PermissionGroup::vs_settings, "b_virtualserver_modify_hostbutton", "Modify individual hostbutton settings"), + make_shared(PermissionType::b_virtualserver_modify_port, PermissionGroup::vs_settings, "b_virtualserver_modify_port", "Modify server port"), +#ifndef LAGENCY + make_shared(PermissionType::b_virtualserver_modify_host, PermissionGroup::vs_settings, "b_virtualserver_modify_host", "Modify server host"), +#endif + make_shared(PermissionType::b_virtualserver_modify_autostart, PermissionGroup::vs_settings, "b_virtualserver_modify_autostart", "Modify server autostart"), + make_shared(PermissionType::b_virtualserver_modify_needed_identity_security_level, PermissionGroup::vs_settings, "b_virtualserver_modify_needed_identity_security_level", "Modify required identity security level"), + make_shared(PermissionType::b_virtualserver_modify_priority_speaker_dimm_modificator, PermissionGroup::vs_settings, "b_virtualserver_modify_priority_speaker_dimm_modificator", "Modify priority speaker dimm modificator"), + make_shared(PermissionType::b_virtualserver_modify_log_settings, PermissionGroup::vs_settings, "b_virtualserver_modify_log_settings", "Modify log settings"), + make_shared(PermissionType::b_virtualserver_modify_min_client_version, PermissionGroup::vs_settings, "b_virtualserver_modify_min_client_version", "Modify min client version"), + make_shared(PermissionType::b_virtualserver_modify_icon_id, PermissionGroup::vs_settings, "b_virtualserver_modify_icon_id", "Modify server icon"), + make_shared(PermissionType::b_virtualserver_modify_weblist, PermissionGroup::vs_settings, "b_virtualserver_modify_weblist", "Modify web server list reporting settings"), + make_shared(PermissionType::b_virtualserver_modify_country_code, PermissionGroup::vs_settings, "b_virtualserver_modify_country_code", "Modify servers country code property"), + make_shared(PermissionType::b_virtualserver_modify_codec_encryption_mode, PermissionGroup::vs_settings, "b_virtualserver_modify_codec_encryption_mode", "Modify codec encryption mode"), + make_shared(PermissionType::b_virtualserver_modify_temporary_passwords, PermissionGroup::vs_settings, "b_virtualserver_modify_temporary_passwords", "Modify temporary serverpasswords"), + make_shared(PermissionType::b_virtualserver_modify_temporary_passwords_own, PermissionGroup::vs_settings, "b_virtualserver_modify_temporary_passwords_own", "Modify own temporary serverpasswords"), + make_shared(PermissionType::b_virtualserver_modify_channel_temp_delete_delay_default, PermissionGroup::vs_settings, "b_virtualserver_modify_channel_temp_delete_delay_default", "Modify default temporary channel delete delay"), + make_shared(PermissionType::b_virtualserver_modify_music_bot_limit, PermissionGroup::vs_settings, "b_virtualserver_modify_music_bot_limit", "Allow client to edit the server music bot limit"), + make_shared(PermissionType::b_virtualserver_modify_default_messages, PermissionGroup::vs_settings, "b_virtualserver_modify_default_messages", "Allows the client to edit the default messages"), + make_shared(PermissionType::i_channel_min_depth, PermissionGroup::channel, "i_channel_min_depth", "Min channel creation depth in hierarchy"), + make_shared(PermissionType::i_channel_max_depth, PermissionGroup::channel, "i_channel_max_depth", "Max channel creation depth in hierarchy"), + make_shared(PermissionType::b_channel_group_inheritance_end, PermissionGroup::channel, "b_channel_group_inheritance_end", "Stop inheritance of channel group permissions"), + make_shared(PermissionType::i_channel_permission_modify_power, PermissionGroup::channel, "i_channel_permission_modify_power", "Modify channel permission power"), + make_shared(PermissionType::i_channel_needed_permission_modify_power, PermissionGroup::channel, "i_channel_needed_permission_modify_power", "Needed modify channel permission power"), + make_shared(PermissionType::b_channel_info_view, PermissionGroup::channel_info, "b_channel_info_view", "Retrieve channel information"), + make_shared(PermissionType::b_channel_create_child, PermissionGroup::channel_create, "b_channel_create_child", "Create sub-channels"), + make_shared(PermissionType::b_channel_create_permanent, PermissionGroup::channel_create, "b_channel_create_permanent", "Create permanent channels"), + make_shared(PermissionType::b_channel_create_semi_permanent, PermissionGroup::channel_create, "b_channel_create_semi_permanent", "Create semi-permanent channels"), + make_shared(PermissionType::b_channel_create_temporary, PermissionGroup::channel_create, "b_channel_create_temporary", "Create temporary channels"), + make_shared(PermissionType::b_channel_create_private, PermissionGroup::channel_create, "b_channel_create_private", "Create private channel"), + make_shared(PermissionType::b_channel_create_with_topic, PermissionGroup::channel_create, "b_channel_create_with_topic", "Create channels with a topic"), + make_shared(PermissionType::b_channel_create_with_description, PermissionGroup::channel_create, "b_channel_create_with_description", "Create channels with a description"), + make_shared(PermissionType::b_channel_create_with_password, PermissionGroup::channel_create, "b_channel_create_with_password", "Create password protected channels"), + make_shared(PermissionType::b_channel_create_modify_with_codec_speex8, PermissionGroup::channel_create, "b_channel_create_modify_with_codec_speex8", "Create channels using Speex Narrowband (8 kHz) codecs"), + make_shared(PermissionType::b_channel_create_modify_with_codec_speex16, PermissionGroup::channel_create, "b_channel_create_modify_with_codec_speex16", "Create channels using Speex Wideband (16 kHz) codecs"), + make_shared(PermissionType::b_channel_create_modify_with_codec_speex32, PermissionGroup::channel_create, "b_channel_create_modify_with_codec_speex32", "Create channels using Speex Ultra-Wideband (32 kHz) codecs"), + make_shared(PermissionType::b_channel_create_modify_with_codec_celtmono48, PermissionGroup::channel_create, "b_channel_create_modify_with_codec_celtmono48", "Create channels using the CELT Mono (48 kHz) codec"), + make_shared(PermissionType::b_channel_create_modify_with_codec_opusvoice, PermissionGroup::channel_create, "b_channel_create_modify_with_codec_opusvoice", "Create channels using OPUS (voice) codec"), + make_shared(PermissionType::b_channel_create_modify_with_codec_opusmusic, PermissionGroup::channel_create, "b_channel_create_modify_with_codec_opusmusic", "Create channels using OPUS (music) codec"), + make_shared(PermissionType::i_channel_create_modify_with_codec_maxquality, PermissionGroup::channel_create, "i_channel_create_modify_with_codec_maxquality", "Create channels with custom codec quality"), + make_shared(PermissionType::i_channel_create_modify_with_codec_latency_factor_min, PermissionGroup::channel_create, "i_channel_create_modify_with_codec_latency_factor_min", "Create channels with minimal custom codec latency factor"), + make_shared(PermissionType::b_channel_create_with_maxclients, PermissionGroup::channel_create, "b_channel_create_with_maxclients", "Create channels with custom max clients"), + make_shared(PermissionType::b_channel_create_with_maxfamilyclients, PermissionGroup::channel_create, "b_channel_create_with_maxfamilyclients", "Create channels with custom max family clients"), + make_shared(PermissionType::b_channel_create_with_sortorder, PermissionGroup::channel_create, "b_channel_create_with_sortorder", "Create channels with custom sort order"), + make_shared(PermissionType::b_channel_create_with_default, PermissionGroup::channel_create, "b_channel_create_with_default", "Create default channels"), + make_shared(PermissionType::b_channel_create_with_needed_talk_power, PermissionGroup::channel_create, "b_channel_create_with_needed_talk_power", "Create channels with needed talk power"), + make_shared(PermissionType::b_channel_create_modify_with_force_password, PermissionGroup::channel_create, "b_channel_create_modify_with_force_password", "Create new channels only with password"), + make_shared(PermissionType::i_channel_create_modify_with_temp_delete_delay, PermissionGroup::channel_create, "i_channel_create_modify_with_temp_delete_delay", "Max delete delay for temporary channels"), + make_shared(PermissionType::i_channel_create_modify_conversation_history_length, PermissionGroup::channel_create, "i_channel_create_modify_conversation_history_length", "Upper limmit for the setting of the max conversation history limit"), + make_shared(PermissionType::b_channel_create_modify_conversation_history_unlimited, PermissionGroup::channel_create, "b_channel_create_modify_conversation_history_unlimited", "Allows the user to set the channel conversation history to unlimited"), + make_shared(PermissionType::b_channel_create_modify_conversation_private, PermissionGroup::channel_create, "b_channel_create_modify_conversation_private", "Allows the user to set the channel conversation to private"), + make_shared(PermissionType::b_channel_modify_parent, PermissionGroup::channel_modify, "b_channel_modify_parent", "Move channels"), + make_shared(PermissionType::b_channel_modify_make_default, PermissionGroup::channel_modify, "b_channel_modify_make_default", "Make channel default"), + make_shared(PermissionType::b_channel_modify_make_permanent, PermissionGroup::channel_modify, "b_channel_modify_make_permanent", "Make channel permanent"), + make_shared(PermissionType::b_channel_modify_make_semi_permanent, PermissionGroup::channel_modify, "b_channel_modify_make_semi_permanent", "Make channel semi-permanent"), + make_shared(PermissionType::b_channel_modify_make_temporary, PermissionGroup::channel_modify, "b_channel_modify_make_temporary", "Make channel temporary"), + make_shared(PermissionType::b_channel_modify_name, PermissionGroup::channel_modify, "b_channel_modify_name", "Modify channel name"), + make_shared(PermissionType::b_channel_modify_topic, PermissionGroup::channel_modify, "b_channel_modify_topic", "Modify channel topic"), + make_shared(PermissionType::b_channel_modify_description, PermissionGroup::channel_modify, "b_channel_modify_description", "Modify channel description"), + make_shared(PermissionType::b_channel_modify_password, PermissionGroup::channel_modify, "b_channel_modify_password", "Modify channel password"), + make_shared(PermissionType::b_channel_modify_codec, PermissionGroup::channel_modify, "b_channel_modify_codec", "Modify channel codec"), + make_shared(PermissionType::b_channel_modify_codec_quality, PermissionGroup::channel_modify, "b_channel_modify_codec_quality", "Modify channel codec quality"), + make_shared(PermissionType::b_channel_modify_codec_latency_factor, PermissionGroup::channel_modify, "b_channel_modify_codec_latency_factor", "Modify channel codec latency factor"), + make_shared(PermissionType::b_channel_modify_maxclients, PermissionGroup::channel_modify, "b_channel_modify_maxclients", "Modify channels max clients"), + make_shared(PermissionType::b_channel_modify_maxfamilyclients, PermissionGroup::channel_modify, "b_channel_modify_maxfamilyclients", "Modify channels max family clients"), + make_shared(PermissionType::b_channel_modify_sortorder, PermissionGroup::channel_modify, "b_channel_modify_sortorder", "Modify channel sort order"), + make_shared(PermissionType::b_channel_modify_needed_talk_power, PermissionGroup::channel_modify, "b_channel_modify_needed_talk_power", "Change needed channel talk power"), + make_shared(PermissionType::i_channel_modify_power, PermissionGroup::channel_modify, "i_channel_modify_power", "Channel modify power"), + make_shared(PermissionType::i_channel_needed_modify_power, PermissionGroup::channel_modify, "i_channel_needed_modify_power", "Needed channel modify power"), + make_shared(PermissionType::b_channel_modify_make_codec_encrypted, PermissionGroup::channel_modify, "b_channel_modify_make_codec_encrypted", "Make channel codec encrypted"), + make_shared(PermissionType::b_channel_modify_temp_delete_delay, PermissionGroup::channel_modify, "b_channel_modify_temp_delete_delay", "Modify temporary channel delete delay"), + make_shared(PermissionType::b_channel_conversation_message_delete, PermissionGroup::channel_modify, "b_channel_conversation_message_delete", "If set the user is able to delete conversation messages"), + + make_shared(PermissionType::b_channel_delete_permanent, PermissionGroup::channel_delete, "b_channel_delete_permanent", "Delete permanent channels"), + make_shared(PermissionType::b_channel_delete_semi_permanent, PermissionGroup::channel_delete, "b_channel_delete_semi_permanent", "Delete semi-permanent channels"), + make_shared(PermissionType::b_channel_delete_temporary, PermissionGroup::channel_delete, "b_channel_delete_temporary", "Delete temporary channels"), + make_shared(PermissionType::b_channel_delete_flag_force, PermissionGroup::channel_delete, "b_channel_delete_flag_force", "Force channel delete"), + make_shared(PermissionType::i_channel_delete_power, PermissionGroup::channel_delete, "i_channel_delete_power", "Delete channel power"), + make_shared(PermissionType::i_channel_needed_delete_power, PermissionGroup::channel_delete, "i_channel_needed_delete_power", "Needed delete channel power"), + make_shared(PermissionType::b_channel_join_permanent, PermissionGroup::channel_access, "b_channel_join_permanent", "Join permanent channels"), + make_shared(PermissionType::b_channel_join_semi_permanent, PermissionGroup::channel_access, "b_channel_join_semi_permanent", "Join semi-permanent channels"), + make_shared(PermissionType::b_channel_join_temporary, PermissionGroup::channel_access, "b_channel_join_temporary", "Join temporary channels"), + make_shared(PermissionType::b_channel_join_ignore_password, PermissionGroup::channel_access, "b_channel_join_ignore_password", "Join channel ignoring its password"), + make_shared(PermissionType::b_channel_join_ignore_maxclients, PermissionGroup::channel_access, "b_channel_join_ignore_maxclients", "Ignore channels max clients limit"), + make_shared(PermissionType::b_channel_ignore_view_power, PermissionGroup::channel_access, "b_channel_ignore_view_power", "If set the client see's every channel"), + make_shared(PermissionType::i_channel_join_power, PermissionGroup::channel_access, "i_channel_join_power", "Channel join power"), + make_shared(PermissionType::i_channel_needed_join_power, PermissionGroup::channel_access, "i_channel_needed_join_power", "Needed channel join power"), + make_shared(PermissionType::b_channel_ignore_join_power, PermissionGroup::channel_access, "b_channel_ignore_join_power", "Allows the client to bypass the channel join power"), + make_shared(PermissionType::i_channel_view_power, PermissionGroup::channel_access, "i_channel_view_power", "Channel view power"), + make_shared(PermissionType::i_channel_needed_view_power, PermissionGroup::channel_access, "i_channel_needed_view_power", "Needed channel view power"), + make_shared(PermissionType::i_channel_subscribe_power, PermissionGroup::channel_access, "i_channel_subscribe_power", "Channel subscribe power"), + make_shared(PermissionType::i_channel_needed_subscribe_power, PermissionGroup::channel_access, "i_channel_needed_subscribe_power", "Needed channel subscribe power"), + make_shared(PermissionType::b_channel_ignore_subscribe_power, PermissionGroup::channel_access, "b_channel_ignore_subscribe_power", "Allows the client to bypass the subscribe power"), + make_shared(PermissionType::i_channel_description_view_power, PermissionGroup::channel_access, "i_channel_description_view_power", "Channel description view power"), + make_shared(PermissionType::i_channel_needed_description_view_power, PermissionGroup::channel_access, "i_channel_needed_description_view_power", "Needed channel needed description view power"), + make_shared(PermissionType::b_channel_ignore_description_view_power, PermissionGroup::channel_access, "b_channel_ignore_description_view_power", "Allows the client to bypass the channel description view power"), + make_shared(PermissionType::i_icon_id, PermissionGroup::group, "i_icon_id", "Group icon identifier"), + make_shared(PermissionType::i_max_icon_filesize, PermissionGroup::group, "i_max_icon_filesize", "Max icon filesize in bytes"), + make_shared(PermissionType::i_max_playlist_size, PermissionGroup::group, "i_max_playlist_size", "Max songs within one playlist"), + make_shared(PermissionType::i_max_playlists, PermissionGroup::group, "i_max_playlists", "Max amount of playlists a client could own"), + make_shared(PermissionType::b_icon_manage, PermissionGroup::group, "b_icon_manage", "Enables icon management"), + make_shared(PermissionType::b_group_is_permanent, PermissionGroup::group, "b_group_is_permanent", "Group is permanent"), + make_shared(PermissionType::i_group_auto_update_type, PermissionGroup::group, "i_group_auto_update_type", "Group auto-update type"), + make_shared(PermissionType::i_group_auto_update_max_value, PermissionGroup::group, "i_group_auto_update_max_value", "Group auto-update max value"), + make_shared(PermissionType::i_group_sort_id, PermissionGroup::group, "i_group_sort_id", "Group sort id"), + make_shared(PermissionType::i_group_show_name_in_tree, PermissionGroup::group, "i_group_show_name_in_tree", "Show group name in tree depending on selected mode"), + make_shared(PermissionType::b_virtualserver_servergroup_list, PermissionGroup::group_info, "b_virtualserver_servergroup_list", "List server groups"), + make_shared(PermissionType::b_virtualserver_servergroup_permission_list, PermissionGroup::group_info, "b_virtualserver_servergroup_permission_list", "Allows the client to view all server group permissions"), + make_shared(PermissionType::b_virtualserver_servergroup_client_list, PermissionGroup::group_info, "b_virtualserver_servergroup_client_list", "List clients from a server group"), + make_shared(PermissionType::b_virtualserver_channelgroup_list, PermissionGroup::group_info, "b_virtualserver_channelgroup_list", "List channel groups"), + make_shared(PermissionType::b_virtualserver_channelgroup_permission_list, PermissionGroup::group_info, "b_virtualserver_channelgroup_permission_list", "Allows the client to view all channel group permissions"), + make_shared(PermissionType::b_virtualserver_channelgroup_client_list, PermissionGroup::group_info, "b_virtualserver_channelgroup_client_list", "List clients from a channel group"), + make_shared(PermissionType::b_virtualserver_client_permission_list, PermissionGroup::group_info, "b_virtualserver_client_permission_list", "Allows the client to view all client permissions"), + make_shared(PermissionType::b_virtualserver_channel_permission_list, PermissionGroup::group_info, "b_virtualserver_channel_permission_list", "Allows the client to view all channel permissions"), + make_shared(PermissionType::b_virtualserver_channelclient_permission_list, PermissionGroup::group_info, "b_virtualserver_channelclient_permission_list", "Allows the client to view all client channel permissions"), + make_shared(PermissionType::b_virtualserver_playlist_permission_list, PermissionGroup::group_info, "b_virtualserver_playlist_permission_list", "Allows the client to view all playlist permissions"), + make_shared(PermissionType::b_virtualserver_servergroup_create, PermissionGroup::group_create, "b_virtualserver_servergroup_create", "Create server groups"), + make_shared(PermissionType::b_virtualserver_channelgroup_create, PermissionGroup::group_create, "b_virtualserver_channelgroup_create", "Create channel groups"), + +#ifdef LAGENCY + make_shared(PermissionType::i_group_modify_power, PermissionGroup::group_168, "i_group_modify_power", "Group modify power"), + make_shared(PermissionType::i_group_needed_modify_power, PermissionGroup::group_168, "i_group_needed_modify_power", "Needed group modify power"), + make_shared(PermissionType::i_group_member_add_power, PermissionGroup::group_168, "i_group_member_add_power", "Group member add power"), + make_shared(PermissionType::i_group_needed_member_add_power, PermissionGroup::group_168, "i_group_needed_member_add_power", "Needed group member add power"), + make_shared(PermissionType::i_group_member_remove_power, PermissionGroup::group_168, "i_group_member_remove_power", "Group member delete power"), + make_shared(PermissionType::i_group_needed_member_remove_power, PermissionGroup::group_168, "i_group_needed_member_remove_power", "Needed group member delete power"), +#else + make_shared(PermissionType::i_server_group_modify_power, PermissionGroup::group_modify, "i_server_group_modify_power", "Server group modify power"), + make_shared(PermissionType::i_server_group_needed_modify_power, PermissionGroup::group_modify, "i_server_group_needed_modify_power", "Needed server group modify power"), + make_shared(PermissionType::i_server_group_member_add_power, PermissionGroup::group_modify, "i_server_group_member_add_power", "Server group member add power"), + make_shared(PermissionType::i_server_group_self_add_power, PermissionGroup::group_modify, "i_server_group_self_add_power", "Server group self add power"), + make_shared(PermissionType::i_server_group_needed_member_add_power, PermissionGroup::group_modify, "i_server_group_needed_member_add_power", "Needed server group member add power"), + make_shared(PermissionType::i_server_group_member_remove_power, PermissionGroup::group_modify, "i_server_group_member_remove_power", "Server group member delete power"), + make_shared(PermissionType::i_server_group_self_remove_power, PermissionGroup::group_modify, "i_server_group_self_remove_power", "Server group self delete power"), + make_shared(PermissionType::i_server_group_needed_member_remove_power, PermissionGroup::group_modify, "i_server_group_needed_member_remove_power", "Needed server group member delete power"), + + make_shared(PermissionType::i_channel_group_modify_power, PermissionGroup::group_modify, "i_channel_group_modify_power", "Channel group modify power"), + make_shared(PermissionType::i_channel_group_needed_modify_power, PermissionGroup::group_modify, "i_channel_group_needed_modify_power", "Needed channel group modify power"), + make_shared(PermissionType::i_channel_group_member_add_power, PermissionGroup::group_modify, "i_channel_group_member_add_power", "Channel group member add power"), + make_shared(PermissionType::i_channel_group_self_add_power, PermissionGroup::group_modify, "i_channel_group_self_add_power", "Channel group self add power"), + make_shared(PermissionType::i_channel_group_needed_member_add_power, PermissionGroup::group_modify, "i_channel_group_needed_member_add_power", "Needed channel group member add power"), + make_shared(PermissionType::i_channel_group_member_remove_power, PermissionGroup::group_modify, "i_channel_group_member_remove_power", "Channel group member delete power"), + make_shared(PermissionType::i_channel_group_self_remove_power, PermissionGroup::group_modify, "i_channel_group_self_remove_power", "Channel group self delete power"), + make_shared(PermissionType::i_channel_group_needed_member_remove_power, PermissionGroup::group_modify, "i_channel_group_needed_member_remove_power", "Needed channel group member delete power"), + + //old enum mapping + make_shared(PermissionType::i_displayed_group_member_add_power, PermissionGroup::group_modify, "i_group_member_add_power", "The displayed member add power (Enables/Disabled the group in the select menu)"), + make_shared(PermissionType::i_displayed_group_needed_member_add_power, PermissionGroup::group_modify, "i_group_needed_member_add_power", "The needed displayed member add power (Enables/Disabled the group in the select menu)"), + make_shared(PermissionType::i_displayed_group_member_remove_power, PermissionGroup::group_modify, "i_group_member_remove_power", "The displayed member add power (Enables/Disabled the group in the select menu)"), + make_shared(PermissionType::i_displayed_group_needed_member_remove_power, PermissionGroup::group_modify, "i_group_needed_member_remove_power", "The needed displayed member add power (Enables/Disabled the group in the select menu)"), + make_shared(PermissionType::i_displayed_group_modify_power, PermissionGroup::group_modify, "i_group_modify_power", "The displayed member add power (Enables/Disabled the group in the select menu)"), + make_shared(PermissionType::i_displayed_group_needed_modify_power, PermissionGroup::group_modify, "i_group_needed_modify_power", "The needed displayed member add power (Enables/Disabled the group in the select menu)"), + + //new enum mapping (must come AFTER the supported permissions) + make_shared(PermissionType::i_displayed_group_member_add_power, PermissionGroup::group_modify, "i_displayed_group_member_add_power", "The displayed member add power (Enables/Disabled the group in the select menu)", false), + make_shared(PermissionType::i_displayed_group_needed_member_add_power, PermissionGroup::group_modify, "i_displayed_group_needed_member_add_power", "The needed displayed member add power (Enables/Disabled the group in the select menu)", false), + make_shared(PermissionType::i_displayed_group_member_remove_power, PermissionGroup::group_modify, "i_displayed_group_member_remove_power", "The displayed member add power (Enables/Disabled the group in the select menu)", false), + make_shared(PermissionType::i_displayed_group_needed_member_remove_power, PermissionGroup::group_modify, "i_displayed_group_needed_member_remove_power", "The needed displayed member add power (Enables/Disabled the group in the select menu)", false), + make_shared(PermissionType::i_displayed_group_modify_power, PermissionGroup::group_modify, "i_displayed_group_modify_power", "The displayed member add power (Enables/Disabled the group in the select menu)", false), + make_shared(PermissionType::i_displayed_group_needed_modify_power, PermissionGroup::group_modify, "i_displayed_group_needed_modify_power", "The needed displayed member add power (Enables/Disabled the group in the select menu)", false), +#endif + + make_shared(PermissionType::i_permission_modify_power, PermissionGroup::group_modify, "i_permission_modify_power", "Permission modify power"), + make_shared(PermissionType::b_permission_modify_power_ignore, PermissionGroup::group_modify, "b_permission_modify_power_ignore", "Ignore needed permission modify power"), + make_shared(PermissionType::b_virtualserver_servergroup_delete, PermissionGroup::group_delete, "b_virtualserver_servergroup_delete", "Delete server groups"), + make_shared(PermissionType::b_virtualserver_channelgroup_delete, PermissionGroup::group_delete, "b_virtualserver_channelgroup_delete", "Delete channel groups"), + make_shared(PermissionType::i_client_permission_modify_power, PermissionGroup::client, "i_client_permission_modify_power", "Client permission modify power"), + make_shared(PermissionType::i_client_needed_permission_modify_power, PermissionGroup::client, "i_client_needed_permission_modify_power", "Needed client permission modify power"), + make_shared(PermissionType::i_client_max_clones_uid, PermissionGroup::client, "i_client_max_clones_uid", "Max additional connections per client identity"), + make_shared(PermissionType::i_client_max_clones_ip, PermissionGroup::client, "i_client_max_clones_ip", "Max additional connections per client address"), + make_shared(PermissionType::i_client_max_clones_hwid, PermissionGroup::client, "i_client_max_clones_hwid", "Max additional connections per client hardware id"), + make_shared(PermissionType::i_client_max_idletime, PermissionGroup::client, "i_client_max_idletime", "Max idle time in seconds"), + make_shared(PermissionType::i_client_max_avatar_filesize, PermissionGroup::client, "i_client_max_avatar_filesize", "Max avatar filesize in bytes"), + make_shared(PermissionType::i_client_max_channel_subscriptions, PermissionGroup::client, "i_client_max_channel_subscriptions", "Max channel subscriptions"), + make_shared(PermissionType::i_client_max_channels, PermissionGroup::client, "i_client_max_channels", "Limit of created channels"), + make_shared(PermissionType::i_client_max_temporary_channels, PermissionGroup::client, "i_client_max_temporary_channels", "Limit of created temporary channels"), + make_shared(PermissionType::i_client_max_semi_channels, PermissionGroup::client, "i_client_max_semi_channels", "Limit of created semi-permanent channels"), + make_shared(PermissionType::i_client_max_permanent_channels, PermissionGroup::client, "i_client_max_permanent_channels", "Limit of created permanent channels"), + make_shared(PermissionType::b_client_use_priority_speaker, PermissionGroup::client, "b_client_use_priority_speaker", "Allows the client to use priority speaker"), + make_shared(PermissionType::b_client_is_priority_speaker, PermissionGroup::client, "b_client_is_priority_speaker", "Toogels the client priority speaker mode"), + make_shared(PermissionType::b_client_skip_channelgroup_permissions, PermissionGroup::client, "b_client_skip_channelgroup_permissions", "Ignore channel group permissions"), + make_shared(PermissionType::b_client_force_push_to_talk, PermissionGroup::client, "b_client_force_push_to_talk", "Force Push-To-Talk capture mode"), + make_shared(PermissionType::b_client_ignore_bans, PermissionGroup::client, "b_client_ignore_bans", "Ignore bans"), + make_shared(PermissionType::b_client_ignore_vpn, PermissionGroup::client, "b_client_ignore_vpn", "Ignore the vpn check"), + make_shared(PermissionType::b_client_ignore_antiflood, PermissionGroup::client, "b_client_ignore_antiflood", "Ignore antiflood measurements"), + make_shared(PermissionType::b_client_enforce_valid_hwid, PermissionGroup::client, "b_client_enforce_valid_hwid", "Enforces the client to have a valid hardware id"), + make_shared(PermissionType::b_client_allow_invalid_packet, PermissionGroup::client, "b_client_allow_invalid_packet", "Allow client to send invalid packets"), + make_shared(PermissionType::b_client_allow_invalid_badges, PermissionGroup::client, "b_client_allow_invalid_badges", "Allow client to have invalid badges"), + make_shared(PermissionType::b_client_issue_client_query_command, PermissionGroup::client, "b_client_issue_client_query_command", "Issue query commands from client"), + make_shared(PermissionType::b_client_use_reserved_slot, PermissionGroup::client, "b_client_use_reserved_slot", "Use an reserved slot"), + make_shared(PermissionType::b_client_use_channel_commander, PermissionGroup::client, "b_client_use_channel_commander", "Use channel commander"), + make_shared(PermissionType::b_client_request_talker, PermissionGroup::client, "b_client_request_talker", "Allow to request talk power"), + make_shared(PermissionType::b_client_avatar_delete_other, PermissionGroup::client, "b_client_avatar_delete_other", "Allow deletion of avatars from other clients"), + make_shared(PermissionType::b_client_is_sticky, PermissionGroup::client, "b_client_is_sticky", "Client will be sticked to current channel"), + make_shared(PermissionType::b_client_ignore_sticky, PermissionGroup::client, "b_client_ignore_sticky", "Client ignores sticky flag"), +#ifndef LAGACY + make_shared(PermissionType::b_client_music_channel_list, PermissionGroup::client_info, "b_client_music_channel_list", "List all music bots in the current channel"), + make_shared(PermissionType::b_client_music_server_list, PermissionGroup::client_info, "b_client_music_server_list", "List all music bots on the sderver"), + make_shared(PermissionType::i_client_music_info, PermissionGroup::client_info, "i_client_music_info", "Permission to view music bot info"), + make_shared(PermissionType::i_client_music_needed_info, PermissionGroup::client_info, "i_client_music_needed_info", "Required permission to view music bot info"), +#endif + make_shared(PermissionType::b_client_info_view, PermissionGroup::client_info, "b_client_info_view", "Retrieve client information"), + make_shared(PermissionType::b_client_permissionoverview_view, PermissionGroup::client_info, "b_client_permissionoverview_view", "Retrieve client permissions overview"), + make_shared(PermissionType::b_client_permissionoverview_own, PermissionGroup::client_info, "b_client_permissionoverview_own", "Retrieve clients own permissions overview"), + make_shared(PermissionType::b_client_remoteaddress_view, PermissionGroup::client_info, "b_client_remoteaddress_view", "View client IP address and port"), + make_shared(PermissionType::i_client_serverquery_view_power, PermissionGroup::client_info, "i_client_serverquery_view_power", "ServerQuery view power"), + make_shared(PermissionType::i_client_needed_serverquery_view_power, PermissionGroup::client_info, "i_client_needed_serverquery_view_power", "Needed ServerQuery view power"), + make_shared(PermissionType::b_client_custom_info_view, PermissionGroup::client_info, "b_client_custom_info_view", "View custom fields"), + make_shared(PermissionType::i_client_kick_from_server_power, PermissionGroup::client_admin, "i_client_kick_from_server_power", "Client kick power from server"), + make_shared(PermissionType::i_client_needed_kick_from_server_power, PermissionGroup::client_admin, "i_client_needed_kick_from_server_power", "Needed client kick power from server"), + make_shared(PermissionType::i_client_kick_from_channel_power, PermissionGroup::client_admin, "i_client_kick_from_channel_power", "Client kick power from channel"), + make_shared(PermissionType::i_client_needed_kick_from_channel_power, PermissionGroup::client_admin, "i_client_needed_kick_from_channel_power", "Needed client kick power from channel"), + make_shared(PermissionType::i_client_ban_power, PermissionGroup::client_admin, "i_client_ban_power", "Client ban power"), + make_shared(PermissionType::i_client_needed_ban_power, PermissionGroup::client_admin, "i_client_needed_ban_power", "Needed client ban power"), + make_shared(PermissionType::i_client_move_power, PermissionGroup::client_admin, "i_client_move_power", "Client move power"), + make_shared(PermissionType::i_client_needed_move_power, PermissionGroup::client_admin, "i_client_needed_move_power", "Needed client move power"), + make_shared(PermissionType::i_client_complain_power, PermissionGroup::client_admin, "i_client_complain_power", "Complain power"), + make_shared(PermissionType::i_client_needed_complain_power, PermissionGroup::client_admin, "i_client_needed_complain_power", "Needed complain power"), + make_shared(PermissionType::b_client_complain_list, PermissionGroup::client_admin, "b_client_complain_list", "Show complain list"), + make_shared(PermissionType::b_client_complain_delete_own, PermissionGroup::client_admin, "b_client_complain_delete_own", "Delete own complains"), + make_shared(PermissionType::b_client_complain_delete, PermissionGroup::client_admin, "b_client_complain_delete", "Delete complains"), + make_shared(PermissionType::b_client_ban_list, PermissionGroup::client_admin, "b_client_ban_list", "Show banlist"), + make_shared(PermissionType::b_client_ban_trigger_list, PermissionGroup::client_admin, "b_client_ban_trigger_list", "Show trigger banlist"), + make_shared(PermissionType::b_client_ban_list_global, PermissionGroup::client_admin, "b_client_ban_list_global", "Show banlist globaly"), + make_shared(PermissionType::b_client_ban_create, PermissionGroup::client_admin, "b_client_ban_create", "Add a ban"), + make_shared(PermissionType::b_client_ban_create_global, PermissionGroup::client_admin, "b_client_ban_create_global", "Allow to create global bans"), + make_shared(PermissionType::b_client_ban_name, PermissionGroup::client_admin, "b_client_ban_name", "Allows the client to ban a client by name"), + make_shared(PermissionType::b_client_ban_ip, PermissionGroup::client_admin, "b_client_ban_ip", "Allows the client to ban a client by ip"), + make_shared(PermissionType::b_client_ban_hwid, PermissionGroup::client_admin, "b_client_ban_hwid", "Allows the client to ban a client hardware id"), + make_shared(PermissionType::b_client_ban_edit, PermissionGroup::client_admin, "b_client_ban_edit", "Allow to edit bans"), + make_shared(PermissionType::b_client_ban_edit_global, PermissionGroup::client_admin, "b_client_ban_edit_global", "Allow to edit global bans"), + make_shared(PermissionType::b_client_ban_delete_own, PermissionGroup::client_admin, "b_client_ban_delete_own", "Delete own bans"), + make_shared(PermissionType::b_client_ban_delete, PermissionGroup::client_admin, "b_client_ban_delete", "Delete bans"), + make_shared(PermissionType::b_client_ban_delete_own_global, PermissionGroup::client_admin, "b_client_ban_delete_own_global", "Delete own global bans"), + make_shared(PermissionType::b_client_ban_delete_global, PermissionGroup::client_admin, "b_client_ban_delete_global", "Delete global bans"), + make_shared(PermissionType::i_client_ban_max_bantime, PermissionGroup::client_admin, "i_client_ban_max_bantime", "Max bantime"), + make_shared(PermissionType::b_client_even_textmessage_send, PermissionGroup::client_basic, "b_client_even_textmessage_send", "Allows the client to send text messages to himself"), + make_shared(PermissionType::i_client_private_textmessage_power, PermissionGroup::client_basic, "i_client_private_textmessage_power", "Client private message power"), + make_shared(PermissionType::i_client_needed_private_textmessage_power, PermissionGroup::client_basic, "i_client_needed_private_textmessage_power", "Needed client private message power"), + make_shared(PermissionType::b_client_server_textmessage_send, PermissionGroup::client_basic, "b_client_server_textmessage_send", "Send text messages to virtual server"), + make_shared(PermissionType::b_client_channel_textmessage_send, PermissionGroup::client_basic, "b_client_channel_textmessage_send", "Send text messages to channel"), + make_shared(PermissionType::b_client_offline_textmessage_send, PermissionGroup::client_basic, "b_client_offline_textmessage_send", "Send offline messages to clients"), + make_shared(PermissionType::i_client_talk_power, PermissionGroup::client_basic, "i_client_talk_power", "Client talk power"), + make_shared(PermissionType::i_client_needed_talk_power, PermissionGroup::client_basic, "i_client_needed_talk_power", "Needed client talk power"), + make_shared(PermissionType::i_client_poke_power, PermissionGroup::client_basic, "i_client_poke_power", "Client poke power"), + make_shared(PermissionType::i_client_needed_poke_power, PermissionGroup::client_basic, "i_client_needed_poke_power", "Needed client poke power"), + make_shared(PermissionType::b_client_set_flag_talker, PermissionGroup::client_basic, "b_client_set_flag_talker", "Set the talker flag for clients and allow them to speak"), + make_shared(PermissionType::i_client_whisper_power, PermissionGroup::client_basic, "i_client_whisper_power", "Client whisper power"), + make_shared(PermissionType::i_client_needed_whisper_power, PermissionGroup::client_basic, "i_client_needed_whisper_power", "Client needed whisper power"), + make_shared(PermissionType::b_client_modify_description, PermissionGroup::client_modify, "b_client_modify_description", "Edit a clients description"), + make_shared(PermissionType::b_client_modify_own_description, PermissionGroup::client_modify, "b_client_modify_own_description", "Allow client to edit own description"), + make_shared(PermissionType::b_client_modify_dbproperties, PermissionGroup::client_modify, "b_client_modify_dbproperties", "Edit a clients properties in the sql"), + make_shared(PermissionType::b_client_delete_dbproperties, PermissionGroup::client_modify, "b_client_delete_dbproperties", "Delete a clients properties in the sql"), + make_shared(PermissionType::b_client_create_modify_serverquery_login, PermissionGroup::client_modify, "b_client_create_modify_serverquery_login", "Create or modify own ServerQuery account"), + + make_shared(PermissionType::b_client_query_create, PermissionGroup::client_modify, "b_client_query_create", "Create your own ServerQuery account"), + make_shared(PermissionType::b_client_query_list, PermissionGroup::client_modify, "b_client_query_list", "List all ServerQuery accounts"), + make_shared(PermissionType::b_client_query_list_own, PermissionGroup::client_modify, "b_client_query_list_own", "List all own ServerQuery accounts"), + make_shared(PermissionType::b_client_query_rename, PermissionGroup::client_modify, "b_client_query_rename", "Rename a ServerQuery account"), + make_shared(PermissionType::b_client_query_rename_own, PermissionGroup::client_modify, "b_client_query_rename_own", "Rename the own ServerQuery account"), + make_shared(PermissionType::b_client_query_change_password, PermissionGroup::client_modify, "b_client_query_change_password", "Change a server query accounts password"), + make_shared(PermissionType::b_client_query_change_own_password, PermissionGroup::client_modify, "b_client_query_change_own_password", "Change a query accounts own password"), + make_shared(PermissionType::b_client_query_change_password_global, PermissionGroup::client_modify, "b_client_query_change_password_global", "Change a global query accounts own password"), + make_shared(PermissionType::b_client_query_delete, PermissionGroup::client_modify, "b_client_query_delete", "Delete a query accounts password"), + make_shared(PermissionType::b_client_query_delete_own, PermissionGroup::client_modify, "b_client_query_delete_own", "Delete own query accounts password"), + +#ifndef LAGENCY + make_shared(PermissionType::b_client_music_create_temporary, PermissionGroup::client, "b_client_music_create_temporary", "Permission to create a music bot"), + make_shared(PermissionType::b_client_music_create_semi_permanent, PermissionGroup::client, "b_client_music_create_semi_permanent", "Allows the client to create semi permanent music bots"), + make_shared(PermissionType::b_client_music_create_permanent, PermissionGroup::client, "b_client_music_create_permanent", "Allows the client to create permanent music bots"), + make_shared(PermissionType::b_client_music_modify_temporary, PermissionGroup::client, "b_client_music_modify_temporary", "Permission to make a music bot temporary"), + make_shared(PermissionType::b_client_music_modify_semi_permanent, PermissionGroup::client, "b_client_music_modify_semi_permanent", "Allows the client to make a bot semi permanent"), + make_shared(PermissionType::b_client_music_modify_permanent, PermissionGroup::client, "b_client_music_modify_permanent", "Allows the client to make a bot permanent"), + make_shared(PermissionType::i_client_music_create_modify_max_volume, PermissionGroup::client, "i_client_music_create_modify_max_volume", "Sets the max allowed music bot volume"), + make_shared(PermissionType::i_client_music_limit, PermissionGroup::client, "i_client_music_limit", "The limit of music bots bound to this client"), + make_shared(PermissionType::i_client_music_delete_power, PermissionGroup::client, "i_client_music_delete_power", "Power to delete the music bot"), + make_shared(PermissionType::i_client_music_needed_delete_power, PermissionGroup::client, "i_client_music_needed_delete_power", "Required power to delete the music bot"), + make_shared(PermissionType::i_client_music_play_power, PermissionGroup::client, "i_client_music_play_power", "Power to play music"), + make_shared(PermissionType::i_client_music_needed_modify_power, PermissionGroup::client, "i_client_music_needed_modify_power", "Required power to modify the bot settings"), + make_shared(PermissionType::i_client_music_modify_power, PermissionGroup::client, "i_client_music_modify_power", "Power to modify the music bot settings"), + make_shared(PermissionType::i_client_music_needed_play_power, PermissionGroup::client, "i_client_music_needed_play_power", "Required power to play music"), + make_shared(PermissionType::i_client_music_rename_power, PermissionGroup::client, "i_client_music_rename_power", "Power to rename the bot"), + make_shared(PermissionType::i_client_music_needed_rename_power, PermissionGroup::client, "i_client_music_needed_rename_power", "The required rename power for a music bot"), + + make_shared(PermissionType::b_playlist_create, PermissionGroup::client, "b_playlist_create", "Allows the client to create playlists"), + make_shared(PermissionType::i_playlist_view_power, PermissionGroup::client, "i_playlist_view_power", "Power to see a playlist, and their songs"), + make_shared(PermissionType::i_playlist_needed_view_power, PermissionGroup::client, "i_playlist_needed_view_power", "Needed power to see a playlist, and their songs"), + make_shared(PermissionType::i_playlist_modify_power, PermissionGroup::client, "i_playlist_modify_power", "Power to modify the playlist properties"), + make_shared(PermissionType::i_playlist_needed_modify_power, PermissionGroup::client, "i_playlist_needed_modify_power", "Needed power to modify the playlist properties"), + make_shared(PermissionType::i_playlist_permission_modify_power, PermissionGroup::client, "i_playlist_permission_modify_power", "Power to modify the playlist permissions"), + make_shared(PermissionType::i_playlist_needed_permission_modify_power, PermissionGroup::client, "i_playlist_needed_permission_modify_power", "Needed power to modify the playlist permissions"), + make_shared(PermissionType::i_playlist_delete_power, PermissionGroup::client, "i_playlist_delete_power", "Power to delete the playlist"), + make_shared(PermissionType::i_playlist_needed_delete_power, PermissionGroup::client, "i_playlist_needed_delete_power", "Needed power to delete the playlist"), + + make_shared(PermissionType::i_playlist_song_add_power, PermissionGroup::client, "i_playlist_song_add_power", "Power to add songs to a playlist"), + make_shared(PermissionType::i_playlist_song_needed_add_power, PermissionGroup::client, "i_playlist_song_needed_add_power", "Needed power to add songs to a playlist"), + make_shared(PermissionType::i_playlist_song_remove_power, PermissionGroup::client, "i_playlist_song_remove_power", "Power to remove songs from a playlist"), + make_shared(PermissionType::i_playlist_song_needed_remove_power, PermissionGroup::client, "i_playlist_song_needed_remove_power", "Needed power to remove songs from a playlist"), + make_shared(PermissionType::i_playlist_song_move_power, PermissionGroup::client, "i_playlist_song_move_power", "Power to move songs witin a playlist"), + make_shared(PermissionType::i_playlist_song_needed_move_power, PermissionGroup::client, "i_playlist_song_needed_move_power", "Needed power to move songs within a playlist"), + + make_shared(PermissionType::b_client_use_bbcode_any, PermissionGroup::client, "b_client_use_bbcode_any", "Allows the client to use any bbcodes"), + make_shared(PermissionType::b_client_use_bbcode_image, PermissionGroup::client, "b_client_use_bbcode_image", "Allows the client to use img bbcode"), + make_shared(PermissionType::b_client_use_bbcode_url, PermissionGroup::client, "b_client_use_bbcode_url", "Allows the client to use url bbcode"), +#endif + make_shared(PermissionType::b_ft_ignore_password, PermissionGroup::ft, "b_ft_ignore_password", "Browse files without channel password"), + make_shared(PermissionType::b_ft_transfer_list, PermissionGroup::ft, "b_ft_transfer_list", "Retrieve list of running filetransfers"), + make_shared(PermissionType::i_ft_file_upload_power, PermissionGroup::ft, "i_ft_file_upload_power", "File upload power"), + make_shared(PermissionType::i_ft_needed_file_upload_power, PermissionGroup::ft, "i_ft_needed_file_upload_power", "Needed file upload power"), + make_shared(PermissionType::i_ft_file_download_power, PermissionGroup::ft, "i_ft_file_download_power", "File download power"), + make_shared(PermissionType::i_ft_needed_file_download_power, PermissionGroup::ft, "i_ft_needed_file_download_power", "Needed file download power"), + make_shared(PermissionType::i_ft_file_delete_power, PermissionGroup::ft, "i_ft_file_delete_power", "File delete power"), + make_shared(PermissionType::i_ft_needed_file_delete_power, PermissionGroup::ft, "i_ft_needed_file_delete_power", "Needed file delete power"), + make_shared(PermissionType::i_ft_file_rename_power, PermissionGroup::ft, "i_ft_file_rename_power", "File rename power"), + make_shared(PermissionType::i_ft_needed_file_rename_power, PermissionGroup::ft, "i_ft_needed_file_rename_power", "Needed file rename power"), + make_shared(PermissionType::i_ft_file_browse_power, PermissionGroup::ft, "i_ft_file_browse_power", "File browse power"), + make_shared(PermissionType::i_ft_needed_file_browse_power, PermissionGroup::ft, "i_ft_needed_file_browse_power", "Needed file browse power"), + make_shared(PermissionType::i_ft_directory_create_power, PermissionGroup::ft, "i_ft_directory_create_power", "Create directory power"), + make_shared(PermissionType::i_ft_needed_directory_create_power, PermissionGroup::ft, "i_ft_needed_directory_create_power", "Needed create directory power"), + make_shared(PermissionType::i_ft_quota_mb_download_per_client, PermissionGroup::ft, "i_ft_quota_mb_download_per_client", "Download quota per client in MByte"), + make_shared(PermissionType::i_ft_quota_mb_upload_per_client, PermissionGroup::ft, "i_ft_quota_mb_upload_per_client", "Upload quota per client in MByte") +}; + +deque ts::permission::neededPermissions = { + b_client_force_push_to_talk, + b_channel_join_ignore_password, + b_ft_ignore_password, + i_client_max_avatar_filesize, + i_client_max_channel_subscriptions, + i_permission_modify_power, + b_virtualserver_servergroup_permission_list, + b_virtualserver_client_permission_list, + b_virtualserver_channelgroup_permission_list, + b_virtualserver_channelclient_permission_list, + b_virtualserver_playlist_permission_list, + b_virtualserver_channelgroup_client_list, + b_client_permissionoverview_view, + b_client_ban_list, + b_client_ban_trigger_list, + b_client_complain_list, + b_client_complain_delete, + b_client_complain_delete_own, + b_virtualserver_log_view, + b_client_create_modify_serverquery_login, + b_virtualserver_connectioninfo_view, + b_client_modify_description, + b_client_server_textmessage_send, + b_client_channel_textmessage_send, +#ifdef LAGENCY +i_group_modify_power, + i_group_member_add_power, + i_group_member_remove_power, +#else + i_displayed_group_modify_power, + i_displayed_group_member_add_power, + i_displayed_group_member_remove_power, + + i_server_group_modify_power, + i_server_group_member_add_power, + i_server_group_self_add_power, + i_server_group_member_remove_power, + i_server_group_self_remove_power, + + i_channel_group_modify_power, + i_channel_group_member_add_power, + i_channel_group_self_add_power, + i_channel_group_member_remove_power, + i_channel_group_self_remove_power, +#endif + i_ft_file_browse_power, + b_permission_modify_power_ignore, + b_virtualserver_modify_hostmessage, + b_virtualserver_modify_ft_settings, + b_virtualserver_modify_default_musicgroup, + b_virtualserver_modify_default_servergroup, + b_virtualserver_modify_default_channelgroup, + b_virtualserver_modify_default_channeladmingroup, + b_virtualserver_modify_hostbanner, + b_virtualserver_modify_channel_forced_silence, + b_virtualserver_modify_needed_identity_security_level, + b_virtualserver_modify_name, + b_virtualserver_modify_welcomemessage, + b_virtualserver_modify_maxclients, + b_virtualserver_modify_password, + b_virtualserver_modify_complain, + b_virtualserver_modify_antiflood, + b_virtualserver_modify_ft_quotas, + b_virtualserver_modify_hostbutton, + b_virtualserver_modify_autostart, + b_virtualserver_modify_port, + b_virtualserver_modify_host, + b_virtualserver_modify_log_settings, + b_virtualserver_modify_priority_speaker_dimm_modificator, + b_virtualserver_modify_music_bot_limit, + b_virtualserver_modify_default_messages, + i_channel_create_modify_conversation_history_length, + b_channel_create_modify_conversation_history_unlimited, + b_channel_create_modify_conversation_private, + b_channel_modify_name, + b_channel_modify_password, + b_channel_modify_topic, + b_channel_modify_description, + b_channel_modify_codec, + b_channel_modify_codec_quality, + b_channel_modify_make_permanent, + b_channel_modify_make_semi_permanent, + b_channel_modify_make_temporary, + b_channel_modify_make_default, + b_channel_modify_maxclients, + b_channel_modify_maxfamilyclients, + b_channel_modify_sortorder, + b_channel_modify_needed_talk_power, + i_channel_permission_modify_power, + b_channel_create_child, + b_channel_create_permanent, + b_channel_create_temporary, + b_channel_create_with_topic, + b_channel_create_with_description, + b_channel_create_with_password, + b_channel_create_with_maxclients, + b_channel_create_with_maxfamilyclients, + b_channel_create_with_sortorder, + b_channel_create_with_default, + b_channel_create_modify_with_codec_speex8, + b_channel_create_modify_with_codec_speex16, + b_channel_create_modify_with_codec_speex32, + b_channel_create_modify_with_codec_celtmono48, + b_channel_create_modify_with_codec_opusvoice, + b_channel_create_modify_with_codec_opusmusic, + i_channel_create_modify_with_codec_maxquality, + i_client_serverquery_view_power, + b_channel_create_semi_permanent, + b_serverinstance_modify_querygroup, + b_serverinstance_modify_templates, + b_virtualserver_channel_permission_list, + b_channel_delete_permanent, + b_channel_delete_semi_permanent, + b_channel_delete_temporary, + b_channel_delete_flag_force, + b_client_set_flag_talker, + b_channel_create_with_needed_talk_power, + b_virtualserver_token_list, + b_virtualserver_token_add, + b_virtualserver_token_use, + b_virtualserver_token_delete, + + /* ban functions */ + b_client_ban_create, + b_client_ban_create_global, + + b_client_ban_list_global, + b_client_ban_list, + + b_client_ban_edit, + b_client_ban_edit_global, + + b_client_ban_name, + b_client_ban_ip, + b_client_ban_hwid, + + b_virtualserver_servergroup_list, + b_virtualserver_servergroup_client_list, + b_virtualserver_channelgroup_list, + i_client_ban_max_bantime, + b_icon_manage, + i_max_icon_filesize, + i_max_playlist_size, + i_max_playlists, + + i_client_needed_whisper_power, + i_client_whisper_power, + + b_virtualserver_modify_icon_id, + b_client_modify_own_description, + b_client_offline_textmessage_send, + b_virtualserver_client_dblist, + b_virtualserver_modify_reserved_slots, + b_channel_conversation_message_delete, + i_channel_create_modify_with_codec_latency_factor_min, + b_channel_modify_codec_latency_factor, + b_channel_modify_make_codec_encrypted, + b_virtualserver_modify_codec_encryption_mode, + b_client_use_channel_commander, + b_virtualserver_servergroup_create, + b_virtualserver_channelgroup_create, + b_client_permissionoverview_own, + i_ft_quota_mb_upload_per_client, + i_ft_quota_mb_download_per_client, + b_virtualserver_modify_weblist, + b_virtualserver_modify_country_code, + b_virtualserver_channelgroup_delete, + b_virtualserver_servergroup_delete, + b_client_ban_delete, + b_client_ban_delete_own, + b_client_ban_delete_global, + b_client_ban_delete_own_global, + b_virtualserver_modify_temporary_passwords, + b_virtualserver_modify_temporary_passwords_own, + b_client_request_talker, + b_client_avatar_delete_other, + b_channel_create_modify_with_force_password, + b_channel_create_private, + b_channel_join_ignore_maxclients, + b_virtualserver_modify_channel_temp_delete_delay_default, + i_channel_create_modify_with_temp_delete_delay, + b_channel_modify_temp_delete_delay, + i_client_poke_power, + + b_client_remoteaddress_view, + + i_client_music_play_power, + i_client_music_modify_power, + i_client_music_rename_power, + b_client_music_create_temporary, + b_client_music_create_semi_permanent, + b_client_music_create_permanent, + b_client_music_modify_temporary, + b_client_music_modify_semi_permanent, + b_client_music_modify_permanent, + i_client_music_create_modify_max_volume, + + b_playlist_create, + i_playlist_view_power, + i_playlist_modify_power, + i_playlist_permission_modify_power, + i_playlist_delete_power, + + i_playlist_song_add_power, + i_playlist_song_remove_power, + i_playlist_song_move_power, + + b_client_query_create, + b_client_query_list, + b_client_query_list_own, + b_client_query_rename, + b_client_query_rename_own, + b_client_query_change_password, + b_client_query_change_own_password, + b_client_query_change_password_global, + b_client_query_delete, + b_client_query_delete_own, +}; + +std::deque permission::availableGroups = { + global, + global_info, + global_vsmanage, + global_admin, + global_settings, + vs, + vs_info, + vs_admin, + vs_settings, + channel, + channel_info, + channel_create, + channel_modify, + channel_delete, + channel_access, + group, + group_info, + group_create, + group_modify, + group_delete, + client, + client_info, + client_admin, + client_basic, + client_modify, + ft +}; + +std::shared_ptr PermissionTypeEntry::unknown = make_shared(PermissionType::unknown, PermissionGroup::global, "unknown", "unknown"); + +vector> permission_id_map; +void permission::setup_permission_resolve() { + permission_id_map.resize(permission::permission_id_max); + + for(auto& permission : availablePermissions) { + if(!permission->clientSupported || permission->type < 0 || permission->type > permission::permission_id_max) + continue; + permission_id_map[permission->type] = permission; + } + + /* fix "holes" as well set the permission id 0 (unknown) */ + for(auto& permission : permission_id_map) { + if(permission) + continue; + permission = PermissionTypeEntry::unknown; + } +} + +std::shared_ptr permission::resolvePermissionData(PermissionType type) { + if((type & PERM_ID_GRANT) > 0) + type &= ~PERM_ID_GRANT; + + assert(!permission_id_map.empty()); + if(type < 0 || type >= permission::permission_id_max) + return PermissionTypeEntry::unknown; + + return permission_id_map[type]; +} + +std::shared_ptr permission::resolvePermissionData(const std::string& name) { + for(auto& elm : availablePermissions) + if(elm->name == name || elm->grant_name == name) + return elm; + return PermissionTypeEntry::unknown; +} + +ChannelId Permission::channelId() { + return this->channel ? this->channel->channelId() : 0; +} + +PermissionManager::PermissionManager() { + memtrack::allocated(this); +} +PermissionManager::~PermissionManager() { + memtrack::freed(this); +} + +std::shared_ptr PermissionManager::registerPermission(ts::permission::PermissionType type, + ts::permission::PermissionValue val, + const std::shared_ptr &channel, + uint16_t flag) { + return this->registerPermission(resolvePermissionData(type), val, channel, flag); +} + +std::shared_ptr PermissionManager::registerPermission(const std::shared_ptr& type, PermissionValue value, const std::shared_ptr& channel, uint16_t flagMask) { + { + auto found = getPermission(type->type, channel, false); + if(!found.empty()) return found.front(); + } + auto permission = std::make_shared(type, value, permNotGranted, flagMask, channel); + this->permissions.push_back(permission); + for(auto& elm : this->updateHandler) + elm(permission); + return permission; +} + +bool PermissionManager::hasPermission(PermissionType type, const std::shared_ptr& channel, bool testGlobal) { + return !this->getPermission(type, channel, testGlobal).empty(); +} + +bool PermissionManager::setPermission(PermissionType type, PermissionValue value, const std::shared_ptr& channel) { + auto list = this->getPermission(type, channel, false); + if(list.empty()) + list.push_back(registerPermission(type, value, channel, PERM_FLAG_PUBLIC)); + + list.front()->value = value; + for(const auto& elm : this->updateHandler) + elm(list.front()); + + return true; +} + +bool PermissionManager::setPermission(ts::permission::PermissionType type, ts::permission::PermissionValue value, const std::shared_ptr &channel, bool negated, bool skiped) { + auto list = this->getPermission(type, channel, false); + if(list.empty()) + list.push_back(registerPermission(type, value, channel, PERM_FLAG_PUBLIC)); + + list.front()->value = value; + list.front()->flag_negate = negated; + list.front()->flag_skip = skiped; + + for(const auto& elm : this->updateHandler) + elm(list.front()); + + return true; +} + +bool PermissionManager::setPermissionGranted(PermissionType type, PermissionValue value, const std::shared_ptr& channel) { + auto list = this->getPermission(type, channel, false); + if(list.empty()) + list.push_back(registerPermission(type, permNotGranted, channel, PERM_FLAG_PUBLIC)); + list.front()->granted = value; + + for(auto& elm : this->updateHandler) + elm(list.front()); + + return true; +} + +void PermissionManager::deletePermission(PermissionType type, const std::shared_ptr& channel) { + auto list = getPermission(type, channel, false); + if(list.empty()) return; + list.front()->value = permNotGranted; + //list.front()->deleted = true; + //this->permissions.erase(std::find(this->permissions.begin(), this->permissions.end(), list.front())); + + for(auto& e : this->updateHandler) + e(list.front()); +} + +std::deque> PermissionManager::getPermission(PermissionType type, const std::shared_ptr& channel, bool testGlobal) { + std::deque> res; + + std::shared_ptr channel_permission; + std::shared_ptr global_permission; + for(const auto &perm : this->permissions) + if(perm->type->type == type) { + if(perm->channel == channel) + channel_permission = perm; + else if(!perm->channel && testGlobal) + global_permission = perm; + } + if(channel_permission) res.push_back(channel_permission); + if(global_permission) res.push_back(global_permission); + return res; +} + +std::vector> PermissionManager::listPermissions(uint16_t flags) { + vector> result; + for(const auto &perm : this->permissions) + if((perm->flagMask & flags) > 0 || true) //FIXME? + result.push_back(perm); + return result; +} + +std::vector> PermissionManager::allPermissions() { + return std::vector>(this->permissions.begin(), this->permissions.end()); +} + +std::deque> PermissionManager::all_channel_specific_permissions() { + std::deque> result; + + for(const auto& permission : this->permissions) + if(permission->channel) + result.push_back(permission); + + return result; +} + +std::deque> PermissionManager::all_channel_unspecific_permissions() { + std::deque> result; + + for(const auto& permission : this->permissions) + if(!permission->channel) + result.push_back(permission); + + return result; +} + +teamspeak::MapType teamspeak::unmapping; +teamspeak::MapType teamspeak::mapping; + +teamspeak::MapType build_mapping(){ + return { + {teamspeak::GENERAL, { + {"b_virtualserver_modify_port", {"b_virtualserver_modify_port", "b_virtualserver_modify_host"}}, + {"i_client_max_clones_uid", {"i_client_max_clones_uid", "i_client_max_clones_ip", "i_client_max_clones_hwid"}}, + {"b_client_ignore_bans", {"b_client_ignore_bans", "b_client_ignore_vpn"}}, + {"b_client_ban_list", {"b_client_ban_list", "b_client_ban_list_global"}}, + + //Permissions which TeaSpeak has but TeamSpeak not + {"", {"b_virtualserver_modify_music_bot_limit"}}, + {"", {"b_client_music_channel_list"}}, + {"", {"b_client_music_server_list"}}, + {"", {"i_client_music_info"}}, + {"", {"i_client_music_needed_info"}}, + + {"", {"b_client_ban_edit"}}, + {"", {"b_client_ban_edit_global"}}, + {"", {"b_client_ban_create_global"}}, + {"", {"b_client_ban_delete_own_global"}}, + {"", {"b_client_ban_delete_global"}}, + + {"", {"b_client_even_textmessage_send"}}, + {"", {"b_client_enforce_valid_hwid"}}, + {"", {"b_client_allow_invalid_packet"}}, + {"", {"b_client_allow_invalid_badges"}}, + + {"", {"b_client_music_create"}}, + {"", {"b_client_music_delete_own"}}, + + {"", {"i_client_music_limit"}}, + {"", {"i_client_music_needed_delete_power"}}, + {"", {"i_client_music_delete_power"}}, + {"", {"i_client_music_play_power"}}, + {"", {"i_client_music_needed_play_power"}}, + {"", {"i_client_music_rename_power"}}, + {"", {"i_client_music_needed_rename_power"}}, + + {"", {"b_client_use_bbcode_any"}}, + {"", {"b_client_use_bbcode_url"}}, + {"", {"b_client_use_bbcode_image"}}, + + {"", {"b_channel_ignore_view_power"}}, + {"", {"i_channel_view_power"}}, + {"", {"i_channel_needed_view_power"}}, + + {"", {"i_client_max_channels"}}, + {"", {"i_client_max_temporary_channels"}}, + {"", {"i_client_max_semi_channels"}}, + {"", {"i_client_max_permanent_channels"}}, + + {"", {"b_virtualserver_modify_default_messages"}}, + + {"b_client_ban_list", {"b_client_ban_list", "b_client_ban_trigger_list"}}, + {"b_virtualserver_select", {"b_virtualserver_select", "b_virtualserver_select_godmode"}}, + {"b_virtualserver_modify_default_servergroup", {"b_virtualserver_modify_default_servergroup", "b_virtualserver_modify_default_musicgroup"}}, + + {"b_client_ban_create", {"b_client_ban_create", "b_client_ban_name", "b_client_ban_ip", "b_client_ban_hwid"}} + }, + }, + {teamspeak::SERVER, { + {"i_group_modify_power", {"i_server_group_modify_power", "i_channel_group_modify_power", "i_displayed_group_modify_power"}}, + {"i_group_member_add_power", {"i_server_group_member_add_power", "i_channel_group_member_add_power", "i_displayed_group_member_add_power"}}, + {"i_group_member_remove_power", {"i_server_group_member_remove_power", "i_channel_group_member_remove_power", "i_displayed_group_member_remove_power"}}, + {"i_group_needed_modify_power", {"i_server_group_needed_modify_power", "i_channel_group_needed_modify_power", "i_displayed_group_needed_modify_power"}}, + {"i_group_needed_member_add_power", {"i_server_group_needed_member_add_power", "i_channel_group_needed_member_add_power", "i_displayed_group_needed_member_add_power"}}, + {"i_group_needed_member_remove_power", {"i_server_group_needed_member_remove_power", "i_channel_group_needed_member_remove_power", "i_displayed_group_needed_member_remove_power"}}, + }, + }, + {teamspeak::CLIENT, { + {"i_group_modify_power", {"i_server_group_modify_power", "i_channel_group_modify_power", "i_displayed_group_modify_power"}}, + {"i_group_member_add_power", {"i_server_group_member_add_power", "i_channel_group_member_add_power", "i_displayed_group_member_add_power"}}, + {"i_group_member_remove_power", {"i_server_group_member_remove_power", "i_channel_group_member_remove_power", "i_displayed_group_member_remove_power"}}, + {"i_group_needed_modify_power", {"i_server_group_needed_modify_power", "i_channel_group_needed_modify_power", "i_displayed_group_needed_modify_power"}}, + {"i_group_needed_member_add_power", {"i_server_group_needed_member_add_power", "i_channel_group_needed_member_add_power", "i_displayed_group_needed_member_add_power"}}, + {"i_group_needed_member_remove_power", {"i_server_group_needed_member_remove_power", "i_channel_group_needed_member_remove_power", "i_displayed_group_needed_member_remove_power"}}, + }, + }, + {teamspeak::CHANNEL, { + {"i_group_modify_power", {"i_channel_group_modify_power", "i_displayed_group_modify_power"}}, + {"i_group_member_add_power", {"i_channel_group_member_add_power", "i_displayed_group_member_add_power"}}, + {"i_group_member_remove_power", {"i_channel_group_member_remove_power", "i_displayed_group_member_remove_power"}}, + {"i_group_needed_modify_power", {"i_channel_group_needed_modify_power", "i_displayed_group_needed_modify_power"}}, + {"i_group_needed_member_add_power", {"i_channel_group_needed_member_add_power", "i_displayed_group_needed_member_add_power"}}, + {"i_group_needed_member_remove_power", {"i_channel_group_needed_member_remove_power", "i_displayed_group_needed_member_remove_power"}}, + }, + }, + }; +}; + +inline teamspeak::MapType build_unmapping() { + teamspeak::MapType result; + + for(const auto& map : teamspeak::mapping) { + for(const auto& entry : map.second) { + for(const auto& key : entry.second) { + auto& m = result[map.first]; + auto it = m.find(key); + if(it == m.end()) + m.insert({key, {}}); + it = m.find(key); + if(!entry.first.empty()) it->second.push_back(entry.first); + } + } + } + + return result; +}; + +inline void init_mapping() { + if(teamspeak::mapping.empty()) teamspeak::mapping = build_mapping(); + if(teamspeak::unmapping.empty()) teamspeak::unmapping = build_unmapping(); +} + +template +inline deque operator+(const deque& a, const deque& b) { + deque result; + result.insert(result.end(), a.begin(), a.end()); + result.insert(result.end(), b.begin(), b.end()); + return result; +} + +inline deque mmget(teamspeak::MapType& map, teamspeak::GroupType type, const std::string& key) { + return map[type].count(key) > 0 ? map[type].find(key)->second : deque{}; +} + +inline std::deque map_entry(std::string key, teamspeak::GroupType type, teamspeak::MapType& map_table) { + init_mapping(); + + if(key.find("_needed_modify_power_") == 1) { + key = key.substr(strlen("x_needed_modify_power_")); + + deque result; + if(map_table[type].count("i_" + key) > 0 || map_table[teamspeak::GroupType::GENERAL].count("i_" + key) > 0) result = mmget(map_table, type, "i_" + key) + mmget(map_table, teamspeak::GroupType::GENERAL, "i_" + key); + else if(map_table[type].count("b_" + key) > 0 || map_table[teamspeak::GroupType::GENERAL].count("b_" + key) > 0) result = mmget(map_table, type, "b_" + key) + mmget(map_table, teamspeak::GroupType::GENERAL, "b_" + key); + else result = {"x_" + key}; + + for(auto& entry : result) + entry = "i_needed_modify_power_" + entry.substr(2); + return result; + } + if(map_table[type].count(key) > 0 || map_table[teamspeak::GroupType::GENERAL].count(key) > 0) return mmget(map_table, type, key) + mmget(map_table, teamspeak::GroupType::GENERAL, key); + return {key}; +} + +std::deque teamspeak::map_key(std::string key, GroupType type) { + return map_entry(key, type, teamspeak::mapping); +} + +std::deque teamspeak::unmap_key(std::string key, GroupType type) { + return map_entry(key, type, teamspeak::unmapping); +} + + +#define AQB(name) \ +{update::QUERY_ADMIN, {name, 1, 100, false, false}}, \ +{update::SERVER_ADMIN, {name, 1, 75, false, false}}, \ + +#define AQBG(name) \ +{update::QUERY_ADMIN, {name, permNotGranted, 100, false, false}}, \ +{update::SERVER_ADMIN, {name, permNotGranted, 75, false, false}}, \ + +#define AQI(name) \ +{update::QUERY_ADMIN, {name, 100, 100, false, false}}, \ +{update::SERVER_ADMIN, {name, 75, 75, false, false}}, \ + +#define AQIG(name) \ +{update::QUERY_ADMIN, {name, permNotGranted, 100, false, false}}, \ +{update::SERVER_ADMIN, {name, permNotGranted, 75, false, false}}, \ + +deque update::migrate = { + AQB("b_virtualserver_modify_music_bot_limit") + {update::QUERY_ADMIN, {"b_client_music_channel_list", 1, 100, false, false}}, + {update::SERVER_ADMIN, {"b_client_music_channel_list", 1, 75, false, false}}, + {update::CHANNEL_ADMIN, {"b_client_music_channel_list", 1, 75, false, false}}, + + {update::QUERY_ADMIN, {"b_client_music_server_list", 1, 100, false, false}}, + {update::SERVER_ADMIN, {"b_client_music_server_list", 1, 75, false, false}}, + + {update::QUERY_ADMIN, {"i_client_music_info", 100, 100, false, false}}, + {update::SERVER_ADMIN, {"i_client_music_info", 75, 75, false, false}}, + + {update::QUERY_ADMIN, {"i_client_music_needed_info", permNotGranted, 100, false, false}}, + {update::SERVER_ADMIN, {"i_client_music_needed_info", permNotGranted, 75, false, false}}, + + {update::QUERY_ADMIN, {"b_client_ban_list_global", 1, 100, false, false}}, + {update::SERVER_ADMIN, {"b_client_ban_list_global", 1, 75, false, false}}, + + {update::QUERY_ADMIN, {"b_client_ban_edit", 1, 100, false, false}}, + {update::SERVER_ADMIN, {"b_client_ban_edit", 1, 75, false, false}}, + + {update::QUERY_ADMIN, {"b_client_ban_create_global", 1, 100, false, false}}, + {update::QUERY_ADMIN, {"b_client_ban_edit_global", 1, 100, false, false}}, + {update::QUERY_ADMIN, {"b_client_ban_delete_own_global", 1, 100, false, false}}, + {update::QUERY_ADMIN, {"b_client_ban_delete_global", 1, 100, false, false}}, + + AQB("b_client_even_textmessage_send") + AQBG("b_client_enforce_valid_hwid") + AQB("b_client_allow_invalid_packet") + AQB("b_client_allow_invalid_badges") + + AQB("b_client_music_create") + AQB("b_client_music_delete_own") + + AQI("i_client_music_limit") + AQIG("i_client_music_needed_delete_power") + AQI("i_client_music_delete_power") + AQI("i_client_music_play_power") + AQIG("i_client_music_needed_play_power") + AQI("i_client_music_rename_power") + AQIG("i_client_music_needed_rename_power") + + + AQB("b_client_use_bbcode_any") + AQB("b_client_use_bbcode_url") + AQB("b_client_use_bbcode_image") + + {update::QUERY_ADMIN, {"b_channel_ignore_view_power", 1, 100, false, false}}, + AQI("i_channel_view_power") + AQIG("i_channel_needed_view_power") + + AQB("b_client_ignore_vpn") + + AQIG("i_client_max_channels") + AQIG("i_client_max_temporary_channels") + AQIG("i_client_max_semi_channels") + AQIG("i_client_max_permanent_channels") + {update::SERVER_NORMAL, {"i_client_max_channels", 2, permNotGranted, false, false}}, + {update::SERVER_GUEST, {"i_client_max_channels", 1, permNotGranted, false, false}}, + + AQB("b_virtualserver_modify_default_messages") + AQB("b_virtualserver_modify_default_musicgroup") + AQB("b_channel_ignore_join_power") + AQB("b_virtualserver_select_godmode") + AQB("b_client_ban_trigger_list") +}; + +v2::PermissionManager::PermissionManager() { + memset(this->block_use_count, 0, sizeof(this->block_use_count)); + memset(this->block_containers, 0, sizeof(this->block_containers)); +} + +v2::PermissionManager::~PermissionManager() { + for(auto& block : this->block_containers) + delete block; +} + +void v2::PermissionManager::load_permission(const ts::permission::PermissionType &permission, const ts::permission::v2::PermissionValues &values, bool flag_skip, bool flag_negate, bool flag_value, bool flag_grant) { + if(permission < 0 || permission >= PermissionType::permission_id_max) + return; + + const auto block = this->calculate_block(permission); + this->ref_allocate_block(block); + + auto& data = this->block_containers[block]->permissions[this->calculate_block_index(permission)]; + data.values = values; + data.flags.database_reference = true; + data.flags.skip = flag_skip; + data.flags.negate = flag_negate; + data.flags.value_set = flag_value; + data.flags.grant_set = flag_grant; + this->unref_block(block); +} + +void v2::PermissionManager::load_permission(const ts::permission::PermissionType &permission, const ts::permission::v2::PermissionValues &values, ChannelId channel_id, bool flag_skip, bool flag_negate, bool flag_value, bool flag_grant) { + if(permission < 0 || permission >= PermissionType::permission_id_max) + return; + + unique_lock channel_perm_lock(this->channel_list_lock); + ChannelPermissionContainer* permission_container = nullptr; + for(auto& entry : this->_channel_permissions) + if(entry->permission == permission && entry->channel_id == channel_id) { + permission_container = &*entry; + break; + } + + if(!permission_container) { + auto container = make_unique(); + container->permission = permission; + container->channel_id = channel_id; + permission_container = &*container; + this->_channel_permissions.push_back(std::move(container)); + + /* now set the channel flag for that permission */ + const auto block = this->calculate_block(permission); + this->ref_allocate_block(block); + + auto& data = this->block_containers[block]->permissions[this->calculate_block_index(permission)]; + data.flags.channel_specific = true; + this->unref_block(block); + } + + permission_container->values = values; + permission_container->flags.database_reference = true; + permission_container->flags.skip = flag_skip; + permission_container->flags.negate = flag_negate; + permission_container->flags.value_set = flag_value; + permission_container->flags.grant_set = flag_grant; +} + +const v2::PermissionFlags v2::PermissionManager::permission_flags(const ts::permission::PermissionType &permission) { + if(permission < 0 || permission >= PermissionType::permission_id_max) + return empty_flags; + + const auto block = this->calculate_block(permission); + if(!this->ref_block(block)) + return empty_flags; + + PermissionFlags result{this->block_containers[block]->permissions[this->calculate_block_index(permission)].flags}; + this->unref_block(block); + return result; +} + +const v2::PermissionValues v2::PermissionManager::permission_values(const ts::permission::PermissionType &permission) { + if(permission < 0 || permission >= PermissionType::permission_id_max) + return v2::empty_permission_values; + + const auto block = this->calculate_block(permission); + if(!this->ref_block(block)) + return v2::empty_permission_values; /* TODO: may consider to throw an exception because the existence should be checked by getting the permission flags */ + + v2::PermissionValues data{this->block_containers[block]->permissions[this->calculate_block_index(permission)].values}; + this->unref_block(block); + return data; +} + +const v2::PermissionFlaggedValue v2::PermissionManager::permission_value_flagged(const ts::permission::PermissionType &permission) { + if(permission < 0 || permission >= PermissionType::permission_id_max) + return v2::empty_permission_flagged_value; + + const auto block = this->calculate_block(permission); + if(!this->ref_block(block)) + return v2::empty_permission_flagged_value; + + auto& data = this->block_containers[block]->permissions[this->calculate_block_index(permission)]; + v2::PermissionFlaggedValue result{data.values.value, data.flags.value_set}; + this->unref_block(block); + return result; +} + +const v2::PermissionFlaggedValue v2::PermissionManager::permission_granted_flagged(const ts::permission::PermissionType &permission) { + if(permission < 0 || permission >= PermissionType::permission_id_max) + return v2::empty_permission_flagged_value; + + const auto block = this->calculate_block(permission); + if(!this->ref_block(block)) + return v2::empty_permission_flagged_value; + + auto& data = this->block_containers[block]->permissions[this->calculate_block_index(permission)]; + v2::PermissionFlaggedValue result{data.values.grant, data.flags.grant_set}; + this->unref_block(block); + return result; +} + +const v2::PermissionContainer v2::PermissionManager::channel_permission(const PermissionType &permission, ts::ChannelId channel_id) { + if(permission < 0 || permission >= PermissionType::permission_id_max) + return empty_channel_permission; + + shared_lock channel_perm_lock(this->channel_list_lock); + for(auto& entry : this->_channel_permissions) + if(entry->permission == permission && entry->channel_id == channel_id) + return v2::PermissionContainer{entry->flags, entry->values}; + return empty_channel_permission; +} + +void v2::PermissionManager::set_permission(const PermissionType &permission, const v2::PermissionValues &values, const v2::PermissionUpdateType &action_value, const v2::PermissionUpdateType &action_grant, int flag_skip, int flag_negate) { + if(permission < 0 || permission >= PermissionType::permission_id_max) + return; + + const auto block = this->calculate_block(permission); + this->ref_allocate_block(block); + + auto& data = this->block_containers[block]->permissions[this->calculate_block_index(permission)]; + if(action_value == v2::PermissionUpdateType::set_value) { + data.flags.value_set = true; + data.flags.flag_value_update = true; + data.values.value = values.value; + } else if(action_value == v2::PermissionUpdateType::delete_value) { + data.flags.value_set = false; + data.flags.flag_value_update = true; + data.values.value = permNotGranted; /* required for the database else it does not "deletes" the value */ + } + + if(action_grant == v2::PermissionUpdateType::set_value) { + data.flags.grant_set = true; + data.flags.flag_grant_update = true; + data.values.grant = values.grant; + } else if(action_grant == v2::PermissionUpdateType::delete_value) { + data.flags.grant_set = false; + data.flags.flag_grant_update = true; + data.values.grant = permNotGranted; /* required for the database else it does not "deletes" the value */ + } + + if(flag_skip >= 0) { + data.flags.flag_value_update = true; + data.flags.skip = flag_skip == 1; + } + + if(flag_negate >= 0) { + data.flags.flag_value_update = true; + data.flags.negate = flag_negate == 1; + } + + this->unref_block(block); + this->trigger_db_update(); +} + +void v2::PermissionManager::set_channel_permission(const PermissionType &permission, ChannelId channel_id, const v2::PermissionValues &values, const v2::PermissionUpdateType &action_value, const v2::PermissionUpdateType &action_grant, int flag_skip, int flag_negate) { + if(permission < 0 || permission >= PermissionType::permission_id_max) + return; + + unique_lock channel_perm_lock(this->channel_list_lock); + ChannelPermissionContainer* permission_container = nullptr; + for(auto& entry : this->_channel_permissions) + if(entry->permission == permission && entry->channel_id == channel_id) { + permission_container = &*entry; + break; + } + + /* register a new permission if we have no permission already*/ + if(!permission_container || !permission_container->flags.permission_set()) { /* if the permission isn't set then we have to register it again */ + if(action_value != v2::PermissionUpdateType::set_value && action_grant == v2::PermissionUpdateType::set_value) { + return; /* we were never willing to set this permission */ + } + + if(!permission_container) { + auto container = make_unique(); + container->permission = permission; + container->channel_id = channel_id; + permission_container = &*container; + this->_channel_permissions.push_back(std::move(container)); + } + + /* now set the channel flag for that permission */ + const auto block = this->calculate_block(permission); + this->ref_allocate_block(block); + + auto& data = this->block_containers[block]->permissions[this->calculate_block_index(permission)]; + data.flags.channel_specific = true; + this->unref_block(block); + } + + if(action_value == v2::PermissionUpdateType::set_value) { + permission_container->flags.value_set = true; + permission_container->flags.flag_value_update = true; + permission_container->values.value = values.value; + } else if(action_value == v2::PermissionUpdateType::delete_value) { + permission_container->flags.value_set = false; + permission_container->flags.flag_value_update = true; + } + + if(action_grant == v2::PermissionUpdateType::set_value) { + permission_container->flags.grant_set = true; + permission_container->flags.flag_grant_update = true; + permission_container->values.grant = values.grant; + } else if(action_grant == v2::PermissionUpdateType::delete_value) { + permission_container->flags.grant_set = false; + permission_container->flags.flag_grant_update = true; + } + + if(flag_skip >= 0) { + permission_container->flags.flag_value_update = true; + permission_container->flags.skip = flag_skip == 1; + } + + if(flag_negate >= 0) { + permission_container->flags.flag_value_update = true; + permission_container->flags.negate = flag_negate == 1; + } + + if(!permission_container->flags.permission_set()) { /* unregister the permission again because its unset, we delete the channel permission as soon we've flushed the updates */ + auto other_channel_permission = std::find_if(this->_channel_permissions.begin(), this->_channel_permissions.end(), [&](unique_ptr& perm) { return perm->permission == permission && perm->flags.permission_set(); }); + if(other_channel_permission == this->_channel_permissions.end()) { /* no more channel specific permissions c*/ + const auto block = this->calculate_block(permission); + if(this->ref_block(block)) { + this->block_containers[block]->permissions[this->calculate_block_index(permission)].flags.channel_specific = false; + this->unref_block(block); + } + } + } + this->trigger_db_update(); +} + +const std::vector> v2::PermissionManager::permissions() { + std::unique_lock use_lock(this->block_use_count_lock); + decltype(this->block_containers) block_containers; /* save the states/nullptr's */ + memcpy(block_containers, this->block_containers, sizeof(this->block_containers)); + size_t block_count = 0; + for(size_t index = 0; index < BULK_COUNT; index++) { + if(block_containers[index]) { + block_count++; + this->block_use_count[index]++; + } + } + use_lock.unlock(); + + vector> result; + result.reserve(block_count * PERMISSIONS_BULK_ENTRY_COUNT); + + for(size_t block_index = 0; block_index < BULK_COUNT; block_index++) { + auto& block = block_containers[block_index]; + if(!block) + continue; + + for(size_t permission_index = 0; permission_index < PERMISSIONS_BULK_ENTRY_COUNT; permission_index++) { + auto& permission = block->permissions[permission_index]; + if(!permission.flags.permission_set()) + continue; + + result.emplace_back((PermissionType) (block_index * PERMISSIONS_BULK_ENTRY_COUNT + permission_index), permission); + } + } + result.shrink_to_fit(); + + use_lock.lock(); + for(size_t index = 0; index < BULK_COUNT; index++) { + if(block_containers[index]) + this->block_use_count[index]--; + } + use_lock.unlock(); + + return result; +} + +const vector> v2::PermissionManager::channel_permissions(ts::ChannelId channel_id) { + shared_lock channel_perm_lock(this->channel_list_lock); + + vector> result; + for(auto& entry : this->_channel_permissions) + if((channel_id == entry->channel_id) && (entry->flags.value_set || entry->flags.grant_set)) + result.emplace_back(entry->permission, v2::PermissionContainer{entry->flags, entry->values}); + return result; +} + +const vector> v2::PermissionManager::channel_permissions() { + shared_lock channel_perm_lock(this->channel_list_lock); + + vector> result; + for(auto& entry : this->_channel_permissions) + if(entry->flags.value_set || entry->flags.grant_set) + result.emplace_back(entry->permission, entry->channel_id, v2::PermissionContainer{entry->flags, entry->values}); + return result; +} + +const std::vector v2::PermissionManager::flush_db_updates() { + if(!this->requires_db_save) + return {}; + + this->requires_db_save = false; + std::vector result; + + { + lock_guard use_lock(this->block_use_count_lock); + size_t block_count = 0; + for (auto &block_container : block_containers) + if (block_container) + block_count++; + result.reserve(block_count * PERMISSIONS_BULK_ENTRY_COUNT); + + for(size_t block_index = 0; block_index < BULK_COUNT; block_index++) { + auto& block = block_containers[block_index]; + if(!block) + continue; + + for(size_t permission_index = 0; permission_index < PERMISSIONS_BULK_ENTRY_COUNT; permission_index++) { + auto& permission = block->permissions[permission_index]; + + if(!permission.flags.flag_value_update && !permission.flags.flag_grant_update) + continue; + + /* we only need an update it the permission has a DB reference or we will set the permission */ + if(permission.flags.database_reference || permission.flags.permission_set()) { + /* + PermissionType permission; + ChannelId channel_id; + + PermissionValues values; + PermissionUpdateType update_value; + PermissionUpdateType update_grant; + + bool flag_db: 1; + bool flag_delete: 1; + bool flag_skip: 1; + bool flag_negate: 1; + */ + auto& entry = result.emplace_back(v2::PermissionDBUpdateEntry{ + (PermissionType) (block_index * PERMISSIONS_BULK_ENTRY_COUNT + permission_index), + (ChannelId) 0, + + permission.values, + (PermissionUpdateType) (permission.flags.flag_value_update ? (permission.flags.value_set ? PermissionUpdateType::set_value : PermissionUpdateType::delete_value) : PermissionUpdateType::do_nothing), + (PermissionUpdateType) (permission.flags.flag_grant_update ? (permission.flags.grant_set ? PermissionUpdateType::set_value : PermissionUpdateType::delete_value) : PermissionUpdateType::do_nothing), + + (bool) permission.flags.database_reference, + (bool) !permission.flags.permission_set(), /* db delete */ + (bool) permission.flags.skip, + (bool) permission.flags.negate + }); + + /* required for the database */ + if(!permission.flags.value_set) + entry.values.value = permNotGranted; + + if(!permission.flags.grant_set) + entry.values.grant = permNotGranted; + + permission.flags.database_reference = permission.flags.permission_set(); + } + + permission.flags.flag_value_update = false; + permission.flags.flag_grant_update = false; + } + } + } + { + lock_guard chanel_lock(this->channel_list_lock); + for(size_t index = 0; index < this->_channel_permissions.size(); index++) { + auto& permission = this->_channel_permissions[index]; + if(!permission->flags.flag_value_update && !permission->flags.flag_grant_update) + continue; + + + /* we only need an update it the permission has a DB reference or we will set the permission */ + if(permission->flags.database_reference || permission->flags.permission_set()) { + + auto& entry = result.emplace_back(v2::PermissionDBUpdateEntry{ + permission->permission, + permission->channel_id, + + permission->values, + (PermissionUpdateType) (permission->flags.flag_value_update ? (permission->flags.value_set ? PermissionUpdateType::set_value : PermissionUpdateType::delete_value) : PermissionUpdateType::do_nothing), + (PermissionUpdateType) (permission->flags.flag_grant_update ? (permission->flags.grant_set ? PermissionUpdateType::set_value : PermissionUpdateType::delete_value) : PermissionUpdateType::do_nothing), + + (bool) permission->flags.database_reference, + (bool) !permission->flags.permission_set(), /* db delete */ + (bool) permission->flags.skip, + (bool) permission->flags.negate + }); + + /* required for the database */ + if(!permission->flags.value_set) + entry.values.value = permNotGranted; + + if(!permission->flags.grant_set) + entry.values.grant = permNotGranted; + + permission->flags.database_reference = permission->flags.permission_set(); + } + + permission->flags.flag_value_update = false; + permission->flags.flag_grant_update = false; + if(!permission->flags.permission_set()) { + this->_channel_permissions.erase(this->_channel_permissions.begin() + index); + index--; + } + } + } + + return result; +} + +size_t v2::PermissionManager::used_memory() { + size_t result = sizeof(*this); + + for (auto &block_container : block_containers) { + if (block_container) + result += sizeof(PermissionContainerBulk); + } + + { + + shared_lock channel_lock(this->channel_list_lock); + result += this->_channel_permissions.size() * (sizeof(ChannelPermissionContainer) + sizeof(unique_ptr)); + } + + return result; +} + +void v2::PermissionManager::cleanup() { + lock_guard use_lock(this->block_use_count_lock); + + for (auto &block_container : block_containers) { + if (!block_container) continue; + + bool used = false; + for(auto& permission : block_container->permissions) { + if(permission.flags.value_set || permission.flags.grant_set || permission.flags.channel_specific) { + used = true; + break; + } + } + if(used) + continue; + + delete block_container; + block_container = nullptr; + } +} diff --git a/src/PermissionManager.h b/src/PermissionManager.h index 079185e..b0ac10b 100644 --- a/src/PermissionManager.h +++ b/src/PermissionManager.h @@ -1,951 +1,951 @@ -#pragma once - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include /* for memset */ -#include "./misc/spin_lock.h" -#include "Definitions.h" -#include "Variable.h" -#include "spdlog/fmt/ostr.h" // must be included - -#define permNotGranted (-2) -#define PERM_ID_GRANT ((ts::permission::PermissionType) (1U << 15U)) - -namespace ts { - class BasicChannel; - namespace permission { - typedef int32_t PermissionValue; - - enum PermissionSqlType { - SQL_PERM_GROUP, - SQL_PERM_CHANNEL, - SQL_PERM_USER, - SQL_PERM_PLAYLIST - }; - enum PermissionType : uint16_t { - undefined = (uint16_t) -1, - - permission_id_min = 0, /* we count unknown as defined permission as well */ - unknown = (uint16_t) 0, - ok = 0, - type_begin = 1, - - /* global */ - - /* global::information */ - b_serverinstance_help_view = type_begin, - b_serverinstance_version_view, - b_serverinstance_info_view, - b_serverinstance_virtualserver_list, - b_serverinstance_binding_list, - b_serverinstance_permission_list, - b_serverinstance_permission_find, - //b_serverinstance_allow_teaspeak_plugin, - - /* global::vs_management */ - b_virtualserver_create, - b_virtualserver_delete, - b_virtualserver_start_any, - b_virtualserver_stop_any, - b_virtualserver_change_machine_id, - b_virtualserver_change_template, - - /* global::administration */ - b_serverquery_login, - b_serverinstance_textmessage_send, - b_serverinstance_log_view, - b_serverinstance_log_add, - b_serverinstance_stop, - - /* global::settings */ - b_serverinstance_modify_settings, - b_serverinstance_modify_querygroup, - b_serverinstance_modify_templates, - - /* virtual_server */ - - /* virtual_server::information */ - b_virtualserver_select, - b_virtualserver_select_godmode, - b_virtualserver_info_view, - b_virtualserver_connectioninfo_view, - b_virtualserver_channel_list, - b_virtualserver_channel_search, - b_virtualserver_client_list, - b_virtualserver_client_search, - b_virtualserver_client_dblist, - b_virtualserver_client_dbsearch, - b_virtualserver_client_dbinfo, - b_virtualserver_permission_find, - b_virtualserver_custom_search, - - /* virtual_server::administration */ - b_virtualserver_start, - b_virtualserver_stop, - b_virtualserver_token_list, - b_virtualserver_token_add, - b_virtualserver_token_use, - b_virtualserver_token_delete, - b_virtualserver_log_view, - b_virtualserver_log_add, - b_virtualserver_join_ignore_password, - b_virtualserver_notify_register, - b_virtualserver_notify_unregister, - b_virtualserver_snapshot_create, - b_virtualserver_snapshot_deploy, - b_virtualserver_permission_reset, - - /* virtual_server::settings */ - b_virtualserver_modify_name, - b_virtualserver_modify_welcomemessage, - b_virtualserver_modify_maxchannels, - b_virtualserver_modify_maxclients, - b_virtualserver_modify_reserved_slots, - b_virtualserver_modify_password, - b_virtualserver_modify_default_servergroup, - b_virtualserver_modify_default_musicgroup, - b_virtualserver_modify_default_channelgroup, - b_virtualserver_modify_default_channeladmingroup, - b_virtualserver_modify_channel_forced_silence, - b_virtualserver_modify_complain, - b_virtualserver_modify_antiflood, - b_virtualserver_modify_ft_settings, - b_virtualserver_modify_ft_quotas, - b_virtualserver_modify_hostmessage, - b_virtualserver_modify_hostbanner, - b_virtualserver_modify_hostbutton, - b_virtualserver_modify_port, -#ifndef LAGENCY - b_virtualserver_modify_host, - b_virtualserver_modify_default_messages, -#endif - b_virtualserver_modify_autostart, - b_virtualserver_modify_needed_identity_security_level, - b_virtualserver_modify_priority_speaker_dimm_modificator, - b_virtualserver_modify_log_settings, - b_virtualserver_modify_min_client_version, - b_virtualserver_modify_icon_id, - b_virtualserver_modify_weblist, - b_virtualserver_modify_country_code, - b_virtualserver_modify_codec_encryption_mode, - b_virtualserver_modify_temporary_passwords, - b_virtualserver_modify_temporary_passwords_own, - b_virtualserver_modify_channel_temp_delete_delay_default, - b_virtualserver_modify_music_bot_limit, - - /* channel */ - i_channel_min_depth, - i_channel_max_depth, - b_channel_group_inheritance_end, - i_channel_permission_modify_power, - i_channel_needed_permission_modify_power, - - /* channel::info */ - b_channel_info_view, - b_virtualserver_channel_permission_list, - - /* channel::create */ - b_channel_create_child, - b_channel_create_permanent, - b_channel_create_semi_permanent, - b_channel_create_temporary, - b_channel_create_private, - b_channel_create_with_topic, - b_channel_create_with_description, - b_channel_create_with_password, - b_channel_create_modify_with_codec_speex8, - b_channel_create_modify_with_codec_speex16, - b_channel_create_modify_with_codec_speex32, - b_channel_create_modify_with_codec_celtmono48, - b_channel_create_modify_with_codec_opusvoice, - b_channel_create_modify_with_codec_opusmusic, - i_channel_create_modify_with_codec_maxquality, - i_channel_create_modify_with_codec_latency_factor_min, - b_channel_create_with_maxclients, - b_channel_create_with_maxfamilyclients, - b_channel_create_with_sortorder, - b_channel_create_with_default, - b_channel_create_with_needed_talk_power, - b_channel_create_modify_with_force_password, - i_channel_create_modify_with_temp_delete_delay, - i_channel_create_modify_conversation_history_length, - b_channel_create_modify_conversation_history_unlimited, - b_channel_create_modify_conversation_private, - - /* channel::modify */ - b_channel_modify_parent, - b_channel_modify_make_default, - b_channel_modify_make_permanent, - b_channel_modify_make_semi_permanent, - b_channel_modify_make_temporary, - b_channel_modify_name, - b_channel_modify_topic, - b_channel_modify_description, - b_channel_modify_password, - b_channel_modify_codec, - b_channel_modify_codec_quality, - b_channel_modify_codec_latency_factor, - b_channel_modify_maxclients, - b_channel_modify_maxfamilyclients, - b_channel_modify_sortorder, - b_channel_modify_needed_talk_power, - i_channel_modify_power, - i_channel_needed_modify_power, - b_channel_modify_make_codec_encrypted, - b_channel_modify_temp_delete_delay, - b_channel_conversation_message_delete, - - /* channel::delete */ - b_channel_delete_permanent, - b_channel_delete_semi_permanent, - b_channel_delete_temporary, - b_channel_delete_flag_force, - i_channel_delete_power, - i_channel_needed_delete_power, - - /* channel::access */ - b_channel_join_permanent, - b_channel_join_semi_permanent, - b_channel_join_temporary, - b_channel_join_ignore_password, - b_channel_join_ignore_maxclients, - i_channel_join_power, - i_channel_needed_join_power, - b_channel_ignore_join_power, - - i_channel_view_power, - i_channel_needed_view_power, - b_channel_ignore_view_power, - - i_channel_subscribe_power, - i_channel_needed_subscribe_power, - b_channel_ignore_subscribe_power, - - i_channel_description_view_power, - i_channel_needed_description_view_power, - b_channel_ignore_description_view_power, - - /* group */ - i_icon_id, - i_max_icon_filesize, - i_max_playlist_size, - i_max_playlists, - b_icon_manage, - b_group_is_permanent, - i_group_auto_update_type, - i_group_auto_update_max_value, - i_group_sort_id, - i_group_show_name_in_tree, - - /* group::info */ - b_virtualserver_servergroup_list, //Unused - b_virtualserver_servergroup_permission_list, - b_virtualserver_servergroup_client_list, - - b_virtualserver_channelgroup_list, //Unused - b_virtualserver_channelgroup_permission_list, - b_virtualserver_channelgroup_client_list, - - /* group::create */ - b_virtualserver_servergroup_create, - b_virtualserver_channelgroup_create, - - /* group::modify */ -#ifdef LAGENCY - i_group_modify_power, - i_group_needed_modify_power, - i_group_member_add_power, - i_group_needed_member_add_power, - i_group_member_remove_power, - i_group_needed_member_remove_power, -#else - //permission patch start - i_server_group_modify_power, - i_server_group_needed_modify_power, - i_server_group_member_add_power, - i_server_group_self_add_power, - i_server_group_needed_member_add_power, - i_server_group_member_remove_power, - i_server_group_self_remove_power, - i_server_group_needed_member_remove_power, - i_channel_group_modify_power, - i_channel_group_needed_modify_power, - i_channel_group_member_add_power, - i_channel_group_self_add_power, - i_channel_group_needed_member_add_power, - i_channel_group_member_remove_power, - i_channel_group_self_remove_power, - i_channel_group_needed_member_remove_power, - - i_displayed_group_member_add_power, - i_displayed_group_needed_member_add_power, - i_displayed_group_member_remove_power, - i_displayed_group_needed_member_remove_power, - i_displayed_group_modify_power, - i_displayed_group_needed_modify_power, - //permission patch end -#endif - i_permission_modify_power, - b_permission_modify_power_ignore, - - /* group::delete */ - b_virtualserver_servergroup_delete, - b_virtualserver_channelgroup_delete, - - /* client */ - i_client_permission_modify_power, - i_client_needed_permission_modify_power, - i_client_max_clones_uid, - i_client_max_clones_ip, - i_client_max_clones_hwid, - i_client_max_idletime, - i_client_max_avatar_filesize, - i_client_max_channel_subscriptions, - i_client_max_channels, - i_client_max_temporary_channels, - i_client_max_semi_channels, - i_client_max_permanent_channels, - b_client_use_priority_speaker, - b_client_is_priority_speaker, - b_client_skip_channelgroup_permissions, - b_client_force_push_to_talk, - b_client_ignore_bans, - b_client_ignore_vpn, - b_client_ignore_antiflood, - b_client_enforce_valid_hwid, - b_client_allow_invalid_packet, - b_client_allow_invalid_badges, - b_client_issue_client_query_command, - b_client_use_reserved_slot, - b_client_use_channel_commander, - b_client_request_talker, - b_client_avatar_delete_other, - b_client_is_sticky, - b_client_ignore_sticky, - - b_client_music_create_permanent, - b_client_music_create_semi_permanent, - b_client_music_create_temporary, - b_client_music_modify_permanent, - b_client_music_modify_semi_permanent, - b_client_music_modify_temporary, - i_client_music_create_modify_max_volume, - - i_client_music_limit, - i_client_music_needed_delete_power, - i_client_music_delete_power, - i_client_music_play_power, - i_client_music_needed_play_power, - i_client_music_modify_power, - i_client_music_needed_modify_power, - i_client_music_rename_power, - i_client_music_needed_rename_power, - - b_virtualserver_playlist_permission_list, - b_playlist_create, - i_playlist_view_power, - i_playlist_needed_view_power, - i_playlist_modify_power, - i_playlist_needed_modify_power, - i_playlist_permission_modify_power, - i_playlist_needed_permission_modify_power, - i_playlist_delete_power, - i_playlist_needed_delete_power, - - i_playlist_song_add_power, - i_playlist_song_needed_add_power, - i_playlist_song_remove_power, - i_playlist_song_needed_remove_power, - i_playlist_song_move_power, - i_playlist_song_needed_move_power, - - /* client::info */ - b_client_info_view, - b_client_permissionoverview_view, - b_client_permissionoverview_own, - b_client_remoteaddress_view, - i_client_serverquery_view_power, - i_client_needed_serverquery_view_power, - b_client_custom_info_view, - b_client_music_channel_list, - b_client_music_server_list, - i_client_music_info, - i_client_music_needed_info, - b_virtualserver_channelclient_permission_list, - b_virtualserver_client_permission_list, - - /* client::admin */ - i_client_kick_from_server_power, - i_client_needed_kick_from_server_power, - i_client_kick_from_channel_power, - i_client_needed_kick_from_channel_power, - i_client_ban_power, - i_client_needed_ban_power, - i_client_move_power, - i_client_needed_move_power, - i_client_complain_power, - i_client_needed_complain_power, - b_client_complain_list, - b_client_complain_delete_own, - b_client_complain_delete, - b_client_ban_list, - b_client_ban_list_global, - b_client_ban_trigger_list, - b_client_ban_create, - b_client_ban_create_global, - b_client_ban_name, - b_client_ban_ip, - b_client_ban_hwid, - b_client_ban_edit, - b_client_ban_edit_global, - b_client_ban_delete_own, - b_client_ban_delete, - b_client_ban_delete_own_global, - b_client_ban_delete_global, - i_client_ban_max_bantime, - - /* client::basics */ - i_client_private_textmessage_power, - i_client_needed_private_textmessage_power, - b_client_even_textmessage_send, - b_client_server_textmessage_send, - b_client_channel_textmessage_send, - b_client_offline_textmessage_send, - i_client_talk_power, - i_client_needed_talk_power, - i_client_poke_power, - i_client_needed_poke_power, - b_client_set_flag_talker, - i_client_whisper_power, - i_client_needed_whisper_power, - - /* client::modify */ - b_client_modify_description, - b_client_modify_own_description, - b_client_use_bbcode_any, - b_client_use_bbcode_url, - b_client_use_bbcode_image, - b_client_modify_dbproperties, - b_client_delete_dbproperties, - b_client_create_modify_serverquery_login, - b_client_query_create, - b_client_query_list, - b_client_query_list_own, - b_client_query_rename, - b_client_query_rename_own, - b_client_query_change_password, - b_client_query_change_own_password, - b_client_query_change_password_global, - b_client_query_delete, - b_client_query_delete_own, - - /* file_transfer */ - b_ft_ignore_password, - b_ft_transfer_list, - i_ft_file_upload_power, - i_ft_needed_file_upload_power, - i_ft_file_download_power, - i_ft_needed_file_download_power, - i_ft_file_delete_power, - i_ft_needed_file_delete_power, - i_ft_file_rename_power, - i_ft_needed_file_rename_power, - i_ft_file_browse_power, - i_ft_needed_file_browse_power, - i_ft_directory_create_power, - i_ft_needed_directory_create_power, - i_ft_quota_mb_download_per_client, - i_ft_quota_mb_upload_per_client, - - permission_id_max - }; - inline PermissionType& operator&=(PermissionType& a, int b) { return a = (PermissionType) ((int) a & b); } - - enum PermissionGroup : uint16_t { - group_begin, - global = group_begin, - global_info = b_serverinstance_permission_find, - global_vsmanage = b_virtualserver_change_template, - global_admin = b_serverinstance_stop, - global_settings = b_serverinstance_modify_templates, - - vs = global_settings, /* we dont have any permissions in here */ - vs_info = b_virtualserver_custom_search, - vs_admin = b_virtualserver_permission_reset, -#ifdef LEGENCY - vs_settings = b_virtualserver_modify_channel_temp_delete_delay_default, -#else - vs_settings = b_virtualserver_modify_music_bot_limit, -#endif - - channel = i_channel_needed_permission_modify_power, - channel_info = b_virtualserver_channel_permission_list, - channel_create = b_channel_create_modify_conversation_private, - channel_modify = b_channel_modify_temp_delete_delay, - channel_delete = i_channel_needed_delete_power, - channel_access = b_channel_ignore_description_view_power, - - group = i_group_show_name_in_tree, - group_info = b_virtualserver_channelgroup_client_list, - group_create = b_virtualserver_channelgroup_create, - group_modify = b_permission_modify_power_ignore, - group_delete = b_virtualserver_channelgroup_delete, -#ifdef LAGENCY - client = b_client_ignore_sticky, -#else - client = i_playlist_song_needed_move_power, -#endif -#ifdef LAGENCY - client_info = b_client_custom_info_view, -#else - client_info = b_virtualserver_client_permission_list, -#endif - client_admin = i_client_ban_max_bantime, - client_basic = i_client_needed_whisper_power, - client_modify = b_client_query_delete_own, - ft = i_ft_quota_mb_upload_per_client, - group_end - }; - - enum PermissionTestType { - PERMTEST_HIGHEST, - PERMTEST_ORDERED, - }; - - struct PermissionTypeEntry { - static std::shared_ptr unknown; - - PermissionTypeEntry& operator=(const PermissionTypeEntry& other) = delete; - /* - PermissionTypeEntry& operator=(const PermissionTypeEntry& other) { - this->type = other.type; - this->group = other.group; - this->name = other.name; - this->description = other.description; - this->clientSupported = other.clientSupported; - - this->grant_name = std::string() + (name[0] == 'i' ? "i" : "i") + "_needed_modify_power_" + name.substr(2); - return *this; - } - - bool operator==(const PermissionTypeEntry& other) { - return other.type == this->type; - } - */ - - PermissionType type; - PermissionGroup group; - std::string name; - std::string grant_name; - inline std::string grantName(bool useBool = false) const { return this->grant_name; } - std::string description; - - bool clientSupported = true; - - // PermissionTypeEntry(PermissionTypeEntry&& ref) : type(ref.type), group(ref.group), name(ref.name), description(ref.description), clientSupported(ref.clientSupported) {} - //PermissionTypeEntry(const PermissionTypeEntry& ref) : type(ref.type), group(ref.group), name(ref.name), description(ref.description), clientSupported(ref.clientSupported) {} - PermissionTypeEntry(PermissionTypeEntry&& ref) = delete; - PermissionTypeEntry(const PermissionTypeEntry& ref) = delete; - //PermissionTypeEntry(const PermissionTypeEntry& ref) : type(ref.type), group(ref.group), name(ref.name), description(ref.description), clientSupported(ref.clientSupported) {} - - PermissionTypeEntry(PermissionType type, PermissionGroup group, std::string name, std::string description, bool clientSupported = true) : type(type), - group(group), - name(std::move(name)), - description(std::move(description)), - clientSupported(clientSupported) { - this->grant_name = std::string() + (this->name[0] == 'i' ? "i" : "i") + "_needed_modify_power_" + this->name.substr(2); - } - }; - - namespace teamspeak { - enum GroupType { - GENERAL, - SERVER, - CHANNEL, - CLIENT - }; - - typedef std::map>> MapType; - extern MapType unmapping; - extern MapType mapping; - extern std::deque map_key(std::string key, GroupType type); //TeamSpeak -> TeaSpeak - extern std::deque unmap_key(std::string key, GroupType type); //TeaSpeak -> TeamSpeak - } - - namespace update { - enum GroupUpdateType { - NONE = 0, - - CHANNEL_GUEST = 10, - CHANNEL_VOICE = 25, - CHANNEL_OPERATOR = 35, - CHANNEL_ADMIN = 40, - - SERVER_GUEST = 15, - SERVER_NORMAL = 30, - SERVER_ADMIN = 45, - - QUERY_GUEST = 20, - QUERY_ADMIN = 50 - }; - - struct UpdatePermission { - UpdatePermission(std::string name, permission::PermissionValue value, permission::PermissionValue granted, bool negated, bool skipped) : name(std::move(name)), value(value), granted(granted), negated(negated), skipped(skipped) {} - UpdatePermission() = default; - - std::string name; - permission::PermissionValue value = permNotGranted; - permission::PermissionValue granted = permNotGranted; - - bool negated = false; - bool skipped = false; - }; - - struct UpdateEntry { - GroupUpdateType type; - UpdatePermission permission; - }; - - extern std::deque migrate; //TeamSpeak -> TeaSpeak - } - - extern std::deque> availablePermissions; - extern std::deque neededPermissions; - extern std::deque availableGroups; - - void setup_permission_resolve(); - std::shared_ptr resolvePermissionData(PermissionType); - std::shared_ptr resolvePermissionData(const std::string&); - -#define PERM_FLAG_PRIVATE 0b1 -#define PERM_FLAG_PUBLIC 0b10 - - class PermissionManager; - struct Permission { - public: - Permission(const std::shared_ptr& type, PermissionValue value, PermissionValue grant, uint16_t flagMask, std::shared_ptr ch) : type(type), channel(std::move(ch)) { - this->type = type; - this->value = value; - this->granted = grant; - this->flagMask = flagMask; - } - - - Permission(const Permission &) = delete; - - Permission() = delete; - ~Permission()= default; - - std::shared_ptr channel = nullptr; - ChannelId channelId(); - - bool hasValue(){ return value != permNotGranted; } - bool hasGrant(){ return granted != permNotGranted; } - std::shared_ptr type; - PermissionValue value; - PermissionValue granted; - uint16_t flagMask; - - bool flag_negate = false; - bool flag_skip = false; - bool dbReference = false; - }; - - class PermissionManager { - public: - PermissionManager(); - ~PermissionManager(); - - std::shared_ptr registerPermission(PermissionType, PermissionValue, const std::shared_ptr& channel, uint16_t = PERM_FLAG_PUBLIC); - std::shared_ptr registerPermission(const std::shared_ptr&, PermissionValue, const std::shared_ptr& channel, uint16_t = PERM_FLAG_PUBLIC); - //void registerAllPermissions(uint16_t flagMask); - - bool setPermissionGranted(PermissionType, PermissionValue, const std::shared_ptr& channel); - bool setPermission(PermissionType, PermissionValue, const std::shared_ptr& channel); - bool setPermission(PermissionType, PermissionValue, const std::shared_ptr& channel, bool negated, bool skiped); - void deletePermission(PermissionType, const std::shared_ptr& channel); - bool hasPermission(PermissionType, const std::shared_ptr& channel, bool testGlobal); - - /** - * @param channel Should be null for general testing - * @return , - */ - std::deque> getPermission(PermissionType, const std::shared_ptr& channel, bool testGlobal = true); - - PermissionValue getPermissionGrand(permission::PermissionTestType test, PermissionType type, const std::shared_ptr& channel, PermissionValue def = permNotGranted){ - PermissionValue result = def; - auto perms = this->getPermission(type, channel); - if(test == PermissionTestType::PERMTEST_HIGHEST) { - PermissionValue higest = permNotGranted; - for(const auto &e : perms) - if((e->granted > higest || e->granted == -1) && higest != -1) higest = e->granted; - if(higest != permNotGranted) - result = higest; - } else if(test == PermissionTestType::PERMTEST_ORDERED) - while(!perms.empty() && result == permNotGranted) { - result = perms.front()->granted; - perms.pop_front(); - }; - return result; - } - - inline PermissionValue getPermissionValue(PermissionType type, const std::shared_ptr& channel = nullptr, PermissionValue default_value = permNotGranted) { - return this->getPermissionValue(permission::PERMTEST_ORDERED, type, channel, default_value); - } - - PermissionValue getPermissionValue(permission::PermissionTestType test, PermissionType type, const std::shared_ptr& channel = nullptr, PermissionValue def = permNotGranted) { - PermissionValue result = permNotGranted; - auto perms = this->getPermission(type, channel); - if(test == permission::PERMTEST_HIGHEST) { - PermissionValue higest = permNotGranted; - for(const auto &e : perms) - if((e->value > higest || e->value == -1) && higest != -1) higest = e->value; - if(higest != permNotGranted) - result = higest; - } else if(test == PermissionTestType::PERMTEST_ORDERED) - while(!perms.empty() && result == permNotGranted) { - result = perms.front()->value; - perms.pop_front(); - }; - return result == permNotGranted ? def : result; - } - - std::vector> listPermissions(uint16_t = ~0); - std::vector> allPermissions(); - - std::deque> all_channel_specific_permissions(); - std::deque> all_channel_unspecific_permissions(); - - void fireUpdate(PermissionType); - void registerUpdateHandler(const std::function)> &fn){ updateHandler.push_back(fn); } - - void clearPermissions(){ - permissions.clear(); - } - private: - std::deque> permissions; - std::deque)>> updateHandler; - }; - - namespace v2 { - #pragma pack(push, 1) - struct PermissionFlags { - bool database_reference: 1; /* if set the permission is known within the database, else it has tp be inserted */ - bool channel_specific: 1; /* set if there are channel specific permissions */ - - bool value_set: 1; - bool grant_set: 1; - - bool skip: 1; - bool negate: 1; - - bool flag_value_update: 1; - bool flag_grant_update: 1; - - ts_always_inline bool permission_set() { - return this->value_set || this->grant_set; - } - }; - static_assert(sizeof(PermissionFlags) == 1); - - struct PermissionValues { - PermissionValue value; - PermissionValue grant; - }; - static_assert(sizeof(PermissionValues) == 8); - static constexpr PermissionValues empty_permission_values{0, 0}; - - struct PermissionContainer { - PermissionFlags flags; - PermissionValues values; - }; - static_assert(sizeof(PermissionContainer) == 9); - - struct ChannelPermissionContainer : public PermissionContainer { - PermissionType permission; - ChannelId channel_id; - }; - static_assert(sizeof(ChannelPermissionContainer) == 19); - static constexpr v2::PermissionFlags empty_flags = {false, false, false, false, false, false, false}; - static constexpr v2::PermissionContainer empty_channel_permission = {empty_flags, v2::empty_permission_values}; - - #pragma pack(pop) - - #pragma pack(push, 1) - template - struct PermissionContainerBulk { - PermissionContainer permissions[element_count]; - - PermissionContainerBulk() { - memset(this->permissions, 0, sizeof(this->permissions)); - } - }; - #pragma pack(pop) - - enum PermissionUpdateType { - do_nothing, - set_value, - delete_value - }; - - struct PermissionDBUpdateEntry { - PermissionType permission; - ChannelId channel_id; - - PermissionValues values; - PermissionUpdateType update_value; - PermissionUpdateType update_grant; - - bool flag_db: 1; /* only needs an update if set */ - bool flag_delete: 1; - bool flag_skip: 1; - bool flag_negate: 1; - }; - - struct PermissionFlaggedValue { - PermissionValue value = permNotGranted; - bool has_value = false; - - constexpr bool has_power() const { return this->has_value && (this->value > 0 || this->value == -1); } - constexpr bool has_infinite_power() const { return this->has_value && this->value == -1; } - - inline bool operator==(const PermissionFlaggedValue& other) const { return other.value == this->value && other.has_value == this->has_value; } - inline bool operator!=(const PermissionFlaggedValue& other) const { return !(*this == other); } - }; - static constexpr PermissionFlaggedValue empty_permission_flagged_value{0, false}; - - - static constexpr bool permission_granted(const PermissionFlaggedValue& required, const PermissionFlaggedValue& given, bool requires_given = true) { - if(!required.has_value) { - return !requires_given || given.has_power(); - } else if(!given.has_power()) { - return false; - } else if(given.has_infinite_power()) { - return true; - } else if(required.has_infinite_power()) { - return false; - } else { - return given.value >= required.value; - } - } - static constexpr bool permission_granted(const PermissionValue& required, const PermissionFlaggedValue& given, bool requires_given = true) { - return permission_granted({required, true}, given, requires_given); - } - - class PermissionManager { - public: - static constexpr size_t PERMISSIONS_BULK_BITS = 4; /* 16 permissions per block */ - static constexpr size_t PERMISSIONS_BULK_ENTRY_COUNT = 1 << PERMISSIONS_BULK_BITS; - static constexpr size_t BULK_COUNT = (PermissionType::permission_id_max / (1 << PERMISSIONS_BULK_BITS)) + ((PermissionType::permission_id_max % PERMISSIONS_BULK_ENTRY_COUNT == 0) ? 0 : 1); - static_assert(PERMISSIONS_BULK_ENTRY_COUNT * BULK_COUNT >= PermissionType::permission_id_max); - - PermissionManager(); - virtual ~PermissionManager(); - - /* load permissions from the database */ - void load_permission(const PermissionType&, const PermissionValues& /* values */, bool /* flag skip */, bool /* flag negate */, bool /* value present */,bool /* grant present */); - void load_permission(const PermissionType&, const PermissionValues& /* values */, ChannelId /* channel */, bool /* flag skip */, bool /* flag negate */, bool /* value present */,bool /* grant present */); - - /* general getters/setters */ - const PermissionFlags permission_flags(const PermissionType&); /* we return a "copy" because the actual permission could be deleted while we're analyzing the flags */ - ts_always_inline const PermissionFlags permission_flags(const std::shared_ptr& permission_info) { return this->permission_flags(permission_info->type); } - - const PermissionValues permission_values(const PermissionType&); - ts_always_inline const PermissionValues permission_values(const std::shared_ptr& permission_info) { return this->permission_values(permission_info->type); } - - const PermissionFlaggedValue permission_value_flagged(const PermissionType&); - ts_always_inline const PermissionFlaggedValue permission_value_flagged(const std::shared_ptr& permission_info) { return this->permission_value_flagged(permission_info->type); } - - const PermissionFlaggedValue permission_granted_flagged(const PermissionType&); - ts_always_inline const PermissionFlaggedValue permission_granted_flagged(const std::shared_ptr& permission_info) { return this->permission_granted_flagged(permission_info->type); } - - /* only worth looking up if channel_specific is set */ - const PermissionContainer channel_permission(const PermissionType& /* permission */, ChannelId /* channel id */); - ts_always_inline const PermissionContainer channel_permission(const std::shared_ptr& permission_info, ChannelId channel_id) { return this->channel_permission(permission_info->type, channel_id); } - - /* modifiers */ - void set_permission(const PermissionType& /* permission */, const PermissionValues& /* values */, const PermissionUpdateType& /* update value */, const PermissionUpdateType& /* update grant */, int /* flag skip */ = -1, int /* flag negate */ = -1); - void set_channel_permission(const PermissionType& /* permission */, ChannelId /* channel id */, const PermissionValues& /* values */, const PermissionUpdateType& /* update value */, const PermissionUpdateType& /* update grant */, int /* flag skip */ = -1, int /* flag negate */ = -1); - - /* bulk info */ - const std::vector> permissions(); - const std::vector> channel_permissions(ChannelId /* channel id */); - const std::vector> channel_permissions(); - - size_t used_memory(); - void cleanup(); - - ts_always_inline bool require_db_updates() { return this->requires_db_save; } - const std::vector flush_db_updates(); - private: - static constexpr size_t PERMISSIONS_BULK_BLOCK_MASK = (~(1 << PERMISSIONS_BULK_BITS)) & ((1 << PERMISSIONS_BULK_BITS) - 1); - - bool requires_db_save = false; - ts_always_inline void trigger_db_update() { this->requires_db_save = true; } - - spin_lock block_use_count_lock{}; - int16_t block_use_count[BULK_COUNT]; - PermissionContainerBulk* block_containers[BULK_COUNT]; - - //TODO: Bulk permissions for channels as well, specially because they're client permissions in terms of the music bot! - std::shared_mutex channel_list_lock{}; - std::deque> _channel_permissions{}; - - ts_always_inline size_t calculate_block(const PermissionType& permission) { - return permission >> PERMISSIONS_BULK_BITS; - } - - ts_always_inline size_t calculate_block_index(const PermissionType& permission) { - return permission & PERMISSIONS_BULK_BLOCK_MASK; - } - - /** - * @param block - * @return true if block exists else false - */ - ts_always_inline bool ref_block(size_t block) { - std::lock_guard use_lock(this->block_use_count_lock); - if(!this->block_containers[block]) - return false; - this->block_use_count[block]++; - assert(this->block_use_count[block] > 0); - return true; - } - - ts_always_inline void unref_block(size_t block) { - std::lock_guard use_lock(this->block_use_count_lock); - this->block_use_count[block]--; - assert(this->block_use_count[block] >= 0); - } - - ts_always_inline void ref_allocate_block(size_t block) { - std::lock_guard use_lock(this->block_use_count_lock); - if(!this->block_containers[block]) - this->block_containers[block] = new PermissionContainerBulk(); - this->block_use_count[block]++; - assert(this->block_use_count[block] > 0); - } - }; - } - } -} - -inline std::ostream& operator<<(std::ostream& os, const ts::permission::v2::PermissionFlaggedValue& c) { - if(c.has_value) - return os << c.value; - else - return os << "unset"; -} - -DEFINE_VARIABLE_TRANSFORM(ts::permission::PermissionType, VARTYPE_INT, std::to_string((int16_t) in), static_cast(in.as())); -DEFINE_VARIABLE_TRANSFORM(ts::permission::PermissionGroup, VARTYPE_INT, std::to_string((uint16_t) in), static_cast(in.as())); +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include /* for memset */ +#include "./misc/spin_lock.h" +#include "Definitions.h" +#include "Variable.h" +#include "spdlog/fmt/ostr.h" // must be included + +#define permNotGranted (-2) +#define PERM_ID_GRANT ((ts::permission::PermissionType) (1U << 15U)) + +namespace ts { + class BasicChannel; + namespace permission { + typedef int32_t PermissionValue; + + enum PermissionSqlType { + SQL_PERM_GROUP, + SQL_PERM_CHANNEL, + SQL_PERM_USER, + SQL_PERM_PLAYLIST + }; + enum PermissionType : uint16_t { + undefined = (uint16_t) -1, + + permission_id_min = 0, /* we count unknown as defined permission as well */ + unknown = (uint16_t) 0, + ok = 0, + type_begin = 1, + + /* global */ + + /* global::information */ + b_serverinstance_help_view = type_begin, + b_serverinstance_version_view, + b_serverinstance_info_view, + b_serverinstance_virtualserver_list, + b_serverinstance_binding_list, + b_serverinstance_permission_list, + b_serverinstance_permission_find, + //b_serverinstance_allow_teaspeak_plugin, + + /* global::vs_management */ + b_virtualserver_create, + b_virtualserver_delete, + b_virtualserver_start_any, + b_virtualserver_stop_any, + b_virtualserver_change_machine_id, + b_virtualserver_change_template, + + /* global::administration */ + b_serverquery_login, + b_serverinstance_textmessage_send, + b_serverinstance_log_view, + b_serverinstance_log_add, + b_serverinstance_stop, + + /* global::settings */ + b_serverinstance_modify_settings, + b_serverinstance_modify_querygroup, + b_serverinstance_modify_templates, + + /* virtual_server */ + + /* virtual_server::information */ + b_virtualserver_select, + b_virtualserver_select_godmode, + b_virtualserver_info_view, + b_virtualserver_connectioninfo_view, + b_virtualserver_channel_list, + b_virtualserver_channel_search, + b_virtualserver_client_list, + b_virtualserver_client_search, + b_virtualserver_client_dblist, + b_virtualserver_client_dbsearch, + b_virtualserver_client_dbinfo, + b_virtualserver_permission_find, + b_virtualserver_custom_search, + + /* virtual_server::administration */ + b_virtualserver_start, + b_virtualserver_stop, + b_virtualserver_token_list, + b_virtualserver_token_add, + b_virtualserver_token_use, + b_virtualserver_token_delete, + b_virtualserver_log_view, + b_virtualserver_log_add, + b_virtualserver_join_ignore_password, + b_virtualserver_notify_register, + b_virtualserver_notify_unregister, + b_virtualserver_snapshot_create, + b_virtualserver_snapshot_deploy, + b_virtualserver_permission_reset, + + /* virtual_server::settings */ + b_virtualserver_modify_name, + b_virtualserver_modify_welcomemessage, + b_virtualserver_modify_maxchannels, + b_virtualserver_modify_maxclients, + b_virtualserver_modify_reserved_slots, + b_virtualserver_modify_password, + b_virtualserver_modify_default_servergroup, + b_virtualserver_modify_default_musicgroup, + b_virtualserver_modify_default_channelgroup, + b_virtualserver_modify_default_channeladmingroup, + b_virtualserver_modify_channel_forced_silence, + b_virtualserver_modify_complain, + b_virtualserver_modify_antiflood, + b_virtualserver_modify_ft_settings, + b_virtualserver_modify_ft_quotas, + b_virtualserver_modify_hostmessage, + b_virtualserver_modify_hostbanner, + b_virtualserver_modify_hostbutton, + b_virtualserver_modify_port, +#ifndef LAGENCY + b_virtualserver_modify_host, + b_virtualserver_modify_default_messages, +#endif + b_virtualserver_modify_autostart, + b_virtualserver_modify_needed_identity_security_level, + b_virtualserver_modify_priority_speaker_dimm_modificator, + b_virtualserver_modify_log_settings, + b_virtualserver_modify_min_client_version, + b_virtualserver_modify_icon_id, + b_virtualserver_modify_weblist, + b_virtualserver_modify_country_code, + b_virtualserver_modify_codec_encryption_mode, + b_virtualserver_modify_temporary_passwords, + b_virtualserver_modify_temporary_passwords_own, + b_virtualserver_modify_channel_temp_delete_delay_default, + b_virtualserver_modify_music_bot_limit, + + /* channel */ + i_channel_min_depth, + i_channel_max_depth, + b_channel_group_inheritance_end, + i_channel_permission_modify_power, + i_channel_needed_permission_modify_power, + + /* channel::info */ + b_channel_info_view, + b_virtualserver_channel_permission_list, + + /* channel::create */ + b_channel_create_child, + b_channel_create_permanent, + b_channel_create_semi_permanent, + b_channel_create_temporary, + b_channel_create_private, + b_channel_create_with_topic, + b_channel_create_with_description, + b_channel_create_with_password, + b_channel_create_modify_with_codec_speex8, + b_channel_create_modify_with_codec_speex16, + b_channel_create_modify_with_codec_speex32, + b_channel_create_modify_with_codec_celtmono48, + b_channel_create_modify_with_codec_opusvoice, + b_channel_create_modify_with_codec_opusmusic, + i_channel_create_modify_with_codec_maxquality, + i_channel_create_modify_with_codec_latency_factor_min, + b_channel_create_with_maxclients, + b_channel_create_with_maxfamilyclients, + b_channel_create_with_sortorder, + b_channel_create_with_default, + b_channel_create_with_needed_talk_power, + b_channel_create_modify_with_force_password, + i_channel_create_modify_with_temp_delete_delay, + i_channel_create_modify_conversation_history_length, + b_channel_create_modify_conversation_history_unlimited, + b_channel_create_modify_conversation_private, + + /* channel::modify */ + b_channel_modify_parent, + b_channel_modify_make_default, + b_channel_modify_make_permanent, + b_channel_modify_make_semi_permanent, + b_channel_modify_make_temporary, + b_channel_modify_name, + b_channel_modify_topic, + b_channel_modify_description, + b_channel_modify_password, + b_channel_modify_codec, + b_channel_modify_codec_quality, + b_channel_modify_codec_latency_factor, + b_channel_modify_maxclients, + b_channel_modify_maxfamilyclients, + b_channel_modify_sortorder, + b_channel_modify_needed_talk_power, + i_channel_modify_power, + i_channel_needed_modify_power, + b_channel_modify_make_codec_encrypted, + b_channel_modify_temp_delete_delay, + b_channel_conversation_message_delete, + + /* channel::delete */ + b_channel_delete_permanent, + b_channel_delete_semi_permanent, + b_channel_delete_temporary, + b_channel_delete_flag_force, + i_channel_delete_power, + i_channel_needed_delete_power, + + /* channel::access */ + b_channel_join_permanent, + b_channel_join_semi_permanent, + b_channel_join_temporary, + b_channel_join_ignore_password, + b_channel_join_ignore_maxclients, + i_channel_join_power, + i_channel_needed_join_power, + b_channel_ignore_join_power, + + i_channel_view_power, + i_channel_needed_view_power, + b_channel_ignore_view_power, + + i_channel_subscribe_power, + i_channel_needed_subscribe_power, + b_channel_ignore_subscribe_power, + + i_channel_description_view_power, + i_channel_needed_description_view_power, + b_channel_ignore_description_view_power, + + /* group */ + i_icon_id, + i_max_icon_filesize, + i_max_playlist_size, + i_max_playlists, + b_icon_manage, + b_group_is_permanent, + i_group_auto_update_type, + i_group_auto_update_max_value, + i_group_sort_id, + i_group_show_name_in_tree, + + /* group::info */ + b_virtualserver_servergroup_list, //Unused + b_virtualserver_servergroup_permission_list, + b_virtualserver_servergroup_client_list, + + b_virtualserver_channelgroup_list, //Unused + b_virtualserver_channelgroup_permission_list, + b_virtualserver_channelgroup_client_list, + + /* group::create */ + b_virtualserver_servergroup_create, + b_virtualserver_channelgroup_create, + + /* group::modify */ +#ifdef LAGENCY + i_group_modify_power, + i_group_needed_modify_power, + i_group_member_add_power, + i_group_needed_member_add_power, + i_group_member_remove_power, + i_group_needed_member_remove_power, +#else + //permission patch start + i_server_group_modify_power, + i_server_group_needed_modify_power, + i_server_group_member_add_power, + i_server_group_self_add_power, + i_server_group_needed_member_add_power, + i_server_group_member_remove_power, + i_server_group_self_remove_power, + i_server_group_needed_member_remove_power, + i_channel_group_modify_power, + i_channel_group_needed_modify_power, + i_channel_group_member_add_power, + i_channel_group_self_add_power, + i_channel_group_needed_member_add_power, + i_channel_group_member_remove_power, + i_channel_group_self_remove_power, + i_channel_group_needed_member_remove_power, + + i_displayed_group_member_add_power, + i_displayed_group_needed_member_add_power, + i_displayed_group_member_remove_power, + i_displayed_group_needed_member_remove_power, + i_displayed_group_modify_power, + i_displayed_group_needed_modify_power, + //permission patch end +#endif + i_permission_modify_power, + b_permission_modify_power_ignore, + + /* group::delete */ + b_virtualserver_servergroup_delete, + b_virtualserver_channelgroup_delete, + + /* client */ + i_client_permission_modify_power, + i_client_needed_permission_modify_power, + i_client_max_clones_uid, + i_client_max_clones_ip, + i_client_max_clones_hwid, + i_client_max_idletime, + i_client_max_avatar_filesize, + i_client_max_channel_subscriptions, + i_client_max_channels, + i_client_max_temporary_channels, + i_client_max_semi_channels, + i_client_max_permanent_channels, + b_client_use_priority_speaker, + b_client_is_priority_speaker, + b_client_skip_channelgroup_permissions, + b_client_force_push_to_talk, + b_client_ignore_bans, + b_client_ignore_vpn, + b_client_ignore_antiflood, + b_client_enforce_valid_hwid, + b_client_allow_invalid_packet, + b_client_allow_invalid_badges, + b_client_issue_client_query_command, + b_client_use_reserved_slot, + b_client_use_channel_commander, + b_client_request_talker, + b_client_avatar_delete_other, + b_client_is_sticky, + b_client_ignore_sticky, + + b_client_music_create_permanent, + b_client_music_create_semi_permanent, + b_client_music_create_temporary, + b_client_music_modify_permanent, + b_client_music_modify_semi_permanent, + b_client_music_modify_temporary, + i_client_music_create_modify_max_volume, + + i_client_music_limit, + i_client_music_needed_delete_power, + i_client_music_delete_power, + i_client_music_play_power, + i_client_music_needed_play_power, + i_client_music_modify_power, + i_client_music_needed_modify_power, + i_client_music_rename_power, + i_client_music_needed_rename_power, + + b_virtualserver_playlist_permission_list, + b_playlist_create, + i_playlist_view_power, + i_playlist_needed_view_power, + i_playlist_modify_power, + i_playlist_needed_modify_power, + i_playlist_permission_modify_power, + i_playlist_needed_permission_modify_power, + i_playlist_delete_power, + i_playlist_needed_delete_power, + + i_playlist_song_add_power, + i_playlist_song_needed_add_power, + i_playlist_song_remove_power, + i_playlist_song_needed_remove_power, + i_playlist_song_move_power, + i_playlist_song_needed_move_power, + + /* client::info */ + b_client_info_view, + b_client_permissionoverview_view, + b_client_permissionoverview_own, + b_client_remoteaddress_view, + i_client_serverquery_view_power, + i_client_needed_serverquery_view_power, + b_client_custom_info_view, + b_client_music_channel_list, + b_client_music_server_list, + i_client_music_info, + i_client_music_needed_info, + b_virtualserver_channelclient_permission_list, + b_virtualserver_client_permission_list, + + /* client::admin */ + i_client_kick_from_server_power, + i_client_needed_kick_from_server_power, + i_client_kick_from_channel_power, + i_client_needed_kick_from_channel_power, + i_client_ban_power, + i_client_needed_ban_power, + i_client_move_power, + i_client_needed_move_power, + i_client_complain_power, + i_client_needed_complain_power, + b_client_complain_list, + b_client_complain_delete_own, + b_client_complain_delete, + b_client_ban_list, + b_client_ban_list_global, + b_client_ban_trigger_list, + b_client_ban_create, + b_client_ban_create_global, + b_client_ban_name, + b_client_ban_ip, + b_client_ban_hwid, + b_client_ban_edit, + b_client_ban_edit_global, + b_client_ban_delete_own, + b_client_ban_delete, + b_client_ban_delete_own_global, + b_client_ban_delete_global, + i_client_ban_max_bantime, + + /* client::basics */ + i_client_private_textmessage_power, + i_client_needed_private_textmessage_power, + b_client_even_textmessage_send, + b_client_server_textmessage_send, + b_client_channel_textmessage_send, + b_client_offline_textmessage_send, + i_client_talk_power, + i_client_needed_talk_power, + i_client_poke_power, + i_client_needed_poke_power, + b_client_set_flag_talker, + i_client_whisper_power, + i_client_needed_whisper_power, + + /* client::modify */ + b_client_modify_description, + b_client_modify_own_description, + b_client_use_bbcode_any, + b_client_use_bbcode_url, + b_client_use_bbcode_image, + b_client_modify_dbproperties, + b_client_delete_dbproperties, + b_client_create_modify_serverquery_login, + b_client_query_create, + b_client_query_list, + b_client_query_list_own, + b_client_query_rename, + b_client_query_rename_own, + b_client_query_change_password, + b_client_query_change_own_password, + b_client_query_change_password_global, + b_client_query_delete, + b_client_query_delete_own, + + /* file_transfer */ + b_ft_ignore_password, + b_ft_transfer_list, + i_ft_file_upload_power, + i_ft_needed_file_upload_power, + i_ft_file_download_power, + i_ft_needed_file_download_power, + i_ft_file_delete_power, + i_ft_needed_file_delete_power, + i_ft_file_rename_power, + i_ft_needed_file_rename_power, + i_ft_file_browse_power, + i_ft_needed_file_browse_power, + i_ft_directory_create_power, + i_ft_needed_directory_create_power, + i_ft_quota_mb_download_per_client, + i_ft_quota_mb_upload_per_client, + + permission_id_max + }; + inline PermissionType& operator&=(PermissionType& a, int b) { return a = (PermissionType) ((int) a & b); } + + enum PermissionGroup : uint16_t { + group_begin, + global = group_begin, + global_info = b_serverinstance_permission_find, + global_vsmanage = b_virtualserver_change_template, + global_admin = b_serverinstance_stop, + global_settings = b_serverinstance_modify_templates, + + vs = global_settings, /* we dont have any permissions in here */ + vs_info = b_virtualserver_custom_search, + vs_admin = b_virtualserver_permission_reset, +#ifdef LEGENCY + vs_settings = b_virtualserver_modify_channel_temp_delete_delay_default, +#else + vs_settings = b_virtualserver_modify_music_bot_limit, +#endif + + channel = i_channel_needed_permission_modify_power, + channel_info = b_virtualserver_channel_permission_list, + channel_create = b_channel_create_modify_conversation_private, + channel_modify = b_channel_modify_temp_delete_delay, + channel_delete = i_channel_needed_delete_power, + channel_access = b_channel_ignore_description_view_power, + + group = i_group_show_name_in_tree, + group_info = b_virtualserver_channelgroup_client_list, + group_create = b_virtualserver_channelgroup_create, + group_modify = b_permission_modify_power_ignore, + group_delete = b_virtualserver_channelgroup_delete, +#ifdef LAGENCY + client = b_client_ignore_sticky, +#else + client = i_playlist_song_needed_move_power, +#endif +#ifdef LAGENCY + client_info = b_client_custom_info_view, +#else + client_info = b_virtualserver_client_permission_list, +#endif + client_admin = i_client_ban_max_bantime, + client_basic = i_client_needed_whisper_power, + client_modify = b_client_query_delete_own, + ft = i_ft_quota_mb_upload_per_client, + group_end + }; + + enum PermissionTestType { + PERMTEST_HIGHEST, + PERMTEST_ORDERED, + }; + + struct PermissionTypeEntry { + static std::shared_ptr unknown; + + PermissionTypeEntry& operator=(const PermissionTypeEntry& other) = delete; + /* + PermissionTypeEntry& operator=(const PermissionTypeEntry& other) { + this->type = other.type; + this->group = other.group; + this->name = other.name; + this->description = other.description; + this->clientSupported = other.clientSupported; + + this->grant_name = std::string() + (name[0] == 'i' ? "i" : "i") + "_needed_modify_power_" + name.substr(2); + return *this; + } + + bool operator==(const PermissionTypeEntry& other) { + return other.type == this->type; + } + */ + + PermissionType type; + PermissionGroup group; + std::string name; + std::string grant_name; + inline std::string grantName(bool useBool = false) const { return this->grant_name; } + std::string description; + + bool clientSupported = true; + + // PermissionTypeEntry(PermissionTypeEntry&& ref) : type(ref.type), group(ref.group), name(ref.name), description(ref.description), clientSupported(ref.clientSupported) {} + //PermissionTypeEntry(const PermissionTypeEntry& ref) : type(ref.type), group(ref.group), name(ref.name), description(ref.description), clientSupported(ref.clientSupported) {} + PermissionTypeEntry(PermissionTypeEntry&& ref) = delete; + PermissionTypeEntry(const PermissionTypeEntry& ref) = delete; + //PermissionTypeEntry(const PermissionTypeEntry& ref) : type(ref.type), group(ref.group), name(ref.name), description(ref.description), clientSupported(ref.clientSupported) {} + + PermissionTypeEntry(PermissionType type, PermissionGroup group, std::string name, std::string description, bool clientSupported = true) : type(type), + group(group), + name(std::move(name)), + description(std::move(description)), + clientSupported(clientSupported) { + this->grant_name = std::string() + (this->name[0] == 'i' ? "i" : "i") + "_needed_modify_power_" + this->name.substr(2); + } + }; + + namespace teamspeak { + enum GroupType { + GENERAL, + SERVER, + CHANNEL, + CLIENT + }; + + typedef std::map>> MapType; + extern MapType unmapping; + extern MapType mapping; + extern std::deque map_key(std::string key, GroupType type); //TeamSpeak -> TeaSpeak + extern std::deque unmap_key(std::string key, GroupType type); //TeaSpeak -> TeamSpeak + } + + namespace update { + enum GroupUpdateType { + NONE = 0, + + CHANNEL_GUEST = 10, + CHANNEL_VOICE = 25, + CHANNEL_OPERATOR = 35, + CHANNEL_ADMIN = 40, + + SERVER_GUEST = 15, + SERVER_NORMAL = 30, + SERVER_ADMIN = 45, + + QUERY_GUEST = 20, + QUERY_ADMIN = 50 + }; + + struct UpdatePermission { + UpdatePermission(std::string name, permission::PermissionValue value, permission::PermissionValue granted, bool negated, bool skipped) : name(std::move(name)), value(value), granted(granted), negated(negated), skipped(skipped) {} + UpdatePermission() = default; + + std::string name; + permission::PermissionValue value = permNotGranted; + permission::PermissionValue granted = permNotGranted; + + bool negated = false; + bool skipped = false; + }; + + struct UpdateEntry { + GroupUpdateType type; + UpdatePermission permission; + }; + + extern std::deque migrate; //TeamSpeak -> TeaSpeak + } + + extern std::deque> availablePermissions; + extern std::deque neededPermissions; + extern std::deque availableGroups; + + void setup_permission_resolve(); + std::shared_ptr resolvePermissionData(PermissionType); + std::shared_ptr resolvePermissionData(const std::string&); + +#define PERM_FLAG_PRIVATE 0b1 +#define PERM_FLAG_PUBLIC 0b10 + + class PermissionManager; + struct Permission { + public: + Permission(const std::shared_ptr& type, PermissionValue value, PermissionValue grant, uint16_t flagMask, std::shared_ptr ch) : type(type), channel(std::move(ch)) { + this->type = type; + this->value = value; + this->granted = grant; + this->flagMask = flagMask; + } + + + Permission(const Permission &) = delete; + + Permission() = delete; + ~Permission()= default; + + std::shared_ptr channel = nullptr; + ChannelId channelId(); + + bool hasValue(){ return value != permNotGranted; } + bool hasGrant(){ return granted != permNotGranted; } + std::shared_ptr type; + PermissionValue value; + PermissionValue granted; + uint16_t flagMask; + + bool flag_negate = false; + bool flag_skip = false; + bool dbReference = false; + }; + + class PermissionManager { + public: + PermissionManager(); + ~PermissionManager(); + + std::shared_ptr registerPermission(PermissionType, PermissionValue, const std::shared_ptr& channel, uint16_t = PERM_FLAG_PUBLIC); + std::shared_ptr registerPermission(const std::shared_ptr&, PermissionValue, const std::shared_ptr& channel, uint16_t = PERM_FLAG_PUBLIC); + //void registerAllPermissions(uint16_t flagMask); + + bool setPermissionGranted(PermissionType, PermissionValue, const std::shared_ptr& channel); + bool setPermission(PermissionType, PermissionValue, const std::shared_ptr& channel); + bool setPermission(PermissionType, PermissionValue, const std::shared_ptr& channel, bool negated, bool skiped); + void deletePermission(PermissionType, const std::shared_ptr& channel); + bool hasPermission(PermissionType, const std::shared_ptr& channel, bool testGlobal); + + /** + * @param channel Should be null for general testing + * @return , + */ + std::deque> getPermission(PermissionType, const std::shared_ptr& channel, bool testGlobal = true); + + PermissionValue getPermissionGrand(permission::PermissionTestType test, PermissionType type, const std::shared_ptr& channel, PermissionValue def = permNotGranted){ + PermissionValue result = def; + auto perms = this->getPermission(type, channel); + if(test == PermissionTestType::PERMTEST_HIGHEST) { + PermissionValue higest = permNotGranted; + for(const auto &e : perms) + if((e->granted > higest || e->granted == -1) && higest != -1) higest = e->granted; + if(higest != permNotGranted) + result = higest; + } else if(test == PermissionTestType::PERMTEST_ORDERED) + while(!perms.empty() && result == permNotGranted) { + result = perms.front()->granted; + perms.pop_front(); + }; + return result; + } + + inline PermissionValue getPermissionValue(PermissionType type, const std::shared_ptr& channel = nullptr, PermissionValue default_value = permNotGranted) { + return this->getPermissionValue(permission::PERMTEST_ORDERED, type, channel, default_value); + } + + PermissionValue getPermissionValue(permission::PermissionTestType test, PermissionType type, const std::shared_ptr& channel = nullptr, PermissionValue def = permNotGranted) { + PermissionValue result = permNotGranted; + auto perms = this->getPermission(type, channel); + if(test == permission::PERMTEST_HIGHEST) { + PermissionValue higest = permNotGranted; + for(const auto &e : perms) + if((e->value > higest || e->value == -1) && higest != -1) higest = e->value; + if(higest != permNotGranted) + result = higest; + } else if(test == PermissionTestType::PERMTEST_ORDERED) + while(!perms.empty() && result == permNotGranted) { + result = perms.front()->value; + perms.pop_front(); + }; + return result == permNotGranted ? def : result; + } + + std::vector> listPermissions(uint16_t = ~0); + std::vector> allPermissions(); + + std::deque> all_channel_specific_permissions(); + std::deque> all_channel_unspecific_permissions(); + + void fireUpdate(PermissionType); + void registerUpdateHandler(const std::function)> &fn){ updateHandler.push_back(fn); } + + void clearPermissions(){ + permissions.clear(); + } + private: + std::deque> permissions; + std::deque)>> updateHandler; + }; + + namespace v2 { + #pragma pack(push, 1) + struct PermissionFlags { + bool database_reference: 1; /* if set the permission is known within the database, else it has tp be inserted */ + bool channel_specific: 1; /* set if there are channel specific permissions */ + + bool value_set: 1; + bool grant_set: 1; + + bool skip: 1; + bool negate: 1; + + bool flag_value_update: 1; + bool flag_grant_update: 1; + + ts_always_inline bool permission_set() { + return this->value_set || this->grant_set; + } + }; + static_assert(sizeof(PermissionFlags) == 1); + + struct PermissionValues { + PermissionValue value; + PermissionValue grant; + }; + static_assert(sizeof(PermissionValues) == 8); + static constexpr PermissionValues empty_permission_values{0, 0}; + + struct PermissionContainer { + PermissionFlags flags; + PermissionValues values; + }; + static_assert(sizeof(PermissionContainer) == 9); + + struct ChannelPermissionContainer : public PermissionContainer { + PermissionType permission; + ChannelId channel_id; + }; + static_assert(sizeof(ChannelPermissionContainer) == 19); + static constexpr v2::PermissionFlags empty_flags = {false, false, false, false, false, false, false}; + static constexpr v2::PermissionContainer empty_channel_permission = {empty_flags, v2::empty_permission_values}; + + #pragma pack(pop) + + #pragma pack(push, 1) + template + struct PermissionContainerBulk { + PermissionContainer permissions[element_count]; + + PermissionContainerBulk() { + memset(this->permissions, 0, sizeof(this->permissions)); + } + }; + #pragma pack(pop) + + enum PermissionUpdateType { + do_nothing, + set_value, + delete_value + }; + + struct PermissionDBUpdateEntry { + PermissionType permission; + ChannelId channel_id; + + PermissionValues values; + PermissionUpdateType update_value; + PermissionUpdateType update_grant; + + bool flag_db: 1; /* only needs an update if set */ + bool flag_delete: 1; + bool flag_skip: 1; + bool flag_negate: 1; + }; + + struct PermissionFlaggedValue { + PermissionValue value = permNotGranted; + bool has_value = false; + + constexpr bool has_power() const { return this->has_value && (this->value > 0 || this->value == -1); } + constexpr bool has_infinite_power() const { return this->has_value && this->value == -1; } + + inline bool operator==(const PermissionFlaggedValue& other) const { return other.value == this->value && other.has_value == this->has_value; } + inline bool operator!=(const PermissionFlaggedValue& other) const { return !(*this == other); } + }; + static constexpr PermissionFlaggedValue empty_permission_flagged_value{0, false}; + + + static constexpr bool permission_granted(const PermissionFlaggedValue& required, const PermissionFlaggedValue& given, bool requires_given = true) { + if(!required.has_value) { + return !requires_given || given.has_power(); + } else if(!given.has_power()) { + return false; + } else if(given.has_infinite_power()) { + return true; + } else if(required.has_infinite_power()) { + return false; + } else { + return given.value >= required.value; + } + } + static constexpr bool permission_granted(const PermissionValue& required, const PermissionFlaggedValue& given, bool requires_given = true) { + return permission_granted({required, true}, given, requires_given); + } + + class PermissionManager { + public: + static constexpr size_t PERMISSIONS_BULK_BITS = 4; /* 16 permissions per block */ + static constexpr size_t PERMISSIONS_BULK_ENTRY_COUNT = 1 << PERMISSIONS_BULK_BITS; + static constexpr size_t BULK_COUNT = (PermissionType::permission_id_max / (1 << PERMISSIONS_BULK_BITS)) + ((PermissionType::permission_id_max % PERMISSIONS_BULK_ENTRY_COUNT == 0) ? 0 : 1); + static_assert(PERMISSIONS_BULK_ENTRY_COUNT * BULK_COUNT >= PermissionType::permission_id_max); + + PermissionManager(); + virtual ~PermissionManager(); + + /* load permissions from the database */ + void load_permission(const PermissionType&, const PermissionValues& /* values */, bool /* flag skip */, bool /* flag negate */, bool /* value present */,bool /* grant present */); + void load_permission(const PermissionType&, const PermissionValues& /* values */, ChannelId /* channel */, bool /* flag skip */, bool /* flag negate */, bool /* value present */,bool /* grant present */); + + /* general getters/setters */ + const PermissionFlags permission_flags(const PermissionType&); /* we return a "copy" because the actual permission could be deleted while we're analyzing the flags */ + ts_always_inline const PermissionFlags permission_flags(const std::shared_ptr& permission_info) { return this->permission_flags(permission_info->type); } + + const PermissionValues permission_values(const PermissionType&); + ts_always_inline const PermissionValues permission_values(const std::shared_ptr& permission_info) { return this->permission_values(permission_info->type); } + + const PermissionFlaggedValue permission_value_flagged(const PermissionType&); + ts_always_inline const PermissionFlaggedValue permission_value_flagged(const std::shared_ptr& permission_info) { return this->permission_value_flagged(permission_info->type); } + + const PermissionFlaggedValue permission_granted_flagged(const PermissionType&); + ts_always_inline const PermissionFlaggedValue permission_granted_flagged(const std::shared_ptr& permission_info) { return this->permission_granted_flagged(permission_info->type); } + + /* only worth looking up if channel_specific is set */ + const PermissionContainer channel_permission(const PermissionType& /* permission */, ChannelId /* channel id */); + ts_always_inline const PermissionContainer channel_permission(const std::shared_ptr& permission_info, ChannelId channel_id) { return this->channel_permission(permission_info->type, channel_id); } + + /* modifiers */ + void set_permission(const PermissionType& /* permission */, const PermissionValues& /* values */, const PermissionUpdateType& /* update value */, const PermissionUpdateType& /* update grant */, int /* flag skip */ = -1, int /* flag negate */ = -1); + void set_channel_permission(const PermissionType& /* permission */, ChannelId /* channel id */, const PermissionValues& /* values */, const PermissionUpdateType& /* update value */, const PermissionUpdateType& /* update grant */, int /* flag skip */ = -1, int /* flag negate */ = -1); + + /* bulk info */ + const std::vector> permissions(); + const std::vector> channel_permissions(ChannelId /* channel id */); + const std::vector> channel_permissions(); + + size_t used_memory(); + void cleanup(); + + ts_always_inline bool require_db_updates() { return this->requires_db_save; } + const std::vector flush_db_updates(); + private: + static constexpr size_t PERMISSIONS_BULK_BLOCK_MASK = (~(1 << PERMISSIONS_BULK_BITS)) & ((1 << PERMISSIONS_BULK_BITS) - 1); + + bool requires_db_save = false; + ts_always_inline void trigger_db_update() { this->requires_db_save = true; } + + spin_lock block_use_count_lock{}; + int16_t block_use_count[BULK_COUNT]; + PermissionContainerBulk* block_containers[BULK_COUNT]; + + //TODO: Bulk permissions for channels as well, specially because they're client permissions in terms of the music bot! + std::shared_mutex channel_list_lock{}; + std::deque> _channel_permissions{}; + + ts_always_inline size_t calculate_block(const PermissionType& permission) { + return permission >> PERMISSIONS_BULK_BITS; + } + + ts_always_inline size_t calculate_block_index(const PermissionType& permission) { + return permission & PERMISSIONS_BULK_BLOCK_MASK; + } + + /** + * @param block + * @return true if block exists else false + */ + ts_always_inline bool ref_block(size_t block) { + std::lock_guard use_lock(this->block_use_count_lock); + if(!this->block_containers[block]) + return false; + this->block_use_count[block]++; + assert(this->block_use_count[block] > 0); + return true; + } + + ts_always_inline void unref_block(size_t block) { + std::lock_guard use_lock(this->block_use_count_lock); + this->block_use_count[block]--; + assert(this->block_use_count[block] >= 0); + } + + ts_always_inline void ref_allocate_block(size_t block) { + std::lock_guard use_lock(this->block_use_count_lock); + if(!this->block_containers[block]) + this->block_containers[block] = new PermissionContainerBulk(); + this->block_use_count[block]++; + assert(this->block_use_count[block] > 0); + } + }; + } + } +} + +inline std::ostream& operator<<(std::ostream& os, const ts::permission::v2::PermissionFlaggedValue& c) { + if(c.has_value) + return os << c.value; + else + return os << "unset"; +} + +DEFINE_VARIABLE_TRANSFORM(ts::permission::PermissionType, VARTYPE_INT, std::to_string((int16_t) in), static_cast(in.as())); +DEFINE_VARIABLE_TRANSFORM(ts::permission::PermissionGroup, VARTYPE_INT, std::to_string((uint16_t) in), static_cast(in.as())); DEFINE_VARIABLE_TRANSFORM(ts::permission::PermissionSqlType, VARTYPE_INT, std::to_string((int8_t) in), static_cast(in.as())); \ No newline at end of file diff --git a/src/protocol/Packet.cpp b/src/protocol/Packet.cpp index a5c7582..7cb3b31 100644 --- a/src/protocol/Packet.cpp +++ b/src/protocol/Packet.cpp @@ -1,242 +1,242 @@ -// -// Created by wolverindev on 07.10.17. -// - -#include -#include -#include -#include "Packet.h" -#include "buffers.h" -#include "misc/endianness.h" - -using namespace std; -namespace ts { - namespace protocol { - - PacketTypeInfo::PacketTypeInfo(const std::string& name, PacketType type, bool ack, int max_length) noexcept { - this->data = new PacketTypeProperties{name, type, max_length, ack}; - this->owns_data = true; - - if(type < 0x0F) - types.insert({type, *this}); - } - - PacketTypeInfo::~PacketTypeInfo() { - if(this->owns_data) - delete this->data; - } - - PacketTypeInfo::PacketTypeInfo(const PacketTypeInfo &red) : data(red.data) { } - - std::map PacketTypeInfo::types; - PacketTypeInfo PacketTypeInfo::fromid(int id) { - for(const auto& elm : types) - if(elm.first == id) return elm.second; - return PacketTypeInfo::Undefined; - } - - PacketTypeInfo PacketTypeInfo::Voice = {"Voice", PacketType::VOICE, false, 1024}; - PacketTypeInfo PacketTypeInfo::VoiceWhisper = {"VoiceWhisper", PacketType::VOICE_WHISPER, false, 1024}; - PacketTypeInfo PacketTypeInfo::Command = {"Command", PacketType::COMMAND, true, 487}; - PacketTypeInfo PacketTypeInfo::CommandLow = {"CommandLow", PacketType::COMMAND_LOW, true, 487}; - PacketTypeInfo PacketTypeInfo::Ping = {"Ping", PacketType::PING, false, 1024}; - PacketTypeInfo PacketTypeInfo::Pong = {"Pong", PacketType::PONG, false, 1024}; - PacketTypeInfo PacketTypeInfo::Ack = {"Ack", PacketType::ACK, false, 1024}; - PacketTypeInfo PacketTypeInfo::AckLow = {"AckLow", PacketType::ACK_LOW, false, 1024}; - PacketTypeInfo PacketTypeInfo::Init1 = {"Init1", PacketType::INIT1, false, 1024}; - PacketTypeInfo PacketTypeInfo::Undefined = {"Undefined", PacketType::UNDEFINED, false, 1024}; - - namespace PacketFlag { - std::string to_string(PacketFlag flag){ - switch(flag){ - case Fragmented: - return "Fragmented"; - case NewProtocol: - return "NewProtocol"; - case Compressed: - return "Compressed"; - case Unencrypted: - return "Unencrypted"; - default: - return "None"; - } - } - } - - BasicPacket::BasicPacket(size_t header_length, size_t data_length) { - this->_header_length = (uint8_t) header_length; - this->_buffer = pipes::buffer(MAC_SIZE + this->_header_length + data_length); - memset(this->_buffer.data_ptr(), 0, this->_buffer.length()); - } - - BasicPacket::~BasicPacket() {} - - void BasicPacket::append_data(const std::vector &data) { - size_t length = 0; - for(const auto& buffer : data) - length += buffer.length(); - - /* we've to allocate a new buffer because out buffer is fixed in size */ - size_t index = this->_buffer.length(); - auto new_buffer = buffer::allocate_buffer(length + index); - new_buffer.write(this->_buffer, index); - - for(const auto& buffer : data) { - new_buffer.write(buffer, buffer.length(), index); - index += buffer.length(); - } - - this->_buffer = new_buffer; - } - - std::string BasicPacket::flags() const { - std::string result; - - if(this->has_flag(PacketFlag::Unencrypted)) result += string(result.empty() ? "" : " | ") + "Unencrypted"; - if(this->has_flag(PacketFlag::Compressed)) result += string(result.empty() ? "" : " | ") + "Compressed"; - if(this->has_flag(PacketFlag::Fragmented)) result += string(result.empty() ? "" : " | ") + "Fragmented"; - if(this->has_flag(PacketFlag::NewProtocol)) result += string(result.empty() ? "" : " | ") + "NewProtocol"; - - if(result.empty()) result = "none"; - return result; - } - - void BasicPacket::applyPacketId(PacketIdManager& manager) { - this->applyPacketId(manager.nextPacketId(this->type()), manager.generationId(this->type())); - } - void BasicPacket::applyPacketId(uint16_t packetId, uint16_t generationId) { - if(this->memory_state.id_branded) - throw std::logic_error("Packet already got a packet id!"); - this->memory_state.id_branded = true; - this->setPacketId(packetId, generationId); - } - Command BasicPacket::asCommand() { - return Command::parse(this->data()); - } - - /** - * @param buffer -> [mac][Header [uint16 BE packetId | [uint8](4bit flags | 4bit type)]][Data] - * @return - */ - std::unique_ptr ServerPacket::from_buffer(const pipes::buffer_view &buffer) { - auto result = make_unique(); - - result->_buffer = buffer.own_buffer(); - result->_header_length = SERVER_HEADER_SIZE; - - return result; - } - - ServerPacket::ServerPacket(uint8_t flagMask, const pipes::buffer_view& data) : BasicPacket(SERVER_HEADER_SIZE, data.length()) { - this->header()[2] = flagMask; - memcpy(this->data().data_ptr(), data.data_ptr(), data.length()); - } - - ServerPacket::ServerPacket(const PacketTypeInfo& type, const pipes::buffer_view& data) : BasicPacket(SERVER_HEADER_SIZE, data.length()) { - this->header()[2] |= (uint8_t) type.type(); - memcpy(this->data().data_ptr(), data.data_ptr(), data.length()); - } - - ServerPacket::ServerPacket(ts::protocol::PacketTypeInfo type, size_t data_length) : BasicPacket(SERVER_HEADER_SIZE, data_length) { - this->header()[2] |= type.type(); - } - - ServerPacket::~ServerPacket() {} - - uint16_t ServerPacket::packetId() const { - return be2le16(&this->header()[0]); - } - - void ServerPacket::setPacketId(uint16_t pkId, uint16_t gen) { - le2be16(pkId, &this->header()[0]); - this->genId = gen; - } - - uint16_t ServerPacket::generationId() const { - return this->genId; - } - - PacketTypeInfo ServerPacket::type() const { - return PacketTypeInfo::fromid(this->header()[2] & 0xF); - } - - - std::unique_ptr ClientPacket::from_buffer(const pipes::buffer_view &buffer) { - auto result = make_unique(); - - result->_buffer = buffer.own_buffer(); - result->_header_length = CLIENT_HEADER_SIZE; - - return result; - } - - ClientPacket::ClientPacket(const PacketTypeInfo &type, const pipes::buffer_view& data) : BasicPacket(CLIENT_HEADER_SIZE, data.length()) { - this->header()[4] = type.type() & 0xF; - memcpy(this->data().data_ptr(), data.data_ptr(), data.length()); - } - - - ClientPacket::ClientPacket(const PacketTypeInfo &type, uint8_t flag_mask, const pipes::buffer_view& data) : ClientPacket(type, data) { - this->header()[4] |= flag_mask; - } - - ClientPacket::~ClientPacket() {} - - uint16_t ClientPacket::packetId() const { - return be2le16(&this->header()[0]); - } - - uint16_t ClientPacket::generationId() const { - return this->genId; - } - - PacketTypeInfo ClientPacket::type() const { - return PacketTypeInfo::fromid(this->header()[4] & 0xF); - } - - void ClientPacket::type(const ts::protocol::PacketTypeInfo &type) { - auto& field = this->header().data_ptr()[4]; - field &= (uint8_t) ~0xF; - field |= type.type(); - } - - void ClientPacket::setPacketId(uint16_t pkId, uint16_t gen) { - this->header()[0] = (uint8_t) ((pkId >> 8) & 0xFF); - this->header()[1] = (uint8_t) ((pkId >> 0) & 0xFF); - this->genId = gen; - } - - uint16_t ClientPacket::clientId() const { - return be2le16(&this->header()[2]); - } - - void ClientPacket::clientId(uint16_t clId) { - this->header()[2] = clId >> 8; - this->header()[3] = clId & 0xFF; - } - - - uint16_t ClientPacketParser::packet_id() const { return be2le16(this->_buffer.data_ptr(), ClientPacketParser::kHeaderOffset + 0); } - uint16_t ClientPacketParser::client_id() const { return be2le16(this->_buffer.data_ptr(), ClientPacketParser::kHeaderOffset + 2); } - uint8_t ClientPacketParser::type() const { return (uint8_t) this->_buffer[ClientPacketParser::kHeaderOffset + 4] & 0xFU; } - uint8_t ClientPacketParser::flags() const { return (uint8_t) this->_buffer[ClientPacketParser::kHeaderOffset + 4] & 0xF0U; } - - bool ClientPacketParser::is_encrypted() const { - if(this->decrypted) return false; - - return (this->flags() & PacketFlag::Unencrypted) == 0; - } - - bool ClientPacketParser::is_compressed() const { - if(this->uncompressed) return false; - - return (this->flags() & PacketFlag::Compressed) > 0; - } - - bool ClientPacketParser::is_fragmented() const { - if(this->defragmented) return false; - - return (this->flags() & PacketFlag::Fragmented) > 0; - } - } +// +// Created by wolverindev on 07.10.17. +// + +#include +#include +#include +#include "Packet.h" +#include "buffers.h" +#include "misc/endianness.h" + +using namespace std; +namespace ts { + namespace protocol { + + PacketTypeInfo::PacketTypeInfo(const std::string& name, PacketType type, bool ack, int max_length) noexcept { + this->data = new PacketTypeProperties{name, type, max_length, ack}; + this->owns_data = true; + + if(type < 0x0F) + types.insert({type, *this}); + } + + PacketTypeInfo::~PacketTypeInfo() { + if(this->owns_data) + delete this->data; + } + + PacketTypeInfo::PacketTypeInfo(const PacketTypeInfo &red) : data(red.data) { } + + std::map PacketTypeInfo::types; + PacketTypeInfo PacketTypeInfo::fromid(int id) { + for(const auto& elm : types) + if(elm.first == id) return elm.second; + return PacketTypeInfo::Undefined; + } + + PacketTypeInfo PacketTypeInfo::Voice = {"Voice", PacketType::VOICE, false, 1024}; + PacketTypeInfo PacketTypeInfo::VoiceWhisper = {"VoiceWhisper", PacketType::VOICE_WHISPER, false, 1024}; + PacketTypeInfo PacketTypeInfo::Command = {"Command", PacketType::COMMAND, true, 487}; + PacketTypeInfo PacketTypeInfo::CommandLow = {"CommandLow", PacketType::COMMAND_LOW, true, 487}; + PacketTypeInfo PacketTypeInfo::Ping = {"Ping", PacketType::PING, false, 1024}; + PacketTypeInfo PacketTypeInfo::Pong = {"Pong", PacketType::PONG, false, 1024}; + PacketTypeInfo PacketTypeInfo::Ack = {"Ack", PacketType::ACK, false, 1024}; + PacketTypeInfo PacketTypeInfo::AckLow = {"AckLow", PacketType::ACK_LOW, false, 1024}; + PacketTypeInfo PacketTypeInfo::Init1 = {"Init1", PacketType::INIT1, false, 1024}; + PacketTypeInfo PacketTypeInfo::Undefined = {"Undefined", PacketType::UNDEFINED, false, 1024}; + + namespace PacketFlag { + std::string to_string(PacketFlag flag){ + switch(flag){ + case Fragmented: + return "Fragmented"; + case NewProtocol: + return "NewProtocol"; + case Compressed: + return "Compressed"; + case Unencrypted: + return "Unencrypted"; + default: + return "None"; + } + } + } + + BasicPacket::BasicPacket(size_t header_length, size_t data_length) { + this->_header_length = (uint8_t) header_length; + this->_buffer = pipes::buffer(MAC_SIZE + this->_header_length + data_length); + memset(this->_buffer.data_ptr(), 0, this->_buffer.length()); + } + + BasicPacket::~BasicPacket() {} + + void BasicPacket::append_data(const std::vector &data) { + size_t length = 0; + for(const auto& buffer : data) + length += buffer.length(); + + /* we've to allocate a new buffer because out buffer is fixed in size */ + size_t index = this->_buffer.length(); + auto new_buffer = buffer::allocate_buffer(length + index); + new_buffer.write(this->_buffer, index); + + for(const auto& buffer : data) { + new_buffer.write(buffer, buffer.length(), index); + index += buffer.length(); + } + + this->_buffer = new_buffer; + } + + std::string BasicPacket::flags() const { + std::string result; + + if(this->has_flag(PacketFlag::Unencrypted)) result += string(result.empty() ? "" : " | ") + "Unencrypted"; + if(this->has_flag(PacketFlag::Compressed)) result += string(result.empty() ? "" : " | ") + "Compressed"; + if(this->has_flag(PacketFlag::Fragmented)) result += string(result.empty() ? "" : " | ") + "Fragmented"; + if(this->has_flag(PacketFlag::NewProtocol)) result += string(result.empty() ? "" : " | ") + "NewProtocol"; + + if(result.empty()) result = "none"; + return result; + } + + void BasicPacket::applyPacketId(PacketIdManager& manager) { + this->applyPacketId(manager.nextPacketId(this->type()), manager.generationId(this->type())); + } + void BasicPacket::applyPacketId(uint16_t packetId, uint16_t generationId) { + if(this->memory_state.id_branded) + throw std::logic_error("Packet already got a packet id!"); + this->memory_state.id_branded = true; + this->setPacketId(packetId, generationId); + } + Command BasicPacket::asCommand() { + return Command::parse(this->data()); + } + + /** + * @param buffer -> [mac][Header [uint16 BE packetId | [uint8](4bit flags | 4bit type)]][Data] + * @return + */ + std::unique_ptr ServerPacket::from_buffer(const pipes::buffer_view &buffer) { + auto result = make_unique(); + + result->_buffer = buffer.own_buffer(); + result->_header_length = SERVER_HEADER_SIZE; + + return result; + } + + ServerPacket::ServerPacket(uint8_t flagMask, const pipes::buffer_view& data) : BasicPacket(SERVER_HEADER_SIZE, data.length()) { + this->header()[2] = flagMask; + memcpy(this->data().data_ptr(), data.data_ptr(), data.length()); + } + + ServerPacket::ServerPacket(const PacketTypeInfo& type, const pipes::buffer_view& data) : BasicPacket(SERVER_HEADER_SIZE, data.length()) { + this->header()[2] |= (uint8_t) type.type(); + memcpy(this->data().data_ptr(), data.data_ptr(), data.length()); + } + + ServerPacket::ServerPacket(ts::protocol::PacketTypeInfo type, size_t data_length) : BasicPacket(SERVER_HEADER_SIZE, data_length) { + this->header()[2] |= type.type(); + } + + ServerPacket::~ServerPacket() {} + + uint16_t ServerPacket::packetId() const { + return be2le16(&this->header()[0]); + } + + void ServerPacket::setPacketId(uint16_t pkId, uint16_t gen) { + le2be16(pkId, &this->header()[0]); + this->genId = gen; + } + + uint16_t ServerPacket::generationId() const { + return this->genId; + } + + PacketTypeInfo ServerPacket::type() const { + return PacketTypeInfo::fromid(this->header()[2] & 0xF); + } + + + std::unique_ptr ClientPacket::from_buffer(const pipes::buffer_view &buffer) { + auto result = make_unique(); + + result->_buffer = buffer.own_buffer(); + result->_header_length = CLIENT_HEADER_SIZE; + + return result; + } + + ClientPacket::ClientPacket(const PacketTypeInfo &type, const pipes::buffer_view& data) : BasicPacket(CLIENT_HEADER_SIZE, data.length()) { + this->header()[4] = type.type() & 0xF; + memcpy(this->data().data_ptr(), data.data_ptr(), data.length()); + } + + + ClientPacket::ClientPacket(const PacketTypeInfo &type, uint8_t flag_mask, const pipes::buffer_view& data) : ClientPacket(type, data) { + this->header()[4] |= flag_mask; + } + + ClientPacket::~ClientPacket() {} + + uint16_t ClientPacket::packetId() const { + return be2le16(&this->header()[0]); + } + + uint16_t ClientPacket::generationId() const { + return this->genId; + } + + PacketTypeInfo ClientPacket::type() const { + return PacketTypeInfo::fromid(this->header()[4] & 0xF); + } + + void ClientPacket::type(const ts::protocol::PacketTypeInfo &type) { + auto& field = this->header().data_ptr()[4]; + field &= (uint8_t) ~0xF; + field |= type.type(); + } + + void ClientPacket::setPacketId(uint16_t pkId, uint16_t gen) { + this->header()[0] = (uint8_t) ((pkId >> 8) & 0xFF); + this->header()[1] = (uint8_t) ((pkId >> 0) & 0xFF); + this->genId = gen; + } + + uint16_t ClientPacket::clientId() const { + return be2le16(&this->header()[2]); + } + + void ClientPacket::clientId(uint16_t clId) { + this->header()[2] = clId >> 8; + this->header()[3] = clId & 0xFF; + } + + + uint16_t ClientPacketParser::packet_id() const { return be2le16(this->_buffer.data_ptr(), ClientPacketParser::kHeaderOffset + 0); } + uint16_t ClientPacketParser::client_id() const { return be2le16(this->_buffer.data_ptr(), ClientPacketParser::kHeaderOffset + 2); } + uint8_t ClientPacketParser::type() const { return (uint8_t) this->_buffer[ClientPacketParser::kHeaderOffset + 4] & 0xFU; } + uint8_t ClientPacketParser::flags() const { return (uint8_t) this->_buffer[ClientPacketParser::kHeaderOffset + 4] & 0xF0U; } + + bool ClientPacketParser::is_encrypted() const { + if(this->decrypted) return false; + + return (this->flags() & PacketFlag::Unencrypted) == 0; + } + + bool ClientPacketParser::is_compressed() const { + if(this->uncompressed) return false; + + return (this->flags() & PacketFlag::Compressed) > 0; + } + + bool ClientPacketParser::is_fragmented() const { + if(this->defragmented) return false; + + return (this->flags() & PacketFlag::Fragmented) > 0; + } + } } \ No newline at end of file diff --git a/src/protocol/Packet.h b/src/protocol/Packet.h index 5581be7..ce456bf 100644 --- a/src/protocol/Packet.h +++ b/src/protocol/Packet.h @@ -1,371 +1,371 @@ -#pragma once - -#include -#include -#include -#include -#include -#include -#include "../query/Command.h" - -namespace ts { - namespace protocol { - enum PacketType : uint8_t { - VOICE = 0x00, - VOICE_WHISPER = 0x01, - COMMAND = 0x02, - COMMAND_LOW = 0x03, - PING = 0x04, - PONG = 0x05, - ACK = 0x06, - ACK_LOW = 0x07, - INIT1 = 0x08, - - PACKET_MAX = INIT1, - UNDEFINED = 0xFF - }; - - struct PacketTypeProperties { - std::string name; - PacketType type; - int max_length; - bool requireAcknowledge; - }; - - class PacketTypeInfo { - public: - static PacketTypeInfo Voice; - static PacketTypeInfo VoiceWhisper; - static PacketTypeInfo Command; - static PacketTypeInfo CommandLow; - static PacketTypeInfo Ping; - static PacketTypeInfo Pong; - static PacketTypeInfo Ack; - static PacketTypeInfo AckLow; - static PacketTypeInfo Init1; - static PacketTypeInfo Undefined; - - static PacketTypeInfo fromid(int id); - - std::string name() const { return data->name; } - PacketType type() const { return data->type; } - - bool requireAcknowledge(){ return data->requireAcknowledge; } - - bool operator==(const PacketTypeInfo& other) const { - return other.data->type == this->data->type; - } - - bool operator!=(const PacketTypeInfo& other){ - return other.data->type != this->data->type; - } - - int max_length() const { return data->max_length; } - inline bool fragmentable() { return *this == PacketTypeInfo::Command || *this == PacketTypeInfo::CommandLow; } - inline bool compressable() { return *this == PacketTypeInfo::Command || *this == PacketTypeInfo::CommandLow; } - - PacketTypeInfo(const PacketTypeInfo&); - PacketTypeInfo(PacketTypeInfo&& remote) : data(remote.data) {} - - ~PacketTypeInfo(); - private: - static std::map types; - PacketTypeInfo(const std::string&, PacketType, bool, int) noexcept; - PacketTypeProperties* data; - bool owns_data = false; - }; - - struct PacketIdManagerData { - PacketIdManagerData(){ - memset(this->packetCounter, 0, sizeof(uint32_t) * 16); - } - uint32_t packetCounter[16]{}; - }; - - class PacketIdManager { - public: - PacketIdManager() : data(new PacketIdManagerData){} - ~PacketIdManager() = default; - PacketIdManager(const PacketIdManager& ref) : data(ref.data) {} - PacketIdManager(PacketIdManager&& ref) : data(std::move(ref.data)) {} - - uint16_t nextPacketId(const PacketTypeInfo &type){ - return static_cast(data->packetCounter[type.type()]++ & 0xFFFF); - } - - uint16_t currentPacketId(const PacketTypeInfo &type){ - return static_cast(data->packetCounter[type.type()] & 0xFFFF); - } - - uint16_t generationId(const PacketTypeInfo &type){ - return static_cast((data->packetCounter[type.type()] >> 16) & 0xFFFF); - } - - void reset() { - memset(&data->packetCounter[0], 0, sizeof(uint32_t) * 16); - } - private: - std::shared_ptr data; - }; - - namespace PacketFlag { - enum PacketFlag : uint8_t { - None = 0x00, - Fragmented = 0x10, //If packet type voice then its toggle the CELT Mono - NewProtocol = 0x20, - Compressed = 0x40, //If packet type voice than its the header - Unencrypted = 0x80 - }; - typedef uint8_t PacketFlags; - - std::string to_string(PacketFlag flag); - } - - #define MAC_SIZE 8 - #define SERVER_HEADER_SIZE 3 - #define CLIENT_HEADER_SIZE 5 - - class BasicPacket { - public: - explicit BasicPacket(size_t header_length, size_t data_length); - virtual ~BasicPacket(); - - BasicPacket(const BasicPacket&) = delete; - BasicPacket(BasicPacket&&) = delete; - - virtual uint16_t packetId() const = 0; - virtual uint16_t generationId() const = 0; - virtual PacketTypeInfo type() const = 0; - - /* packet flag info */ - inline bool has_flag(PacketFlag::PacketFlag flag) const { return this->_flags_type_byte() & flag; } - inline uint8_t flag_mask() const { return this->_flags_type_byte(); }; - [[nodiscard]] std::string flags() const; - - /* manipulate flags */ - inline void set_flags(PacketFlag::PacketFlags flags) { - uint8_t& byte = this->_flags_type_byte(); - byte &= 0xF; /* clear all flags */ - byte |= (flags & 0xF0); - } - inline void enable_flag(PacketFlag::PacketFlag flag){ this->toggle_flag(flag, true); } - inline void toggle_flag(PacketFlag::PacketFlag flag, bool state) { - if(state) - this->_flags_type_byte() |= flag; - else - this->_flags_type_byte() &= (uint8_t) ~flag; - } - - virtual void applyPacketId(PacketIdManager &); - virtual void applyPacketId(uint16_t, uint16_t); - - void setListener(std::unique_ptr> listener){ - if(!this->type().requireAcknowledge()) - throw std::logic_error("Packet type does not support a acknowledge listener!"); - this->listener = std::move(listener); - } - inline std::unique_ptr>& getListener() { return this->listener; } - - inline size_t length() const { return this->_buffer.length(); } - inline const pipes::buffer_view mac() const { return this->_buffer.view(0, MAC_SIZE); } - inline pipes::buffer mac() { return this->_buffer.range(0, MAC_SIZE); } - inline size_t mac_length() const { return MAC_SIZE; } - - inline const pipes::buffer_view header() const { return this->_buffer.view(MAC_SIZE, this->_header_length); } - inline pipes::buffer header() { return this->_buffer.range(MAC_SIZE, this->_header_length); } - inline size_t header_length() const { return this->_header_length; } - - inline size_t data_length() const { return this->_buffer.length() - (MAC_SIZE + this->_header_length); } - inline const pipes::buffer_view data() const { return this->_buffer.view(MAC_SIZE + this->_header_length); } - inline pipes::buffer data() { return this->_buffer.range(MAC_SIZE + this->_header_length); } - - void append_data(const std::vector &data); - - inline void data(const pipes::buffer_view &data){ - this->_buffer.resize(MAC_SIZE + this->_header_length + data.length()); - memcpy((char*) this->_buffer.data_ptr() + MAC_SIZE + this->_header_length, data.data_ptr(), data.length()); - } - - inline void mac(const pipes::buffer_view &_new){ - assert(_new.length() >= MAC_SIZE); - memcpy(this->_buffer.data_ptr(), _new.data_ptr(), MAC_SIZE); - } - - [[nodiscard]] inline bool isEncrypted() const { return this->memory_state.encrypted; } - inline void setEncrypted(bool flag){ this->memory_state.encrypted = flag; } - - [[nodiscard]] inline bool isCompressed() const { return this->memory_state.compressed; } - inline void setCompressed(bool flag){ this->memory_state.compressed = flag; } - - [[nodiscard]] inline bool isFragmentEntry() const { return this->memory_state.fragment_entry; } - inline void setFragmentedEntry(bool flag){ this->memory_state.fragment_entry = flag; } - - Command asCommand(); - - //Has the size of a byte - union { -#ifdef WIN32 - __pragma(pack(push, 1)) -#endif - struct { - bool encrypted: 1; - bool compressed: 1; - bool fragment_entry: 1; - - bool id_branded: 1; - } -#ifdef WIN32 - __pragma(pack(pop)); -#else - __attribute__((packed)); -#endif - - uint8_t flags = 0; - } memory_state; - - pipes::buffer buffer() { return this->_buffer; } - void buffer(pipes::buffer buffer) { - assert(buffer.length() >= this->_header_length + MAC_SIZE); - this->_buffer = std::move(buffer); - } - protected: - BasicPacket() = default; - - virtual const uint8_t& _flags_type_byte() const = 0; - virtual uint8_t& _flags_type_byte() = 0; - - virtual void setPacketId(uint16_t, uint16_t) = 0; - uint8_t _header_length; - pipes::buffer _buffer; - - uint16_t genId = 0; - std::unique_ptr> listener; - }; - - - /** - * Packet from the client - */ - class ClientPacket : public BasicPacket { - friend std::unique_ptr std::make_unique(); - public: - static constexpr size_t META_MAC_SIZE = 8; - static constexpr size_t META_HEADER_SIZE = CLIENT_HEADER_SIZE; - static constexpr size_t META_SIZE = META_MAC_SIZE + META_HEADER_SIZE; - - [[nodiscard]] static std::unique_ptr from_buffer(const pipes::buffer_view& buffer); - - ClientPacket(const PacketTypeInfo& type, const pipes::buffer_view& data); - ClientPacket(const PacketTypeInfo& type, uint8_t flag_mask, const pipes::buffer_view& data); - ~ClientPacket() override; - ClientPacket(const ClientPacket&) = delete; - ClientPacket(ClientPacket&&) = delete; - - uint16_t clientId() const; - void clientId(uint16_t); - - uint16_t packetId() const override; - - uint16_t generationId() const override; - void generationId(uint16_t generation) { this->genId = generation; } - - PacketTypeInfo type() const override; - void type(const PacketTypeInfo&); - - private: - ClientPacket() = default; - - const uint8_t &_flags_type_byte() const override { - return this->header().data_ptr()[4]; - } - - uint8_t &_flags_type_byte() override { - return this->header().data_ptr()[4]; - } - - void setPacketId(uint16_t, uint16_t) override; - }; - - class ClientPacketParser { - public: - constexpr static auto kHeaderOffset = 8; - constexpr static auto kHeaderLength = CLIENT_HEADER_SIZE; - - constexpr static auto kPayloadOffset = kHeaderOffset + CLIENT_HEADER_SIZE; - explicit ClientPacketParser(pipes::buffer_view buffer) : _buffer{std::move(buffer)} {} - ClientPacketParser(const ClientPacketParser&) = delete; - - [[nodiscard]] inline bool valid() const { - if(this->_buffer.length() < kPayloadOffset) return false; - return this->type() <= 8; - } - - [[nodiscard]] inline const void* data_ptr() const { return this->_buffer.data_ptr(); } - [[nodiscard]] inline void* mutable_data_ptr() { return (void*) this->_buffer.data_ptr(); } - - [[nodiscard]] inline pipes::buffer_view buffer() const { return this->_buffer; } - [[nodiscard]] inline pipes::buffer_view mac() const { return this->_buffer.view(0, 8); } - [[nodiscard]] inline pipes::buffer_view payload() const { return this->_buffer.view(kPayloadOffset); } - [[nodiscard]] inline size_t payload_length() const { return this->_buffer.length() - kPayloadOffset; } - - [[nodiscard]] uint16_t client_id() const; - [[nodiscard]] uint16_t packet_id() const; - [[nodiscard]] uint8_t type() const; - [[nodiscard]] uint8_t flags() const; - - [[nodiscard]] bool is_encrypted() const; - [[nodiscard]] bool is_compressed() const; - [[nodiscard]] bool is_fragmented() const; - - [[nodiscard]] uint16_t estimated_generation() const { return this->generation; } - void set_estimated_generation(uint16_t gen) { this->generation = gen; } - - inline void set_decrypted() { this->decrypted = true; } - inline void set_uncompressed() { this->uncompressed = true; } - inline void set_defragmented() { this->defragmented = true; } - private: - uint16_t generation{}; - bool decrypted{false}, uncompressed{false}, defragmented{false}; - pipes::buffer_view _buffer{}; - }; - - /** - * Packet from the server - */ - class ServerPacket : public BasicPacket { - friend std::unique_ptr std::make_unique(); - public: - static constexpr size_t META_MAC_SIZE = 8; - static constexpr size_t META_HEADER_SIZE = SERVER_HEADER_SIZE; - static constexpr size_t META_SIZE = META_MAC_SIZE + META_HEADER_SIZE; - - [[nodiscard]] static std::unique_ptr from_buffer(const pipes::buffer_view& buffer); - - ServerPacket(uint8_t flagMask, const pipes::buffer_view& data); - ServerPacket(const PacketTypeInfo& type, const pipes::buffer_view& data); - ServerPacket(PacketTypeInfo type, size_t /* data length */); - ~ServerPacket() override; - - ServerPacket(const ServerPacket&) = delete; - ServerPacket(ServerPacket&&) = delete; - - [[nodiscard]] uint16_t packetId() const override; - [[nodiscard]] uint16_t generationId() const override; - void generationId(uint16_t generation) { this->genId = generation; } - [[nodiscard]] PacketTypeInfo type() const override; - - private: - ServerPacket() = default; - - [[nodiscard]] const uint8_t &_flags_type_byte() const override { - return this->header().data_ptr()[2]; - } - - uint8_t &_flags_type_byte() override { - return this->header().data_ptr()[2]; - } - - void setPacketId(uint16_t, uint16_t) override; - }; - } +#pragma once + +#include +#include +#include +#include +#include +#include +#include "../query/Command.h" + +namespace ts { + namespace protocol { + enum PacketType : uint8_t { + VOICE = 0x00, + VOICE_WHISPER = 0x01, + COMMAND = 0x02, + COMMAND_LOW = 0x03, + PING = 0x04, + PONG = 0x05, + ACK = 0x06, + ACK_LOW = 0x07, + INIT1 = 0x08, + + PACKET_MAX = INIT1, + UNDEFINED = 0xFF + }; + + struct PacketTypeProperties { + std::string name; + PacketType type; + int max_length; + bool requireAcknowledge; + }; + + class PacketTypeInfo { + public: + static PacketTypeInfo Voice; + static PacketTypeInfo VoiceWhisper; + static PacketTypeInfo Command; + static PacketTypeInfo CommandLow; + static PacketTypeInfo Ping; + static PacketTypeInfo Pong; + static PacketTypeInfo Ack; + static PacketTypeInfo AckLow; + static PacketTypeInfo Init1; + static PacketTypeInfo Undefined; + + static PacketTypeInfo fromid(int id); + + std::string name() const { return data->name; } + PacketType type() const { return data->type; } + + bool requireAcknowledge(){ return data->requireAcknowledge; } + + bool operator==(const PacketTypeInfo& other) const { + return other.data->type == this->data->type; + } + + bool operator!=(const PacketTypeInfo& other){ + return other.data->type != this->data->type; + } + + int max_length() const { return data->max_length; } + inline bool fragmentable() { return *this == PacketTypeInfo::Command || *this == PacketTypeInfo::CommandLow; } + inline bool compressable() { return *this == PacketTypeInfo::Command || *this == PacketTypeInfo::CommandLow; } + + PacketTypeInfo(const PacketTypeInfo&); + PacketTypeInfo(PacketTypeInfo&& remote) : data(remote.data) {} + + ~PacketTypeInfo(); + private: + static std::map types; + PacketTypeInfo(const std::string&, PacketType, bool, int) noexcept; + PacketTypeProperties* data; + bool owns_data = false; + }; + + struct PacketIdManagerData { + PacketIdManagerData(){ + memset(this->packetCounter, 0, sizeof(uint32_t) * 16); + } + uint32_t packetCounter[16]{}; + }; + + class PacketIdManager { + public: + PacketIdManager() : data(new PacketIdManagerData){} + ~PacketIdManager() = default; + PacketIdManager(const PacketIdManager& ref) : data(ref.data) {} + PacketIdManager(PacketIdManager&& ref) : data(std::move(ref.data)) {} + + uint16_t nextPacketId(const PacketTypeInfo &type){ + return static_cast(data->packetCounter[type.type()]++ & 0xFFFF); + } + + uint16_t currentPacketId(const PacketTypeInfo &type){ + return static_cast(data->packetCounter[type.type()] & 0xFFFF); + } + + uint16_t generationId(const PacketTypeInfo &type){ + return static_cast((data->packetCounter[type.type()] >> 16) & 0xFFFF); + } + + void reset() { + memset(&data->packetCounter[0], 0, sizeof(uint32_t) * 16); + } + private: + std::shared_ptr data; + }; + + namespace PacketFlag { + enum PacketFlag : uint8_t { + None = 0x00, + Fragmented = 0x10, //If packet type voice then its toggle the CELT Mono + NewProtocol = 0x20, + Compressed = 0x40, //If packet type voice than its the header + Unencrypted = 0x80 + }; + typedef uint8_t PacketFlags; + + std::string to_string(PacketFlag flag); + } + + #define MAC_SIZE 8 + #define SERVER_HEADER_SIZE 3 + #define CLIENT_HEADER_SIZE 5 + + class BasicPacket { + public: + explicit BasicPacket(size_t header_length, size_t data_length); + virtual ~BasicPacket(); + + BasicPacket(const BasicPacket&) = delete; + BasicPacket(BasicPacket&&) = delete; + + virtual uint16_t packetId() const = 0; + virtual uint16_t generationId() const = 0; + virtual PacketTypeInfo type() const = 0; + + /* packet flag info */ + inline bool has_flag(PacketFlag::PacketFlag flag) const { return this->_flags_type_byte() & flag; } + inline uint8_t flag_mask() const { return this->_flags_type_byte(); }; + [[nodiscard]] std::string flags() const; + + /* manipulate flags */ + inline void set_flags(PacketFlag::PacketFlags flags) { + uint8_t& byte = this->_flags_type_byte(); + byte &= 0xF; /* clear all flags */ + byte |= (flags & 0xF0); + } + inline void enable_flag(PacketFlag::PacketFlag flag){ this->toggle_flag(flag, true); } + inline void toggle_flag(PacketFlag::PacketFlag flag, bool state) { + if(state) + this->_flags_type_byte() |= flag; + else + this->_flags_type_byte() &= (uint8_t) ~flag; + } + + virtual void applyPacketId(PacketIdManager &); + virtual void applyPacketId(uint16_t, uint16_t); + + void setListener(std::unique_ptr> listener){ + if(!this->type().requireAcknowledge()) + throw std::logic_error("Packet type does not support a acknowledge listener!"); + this->listener = std::move(listener); + } + inline std::unique_ptr>& getListener() { return this->listener; } + + inline size_t length() const { return this->_buffer.length(); } + inline const pipes::buffer_view mac() const { return this->_buffer.view(0, MAC_SIZE); } + inline pipes::buffer mac() { return this->_buffer.range(0, MAC_SIZE); } + inline size_t mac_length() const { return MAC_SIZE; } + + inline const pipes::buffer_view header() const { return this->_buffer.view(MAC_SIZE, this->_header_length); } + inline pipes::buffer header() { return this->_buffer.range(MAC_SIZE, this->_header_length); } + inline size_t header_length() const { return this->_header_length; } + + inline size_t data_length() const { return this->_buffer.length() - (MAC_SIZE + this->_header_length); } + inline const pipes::buffer_view data() const { return this->_buffer.view(MAC_SIZE + this->_header_length); } + inline pipes::buffer data() { return this->_buffer.range(MAC_SIZE + this->_header_length); } + + void append_data(const std::vector &data); + + inline void data(const pipes::buffer_view &data){ + this->_buffer.resize(MAC_SIZE + this->_header_length + data.length()); + memcpy((char*) this->_buffer.data_ptr() + MAC_SIZE + this->_header_length, data.data_ptr(), data.length()); + } + + inline void mac(const pipes::buffer_view &_new){ + assert(_new.length() >= MAC_SIZE); + memcpy(this->_buffer.data_ptr(), _new.data_ptr(), MAC_SIZE); + } + + [[nodiscard]] inline bool isEncrypted() const { return this->memory_state.encrypted; } + inline void setEncrypted(bool flag){ this->memory_state.encrypted = flag; } + + [[nodiscard]] inline bool isCompressed() const { return this->memory_state.compressed; } + inline void setCompressed(bool flag){ this->memory_state.compressed = flag; } + + [[nodiscard]] inline bool isFragmentEntry() const { return this->memory_state.fragment_entry; } + inline void setFragmentedEntry(bool flag){ this->memory_state.fragment_entry = flag; } + + Command asCommand(); + + //Has the size of a byte + union { +#ifdef WIN32 + __pragma(pack(push, 1)) +#endif + struct { + bool encrypted: 1; + bool compressed: 1; + bool fragment_entry: 1; + + bool id_branded: 1; + } +#ifdef WIN32 + __pragma(pack(pop)); +#else + __attribute__((packed)); +#endif + + uint8_t flags = 0; + } memory_state; + + pipes::buffer buffer() { return this->_buffer; } + void buffer(pipes::buffer buffer) { + assert(buffer.length() >= this->_header_length + MAC_SIZE); + this->_buffer = std::move(buffer); + } + protected: + BasicPacket() = default; + + virtual const uint8_t& _flags_type_byte() const = 0; + virtual uint8_t& _flags_type_byte() = 0; + + virtual void setPacketId(uint16_t, uint16_t) = 0; + uint8_t _header_length; + pipes::buffer _buffer; + + uint16_t genId = 0; + std::unique_ptr> listener; + }; + + + /** + * Packet from the client + */ + class ClientPacket : public BasicPacket { + friend std::unique_ptr std::make_unique(); + public: + static constexpr size_t META_MAC_SIZE = 8; + static constexpr size_t META_HEADER_SIZE = CLIENT_HEADER_SIZE; + static constexpr size_t META_SIZE = META_MAC_SIZE + META_HEADER_SIZE; + + [[nodiscard]] static std::unique_ptr from_buffer(const pipes::buffer_view& buffer); + + ClientPacket(const PacketTypeInfo& type, const pipes::buffer_view& data); + ClientPacket(const PacketTypeInfo& type, uint8_t flag_mask, const pipes::buffer_view& data); + ~ClientPacket() override; + ClientPacket(const ClientPacket&) = delete; + ClientPacket(ClientPacket&&) = delete; + + uint16_t clientId() const; + void clientId(uint16_t); + + uint16_t packetId() const override; + + uint16_t generationId() const override; + void generationId(uint16_t generation) { this->genId = generation; } + + PacketTypeInfo type() const override; + void type(const PacketTypeInfo&); + + private: + ClientPacket() = default; + + const uint8_t &_flags_type_byte() const override { + return this->header().data_ptr()[4]; + } + + uint8_t &_flags_type_byte() override { + return this->header().data_ptr()[4]; + } + + void setPacketId(uint16_t, uint16_t) override; + }; + + class ClientPacketParser { + public: + constexpr static auto kHeaderOffset = 8; + constexpr static auto kHeaderLength = CLIENT_HEADER_SIZE; + + constexpr static auto kPayloadOffset = kHeaderOffset + CLIENT_HEADER_SIZE; + explicit ClientPacketParser(pipes::buffer_view buffer) : _buffer{std::move(buffer)} {} + ClientPacketParser(const ClientPacketParser&) = delete; + + [[nodiscard]] inline bool valid() const { + if(this->_buffer.length() < kPayloadOffset) return false; + return this->type() <= 8; + } + + [[nodiscard]] inline const void* data_ptr() const { return this->_buffer.data_ptr(); } + [[nodiscard]] inline void* mutable_data_ptr() { return (void*) this->_buffer.data_ptr(); } + + [[nodiscard]] inline pipes::buffer_view buffer() const { return this->_buffer; } + [[nodiscard]] inline pipes::buffer_view mac() const { return this->_buffer.view(0, 8); } + [[nodiscard]] inline pipes::buffer_view payload() const { return this->_buffer.view(kPayloadOffset); } + [[nodiscard]] inline size_t payload_length() const { return this->_buffer.length() - kPayloadOffset; } + + [[nodiscard]] uint16_t client_id() const; + [[nodiscard]] uint16_t packet_id() const; + [[nodiscard]] uint8_t type() const; + [[nodiscard]] uint8_t flags() const; + + [[nodiscard]] bool is_encrypted() const; + [[nodiscard]] bool is_compressed() const; + [[nodiscard]] bool is_fragmented() const; + + [[nodiscard]] uint16_t estimated_generation() const { return this->generation; } + void set_estimated_generation(uint16_t gen) { this->generation = gen; } + + inline void set_decrypted() { this->decrypted = true; } + inline void set_uncompressed() { this->uncompressed = true; } + inline void set_defragmented() { this->defragmented = true; } + private: + uint16_t generation{}; + bool decrypted{false}, uncompressed{false}, defragmented{false}; + pipes::buffer_view _buffer{}; + }; + + /** + * Packet from the server + */ + class ServerPacket : public BasicPacket { + friend std::unique_ptr std::make_unique(); + public: + static constexpr size_t META_MAC_SIZE = 8; + static constexpr size_t META_HEADER_SIZE = SERVER_HEADER_SIZE; + static constexpr size_t META_SIZE = META_MAC_SIZE + META_HEADER_SIZE; + + [[nodiscard]] static std::unique_ptr from_buffer(const pipes::buffer_view& buffer); + + ServerPacket(uint8_t flagMask, const pipes::buffer_view& data); + ServerPacket(const PacketTypeInfo& type, const pipes::buffer_view& data); + ServerPacket(PacketTypeInfo type, size_t /* data length */); + ~ServerPacket() override; + + ServerPacket(const ServerPacket&) = delete; + ServerPacket(ServerPacket&&) = delete; + + [[nodiscard]] uint16_t packetId() const override; + [[nodiscard]] uint16_t generationId() const override; + void generationId(uint16_t generation) { this->genId = generation; } + [[nodiscard]] PacketTypeInfo type() const override; + + private: + ServerPacket() = default; + + [[nodiscard]] const uint8_t &_flags_type_byte() const override { + return this->header().data_ptr()[2]; + } + + uint8_t &_flags_type_byte() override { + return this->header().data_ptr()[2]; + } + + void setPacketId(uint16_t, uint16_t) override; + }; + } } \ No newline at end of file diff --git a/src/query/Command.h b/src/query/Command.h index 215f4b0..52f964b 100644 --- a/src/query/Command.h +++ b/src/query/Command.h @@ -1,220 +1,220 @@ -#pragma once - -#ifdef byte - #define byte asdd - #ifndef WIN32 - #warning The byte macro is already defined! Undefining it! - #endif - #undef byte -#endif - -#include -#include -#include -#include -#include -#include -#include -#include "../Variable.h" - -#ifdef HAVE_JSON - #include -#endif - -namespace ts { -#define PARM_TYPE(type, fromString, toString) \ - BaseCommandParm(std::string key, type value) : BaseCommandParm(std::pair(key, "")) {\ - toString; \ - } \ -BaseCommandParm& operator=(type value){ \ - toString; \ - return *this; \ -} \ - \ -operator type(){ \ - fromString; \ -} - - class Command; - class ValueList; - - - //PARM_TYPE(ts::Property, return ts::Property(nullptr, key(), value(), 0), f_value() = value.value()); - - class ParameterBulk { - friend class Command; - friend class ValueList; - public: - ParameterBulk(const ParameterBulk& ref) : parms(ref.parms) {} - - - variable operator[](size_t index){ - if(parms.size() > index) return parms[index]; - return variable{"", "", VARTYPE_NULL}; - } - - const variable& operator[](const std::string& key) const { - for(const auto& elm : parms) - if(elm.key() == key){ - return elm; - } - - throw std::invalid_argument("could not find key [" + key + "]"); - } - - variable& operator[](const std::string& key) { - for(auto& elm : parms) - if(elm.key() == key){ - return elm; - } - this->parms.push_back(variable(key, "", VariableType::VARTYPE_NULL)); - return this->operator[](key); - } - - bool has(const std::string& key) const { - for(const auto& elm : parms) - if(elm.key() == key && elm.type() != VariableType::VARTYPE_NULL) return true; - return false; - } - - std::deque keys() const { - std::deque result; - for(const auto& elm : parms) - result.push_back(elm.key()); - return result; - } - - ParameterBulk& operator=(const ParameterBulk& ref){ - parms = ref.parms; - return *this; - } - private: - ParameterBulk() {} - ParameterBulk(std::deque p) : parms(p) {} - std::deque parms; - }; - - class ValueList { - friend class Command; - public: - ValueList() = delete; - ValueList(const ValueList& ref) : key(ref.key), bulkList(ref.bulkList) {} - - variable operator[](int index){ - while(index >= bulkList.size()) bulkList.push_back(ParameterBulk()); - return bulkList[index][key]; - } - - variable first() const { - int index = 0; - while(index < bulkList.size() && !bulkList[index].has(key)) index++; - if(index < bulkList.size()) return bulkList[index][key]; - - return variable{this->key, "", VariableType::VARTYPE_NULL}; - throw std::invalid_argument("could not find key [" + key + "]"); - } - - size_t size(){ - size_t count = 0; - for(const auto& blk : this->bulkList) - if(blk.has(this->key)) count++; - return count; - } - - template - ValueList& operator=(T var){ operator[](0) = var; return *this; } - ValueList& operator=(ts::ValueList& var){ operator[](0) = var.first().value(); return *this; } - - - template - T as() const { return first().as(); } - - template - operator T() { return as(); } - - template - bool operator==(T other){ return as() == other; } - template - bool operator!=(T other){ return as() != other; } - - std::string string() const { return as(); } - - friend std::ostream& operator<<(std::ostream&, const ValueList&); - private: - ValueList(std::string& key, std::deque& bulkList) : key(key), bulkList(bulkList) {} - std::string key; - public: - std::deque& bulkList; - }; - - inline std::ostream& operator<<(std::ostream& stream,const ValueList& list) { - stream << "{ Key: " << list.key << " = ["; - for(auto it = list.bulkList.begin(); it != list.bulkList.end(); it++) - if(it->has(list.key)) { - stream << (*it)[list.key].value(); - if(it + 1 != list.bulkList.end()) - stream << ", "; - } - stream << "]}"; - return stream; - } - - class Command { - public: - static Command parse(const pipes::buffer_view& buffer, bool expect_command = true, bool drop_non_utf8 = false); - - explicit Command(const std::string& command); - explicit Command(const std::string& command, std::initializer_list); - explicit Command(const std::string& command, std::initializer_list>); - - Command(const Command&); - ~Command(); - - inline std::string command() const { return getCommand(); } - std::string getCommand() const { return _command; } - - std::string build(bool escaped = true) const; - -#ifdef HAVE_JSON - Json::Value buildJson() const; -#endif - - const ParameterBulk& operator[](size_t index) const { - if(bulks.size() <= index) throw std::invalid_argument("got out of length"); - return bulks[index]; - } - - ParameterBulk& operator[](size_t index){ - while(bulks.size() <= index) bulks.push_back(ParameterBulk{}); - return bulks[index]; - } - - ValueList operator[](std::string key){ - return ValueList(key, bulks); - } - - size_t bulkCount() const { return bulks.size(); } - void pop_bulk(); - void push_bulk_front(); - - bool hasParm(std::string); - void clear_parameters() { this->paramethers.clear(); } - std::deque parms(); - void enableParm(const std::string& key){ toggleParm(key, true); } - void disableParm(const std::string& key){ toggleParm(key, false); } - void toggleParm(const std::string& key, bool flag); - - void reverseBulks(); - private: - Command(); - - std::string _command; - std::deque bulks; - std::deque paramethers; - }; -} - -DEFINE_VARIABLE_TRANSFORM_TO_STR(ts::ValueList, in.string()); -DEFINE_VARIABLE_TRANSFORM_TYPE(ts::ValueList, VARTYPE_TEXT); - +#pragma once + +#ifdef byte + #define byte asdd + #ifndef WIN32 + #warning The byte macro is already defined! Undefining it! + #endif + #undef byte +#endif + +#include +#include +#include +#include +#include +#include +#include +#include "../Variable.h" + +#ifdef HAVE_JSON + #include +#endif + +namespace ts { +#define PARM_TYPE(type, fromString, toString) \ + BaseCommandParm(std::string key, type value) : BaseCommandParm(std::pair(key, "")) {\ + toString; \ + } \ +BaseCommandParm& operator=(type value){ \ + toString; \ + return *this; \ +} \ + \ +operator type(){ \ + fromString; \ +} + + class Command; + class ValueList; + + + //PARM_TYPE(ts::Property, return ts::Property(nullptr, key(), value(), 0), f_value() = value.value()); + + class ParameterBulk { + friend class Command; + friend class ValueList; + public: + ParameterBulk(const ParameterBulk& ref) : parms(ref.parms) {} + + + variable operator[](size_t index){ + if(parms.size() > index) return parms[index]; + return variable{"", "", VARTYPE_NULL}; + } + + const variable& operator[](const std::string& key) const { + for(const auto& elm : parms) + if(elm.key() == key){ + return elm; + } + + throw std::invalid_argument("could not find key [" + key + "]"); + } + + variable& operator[](const std::string& key) { + for(auto& elm : parms) + if(elm.key() == key){ + return elm; + } + this->parms.push_back(variable(key, "", VariableType::VARTYPE_NULL)); + return this->operator[](key); + } + + bool has(const std::string& key) const { + for(const auto& elm : parms) + if(elm.key() == key && elm.type() != VariableType::VARTYPE_NULL) return true; + return false; + } + + std::deque keys() const { + std::deque result; + for(const auto& elm : parms) + result.push_back(elm.key()); + return result; + } + + ParameterBulk& operator=(const ParameterBulk& ref){ + parms = ref.parms; + return *this; + } + private: + ParameterBulk() {} + ParameterBulk(std::deque p) : parms(p) {} + std::deque parms; + }; + + class ValueList { + friend class Command; + public: + ValueList() = delete; + ValueList(const ValueList& ref) : key(ref.key), bulkList(ref.bulkList) {} + + variable operator[](int index){ + while(index >= bulkList.size()) bulkList.push_back(ParameterBulk()); + return bulkList[index][key]; + } + + variable first() const { + int index = 0; + while(index < bulkList.size() && !bulkList[index].has(key)) index++; + if(index < bulkList.size()) return bulkList[index][key]; + + return variable{this->key, "", VariableType::VARTYPE_NULL}; + throw std::invalid_argument("could not find key [" + key + "]"); + } + + size_t size(){ + size_t count = 0; + for(const auto& blk : this->bulkList) + if(blk.has(this->key)) count++; + return count; + } + + template + ValueList& operator=(T var){ operator[](0) = var; return *this; } + ValueList& operator=(ts::ValueList& var){ operator[](0) = var.first().value(); return *this; } + + + template + T as() const { return first().as(); } + + template + operator T() { return as(); } + + template + bool operator==(T other){ return as() == other; } + template + bool operator!=(T other){ return as() != other; } + + std::string string() const { return as(); } + + friend std::ostream& operator<<(std::ostream&, const ValueList&); + private: + ValueList(std::string& key, std::deque& bulkList) : key(key), bulkList(bulkList) {} + std::string key; + public: + std::deque& bulkList; + }; + + inline std::ostream& operator<<(std::ostream& stream,const ValueList& list) { + stream << "{ Key: " << list.key << " = ["; + for(auto it = list.bulkList.begin(); it != list.bulkList.end(); it++) + if(it->has(list.key)) { + stream << (*it)[list.key].value(); + if(it + 1 != list.bulkList.end()) + stream << ", "; + } + stream << "]}"; + return stream; + } + + class Command { + public: + static Command parse(const pipes::buffer_view& buffer, bool expect_command = true, bool drop_non_utf8 = false); + + explicit Command(const std::string& command); + explicit Command(const std::string& command, std::initializer_list); + explicit Command(const std::string& command, std::initializer_list>); + + Command(const Command&); + ~Command(); + + inline std::string command() const { return getCommand(); } + std::string getCommand() const { return _command; } + + std::string build(bool escaped = true) const; + +#ifdef HAVE_JSON + Json::Value buildJson() const; +#endif + + const ParameterBulk& operator[](size_t index) const { + if(bulks.size() <= index) throw std::invalid_argument("got out of length"); + return bulks[index]; + } + + ParameterBulk& operator[](size_t index){ + while(bulks.size() <= index) bulks.push_back(ParameterBulk{}); + return bulks[index]; + } + + ValueList operator[](std::string key){ + return ValueList(key, bulks); + } + + size_t bulkCount() const { return bulks.size(); } + void pop_bulk(); + void push_bulk_front(); + + bool hasParm(std::string); + void clear_parameters() { this->paramethers.clear(); } + std::deque parms(); + void enableParm(const std::string& key){ toggleParm(key, true); } + void disableParm(const std::string& key){ toggleParm(key, false); } + void toggleParm(const std::string& key, bool flag); + + void reverseBulks(); + private: + Command(); + + std::string _command; + std::deque bulks; + std::deque paramethers; + }; +} + +DEFINE_VARIABLE_TRANSFORM_TO_STR(ts::ValueList, in.string()); +DEFINE_VARIABLE_TRANSFORM_TYPE(ts::ValueList, VARTYPE_TEXT); + #undef PARM_TYPE \ No newline at end of file diff --git a/src/sql/mysql/MySQL.cpp b/src/sql/mysql/MySQL.cpp index ac33c39..eac8ea0 100644 --- a/src/sql/mysql/MySQL.cpp +++ b/src/sql/mysql/MySQL.cpp @@ -1,858 +1,858 @@ -#include - -#include "MySQL.h" -#include "src/log/LogUtils.h" -#include - -#include -#include - -#include - -#define CR_CONNECTION_ERROR (2002) -#define CR_SERVER_GONE_ERROR (2006) -#define CR_SERVER_LOST (2013) - -using namespace std; -using namespace sql; -using namespace sql::mysql; - -//TODO: Cache statements in general any only reapply the values - -MySQLManager::MySQLManager() : SqlManager(SqlType::TYPE_MYSQL) {} -MySQLManager::~MySQLManager() {} - -//Property info: https://dev.mysql.com/doc/connector-j/5.1/en/connector-j-reference-configuration-properties.html -//mysql://[host][:port]/[database][?propertyName1=propertyValue1[&propertyName2=propertyValue2]...] - -#define MYSQL_PREFIX "mysql://" -inline result parse_url(const string& url, std::map& connect_map) { - string target_url; - if(url.find(MYSQL_PREFIX) != 0) - return {ERROR_MYSQL_INVLID_URL, "Missing mysql:// at begin"}; - - auto index_parms = url.find('?'); - if(index_parms == string::npos) { - target_url = "tcp://" + url.substr(strlen(MYSQL_PREFIX)); - } else { - target_url = "tcp://" + url.substr(strlen(MYSQL_PREFIX), index_parms - strlen(MYSQL_PREFIX)); - auto parms = url.substr(index_parms + 1); - size_t index = 0; - do { - auto idx = parms.find('&', index); - auto element = parms.substr(index, idx - index); - - auto key_idx = element.find('='); - auto key = element.substr(0, key_idx); - auto value = element.substr(key_idx + 1); - connect_map[key] = http::decode_url(value); - logTrace(LOG_GENERAL, "Got mysql property {}. Value: {}", key, value); - - index = idx + 1; - } while(index != 0); - } - - //if(!connect_map["hostName"].get() || strcmp(*connect_map["hostName"].get(), "")) - connect_map["hostName"] = target_url; - logTrace(LOG_GENERAL, "Got mysql property {}. Value: {}", "hostName", target_url); - return result::success; -} - -//mysql://[host][:port]/[database][?propertyName1=propertyValue1[&propertyName2=propertyValue2]...] -inline bool parse_mysql_data(const string& url, string& error, string& host, uint16_t& port, string& database, map& properties) { - size_t parse_index = 0; - /* parse the scheme */ - { - auto index = url.find("://", parse_index); - if(index == -1 || url.substr(parse_index, index - parse_index) != "mysql") { - error = "missing/invalid URL scheme"; - return false; - } - - parse_index = index + 3; - if(parse_index >= url.length()) { - error = "unexpected EOL after scheme"; - return false; - } - } - - /* parse host[:port]*/ - { - auto index = url.find('/', parse_index); - if(index == -1) { - error = "missing host/port"; - return false; - } - - auto host_port = url.substr(parse_index, index - parse_index); - - auto port_index = host_port.find(':'); - if(port_index == -1) { - host = host_port; - } else { - host = host_port.substr(0, port_index); - auto port_str = host_port.substr(port_index + 1); - try { - port = stol(port_str); - } catch(std::exception& ex) { - error = "failed to parse port"; - return false; - } - } - if(host.empty()) { - error = "host is empty"; - return false; - } - - parse_index = index + 1; - if(parse_index >= url.length()) { - error = "unexpected EOL after host/port"; - return false; - } - } - - /* the database */ - { - auto index = url.find('?', parse_index); - if(index == -1) { - database = url.substr(parse_index); - parse_index = url.length(); - } else { - database = url.substr(parse_index, index - parse_index); - parse_index = index + 1; - } - - if(database.empty()) { - error = "database is empty"; - return false; - } - } - - /* properties */ - string full_property, property_key, property_value; - while(parse_index < url.length()){ - /* "read" the next property */ - { - auto index = url.find('&', parse_index); /* next entry */ - if(index == -1) { - full_property = url.substr(parse_index); - parse_index = url.length(); - } else { - full_property = url.substr(parse_index, index - parse_index); - parse_index = index + 1; - } - } - - /* parse it */ - { - auto index = full_property.find('='); - if(index == -1) { - error = "invalid property format (missing '=')"; - return false; - } - - property_key = full_property.substr(0, index); - property_value = full_property.substr(index + 1); - if(property_key.empty() || property_value.empty()) { - error = "invalid property key/value (empty)"; - return false; - } - - properties[property_key] = http::decode_url(property_value); - } - } - return true; -} - -mysql::Connection::~Connection() { - { - lock_guard lock(this->used_lock); - assert(!this->used); - } - - if(this->handle) { - mysql_close(this->handle); - this->handle = nullptr; - } -} - -result MySQLManager::connect(const std::string &url) { - this->disconnecting = false; - string error; - - map properties; - string host, database; - uint16_t port; - - if(!parse_mysql_data(url, error, host, port, database, properties)) { - error = "URL parsing failed: " + error; - return {ERROR_MYSQL_INVLID_URL, error}; - } - - size_t connections = 4; - if(properties.count("connections") > 0) { - try { - connections = stol(properties["connections"]); - } catch(std::exception& ex) { - return {ERROR_MYSQL_INVLID_PROPERTIES, "could not parse connection count"}; - } - } - - string username, password; - if(properties.count("userName") > 0) username = properties["userName"]; - if(properties.count("username") > 0) username = properties["username"]; - if(username.empty()) return {ERROR_MYSQL_INVLID_PROPERTIES, "missing username property"}; - - if(properties.count("password") > 0) password = properties["password"]; - if(password.empty()) return {ERROR_MYSQL_INVLID_PROPERTIES, "missing password property"}; - - //debugMessage(LOG_GENERAL, R"([MYSQL] Starting {} connections to {}:{} with database "{}" as user "{}")", connections, host, port, database, username); - - for(size_t index = 0; index < connections; index++) { - auto connection = make_shared(); - connection->handle = mysql_init(nullptr); - if(!connection->handle) - return {-1, "failed to allocate connection " + to_string(index)}; - - { - bool reconnect{true}; - mysql_options(connection->handle, MYSQL_OPT_RECONNECT, &reconnect); - } - mysql_options(connection->handle, MYSQL_SET_CHARSET_NAME, "utf8"); - mysql_options(connection->handle, MYSQL_INIT_COMMAND, "SET NAMES utf8"); - - auto result = mysql_real_connect(connection->handle, host.c_str(), username.c_str(), password.c_str(), database.c_str(), port, nullptr, 0); //CLIENT_MULTI_RESULTS | CLIENT_MULTI_STATEMENTS - if(!result) - return {-1, "failed to connect to server with connection " + to_string(index) + ": " + mysql_error(connection->handle)}; - - connection->used = false; - this->connections.push_back(connection); - } - return result::success; -} - -bool MySQLManager::connected() { - lock_guard lock(this->connections_lock); - return !this->connections.empty(); -} - -result MySQLManager::disconnect() { - lock_guard lock(this->connections_lock); - this->disconnecting = true; - - this->connections.clear(); - this->connections_condition.notify_all(); - - this->disconnecting = false; - return result::success; -} - -struct StatementGuard { - MYSQL_STMT* stmt; - - ~StatementGuard() { - mysql_stmt_close(this->stmt); - } -}; - -struct ResultGuard { - MYSQL_RES* result; - - ~ResultGuard() { - mysql_free_result(this->result); - } -}; - -template -struct FreeGuard { - T* ptr; - - ~FreeGuard() { - if(this->ptr) ::free(this->ptr); - } -}; - -template -struct DeleteAGuard { - T* ptr; - - ~DeleteAGuard() { - delete[] this->ptr; - } -}; - -std::shared_ptr MySQLManager::allocateCommandData() { - return make_shared(); -} - -std::shared_ptr MySQLManager::copyCommandData(std::shared_ptr ptr) { - auto _new = this->allocateCommandData(); - _new->handle = ptr->handle; - _new->lock = ptr->lock; - _new->sql_command = ptr->sql_command; - _new->variables = ptr->variables; - - auto __new = static_pointer_cast(_new); - auto __ptr = static_pointer_cast(ptr); - //__new->stmt = __ptr->stmt; - return __new; -} - -namespace sql::mysql { - bool evaluate_sql_query(string& sql, const std::vector& vars, std::vector& result) { - char quote = 0; - for(int index = 0; index < sql.length(); index++) { - if(sql[index] == '\'' || sql[index] == '"' || sql[index] == '`') { - if(quote > 0) { - if(quote == sql[index]) quote = 0; - } else { - quote = sql[index]; - } - continue; - } - if(quote > 0) continue; - if(sql[index] != ':') continue; - - auto index_end = sql.find_first_not_of("0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ_", index + 1); - if(index_end == string::npos) index_end = sql.length(); - - string key = sql.substr(index, index_end - index); - //Now we can replace it with a ? - sql.replace(index, index_end - index, "?", 1); - - bool insert = false; - for(const auto& e : vars) - if(e.key() == key) { - result.push_back(e); - insert = true; - break; - } - if(!insert) - result.emplace_back(); - } - return true; - } - - struct BindMemory { }; - - /* memory must be freed via ::free! */ - bool create_bind(BindMemory*& memory, const std::vector& variables) { - size_t required_bytes = sizeof(MYSQL_BIND) * variables.size(); - - /* first lets calculate the required memory */ - { - for(auto& variable : variables) { - switch (variable.type()) { - case VARTYPE_NULL: - break; - case VARTYPE_BOOLEAN: - required_bytes += sizeof(bool); - break; - case VARTYPE_INT: - required_bytes += sizeof(int32_t); - break; - case VARTYPE_LONG: - required_bytes += sizeof(int64_t); - break; - case VARTYPE_DOUBLE: - required_bytes += sizeof(double); - break; - case VARTYPE_FLOAT: - required_bytes += sizeof(float); - break; - case VARTYPE_TEXT: - //TODO: Use a direct pointer to the variable's value instead of copying it - required_bytes += sizeof(unsigned long*) + variable.value().length(); - break; - default: - return false; /* unknown variable type */ - } - } - } - if(!required_bytes) { - memory = nullptr; - return true; - } - - //logTrace(LOG_GENERAL, "[MYSQL] Allocated {} bytes for parameters", required_bytes); - memory = (BindMemory*) malloc(required_bytes); - if(!memory) - return false; - - memset(memory, 0, required_bytes); - /* lets fill the values */ - { - size_t memory_index = variables.size() * sizeof(MYSQL_BIND); - auto bind_ptr = (MYSQL_BIND*) memory; - auto payload_ptr = (char*) memory + sizeof(MYSQL_BIND) * variables.size(); - - for(size_t index = 0; index < variables.size(); index++) { - bind_ptr->buffer = payload_ptr; - - auto& variable = variables[index]; - switch (variable.type()) { - case VARTYPE_NULL: - bind_ptr->buffer_type = enum_field_types::MYSQL_TYPE_NULL; - break; - case VARTYPE_BOOLEAN: - bind_ptr->buffer_type = enum_field_types::MYSQL_TYPE_TINY; - bind_ptr->buffer_length = sizeof(bool); - *(bool*) payload_ptr = variable.as(); - break; - case VARTYPE_INT: - bind_ptr->buffer_type = enum_field_types::MYSQL_TYPE_LONG; - bind_ptr->buffer_length = sizeof(int32_t); - *(int32_t*) payload_ptr = variable.as(); - break; - case VARTYPE_LONG: - bind_ptr->buffer_type = enum_field_types::MYSQL_TYPE_LONGLONG; - bind_ptr->buffer_length = sizeof(int64_t); - *(int64_t*) payload_ptr = variable.as(); - break; - case VARTYPE_DOUBLE: - bind_ptr->buffer_type = enum_field_types::MYSQL_TYPE_DOUBLE; - bind_ptr->buffer_length = sizeof(double); - *(double*) payload_ptr = variable.as(); - break; - case VARTYPE_FLOAT: - bind_ptr->buffer_type = enum_field_types::MYSQL_TYPE_FLOAT; - bind_ptr->buffer_length = sizeof(float); - *(float*) payload_ptr = variable.as(); - break; - case VARTYPE_TEXT: { - auto value = variable.value(); - - //TODO: Use a direct pointer to the variable's value instead of copying it - //May use a string object allocated on the memory_ptr? (Special deinit needed then!) - bind_ptr->buffer_type = enum_field_types::MYSQL_TYPE_STRING; - bind_ptr->buffer_length = value.length(); - - bind_ptr->length = (unsigned long*) payload_ptr; - *bind_ptr->length = bind_ptr->buffer_length; - - payload_ptr += sizeof(unsigned long*); - memory_index += sizeof(unsigned long*); - - memcpy(payload_ptr, value.data(), value.length()); - bind_ptr->buffer = payload_ptr; - break; - } - default: - return false; /* unknown variable type */ - } - - payload_ptr += bind_ptr->buffer_length; - bind_ptr++; - assert(memory_index <= required_bytes); - } - } - - return true; - } - - struct ResultBindDescriptor { - size_t primitive_size = 0; - - void(*destroy)(char*& /* primitive ptr */) = nullptr; - bool(*create)(const MYSQL_FIELD& /* field */, MYSQL_BIND& /* bind */, char*& /* primitive ptr */) = nullptr; - - bool(*get_as_string)(MYSQL_BIND& /* bind */, std::string& /* result */) = nullptr; - }; - - /* memory to primitive string */ - template - bool m2ps(MYSQL_BIND& bind, string& str) { - if(bind.error_value || (bind.error && *bind.error)) return false; - str = std::to_string(*(T*) bind.buffer); - return true; - } - - template - void _do_destroy_primitive(char*& primitive_ptr) { - primitive_ptr += size; - } - - template - bool _do_bind_primitive(const MYSQL_FIELD&, MYSQL_BIND& bind, char*& primitive_ptr) { - bind.buffer = (void*) primitive_ptr; - bind.buffer_length = size; - bind.buffer_type = (enum_field_types) type; - primitive_ptr += size; - return true; - } - - #define CREATE_PRIMATIVE_BIND_DESCRIPTOR(mysql_type, c_type, size) \ - case mysql_type:\ - static ResultBindDescriptor _ ##mysql_type = {\ - size,\ - _do_destroy_primitive,\ - _do_bind_primitive,\ - m2ps\ - };\ - return &_ ##mysql_type; - - const ResultBindDescriptor* get_bind_descriptor(enum_field_types type) { - switch (type) { - case MYSQL_TYPE_NULL: - static ResultBindDescriptor _null = { - /* primitive_size */ 0, - /* destroy */ _do_destroy_primitive<0>, - /* create */ _do_bind_primitive, - /* get_as_string */ [](MYSQL_BIND&, string& str) { str.clear(); return true; } - }; - return &_null; - CREATE_PRIMATIVE_BIND_DESCRIPTOR(MYSQL_TYPE_TINY, int8_t, 1); - CREATE_PRIMATIVE_BIND_DESCRIPTOR(MYSQL_TYPE_SHORT, int16_t, 2); - CREATE_PRIMATIVE_BIND_DESCRIPTOR(MYSQL_TYPE_INT24, int32_t, 4); - CREATE_PRIMATIVE_BIND_DESCRIPTOR(MYSQL_TYPE_LONG, int32_t, 4); - CREATE_PRIMATIVE_BIND_DESCRIPTOR(MYSQL_TYPE_LONGLONG, int64_t, 8); - CREATE_PRIMATIVE_BIND_DESCRIPTOR(MYSQL_TYPE_DOUBLE, double, sizeof(double)); - CREATE_PRIMATIVE_BIND_DESCRIPTOR(MYSQL_TYPE_FLOAT, float, sizeof(float)); - case MYSQL_TYPE_VAR_STRING: - case MYSQL_TYPE_STRING: - case MYSQL_TYPE_BLOB: - static ResultBindDescriptor _string = { - /* we store the allocated buffer in the primitive types buffer and the length */ - /* primitive_size */ sizeof(void*) + sizeof(unsigned long*), - - /* destroy */ [](char*& primitive) { - ::free(*(void**) primitive); - primitive += sizeof(void*); - primitive += sizeof(unsigned long*); - }, - /* create */ [](const MYSQL_FIELD& field, MYSQL_BIND& bind, char*& primitive) { - bind.buffer_length = field.max_length > 0 ? field.max_length : min(field.length, 5UL * 1024UL * 1024UL); - bind.buffer = malloc(bind.buffer_length); - bind.buffer_type = MYSQL_TYPE_BLOB; - - *(void**) primitive = bind.buffer; - primitive += sizeof(void*); - - bind.length = (unsigned long*) primitive; - primitive += sizeof(unsigned long*); - - return bind.buffer != nullptr; - }, - /* get_as_string */ [](MYSQL_BIND& bind, std::string& result) { - auto length = bind.length ? *bind.length : bind.length_value; - result.reserve(length); - result.assign((const char*) bind.buffer, length); - return true; - } - }; - return &_string; - default: - return nullptr; - } - } - - #undef CREATE_PRIMATIVE_BIND_DESCRIPTOR - - struct ResultBind { - size_t field_count = 0; - BindMemory* memory = nullptr; - const ResultBindDescriptor** descriptors = nullptr; - - ~ResultBind() { - if(memory) { - auto memory_ptr = (char*) this->memory + (sizeof(MYSQL_BIND) * field_count); - - for(size_t index = 0; index < this->field_count; index++) - this->descriptors[index]->destroy(memory_ptr); - - ::free(memory); - } - delete[] descriptors; - } - - ResultBind(size_t a, BindMemory* b, const ResultBindDescriptor** c) : field_count{a}, memory{b}, descriptors{c} {} - ResultBind(const ResultBind&) = delete; - ResultBind(ResultBind&&) = default; - - inline bool get_as_string(size_t column, string& result) { - if(!descriptors) return false; - - auto& bind_ptr = *(MYSQL_BIND*) ((char*) this->memory + sizeof(MYSQL_BIND) * column); - return this->descriptors[column]->get_as_string(bind_ptr, result); - } - - inline bool get_as_string(string* results) { - if(!descriptors) return false; - - auto bind_ptr = (MYSQL_BIND*) this->memory; - for(int index = 0; index < this->field_count; index++) - if(!this->descriptors[index]->get_as_string(*bind_ptr, results[index])) - return false; - else - bind_ptr++; - return true; - } - }; - - bool create_result_bind(size_t field_count, MYSQL_FIELD* fields, ResultBind& result) { - size_t required_bytes = sizeof(MYSQL_BIND) * field_count; - - assert(!result.field_count); - assert(!result.descriptors); - assert(!result.memory); - result.descriptors = new const ResultBindDescriptor*[field_count]; - result.field_count = field_count; - - for(size_t index = 0; index < field_count; index++) { - result.descriptors[index] = get_bind_descriptor(fields[index].type); - if(!result.descriptors[index]) return false; - - required_bytes += result.descriptors[index]->primitive_size; - } - - if(!required_bytes) { - result.memory = nullptr; - return true; - } - - //logTrace(LOG_GENERAL, "[MYSQL] Allocated {} bytes for response", required_bytes); - result.memory = (BindMemory*) malloc(required_bytes); - if(!result.memory) - return false; - - memset(result.memory, 0, required_bytes); - auto memory_ptr = (char*) result.memory + (sizeof(MYSQL_BIND) * field_count); - auto bind_ptr = (MYSQL_BIND*) result.memory; - for(size_t index = 0; index < field_count; index++) { - if(!result.descriptors[index]->create(fields[index], *bind_ptr, memory_ptr)) return false; - bind_ptr->buffer_type = fields[index].type; - bind_ptr++; - } - assert(memory_ptr == ((char*) result.memory + required_bytes)); /* Overflow check */ - return true; - } - } - -AcquiredConnection::AcquiredConnection(MySQLManager* owner, std::shared_ptr connection) : owner(owner), connection(std::move(connection)) { } -AcquiredConnection::~AcquiredConnection() { - { - lock_guard lock{this->connection->used_lock}; - this->connection->used = false; - } - - { - lock_guard lock(this->owner->connections_lock); - this->owner->connections_condition.notify_one(); - } -} -std::unique_ptr MySQLManager::next_connection() { - unique_ptr result; - { - unique_lock connections_lock(this->connections_lock); - - while(!result) { - size_t available_connections = 0; - for(const auto& connection : this->connections) { - available_connections++; - - { - lock_guard use_lock(connection->used_lock); - if(connection->used) continue; - connection->used = true; - } - - result = std::make_unique(this, connection); - break; - } - - if(!result) { - if(available_connections == 0) { - if(this->listener_disconnected) - this->listener_disconnected(false); - this->disconnect(); - return nullptr; - } - - this->connections_condition.wait(connections_lock); /* wait for the next connection */ - } - } - } - //TODO: Test if the connection hasn't been used for a longer while if so use mysql_ping() to verify the connection - - return result; -} - -void MySQLManager::connection_closed(const std::shared_ptr &connection) { - bool call_disconnect = false; - { - unique_lock connections_lock(this->connections_lock); - auto index = find(this->connections.begin(), this->connections.end(), connection); - if(index == this->connections.end()) return; - - this->connections.erase(index); - call_disconnect = this->connections.empty(); - } - - auto dl = this->listener_disconnected; - if(call_disconnect && dl) - dl(this->disconnecting); -} - -result MySQLManager::executeCommand(std::shared_ptr _ptr) { - auto ptr = static_pointer_cast(_ptr); - if(!ptr) { return {-1, "invalid command handle"}; } - - std::lock_guard lock(ptr->lock); - auto command = ptr->sql_command; - - auto variables = ptr->variables; - vector mapped_variables; - if(!sql::mysql::evaluate_sql_query(command, variables, mapped_variables)) - return {ptr->sql_command, -1, "Could not map sqlite vars to mysql!"}; - - FreeGuard bind_parameter_memory{nullptr}; - if(!sql::mysql::create_bind(bind_parameter_memory.ptr, mapped_variables)) - return {ptr->sql_command, -1, "Failed to allocate bind memory!"}; - - ResultBind bind_result_data{.field_count = 0, .memory = nullptr, .descriptors = nullptr}; - - auto connection = this->next_connection(); - if(!connection) return {ptr->sql_command, -1, "Could not get a valid connection!"}; - - StatementGuard stmt_guard{mysql_stmt_init(connection->connection->handle)}; - if(!stmt_guard.stmt) - return {ptr->sql_command, -1, "failed to allocate statement"}; - - if(mysql_stmt_prepare(stmt_guard.stmt, command.c_str(), command.length())) { - auto errc = mysql_stmt_errno(stmt_guard.stmt); - if(errc == CR_SERVER_GONE_ERROR || errc == CR_SERVER_LOST || errc == CR_CONNECTION_ERROR) - this->connection_closed(connection->connection); - - return {ptr->sql_command, -1, "failed to prepare statement: " + string(mysql_stmt_error(stmt_guard.stmt))}; - } - - /* validate all parameters */ - auto parameter_count = mysql_stmt_param_count(stmt_guard.stmt); - if(parameter_count != mapped_variables.size()) - return {ptr->sql_command, -1, "invalid parameter count. Statement contains " + to_string(parameter_count) + " parameters but only " + to_string(mapped_variables.size()) + " are given."}; - - if(bind_parameter_memory.ptr) { - if(mysql_stmt_bind_param(stmt_guard.stmt, (MYSQL_BIND*) bind_parameter_memory.ptr)) - return {ptr->sql_command, -1, "failed to bind parameters to statement: " + string(mysql_stmt_error(stmt_guard.stmt))}; - } else if(parameter_count > 0) - return {ptr->sql_command, -1, "invalid parameter count. Statement contains " + to_string(parameter_count) + " parameters but only " + to_string(mapped_variables.size()) + " are given (bind nullptr)."}; - - - if(mysql_stmt_execute(stmt_guard.stmt)) { - auto errc = mysql_stmt_errno(stmt_guard.stmt); - if(errc == CR_SERVER_GONE_ERROR || errc == CR_SERVER_LOST || errc == CR_CONNECTION_ERROR) - this->connection_closed(connection->connection); - - return {ptr->sql_command, -1, "failed to execute query statement: " + string(mysql_stmt_error(stmt_guard.stmt))}; - } - - return result::success; -} - -result MySQLManager::queryCommand(shared_ptr _ptr, const QueryCallback &fn) { - auto ptr = static_pointer_cast(_ptr); - if(!ptr) { return {-1, "invalid command handle"}; } - - std::lock_guard lock(ptr->lock); - auto command = ptr->sql_command; - - auto variables = ptr->variables; - vector mapped_variables; - if(!sql::mysql::evaluate_sql_query(command, variables, mapped_variables)) return {ptr->sql_command, -1, "Could not map sqlite vars to mysql!"}; - - FreeGuard bind_parameter_memory{nullptr}; - if(!sql::mysql::create_bind(bind_parameter_memory.ptr, mapped_variables)) return {ptr->sql_command, -1, "Failed to allocate bind memory!"}; - - ResultBind bind_result_data{.field_count = 0, .memory = nullptr, .descriptors = nullptr}; - - auto connection = this->next_connection(); - if(!connection) return {ptr->sql_command, -1, "Could not get a valid connection!"}; - - StatementGuard stmt_guard{mysql_stmt_init(connection->connection->handle)}; - if(!stmt_guard.stmt) - return {ptr->sql_command, -1, "failed to allocate statement"}; - - if(mysql_stmt_prepare(stmt_guard.stmt, command.c_str(), command.length())) { - auto errc = mysql_stmt_errno(stmt_guard.stmt); - if(errc == CR_SERVER_GONE_ERROR || errc == CR_SERVER_LOST || errc == CR_CONNECTION_ERROR) - this->connection_closed(connection->connection); - - return {ptr->sql_command, -1, "failed to prepare statement: " + string(mysql_stmt_error(stmt_guard.stmt))}; - } - - /* validate all parameters */ - { - auto parameter_count = mysql_stmt_param_count(stmt_guard.stmt); - if(parameter_count != mapped_variables.size()) - return {ptr->sql_command, -1, "invalid parameter count. Statement contains " + to_string(parameter_count) + " parameters but only " + to_string(mapped_variables.size()) + " are given."}; - } - - if(bind_parameter_memory.ptr) { - if(mysql_stmt_bind_param(stmt_guard.stmt, (MYSQL_BIND*) bind_parameter_memory.ptr)) - return {ptr->sql_command, -1, "failed to bind parameters to statement: " + string(mysql_stmt_error(stmt_guard.stmt))}; - } - - if(mysql_stmt_execute(stmt_guard.stmt)) { - auto errc = mysql_stmt_errno(stmt_guard.stmt); - if(errc == CR_SERVER_GONE_ERROR || errc == CR_SERVER_LOST || errc == CR_CONNECTION_ERROR) - this->connection_closed(connection->connection); - - return {ptr->sql_command, -1, "failed to execute query statement: " + string(mysql_stmt_error(stmt_guard.stmt))}; - } - - //if(mysql_stmt_store_result(stmt_guard.stmt)) - // return {ptr->sql_command, -1, "failed to store query result: " + string(mysql_stmt_error(stmt_guard.stmt))}; - - ResultGuard result_guard{mysql_stmt_result_metadata(stmt_guard.stmt)}; - if(!result_guard.result) - return {ptr->sql_command, -1, "failed to query result metadata: " + string(mysql_stmt_error(stmt_guard.stmt))}; - - - auto field_count = mysql_num_fields(result_guard.result); - DeleteAGuard field_names{new string[field_count]}; - DeleteAGuard field_values{new string[field_count]}; - - { - auto field_meta = mysql_fetch_fields(result_guard.result); - if(!field_meta && field_count > 0) - return {ptr->sql_command, -1, "failed to fetch field meta"}; - - if(!sql::mysql::create_result_bind(field_count, field_meta, bind_result_data)) - return {ptr->sql_command, -1, "failed to allocate result buffer"}; - - if(mysql_stmt_bind_result(stmt_guard.stmt, (MYSQL_BIND*) bind_result_data.memory)) - return {ptr->sql_command, -1, "failed to bind response buffer to statement: " + string(mysql_stmt_error(stmt_guard.stmt))}; - - for(size_t index = 0; index < field_count; index++) { - field_names.ptr[index] = field_meta[index].name; // field_meta cant be null because it has been checked above - //cout << field_names.ptr[index] << " - " << field_meta[index].max_length << endl; - } - } - - bool user_quit = false; - int stmt_code, row_id = 0; - while(!(stmt_code = mysql_stmt_fetch(stmt_guard.stmt))) { - bind_result_data.get_as_string(field_values.ptr); - - if(fn(field_count, field_values.ptr, field_names.ptr) != 0) { - user_quit = true; - break; - } - - row_id++; - } - - if(!user_quit) { - if(stmt_code == 1) { - auto errc = mysql_stmt_errno(stmt_guard.stmt); - if(errc == CR_SERVER_GONE_ERROR || errc == CR_SERVER_LOST || errc == CR_CONNECTION_ERROR) - this->connection_closed(connection->connection); - - return {ptr->sql_command, -1, "failed to fetch response row " + to_string(row_id) + ": " + string(mysql_stmt_error(stmt_guard.stmt))}; - } else if(stmt_code == MYSQL_NO_DATA) - ; - else if(stmt_code == MYSQL_DATA_TRUNCATED) - return {ptr->sql_command, -1, "response data has been truncated"}; - } - return result::success; +#include + +#include "MySQL.h" +#include "src/log/LogUtils.h" +#include + +#include +#include + +#include + +#define CR_CONNECTION_ERROR (2002) +#define CR_SERVER_GONE_ERROR (2006) +#define CR_SERVER_LOST (2013) + +using namespace std; +using namespace sql; +using namespace sql::mysql; + +//TODO: Cache statements in general any only reapply the values + +MySQLManager::MySQLManager() : SqlManager(SqlType::TYPE_MYSQL) {} +MySQLManager::~MySQLManager() {} + +//Property info: https://dev.mysql.com/doc/connector-j/5.1/en/connector-j-reference-configuration-properties.html +//mysql://[host][:port]/[database][?propertyName1=propertyValue1[&propertyName2=propertyValue2]...] + +#define MYSQL_PREFIX "mysql://" +inline result parse_url(const string& url, std::map& connect_map) { + string target_url; + if(url.find(MYSQL_PREFIX) != 0) + return {ERROR_MYSQL_INVLID_URL, "Missing mysql:// at begin"}; + + auto index_parms = url.find('?'); + if(index_parms == string::npos) { + target_url = "tcp://" + url.substr(strlen(MYSQL_PREFIX)); + } else { + target_url = "tcp://" + url.substr(strlen(MYSQL_PREFIX), index_parms - strlen(MYSQL_PREFIX)); + auto parms = url.substr(index_parms + 1); + size_t index = 0; + do { + auto idx = parms.find('&', index); + auto element = parms.substr(index, idx - index); + + auto key_idx = element.find('='); + auto key = element.substr(0, key_idx); + auto value = element.substr(key_idx + 1); + connect_map[key] = http::decode_url(value); + logTrace(LOG_GENERAL, "Got mysql property {}. Value: {}", key, value); + + index = idx + 1; + } while(index != 0); + } + + //if(!connect_map["hostName"].get() || strcmp(*connect_map["hostName"].get(), "")) + connect_map["hostName"] = target_url; + logTrace(LOG_GENERAL, "Got mysql property {}. Value: {}", "hostName", target_url); + return result::success; +} + +//mysql://[host][:port]/[database][?propertyName1=propertyValue1[&propertyName2=propertyValue2]...] +inline bool parse_mysql_data(const string& url, string& error, string& host, uint16_t& port, string& database, map& properties) { + size_t parse_index = 0; + /* parse the scheme */ + { + auto index = url.find("://", parse_index); + if(index == -1 || url.substr(parse_index, index - parse_index) != "mysql") { + error = "missing/invalid URL scheme"; + return false; + } + + parse_index = index + 3; + if(parse_index >= url.length()) { + error = "unexpected EOL after scheme"; + return false; + } + } + + /* parse host[:port]*/ + { + auto index = url.find('/', parse_index); + if(index == -1) { + error = "missing host/port"; + return false; + } + + auto host_port = url.substr(parse_index, index - parse_index); + + auto port_index = host_port.find(':'); + if(port_index == -1) { + host = host_port; + } else { + host = host_port.substr(0, port_index); + auto port_str = host_port.substr(port_index + 1); + try { + port = stol(port_str); + } catch(std::exception& ex) { + error = "failed to parse port"; + return false; + } + } + if(host.empty()) { + error = "host is empty"; + return false; + } + + parse_index = index + 1; + if(parse_index >= url.length()) { + error = "unexpected EOL after host/port"; + return false; + } + } + + /* the database */ + { + auto index = url.find('?', parse_index); + if(index == -1) { + database = url.substr(parse_index); + parse_index = url.length(); + } else { + database = url.substr(parse_index, index - parse_index); + parse_index = index + 1; + } + + if(database.empty()) { + error = "database is empty"; + return false; + } + } + + /* properties */ + string full_property, property_key, property_value; + while(parse_index < url.length()){ + /* "read" the next property */ + { + auto index = url.find('&', parse_index); /* next entry */ + if(index == -1) { + full_property = url.substr(parse_index); + parse_index = url.length(); + } else { + full_property = url.substr(parse_index, index - parse_index); + parse_index = index + 1; + } + } + + /* parse it */ + { + auto index = full_property.find('='); + if(index == -1) { + error = "invalid property format (missing '=')"; + return false; + } + + property_key = full_property.substr(0, index); + property_value = full_property.substr(index + 1); + if(property_key.empty() || property_value.empty()) { + error = "invalid property key/value (empty)"; + return false; + } + + properties[property_key] = http::decode_url(property_value); + } + } + return true; +} + +mysql::Connection::~Connection() { + { + lock_guard lock(this->used_lock); + assert(!this->used); + } + + if(this->handle) { + mysql_close(this->handle); + this->handle = nullptr; + } +} + +result MySQLManager::connect(const std::string &url) { + this->disconnecting = false; + string error; + + map properties; + string host, database; + uint16_t port; + + if(!parse_mysql_data(url, error, host, port, database, properties)) { + error = "URL parsing failed: " + error; + return {ERROR_MYSQL_INVLID_URL, error}; + } + + size_t connections = 4; + if(properties.count("connections") > 0) { + try { + connections = stol(properties["connections"]); + } catch(std::exception& ex) { + return {ERROR_MYSQL_INVLID_PROPERTIES, "could not parse connection count"}; + } + } + + string username, password; + if(properties.count("userName") > 0) username = properties["userName"]; + if(properties.count("username") > 0) username = properties["username"]; + if(username.empty()) return {ERROR_MYSQL_INVLID_PROPERTIES, "missing username property"}; + + if(properties.count("password") > 0) password = properties["password"]; + if(password.empty()) return {ERROR_MYSQL_INVLID_PROPERTIES, "missing password property"}; + + //debugMessage(LOG_GENERAL, R"([MYSQL] Starting {} connections to {}:{} with database "{}" as user "{}")", connections, host, port, database, username); + + for(size_t index = 0; index < connections; index++) { + auto connection = make_shared(); + connection->handle = mysql_init(nullptr); + if(!connection->handle) + return {-1, "failed to allocate connection " + to_string(index)}; + + { + bool reconnect{true}; + mysql_options(connection->handle, MYSQL_OPT_RECONNECT, &reconnect); + } + mysql_options(connection->handle, MYSQL_SET_CHARSET_NAME, "utf8"); + mysql_options(connection->handle, MYSQL_INIT_COMMAND, "SET NAMES utf8"); + + auto result = mysql_real_connect(connection->handle, host.c_str(), username.c_str(), password.c_str(), database.c_str(), port, nullptr, 0); //CLIENT_MULTI_RESULTS | CLIENT_MULTI_STATEMENTS + if(!result) + return {-1, "failed to connect to server with connection " + to_string(index) + ": " + mysql_error(connection->handle)}; + + connection->used = false; + this->connections.push_back(connection); + } + return result::success; +} + +bool MySQLManager::connected() { + lock_guard lock(this->connections_lock); + return !this->connections.empty(); +} + +result MySQLManager::disconnect() { + lock_guard lock(this->connections_lock); + this->disconnecting = true; + + this->connections.clear(); + this->connections_condition.notify_all(); + + this->disconnecting = false; + return result::success; +} + +struct StatementGuard { + MYSQL_STMT* stmt; + + ~StatementGuard() { + mysql_stmt_close(this->stmt); + } +}; + +struct ResultGuard { + MYSQL_RES* result; + + ~ResultGuard() { + mysql_free_result(this->result); + } +}; + +template +struct FreeGuard { + T* ptr; + + ~FreeGuard() { + if(this->ptr) ::free(this->ptr); + } +}; + +template +struct DeleteAGuard { + T* ptr; + + ~DeleteAGuard() { + delete[] this->ptr; + } +}; + +std::shared_ptr MySQLManager::allocateCommandData() { + return make_shared(); +} + +std::shared_ptr MySQLManager::copyCommandData(std::shared_ptr ptr) { + auto _new = this->allocateCommandData(); + _new->handle = ptr->handle; + _new->lock = ptr->lock; + _new->sql_command = ptr->sql_command; + _new->variables = ptr->variables; + + auto __new = static_pointer_cast(_new); + auto __ptr = static_pointer_cast(ptr); + //__new->stmt = __ptr->stmt; + return __new; +} + +namespace sql::mysql { + bool evaluate_sql_query(string& sql, const std::vector& vars, std::vector& result) { + char quote = 0; + for(int index = 0; index < sql.length(); index++) { + if(sql[index] == '\'' || sql[index] == '"' || sql[index] == '`') { + if(quote > 0) { + if(quote == sql[index]) quote = 0; + } else { + quote = sql[index]; + } + continue; + } + if(quote > 0) continue; + if(sql[index] != ':') continue; + + auto index_end = sql.find_first_not_of("0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ_", index + 1); + if(index_end == string::npos) index_end = sql.length(); + + string key = sql.substr(index, index_end - index); + //Now we can replace it with a ? + sql.replace(index, index_end - index, "?", 1); + + bool insert = false; + for(const auto& e : vars) + if(e.key() == key) { + result.push_back(e); + insert = true; + break; + } + if(!insert) + result.emplace_back(); + } + return true; + } + + struct BindMemory { }; + + /* memory must be freed via ::free! */ + bool create_bind(BindMemory*& memory, const std::vector& variables) { + size_t required_bytes = sizeof(MYSQL_BIND) * variables.size(); + + /* first lets calculate the required memory */ + { + for(auto& variable : variables) { + switch (variable.type()) { + case VARTYPE_NULL: + break; + case VARTYPE_BOOLEAN: + required_bytes += sizeof(bool); + break; + case VARTYPE_INT: + required_bytes += sizeof(int32_t); + break; + case VARTYPE_LONG: + required_bytes += sizeof(int64_t); + break; + case VARTYPE_DOUBLE: + required_bytes += sizeof(double); + break; + case VARTYPE_FLOAT: + required_bytes += sizeof(float); + break; + case VARTYPE_TEXT: + //TODO: Use a direct pointer to the variable's value instead of copying it + required_bytes += sizeof(unsigned long*) + variable.value().length(); + break; + default: + return false; /* unknown variable type */ + } + } + } + if(!required_bytes) { + memory = nullptr; + return true; + } + + //logTrace(LOG_GENERAL, "[MYSQL] Allocated {} bytes for parameters", required_bytes); + memory = (BindMemory*) malloc(required_bytes); + if(!memory) + return false; + + memset(memory, 0, required_bytes); + /* lets fill the values */ + { + size_t memory_index = variables.size() * sizeof(MYSQL_BIND); + auto bind_ptr = (MYSQL_BIND*) memory; + auto payload_ptr = (char*) memory + sizeof(MYSQL_BIND) * variables.size(); + + for(size_t index = 0; index < variables.size(); index++) { + bind_ptr->buffer = payload_ptr; + + auto& variable = variables[index]; + switch (variable.type()) { + case VARTYPE_NULL: + bind_ptr->buffer_type = enum_field_types::MYSQL_TYPE_NULL; + break; + case VARTYPE_BOOLEAN: + bind_ptr->buffer_type = enum_field_types::MYSQL_TYPE_TINY; + bind_ptr->buffer_length = sizeof(bool); + *(bool*) payload_ptr = variable.as(); + break; + case VARTYPE_INT: + bind_ptr->buffer_type = enum_field_types::MYSQL_TYPE_LONG; + bind_ptr->buffer_length = sizeof(int32_t); + *(int32_t*) payload_ptr = variable.as(); + break; + case VARTYPE_LONG: + bind_ptr->buffer_type = enum_field_types::MYSQL_TYPE_LONGLONG; + bind_ptr->buffer_length = sizeof(int64_t); + *(int64_t*) payload_ptr = variable.as(); + break; + case VARTYPE_DOUBLE: + bind_ptr->buffer_type = enum_field_types::MYSQL_TYPE_DOUBLE; + bind_ptr->buffer_length = sizeof(double); + *(double*) payload_ptr = variable.as(); + break; + case VARTYPE_FLOAT: + bind_ptr->buffer_type = enum_field_types::MYSQL_TYPE_FLOAT; + bind_ptr->buffer_length = sizeof(float); + *(float*) payload_ptr = variable.as(); + break; + case VARTYPE_TEXT: { + auto value = variable.value(); + + //TODO: Use a direct pointer to the variable's value instead of copying it + //May use a string object allocated on the memory_ptr? (Special deinit needed then!) + bind_ptr->buffer_type = enum_field_types::MYSQL_TYPE_STRING; + bind_ptr->buffer_length = value.length(); + + bind_ptr->length = (unsigned long*) payload_ptr; + *bind_ptr->length = bind_ptr->buffer_length; + + payload_ptr += sizeof(unsigned long*); + memory_index += sizeof(unsigned long*); + + memcpy(payload_ptr, value.data(), value.length()); + bind_ptr->buffer = payload_ptr; + break; + } + default: + return false; /* unknown variable type */ + } + + payload_ptr += bind_ptr->buffer_length; + bind_ptr++; + assert(memory_index <= required_bytes); + } + } + + return true; + } + + struct ResultBindDescriptor { + size_t primitive_size = 0; + + void(*destroy)(char*& /* primitive ptr */) = nullptr; + bool(*create)(const MYSQL_FIELD& /* field */, MYSQL_BIND& /* bind */, char*& /* primitive ptr */) = nullptr; + + bool(*get_as_string)(MYSQL_BIND& /* bind */, std::string& /* result */) = nullptr; + }; + + /* memory to primitive string */ + template + bool m2ps(MYSQL_BIND& bind, string& str) { + if(bind.error_value || (bind.error && *bind.error)) return false; + str = std::to_string(*(T*) bind.buffer); + return true; + } + + template + void _do_destroy_primitive(char*& primitive_ptr) { + primitive_ptr += size; + } + + template + bool _do_bind_primitive(const MYSQL_FIELD&, MYSQL_BIND& bind, char*& primitive_ptr) { + bind.buffer = (void*) primitive_ptr; + bind.buffer_length = size; + bind.buffer_type = (enum_field_types) type; + primitive_ptr += size; + return true; + } + + #define CREATE_PRIMATIVE_BIND_DESCRIPTOR(mysql_type, c_type, size) \ + case mysql_type:\ + static ResultBindDescriptor _ ##mysql_type = {\ + size,\ + _do_destroy_primitive,\ + _do_bind_primitive,\ + m2ps\ + };\ + return &_ ##mysql_type; + + const ResultBindDescriptor* get_bind_descriptor(enum_field_types type) { + switch (type) { + case MYSQL_TYPE_NULL: + static ResultBindDescriptor _null = { + /* primitive_size */ 0, + /* destroy */ _do_destroy_primitive<0>, + /* create */ _do_bind_primitive, + /* get_as_string */ [](MYSQL_BIND&, string& str) { str.clear(); return true; } + }; + return &_null; + CREATE_PRIMATIVE_BIND_DESCRIPTOR(MYSQL_TYPE_TINY, int8_t, 1); + CREATE_PRIMATIVE_BIND_DESCRIPTOR(MYSQL_TYPE_SHORT, int16_t, 2); + CREATE_PRIMATIVE_BIND_DESCRIPTOR(MYSQL_TYPE_INT24, int32_t, 4); + CREATE_PRIMATIVE_BIND_DESCRIPTOR(MYSQL_TYPE_LONG, int32_t, 4); + CREATE_PRIMATIVE_BIND_DESCRIPTOR(MYSQL_TYPE_LONGLONG, int64_t, 8); + CREATE_PRIMATIVE_BIND_DESCRIPTOR(MYSQL_TYPE_DOUBLE, double, sizeof(double)); + CREATE_PRIMATIVE_BIND_DESCRIPTOR(MYSQL_TYPE_FLOAT, float, sizeof(float)); + case MYSQL_TYPE_VAR_STRING: + case MYSQL_TYPE_STRING: + case MYSQL_TYPE_BLOB: + static ResultBindDescriptor _string = { + /* we store the allocated buffer in the primitive types buffer and the length */ + /* primitive_size */ sizeof(void*) + sizeof(unsigned long*), + + /* destroy */ [](char*& primitive) { + ::free(*(void**) primitive); + primitive += sizeof(void*); + primitive += sizeof(unsigned long*); + }, + /* create */ [](const MYSQL_FIELD& field, MYSQL_BIND& bind, char*& primitive) { + bind.buffer_length = field.max_length > 0 ? field.max_length : min(field.length, 5UL * 1024UL * 1024UL); + bind.buffer = malloc(bind.buffer_length); + bind.buffer_type = MYSQL_TYPE_BLOB; + + *(void**) primitive = bind.buffer; + primitive += sizeof(void*); + + bind.length = (unsigned long*) primitive; + primitive += sizeof(unsigned long*); + + return bind.buffer != nullptr; + }, + /* get_as_string */ [](MYSQL_BIND& bind, std::string& result) { + auto length = bind.length ? *bind.length : bind.length_value; + result.reserve(length); + result.assign((const char*) bind.buffer, length); + return true; + } + }; + return &_string; + default: + return nullptr; + } + } + + #undef CREATE_PRIMATIVE_BIND_DESCRIPTOR + + struct ResultBind { + size_t field_count = 0; + BindMemory* memory = nullptr; + const ResultBindDescriptor** descriptors = nullptr; + + ~ResultBind() { + if(memory) { + auto memory_ptr = (char*) this->memory + (sizeof(MYSQL_BIND) * field_count); + + for(size_t index = 0; index < this->field_count; index++) + this->descriptors[index]->destroy(memory_ptr); + + ::free(memory); + } + delete[] descriptors; + } + + ResultBind(size_t a, BindMemory* b, const ResultBindDescriptor** c) : field_count{a}, memory{b}, descriptors{c} {} + ResultBind(const ResultBind&) = delete; + ResultBind(ResultBind&&) = default; + + inline bool get_as_string(size_t column, string& result) { + if(!descriptors) return false; + + auto& bind_ptr = *(MYSQL_BIND*) ((char*) this->memory + sizeof(MYSQL_BIND) * column); + return this->descriptors[column]->get_as_string(bind_ptr, result); + } + + inline bool get_as_string(string* results) { + if(!descriptors) return false; + + auto bind_ptr = (MYSQL_BIND*) this->memory; + for(int index = 0; index < this->field_count; index++) + if(!this->descriptors[index]->get_as_string(*bind_ptr, results[index])) + return false; + else + bind_ptr++; + return true; + } + }; + + bool create_result_bind(size_t field_count, MYSQL_FIELD* fields, ResultBind& result) { + size_t required_bytes = sizeof(MYSQL_BIND) * field_count; + + assert(!result.field_count); + assert(!result.descriptors); + assert(!result.memory); + result.descriptors = new const ResultBindDescriptor*[field_count]; + result.field_count = field_count; + + for(size_t index = 0; index < field_count; index++) { + result.descriptors[index] = get_bind_descriptor(fields[index].type); + if(!result.descriptors[index]) return false; + + required_bytes += result.descriptors[index]->primitive_size; + } + + if(!required_bytes) { + result.memory = nullptr; + return true; + } + + //logTrace(LOG_GENERAL, "[MYSQL] Allocated {} bytes for response", required_bytes); + result.memory = (BindMemory*) malloc(required_bytes); + if(!result.memory) + return false; + + memset(result.memory, 0, required_bytes); + auto memory_ptr = (char*) result.memory + (sizeof(MYSQL_BIND) * field_count); + auto bind_ptr = (MYSQL_BIND*) result.memory; + for(size_t index = 0; index < field_count; index++) { + if(!result.descriptors[index]->create(fields[index], *bind_ptr, memory_ptr)) return false; + bind_ptr->buffer_type = fields[index].type; + bind_ptr++; + } + assert(memory_ptr == ((char*) result.memory + required_bytes)); /* Overflow check */ + return true; + } + } + +AcquiredConnection::AcquiredConnection(MySQLManager* owner, std::shared_ptr connection) : owner(owner), connection(std::move(connection)) { } +AcquiredConnection::~AcquiredConnection() { + { + lock_guard lock{this->connection->used_lock}; + this->connection->used = false; + } + + { + lock_guard lock(this->owner->connections_lock); + this->owner->connections_condition.notify_one(); + } +} +std::unique_ptr MySQLManager::next_connection() { + unique_ptr result; + { + unique_lock connections_lock(this->connections_lock); + + while(!result) { + size_t available_connections = 0; + for(const auto& connection : this->connections) { + available_connections++; + + { + lock_guard use_lock(connection->used_lock); + if(connection->used) continue; + connection->used = true; + } + + result = std::make_unique(this, connection); + break; + } + + if(!result) { + if(available_connections == 0) { + if(this->listener_disconnected) + this->listener_disconnected(false); + this->disconnect(); + return nullptr; + } + + this->connections_condition.wait(connections_lock); /* wait for the next connection */ + } + } + } + //TODO: Test if the connection hasn't been used for a longer while if so use mysql_ping() to verify the connection + + return result; +} + +void MySQLManager::connection_closed(const std::shared_ptr &connection) { + bool call_disconnect = false; + { + unique_lock connections_lock(this->connections_lock); + auto index = find(this->connections.begin(), this->connections.end(), connection); + if(index == this->connections.end()) return; + + this->connections.erase(index); + call_disconnect = this->connections.empty(); + } + + auto dl = this->listener_disconnected; + if(call_disconnect && dl) + dl(this->disconnecting); +} + +result MySQLManager::executeCommand(std::shared_ptr _ptr) { + auto ptr = static_pointer_cast(_ptr); + if(!ptr) { return {-1, "invalid command handle"}; } + + std::lock_guard lock(ptr->lock); + auto command = ptr->sql_command; + + auto variables = ptr->variables; + vector mapped_variables; + if(!sql::mysql::evaluate_sql_query(command, variables, mapped_variables)) + return {ptr->sql_command, -1, "Could not map sqlite vars to mysql!"}; + + FreeGuard bind_parameter_memory{nullptr}; + if(!sql::mysql::create_bind(bind_parameter_memory.ptr, mapped_variables)) + return {ptr->sql_command, -1, "Failed to allocate bind memory!"}; + + ResultBind bind_result_data{.field_count = 0, .memory = nullptr, .descriptors = nullptr}; + + auto connection = this->next_connection(); + if(!connection) return {ptr->sql_command, -1, "Could not get a valid connection!"}; + + StatementGuard stmt_guard{mysql_stmt_init(connection->connection->handle)}; + if(!stmt_guard.stmt) + return {ptr->sql_command, -1, "failed to allocate statement"}; + + if(mysql_stmt_prepare(stmt_guard.stmt, command.c_str(), command.length())) { + auto errc = mysql_stmt_errno(stmt_guard.stmt); + if(errc == CR_SERVER_GONE_ERROR || errc == CR_SERVER_LOST || errc == CR_CONNECTION_ERROR) + this->connection_closed(connection->connection); + + return {ptr->sql_command, -1, "failed to prepare statement: " + string(mysql_stmt_error(stmt_guard.stmt))}; + } + + /* validate all parameters */ + auto parameter_count = mysql_stmt_param_count(stmt_guard.stmt); + if(parameter_count != mapped_variables.size()) + return {ptr->sql_command, -1, "invalid parameter count. Statement contains " + to_string(parameter_count) + " parameters but only " + to_string(mapped_variables.size()) + " are given."}; + + if(bind_parameter_memory.ptr) { + if(mysql_stmt_bind_param(stmt_guard.stmt, (MYSQL_BIND*) bind_parameter_memory.ptr)) + return {ptr->sql_command, -1, "failed to bind parameters to statement: " + string(mysql_stmt_error(stmt_guard.stmt))}; + } else if(parameter_count > 0) + return {ptr->sql_command, -1, "invalid parameter count. Statement contains " + to_string(parameter_count) + " parameters but only " + to_string(mapped_variables.size()) + " are given (bind nullptr)."}; + + + if(mysql_stmt_execute(stmt_guard.stmt)) { + auto errc = mysql_stmt_errno(stmt_guard.stmt); + if(errc == CR_SERVER_GONE_ERROR || errc == CR_SERVER_LOST || errc == CR_CONNECTION_ERROR) + this->connection_closed(connection->connection); + + return {ptr->sql_command, -1, "failed to execute query statement: " + string(mysql_stmt_error(stmt_guard.stmt))}; + } + + return result::success; +} + +result MySQLManager::queryCommand(shared_ptr _ptr, const QueryCallback &fn) { + auto ptr = static_pointer_cast(_ptr); + if(!ptr) { return {-1, "invalid command handle"}; } + + std::lock_guard lock(ptr->lock); + auto command = ptr->sql_command; + + auto variables = ptr->variables; + vector mapped_variables; + if(!sql::mysql::evaluate_sql_query(command, variables, mapped_variables)) return {ptr->sql_command, -1, "Could not map sqlite vars to mysql!"}; + + FreeGuard bind_parameter_memory{nullptr}; + if(!sql::mysql::create_bind(bind_parameter_memory.ptr, mapped_variables)) return {ptr->sql_command, -1, "Failed to allocate bind memory!"}; + + ResultBind bind_result_data{.field_count = 0, .memory = nullptr, .descriptors = nullptr}; + + auto connection = this->next_connection(); + if(!connection) return {ptr->sql_command, -1, "Could not get a valid connection!"}; + + StatementGuard stmt_guard{mysql_stmt_init(connection->connection->handle)}; + if(!stmt_guard.stmt) + return {ptr->sql_command, -1, "failed to allocate statement"}; + + if(mysql_stmt_prepare(stmt_guard.stmt, command.c_str(), command.length())) { + auto errc = mysql_stmt_errno(stmt_guard.stmt); + if(errc == CR_SERVER_GONE_ERROR || errc == CR_SERVER_LOST || errc == CR_CONNECTION_ERROR) + this->connection_closed(connection->connection); + + return {ptr->sql_command, -1, "failed to prepare statement: " + string(mysql_stmt_error(stmt_guard.stmt))}; + } + + /* validate all parameters */ + { + auto parameter_count = mysql_stmt_param_count(stmt_guard.stmt); + if(parameter_count != mapped_variables.size()) + return {ptr->sql_command, -1, "invalid parameter count. Statement contains " + to_string(parameter_count) + " parameters but only " + to_string(mapped_variables.size()) + " are given."}; + } + + if(bind_parameter_memory.ptr) { + if(mysql_stmt_bind_param(stmt_guard.stmt, (MYSQL_BIND*) bind_parameter_memory.ptr)) + return {ptr->sql_command, -1, "failed to bind parameters to statement: " + string(mysql_stmt_error(stmt_guard.stmt))}; + } + + if(mysql_stmt_execute(stmt_guard.stmt)) { + auto errc = mysql_stmt_errno(stmt_guard.stmt); + if(errc == CR_SERVER_GONE_ERROR || errc == CR_SERVER_LOST || errc == CR_CONNECTION_ERROR) + this->connection_closed(connection->connection); + + return {ptr->sql_command, -1, "failed to execute query statement: " + string(mysql_stmt_error(stmt_guard.stmt))}; + } + + //if(mysql_stmt_store_result(stmt_guard.stmt)) + // return {ptr->sql_command, -1, "failed to store query result: " + string(mysql_stmt_error(stmt_guard.stmt))}; + + ResultGuard result_guard{mysql_stmt_result_metadata(stmt_guard.stmt)}; + if(!result_guard.result) + return {ptr->sql_command, -1, "failed to query result metadata: " + string(mysql_stmt_error(stmt_guard.stmt))}; + + + auto field_count = mysql_num_fields(result_guard.result); + DeleteAGuard field_names{new string[field_count]}; + DeleteAGuard field_values{new string[field_count]}; + + { + auto field_meta = mysql_fetch_fields(result_guard.result); + if(!field_meta && field_count > 0) + return {ptr->sql_command, -1, "failed to fetch field meta"}; + + if(!sql::mysql::create_result_bind(field_count, field_meta, bind_result_data)) + return {ptr->sql_command, -1, "failed to allocate result buffer"}; + + if(mysql_stmt_bind_result(stmt_guard.stmt, (MYSQL_BIND*) bind_result_data.memory)) + return {ptr->sql_command, -1, "failed to bind response buffer to statement: " + string(mysql_stmt_error(stmt_guard.stmt))}; + + for(size_t index = 0; index < field_count; index++) { + field_names.ptr[index] = field_meta[index].name; // field_meta cant be null because it has been checked above + //cout << field_names.ptr[index] << " - " << field_meta[index].max_length << endl; + } + } + + bool user_quit = false; + int stmt_code, row_id = 0; + while(!(stmt_code = mysql_stmt_fetch(stmt_guard.stmt))) { + bind_result_data.get_as_string(field_values.ptr); + + if(fn(field_count, field_values.ptr, field_names.ptr) != 0) { + user_quit = true; + break; + } + + row_id++; + } + + if(!user_quit) { + if(stmt_code == 1) { + auto errc = mysql_stmt_errno(stmt_guard.stmt); + if(errc == CR_SERVER_GONE_ERROR || errc == CR_SERVER_LOST || errc == CR_CONNECTION_ERROR) + this->connection_closed(connection->connection); + + return {ptr->sql_command, -1, "failed to fetch response row " + to_string(row_id) + ": " + string(mysql_stmt_error(stmt_guard.stmt))}; + } else if(stmt_code == MYSQL_NO_DATA) + ; + else if(stmt_code == MYSQL_DATA_TRUNCATED) + return {ptr->sql_command, -1, "response data has been truncated"}; + } + return result::success; } \ No newline at end of file diff --git a/src/sql/mysql/MySQL.h b/src/sql/mysql/MySQL.h index 8986774..3e06893 100644 --- a/src/sql/mysql/MySQL.h +++ b/src/sql/mysql/MySQL.h @@ -1,74 +1,74 @@ -#pragma once - -#include -#include -#include -#include -#include "sql/SqlQuery.h" - -#include "../../misc/spin_lock.h" -#include - -#define ERROR_MYSQL_MISSING_DRIVER -1 -#define ERROR_MYSQL_INVLID_CONNECT -2 -#define ERROR_MYSQL_INVLID_PROPERTIES -3 -#define ERROR_MYSQL_INVLID_URL -4 - -namespace sql::mysql { - class MySQLManager; - - bool evaluate_sql_query(std::string& sql, const std::vector& vars, std::vector& result); - - class MySQLCommand : public CommandData { }; - - struct Connection { - MYSQL* handle = nullptr; - - spin_lock used_lock; - bool used = false; - - ~Connection(); - }; - - struct AcquiredConnection { - MySQLManager* owner; - std::shared_ptr connection; - - AcquiredConnection(MySQLManager* owner, std::shared_ptr ); - ~AcquiredConnection(); - }; - - class MySQLManager : public SqlManager { - friend struct AcquiredConnection; - public: - //typedef std::function&)> ListenerConnectionDisconnect; - //typedef std::function&)> ListenerConnectionCreated; - - typedef std::function ListenerConnected; - typedef std::function ListenerDisconnected; - - MySQLManager(); - virtual ~MySQLManager(); - - result connect(const std::string &string) override; - bool connected() override; - result disconnect() override; - - ListenerDisconnected listener_disconnected; - protected: - std::shared_ptr copyCommandData(std::shared_ptr ptr) override; - std::shared_ptr allocateCommandData() override; - result executeCommand(std::shared_ptr ptr) override; - result queryCommand(std::shared_ptr ptr, const QueryCallback &fn) override; - - public: - std::unique_ptr next_connection(); - void connection_closed(const std::shared_ptr& /* connection */); - - std::mutex connections_lock; - std::condition_variable connections_condition; - std::deque> connections; - - bool disconnecting = false; - }; +#pragma once + +#include +#include +#include +#include +#include "sql/SqlQuery.h" + +#include "../../misc/spin_lock.h" +#include + +#define ERROR_MYSQL_MISSING_DRIVER -1 +#define ERROR_MYSQL_INVLID_CONNECT -2 +#define ERROR_MYSQL_INVLID_PROPERTIES -3 +#define ERROR_MYSQL_INVLID_URL -4 + +namespace sql::mysql { + class MySQLManager; + + bool evaluate_sql_query(std::string& sql, const std::vector& vars, std::vector& result); + + class MySQLCommand : public CommandData { }; + + struct Connection { + MYSQL* handle = nullptr; + + spin_lock used_lock; + bool used = false; + + ~Connection(); + }; + + struct AcquiredConnection { + MySQLManager* owner; + std::shared_ptr connection; + + AcquiredConnection(MySQLManager* owner, std::shared_ptr ); + ~AcquiredConnection(); + }; + + class MySQLManager : public SqlManager { + friend struct AcquiredConnection; + public: + //typedef std::function&)> ListenerConnectionDisconnect; + //typedef std::function&)> ListenerConnectionCreated; + + typedef std::function ListenerConnected; + typedef std::function ListenerDisconnected; + + MySQLManager(); + virtual ~MySQLManager(); + + result connect(const std::string &string) override; + bool connected() override; + result disconnect() override; + + ListenerDisconnected listener_disconnected; + protected: + std::shared_ptr copyCommandData(std::shared_ptr ptr) override; + std::shared_ptr allocateCommandData() override; + result executeCommand(std::shared_ptr ptr) override; + result queryCommand(std::shared_ptr ptr, const QueryCallback &fn) override; + + public: + std::unique_ptr next_connection(); + void connection_closed(const std::shared_ptr& /* connection */); + + std::mutex connections_lock; + std::condition_variable connections_condition; + std::deque> connections; + + bool disconnecting = false; + }; } \ No newline at end of file