From 7dcf4a54ef7d5ba26e9796b01794843f4b4a3efc Mon Sep 17 00:00:00 2001 From: WolverinDEV Date: Thu, 13 Aug 2020 12:58:19 +0200 Subject: [PATCH] Some changes --- git-teaspeak | 2 +- server/CMakeLists.txt | 2 +- server/src/DatabaseHelper.cpp | 8 +- server/src/client/ConnectedClient.cpp | 8 ++ server/src/client/ConnectedClient.h | 2 + server/src/client/SpeakingClient.cpp | 29 ++--- server/src/client/SpeakingClient.h | 152 ++++++++++++----------- server/src/client/query/QueryClient.cpp | 16 +++ server/src/client/web/VoiceBridge.cpp | 57 ++++++--- server/src/client/web/VoiceBridge.h | 10 +- server/src/client/web/WebClient.cpp | 77 +++++++----- server/src/client/web/WebClient.h | 154 ++++++++++++------------ server/src/manager/SqlDataManager.cpp | 18 ++- 13 files changed, 310 insertions(+), 225 deletions(-) diff --git a/git-teaspeak b/git-teaspeak index 32b1ef9..fe633dc 160000 --- a/git-teaspeak +++ b/git-teaspeak @@ -1 +1 @@ -Subproject commit 32b1ef973d44c668fc2e74735491db3896a32c2d +Subproject commit fe633dc99a6ea91c2c0a8cb13a51a7587a54cb2e diff --git a/server/CMakeLists.txt b/server/CMakeLists.txt index b408186..261bfa1 100644 --- a/server/CMakeLists.txt +++ b/server/CMakeLists.txt @@ -257,7 +257,7 @@ target_link_libraries(PermMapHelper SET(CPACK_PACKAGE_VERSION_MAJOR "1") SET(CPACK_PACKAGE_VERSION_MINOR "4") -SET(CPACK_PACKAGE_VERSION_PATCH "18") +SET(CPACK_PACKAGE_VERSION_PATCH "19") if (BUILD_TYPE_NAME EQUAL OFF) SET(CPACK_PACKAGE_VERSION_DATA "beta") elseif (BUILD_TYPE_NAME STREQUAL "") diff --git a/server/src/DatabaseHelper.cpp b/server/src/DatabaseHelper.cpp index acdd340..7d98f10 100644 --- a/server/src/DatabaseHelper.cpp +++ b/server/src/DatabaseHelper.cpp @@ -994,6 +994,10 @@ std::shared_ptr DatabaseHelper::loadClientProperties(const std::shar column = "client_nickname"; break; + case property::CONNECTION_CLIENT_IP: + column = "client_ip"; + break; + case property::CLIENT_LASTCONNECTED: column = "client_last_connected"; break; @@ -1326,10 +1330,10 @@ void DatabaseHelper::listDatabaseClients( client.client_ip = values[index++]; assert(names[index] == "client_created"); - client.client_last_connected = values[index++]; + client.client_created = values[index++]; assert(names[index] == "client_last_connected"); - client.client_created = values[index++]; + client.client_last_connected = values[index++]; assert(names[index] == "client_total_connections"); client.client_total_connections = values[index++]; diff --git a/server/src/client/ConnectedClient.cpp b/server/src/client/ConnectedClient.cpp index ce69df9..5222937 100644 --- a/server/src/client/ConnectedClient.cpp +++ b/server/src/client/ConnectedClient.cpp @@ -36,6 +36,14 @@ ConnectedClient::~ConnectedClient() { memtrack::freed(this); } +bool ConnectedClient::loadDataForCurrentServer() { + auto result = DataClient::loadDataForCurrentServer(); + if(!result) return false; + + this->properties()[property::CONNECTION_CLIENT_IP] = this->getLoggingPeerIp(); + return true; +} + std::shared_ptr ConnectedClient::request_connection_info(const std::shared_ptr &receiver, bool& send_temp) { auto& info = this->connection_info; diff --git a/server/src/client/ConnectedClient.h b/server/src/client/ConnectedClient.h index f927878..60ae9ce 100644 --- a/server/src/client/ConnectedClient.h +++ b/server/src/client/ConnectedClient.h @@ -375,6 +375,8 @@ namespace ts { std::weak_ptr subscribed_bot; std::weak_ptr _subscribed_playlist{}; + bool loadDataForCurrentServer() override; + virtual void tick(const std::chrono::system_clock::time_point &time); //Locked by everything who has something todo with command handling threads::Mutex command_lock; /* Note: This mutex must be recursive! */ diff --git a/server/src/client/SpeakingClient.cpp b/server/src/client/SpeakingClient.cpp index 51511de..5572690 100644 --- a/server/src/client/SpeakingClient.cpp +++ b/server/src/client/SpeakingClient.cpp @@ -124,7 +124,7 @@ enum WhisperType { CHANNEL_COMMANDER = 2, ALL = 3, - ECHO_TEXT = 0x10, + ECHO = 0x10, }; enum WhisperTarget { @@ -184,7 +184,7 @@ void SpeakingClient::handlePacketVoiceWhisper(const pipes::buffer_view& data, bo #endif deque> available_clients; - if(type == WhisperType::ECHO_TEXT) { + if(type == WhisperType::ECHO) { available_clients.push_back(dynamic_pointer_cast(this->ref())); } else { for(const auto& client : this->server->getClients()) { @@ -269,21 +269,22 @@ void SpeakingClient::handlePacketVoiceWhisper(const pipes::buffer_view& data, bo auto speakingClient = dynamic_pointer_cast(cl); return !speakingClient->shouldReceiveVoiceWhisper(self_lock); }), available_clients.end()); - } - if(available_clients.empty()) { - if(update_whisper_error(this->speak_last_no_whisper_target)) { - command_result result{error::whisper_no_targets}; - this->notifyError(result); + if(available_clients.empty()) { + if(update_whisper_error(this->speak_last_no_whisper_target)) { + command_result result{error::whisper_no_targets}; + this->notifyError(result); + } + return; } - return; - } - if(available_clients.size() > this->server->properties()[property::VIRTUALSERVER_MIN_CLIENTS_IN_CHANNEL_BEFORE_FORCED_SILENCE].as_save()) { - if(update_whisper_error(this->speak_last_too_many_whisper_targets)) { - command_result result{error::whisper_too_many_targets}; - this->notifyError(result); + + if(available_clients.size() > this->server->properties()[property::VIRTUALSERVER_MIN_CLIENTS_IN_CHANNEL_BEFORE_FORCED_SILENCE].as_save()) { + if(update_whisper_error(this->speak_last_too_many_whisper_targets)) { + command_result result{error::whisper_too_many_targets}; + this->notifyError(result); + } + return; } - return; } //Create the packet data diff --git a/server/src/client/SpeakingClient.h b/server/src/client/SpeakingClient.h index 643d5c7..4f5baad 100644 --- a/server/src/client/SpeakingClient.h +++ b/server/src/client/SpeakingClient.h @@ -3,98 +3,96 @@ #include #include "ConnectedClient.h" -namespace ts { - namespace server { - class VirtualServer; - class SpeakingClient : public ConnectedClient { - public: - struct VoicePacketFlags { - bool encrypted : 1; - bool head : 1; - bool fragmented : 1; /* used by MONO. IDK What this is */ - bool new_protocol : 1; - char _unused : 4; +namespace ts::server { + class VirtualServer; + class SpeakingClient : public ConnectedClient { + public: + struct VoicePacketFlags { + bool encrypted : 1; + bool head : 1; + bool fragmented : 1; /* used by MONO. IDK What this is */ + bool new_protocol : 1; + char _unused : 4; - VoicePacketFlags() : encrypted{false}, head{false}, fragmented{false}, new_protocol{false}, _unused{0} { } - }; - static_assert(sizeof(VoicePacketFlags) == 1); + VoicePacketFlags() : encrypted{false}, head{false}, fragmented{false}, new_protocol{false}, _unused{0} { } + }; + static_assert(sizeof(VoicePacketFlags) == 1); - enum HandshakeState { - BEGIN, - IDENTITY_PROOF, - SUCCEEDED - }; - enum IdentityType : uint8_t { - TEASPEAK_FORUM, - TEAMSPEAK, - NICKNAME, + enum HandshakeState { + BEGIN, + IDENTITY_PROOF, + SUCCEEDED + }; + enum IdentityType : uint8_t { + TEASPEAK_FORUM, + TEAMSPEAK, + NICKNAME, - UNSET = 0xff - }; + UNSET = 0xff + }; - SpeakingClient(sql::SqlManager* a, const std::shared_ptr& b) : ConnectedClient(a, b) { - speak_begin = std::chrono::system_clock::now(); - speak_last_packet = std::chrono::system_clock::now(); - }; - ~SpeakingClient() override = default; + SpeakingClient(sql::SqlManager* a, const std::shared_ptr& b) : ConnectedClient(a, b) { + speak_begin = std::chrono::system_clock::now(); + speak_last_packet = std::chrono::system_clock::now(); + }; + ~SpeakingClient() override = default; - //Voice - virtual void send_voice_packet(const pipes::buffer_view& /* voice packet data */, const VoicePacketFlags& /* flags */) = 0; - virtual bool shouldReceiveVoice(const std::shared_ptr &sender); + //Voice + virtual void send_voice_packet(const pipes::buffer_view& /* voice packet data */, const VoicePacketFlags& /* flags */) = 0; + virtual bool shouldReceiveVoice(const std::shared_ptr &sender); - //Whisper - bool shouldReceiveVoiceWhisper(const std::shared_ptr &sender); - virtual void send_voice_whisper_packet(const pipes::buffer_view& /* voice packet data */, const VoicePacketFlags& /* flags */) = 0; + //Whisper + bool shouldReceiveVoiceWhisper(const std::shared_ptr &sender); + virtual void send_voice_whisper_packet(const pipes::buffer_view& /* voice packet data */, const VoicePacketFlags& /* flags */) = 0; - inline std::chrono::milliseconds takeSpokenTime() { - auto time = this->speak_time; - this->speak_time = std::chrono::milliseconds(0); - return time; - } - protected: - void tick(const std::chrono::system_clock::time_point &time) override; + inline std::chrono::milliseconds takeSpokenTime() { + auto time = this->speak_time; + this->speak_time = std::chrono::milliseconds(0); + return time; + } + protected: + void tick(const std::chrono::system_clock::time_point &time) override; - protected: - public: - void updateChannelClientProperties(bool channel_lock, bool notify) override; + protected: + public: + void updateChannelClientProperties(bool channel_lock, bool notify) override; - protected: - command_result handleCommand(Command &command) override; + protected: + command_result handleCommand(Command &command) override; - public: - void handlePacketVoice(const pipes::buffer_view&, bool head, bool fragmented); - virtual void handlePacketVoiceWhisper(const pipes::buffer_view&, bool /* new */); + public: + void handlePacketVoice(const pipes::buffer_view&, bool head, bool fragmented); + virtual void handlePacketVoiceWhisper(const pipes::buffer_view&, bool /* new */); - void processJoin(); - void processLeave(); + void processJoin(); + void processLeave(); - virtual command_result handleCommandHandshakeBegin(Command&); - virtual command_result handleCommandHandshakeIdentityProof(Command &); - virtual command_result handleCommandClientInit(Command&); + virtual command_result handleCommandHandshakeBegin(Command&); + virtual command_result handleCommandHandshakeIdentityProof(Command &); + virtual command_result handleCommandClientInit(Command&); - void triggerVoiceEnd(); - inline void updateSpeak(bool onlyUpdate, const std::chrono::system_clock::time_point &time); - std::chrono::milliseconds speak_accuracy = std::chrono::seconds{1}; + void triggerVoiceEnd(); + inline void updateSpeak(bool onlyUpdate, const std::chrono::system_clock::time_point &time); + std::chrono::milliseconds speak_accuracy = std::chrono::seconds{1}; - threads::Mutex speak_lock; - std::chrono::milliseconds speak_time = std::chrono::milliseconds{0}; - std::chrono::system_clock::time_point speak_begin; - std::chrono::system_clock::time_point speak_last_packet; + threads::Mutex speak_lock; + std::chrono::milliseconds speak_time = std::chrono::milliseconds{0}; + std::chrono::system_clock::time_point speak_begin; + std::chrono::system_clock::time_point speak_last_packet; - std::chrono::system_clock::time_point speak_last_no_whisper_target; - std::chrono::system_clock::time_point speak_last_too_many_whisper_targets; + std::chrono::system_clock::time_point speak_last_no_whisper_target; + std::chrono::system_clock::time_point speak_last_too_many_whisper_targets; - permission::v2::PermissionFlaggedValue max_idle_time{permission::v2::empty_permission_flagged_value}; - struct { - HandshakeState state{HandshakeState::BEGIN}; + permission::v2::PermissionFlaggedValue max_idle_time{permission::v2::empty_permission_flagged_value}; + struct { + HandshakeState state{HandshakeState::BEGIN}; - IdentityType identityType{IdentityType::UNSET}; - std::string proof_message; - //TeamSpeak - std::shared_ptr identityKey; - //TeaSpeak - std::shared_ptr identityData; - } handshake; - }; - } + IdentityType identityType{IdentityType::UNSET}; + std::string proof_message; + //TeamSpeak + std::shared_ptr identityKey; + //TeaSpeak + std::shared_ptr identityData; + } handshake; + }; } \ No newline at end of file diff --git a/server/src/client/query/QueryClient.cpp b/server/src/client/query/QueryClient.cpp index e7a16ed..607dddf 100644 --- a/server/src/client/query/QueryClient.cpp +++ b/server/src/client/query/QueryClient.cpp @@ -475,6 +475,22 @@ bool QueryClient::handleMessage(const pipes::buffer_view& message) { return true; } + if(auto non_escape{command.find_first_not_of('\r')}; non_escape == std::string::npos) { + logTrace(LOG_QUERY, "[{}:{}] Got query idle command (\\r)", this->getLoggingPeerIp(), this->getPeerPort()); + CMD_RESET_IDLE; //if idle time over 5 min than connection drop + return true; + } else { + command = command.substr(non_escape); + } + + if(auto non_escape{command.find_first_not_of('\n')}; non_escape == std::string::npos) { + logTrace(LOG_QUERY, "[{}:{}] Got query idle command (\\n)", this->getLoggingPeerIp(), this->getPeerPort()); + CMD_RESET_IDLE; //if idle time over 5 min than connection drop + return true; + } else { + command = command.substr(non_escape); + } + if((uint8_t) command[0] == 255) { string commands{}; diff --git a/server/src/client/web/VoiceBridge.cpp b/server/src/client/web/VoiceBridge.cpp index cb31be0..99c2879 100644 --- a/server/src/client/web/VoiceBridge.cpp +++ b/server/src/client/web/VoiceBridge.cpp @@ -145,7 +145,7 @@ std::string VoiceBridge::generate_answer() { } void VoiceBridge::execute_tick() { - if(!this->_voice_channel) { + if(!this->voice_channel_) { if(this->offer_timestamp.time_since_epoch().count() > 0 && this->offer_timestamp + chrono::seconds{20} < chrono::system_clock::now()) { this->offer_timestamp = chrono::system_clock::time_point(); this->connection->callback_setup_fail(rtc::PeerConnection::ConnectionComponent::BASE, "setup timeout"); @@ -182,26 +182,47 @@ void VoiceBridge::handle_media_stream(const std::shared_ptr &undef } void VoiceBridge::handle_data_channel(const std::shared_ptr &channel) { - if(channel->lable() == "main") { - this->_voice_channel = channel; + if(channel->lable() == "main" || channel->lable() == "voice") { + this->voice_channel_ = channel; debugMessage(this->server_id(), "{} Got voice channel!", CLIENT_STR_LOG_PREFIX_(this->owner())); this->callback_initialized(); + + weak_ptr weak_channel = channel; + channel->callback_binary = [&, weak_channel](const pipes::buffer_view& buffer) { + if(buffer.length() < 2) + return; + + this->callback_voice_data(buffer.view(2), buffer[0] == 1, buffer[1] == 1); /* buffer.substr(2), buffer[0] == 1, buffer[1] == 1 */ + }; + + channel->callback_close = [&, weak_channel] { + auto channel_ref = weak_channel.lock(); + if(channel_ref == this->voice_channel_) { + this->voice_channel_ = nullptr; + //TODO may callback? + debugMessage(this->server_id(), "{} Voice channel disconnected!", CLIENT_STR_LOG_PREFIX_(this->owner())); + } + }; + } else if(channel->lable() == "voice-whisper") { + this->voice_whisper_channel_ = channel; + debugMessage(this->server_id(), "{} Got voice whisper channel", CLIENT_STR_LOG_PREFIX_(this->owner())); + + weak_ptr weak_channel = channel; + channel->callback_binary = [&, weak_channel](const pipes::buffer_view& buffer) { + if(buffer.length() < 1) + return; + + this->callback_voice_whisper_data(buffer.view(2), buffer[0] == 1); + }; + + channel->callback_close = [&, weak_channel] { + auto channel_ref = weak_channel.lock(); + if(channel_ref == this->voice_whisper_channel_) { + this->voice_whisper_channel_ = nullptr; + debugMessage(this->server_id(), "{} Voice whisper channel has been closed.", CLIENT_STR_LOG_PREFIX_(this->owner())); + } + }; } - - weak_ptr weak_channel = channel; - channel->callback_binary = [&, weak_channel](const pipes::buffer_view& buffer) { - if(buffer.length() < 2) - return; - this->callback_voice_data(buffer.view(2), buffer[0] == 1, buffer[1] == 1); /* buffer.substr(2), buffer[0] == 1, buffer[1] == 1 */ - }; - - channel->callback_close = [&, channel] { - if(channel == this->_voice_channel) { - this->_voice_channel = nullptr; - //TODO may callback? - debugMessage(this->server_id(), "{} Voice channel disconnected!", CLIENT_STR_LOG_PREFIX_(this->owner())); - } - }; } void VoiceBridge::handle_audio_data(const std::shared_ptr &channel, const pipes::buffer_view &data, size_t payload_offset) { diff --git a/server/src/client/web/VoiceBridge.h b/server/src/client/web/VoiceBridge.h index 1fd3c56..314e07a 100644 --- a/server/src/client/web/VoiceBridge.h +++ b/server/src/client/web/VoiceBridge.h @@ -12,12 +12,14 @@ namespace ts { class VoiceBridge { public: typedef std::function cb_voice_data; + typedef std::function cb_voice_whisper_data; typedef std::function cb_ice_candidate; typedef std::function cb_ice_candidate_finish; typedef std::function cb_initialized; typedef std::function cb_failed; - std::shared_ptr voice_channel() { return this->_voice_channel; } + std::shared_ptr voice_channel() { return this->voice_channel_; } + std::shared_ptr voice_whisper_channel() { return this->voice_whisper_channel_; } explicit VoiceBridge(const std::shared_ptr&); virtual ~VoiceBridge(); @@ -31,6 +33,7 @@ namespace ts { cb_ice_candidate callback_ice_candidate; cb_ice_candidate_finish callback_ice_candidate_finished; cb_voice_data callback_voice_data; + cb_voice_whisper_data callback_voice_whisper_data; cb_initialized callback_initialized; cb_failed callback_failed; @@ -48,7 +51,10 @@ namespace ts { std::weak_ptr _owner; std::chrono::system_clock::time_point offer_timestamp; std::unique_ptr connection; - std::shared_ptr _voice_channel; + + std::shared_ptr voice_channel_; + std::shared_ptr voice_whisper_channel_; + std::weak_ptr _audio_channel; struct { uint16_t packet_id = 0; diff --git a/server/src/client/web/WebClient.cpp b/server/src/client/web/WebClient.cpp index 5f235f6..1c22758 100644 --- a/server/src/client/web/WebClient.cpp +++ b/server/src/client/web/WebClient.cpp @@ -156,22 +156,29 @@ void WebClient::sendJson(const Json::Value& json) { } void WebClient::sendCommand(const ts::Command &command, bool low) { - Json::Value value = command.buildJson(); - value["type"] = "command"; - this->sendJson(value); + if(this->allow_raw_commands) { + Json::Value value{}; + value["type"] = "command-raw"; + value["payload"] = command.build(); + this->sendJson(value); + } else { + Json::Value value = command.buildJson(); + value["type"] = "command"; + this->sendJson(value); + } } void WebClient::sendCommand(const ts::command_builder &command, bool low) { -#if false - Json::Value value{}; - value["type"] = "command2"; - value["payload"] = command.build(); - this->sendJson(value); -#else - auto data = command.build(); - Command parsed_command = Command::parse(pipes::buffer_view{data.data(), data.length()}, true, false); - this->sendCommand(parsed_command, low); -#endif + if(this->allow_raw_commands) { + Json::Value value{}; + value["type"] = "command-raw"; + value["payload"] = command.build(); + this->sendJson(value); + } else { + auto data = command.build(); + Command parsed_command = Command::parse(pipes::buffer_view{data.data(), data.length()}, true, false); + this->sendCommand(parsed_command, low); + } } bool WebClient::close_connection(const std::chrono::system_clock::time_point& timeout) { @@ -418,7 +425,7 @@ void WebClient::disconnectFinal() { this->handle->unregisterConnection(static_pointer_cast(self_lock)); } -Json::CharReaderBuilder json_reader_builder = []{ +Json::CharReaderBuilder json_reader_builder = []() noexcept { Json::CharReaderBuilder reader_builder; return reader_builder; @@ -432,7 +439,8 @@ void WebClient::handleMessage(const std::string &message) { unique_ptr reader{json_reader_builder.newCharReader()}; string error_message; - if(!reader->parse(message.data(),message.data() + message.length(), &val, &error_message)) throw Json::Exception("Could not parse payload! (" + error_message + ")"); + if(!reader->parse(message.data(),message.data() + message.length(), &val, &error_message)) + throw Json::Exception("Could not parse payload! (" + error_message + ")"); } catch (const std::exception& ex) { logError(this->server->getServerId(), "Could not parse web message! Message: " + string(ex.what())); logTrace(this->server->getServerId(), "Payload: " + message); @@ -458,7 +466,7 @@ void WebClient::handleMessage(const std::string &message) { } else if(val["type"].asString() == "WebRTC") { auto subType = val["request"].asString(); if(subType == "create") { - unique_lock voice_bridge_lock(this->voice_bridge_lock); + std::unique_lock voice_bridge_lock_{this->voice_bridge_lock}; if(this->voice_bridge) { logError(this->server->getServerId(), "[{}] Tried to register a WebRTC channel twice!", CLIENT_STR_LOG_PREFIX_(this)); @@ -467,14 +475,19 @@ void WebClient::handleMessage(const std::string &message) { lock = nullptr; }).detach(); } - //TODO test if bridge already exists! - this->voice_bridge = make_unique(dynamic_pointer_cast(_this.lock())); //FIXME Add config + + this->voice_bridge = make_unique(dynamic_pointer_cast(this->ref())); //FIXME Add config this->voice_bridge->callback_voice_data = [&](const pipes::buffer_view& buffer, bool a, bool b) { /* may somehow get the "real" packet size? */ this->connectionStatistics->logIncomingPacket(stats::ConnectionStatistics::category::VOICE, buffer.length()); this->handlePacketVoice(buffer, a, b); }; + this->voice_bridge->callback_voice_whisper_data = [&](const pipes::buffer_view& buffer, bool a) { + /* may somehow get the "real" packet size? */ + this->connectionStatistics->logIncomingPacket(stats::ConnectionStatistics::category::VOICE, buffer.length()); + this->handlePacketVoiceWhisper(buffer, a); + }; this->voice_bridge->callback_initialized = [&](){ debugMessage(this->getServerId(), "{} Voice bridge initialized!", CLIENT_STR_LOG_PREFIX); }; @@ -521,7 +534,7 @@ void WebClient::handleMessage(const std::string &message) { }; auto vbp = &*this->voice_bridge; - voice_bridge_lock.unlock(); + voice_bridge_lock_.unlock(); shared_lock read_voice_bridge_lock(this->voice_bridge_lock); if(vbp != &*this->voice_bridge) { @@ -628,6 +641,8 @@ void WebClient::handleMessage(const std::string &message) { } this->js_ping.last_response = system_clock::now(); this->js_ping.value = duration_cast(this->js_ping.last_response - this->js_ping.last_request); + } else if(val["type"].asString() == "enable-raw-commands") { + this->allow_raw_commands = true; } } catch (const std::exception& ex) { logError(this->server->getServerId(), "Could not handle json packet! Message {}", ex.what()); @@ -664,21 +679,18 @@ command_result WebClient::handleCommandClientInit(Command &command) { bool WebClient::shouldReceiveVoice(const std::shared_ptr &sender) { shared_lock read_voice_bridge_lock(this->voice_bridge_lock); if(!this->voice_bridge || !this->voice_bridge->voice_channel()) return false; + return SpeakingClient::shouldReceiveVoice(sender); } -void WebClient::handlePacketVoiceWhisper(const pipes::buffer_view &string, bool flag) { - shared_lock read_voice_bridge_lock(this->voice_bridge_lock); - if(!this->voice_bridge || !this->voice_bridge->voice_channel()) return; - SpeakingClient::handlePacketVoiceWhisper(string, flag); -} - void WebClient::send_voice_packet(const pipes::buffer_view &view, const SpeakingClient::VoicePacketFlags &flags) { - shared_lock read_voice_bridge_lock(this->voice_bridge_lock); + std::shared_lock read_voice_bridge_lock(this->voice_bridge_lock); if(this->voice_bridge) { auto channel = this->voice_bridge->voice_channel(); if(channel) { channel->send(view); + read_voice_bridge_lock.unlock(); + /* may somehow get the "real" packet size? */ this->connectionStatistics->logOutgoingPacket(stats::ConnectionStatistics::category::VOICE, view.length()); } @@ -686,6 +698,15 @@ void WebClient::send_voice_packet(const pipes::buffer_view &view, const Speaking } void WebClient::send_voice_whisper_packet(const pipes::buffer_view &view, const SpeakingClient::VoicePacketFlags &flags) { - logError(this->server->getServerId(), "Web client got whisper packet"); - //As well log the data! + std::shared_lock read_voice_bridge_lock(this->voice_bridge_lock); + if(this->voice_bridge) { + auto channel = this->voice_bridge->voice_whisper_channel(); + if(channel) { + channel->send(view); + read_voice_bridge_lock.unlock(); + + /* may somehow get the "real" packet size? */ + this->connectionStatistics->logOutgoingPacket(stats::ConnectionStatistics::category::VOICE, view.length()); + } + } } diff --git a/server/src/client/web/WebClient.h b/server/src/client/web/WebClient.h index cd69d58..3d9f652 100644 --- a/server/src/client/web/WebClient.h +++ b/server/src/client/web/WebClient.h @@ -14,105 +14,103 @@ #include #include -namespace ts { - namespace server { - class WebControlServer; +namespace ts::server { + class WebControlServer; - class WebClient : public SpeakingClient { - friend class WebControlServer; - public: - WebClient(WebControlServer*, int socketFd); - ~WebClient() override; + class WebClient : public SpeakingClient { + friend class WebControlServer; + public: + WebClient(WebControlServer*, int socketFd); + ~WebClient() override; - void sendJson(const Json::Value&); - void sendCommand(const ts::Command &command, bool low = false) override; - void sendCommand(const ts::command_builder &command, bool low) override; + void sendJson(const Json::Value&); + void sendCommand(const ts::Command &command, bool low) override; + void sendCommand(const ts::command_builder &command, bool low) override; - bool disconnect(const std::string &reason) override; - bool close_connection(const std::chrono::system_clock::time_point& timeout = std::chrono::system_clock::time_point()) override; + bool disconnect(const std::string &reason) override; + bool close_connection(const std::chrono::system_clock::time_point& timeout = std::chrono::system_clock::time_point()) override; - bool shouldReceiveVoice(const std::shared_ptr &sender) override; + bool shouldReceiveVoice(const std::shared_ptr &sender) override; - inline std::chrono::nanoseconds client_ping() { return this->client_ping_layer_7(); } - inline std::chrono::nanoseconds client_ping_layer_5() { return this->ping.value; } - inline std::chrono::nanoseconds client_ping_layer_7() { return this->js_ping.value; } - protected: - void handlePacketVoiceWhisper(const pipes::buffer_view &string, bool) override; - protected: - void tick(const std::chrono::system_clock::time_point&) override; /* Every 500ms */ + inline std::chrono::nanoseconds client_ping() { return this->client_ping_layer_7(); } + inline std::chrono::nanoseconds client_ping_layer_5() { return this->ping.value; } + inline std::chrono::nanoseconds client_ping_layer_7() { return this->js_ping.value; } - void applySelfLock(const std::shared_ptr &cl){ _this = cl; } - private: - WebControlServer* handle; - std::chrono::time_point connectedTimestamp; + protected: + void tick(const std::chrono::system_clock::time_point&) override; /* Every 500ms */ - std::shared_mutex voice_bridge_lock; - std::unique_ptr voice_bridge; - int file_descriptor; + void applySelfLock(const std::shared_ptr &cl){ _this = cl; } + private: + WebControlServer* handle; + std::chrono::time_point connectedTimestamp; - std::mutex event_lock; - ::event* readEvent; - ::event* writeEvent; + std::shared_mutex voice_bridge_lock; + std::unique_ptr voice_bridge; + int file_descriptor; - struct { - uint8_t current_id = 0; - std::chrono::system_clock::time_point last_request; - std::chrono::system_clock::time_point last_response; + std::mutex event_lock; + ::event* readEvent; + ::event* writeEvent; - std::chrono::nanoseconds value{}; - std::chrono::nanoseconds timeout{2000}; - } ping; + struct { + uint8_t current_id = 0; + std::chrono::system_clock::time_point last_request; + std::chrono::system_clock::time_point last_response; - struct { - uint8_t current_id = 0; - std::chrono::system_clock::time_point last_request; - std::chrono::system_clock::time_point last_response; + std::chrono::nanoseconds value{}; + std::chrono::nanoseconds timeout{2000}; + } ping; - std::chrono::nanoseconds value{}; - std::chrono::nanoseconds timeout{2000}; - } js_ping; + struct { + uint8_t current_id = 0; + std::chrono::system_clock::time_point last_request; + std::chrono::system_clock::time_point last_response; - std::mutex queue_lock; - std::deque queue_read; - std::deque queue_write; - threads::Mutex execute_lock; /* needs to be recursive! */ + std::chrono::nanoseconds value{}; + std::chrono::nanoseconds timeout{2000}; + } js_ping; - std::thread flush_thread; - std::recursive_mutex close_lock; - private: - void initialize(); + std::mutex queue_lock; + std::deque queue_read; + std::deque queue_write; + threads::Mutex execute_lock; /* needs to be recursive! */ - bool ssl_detected = false; - bool ssl_encrypted = true; - pipes::SSL ssl_handler; - pipes::WebSocket ws_handler; - void handleMessageRead(int, short, void*); - void handleMessageWrite(int, short, void*); - void enqueue_raw_packet(const pipes::buffer_view& /* buffer */); + std::thread flush_thread; + std::recursive_mutex close_lock; + private: + void initialize(); - void processNextMessage(const std::chrono::system_clock::time_point& /* scheduled */); - void registerMessageProcess(); + bool allow_raw_commands{false}; + bool ssl_detected{false}; + bool ssl_encrypted{true}; + pipes::SSL ssl_handler; + pipes::WebSocket ws_handler; + void handleMessageRead(int, short, void*); + void handleMessageWrite(int, short, void*); + void enqueue_raw_packet(const pipes::buffer_view& /* buffer */); - std::shared_ptr> event_handle_packet; + void processNextMessage(const std::chrono::system_clock::time_point& /* scheduled */); + void registerMessageProcess(); - //WS events - void onWSConnected(); - void onWSDisconnected(const std::string& reason); - void onWSMessage(const pipes::WSMessage&); - protected: - void disconnectFinal(); - void handleMessage(const std::string &); + std::shared_ptr> event_handle_packet; - public: - void send_voice_packet(const pipes::buffer_view &view, const VoicePacketFlags &flags) override; - void send_voice_whisper_packet(const pipes::buffer_view &view, const VoicePacketFlags &flags) override; + //WS events + void onWSConnected(); + void onWSDisconnected(const std::string& reason); + void onWSMessage(const pipes::WSMessage&); + protected: + void disconnectFinal(); + void handleMessage(const std::string &); - protected: + public: + void send_voice_packet(const pipes::buffer_view &view, const VoicePacketFlags &flags) override; + void send_voice_whisper_packet(const pipes::buffer_view &view, const VoicePacketFlags &flags) override; - command_result handleCommand(Command &command) override; - command_result handleCommandClientInit(Command &command) override; - }; - } + protected: + + command_result handleCommand(Command &command) override; + command_result handleCommandClientInit(Command &command) override; + }; } #endif \ No newline at end of file diff --git a/server/src/manager/SqlDataManager.cpp b/server/src/manager/SqlDataManager.cpp index 518315d..394a146 100644 --- a/server/src/manager/SqlDataManager.cpp +++ b/server/src/manager/SqlDataManager.cpp @@ -79,9 +79,19 @@ do { \ } \ } while(0) +std::string replace_all(std::string str, const std::string& from, const std::string& to) { + size_t start_pos = 0; + while((start_pos = str.find(from, start_pos)) != std::string::npos) { + str.replace(start_pos, from.length(), to); + start_pos += to.length(); + } + return str; +} + template inline bool execute_commands(sql::SqlManager* sql, std::string& error, const std::array& commands) { std::string insert_or_ignore{sql->getType() == sql::TYPE_SQLITE ? "INSERT OR IGNORE" : "INSERT IGNORE"}; + std::string auto_increment{sql->getType() == sql::TYPE_SQLITE ? "AUTOINCREMENT" : "AUTO_INCREMENT"}; for(const auto& cmd : commands) { std::string command{cmd}; @@ -89,8 +99,8 @@ inline bool execute_commands(sql::SqlManager* sql, std::string& error, const std return !std::isspace(ch); })); - if(command.starts_with("[INSERT_OR_IGNORE]")) - command = insert_or_ignore + command.substr(18); + command = replace_all(command, "[INSERT_OR_IGNORE]", insert_or_ignore); + command = replace_all(command, "[AUTO_INCREMENT]", auto_increment); auto result = sql::command(sql, command).execute(); if(!result) { @@ -451,7 +461,7 @@ bool SqlDataManager::update_database(std::string &error) { case 12: { constexpr static std::string_view kCreateClientsV2{R"( CREATE TABLE `clients_v2` ( - `client_database_id` INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT, + `client_database_id` INTEGER NOT NULL PRIMARY KEY [AUTO_INCREMENT], `client_unique_id` VARCHAR(40) UNIQUE, `client_created` BIGINT, `client_login_name` VARCHAR(20) UNIQUE @@ -515,7 +525,7 @@ bool SqlDataManager::update_database(std::string &error) { "CREATE INDEX `idx_properties_serverid_id_value` ON `properties` (`serverId`, `id`, `key`);", "CREATE INDEX `idx_properties_serverid_id_type` ON `properties` (`serverId`, `id`, `type`);", - "CREATE TABLE `groups_v2` (`serverId` INT NOT NULL, `groupId` INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT, `target` INT, `type` INT, `displayName` VARCHAR(128), `original_group_id` INTEGER);", + "CREATE TABLE `groups_v2` (`serverId` INT NOT NULL, `groupId` INTEGER NOT NULL PRIMARY KEY [AUTO_INCREMENT], `target` INT, `type` INT, `displayName` VARCHAR(128), `original_group_id` INTEGER);", "INSERT INTO `groups_v2` (`serverId`, `groupId`, `target`, `type`, `displayName`) SELECT `serverId`, `groupId`, `target`, `type`, `displayName` FROM `groups`;", "DROP TABLE `groups`;", "ALTER TABLE `groups_v2` RENAME TO groups;",