Some changes

This commit is contained in:
WolverinDEV 2020-08-13 12:58:19 +02:00
parent 7fdd272d76
commit 7dcf4a54ef
13 changed files with 310 additions and 225 deletions

@ -1 +1 @@
Subproject commit 32b1ef973d44c668fc2e74735491db3896a32c2d Subproject commit fe633dc99a6ea91c2c0a8cb13a51a7587a54cb2e

View File

@ -257,7 +257,7 @@ target_link_libraries(PermMapHelper
SET(CPACK_PACKAGE_VERSION_MAJOR "1") SET(CPACK_PACKAGE_VERSION_MAJOR "1")
SET(CPACK_PACKAGE_VERSION_MINOR "4") SET(CPACK_PACKAGE_VERSION_MINOR "4")
SET(CPACK_PACKAGE_VERSION_PATCH "18") SET(CPACK_PACKAGE_VERSION_PATCH "19")
if (BUILD_TYPE_NAME EQUAL OFF) if (BUILD_TYPE_NAME EQUAL OFF)
SET(CPACK_PACKAGE_VERSION_DATA "beta") SET(CPACK_PACKAGE_VERSION_DATA "beta")
elseif (BUILD_TYPE_NAME STREQUAL "") elseif (BUILD_TYPE_NAME STREQUAL "")

View File

@ -994,6 +994,10 @@ std::shared_ptr<Properties> DatabaseHelper::loadClientProperties(const std::shar
column = "client_nickname"; column = "client_nickname";
break; break;
case property::CONNECTION_CLIENT_IP:
column = "client_ip";
break;
case property::CLIENT_LASTCONNECTED: case property::CLIENT_LASTCONNECTED:
column = "client_last_connected"; column = "client_last_connected";
break; break;
@ -1326,10 +1330,10 @@ void DatabaseHelper::listDatabaseClients(
client.client_ip = values[index++]; client.client_ip = values[index++];
assert(names[index] == "client_created"); assert(names[index] == "client_created");
client.client_last_connected = values[index++]; client.client_created = values[index++];
assert(names[index] == "client_last_connected"); assert(names[index] == "client_last_connected");
client.client_created = values[index++]; client.client_last_connected = values[index++];
assert(names[index] == "client_total_connections"); assert(names[index] == "client_total_connections");
client.client_total_connections = values[index++]; client.client_total_connections = values[index++];

View File

@ -36,6 +36,14 @@ ConnectedClient::~ConnectedClient() {
memtrack::freed<ConnectedClient>(this); memtrack::freed<ConnectedClient>(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<ConnectionInfoData> ConnectedClient::request_connection_info(const std::shared_ptr<ConnectedClient> &receiver, bool& send_temp) { std::shared_ptr<ConnectionInfoData> ConnectedClient::request_connection_info(const std::shared_ptr<ConnectedClient> &receiver, bool& send_temp) {
auto& info = this->connection_info; auto& info = this->connection_info;

View File

@ -375,6 +375,8 @@ namespace ts {
std::weak_ptr<MusicClient> subscribed_bot; std::weak_ptr<MusicClient> subscribed_bot;
std::weak_ptr<ts::music::Playlist> _subscribed_playlist{}; std::weak_ptr<ts::music::Playlist> _subscribed_playlist{};
bool loadDataForCurrentServer() override;
virtual void tick(const std::chrono::system_clock::time_point &time); virtual void tick(const std::chrono::system_clock::time_point &time);
//Locked by everything who has something todo with command handling //Locked by everything who has something todo with command handling
threads::Mutex command_lock; /* Note: This mutex must be recursive! */ threads::Mutex command_lock; /* Note: This mutex must be recursive! */

View File

@ -124,7 +124,7 @@ enum WhisperType {
CHANNEL_COMMANDER = 2, CHANNEL_COMMANDER = 2,
ALL = 3, ALL = 3,
ECHO_TEXT = 0x10, ECHO = 0x10,
}; };
enum WhisperTarget { enum WhisperTarget {
@ -184,7 +184,7 @@ void SpeakingClient::handlePacketVoiceWhisper(const pipes::buffer_view& data, bo
#endif #endif
deque<shared_ptr<SpeakingClient>> available_clients; deque<shared_ptr<SpeakingClient>> available_clients;
if(type == WhisperType::ECHO_TEXT) { if(type == WhisperType::ECHO) {
available_clients.push_back(dynamic_pointer_cast<SpeakingClient>(this->ref())); available_clients.push_back(dynamic_pointer_cast<SpeakingClient>(this->ref()));
} else { } else {
for(const auto& client : this->server->getClients()) { 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<SpeakingClient>(cl); auto speakingClient = dynamic_pointer_cast<SpeakingClient>(cl);
return !speakingClient->shouldReceiveVoiceWhisper(self_lock); return !speakingClient->shouldReceiveVoiceWhisper(self_lock);
}), available_clients.end()); }), available_clients.end());
}
if(available_clients.empty()) { if(available_clients.empty()) {
if(update_whisper_error(this->speak_last_no_whisper_target)) { if(update_whisper_error(this->speak_last_no_whisper_target)) {
command_result result{error::whisper_no_targets}; command_result result{error::whisper_no_targets};
this->notifyError(result); this->notifyError(result);
}
return;
} }
return;
} if(available_clients.size() > this->server->properties()[property::VIRTUALSERVER_MIN_CLIENTS_IN_CHANNEL_BEFORE_FORCED_SILENCE].as_save<size_t>()) {
if(available_clients.size() > this->server->properties()[property::VIRTUALSERVER_MIN_CLIENTS_IN_CHANNEL_BEFORE_FORCED_SILENCE].as_save<size_t>()) { if(update_whisper_error(this->speak_last_too_many_whisper_targets)) {
if(update_whisper_error(this->speak_last_too_many_whisper_targets)) { command_result result{error::whisper_too_many_targets};
command_result result{error::whisper_too_many_targets}; this->notifyError(result);
this->notifyError(result); }
return;
} }
return;
} }
//Create the packet data //Create the packet data

View File

@ -3,98 +3,96 @@
#include <json/json.h> #include <json/json.h>
#include "ConnectedClient.h" #include "ConnectedClient.h"
namespace ts { namespace ts::server {
namespace server { class VirtualServer;
class VirtualServer; class SpeakingClient : public ConnectedClient {
class SpeakingClient : public ConnectedClient { public:
public: struct VoicePacketFlags {
struct VoicePacketFlags { bool encrypted : 1;
bool encrypted : 1; bool head : 1;
bool head : 1; bool fragmented : 1; /* used by MONO. IDK What this is */
bool fragmented : 1; /* used by MONO. IDK What this is */ bool new_protocol : 1;
bool new_protocol : 1; char _unused : 4;
char _unused : 4;
VoicePacketFlags() : encrypted{false}, head{false}, fragmented{false}, new_protocol{false}, _unused{0} { } VoicePacketFlags() : encrypted{false}, head{false}, fragmented{false}, new_protocol{false}, _unused{0} { }
}; };
static_assert(sizeof(VoicePacketFlags) == 1); static_assert(sizeof(VoicePacketFlags) == 1);
enum HandshakeState { enum HandshakeState {
BEGIN, BEGIN,
IDENTITY_PROOF, IDENTITY_PROOF,
SUCCEEDED SUCCEEDED
}; };
enum IdentityType : uint8_t { enum IdentityType : uint8_t {
TEASPEAK_FORUM, TEASPEAK_FORUM,
TEAMSPEAK, TEAMSPEAK,
NICKNAME, NICKNAME,
UNSET = 0xff UNSET = 0xff
}; };
SpeakingClient(sql::SqlManager* a, const std::shared_ptr<VirtualServer>& b) : ConnectedClient(a, b) { SpeakingClient(sql::SqlManager* a, const std::shared_ptr<VirtualServer>& b) : ConnectedClient(a, b) {
speak_begin = std::chrono::system_clock::now(); speak_begin = std::chrono::system_clock::now();
speak_last_packet = std::chrono::system_clock::now(); speak_last_packet = std::chrono::system_clock::now();
}; };
~SpeakingClient() override = default; ~SpeakingClient() override = default;
//Voice //Voice
virtual void send_voice_packet(const pipes::buffer_view& /* voice packet data */, const VoicePacketFlags& /* flags */) = 0; virtual void send_voice_packet(const pipes::buffer_view& /* voice packet data */, const VoicePacketFlags& /* flags */) = 0;
virtual bool shouldReceiveVoice(const std::shared_ptr<ConnectedClient> &sender); virtual bool shouldReceiveVoice(const std::shared_ptr<ConnectedClient> &sender);
//Whisper //Whisper
bool shouldReceiveVoiceWhisper(const std::shared_ptr<ConnectedClient> &sender); bool shouldReceiveVoiceWhisper(const std::shared_ptr<ConnectedClient> &sender);
virtual void send_voice_whisper_packet(const pipes::buffer_view& /* voice packet data */, const VoicePacketFlags& /* flags */) = 0; virtual void send_voice_whisper_packet(const pipes::buffer_view& /* voice packet data */, const VoicePacketFlags& /* flags */) = 0;
inline std::chrono::milliseconds takeSpokenTime() { inline std::chrono::milliseconds takeSpokenTime() {
auto time = this->speak_time; auto time = this->speak_time;
this->speak_time = std::chrono::milliseconds(0); this->speak_time = std::chrono::milliseconds(0);
return time; return time;
} }
protected: protected:
void tick(const std::chrono::system_clock::time_point &time) override; void tick(const std::chrono::system_clock::time_point &time) override;
protected: protected:
public: public:
void updateChannelClientProperties(bool channel_lock, bool notify) override; void updateChannelClientProperties(bool channel_lock, bool notify) override;
protected: protected:
command_result handleCommand(Command &command) override; command_result handleCommand(Command &command) override;
public: public:
void handlePacketVoice(const pipes::buffer_view&, bool head, bool fragmented); void handlePacketVoice(const pipes::buffer_view&, bool head, bool fragmented);
virtual void handlePacketVoiceWhisper(const pipes::buffer_view&, bool /* new */); virtual void handlePacketVoiceWhisper(const pipes::buffer_view&, bool /* new */);
void processJoin(); void processJoin();
void processLeave(); void processLeave();
virtual command_result handleCommandHandshakeBegin(Command&); virtual command_result handleCommandHandshakeBegin(Command&);
virtual command_result handleCommandHandshakeIdentityProof(Command &); virtual command_result handleCommandHandshakeIdentityProof(Command &);
virtual command_result handleCommandClientInit(Command&); virtual command_result handleCommandClientInit(Command&);
void triggerVoiceEnd(); void triggerVoiceEnd();
inline void updateSpeak(bool onlyUpdate, const std::chrono::system_clock::time_point &time); inline void updateSpeak(bool onlyUpdate, const std::chrono::system_clock::time_point &time);
std::chrono::milliseconds speak_accuracy = std::chrono::seconds{1}; std::chrono::milliseconds speak_accuracy = std::chrono::seconds{1};
threads::Mutex speak_lock; threads::Mutex speak_lock;
std::chrono::milliseconds speak_time = std::chrono::milliseconds{0}; std::chrono::milliseconds speak_time = std::chrono::milliseconds{0};
std::chrono::system_clock::time_point speak_begin; 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_packet;
std::chrono::system_clock::time_point speak_last_no_whisper_target; 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_too_many_whisper_targets;
permission::v2::PermissionFlaggedValue max_idle_time{permission::v2::empty_permission_flagged_value}; permission::v2::PermissionFlaggedValue max_idle_time{permission::v2::empty_permission_flagged_value};
struct { struct {
HandshakeState state{HandshakeState::BEGIN}; HandshakeState state{HandshakeState::BEGIN};
IdentityType identityType{IdentityType::UNSET}; IdentityType identityType{IdentityType::UNSET};
std::string proof_message; std::string proof_message;
//TeamSpeak //TeamSpeak
std::shared_ptr<ecc_key> identityKey; std::shared_ptr<ecc_key> identityKey;
//TeaSpeak //TeaSpeak
std::shared_ptr<Json::Value> identityData; std::shared_ptr<Json::Value> identityData;
} handshake; } handshake;
}; };
}
} }

View File

@ -475,6 +475,22 @@ bool QueryClient::handleMessage(const pipes::buffer_view& message) {
return true; 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) { if((uint8_t) command[0] == 255) {
string commands{}; string commands{};

View File

@ -145,7 +145,7 @@ std::string VoiceBridge::generate_answer() {
} }
void VoiceBridge::execute_tick() { 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()) { 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->offer_timestamp = chrono::system_clock::time_point();
this->connection->callback_setup_fail(rtc::PeerConnection::ConnectionComponent::BASE, "setup timeout"); this->connection->callback_setup_fail(rtc::PeerConnection::ConnectionComponent::BASE, "setup timeout");
@ -182,26 +182,47 @@ void VoiceBridge::handle_media_stream(const std::shared_ptr<rtc::Channel> &undef
} }
void VoiceBridge::handle_data_channel(const std::shared_ptr<rtc::DataChannel> &channel) { void VoiceBridge::handle_data_channel(const std::shared_ptr<rtc::DataChannel> &channel) {
if(channel->lable() == "main") { if(channel->lable() == "main" || channel->lable() == "voice") {
this->_voice_channel = channel; this->voice_channel_ = channel;
debugMessage(this->server_id(), "{} Got voice channel!", CLIENT_STR_LOG_PREFIX_(this->owner())); debugMessage(this->server_id(), "{} Got voice channel!", CLIENT_STR_LOG_PREFIX_(this->owner()));
this->callback_initialized(); this->callback_initialized();
weak_ptr<rtc::DataChannel> 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<rtc::DataChannel> 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<rtc::DataChannel> 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<rtc::MediaChannel> &channel, const pipes::buffer_view &data, size_t payload_offset) { void VoiceBridge::handle_audio_data(const std::shared_ptr<rtc::MediaChannel> &channel, const pipes::buffer_view &data, size_t payload_offset) {

View File

@ -12,12 +12,14 @@ namespace ts {
class VoiceBridge { class VoiceBridge {
public: public:
typedef std::function<void(const pipes::buffer_view&, bool, bool)> cb_voice_data; typedef std::function<void(const pipes::buffer_view&, bool, bool)> cb_voice_data;
typedef std::function<void(const pipes::buffer_view&, bool)> cb_voice_whisper_data;
typedef std::function<void(const rtc::IceCandidate&)> cb_ice_candidate; typedef std::function<void(const rtc::IceCandidate&)> cb_ice_candidate;
typedef std::function<void(const std::string& /* sdpMid */, int /* sdpMLineIndex */)> cb_ice_candidate_finish; typedef std::function<void(const std::string& /* sdpMid */, int /* sdpMLineIndex */)> cb_ice_candidate_finish;
typedef std::function<void()> cb_initialized; typedef std::function<void()> cb_initialized;
typedef std::function<void()> cb_failed; typedef std::function<void()> cb_failed;
std::shared_ptr<rtc::DataChannel> voice_channel() { return this->_voice_channel; } std::shared_ptr<rtc::DataChannel> voice_channel() { return this->voice_channel_; }
std::shared_ptr<rtc::DataChannel> voice_whisper_channel() { return this->voice_whisper_channel_; }
explicit VoiceBridge(const std::shared_ptr<server::WebClient>&); explicit VoiceBridge(const std::shared_ptr<server::WebClient>&);
virtual ~VoiceBridge(); virtual ~VoiceBridge();
@ -31,6 +33,7 @@ namespace ts {
cb_ice_candidate callback_ice_candidate; cb_ice_candidate callback_ice_candidate;
cb_ice_candidate_finish callback_ice_candidate_finished; cb_ice_candidate_finish callback_ice_candidate_finished;
cb_voice_data callback_voice_data; cb_voice_data callback_voice_data;
cb_voice_whisper_data callback_voice_whisper_data;
cb_initialized callback_initialized; cb_initialized callback_initialized;
cb_failed callback_failed; cb_failed callback_failed;
@ -48,7 +51,10 @@ namespace ts {
std::weak_ptr<server::WebClient> _owner; std::weak_ptr<server::WebClient> _owner;
std::chrono::system_clock::time_point offer_timestamp; std::chrono::system_clock::time_point offer_timestamp;
std::unique_ptr<rtc::PeerConnection> connection; std::unique_ptr<rtc::PeerConnection> connection;
std::shared_ptr<rtc::DataChannel> _voice_channel;
std::shared_ptr<rtc::DataChannel> voice_channel_;
std::shared_ptr<rtc::DataChannel> voice_whisper_channel_;
std::weak_ptr<rtc::AudioChannel> _audio_channel; std::weak_ptr<rtc::AudioChannel> _audio_channel;
struct { struct {
uint16_t packet_id = 0; uint16_t packet_id = 0;

View File

@ -156,22 +156,29 @@ void WebClient::sendJson(const Json::Value& json) {
} }
void WebClient::sendCommand(const ts::Command &command, bool low) { void WebClient::sendCommand(const ts::Command &command, bool low) {
Json::Value value = command.buildJson(); if(this->allow_raw_commands) {
value["type"] = "command"; Json::Value value{};
this->sendJson(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) { void WebClient::sendCommand(const ts::command_builder &command, bool low) {
#if false if(this->allow_raw_commands) {
Json::Value value{}; Json::Value value{};
value["type"] = "command2"; value["type"] = "command-raw";
value["payload"] = command.build(); value["payload"] = command.build();
this->sendJson(value); this->sendJson(value);
#else } else {
auto data = command.build(); auto data = command.build();
Command parsed_command = Command::parse(pipes::buffer_view{data.data(), data.length()}, true, false); Command parsed_command = Command::parse(pipes::buffer_view{data.data(), data.length()}, true, false);
this->sendCommand(parsed_command, low); this->sendCommand(parsed_command, low);
#endif }
} }
bool WebClient::close_connection(const std::chrono::system_clock::time_point& timeout) { 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<WebClient>(self_lock)); this->handle->unregisterConnection(static_pointer_cast<WebClient>(self_lock));
} }
Json::CharReaderBuilder json_reader_builder = []{ Json::CharReaderBuilder json_reader_builder = []() noexcept {
Json::CharReaderBuilder reader_builder; Json::CharReaderBuilder reader_builder;
return reader_builder; return reader_builder;
@ -432,7 +439,8 @@ void WebClient::handleMessage(const std::string &message) {
unique_ptr<Json::CharReader> reader{json_reader_builder.newCharReader()}; unique_ptr<Json::CharReader> reader{json_reader_builder.newCharReader()};
string error_message; string error_message;
if(!reader->parse(message.data(),message.data() + message.length(), &val, &error_message)) throw Json::Exception("Could not parse payload! (" + error_message + ")"); 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) { } catch (const std::exception& ex) {
logError(this->server->getServerId(), "Could not parse web message! Message: " + string(ex.what())); logError(this->server->getServerId(), "Could not parse web message! Message: " + string(ex.what()));
logTrace(this->server->getServerId(), "Payload: " + message); logTrace(this->server->getServerId(), "Payload: " + message);
@ -458,7 +466,7 @@ void WebClient::handleMessage(const std::string &message) {
} else if(val["type"].asString() == "WebRTC") { } else if(val["type"].asString() == "WebRTC") {
auto subType = val["request"].asString(); auto subType = val["request"].asString();
if(subType == "create") { 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) { if(this->voice_bridge) {
logError(this->server->getServerId(), "[{}] Tried to register a WebRTC channel twice!", CLIENT_STR_LOG_PREFIX_(this)); 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; lock = nullptr;
}).detach(); }).detach();
} }
//TODO test if bridge already exists!
this->voice_bridge = make_unique<web::VoiceBridge>(dynamic_pointer_cast<WebClient>(_this.lock())); //FIXME Add config this->voice_bridge = make_unique<web::VoiceBridge>(dynamic_pointer_cast<WebClient>(this->ref())); //FIXME Add config
this->voice_bridge->callback_voice_data = [&](const pipes::buffer_view& buffer, bool a, bool b) { this->voice_bridge->callback_voice_data = [&](const pipes::buffer_view& buffer, bool a, bool b) {
/* may somehow get the "real" packet size? */ /* may somehow get the "real" packet size? */
this->connectionStatistics->logIncomingPacket(stats::ConnectionStatistics::category::VOICE, buffer.length()); this->connectionStatistics->logIncomingPacket(stats::ConnectionStatistics::category::VOICE, buffer.length());
this->handlePacketVoice(buffer, a, b); 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 = [&](){ this->voice_bridge->callback_initialized = [&](){
debugMessage(this->getServerId(), "{} Voice bridge initialized!", CLIENT_STR_LOG_PREFIX); 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; auto vbp = &*this->voice_bridge;
voice_bridge_lock.unlock(); voice_bridge_lock_.unlock();
shared_lock read_voice_bridge_lock(this->voice_bridge_lock); shared_lock read_voice_bridge_lock(this->voice_bridge_lock);
if(vbp != &*this->voice_bridge) { 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.last_response = system_clock::now();
this->js_ping.value = duration_cast<nanoseconds>(this->js_ping.last_response - this->js_ping.last_request); this->js_ping.value = duration_cast<nanoseconds>(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) { } catch (const std::exception& ex) {
logError(this->server->getServerId(), "Could not handle json packet! Message {}", ex.what()); 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<ConnectedClient> &sender) { bool WebClient::shouldReceiveVoice(const std::shared_ptr<ConnectedClient> &sender) {
shared_lock read_voice_bridge_lock(this->voice_bridge_lock); shared_lock read_voice_bridge_lock(this->voice_bridge_lock);
if(!this->voice_bridge || !this->voice_bridge->voice_channel()) return false; if(!this->voice_bridge || !this->voice_bridge->voice_channel()) return false;
return SpeakingClient::shouldReceiveVoice(sender); 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) { 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) { if(this->voice_bridge) {
auto channel = this->voice_bridge->voice_channel(); auto channel = this->voice_bridge->voice_channel();
if(channel) { if(channel) {
channel->send(view); channel->send(view);
read_voice_bridge_lock.unlock();
/* may somehow get the "real" packet size? */ /* may somehow get the "real" packet size? */
this->connectionStatistics->logOutgoingPacket(stats::ConnectionStatistics::category::VOICE, view.length()); 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) { void WebClient::send_voice_whisper_packet(const pipes::buffer_view &view, const SpeakingClient::VoicePacketFlags &flags) {
logError(this->server->getServerId(), "Web client got whisper packet"); std::shared_lock read_voice_bridge_lock(this->voice_bridge_lock);
//As well log the data! 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());
}
}
} }

View File

@ -14,105 +14,103 @@
#include <json/json.h> #include <json/json.h>
#include <EventLoop.h> #include <EventLoop.h>
namespace ts { namespace ts::server {
namespace server { class WebControlServer;
class WebControlServer;
class WebClient : public SpeakingClient { class WebClient : public SpeakingClient {
friend class WebControlServer; friend class WebControlServer;
public: public:
WebClient(WebControlServer*, int socketFd); WebClient(WebControlServer*, int socketFd);
~WebClient() override; ~WebClient() override;
void sendJson(const Json::Value&); void sendJson(const Json::Value&);
void sendCommand(const ts::Command &command, bool low = false) override; void sendCommand(const ts::Command &command, bool low) override;
void sendCommand(const ts::command_builder &command, bool low) override; void sendCommand(const ts::command_builder &command, bool low) override;
bool disconnect(const std::string &reason) 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 close_connection(const std::chrono::system_clock::time_point& timeout = std::chrono::system_clock::time_point()) override;
bool shouldReceiveVoice(const std::shared_ptr<ConnectedClient> &sender) override; bool shouldReceiveVoice(const std::shared_ptr<ConnectedClient> &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: inline std::chrono::nanoseconds client_ping() { return this->client_ping_layer_7(); }
void tick(const std::chrono::system_clock::time_point&) override; /* Every 500ms */ 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<WebClient> &cl){ _this = cl; } protected:
private: void tick(const std::chrono::system_clock::time_point&) override; /* Every 500ms */
WebControlServer* handle;
std::chrono::time_point<std::chrono::system_clock> connectedTimestamp;
std::shared_mutex voice_bridge_lock; void applySelfLock(const std::shared_ptr<WebClient> &cl){ _this = cl; }
std::unique_ptr<web::VoiceBridge> voice_bridge; private:
int file_descriptor; WebControlServer* handle;
std::chrono::time_point<std::chrono::system_clock> connectedTimestamp;
std::mutex event_lock; std::shared_mutex voice_bridge_lock;
::event* readEvent; std::unique_ptr<web::VoiceBridge> voice_bridge;
::event* writeEvent; int file_descriptor;
struct { std::mutex event_lock;
uint8_t current_id = 0; ::event* readEvent;
std::chrono::system_clock::time_point last_request; ::event* writeEvent;
std::chrono::system_clock::time_point last_response;
std::chrono::nanoseconds value{}; struct {
std::chrono::nanoseconds timeout{2000}; uint8_t current_id = 0;
} ping; std::chrono::system_clock::time_point last_request;
std::chrono::system_clock::time_point last_response;
struct { std::chrono::nanoseconds value{};
uint8_t current_id = 0; std::chrono::nanoseconds timeout{2000};
std::chrono::system_clock::time_point last_request; } ping;
std::chrono::system_clock::time_point last_response;
std::chrono::nanoseconds value{}; struct {
std::chrono::nanoseconds timeout{2000}; uint8_t current_id = 0;
} js_ping; std::chrono::system_clock::time_point last_request;
std::chrono::system_clock::time_point last_response;
std::mutex queue_lock; std::chrono::nanoseconds value{};
std::deque<pipes::buffer> queue_read; std::chrono::nanoseconds timeout{2000};
std::deque<pipes::buffer> queue_write; } js_ping;
threads::Mutex execute_lock; /* needs to be recursive! */
std::thread flush_thread; std::mutex queue_lock;
std::recursive_mutex close_lock; std::deque<pipes::buffer> queue_read;
private: std::deque<pipes::buffer> queue_write;
void initialize(); threads::Mutex execute_lock; /* needs to be recursive! */
bool ssl_detected = false; std::thread flush_thread;
bool ssl_encrypted = true; std::recursive_mutex close_lock;
pipes::SSL ssl_handler; private:
pipes::WebSocket ws_handler; void initialize();
void handleMessageRead(int, short, void*);
void handleMessageWrite(int, short, void*);
void enqueue_raw_packet(const pipes::buffer_view& /* buffer */);
void processNextMessage(const std::chrono::system_clock::time_point& /* scheduled */); bool allow_raw_commands{false};
void registerMessageProcess(); 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::ProxiedEventEntry<WebClient>> event_handle_packet; void processNextMessage(const std::chrono::system_clock::time_point& /* scheduled */);
void registerMessageProcess();
//WS events std::shared_ptr<event::ProxiedEventEntry<WebClient>> event_handle_packet;
void onWSConnected();
void onWSDisconnected(const std::string& reason);
void onWSMessage(const pipes::WSMessage&);
protected:
void disconnectFinal();
void handleMessage(const std::string &);
public: //WS events
void send_voice_packet(const pipes::buffer_view &view, const VoicePacketFlags &flags) override; void onWSConnected();
void send_voice_whisper_packet(const pipes::buffer_view &view, const VoicePacketFlags &flags) override; 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; protected:
command_result handleCommandClientInit(Command &command) override;
}; command_result handleCommand(Command &command) override;
} command_result handleCommandClientInit(Command &command) override;
};
} }
#endif #endif

View File

@ -79,9 +79,19 @@ do { \
} \ } \
} while(0) } 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 <typename T, size_t N> template <typename T, size_t N>
inline bool execute_commands(sql::SqlManager* sql, std::string& error, const std::array<T, N>& commands) { inline bool execute_commands(sql::SqlManager* sql, std::string& error, const std::array<T, N>& commands) {
std::string insert_or_ignore{sql->getType() == sql::TYPE_SQLITE ? "INSERT OR IGNORE" : "INSERT IGNORE"}; 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) { for(const auto& cmd : commands) {
std::string command{cmd}; std::string command{cmd};
@ -89,8 +99,8 @@ inline bool execute_commands(sql::SqlManager* sql, std::string& error, const std
return !std::isspace(ch); return !std::isspace(ch);
})); }));
if(command.starts_with("[INSERT_OR_IGNORE]")) command = replace_all(command, "[INSERT_OR_IGNORE]", insert_or_ignore);
command = insert_or_ignore + command.substr(18); command = replace_all(command, "[AUTO_INCREMENT]", auto_increment);
auto result = sql::command(sql, command).execute(); auto result = sql::command(sql, command).execute();
if(!result) { if(!result) {
@ -451,7 +461,7 @@ bool SqlDataManager::update_database(std::string &error) {
case 12: { case 12: {
constexpr static std::string_view kCreateClientsV2{R"( constexpr static std::string_view kCreateClientsV2{R"(
CREATE TABLE `clients_v2` ( 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_unique_id` VARCHAR(40) UNIQUE,
`client_created` BIGINT, `client_created` BIGINT,
`client_login_name` VARCHAR(20) UNIQUE `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_value` ON `properties` (`serverId`, `id`, `key`);",
"CREATE INDEX `idx_properties_serverid_id_type` ON `properties` (`serverId`, `id`, `type`);", "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`;", "INSERT INTO `groups_v2` (`serverId`, `groupId`, `target`, `type`, `displayName`) SELECT `serverId`, `groupId`, `target`, `type`, `displayName` FROM `groups`;",
"DROP TABLE `groups`;", "DROP TABLE `groups`;",
"ALTER TABLE `groups_v2` RENAME TO groups;", "ALTER TABLE `groups_v2` RENAME TO groups;",