Some changes
This commit is contained in:
parent
7fdd272d76
commit
7dcf4a54ef
@ -1 +1 @@
|
||||
Subproject commit 32b1ef973d44c668fc2e74735491db3896a32c2d
|
||||
Subproject commit fe633dc99a6ea91c2c0a8cb13a51a7587a54cb2e
|
@ -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 "")
|
||||
|
@ -994,6 +994,10 @@ std::shared_ptr<Properties> 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++];
|
||||
|
@ -36,6 +36,14 @@ ConnectedClient::~ConnectedClient() {
|
||||
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) {
|
||||
auto& info = this->connection_info;
|
||||
|
||||
|
@ -375,6 +375,8 @@ namespace ts {
|
||||
std::weak_ptr<MusicClient> subscribed_bot;
|
||||
std::weak_ptr<ts::music::Playlist> _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! */
|
||||
|
@ -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<shared_ptr<SpeakingClient>> available_clients;
|
||||
if(type == WhisperType::ECHO_TEXT) {
|
||||
if(type == WhisperType::ECHO) {
|
||||
available_clients.push_back(dynamic_pointer_cast<SpeakingClient>(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<SpeakingClient>(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<size_t>()) {
|
||||
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<size_t>()) {
|
||||
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
|
||||
|
@ -3,98 +3,96 @@
|
||||
#include <json/json.h>
|
||||
#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<VirtualServer>& 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<VirtualServer>& 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<ConnectedClient> &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<ConnectedClient> &sender);
|
||||
|
||||
//Whisper
|
||||
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;
|
||||
//Whisper
|
||||
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;
|
||||
|
||||
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<ecc_key> identityKey;
|
||||
//TeaSpeak
|
||||
std::shared_ptr<Json::Value> identityData;
|
||||
} handshake;
|
||||
};
|
||||
}
|
||||
IdentityType identityType{IdentityType::UNSET};
|
||||
std::string proof_message;
|
||||
//TeamSpeak
|
||||
std::shared_ptr<ecc_key> identityKey;
|
||||
//TeaSpeak
|
||||
std::shared_ptr<Json::Value> identityData;
|
||||
} handshake;
|
||||
};
|
||||
}
|
@ -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{};
|
||||
|
||||
|
@ -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<rtc::Channel> &undef
|
||||
}
|
||||
|
||||
void VoiceBridge::handle_data_channel(const std::shared_ptr<rtc::DataChannel> &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<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) {
|
||||
|
@ -12,12 +12,14 @@ namespace ts {
|
||||
class VoiceBridge {
|
||||
public:
|
||||
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 std::string& /* sdpMid */, int /* sdpMLineIndex */)> cb_ice_candidate_finish;
|
||||
typedef std::function<void()> cb_initialized;
|
||||
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>&);
|
||||
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<server::WebClient> _owner;
|
||||
std::chrono::system_clock::time_point offer_timestamp;
|
||||
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;
|
||||
struct {
|
||||
uint16_t packet_id = 0;
|
||||
|
@ -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<WebClient>(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<Json::CharReader> 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<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) {
|
||||
/* 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<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) {
|
||||
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) {
|
||||
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());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -14,105 +14,103 @@
|
||||
#include <json/json.h>
|
||||
#include <EventLoop.h>
|
||||
|
||||
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<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:
|
||||
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<WebClient> &cl){ _this = cl; }
|
||||
private:
|
||||
WebControlServer* handle;
|
||||
std::chrono::time_point<std::chrono::system_clock> connectedTimestamp;
|
||||
protected:
|
||||
void tick(const std::chrono::system_clock::time_point&) override; /* Every 500ms */
|
||||
|
||||
std::shared_mutex voice_bridge_lock;
|
||||
std::unique_ptr<web::VoiceBridge> voice_bridge;
|
||||
int file_descriptor;
|
||||
void applySelfLock(const std::shared_ptr<WebClient> &cl){ _this = cl; }
|
||||
private:
|
||||
WebControlServer* handle;
|
||||
std::chrono::time_point<std::chrono::system_clock> connectedTimestamp;
|
||||
|
||||
std::mutex event_lock;
|
||||
::event* readEvent;
|
||||
::event* writeEvent;
|
||||
std::shared_mutex voice_bridge_lock;
|
||||
std::unique_ptr<web::VoiceBridge> 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<pipes::buffer> queue_read;
|
||||
std::deque<pipes::buffer> 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<pipes::buffer> queue_read;
|
||||
std::deque<pipes::buffer> 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::ProxiedEventEntry<WebClient>> 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::ProxiedEventEntry<WebClient>> 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
|
@ -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 <typename T, size_t N>
|
||||
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 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;",
|
||||
|
Loading…
Reference in New Issue
Block a user