Reinitialized project to reduce the git branch size
This commit is contained in:
@@ -0,0 +1,525 @@
|
||||
#include <algorithm>
|
||||
#include <src/server/QueryServer.h>
|
||||
#include "QueryClient.h"
|
||||
#include <netinet/tcp.h>
|
||||
#include <src/Configuration.h>
|
||||
#include <log/LogUtils.h>
|
||||
#include <misc/memtracker.h>
|
||||
#include "src/InstanceHandler.h"
|
||||
#include <pipes/errors.h>
|
||||
#include <query/command2.h>
|
||||
#include <misc/std_unique_ptr.h>
|
||||
|
||||
using namespace std;
|
||||
using namespace std::chrono;
|
||||
using namespace ts;
|
||||
using namespace ts::server;
|
||||
|
||||
#if defined(TCP_CORK) && !defined(TCP_NOPUSH)
|
||||
#define TCP_NOPUSH TCP_CORK
|
||||
#endif
|
||||
|
||||
//#define DEBUG_TRAFFIC
|
||||
|
||||
QueryClient::QueryClient(QueryServer* handle, int sockfd) : ConnectedClient(handle->sql, nullptr), handle(handle), clientFd(sockfd) {
|
||||
memtrack::allocated<QueryClient>(this);
|
||||
int enabled = 1;
|
||||
int disabled = 0;
|
||||
setsockopt(sockfd, SOL_SOCKET, SO_KEEPALIVE, &enabled, sizeof(enabled));
|
||||
if(setsockopt(sockfd, IPPROTO_TCP, TCP_NOPUSH, &disabled, sizeof disabled) < 0) {
|
||||
logError(this->getServerId(), "[Query] Could not disable nopush for {} ({}/{})", CLIENT_STR_LOG_PREFIX_(this), errno, strerror(errno));
|
||||
}
|
||||
if(setsockopt(sockfd, IPPROTO_TCP, TCP_NODELAY, &enabled, sizeof enabled) < 0) {
|
||||
logError(this->getServerId(), "[Query] Could not disable no delay for {} ({}/{})", CLIENT_STR_LOG_PREFIX_(this), errno, strerror(errno));
|
||||
}
|
||||
|
||||
this->readEvent = event_new(this->handle->eventLoop, this->clientFd, EV_READ | EV_PERSIST, [](int a, short b, void* c){ ((QueryClient*) c)->handleMessageRead(a, b, c); }, this);
|
||||
this->writeEvent = event_new(this->handle->eventLoop, this->clientFd, EV_WRITE, [](int a, short b, void* c){ ((QueryClient*) c)->handleMessageWrite(a, b, c); }, this);
|
||||
|
||||
this->state = ConnectionState::CONNECTED;
|
||||
connectedTimestamp = system_clock::now();
|
||||
|
||||
this->resetEventMask();
|
||||
}
|
||||
|
||||
void QueryClient::applySelfLock(const std::shared_ptr<ts::server::QueryClient> &cl) {
|
||||
this->_this = cl;
|
||||
}
|
||||
|
||||
QueryClient::~QueryClient() {
|
||||
memtrack::freed<QueryClient>(this);
|
||||
// if(this->closeLock.tryLock() != 0)
|
||||
// logCritical("Query manager deleted, but is still in usage! (closeLock)");
|
||||
// if(this->bufferLock.tryLock() != 0)
|
||||
// logCritical("Query manager deleted, but is still in usage! (bufferLock)");
|
||||
this->ssl_handler.finalize();
|
||||
}
|
||||
|
||||
void QueryClient::preInitialize() {
|
||||
this->properties()[property::CLIENT_TYPE] = ClientType::CLIENT_QUERY;
|
||||
this->properties()[property::CLIENT_TYPE_EXACT] = ClientType::CLIENT_QUERY;
|
||||
this->properties()[property::CLIENT_UNIQUE_IDENTIFIER] = "UnknownQuery";
|
||||
this->properties()[property::CLIENT_NICKNAME] = string() + "ServerQuery#" + this->getLoggingPeerIp() + "/" + to_string(this->getPeerPort());
|
||||
|
||||
DatabaseHelper::assignDatabaseId(this->sql, this->getServerId(), _this.lock());
|
||||
|
||||
|
||||
if(ts::config::query::sslMode == 0) {
|
||||
this->connectionType = ConnectionType::PLAIN;
|
||||
this->postInitialize();
|
||||
}
|
||||
}
|
||||
|
||||
void QueryClient::postInitialize() {
|
||||
lock_guard<recursive_mutex> lock(this->lock_packet_handle); /* we dont want to handle anything while we're initializing */
|
||||
this->connectTimestamp = system_clock::now();
|
||||
this->properties()[property::CLIENT_LASTCONNECTED] = duration_cast<seconds>(this->connectTimestamp.time_since_epoch()).count();
|
||||
|
||||
if(ts::config::query::sslMode == 1 && this->connectionType != ConnectionType::SSL_ENCRIPTED) {
|
||||
this->notifyError({findError("failed_connection_initialisation"), "Please use a SSL encryption!"});
|
||||
this->disconnect("Please us a SSL encryption for more security.\nThe server denies also all other connections!");
|
||||
return;
|
||||
}
|
||||
|
||||
writeMessage(config::query::motd);
|
||||
assert(this->handle);
|
||||
if(this->handle->ip_blacklist) {
|
||||
assert(this->handle->ip_blacklist);
|
||||
if(this->handle->ip_blacklist->contains(this->remote_address)) {
|
||||
Command cmd("error");
|
||||
auto err = findError("client_login_not_permitted");
|
||||
cmd["id"] = err.errorId;
|
||||
cmd["msg"] = err.message;
|
||||
cmd["extra_msg"] = "You're not permitted to use the query interface! (Your blacklisted)";
|
||||
this->sendCommand(cmd);
|
||||
this->disconnect("blacklisted");
|
||||
return;;
|
||||
}
|
||||
if(this->handle->ip_whitelist)
|
||||
this->whitelisted = this->handle->ip_whitelist->contains(this->remote_address);
|
||||
else
|
||||
this->whitelisted = false;
|
||||
debugMessage(LOG_QUERY, "Got new query client from {}. Whitelisted: {}", this->getLoggingPeerIp(), this->whitelisted);
|
||||
}
|
||||
|
||||
if(!this->whitelisted) {
|
||||
threads::MutexLock lock(this->handle->loginLock);
|
||||
if(this->handle->queryBann.count(this->getPeerIp()) > 0) {
|
||||
auto ban = this->handle->queryBann[this->getPeerIp()];
|
||||
Command cmd("error");
|
||||
auto err = findError("server_connect_banned");
|
||||
cmd["id"] = err.errorId;
|
||||
cmd["msg"] = err.message;
|
||||
cmd["extra_msg"] = "you may retry in " + to_string(duration_cast<seconds>(ban - system_clock::now()).count()) + " seconds";
|
||||
this->sendCommand(cmd);
|
||||
this->disconnect("");
|
||||
}
|
||||
}
|
||||
|
||||
this->update_cached_permissions();
|
||||
}
|
||||
|
||||
void QueryClient::writeMessage(const std::string& message) {
|
||||
if(this->state == ConnectionState::DISCONNECTED || !this->handle) return;
|
||||
|
||||
if(this->connectionType == ConnectionType::PLAIN) this->writeRawMessage(message);
|
||||
else if(this->connectionType == ConnectionType::SSL_ENCRIPTED) this->ssl_handler.send(pipes::buffer_view{(void*) message.data(), message.length()});
|
||||
else logCritical("Invalid query connection type!");
|
||||
}
|
||||
|
||||
|
||||
bool QueryClient::disconnect(const std::string &reason) {
|
||||
if(!reason.empty()) {
|
||||
Command cmd("disconnect");
|
||||
cmd["reason"] = reason;
|
||||
this->sendCommand(cmd);
|
||||
}
|
||||
return this->closeConnection(system_clock::now() + seconds(1));
|
||||
}
|
||||
|
||||
bool QueryClient::closeConnection(const std::chrono::system_clock::time_point& flushTimeout) {
|
||||
auto ownLock = dynamic_pointer_cast<QueryClient>(_this.lock());
|
||||
if(!ownLock) return false;
|
||||
|
||||
unique_lock<std::recursive_mutex> handleLock(this->lock_packet_handle);
|
||||
unique_lock<threads::Mutex> lock(this->closeLock);
|
||||
|
||||
bool flushing = flushTimeout.time_since_epoch().count() != 0;
|
||||
if(this->state == ConnectionState::DISCONNECTED || (flushing && this->state == ConnectionState::DISCONNECTING)) return false;
|
||||
this->state = flushing ? ConnectionState::DISCONNECTING : ConnectionState::DISCONNECTED;
|
||||
|
||||
if(this->readEvent) { //Attention dont trigger this within the read thread!
|
||||
event_del_block(this->readEvent);
|
||||
event_free(this->readEvent);
|
||||
this->readEvent = nullptr;
|
||||
}
|
||||
|
||||
if(this->server){
|
||||
{
|
||||
unique_lock channel_lock(this->server->channel_tree_lock);
|
||||
this->server->unregisterClient(_this.lock(), "disconnected", channel_lock);
|
||||
}
|
||||
this->server->groups->disableCache(_this.lock());
|
||||
this->server = nullptr;
|
||||
}
|
||||
|
||||
if(flushing){
|
||||
this->flushThread = new threads::Thread(THREAD_SAVE_OPERATIONS | THREAD_EXECUTE_LATER, [ownLock, flushTimeout](){
|
||||
while(ownLock->state == ConnectionState::DISCONNECTING && flushTimeout > system_clock::now()){
|
||||
{
|
||||
lock_guard<threads::Mutex> l(ownLock->bufferLock);
|
||||
if(ownLock->readQueue.empty() && ownLock->writeQueue.empty()) break;
|
||||
}
|
||||
usleep(10 * 1000);
|
||||
}
|
||||
if(ownLock->state == ConnectionState::DISCONNECTING) ownLock->disconnectFinal();
|
||||
});
|
||||
flushThread->name("Flush thread QC").execute();
|
||||
} else {
|
||||
threads::MutexLock l1(this->flushThreadLock);
|
||||
handleLock.unlock();
|
||||
lock.unlock();
|
||||
if(this->flushThread){
|
||||
threads::NegatedMutexLock l(this->closeLock);
|
||||
this->flushThread->join();
|
||||
}
|
||||
disconnectFinal();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void QueryClient::disconnectFinal() {
|
||||
lock_guard<recursive_mutex> lock_tick(this->lock_query_tick);
|
||||
lock_guard<recursive_mutex> lock_handle(this->lock_packet_handle);
|
||||
threads::MutexLock lock_close(this->closeLock);
|
||||
threads::MutexLock lock_buffer(this->bufferLock);
|
||||
|
||||
if(final_disconnected) {
|
||||
logError(LOG_QUERY, "Tried to disconnect a client twice!");
|
||||
return;
|
||||
}
|
||||
final_disconnected = true;
|
||||
this->state = ConnectionState::DISCONNECTED;
|
||||
{
|
||||
threads::MutexTryLock l(this->flushThreadLock);
|
||||
if(!!l) {
|
||||
if(this->flushThread) {
|
||||
this->flushThread->detach();
|
||||
delete this->flushThread; //Release the captured this lock
|
||||
this->flushThread = nullptr;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(this->writeEvent) {
|
||||
event_del_block(this->writeEvent);
|
||||
event_free(this->writeEvent);
|
||||
this->writeEvent = nullptr;
|
||||
}
|
||||
if(this->readEvent) {
|
||||
event_del_block(this->readEvent);
|
||||
event_free(this->readEvent);
|
||||
this->readEvent = nullptr;
|
||||
}
|
||||
if(this->clientFd > 0) {
|
||||
if(shutdown(this->clientFd, SHUT_RDWR) < 0)
|
||||
debugMessage(LOG_QUERY, "Could not shutdown query client socket! {} ({})", errno, strerror(errno));
|
||||
if(close(this->clientFd) < 0)
|
||||
debugMessage(LOG_QUERY, "Failed to close the query client socket! {} ({})", errno, strerror(errno));
|
||||
this->clientFd = -1;
|
||||
}
|
||||
|
||||
if(this->server) {
|
||||
{
|
||||
unique_lock channel_lock(this->server->channel_tree_lock);
|
||||
this->server->unregisterClient(_this.lock(), "disconnected", channel_lock);
|
||||
}
|
||||
this->server->groups->disableCache(_this.lock());
|
||||
this->server = nullptr;
|
||||
}
|
||||
|
||||
this->readQueue.clear();
|
||||
this->writeQueue.clear();
|
||||
|
||||
if(this->handle)
|
||||
this->handle->unregisterConnection(dynamic_pointer_cast<QueryClient>(_this.lock()));
|
||||
}
|
||||
|
||||
void QueryClient::writeRawMessage(const std::string &message) {
|
||||
{
|
||||
threads::MutexLock lock(this->bufferLock);
|
||||
this->writeQueue.push_back(message);
|
||||
}
|
||||
if(this->writeEvent) event_add(this->writeEvent, nullptr);
|
||||
}
|
||||
|
||||
void QueryClient::handleMessageWrite(int fd, short, void *) {
|
||||
auto ownLock = _this.lock();
|
||||
threads::MutexTryLock lock(this->bufferLock);
|
||||
if(this->state == ConnectionState::DISCONNECTED) return;
|
||||
if(!lock) {
|
||||
if(this->writeEvent) event_add(this->writeEvent, nullptr);
|
||||
return;
|
||||
}
|
||||
|
||||
int writes = 0;
|
||||
string buffer;
|
||||
while(writes < 10 && !this->writeQueue.empty()) {
|
||||
if(buffer.empty()) {
|
||||
buffer = std::move(this->writeQueue.front());
|
||||
this->writeQueue.pop_front();
|
||||
}
|
||||
auto length = send(fd, buffer.data(), buffer.length(), MSG_NOSIGNAL);
|
||||
#ifdef DEBUG_TRAFFIC
|
||||
debugMessage("Write " + to_string(buffer.length()));
|
||||
hexDump((void *) buffer.data(), buffer.length());
|
||||
#endif
|
||||
if(length == -1) {
|
||||
if (errno == EINTR || errno == EAGAIN) {
|
||||
if(this->writeEvent)
|
||||
event_add(this->writeEvent, nullptr);
|
||||
return;
|
||||
}
|
||||
else {
|
||||
logError(LOG_QUERY, "{} Failed to write message: {} ({} => {})", CLIENT_STR_LOG_PREFIX, length, errno, strerror(errno));
|
||||
threads::Thread([=](){ ownLock->closeConnection(); }).detach();
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
if(buffer.length() == length)
|
||||
buffer = "";
|
||||
else
|
||||
buffer = buffer.substr(length);
|
||||
}
|
||||
writes++;
|
||||
}
|
||||
if(!buffer.empty())
|
||||
this->writeQueue.push_front(buffer);
|
||||
|
||||
if(!this->writeQueue.empty() && this->writeEvent)
|
||||
event_add(this->writeEvent, nullptr);
|
||||
}
|
||||
|
||||
void QueryClient::handleMessageRead(int fd, short, void *) {
|
||||
auto ownLock = dynamic_pointer_cast<QueryClient>(_this.lock());
|
||||
if(!ownLock) {
|
||||
logCritical(LOG_QUERY, "Could not get own lock!");
|
||||
return;
|
||||
|
||||
}
|
||||
|
||||
string buffer(1024, 0);
|
||||
|
||||
auto length = read(fd, (void*) buffer.data(), buffer.length());
|
||||
if(length <= 0){
|
||||
if(errno == EINTR || errno == EAGAIN)
|
||||
;//event_add(this->readEvent, nullptr);
|
||||
else {
|
||||
logError(LOG_QUERY, "{} Failed to read! Code: {} errno: {} message: {}", CLIENT_STR_LOG_PREFIX, length, errno, strerror(errno));
|
||||
event_del_noblock(this->readEvent);
|
||||
threads::Thread(THREAD_SAVE_OPERATIONS, [ownLock](){ ownLock->closeConnection(); }).detach();
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
buffer.resize(length);
|
||||
{
|
||||
threads::MutexLock l(this->bufferLock);
|
||||
if(this->state == ConnectionState::DISCONNECTED)
|
||||
return;
|
||||
this->readQueue.push_back(std::move(buffer));
|
||||
#ifdef DEBUG_TRAFFIC
|
||||
debugMessage("Read " + to_string(buffer.length()));
|
||||
hexDump((void *) buffer.data(), buffer.length());
|
||||
#endif
|
||||
}
|
||||
|
||||
if(this->handle)
|
||||
this->handle->executePool()->execute([ownLock]() {
|
||||
int counter = 0;
|
||||
while(ownLock->tickIOMessageProgress() && counter++ < 15);
|
||||
});
|
||||
}
|
||||
|
||||
bool QueryClient::tickIOMessageProgress() {
|
||||
lock_guard<recursive_mutex> lock(this->lock_packet_handle);
|
||||
if(!this->handle || this->state == ConnectionState::DISCONNECTED || this->state == ConnectionState::DISCONNECTING) return false;
|
||||
|
||||
string message;
|
||||
bool next = false;
|
||||
{
|
||||
threads::MutexLock l(this->bufferLock);
|
||||
if(this->readQueue.empty()) return false;
|
||||
message = std::move(this->readQueue.front());
|
||||
this->readQueue.pop_front();
|
||||
next |= this->readQueue.empty();
|
||||
}
|
||||
|
||||
|
||||
if(this->connectionType == ConnectionType::PLAIN) {
|
||||
int count = 0;
|
||||
while(this->handleMessage(pipes::buffer_view{(void*) message.data(), message.length()}) && count++ < 15) message = "";
|
||||
next |= count == 15;
|
||||
} else if(this->connectionType == ConnectionType::SSL_ENCRIPTED) {
|
||||
this->ssl_handler.process_incoming_data(pipes::buffer_view{(void*) message.data(), message.length()});
|
||||
} else if(this->connectionType == ConnectionType::UNKNOWN) {
|
||||
if(config::query::sslMode != 0 && pipes::SSL::isSSLHeader(message)) {
|
||||
this->initializeSSL();
|
||||
|
||||
/*
|
||||
* - Content
|
||||
* \x16
|
||||
* -Version (1)
|
||||
* \x03 \x00
|
||||
* - length (2)
|
||||
* \x00 \x04
|
||||
*
|
||||
* - Header
|
||||
* \x00 -> hello request (3)
|
||||
* \x05 -> length (4)
|
||||
*/
|
||||
|
||||
//this->writeRawMessage(string("\x16\x03\x01\x00\x05\x00\x00\x00\x00\x00", 10));
|
||||
} else {
|
||||
this->connectionType = ConnectionType::PLAIN;
|
||||
this->postInitialize();
|
||||
}
|
||||
next = true;
|
||||
{
|
||||
threads::MutexLock l(this->bufferLock);
|
||||
this->readQueue.push_front(std::move(message));
|
||||
}
|
||||
}
|
||||
return next;
|
||||
}
|
||||
|
||||
extern InstanceHandler* serverInstance;
|
||||
|
||||
void QueryClient::initializeSSL() {
|
||||
this->connectionType = ConnectionType::SSL_ENCRIPTED;
|
||||
|
||||
this->ssl_handler.direct_process(pipes::PROCESS_DIRECTION_OUT, true);
|
||||
this->ssl_handler.direct_process(pipes::PROCESS_DIRECTION_IN, true);
|
||||
|
||||
this->ssl_handler.callback_data(std::bind(&QueryClient::handleMessage, this, placeholders::_1));
|
||||
this->ssl_handler.callback_write(std::bind(&QueryClient::writeRawMessage, this, placeholders::_1));
|
||||
this->ssl_handler.callback_initialized = std::bind(&QueryClient::postInitialize, this);
|
||||
|
||||
this->ssl_handler.callback_error([&](int code, const std::string& message) {
|
||||
if(code == PERROR_SSL_ACCEPT) {
|
||||
this->disconnect("invalid accept");
|
||||
} else if(code == PERROR_SSL_TIMEOUT)
|
||||
this->disconnect("invalid accept (timeout)");
|
||||
else
|
||||
logError(LOG_QUERY, "Got unknown ssl error ({} | {})", code, message);
|
||||
});
|
||||
|
||||
{
|
||||
auto context = serverInstance->sslManager()->getQueryContext();
|
||||
|
||||
auto options = make_shared<pipes::SSL::Options>();
|
||||
options->type = pipes::SSL::SERVER;
|
||||
options->context_method = TLS_method();
|
||||
options->default_keypair({context->privateKey, context->certificate});
|
||||
if(!this->ssl_handler.initialize(options)) {
|
||||
logError(LOG_QUERY, "[{}] Failed to setup ssl!", CLIENT_STR_LOG_PREFIX);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool QueryClient::handleMessage(const pipes::buffer_view& message) {
|
||||
{
|
||||
threads::MutexLock l(this->closeLock);
|
||||
if(this->state == ConnectionState::DISCONNECTED)
|
||||
return false;
|
||||
}
|
||||
#ifdef DEBUG_TRAFFIC
|
||||
debugMessage("Handling message " + to_string(message.length()));
|
||||
hexDump((void *) message.data(), message.length());
|
||||
#endif
|
||||
|
||||
|
||||
string command;
|
||||
{
|
||||
this->lineBuffer += message.string();
|
||||
int length = 2;
|
||||
auto pos = this->lineBuffer.find("\r\n");
|
||||
if(pos == string::npos) pos = this->lineBuffer.find("\n\r");
|
||||
if(pos == string::npos) {
|
||||
length = 1;
|
||||
pos = this->lineBuffer.find('\n');
|
||||
}
|
||||
|
||||
if(pos != string::npos){
|
||||
command = this->lineBuffer.substr(0, pos);
|
||||
if(this->lineBuffer.size() > pos + length)
|
||||
this->lineBuffer = this->lineBuffer.substr(pos + length);
|
||||
else
|
||||
this->lineBuffer.clear();
|
||||
}
|
||||
if(pos == string::npos) return false;
|
||||
}
|
||||
|
||||
if(command.empty() || command.find_first_not_of(' ') == string::npos) { //Empty command
|
||||
logTrace(LOG_QUERY, "[{}:{}] Got query idle command (Empty command or spaces)", this->getLoggingPeerIp(), this->getPeerPort());
|
||||
CMD_RESET_IDLE; //if idle time over 5 min than connection drop
|
||||
return true;
|
||||
}
|
||||
logTrace(LOG_QUERY, "[{}:{}] Got query command {}", this->getLoggingPeerIp(), this->getPeerPort(), command);
|
||||
|
||||
unique_ptr<Command> cmd;
|
||||
try {
|
||||
cmd = make_unique<Command>(Command::parse(pipes::buffer_view{(void*) command.data(), command.length()}, true, !ts::config::server::strict_ut8_mode));
|
||||
} catch(std::invalid_argument& ex) {
|
||||
this->notifyError(CommandResult{findError("parameter_convert"), ex.what()});
|
||||
return false;
|
||||
} catch(command_malformed_exception& ex) {
|
||||
this->notifyError(CommandResult{findError("parameter_convert"), "invalid character @" + to_string(ex.index())});
|
||||
return false;
|
||||
} catch(std::exception& ex) {
|
||||
this->notifyError(CommandResult{ErrorType::VSError, ex.what()});
|
||||
return false;
|
||||
}
|
||||
|
||||
try {
|
||||
this->handleCommandFull(*cmd);
|
||||
} catch(std::exception& ex) {
|
||||
this->notifyError(CommandResult{ErrorType::VSError, ex.what()});
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void QueryClient::sendCommand(const ts::Command &command, bool) {
|
||||
auto cmd = command.build();
|
||||
writeMessage(cmd + config::query::newlineCharacter);
|
||||
logTrace(LOG_QUERY, "Send command {}", cmd);
|
||||
}
|
||||
|
||||
void QueryClient::tick(const std::chrono::system_clock::time_point &time) {
|
||||
ConnectedClient::tick(time);
|
||||
}
|
||||
|
||||
void QueryClient::queryTick() {
|
||||
lock_guard<recursive_mutex> lock_tick(this->lock_query_tick);
|
||||
if(this->idleTimestamp.time_since_epoch().count() > 0 && system_clock::now() - this->idleTimestamp > minutes(5)){
|
||||
debugMessage(LOG_QUERY, "Dropping client " + this->getLoggingPeerIp() + "|" + this->getDisplayName() + ". (Timeout)");
|
||||
this->closeConnection(system_clock::now() + seconds(1));
|
||||
}
|
||||
|
||||
if(this->connectionType == ConnectionType::UNKNOWN && system_clock::now() - milliseconds(500) > connectedTimestamp) {
|
||||
this->connectionType = ConnectionType::PLAIN;
|
||||
this->postInitialize();
|
||||
}
|
||||
}
|
||||
|
||||
bool QueryClient::notifyChannelSubscribed(const deque<shared_ptr<BasicChannel>> &) {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool QueryClient::notifyChannelUnsubscribed(const deque<shared_ptr<BasicChannel>> &){
|
||||
return false;
|
||||
}
|
||||
|
||||
bool QueryClient::ignoresFlood() {
|
||||
return this->whitelisted || ConnectedClient::ignoresFlood();
|
||||
}
|
||||
@@ -0,0 +1,185 @@
|
||||
#pragma once
|
||||
|
||||
#include <src/client/ConnectedClient.h>
|
||||
#include <poll.h>
|
||||
#include <protocol/buffers.h>
|
||||
#include <pipes/ssl.h>
|
||||
#include "misc/queue.h"
|
||||
|
||||
namespace ts {
|
||||
namespace server {
|
||||
class QueryServer;
|
||||
|
||||
class QueryClient : public ConnectedClient {
|
||||
friend class QueryServer;
|
||||
|
||||
enum ConnectionType {
|
||||
PLAIN,
|
||||
SSL_ENCRIPTED,
|
||||
UNKNOWN
|
||||
};
|
||||
public:
|
||||
QueryClient(QueryServer*, int sockfd);
|
||||
~QueryClient() override;
|
||||
|
||||
|
||||
void writeMessage(const std::string&);
|
||||
|
||||
void sendCommand(const ts::Command &command, bool low = false) override;
|
||||
|
||||
bool disconnect(const std::string &reason) override;
|
||||
bool closeConnection(const std::chrono::system_clock::time_point& timeout = std::chrono::system_clock::time_point()) override;
|
||||
void disconnectFinal();
|
||||
|
||||
bool eventActive(QueryEventGroup, QueryEventSpecifier);
|
||||
void toggleEvent(QueryEventGroup, QueryEventSpecifier, bool);
|
||||
void resetEventMask();
|
||||
|
||||
bool ignoresFlood() override;
|
||||
|
||||
inline std::shared_ptr<QueryAccount> getQueryAccount() { return this->query_account; }
|
||||
|
||||
std::shared_recursive_mutex server_lock;
|
||||
protected:
|
||||
void preInitialize();
|
||||
void postInitialize();
|
||||
void tick(const std::chrono::system_clock::time_point &time) override;
|
||||
void queryTick();
|
||||
|
||||
protected:
|
||||
void initializeSSL();
|
||||
|
||||
bool handleMessage(const pipes::buffer_view&);
|
||||
bool tickIOMessageProgress();
|
||||
|
||||
void handleMessageRead(int, short, void*);
|
||||
void handleMessageWrite(int, short, void*);
|
||||
void writeRawMessage(const std::string&);
|
||||
|
||||
void applySelfLock(const std::shared_ptr<QueryClient> &cl);
|
||||
private:
|
||||
QueryServer* handle;
|
||||
|
||||
ConnectionType connectionType = ConnectionType::UNKNOWN;
|
||||
|
||||
bool whitelisted = false;
|
||||
int clientFd = -1;
|
||||
|
||||
::event* readEvent = nullptr;
|
||||
::event* writeEvent = nullptr;
|
||||
threads::Mutex closeLock;
|
||||
|
||||
pipes::SSL ssl_handler;
|
||||
|
||||
threads::Mutex bufferLock;
|
||||
std::deque<std::string> writeQueue;
|
||||
std::deque<std::string> readQueue;
|
||||
|
||||
threads::Mutex flushThreadLock;
|
||||
threads::Thread* flushThread = nullptr;
|
||||
bool final_disconnected = false;
|
||||
|
||||
std::string lineBuffer;
|
||||
std::chrono::time_point<std::chrono::system_clock> connectedTimestamp;
|
||||
uint16_t eventMask[QueryEventGroup::QEVENTGROUP_MAX];
|
||||
|
||||
std::recursive_mutex lock_packet_handle;
|
||||
std::recursive_mutex lock_query_tick;
|
||||
|
||||
std::shared_ptr<QueryAccount> query_account;
|
||||
protected:
|
||||
CommandResult handleCommand(Command &command) override;
|
||||
|
||||
public:
|
||||
//Silent events
|
||||
bool notifyClientNeededPermissions() override;
|
||||
bool notifyChannelSubscribed(const std::deque<std::shared_ptr<BasicChannel>> &deque) override;
|
||||
bool notifyChannelUnsubscribed(const std::deque<std::shared_ptr<BasicChannel>> &deque) override;
|
||||
|
||||
bool notifyServerUpdated(std::shared_ptr<ConnectedClient> ptr) override;
|
||||
bool notifyClientPoke(std::shared_ptr<ConnectedClient> invoker, std::string msg) override;
|
||||
|
||||
bool notifyClientUpdated(const std::shared_ptr<ConnectedClient> &ptr, const std::deque<std::shared_ptr<property::PropertyDescription>> &deque, bool lock_channel_tree) override;
|
||||
|
||||
bool notifyPluginCmd(std::string name, std::string msg,std::shared_ptr<ConnectedClient>) override;
|
||||
bool notifyClientChatComposing(const std::shared_ptr<ConnectedClient> &ptr) override;
|
||||
bool notifyClientChatClosed(const std::shared_ptr<ConnectedClient> &ptr) override;
|
||||
bool notifyTextMessage(ChatMessageMode mode, const std::shared_ptr<ConnectedClient> &sender, uint64_t targetId, const std::string &textMessage) override;
|
||||
bool notifyServerGroupClientAdd(const std::shared_ptr<ConnectedClient> &invoker, const std::shared_ptr<ConnectedClient> &client, const std::shared_ptr<Group> &group) override;
|
||||
bool notifyServerGroupClientRemove(std::shared_ptr<ConnectedClient> invoker, std::shared_ptr<ConnectedClient> client, std::shared_ptr<Group> group) override;
|
||||
|
||||
bool notifyClientChannelGroupChanged(const std::shared_ptr<ConnectedClient> &invoker, const std::shared_ptr<ConnectedClient> &client, const std::shared_ptr<BasicChannel> &ptr, const std::shared_ptr<BasicChannel> &shared_ptr,
|
||||
const std::shared_ptr<Group> &group, bool lock_channel_tree) override;
|
||||
|
||||
bool notifyChannelMoved(const std::shared_ptr<BasicChannel> &channel, ChannelId order, const std::shared_ptr<ConnectedClient> &invoker) override;
|
||||
bool notifyChannelCreate(const std::shared_ptr<BasicChannel> &channel, ChannelId orderId,
|
||||
const std::shared_ptr<ConnectedClient> &invoker) override;
|
||||
bool notifyChannelDescriptionChanged(std::shared_ptr<BasicChannel> channel) override;
|
||||
bool notifyChannelPasswordChanged(std::shared_ptr<BasicChannel> ) override;
|
||||
bool notifyChannelEdited(std::shared_ptr<BasicChannel> channel, std::vector<std::string> keys, ConnectedClient *invoker) override;
|
||||
|
||||
bool notifyChannelDeleted(const std::deque<ChannelId> &deque, const std::shared_ptr<ConnectedClient> &ptr) override;
|
||||
|
||||
bool notifyMusicQueueAdd(const std::shared_ptr<MusicClient> &bot, const std::shared_ptr<ts::music::SongInfo> &entry, int index, const std::shared_ptr<ConnectedClient> &invoker) override;
|
||||
bool notifyMusicQueueRemove(const std::shared_ptr<MusicClient> &bot, const std::deque<std::shared_ptr<music::SongInfo>> &entry, const std::shared_ptr<ConnectedClient> &invoker) override;
|
||||
bool notifyMusicQueueOrderChange(const std::shared_ptr<MusicClient> &bot, const std::shared_ptr<ts::music::SongInfo> &entry, int order, const std::shared_ptr<ConnectedClient> &invoker) override;
|
||||
|
||||
bool notifyMusicPlayerSongChange(const std::shared_ptr<MusicClient> &bot, const std::shared_ptr<music::SongInfo> &newEntry) override;
|
||||
|
||||
bool notifyClientEnterView(const std::shared_ptr<ConnectedClient> &client, const std::shared_ptr<ConnectedClient> &invoker, const std::string &string, const std::shared_ptr<BasicChannel> &to, ViewReasonId reasonId,
|
||||
const std::shared_ptr<BasicChannel> &from, bool) override;
|
||||
|
||||
bool notifyClientEnterView(const std::deque<std::shared_ptr<ConnectedClient>> &deque, const ViewReasonSystemT &t) override;
|
||||
|
||||
bool
|
||||
notifyClientMoved(const std::shared_ptr<ConnectedClient> &client, const std::shared_ptr<BasicChannel> &target_channel, ViewReasonId reason, std::string msg, std::shared_ptr<ConnectedClient> invoker, bool lock_channel_tree) override;
|
||||
|
||||
bool notifyClientLeftView(const std::shared_ptr<ConnectedClient> &client, const std::shared_ptr<BasicChannel> &target_channel, ViewReasonId reasonId, const std::string &reasonMessage, std::shared_ptr<ConnectedClient> invoker,
|
||||
bool lock_channel_tree) override;
|
||||
|
||||
bool notifyClientLeftView(const std::deque<std::shared_ptr<ConnectedClient>> &deque, const std::string &string, bool b, const ViewReasonServerLeftT &t) override;
|
||||
|
||||
bool
|
||||
notifyClientLeftViewKicked(const std::shared_ptr<ConnectedClient> &client, const std::shared_ptr<BasicChannel> &target_channel, const std::string &message, std::shared_ptr<ConnectedClient> invoker, bool lock_channel_tree) override;
|
||||
|
||||
bool notifyClientLeftViewBanned(const std::shared_ptr<ConnectedClient> &client, const std::string &message, std::shared_ptr<ConnectedClient> invoker, size_t length, bool lock_channel_tree) override;
|
||||
|
||||
private:
|
||||
CommandResult handleCommandExit(Command&);
|
||||
CommandResult handleCommandLogin(Command&);
|
||||
CommandResult handleCommandLogout(Command&);
|
||||
CommandResult handleCommandServerSelect(Command &);
|
||||
CommandResult handleCommandServerInfo(Command&);
|
||||
CommandResult handleCommandChannelList(Command&);
|
||||
CommandResult handleCommandJoin(Command&);
|
||||
CommandResult handleCommandLeft(Command&);
|
||||
|
||||
CommandResult handleCommandServerList(Command&);
|
||||
CommandResult handleCommandServerCreate(Command&);
|
||||
CommandResult handleCommandServerDelete(Command&);
|
||||
CommandResult handleCommandServerStart(Command&);
|
||||
CommandResult handleCommandServerStop(Command&);
|
||||
|
||||
CommandResult handleCommandInstanceInfo(Command&);
|
||||
CommandResult handleCommandInstanceEdit(Command&);
|
||||
|
||||
CommandResult handleCommandBindingList(Command&);
|
||||
|
||||
CommandResult handleCommandHostInfo(Command&);
|
||||
|
||||
CommandResult handleCommandGlobalMessage(Command&);
|
||||
|
||||
CommandResult handleCommandServerIdGetByPort(Command&);
|
||||
|
||||
CommandResult handleCommandServerSnapshotDeploy(Command&);
|
||||
CommandResult handleCommandServerSnapshotCreate(Command&);
|
||||
CommandResult handleCommandServerProcessStop(Command&);
|
||||
|
||||
CommandResult handleCommandServerNotifyRegister(Command&);
|
||||
CommandResult handleCommandServerNotifyList(Command&);
|
||||
CommandResult handleCommandServerNotifyUnregister(Command&);
|
||||
|
||||
CommandResult handleCommandSetCompressionMode(Command&);
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,926 @@
|
||||
#include "Properties.h"
|
||||
#include "query/Command.h"
|
||||
#include <algorithm>
|
||||
#include <src/server/QueryServer.h>
|
||||
#include <src/ServerManager.h>
|
||||
#include <src/InstanceHandler.h>
|
||||
#include <log/LogUtils.h>
|
||||
#include <misc/digest.h>
|
||||
#include "QueryClient.h"
|
||||
#include <misc/base64.h>
|
||||
#include <src/ShutdownHelper.h>
|
||||
#include <ThreadPool/Timer.h>
|
||||
|
||||
using namespace std;
|
||||
using namespace std::chrono;
|
||||
using namespace ts;
|
||||
using namespace ts::server;
|
||||
|
||||
extern ts::server::InstanceHandler* serverInstance;
|
||||
|
||||
CommandResult QueryClient::handleCommand(Command& cmd) {
|
||||
if (cmd.command() == "exit" || cmd.command() == "quit") return this->handleCommandExit(cmd);
|
||||
else if (cmd.command() == "use" || cmd.command() == "serverselect") return this->handleCommandServerSelect(cmd);
|
||||
else if (cmd.command() == "serverinfo") return this->handleCommandServerInfo(cmd);
|
||||
else if (cmd.command() == "channellist") return this->handleCommandChannelList(cmd);
|
||||
else if (cmd.command() == "login") return this->handleCommandLogin(cmd);
|
||||
else if (cmd.command() == "logout") return this->handleCommandLogout(cmd);
|
||||
else if (cmd.command() == "join") return this->handleCommandJoin(cmd);
|
||||
else if (cmd.command() == "left") return this->handleCommandLeft(cmd);
|
||||
else if (cmd.command() == "globalmessage" || cmd.command() == "gm") return this->handleCommandGlobalMessage(cmd);
|
||||
|
||||
else if (cmd.command() == "serverlist") return this->handleCommandServerList(cmd);
|
||||
else if (cmd.command() == "servercreate") return this->handleCommandServerCreate(cmd);
|
||||
else if (cmd.command() == "serverstart") return this->handleCommandServerStart(cmd);
|
||||
else if (cmd.command() == "serverstop") return this->handleCommandServerStop(cmd);
|
||||
else if (cmd.command() == "serverdelete") return this->handleCommandServerDelete(cmd);
|
||||
else if (cmd.command() == "serveridgetbyport") return this->handleCommandServerIdGetByPort(cmd);
|
||||
else if (cmd.command() == "instanceinfo") return this->handleCommandInstanceInfo(cmd);
|
||||
else if (cmd.command() == "instanceedit") return this->handleCommandInstanceEdit(cmd);
|
||||
else if (cmd.command() == "hostinfo") return this->handleCommandHostInfo(cmd);
|
||||
|
||||
else if (cmd.command() == "bindinglist") return this->handleCommandBindingList(cmd);
|
||||
|
||||
else if (cmd.command() == "serversnapshotdeploy") return this->handleCommandServerSnapshotDeploy(cmd);
|
||||
else if (cmd.command() == "serversnapshotcreate") return this->handleCommandServerSnapshotCreate(cmd);
|
||||
|
||||
else if (cmd.command() == "serverprocessstop") return this->handleCommandServerProcessStop(cmd);
|
||||
|
||||
else if (cmd.command() == "servernotifyregister") return this->handleCommandServerNotifyRegister(cmd);
|
||||
else if (cmd.command() == "servernotifylist") return this->handleCommandServerNotifyList(cmd);
|
||||
else if (cmd.command() == "servernotifyunregister") return this->handleCommandServerNotifyUnregister(cmd);
|
||||
|
||||
return ConnectedClient::handleCommand(cmd);
|
||||
}
|
||||
|
||||
CommandResult QueryClient::handleCommandExit(Command &) {
|
||||
logMessage(LOG_QUERY, "[Query] {}:{} disconnected. (Requested by client)", this->getLoggingPeerIp(), this->getPeerPort());
|
||||
this->closeConnection(system_clock::now() + seconds(1));
|
||||
return CommandResult::Success;
|
||||
}
|
||||
|
||||
struct QuerySqlData {
|
||||
std::string username;
|
||||
std::string password;
|
||||
std::string uniqueId;
|
||||
};
|
||||
//login client_login_name=andreas client_login_password=meinPW
|
||||
CommandResult QueryClient::handleCommandLogin(Command& cmd) {
|
||||
CMD_RESET_IDLE;
|
||||
|
||||
std::string username, password;
|
||||
if(cmd[0].has("client_login_name") && cmd[0].has("client_login_password")){
|
||||
username = cmd["client_login_name"].string();
|
||||
password = cmd["client_login_password"].string();
|
||||
} else {
|
||||
username = cmd[0][0].key();
|
||||
password = cmd[0][1].key();
|
||||
}
|
||||
debugMessage("Attempted query login with " + username + " - " + password);
|
||||
|
||||
auto _account = serverInstance->getQueryServer()->find_query_account_by_name(username);
|
||||
auto account = _account ? serverInstance->getQueryServer()->load_password(_account) : nullptr;
|
||||
|
||||
{
|
||||
threads::MutexLock lock(this->handle->loginLock);
|
||||
|
||||
if(!account)
|
||||
return {findError("client_invalid_password"), "username or password dose not match"};
|
||||
|
||||
if (account->password != password) {
|
||||
if(!this->whitelisted) {
|
||||
this->handle->loginAttempts[this->getPeerIp()]++;
|
||||
if(this->handle->loginAttempts[this->getPeerIp()] > 3) {
|
||||
this->handle->queryBann[this->getPeerIp()] = system_clock::now() + seconds(serverInstance->properties()[property::SERVERINSTANCE_SERVERQUERY_BAN_TIME].as<uint64_t>()); //TODO configurable | Disconnect all others?
|
||||
this->postCommandHandler.emplace_back([&](){
|
||||
this->closeConnection(system_clock::now() + seconds(1));
|
||||
});
|
||||
return {findError("ban_flooding"), ""};
|
||||
}
|
||||
}
|
||||
return {findError("client_invalid_password"), "username or password dose not match"};
|
||||
}
|
||||
}
|
||||
if(!this->properties()[property::CLIENT_LOGIN_NAME].as<string>().empty()) {
|
||||
Command log("logout");
|
||||
if(!this->handleCommandLogout(log)) return {ErrorType::VSError, "Failed to logout!"};
|
||||
}
|
||||
|
||||
this->query_account = account;
|
||||
|
||||
auto joined = this->currentChannel;
|
||||
if(this->server) {
|
||||
{
|
||||
unique_lock tree_lock(this->server->channel_tree_lock);
|
||||
if(joined)
|
||||
this->server->client_move(this->ref(), nullptr, nullptr, "", ViewReasonId::VREASON_USER_ACTION, false, tree_lock);
|
||||
this->server->unregisterClient(_this.lock(), "login", tree_lock);
|
||||
}
|
||||
this->server->groups->disableCache(_this.lock());
|
||||
} else serverInstance->getGroupManager()->disableCache(_this.lock());
|
||||
|
||||
logMessage(LOG_QUERY, "Got new authenticated client. Username: {}, Unique-ID: {}, Bounded Server: {}", account->username, account->unique_id, account->bound_server);
|
||||
|
||||
this->properties()[property::CLIENT_LOGIN_NAME] = username;
|
||||
this->properties()[property::CLIENT_UNIQUE_IDENTIFIER] = account->unique_id; //TODO load from table
|
||||
this->properties()[property::CLIENT_NICKNAME] = username;
|
||||
|
||||
auto target_server = this->server; /* keep the server alive 'ill we've joined the server */
|
||||
if(account->bound_server) {
|
||||
target_server = serverInstance->getVoiceServerManager()->findServerById(account->bound_server);
|
||||
if(!target_server)
|
||||
return {findError("server_invalid_id"), "server does not exists anymore"};
|
||||
}
|
||||
this->server = target_server;
|
||||
|
||||
DatabaseHelper::assignDatabaseId(this->sql, static_cast<ServerId>(target_server ? target_server->getServerId() : 0), _this.lock());
|
||||
if(target_server) {
|
||||
target_server->groups->enableCache(_this.lock());
|
||||
target_server->registerClient(_this.lock());
|
||||
|
||||
shared_lock server_tree_lock(target_server->channel_tree_lock);
|
||||
target_server->notifyClientPropertyUpdates(_this.lock(), deque<property::ClientProperties>{property::CLIENT_NICKNAME, property::CLIENT_UNIQUE_IDENTIFIER});
|
||||
|
||||
shared_lock client_tree_lock(this->channel_lock);
|
||||
this->channels->reset();
|
||||
this->channels->insert_channels(target_server->channelTree->tree_head(), true, false);
|
||||
} else {
|
||||
serverInstance->getGroupManager()->enableCache(_this.lock());
|
||||
this->update_cached_permissions();
|
||||
}
|
||||
|
||||
if(target_server) {
|
||||
unique_lock tree_lock(target_server->channel_tree_lock);
|
||||
if(joined)
|
||||
this->server->client_move(this->ref(), joined, nullptr, "", ViewReasonId::VREASON_USER_ACTION, false, tree_lock);
|
||||
}
|
||||
this->properties()[property::CLIENT_TOTALCONNECTIONS]++;
|
||||
this->updateChannelClientProperties(true, true);
|
||||
|
||||
return CommandResult::Success;
|
||||
}
|
||||
|
||||
CommandResult QueryClient::handleCommandLogout(Command &) {
|
||||
CMD_RESET_IDLE;
|
||||
if(this->properties()[property::CLIENT_LOGIN_NAME].as<string>().empty()) return {findError("client_not_logged_in"), "not logged in"};
|
||||
this->properties()[property::CLIENT_LOGIN_NAME] = "";
|
||||
this->query_account = nullptr;
|
||||
|
||||
auto joined = this->currentChannel;
|
||||
if(this->server){
|
||||
{
|
||||
unique_lock tree_lock(this->server->channel_tree_lock);
|
||||
if(joined)
|
||||
this->server->client_move(this->ref(), nullptr, nullptr, "", ViewReasonId::VREASON_USER_ACTION, false, tree_lock);
|
||||
this->server->unregisterClient(_this.lock(), "logout", tree_lock);
|
||||
}
|
||||
this->server->groups->disableCache(_this.lock());
|
||||
} else serverInstance->getGroupManager()->disableCache(_this.lock());
|
||||
|
||||
this->properties()[property::CLIENT_UNIQUE_IDENTIFIER] = "UnknownQuery"; //TODO load from table
|
||||
this->properties()[property::CLIENT_NICKNAME] = string() + "ServerQuery#" + this->getLoggingPeerIp() + "/" + to_string(ntohs(this->getPeerPort()));
|
||||
DatabaseHelper::assignDatabaseId(this->sql, static_cast<ServerId>(this->server ? this->server->getServerId() : 0), _this.lock());
|
||||
|
||||
if(this->server){
|
||||
this->server->groups->enableCache(_this.lock());
|
||||
this->server->registerClient(this->ref());
|
||||
|
||||
shared_lock server_channel_r_lock(this->server->channel_tree_lock);
|
||||
unique_lock client_channel_lock(this->channel_lock);
|
||||
|
||||
this->channels->reset();
|
||||
this->channels->insert_channels(this->server->channelTree->tree_head(), true, false);
|
||||
client_channel_lock.unlock();
|
||||
server_channel_r_lock.unlock();
|
||||
if(joined) {
|
||||
unique_lock server_channel_w_lock(this->server->channel_tree_lock, defer_lock);
|
||||
this->server->client_move(this->ref(), joined, nullptr, "", ViewReasonId::VREASON_USER_ACTION, false, server_channel_w_lock);
|
||||
}
|
||||
} else {
|
||||
serverInstance->getGroupManager()->enableCache(_this.lock());
|
||||
}
|
||||
|
||||
this->updateChannelClientProperties(true, true);
|
||||
|
||||
return CommandResult::Success;
|
||||
}
|
||||
|
||||
CommandResult QueryClient::handleCommandServerSelect(Command &cmd) {
|
||||
CMD_RESET_IDLE;
|
||||
shared_ptr<TSServer> target;
|
||||
|
||||
if(cmd[0].has("port")){
|
||||
target = serverInstance->getVoiceServerManager()->findServerByPort(cmd["port"].as<uint16_t>());
|
||||
} else if(cmd[0].has("sid")) {
|
||||
target = serverInstance->getVoiceServerManager()->findServerById(cmd["sid"].as<ServerId>());
|
||||
}
|
||||
|
||||
for(auto& parm : cmd[0].keys())
|
||||
if(parm.find_first_not_of("0123456789") == string::npos)
|
||||
target = serverInstance->getVoiceServerManager()->findServerById(static_cast<ServerId>(stol(parm)));
|
||||
|
||||
if(!target && (!cmd[0].has("0") && (!cmd[0].has("sid") || !cmd["sid"] == 0))) return {findError("server_invalid_id"), "Invalid server id"};
|
||||
|
||||
|
||||
if(target == this->server) return CommandResult::Success;
|
||||
|
||||
if(target) {
|
||||
if(this->query_account && this->query_account->bound_server > 0) {
|
||||
if(target->getServerId() != this->query_account->bound_server)
|
||||
return {findError("server_invalid_id"), "You're a server bound query, and the target server isn't your origin."};
|
||||
} else {
|
||||
auto allowed = target->calculatePermission(permission::PERMTEST_ORDERED, this->getClientDatabaseId(), permission::b_virtualserver_select, this->getType(), nullptr);
|
||||
if(allowed != -1 && allowed <= 0) return CommandResultPermissionError{permission::b_virtualserver_select};
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
auto server_locked = this->server;
|
||||
if(server_locked) {
|
||||
//unregister manager from old server
|
||||
{
|
||||
unique_lock tree_lock(this->server->channel_tree_lock);
|
||||
if(this->currentChannel)
|
||||
this->server->client_move(this->ref(), nullptr, nullptr, "", ViewReasonId::VREASON_USER_ACTION, false, tree_lock);
|
||||
this->server->unregisterClient(_this.lock(), "server switch", tree_lock);
|
||||
}
|
||||
server_locked->groups->disableCache(_this.lock());
|
||||
this->channels->reset();
|
||||
} else serverInstance->getGroupManager()->disableCache(_this.lock());
|
||||
}
|
||||
this->resetEventMask();
|
||||
|
||||
//register at current server
|
||||
{
|
||||
unique_lock server_lock(this->server_lock);
|
||||
this->server = target;
|
||||
}
|
||||
|
||||
if(cmd[0].has("client_nickname"))
|
||||
this->properties()[property::CLIENT_NICKNAME] = cmd["client_nickname"].string();
|
||||
|
||||
DatabaseHelper::assignDatabaseId(this->sql, static_cast<ServerId>(this->server ? this->server->getServerId() : 0), _this.lock());
|
||||
if(this->server) {
|
||||
this->server->groups->enableCache(_this.lock());
|
||||
this->server->registerClient(_this.lock());
|
||||
|
||||
{
|
||||
shared_lock server_channel_lock(target->channel_tree_lock);
|
||||
|
||||
unique_lock client_channel_lock(this->channel_lock);
|
||||
this->subscribeToAll = true;
|
||||
this->channels->insert_channels(this->server->channelTree->tree_head(), true, false);
|
||||
this->subscribeChannel(this->server->channelTree->channels(), false, true);
|
||||
}
|
||||
|
||||
auto negated_enforce_join = this->permissionGranted(permission::PERMTEST_ORDERED, permission::b_virtualserver_select_godmode, 1);
|
||||
if(!negated_enforce_join)
|
||||
this->server->assignDefaultChannel(this->ref(), true);
|
||||
else
|
||||
this->update_cached_permissions();
|
||||
} else {
|
||||
serverInstance->getGroupManager()->enableCache(_this.lock());
|
||||
this->update_cached_permissions();
|
||||
}
|
||||
this->updateChannelClientProperties(true, true);
|
||||
|
||||
return CommandResult::Success;
|
||||
}
|
||||
|
||||
CommandResult QueryClient::handleCommandJoin(Command &) {
|
||||
CMD_REQ_SERVER;
|
||||
CMD_RESET_IDLE;
|
||||
if(!this->server->running())
|
||||
return {findError("server_is_not_running"), "server isnt started yet"};
|
||||
|
||||
if(this->currentChannel)
|
||||
return {findError("server_already_joined"), "already joined!"};
|
||||
|
||||
debugMessage("Uid -> " + this->properties()[property::CLIENT_UNIQUE_IDENTIFIER].as<string>());
|
||||
this->server->assignDefaultChannel(this->ref(), true);
|
||||
return CommandResult::Success;
|
||||
}
|
||||
|
||||
CommandResult QueryClient::handleCommandLeft(Command&) {
|
||||
CMD_REQ_SERVER;
|
||||
CMD_REQ_CHANNEL;
|
||||
CMD_RESET_IDLE;
|
||||
|
||||
PERM_CHECK_CHANNELR(permission::b_virtualserver_select_godmode, 1, nullptr, 1);
|
||||
unique_lock server_channel_lock(this->server->channel_tree_lock);
|
||||
this->server->client_move(this->ref(), nullptr, nullptr, "leaving", ViewReasonId::VREASON_SERVER_LEFT, true, server_channel_lock);
|
||||
return CommandResult::Success;
|
||||
}
|
||||
|
||||
CommandResult QueryClient::handleCommandServerInfo(Command &) {
|
||||
CMD_RESET_IDLE;
|
||||
PERM_CHECKR(permission::b_virtualserver_info_view, 1, true);
|
||||
|
||||
Command cmd("");
|
||||
|
||||
for(const auto &prop : (this->server ? this->server->properties() : *serverInstance->getDefaultServerProperties()).list_properties(property::FLAG_SERVER_VIEW | property::FLAG_SERVER_VARIABLE, this->getType() == CLIENT_TEAMSPEAK ? property::FLAG_NEW : (uint16_t) 0)) {
|
||||
cmd[prop.type().name] = prop.as<string>();
|
||||
if(prop.type() == property::VIRTUALSERVER_HOST)
|
||||
cmd["virtualserver_ip"] = prop.as<string>();
|
||||
}
|
||||
|
||||
cmd["virtualserver_status"] = this->server ? ServerState::string(this->server->state) : "template";
|
||||
|
||||
|
||||
if(this->server && this->permissionGranted(permission::PERMTEST_ORDERED, permission::b_virtualserver_connectioninfo_view, 1, nullptr, true)) {
|
||||
auto stats = this->server->getServerStatistics()->statistics();
|
||||
auto report = this->server->serverStatistics->dataReport();
|
||||
cmd["connection_bandwidth_sent_last_second_total"] = report.send_second;
|
||||
cmd["connection_bandwidth_sent_last_minute_total"] = report.send_minute;
|
||||
cmd["connection_bandwidth_received_last_second_total"] = report.recv_second;
|
||||
cmd["connection_bandwidth_received_last_minute_total"] = report.recv_minute;
|
||||
|
||||
cmd["connection_filetransfer_bandwidth_sent"] = report.file_send;
|
||||
cmd["connection_filetransfer_bandwidth_received"] = report.file_recv;
|
||||
cmd["connection_filetransfer_bytes_sent_total"] = (*stats)[property::CONNECTION_FILETRANSFER_BYTES_SENT_TOTAL].as<string>();
|
||||
cmd["connection_filetransfer_bytes_received_total"] = (*stats)[property::CONNECTION_FILETRANSFER_BYTES_RECEIVED_TOTAL].as<string>();
|
||||
|
||||
cmd["connection_packets_sent_speech"] = (*stats)[property::CONNECTION_FILETRANSFER_BANDWIDTH_SENT].value();
|
||||
cmd["connection_bytes_sent_speech"] = (*stats)[property::CONNECTION_FILETRANSFER_BANDWIDTH_RECEIVED].value();
|
||||
cmd["connection_packets_received_speech"] = (*stats)[property::CONNECTION_FILETRANSFER_BYTES_SENT_TOTAL].value();
|
||||
cmd["connection_bytes_received_speech"] = (*stats)[property::CONNECTION_FILETRANSFER_BYTES_RECEIVED_TOTAL].value();
|
||||
|
||||
cmd["connection_packets_sent_keepalive"] = (*stats)[property::CONNECTION_PACKETS_SENT_KEEPALIVE].value();
|
||||
cmd["connection_packets_received_keepalive"] = (*stats)[property::CONNECTION_PACKETS_RECEIVED_KEEPALIVE].value();
|
||||
cmd["connection_bytes_received_keepalive"] = (*stats)[property::CONNECTION_BYTES_RECEIVED_KEEPALIVE].value();
|
||||
cmd["connection_bytes_sent_keepalive"] = (*stats)[property::CONNECTION_BYTES_SENT_KEEPALIVE].value();
|
||||
|
||||
cmd["connection_packets_sent_control"] = (*stats)[property::CONNECTION_PACKETS_SENT_CONTROL].value();
|
||||
cmd["connection_bytes_sent_control"] = (*stats)[property::CONNECTION_BYTES_SENT_CONTROL].value();
|
||||
cmd["connection_packets_received_control"] = (*stats)[property::CONNECTION_PACKETS_RECEIVED_CONTROL].value();
|
||||
cmd["connection_bytes_received_control"] = (*stats)[property::CONNECTION_BYTES_RECEIVED_CONTROL].value();
|
||||
|
||||
cmd["connection_packets_sent_total"] = (*stats)[property::CONNECTION_PACKETS_SENT_TOTAL].value();
|
||||
cmd["connection_bytes_sent_total"] = (*stats)[property::CONNECTION_BYTES_SENT_TOTAL].value();
|
||||
cmd["connection_packets_received_total"] = (*stats)[property::CONNECTION_PACKETS_RECEIVED_TOTAL].value();
|
||||
cmd["connection_bytes_received_total"] = (*stats)[property::CONNECTION_BYTES_RECEIVED_TOTAL].value();
|
||||
} else {
|
||||
cmd["connection_bandwidth_sent_last_second_total"] = "0";
|
||||
cmd["connection_bandwidth_sent_last_minute_total"] = "0";
|
||||
cmd["connection_bandwidth_received_last_second_total"] = "0";
|
||||
cmd["connection_bandwidth_received_last_minute_total"] = "0";
|
||||
|
||||
cmd["connection_filetransfer_bandwidth_sent"] = "0";
|
||||
cmd["connection_filetransfer_bandwidth_received"] = "0";
|
||||
cmd["connection_filetransfer_bytes_sent_total"] = "0";
|
||||
cmd["connection_filetransfer_bytes_received_total"] = "0";
|
||||
|
||||
cmd["connection_packets_sent_speech"] = "0";
|
||||
cmd["connection_bytes_sent_speech"] = "0";
|
||||
cmd["connection_packets_received_speech"] = "0";
|
||||
cmd["connection_bytes_received_speech"] = "0";
|
||||
|
||||
cmd["connection_packets_sent_keepalive"] = "0";
|
||||
cmd["connection_packets_received_keepalive"] = "0";
|
||||
cmd["connection_bytes_received_keepalive"] = "0";
|
||||
cmd["connection_bytes_sent_keepalive"] = "0";
|
||||
|
||||
cmd["connection_packets_sent_control"] = "0";
|
||||
cmd["connection_bytes_sent_control"] = "0";
|
||||
cmd["connection_packets_received_control"] = "0";
|
||||
cmd["connection_bytes_received_control"] = "0";
|
||||
|
||||
cmd["connection_packets_sent_total"] = "0";
|
||||
cmd["connection_bytes_sent_total"] = "0";
|
||||
cmd["connection_packets_received_total"] = "0";
|
||||
cmd["connection_bytes_received_total"] = "0";
|
||||
}
|
||||
|
||||
this->sendCommand(cmd);
|
||||
return CommandResult::Success;
|
||||
}
|
||||
|
||||
CommandResult QueryClient::handleCommandChannelList(Command& cmd) {
|
||||
PERM_CHECKR(permission::b_virtualserver_channel_list, 1, true);
|
||||
CMD_RESET_IDLE;
|
||||
|
||||
Command result("");
|
||||
|
||||
int index = 0;
|
||||
shared_lock channel_lock(this->server ? this->server->channel_tree_lock : serverInstance->getChannelTreeLock());
|
||||
auto entries = this->server ? this->channels->channels() : serverInstance->getChannelTree()->channels();
|
||||
channel_lock.unlock();
|
||||
|
||||
for(const auto& channel : entries){
|
||||
if(!channel) continue;
|
||||
|
||||
auto& bulk = result[index];
|
||||
bulk["cid"] = channel->channelId();
|
||||
bulk["pid"] = channel->properties()[property::CHANNEL_PID].as<string>();
|
||||
bulk["channel_name"] = channel->name();
|
||||
bulk["channel_order"] = channel->channelOrder();
|
||||
bulk["total_clients"] = this->server ? this->server->getClientsByChannel(channel).size() : 0;
|
||||
/* bulk["channel_needed_subscribe_power"] = channel->permissions()->getPermissionValue(permission::PERMTEST_ORDERED, permission::i_channel_needed_subscribe_power, channel, 0); */
|
||||
|
||||
if(cmd.hasParm("flags")){
|
||||
bulk["channel_flag_default"] = channel->properties()[property::CHANNEL_FLAG_DEFAULT].as<string>();
|
||||
bulk["channel_flag_password"] = channel->properties()[property::CHANNEL_FLAG_PASSWORD].as<string>();
|
||||
bulk["channel_flag_permanent"] = channel->properties()[property::CHANNEL_FLAG_PERMANENT].as<string>();
|
||||
bulk["channel_flag_semi_permanent"] = channel->properties()[property::CHANNEL_FLAG_SEMI_PERMANENT].as<string>();
|
||||
}
|
||||
if(cmd.hasParm("voice")){
|
||||
bulk["channel_codec"] = channel->properties()[property::CHANNEL_CODEC].as<string>();
|
||||
bulk["channel_codec_quality"] = channel->properties()[property::CHANNEL_CODEC_QUALITY].as<string>();
|
||||
bulk["channel_needed_talk_power"] = channel->properties()[property::CHANNEL_NEEDED_TALK_POWER].as<string>();
|
||||
}
|
||||
if(cmd.hasParm("icon")){
|
||||
bulk["channel_icon_id"] = channel->properties()[property::CHANNEL_ICON_ID].as<string>();
|
||||
}
|
||||
if(cmd.hasParm("limits")){
|
||||
bulk["total_clients_family"] = this->server ? this->server->getClientsByChannelRoot(channel, false).size() : 0;
|
||||
bulk["total_clients"] = this->server ? this->server->getClientsByChannel(channel).size() : 0;
|
||||
|
||||
bulk["channel_maxclients"] = channel->properties()[property::CHANNEL_MAXCLIENTS].as<string>();
|
||||
bulk["channel_maxfamilyclients"] = channel->properties()[property::CHANNEL_MAXFAMILYCLIENTS].as<string>();
|
||||
|
||||
{
|
||||
auto needed_power = channel->permissions()->permission_value_flagged(permission::i_channel_subscribe_power);
|
||||
bulk["channel_needed_subscribe_power"] = needed_power.has_value ? needed_power.value : 0;
|
||||
}
|
||||
}
|
||||
if(cmd.hasParm("topic")) {
|
||||
bulk["channel_topic"] = channel->properties()[property::CHANNEL_TOPIC].as<string>();
|
||||
}
|
||||
if(cmd.hasParm("times")){
|
||||
bulk["seconds_empty"] = channel->emptySince();
|
||||
}
|
||||
index++;
|
||||
}
|
||||
|
||||
this->sendCommand(result);
|
||||
return CommandResult::Success;
|
||||
}
|
||||
|
||||
CommandResult QueryClient::handleCommandServerList(Command& cmd) {
|
||||
CMD_RESET_IDLE;
|
||||
PERM_CHECKR(permission::b_serverinstance_virtualserver_list, 1, true);
|
||||
Command res("");
|
||||
|
||||
int index = 0;
|
||||
for(const auto& server : serverInstance->getVoiceServerManager()->serverInstances()){
|
||||
res[index]["virtualserver_id"] = server->getServerId();
|
||||
res[index]["virtualserver_host"] = server->properties()[property::VIRTUALSERVER_HOST].as<string>();
|
||||
res[index]["virtualserver_port"] = server->properties()[property::VIRTUALSERVER_PORT].as<string>();
|
||||
res[index]["virtualserver_web_host"] = server->properties()[property::VIRTUALSERVER_WEB_HOST].as<string>();
|
||||
res[index]["virtualserver_web_port"] = server->properties()[property::VIRTUALSERVER_WEB_PORT].as<string>();
|
||||
res[index]["virtualserver_status"] = ServerState::string(server->state);
|
||||
res[index]["virtualserver_clientsonline"] = server->properties()[property::VIRTUALSERVER_CLIENTS_ONLINE].as<string>();
|
||||
res[index]["virtualserver_queryclientsonline"] = server->properties()[property::VIRTUALSERVER_QUERYCLIENTS_ONLINE].as<string>();
|
||||
res[index]["virtualserver_maxclients"] = server->properties()[property::VIRTUALSERVER_MAXCLIENTS].as<string>();
|
||||
if(server->startTimestamp.time_since_epoch().count() > 0 && server->state == ServerState::ONLINE)
|
||||
res[index]["virtualserver_uptime"] = duration_cast<seconds>(system_clock::now() - server->startTimestamp).count();
|
||||
else
|
||||
res[index]["virtualserver_uptime"] = 0;
|
||||
res[index]["virtualserver_name"] = server->properties()[property::VIRTUALSERVER_NAME].as<string>();
|
||||
res[index]["virtualserver_autostart"] = server->properties()[property::VIRTUALSERVER_AUTOSTART].as<string>();
|
||||
res[index]["virtualserver_machine_id"] = server->properties()[property::VIRTUALSERVER_MACHINE_ID].as<string>();
|
||||
if(cmd.hasParm("uid"))
|
||||
res[index]["virtualserver_unique_identifier"] = server->properties()[property::VIRTUALSERVER_UNIQUE_IDENTIFIER].as<string>();
|
||||
else res[index]["virtualserver_unique_identifier"] = "";
|
||||
index++;
|
||||
}
|
||||
|
||||
this->sendCommand(res);
|
||||
return CommandResult::Success;
|
||||
}
|
||||
|
||||
CommandResult QueryClient::handleCommandServerCreate(Command& cmd) {
|
||||
CMD_RESET_IDLE;
|
||||
PERM_CHECKR(permission::b_virtualserver_create, 1, true);
|
||||
|
||||
if(serverInstance->getVoiceServerManager()->getState() != ServerManager::STARTED) {
|
||||
return {findError("vs_critical"), "Server manager isn't started yet or not finished starting"};
|
||||
}
|
||||
|
||||
string startError;
|
||||
shared_ptr<TSServer> server;
|
||||
|
||||
milliseconds time_create, time_wait, time_start, time_global;
|
||||
{
|
||||
auto start = system_clock::now();
|
||||
threads::MutexLock lock(serverInstance->getVoiceServerManager()->server_create_lock);
|
||||
auto instances = serverInstance->getVoiceServerManager()->serverInstances();
|
||||
if(config::server::max_virtual_server != -1 && instances.size() > config::server::max_virtual_server)
|
||||
return {findError("server_max_vs_reached"), "You reached the via config.yml enabled virtual server limit."};
|
||||
if(instances.size() >= 65535)
|
||||
return {findError("server_max_vs_reached"), "You cant create anymore virtual servers. (Software limit reached)"};
|
||||
{
|
||||
auto end = system_clock::now();
|
||||
time_wait = duration_cast<milliseconds>(end - start);
|
||||
}
|
||||
|
||||
uint16_t freePort = serverInstance->getVoiceServerManager()->next_available_port();
|
||||
|
||||
std::string host = cmd[0].has("virtualserver_host") ? cmd["virtualserver_host"].as<string>() : config::binding::DefaultVoiceHost;
|
||||
uint16_t port = cmd[0].has("virtualserver_port") ? cmd["virtualserver_port"].as<uint16_t>() : freePort;
|
||||
{
|
||||
auto _start = system_clock::now();
|
||||
server = serverInstance->getVoiceServerManager()->createServer(host, port);
|
||||
auto _end = system_clock::now();
|
||||
time_create = duration_cast<milliseconds>(_end - _start);
|
||||
}
|
||||
if(!server) return {findError("vs_critical"), "could not create new server"};
|
||||
|
||||
for(const auto& key : cmd[0].keys()){
|
||||
if(key == "virtualserver_port") continue;
|
||||
if(key == "virtualserver_host") continue;
|
||||
|
||||
auto info = property::impl::info<property::VirtualServerProperties>(key);
|
||||
if(*info == property::VIRTUALSERVER_UNDEFINED) {
|
||||
logError(server->getServerId(), "Tried to change unknown server property " + key);
|
||||
continue;
|
||||
}
|
||||
if(!info->validate_input(cmd[key].as<string>())) {
|
||||
logError(server->getServerId(), "Tried to change " + key + " to an invalid value: " + cmd[key].as<string>());
|
||||
continue;
|
||||
}
|
||||
server->properties()[info] = cmd[key].string();
|
||||
}
|
||||
|
||||
if(!cmd.hasParm("offline")) {
|
||||
auto _start = system_clock::now();
|
||||
if(!server->start(startError));
|
||||
auto _end = system_clock::now();
|
||||
time_start = duration_cast<milliseconds>(_end - _start);
|
||||
}
|
||||
auto end = system_clock::now();
|
||||
|
||||
time_global = duration_cast<milliseconds>(end - start);
|
||||
}
|
||||
|
||||
Command res("");
|
||||
res["sid"] = server->getServerId();
|
||||
res["error"] = startError;
|
||||
res["virtualserver_port"] = server->properties()[property::VIRTUALSERVER_PORT].as<string>();
|
||||
res["token"] = server->tokenManager->avariableTokes().empty() ? "unknown" : server->tokenManager->avariableTokes()[0]->token;
|
||||
res["time_create"] = time_create.count();
|
||||
res["time_start"] = time_start.count();
|
||||
res["time_global"] = time_global.count();
|
||||
res["time_wait"] = time_wait.count();
|
||||
this->sendCommand(res);
|
||||
return CommandResult::Success;
|
||||
}
|
||||
|
||||
CommandResult QueryClient::handleCommandServerDelete(Command& cmd) {
|
||||
CMD_RESET_IDLE;
|
||||
PERM_CHECKR(permission::b_virtualserver_delete, 1, true);
|
||||
|
||||
if(serverInstance->getVoiceServerManager()->getState() != ServerManager::STARTED)
|
||||
return {findError("vs_critical"), "Server manager isn't started yet or not finished starting"};
|
||||
|
||||
auto server = serverInstance->getVoiceServerManager()->findServerById(cmd["sid"]);
|
||||
if(!server) return {findError("server_invalid_id"), "invalid bounded server"};
|
||||
if(!serverInstance->getVoiceServerManager()->deleteServer(server)) return {ErrorType::VSError};
|
||||
return CommandResult::Success;
|
||||
}
|
||||
|
||||
CommandResult QueryClient::handleCommandServerStart(Command& cmd) {
|
||||
CMD_RESET_IDLE;
|
||||
|
||||
PERM_CHECKR(permission::b_virtualserver_start_any, 1, true);
|
||||
if(serverInstance->getVoiceServerManager()->getState() != ServerManager::STARTED)
|
||||
return {findError("vs_critical"), "Server manager isn't started yet or not finished starting"};
|
||||
|
||||
auto server = serverInstance->getVoiceServerManager()->findServerById(cmd["sid"]);
|
||||
if(!server) return {findError("server_invalid_id"), "invalid bounded server"};
|
||||
if(server->running()) return {findError("server_running"), "server already running"};
|
||||
|
||||
if(server->calculatePermission(permission::PERMTEST_ORDERED, this->getClientDatabaseId(), permission::b_virtualserver_start, ClientType::CLIENT_QUERY, nullptr) != 1) {
|
||||
if(!this->permissionGranted(permission::PERMTEST_HIGHEST, permission::b_virtualserver_start_any, 1))
|
||||
return CommandResultPermissionError{permission::b_virtualserver_start};
|
||||
}
|
||||
|
||||
string err;
|
||||
if(!server->start(err)) return {findError("vs_critical"), err};
|
||||
return CommandResult::Success;
|
||||
}
|
||||
|
||||
CommandResult QueryClient::handleCommandServerStop(Command& cmd) {
|
||||
CMD_RESET_IDLE;
|
||||
PERM_CHECKR(permission::b_virtualserver_stop, 1, true);
|
||||
if(serverInstance->getVoiceServerManager()->getState() != ServerManager::STARTED)
|
||||
return {findError("vs_critical"), "Server manager isn't started yet or not finished starting"};
|
||||
|
||||
auto server = serverInstance->getVoiceServerManager()->findServerById(cmd["sid"]);
|
||||
if(!server) return {findError("server_invalid_id"), "invalid bounded server"};
|
||||
if(!server->running()) return {findError("server_is_not_running"), "server is not running"};
|
||||
|
||||
if(server->calculatePermission(permission::PERMTEST_ORDERED, this->getClientDatabaseId(), permission::b_virtualserver_stop, ClientType::CLIENT_QUERY, nullptr) != 1) {
|
||||
if(!this->permissionGranted(permission::PERMTEST_HIGHEST, permission::b_virtualserver_stop_any, 1))
|
||||
return CommandResultPermissionError{permission::b_virtualserver_stop};
|
||||
}
|
||||
|
||||
server->stop("server stopped");
|
||||
return CommandResult::Success;
|
||||
}
|
||||
|
||||
|
||||
CommandResult QueryClient::handleCommandInstanceInfo(Command& cmd) {
|
||||
PERM_CHECKR(permission::b_serverinstance_info_view, 1, true);
|
||||
|
||||
Command res("");
|
||||
for(const auto& e : serverInstance->properties().list_properties(property::FLAG_INSTANCE_VARIABLE, this->getType() == CLIENT_TEAMSPEAK ? property::FLAG_NEW : (uint16_t) 0))
|
||||
res[e.type().name] = e.as<string>();
|
||||
if(!this->properties()[property::CLIENT_LOGIN_NAME].value().empty())
|
||||
res["serverinstance_teaspeak"] = true;
|
||||
|
||||
this->sendCommand(res);
|
||||
return CommandResult::Success;
|
||||
}
|
||||
|
||||
CommandResult QueryClient::handleCommandInstanceEdit(Command& cmd) {
|
||||
PERM_CHECKR(permission::b_serverinstance_modify_settings, 1, true);
|
||||
|
||||
for(const auto &key : cmd[0].keys()){
|
||||
auto info = property::impl::info<property::InstanceProperties>(key);
|
||||
if(*info == property::SERVERINSTANCE_UNDEFINED) {
|
||||
logError(LOG_QUERY, "Query {} tried to change a non existing instance property: {}", this->getLoggingPeerIp(), key);
|
||||
continue;
|
||||
}
|
||||
if(!info->validate_input(cmd[key])) {
|
||||
logError(LOG_QUERY, "Query {} tried to change {} to an invalid value {}", this->getLoggingPeerIp(), key, cmd[key].as<string>());
|
||||
continue;
|
||||
}
|
||||
if(*info == property::SERVERINSTANCE_VIRTUAL_SERVER_ID_INDEX) {
|
||||
/* ensure we've a valid id */
|
||||
serverInstance->properties()[info] = cmd[key].as<ServerId>();
|
||||
continue;
|
||||
}
|
||||
serverInstance->properties()[info] = cmd[key].string();
|
||||
}
|
||||
return CommandResult::Success;
|
||||
}
|
||||
|
||||
CommandResult QueryClient::handleCommandHostInfo(Command &) {
|
||||
PERM_CHECKR(permission::b_serverinstance_info_view, 1, true);
|
||||
|
||||
Command res("");
|
||||
res["instance_uptime"] = duration_cast<seconds>(system_clock::now() - serverInstance->getStartTimestamp()).count();
|
||||
res["host_timestamp_utc"] = duration_cast<seconds>(system_clock::now().time_since_epoch()).count();
|
||||
|
||||
auto vsReport = serverInstance->getVoiceServerManager()->report();
|
||||
res["virtualservers_running_total"] = vsReport.online;
|
||||
res["virtualservers_total_maxclients"] = vsReport.slots;
|
||||
res["virtualservers_total_clients_online"] = vsReport.onlineClients;
|
||||
res["virtualservers_total_channels_online"] = vsReport.onlineChannels;
|
||||
|
||||
|
||||
auto stats = serverInstance->getStatistics()->statistics();
|
||||
res["connection_packets_sent_total"] = (*stats)[property::CONNECTION_PACKETS_SENT_TOTAL].as<string>();
|
||||
res["connection_bytes_sent_total"] = (*stats)[property::CONNECTION_BYTES_SENT_TOTAL].as<string>();
|
||||
res["connection_packets_received_total"] = (*stats)[property::CONNECTION_PACKETS_RECEIVED_TOTAL].as<string>();
|
||||
res["connection_bytes_received_total"] = (*stats)[property::CONNECTION_BYTES_RECEIVED_TOTAL].as<string>();
|
||||
|
||||
auto report = serverInstance->getStatistics()->dataReport();
|
||||
res["connection_bandwidth_sent_last_second_total"] = report.send_second;
|
||||
res["connection_bandwidth_sent_last_minute_total"] = report.send_minute;
|
||||
res["connection_bandwidth_received_last_second_total"] = report.recv_second;
|
||||
res["connection_bandwidth_received_last_minute_total"] = report.recv_minute;
|
||||
|
||||
res["connection_filetransfer_bandwidth_sent"] = report.file_send;
|
||||
res["connection_filetransfer_bandwidth_received"] = report.file_recv;
|
||||
res["connection_filetransfer_bytes_sent_total"] = (*stats)[property::CONNECTION_FILETRANSFER_BYTES_SENT_TOTAL].as<string>();
|
||||
res["connection_filetransfer_bytes_received_total"] = (*stats)[property::CONNECTION_FILETRANSFER_BYTES_RECEIVED_TOTAL].as<string>();
|
||||
|
||||
this->sendCommand(res);
|
||||
return CommandResult::Success;
|
||||
}
|
||||
|
||||
CommandResult QueryClient::handleCommandGlobalMessage(Command& cmd) {
|
||||
PERM_CHECKR(permission::b_serverinstance_textmessage_send, 1, true);
|
||||
|
||||
for(const auto &server : serverInstance->getVoiceServerManager()->serverInstances())
|
||||
if(server->running()) server->broadcastMessage(server->getServerRoot(), cmd["msg"]);
|
||||
|
||||
return CommandResult::Success;
|
||||
}
|
||||
|
||||
CommandResult QueryClient::handleCommandServerIdGetByPort(Command& cmd) {
|
||||
uint16_t port = cmd["virtualserver_port"];
|
||||
auto server = serverInstance->getVoiceServerManager()->findServerByPort(port);
|
||||
if(!server) return {findError("databse_empty_result"), "Invalid port"};
|
||||
Command res("");
|
||||
res["server_id"] = server->getServerId();
|
||||
this->sendCommand(res);
|
||||
return CommandResult::Success;
|
||||
}
|
||||
|
||||
CommandResult QueryClient::handleCommandBindingList(Command& cmd) {
|
||||
Command res("");
|
||||
res["ip"] = "0.0.0.0 ";
|
||||
this->sendCommand(res); //TODO maybe list here all bindings from voice & file and query server?
|
||||
return CommandResult::Success;
|
||||
}
|
||||
|
||||
//TODO with mapping!
|
||||
CommandResult QueryClient::handleCommandServerSnapshotDeploy(Command& cmd) {
|
||||
PERM_CHECKR(permission::b_virtualserver_snapshot_deploy, 1, true);
|
||||
CMD_RESET_IDLE;
|
||||
|
||||
auto start = system_clock::now();
|
||||
string error;
|
||||
string host = "0.0.0.0";
|
||||
uint16_t port = 0;
|
||||
if(this->server) {
|
||||
host = this->server->properties()[property::VIRTUALSERVER_HOST].as<string>();
|
||||
port = this->server->properties()[property::VIRTUALSERVER_PORT].as<uint16_t>();
|
||||
}
|
||||
|
||||
auto hash = cmd["hash"].string();
|
||||
if(hash.empty()) return {findError("parameter_invalid"), "Invalid hash (not present)"};
|
||||
debugMessage("Hash: '" + hash + "'");
|
||||
bool mapping = cmd.hasParm("mapping");
|
||||
cmd.disableParm("mapping");
|
||||
|
||||
bool ignore_hash = cmd.hasParm("ignorehash");
|
||||
cmd.disableParm("ignorehash");
|
||||
|
||||
cmd.pop_bulk();
|
||||
auto str = cmd.build().substr(strlen("serversnapshotdeploy "));
|
||||
if(!ignore_hash) {
|
||||
auto buildHash = base64::encode(digest::sha1(str));
|
||||
if(buildHash != hash) return {findError("parameter_invalid"), "Invalid hash (Expected: \"" + hash + "\", Got: \"" + buildHash + "\")"};
|
||||
}
|
||||
|
||||
unique_lock server_create_lock(serverInstance->getVoiceServerManager()->server_create_lock);
|
||||
if(port == 0)
|
||||
port = serverInstance->getVoiceServerManager()->next_available_port();
|
||||
auto result = serverInstance->getVoiceServerManager()->createServerFromSnapshot(this->server, host, port, cmd, error);
|
||||
server_create_lock.unlock();
|
||||
auto end = system_clock::now();
|
||||
|
||||
Command res("");
|
||||
if(!result){
|
||||
logError("Could not apply server snapshot: " + error);
|
||||
res["success"] = false;
|
||||
res["sid"] = 0;
|
||||
res["message"] = error;
|
||||
} else {
|
||||
res["success"] = true;
|
||||
res["sid"] = result->getServerId();
|
||||
res["message"] = "";
|
||||
|
||||
if(!result->start(error)){
|
||||
res["error_start"] = error;
|
||||
res["started"] = false;
|
||||
} else {
|
||||
res["error_start"] = "";
|
||||
res["started"] = true;
|
||||
}
|
||||
}
|
||||
|
||||
res["time"] = duration_cast<milliseconds>(end - start).count();
|
||||
this->sendCommand(res);
|
||||
return CommandResult::Success;
|
||||
}
|
||||
|
||||
CommandResult QueryClient::handleCommandServerSnapshotCreate(Command& cmd) {
|
||||
PERM_CHECKR(permission::b_virtualserver_snapshot_create, 1, true);
|
||||
CMD_RESET_IDLE;
|
||||
CMD_REQ_SERVER;
|
||||
|
||||
Command result("");
|
||||
string error;
|
||||
|
||||
int version = cmd[0].has("version") ? cmd[0]["version"] : -1;
|
||||
if(version == -1 && (cmd.hasParm("lagacy") || cmd.hasParm("legacy")))
|
||||
version = 0;
|
||||
if(!serverInstance->getVoiceServerManager()->createServerSnapshot(result, this->server, version, error))
|
||||
return {ErrorType::VSError, error};
|
||||
|
||||
string data = result.build();
|
||||
auto buildHash = base64::encode(digest::sha1(data));
|
||||
|
||||
result.push_bulk_front();
|
||||
result[0]["hash"] = buildHash;
|
||||
|
||||
this->sendCommand(result);
|
||||
return CommandResult::Success;
|
||||
}
|
||||
|
||||
extern bool mainThreadActive;
|
||||
CommandResult QueryClient::handleCommandServerProcessStop(Command& cmd) {
|
||||
PERM_CHECKR(permission::b_serverinstance_stop, 1, true);
|
||||
|
||||
if(cmd[0].has("type")) {
|
||||
if(cmd["type"] == "cancel") {
|
||||
auto task = ts::server::scheduledShutdown();
|
||||
if(!task) return {findError("server_is_not_shutting_down"), "There isn't a shutdown scheduled"};
|
||||
ts::server::cancelShutdown(true);
|
||||
return CommandResult::Success;
|
||||
} else if(cmd["type"] == "schedule") {
|
||||
if(!cmd[0].has("time")) return {findError("parameter_missing"), "Missing time"};
|
||||
ts::server::scheduleShutdown(system_clock::now() + seconds(cmd["time"].as<uint64_t>()), cmd[0].has("msg") ? cmd["msg"].string() : ts::config::messages::applicationStopped);
|
||||
return CommandResult::Success;
|
||||
}
|
||||
}
|
||||
|
||||
string reason = ts::config::messages::applicationStopped;
|
||||
if(cmd[0].has("msg"))
|
||||
reason = cmd["msg"].string();
|
||||
if(cmd[0].has("reasonmsg"))
|
||||
reason = cmd["reasonmsg"].string();
|
||||
serverInstance->getVoiceServerManager()->shutdownAll(reason);
|
||||
mainThreadActive = false;
|
||||
return CommandResult::Success;
|
||||
}
|
||||
|
||||
#define XMACRO_EV(evName0, evSpec0, evName1, evSpec1) \
|
||||
if(cmd["event"].value() == "all" || (cmd["event"].value() == (evName0) && (cmd["specifier"].value() == "all" || cmd["specifier"].value() == (evSpec0)))) \
|
||||
events.push_back({QueryEventGroup::evName1, QueryEventSpecifier::evSpec1});
|
||||
|
||||
inline CommandResult parseEvent(ParameterBulk& cmd, vector<pair<QueryEventGroup, QueryEventSpecifier>>& events){
|
||||
auto start = events.size();
|
||||
#include "XMacroEventTypes.h"
|
||||
if(start == events.size()) return {findError("parameter_invalid"), "invalid group/specifier"};
|
||||
return CommandResult::Success;
|
||||
}
|
||||
#undef XMACRO_EV
|
||||
|
||||
|
||||
#define XMACRO_LAGACY_EV(lagacyName, gr, spec) \
|
||||
if(event == lagacyName) this->toggleEvent(QueryEventGroup::gr, QueryEventSpecifier::spec, true);
|
||||
|
||||
CommandResult QueryClient::handleCommandServerNotifyRegister(Command &cmd) {
|
||||
CMD_REQ_SERVER;
|
||||
CMD_REQ_PARM("event");
|
||||
CMD_RESET_IDLE;
|
||||
if(!cmd[0].has("specifier") && cmd["event"].as<string>() != "all") { //Lagacy support
|
||||
logMessage("[Query] Client " + this->getLoggingPeerIp() + ":" + to_string(this->getPeerPort()) + " uses the lagacy notify system, which is deprecated!");
|
||||
string event = cmd["event"];
|
||||
std::transform(event.begin(), event.end(), event.begin(), ::tolower);
|
||||
#include "XMacroEventTypes.h"
|
||||
return CommandResult::Success;
|
||||
}
|
||||
|
||||
//TODO implement bulk
|
||||
vector<pair<QueryEventGroup, QueryEventSpecifier>> events;
|
||||
//parameter_invalid
|
||||
auto result = parseEvent(cmd[0], events);
|
||||
if(!result) return result;
|
||||
for(const auto& ev : events)
|
||||
this->toggleEvent(ev.first, ev.second, true);
|
||||
return CommandResult::Success;
|
||||
}
|
||||
#undef XMACRO_LAGACY_EV
|
||||
|
||||
#define XMACRO_EV(evName0, evSpec0, evName1, evSpec1) \
|
||||
else if((evName1) == group && (evSpec1) == spec){ \
|
||||
res[index]["event"] = evName0; \
|
||||
res[index]["specifier"] = evSpec0; \
|
||||
index++; \
|
||||
}
|
||||
|
||||
CommandResult QueryClient::handleCommandServerNotifyList(Command& cmd) {
|
||||
CMD_REQ_SERVER;
|
||||
CMD_RESET_IDLE;
|
||||
|
||||
Command res("");
|
||||
int index = 0;
|
||||
|
||||
for(int group = QueryEventGroup::QEVENTGROUP_MIN; group < QueryEventGroup::QEVENTGROUP_MAX; group = group + 1)
|
||||
for(int spec = QueryEventSpecifier::QEVENTSPECIFIER_MIN; spec < QueryEventSpecifier::QEVENTSPECIFIER_MAX; spec = spec + 1) {
|
||||
bool state = this->eventActive((QueryEventGroup) group, (QueryEventSpecifier) spec);
|
||||
if(state || cmd.hasParm("all")){
|
||||
res[index]["active"] = state;
|
||||
if(false);
|
||||
#include "XMacroEventTypes.h"
|
||||
}
|
||||
}
|
||||
|
||||
if(index == 0) return {findError("database_empty_result"), ""};
|
||||
this->sendCommand(res);
|
||||
return CommandResult::Success;
|
||||
}
|
||||
#undef XMACRO_EV
|
||||
|
||||
#define XMACRO_LAGACY_EV(lagacyName, gr, spec) \
|
||||
if(event == lagacyName) this->toggleEvent(QueryEventGroup::gr, QueryEventSpecifier::spec, false);
|
||||
|
||||
CommandResult QueryClient::handleCommandServerNotifyUnregister(Command &cmd) {
|
||||
CMD_REQ_SERVER;
|
||||
CMD_REQ_PARM("event");
|
||||
CMD_RESET_IDLE;
|
||||
if(!cmd[0].has("specifier")){
|
||||
logMessage("[Query] Client " + this->getLoggingPeerIp() + ":" + to_string(this->getPeerPort()) + " uses the lagacy notify system, which is deprecated!");
|
||||
string event = cmd["event"];
|
||||
std::transform(event.begin(), event.end(), event.begin(), ::tolower);
|
||||
#include "XMacroEventTypes.h"
|
||||
return CommandResult::Success;
|
||||
}
|
||||
|
||||
//TODO implemt bulk
|
||||
vector<pair<QueryEventGroup, QueryEventSpecifier>> events;
|
||||
auto result = parseEvent(cmd[0], events);
|
||||
if(!result) return result;
|
||||
for(const auto& ev : events)
|
||||
this->toggleEvent(ev.first, ev.second, false);
|
||||
return CommandResult::Success;
|
||||
}
|
||||
|
||||
#undef XMACRO_LAGACY_EV
|
||||
@@ -0,0 +1,227 @@
|
||||
#include "Properties.h"
|
||||
#include "query/Command.h"
|
||||
#include <algorithm>
|
||||
#include <src/server/QueryServer.h>
|
||||
#include <src/ServerManager.h>
|
||||
#include <src/InstanceHandler.h>
|
||||
#include "QueryClient.h"
|
||||
|
||||
using namespace std;
|
||||
using namespace std::chrono;
|
||||
using namespace ts;
|
||||
using namespace ts::server;
|
||||
|
||||
extern ts::server::InstanceHandler* serverInstance;
|
||||
|
||||
bool QueryClient::eventActive(QueryEventGroup group, QueryEventSpecifier specifier) {
|
||||
return (this->eventMask[group] & (1U << specifier)) > 0;
|
||||
}
|
||||
|
||||
void QueryClient::toggleEvent(QueryEventGroup group, QueryEventSpecifier specifier, bool flag) {
|
||||
if(flag)
|
||||
this->eventMask[group] |= 1U << specifier;
|
||||
else
|
||||
this->eventMask[group] &= ~(1U << specifier);
|
||||
}
|
||||
|
||||
void QueryClient::resetEventMask() {
|
||||
memset(eventMask, 0, sizeof(eventMask) / sizeof(*eventMask) * sizeof(uint16_t));
|
||||
}
|
||||
|
||||
bool QueryClient::notifyClientNeededPermissions() {
|
||||
return true; //Query gets no needed permissions
|
||||
}
|
||||
|
||||
#define CHK_EVENT(group, specifier) \
|
||||
do { \
|
||||
if(!this->eventActive(QueryEventGroup::group, QueryEventSpecifier::specifier)) return false; \
|
||||
} while(0)
|
||||
|
||||
bool QueryClient::notifyServerUpdated(shared_ptr<ConnectedClient> ptr) {
|
||||
CHK_EVENT(QEVENTGROUP_SERVER, QEVENTSPECIFIER_SERVER_EDIT);
|
||||
return ConnectedClient::notifyServerUpdated(ptr);
|
||||
}
|
||||
|
||||
bool QueryClient::notifyClientUpdated(const std::shared_ptr<ConnectedClient> &ptr, const std::deque<std::shared_ptr<property::PropertyDescription>> &deque, bool lock_channel_tree) {
|
||||
CHK_EVENT(QEVENTGROUP_CLIENT_MISC, QEVENTSPECIFIER_CLIENT_MISC_UPDATE);
|
||||
return ConnectedClient::notifyClientUpdated(ptr, deque, lock_channel_tree);
|
||||
}
|
||||
|
||||
bool QueryClient::notifyClientPoke(std::shared_ptr<ConnectedClient> invoker, std::string msg) {
|
||||
CHK_EVENT(QEVENTGROUP_CLIENT_MISC, QEVENTSPECIFIER_CLIENT_MISC_POKE);
|
||||
return ConnectedClient::notifyClientPoke(invoker, msg);
|
||||
}
|
||||
|
||||
bool QueryClient::notifyPluginCmd(std::string name, std::string msg, std::shared_ptr<ConnectedClient> sender) {
|
||||
CHK_EVENT(QEVENTGROUP_CLIENT_MISC, QEVENTSPECIFIER_CLIENT_MISC_COMMAND);
|
||||
return ConnectedClient::notifyPluginCmd(name, msg, sender);
|
||||
}
|
||||
|
||||
bool QueryClient::notifyClientChatComposing(const shared_ptr<ConnectedClient> &ptr) {
|
||||
CHK_EVENT(QEVENTGROUP_CHAT, QEVENTSPECIFIER_CHAT_COMPOSING);
|
||||
return ConnectedClient::notifyClientChatComposing(ptr);
|
||||
}
|
||||
|
||||
bool QueryClient::notifyClientChatClosed(const shared_ptr<ConnectedClient> &ptr) {
|
||||
CHK_EVENT(QEVENTGROUP_CHAT, QEVENTSPECIFIER_CHAT_CLOSED);
|
||||
return ConnectedClient::notifyClientChatClosed(ptr);
|
||||
}
|
||||
|
||||
bool QueryClient::notifyTextMessage(ChatMessageMode mode, const shared_ptr<ConnectedClient> &sender, uint64_t targetId, const string &textMessage) {
|
||||
if(mode == ChatMessageMode::TEXTMODE_PRIVATE) CHK_EVENT(QEVENTGROUP_CHAT, QEVENTSPECIFIER_CHAT_MESSAGE_PRIVATE);
|
||||
else if(mode == ChatMessageMode::TEXTMODE_CHANNEL) CHK_EVENT(QEVENTGROUP_CHAT, QEVENTSPECIFIER_CHAT_MESSAGE_CHANNEL);
|
||||
else if(mode == ChatMessageMode::TEXTMODE_SERVER) CHK_EVENT(QEVENTGROUP_CHAT, QEVENTSPECIFIER_CHAT_MESSAGE_SERVER);
|
||||
return ConnectedClient::notifyTextMessage(mode, sender, targetId, textMessage);
|
||||
}
|
||||
|
||||
bool QueryClient::notifyServerGroupClientAdd(const shared_ptr<ConnectedClient> &invoker, const shared_ptr<ConnectedClient> &client, const shared_ptr<Group> &group) {
|
||||
CHK_EVENT(QEVENTGROUP_CLIENT_GROUPS, QEVENTSPECIFIER_CLIENT_GROUPS_ADD);
|
||||
return ConnectedClient::notifyServerGroupClientAdd(invoker, client, group);
|
||||
}
|
||||
|
||||
bool QueryClient::notifyServerGroupClientRemove(std::shared_ptr<ConnectedClient> invoker, std::shared_ptr<ConnectedClient> client, std::shared_ptr<Group> group) {
|
||||
CHK_EVENT(QEVENTGROUP_CLIENT_GROUPS, QEVENTSPECIFIER_CLIENT_GROUPS_REMOVE);
|
||||
return ConnectedClient::notifyServerGroupClientRemove(invoker, client, group);
|
||||
}
|
||||
|
||||
bool QueryClient::notifyClientChannelGroupChanged(const std::shared_ptr<ConnectedClient> &invoker, const std::shared_ptr<ConnectedClient> &client, const std::shared_ptr<BasicChannel> &ptr, const std::shared_ptr<BasicChannel> &shared_ptr,
|
||||
const std::shared_ptr<Group> &group, bool lock_channel_tree) {
|
||||
CHK_EVENT(QEVENTGROUP_CLIENT_GROUPS, QEVENTSPECIFIER_CLIENT_GROUPS_CHANNEL_CHANGED);
|
||||
return ConnectedClient::notifyClientChannelGroupChanged(invoker, client, ptr, shared_ptr, group, lock_channel_tree);
|
||||
}
|
||||
|
||||
bool QueryClient::notifyChannelMoved(const std::shared_ptr<BasicChannel> &channel, ChannelId order, const std::shared_ptr<ConnectedClient> &invoker) {
|
||||
CHK_EVENT(QEVENTGROUP_CHANNEL, QEVENTSPECIFIER_CHANNEL_MOVE);
|
||||
return ConnectedClient::notifyChannelMoved(channel, order, invoker);
|
||||
}
|
||||
|
||||
bool QueryClient::notifyChannelCreate(const std::shared_ptr<BasicChannel> &channel, ChannelId orderId,
|
||||
const std::shared_ptr<ConnectedClient> &invoker) {
|
||||
CHK_EVENT(QEVENTGROUP_CHANNEL, QEVENTSPECIFIER_CHANNEL_CREATE);
|
||||
return ConnectedClient::notifyChannelCreate(channel, orderId, invoker);
|
||||
}
|
||||
|
||||
bool QueryClient::notifyChannelDescriptionChanged(std::shared_ptr<BasicChannel> channel) {
|
||||
CHK_EVENT(QEVENTGROUP_CHANNEL, QEVENTSPECIFIER_CHANNEL_DESC_EDIT);
|
||||
return ConnectedClient::notifyChannelDescriptionChanged(channel);
|
||||
}
|
||||
|
||||
bool QueryClient::notifyChannelPasswordChanged(std::shared_ptr<BasicChannel> channel) {
|
||||
CHK_EVENT(QEVENTGROUP_CHANNEL, QEVENTSPECIFIER_CHANNEL_PASSWORD_EDIT);
|
||||
return ConnectedClient::notifyChannelPasswordChanged(channel);
|
||||
}
|
||||
|
||||
bool QueryClient::notifyChannelEdited(std::shared_ptr<BasicChannel> channel, std::vector<std::string> keys, ConnectedClient *invoker) {
|
||||
CHK_EVENT(QEVENTGROUP_CHANNEL, QEVENTSPECIFIER_CHANNEL_EDIT);
|
||||
return ConnectedClient::notifyChannelEdited(channel, keys, invoker);
|
||||
}
|
||||
|
||||
bool QueryClient::notifyChannelDeleted(const std::deque<ChannelId> &deque, const std::shared_ptr<ConnectedClient> &ptr) {
|
||||
CHK_EVENT(QEVENTGROUP_CHANNEL, QEVENTSPECIFIER_CHANNEL_DELETED);
|
||||
return ConnectedClient::notifyChannelDeleted(deque, ptr);
|
||||
}
|
||||
|
||||
bool QueryClient::notifyClientEnterView(const std::deque<std::shared_ptr<ConnectedClient>> &deque, const ViewReasonSystemT &t) {
|
||||
if(!this->eventActive(QueryEventGroup::QEVENTGROUP_CLIENT_VIEW, QueryEventSpecifier::QEVENTSPECIFIER_CLIENT_VIEW_JOIN)) {
|
||||
assert(mutex_locked(this->channel_lock));
|
||||
this->visibleClients.insert(this->visibleClients.end(), deque.begin(), deque.end());
|
||||
return true;
|
||||
} else
|
||||
return ConnectedClient::notifyClientEnterView(deque, t);
|
||||
}
|
||||
|
||||
bool QueryClient::notifyClientEnterView(const std::shared_ptr<ts::server::ConnectedClient> &client, const std::shared_ptr<ts::server::ConnectedClient> &invoker, const std::string &string, const std::shared_ptr<ts::BasicChannel> &to,
|
||||
ts::ViewReasonId reasonId, const std::shared_ptr<ts::BasicChannel> &from, bool lock) {
|
||||
if(!this->eventActive(QueryEventGroup::QEVENTGROUP_CLIENT_VIEW, QueryEventSpecifier::QEVENTSPECIFIER_CLIENT_VIEW_JOIN)) {
|
||||
assert(!lock);
|
||||
this->visibleClients.push_back(client);
|
||||
return true;
|
||||
}
|
||||
return ConnectedClient::notifyClientEnterView(client, invoker, string, to, reasonId, from, lock);
|
||||
}
|
||||
|
||||
bool QueryClient::notifyClientMoved(const std::shared_ptr<ConnectedClient> &client, const std::shared_ptr<BasicChannel> &target_channel, ViewReasonId reason, std::string msg, std::shared_ptr<ConnectedClient> invoker, bool lock_channel_tree) {
|
||||
return ConnectedClient::notifyClientMoved(client, target_channel, reason, msg, invoker, lock_channel_tree);
|
||||
}
|
||||
|
||||
bool QueryClient::notifyClientLeftView(const std::shared_ptr<ConnectedClient> &client, const std::shared_ptr<BasicChannel> &target_channel, ViewReasonId reasonId, const std::string &reasonMessage, std::shared_ptr<ConnectedClient> invoker,
|
||||
bool lock_channel_tree) {
|
||||
if(!this->eventActive(QueryEventGroup::QEVENTGROUP_CLIENT_VIEW, QueryEventSpecifier::QEVENTSPECIFIER_CLIENT_VIEW_LEAVE)) {
|
||||
this->visibleClients.erase(std::remove_if(this->visibleClients.begin(), this->visibleClients.end(), [&, client](const weak_ptr<ConnectedClient>& weak) {
|
||||
auto c = weak.lock();
|
||||
if(!c) {
|
||||
logError(this->getServerId(), "{} Got \"dead\" client in visible client list! This can cause a remote client disconnect within the future!", CLIENT_STR_LOG_PREFIX);
|
||||
return true;
|
||||
}
|
||||
return c == client;
|
||||
}), this->visibleClients.end());
|
||||
return true;
|
||||
}
|
||||
return ConnectedClient::notifyClientLeftView(client, target_channel, reasonId, reasonMessage, invoker, lock_channel_tree);
|
||||
}
|
||||
|
||||
bool QueryClient::notifyClientLeftView(const std::deque<std::shared_ptr<ConnectedClient>> &clients, const std::string &string, bool b, const ViewReasonServerLeftT &t) {
|
||||
if(!this->eventActive(QueryEventGroup::QEVENTGROUP_CLIENT_VIEW, QueryEventSpecifier::QEVENTSPECIFIER_CLIENT_VIEW_LEAVE)) {
|
||||
this->visibleClients.erase(std::remove_if(this->visibleClients.begin(), this->visibleClients.end(), [&](const weak_ptr<ConnectedClient>& weak) {
|
||||
auto c = weak.lock();
|
||||
if(!c) {
|
||||
logError(this->getServerId(), "{} Got \"dead\" client in visible client list! This can cause a remote client disconnect within the future!", CLIENT_STR_LOG_PREFIX);
|
||||
return true;
|
||||
}
|
||||
return std::find(clients.begin(), clients.end(), c) != clients.end();
|
||||
}), this->visibleClients.end());
|
||||
return true;
|
||||
}
|
||||
return ConnectedClient::notifyClientLeftView(clients, string, b, t);
|
||||
}
|
||||
|
||||
bool QueryClient::notifyClientLeftViewKicked(const std::shared_ptr<ConnectedClient> &client, const std::shared_ptr<BasicChannel> &target_channel, const std::string &message, std::shared_ptr<ConnectedClient> invoker, bool lock_channel_tree) {
|
||||
if(!this->eventActive(QueryEventGroup::QEVENTGROUP_CLIENT_VIEW, QueryEventSpecifier::QEVENTSPECIFIER_CLIENT_VIEW_LEAVE)) {
|
||||
this->visibleClients.erase(std::remove_if(this->visibleClients.begin(), this->visibleClients.end(), [&, client](const weak_ptr<ConnectedClient>& weak) {
|
||||
auto c = weak.lock();
|
||||
if(!c) {
|
||||
logError(this->getServerId(), "{} Got \"dead\" client in visible client list! This can cause a remote client disconnect within the future!", CLIENT_STR_LOG_PREFIX);
|
||||
return true;
|
||||
}
|
||||
return c == client;
|
||||
}), this->visibleClients.end());
|
||||
return true;
|
||||
}
|
||||
return ConnectedClient::notifyClientLeftViewKicked(client, target_channel, message, invoker, lock_channel_tree);
|
||||
}
|
||||
|
||||
bool QueryClient::notifyClientLeftViewBanned(const std::shared_ptr<ConnectedClient> &client, const std::string &message, std::shared_ptr<ConnectedClient> invoker, size_t length, bool lock_channel_tree) {
|
||||
if(!this->eventActive(QueryEventGroup::QEVENTGROUP_CLIENT_VIEW, QueryEventSpecifier::QEVENTSPECIFIER_CLIENT_VIEW_LEAVE)) {
|
||||
this->visibleClients.erase(std::remove_if(this->visibleClients.begin(), this->visibleClients.end(), [&, client](const weak_ptr<ConnectedClient>& weak) {
|
||||
auto c = weak.lock();
|
||||
if(!c) {
|
||||
logError(this->getServerId(), "{} Got \"dead\" client in visible client list! This can cause a remote client disconnect within the future!", CLIENT_STR_LOG_PREFIX);
|
||||
return true;
|
||||
}
|
||||
return c == client;
|
||||
}), this->visibleClients.end());
|
||||
return true;
|
||||
}
|
||||
return ConnectedClient::notifyClientLeftViewBanned(client, message, invoker, length, lock_channel_tree);
|
||||
}
|
||||
|
||||
bool QueryClient::notifyMusicQueueAdd(const std::shared_ptr<MusicClient> &bot, const std::shared_ptr<ts::music::SongInfo> &entry, int index, const std::shared_ptr<ConnectedClient> &invoker) {
|
||||
CHK_EVENT(QEVENTGROUP_MUSIC, QEVENTSPECIFIER_MUSIC_QUEUE);
|
||||
return ConnectedClient::notifyMusicQueueAdd(bot, entry, index, invoker);
|
||||
}
|
||||
|
||||
bool QueryClient::notifyMusicQueueRemove(const std::shared_ptr<MusicClient> &bot, const std::deque<std::shared_ptr<music::SongInfo>> &entry, const std::shared_ptr<ConnectedClient> &invoker) {
|
||||
CHK_EVENT(QEVENTGROUP_MUSIC, QEVENTSPECIFIER_MUSIC_QUEUE);
|
||||
return ConnectedClient::notifyMusicQueueRemove(bot, entry, invoker);
|
||||
}
|
||||
|
||||
bool QueryClient::notifyMusicQueueOrderChange(const std::shared_ptr<MusicClient> &bot, const std::shared_ptr<ts::music::SongInfo> &entry, int order, const std::shared_ptr<ConnectedClient> &invoker) {
|
||||
CHK_EVENT(QEVENTGROUP_MUSIC, QEVENTSPECIFIER_MUSIC_QUEUE);
|
||||
return ConnectedClient::notifyMusicQueueOrderChange(bot, entry, order, invoker);
|
||||
}
|
||||
|
||||
|
||||
bool QueryClient::notifyMusicPlayerSongChange(const std::shared_ptr<MusicClient> &bot, const shared_ptr<ts::music::SongInfo> &newEntry) {
|
||||
CHK_EVENT(QEVENTGROUP_MUSIC, QEVENTSPECIFIER_MUSIC_PLAYER);
|
||||
return ConnectedClient::notifyMusicPlayerSongChange(bot, newEntry);
|
||||
}
|
||||
@@ -0,0 +1,60 @@
|
||||
#ifndef XMACRO_EV
|
||||
#define XMACRO_EV_UNDEF
|
||||
#define XMACRO_EV(a, b, c, d)
|
||||
#endif
|
||||
#ifndef XMACRO_LAGACY_EV
|
||||
#define XMACRO_LAGACY_EV_UNDEF
|
||||
#define XMACRO_LAGACY_EV(var, gr, spec)
|
||||
#endif
|
||||
|
||||
XMACRO_EV("server", "edit", QEVENTGROUP_SERVER, QEVENTSPECIFIER_SERVER_EDIT)
|
||||
XMACRO_EV("client", "poke", QEVENTGROUP_CLIENT_MISC, QEVENTSPECIFIER_CLIENT_MISC_POKE)
|
||||
XMACRO_EV("client", "update", QEVENTGROUP_CLIENT_MISC, QEVENTSPECIFIER_CLIENT_MISC_UPDATE)
|
||||
XMACRO_EV("client", "command", QEVENTGROUP_CLIENT_MISC, QEVENTSPECIFIER_CLIENT_MISC_COMMAND)
|
||||
XMACRO_EV("client", "join", QEVENTGROUP_CLIENT_VIEW, QEVENTSPECIFIER_CLIENT_VIEW_JOIN)
|
||||
XMACRO_EV("client", "leave", QEVENTGROUP_CLIENT_VIEW, QEVENTSPECIFIER_CLIENT_VIEW_LEAVE)
|
||||
XMACRO_EV("client", "switch", QEVENTGROUP_CLIENT_VIEW, QEVENTSPECIFIER_CLIENT_VIEW_SWITCH)
|
||||
XMACRO_EV("client", "add", QEVENTGROUP_CLIENT_GROUPS, QEVENTSPECIFIER_CLIENT_GROUPS_ADD)
|
||||
XMACRO_EV("client", "remove", QEVENTGROUP_CLIENT_GROUPS, QEVENTSPECIFIER_CLIENT_GROUPS_REMOVE)
|
||||
XMACRO_EV("client", "change", QEVENTGROUP_CLIENT_GROUPS, QEVENTSPECIFIER_CLIENT_GROUPS_CHANNEL_CHANGED)
|
||||
XMACRO_EV("chat", "composing", QEVENTGROUP_CHAT, QEVENTSPECIFIER_CHAT_COMPOSING)
|
||||
XMACRO_EV("chat", "receive_server", QEVENTGROUP_CHAT, QEVENTSPECIFIER_CHAT_MESSAGE_SERVER)
|
||||
XMACRO_EV("chat", "receive_channel", QEVENTGROUP_CHAT, QEVENTSPECIFIER_CHAT_MESSAGE_CHANNEL)
|
||||
XMACRO_EV("chat", "receive_private", QEVENTGROUP_CHAT, QEVENTSPECIFIER_CHAT_MESSAGE_PRIVATE)
|
||||
XMACRO_EV("chat", "close", QEVENTGROUP_CHAT, QEVENTSPECIFIER_CHAT_CLOSED)
|
||||
XMACRO_EV("channel", "create", QEVENTGROUP_CHANNEL, QEVENTSPECIFIER_CHANNEL_CREATE)
|
||||
XMACRO_EV("channel", "edit", QEVENTGROUP_CHANNEL, QEVENTSPECIFIER_CHANNEL_EDIT)
|
||||
XMACRO_EV("channel", "desc", QEVENTGROUP_CHANNEL, QEVENTSPECIFIER_CHANNEL_DESC_EDIT)
|
||||
XMACRO_EV("channel", "password", QEVENTGROUP_CHANNEL, QEVENTSPECIFIER_CHANNEL_PASSWORD_EDIT)
|
||||
XMACRO_EV("channel", "move", QEVENTGROUP_CHANNEL, QEVENTSPECIFIER_CHANNEL_MOVE)
|
||||
XMACRO_EV("channel", "delete", QEVENTGROUP_CHANNEL, QEVENTSPECIFIER_CHANNEL_DELETED)
|
||||
XMACRO_EV("music", "queue", QEVENTGROUP_MUSIC, QEVENTSPECIFIER_MUSIC_QUEUE)
|
||||
XMACRO_EV("music", "player", QEVENTGROUP_MUSIC, QEVENTSPECIFIER_MUSIC_PLAYER)
|
||||
|
||||
|
||||
XMACRO_LAGACY_EV("server", QEVENTGROUP_SERVER, QEVENTSPECIFIER_SERVER_EDIT)
|
||||
XMACRO_LAGACY_EV("server", QEVENTGROUP_CLIENT_VIEW, QEVENTSPECIFIER_CLIENT_VIEW_JOIN)
|
||||
XMACRO_LAGACY_EV("server", QEVENTGROUP_CLIENT_VIEW, QEVENTSPECIFIER_CLIENT_VIEW_LEAVE)
|
||||
|
||||
XMACRO_LAGACY_EV("channel", QEVENTGROUP_CLIENT_VIEW, QEVENTSPECIFIER_CLIENT_VIEW_JOIN)
|
||||
XMACRO_LAGACY_EV("channel", QEVENTGROUP_CLIENT_VIEW, QEVENTSPECIFIER_CLIENT_VIEW_LEAVE)
|
||||
XMACRO_LAGACY_EV("channel", QEVENTGROUP_CLIENT_VIEW, QEVENTSPECIFIER_CLIENT_VIEW_SWITCH)
|
||||
XMACRO_LAGACY_EV("channel", QEVENTGROUP_CHANNEL, QEVENTSPECIFIER_CHANNEL_CREATE)
|
||||
XMACRO_LAGACY_EV("channel", QEVENTGROUP_CHANNEL, QEVENTSPECIFIER_CHANNEL_EDIT)
|
||||
XMACRO_LAGACY_EV("channel", QEVENTGROUP_CHANNEL, QEVENTSPECIFIER_CHANNEL_DELETED)
|
||||
XMACRO_LAGACY_EV("channel", QEVENTGROUP_CHANNEL, QEVENTSPECIFIER_CHANNEL_MOVE)
|
||||
XMACRO_LAGACY_EV("channel", QEVENTGROUP_CHANNEL, QEVENTSPECIFIER_CHANNEL_DESC_EDIT)
|
||||
XMACRO_LAGACY_EV("channel", QEVENTGROUP_CHANNEL, QEVENTSPECIFIER_CHANNEL_PASSWORD_EDIT)
|
||||
|
||||
XMACRO_LAGACY_EV("textserver", QEVENTGROUP_CHAT, QEVENTSPECIFIER_CHAT_MESSAGE_SERVER)
|
||||
XMACRO_LAGACY_EV("textchannel", QEVENTGROUP_CHAT, QEVENTSPECIFIER_CHAT_MESSAGE_CHANNEL)
|
||||
XMACRO_LAGACY_EV("textprivate", QEVENTGROUP_CHAT, QEVENTSPECIFIER_CHAT_MESSAGE_PRIVATE)
|
||||
|
||||
#ifdef XMACRO_EV_UNDEF
|
||||
#undef XMACRO_EV_UNDEF
|
||||
#undef XMACRO_EV
|
||||
#endif
|
||||
#ifdef XMACRO_LAGACY_EV_UNDEF
|
||||
#undef XMACRO_LAGACY_EV_UNDEF
|
||||
#undef XMACRO_LAGACY_EV
|
||||
#endif
|
||||
Reference in New Issue
Block a user