First draft of the new snapshot system and database changes
This commit is contained in:
@@ -391,8 +391,8 @@ bool ConnectedClient::handle_text_command(
|
||||
for(const auto& song : bot_playlist->list_songs()) {
|
||||
string invoker = "unknown";
|
||||
for(const auto& e : dbinfo) {
|
||||
if(e->cldbid == song->invoker) {
|
||||
invoker = "[URL=client://0/" + e->uniqueId + "~" + e->lastName + "]" + e->lastName + "[/URL]";
|
||||
if(e->client_database_id == song->invoker) {
|
||||
invoker = "[URL=client://0/" + e->client_unique_id + "~" + e->client_nickname + "]" + e->client_nickname + "[/URL]";
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,11 +1,10 @@
|
||||
#include <log/LogUtils.h>
|
||||
#include <src/client/voice/VoiceClient.h>
|
||||
#include <misc/sassert.h>
|
||||
#include <misc/hex.h>
|
||||
#include "DataClient.h"
|
||||
#include "ConnectedClient.h"
|
||||
#include "src/InstanceHandler.h"
|
||||
#include "misc/base64.h"
|
||||
#include <misc/base64.h>
|
||||
|
||||
#include "./DataClient.h"
|
||||
#include "../InstanceHandler.h"
|
||||
|
||||
using namespace std;
|
||||
using namespace ts;
|
||||
@@ -22,52 +21,78 @@ DataClient::~DataClient() {
|
||||
this->_properties = nullptr;
|
||||
}
|
||||
|
||||
bool DataClient::loadDataForCurrentServer() { //TODO for query
|
||||
if(this->getUid().empty()) return false;
|
||||
constexpr static std::string_view kClientLoadCommand{R"(
|
||||
SELECT
|
||||
`client_database_id`,
|
||||
`client_created`,
|
||||
|
||||
`client_last_connected`,
|
||||
`client_total_connections`,
|
||||
|
||||
`client_month_upload`,
|
||||
`client_month_download`,
|
||||
|
||||
`client_total_upload`,
|
||||
`client_total_download`
|
||||
FROM `clients_server` WHERE `server_id` = :sid AND `client_unique_id` = :uid
|
||||
)"};
|
||||
|
||||
bool DataClient::loadDataForCurrentServer() {
|
||||
auto uniqueId = this->getUid();
|
||||
if(uniqueId.empty())
|
||||
return false;
|
||||
|
||||
auto ref_server = this->server;
|
||||
auto server_id = ref_server ? ref_server->getServerId() : 0;
|
||||
|
||||
properties()[property::CLIENT_DATABASE_ID] = 0;
|
||||
properties()[property::CLIENT_CREATED] = 0;
|
||||
properties()[property::CLIENT_TOTALCONNECTIONS] = 0;
|
||||
this->clientPermissions = std::make_shared<permission::v2::PermissionManager>();
|
||||
|
||||
auto properties = this->properties();
|
||||
sql::command{this->sql, std::string{kClientLoadCommand}, variable{":uid", uniqueId}, variable{":sid", server_id}}.query([&](int length, std::string* values, std::string* names) {
|
||||
auto index{0};
|
||||
|
||||
ClientDbId client_db_id = 0;
|
||||
sql::command(this->sql, "SELECT `cldbid`,`firstConnect`,`connections` FROM `clients` WHERE `serverId` = :sid AND `clientUid` = :uid LIMIT 1",
|
||||
variable{":sid", server_id},
|
||||
variable{":uid", this->getUid()}
|
||||
).query([&](DataClient* cl, int length, string* values, string* names){
|
||||
for (int index = 0; index < length; index++) {
|
||||
try {
|
||||
if (names[index] == "cldbid") {
|
||||
client_db_id = stoull(values[index]);
|
||||
} else if (names[index] == "firstConnect") {
|
||||
cl->properties()[property::CLIENT_CREATED] = values[index];
|
||||
} else if (names[index] == "connections") {
|
||||
cl->properties()[property::CLIENT_TOTALCONNECTIONS] = values[index];
|
||||
} else {
|
||||
logWarning(LOG_INSTANCE, "Received unknown column with name {} within client list", names[index]);
|
||||
}
|
||||
} catch(const std::exception& ex) {
|
||||
logError(server_id, "Failed to load client {} base properties from database. Colum parsing for column {} failed. Value: {}. Message: {}",
|
||||
this->getUid(),
|
||||
names[index],
|
||||
values[index],
|
||||
ex.what()
|
||||
);
|
||||
return 0;
|
||||
}
|
||||
try {
|
||||
assert(names[index] == "client_database_id");
|
||||
properties[property::CLIENT_DATABASE_ID] = std::stoull(values[index++]);
|
||||
|
||||
assert(names[index] == "client_created");
|
||||
properties[property::CLIENT_CREATED] = std::stoull(values[index++]);
|
||||
|
||||
assert(names[index] == "client_last_connected");
|
||||
properties[property::CLIENT_LASTCONNECTED] = std::stoull(values[index++]);
|
||||
|
||||
assert(names[index] == "client_total_connections");
|
||||
properties[property::CLIENT_TOTALCONNECTIONS] = std::stoull(values[index++]);
|
||||
|
||||
assert(names[index] == "client_month_upload");
|
||||
properties[property::CLIENT_MONTH_BYTES_UPLOADED] = std::stoull(values[index++]);
|
||||
|
||||
assert(names[index] == "client_month_download");
|
||||
properties[property::CLIENT_MONTH_BYTES_DOWNLOADED] = std::stoull(values[index++]);
|
||||
|
||||
assert(names[index] == "client_total_upload");
|
||||
properties[property::CLIENT_TOTAL_BYTES_UPLOADED] = std::stoull(values[index++]);
|
||||
|
||||
assert(names[index] == "client_total_download");
|
||||
properties[property::CLIENT_TOTAL_BYTES_DOWNLOADED] = std::stoull(values[index++]);
|
||||
|
||||
assert(index == length);
|
||||
} catch (std::exception& ex) {
|
||||
logError(server_id, "Failed to load client {} base properties from database: {} (Index {})",
|
||||
this->getUid(),
|
||||
ex.what(),
|
||||
index - 1
|
||||
);
|
||||
properties[property::CLIENT_DATABASE_ID] = 0;
|
||||
}
|
||||
return 0;
|
||||
}, this);
|
||||
});
|
||||
|
||||
if(client_db_id == 0)
|
||||
if(this->properties()[property::CLIENT_DATABASE_ID].as<ClientDbId>() == 0)
|
||||
return false;
|
||||
|
||||
this->properties()[property::CLIENT_DATABASE_ID] = client_db_id; /* do this before the property saving (it saved the cldbid as well!)*/
|
||||
|
||||
//Load general properties
|
||||
deque<ts::PropertyWrapper> copied;
|
||||
std::deque<ts::PropertyWrapper> copied;
|
||||
for(const auto& prop : this->_properties->list_properties()){
|
||||
if((prop.type().flags & property::FLAG_GLOBAL) == 0) continue;
|
||||
if(prop.type().default_value == prop.value()) continue;
|
||||
@@ -80,28 +105,7 @@ bool DataClient::loadDataForCurrentServer() { //TODO for query
|
||||
return false;
|
||||
}
|
||||
|
||||
auto client_type = this->getType();
|
||||
if(client_type == CLIENT_TEAMSPEAK || ref_server) {
|
||||
this->_properties = serverInstance->databaseHelper()->loadClientProperties(ref_server, this->getClientDatabaseId(), client_type);
|
||||
} else {
|
||||
this->_properties = DatabaseHelper::default_properties_client(nullptr, client_type);
|
||||
|
||||
this->_properties->registerNotifyHandler([&, server_id, client_db_id](Property& prop){
|
||||
std::string query;
|
||||
if(prop.type() == property::CLIENT_TOTALCONNECTIONS)
|
||||
query = "UPDATE `clients` SET `connections` = :value WHERE `serverId` = :sid AND `cldbid` = :cldbid";
|
||||
else if(prop.type() == property::CLIENT_NICKNAME)
|
||||
query = "UPDATE `clients` SET `lastName` = :value WHERE `serverId` = :sid AND `cldbid` = :cldbid";
|
||||
else if(prop.type() == property::CLIENT_LASTCONNECTED)
|
||||
query = "UPDATE `clients` SET `lastConnect` = :value WHERE `serverId` = :sid AND `cldbid` = :cldbid";
|
||||
else
|
||||
return;
|
||||
|
||||
debugMessage(server_id, "[Property] Updating general client table property for client {}. Key: {} Value: {}", client_db_id, prop.type().name, prop.value());
|
||||
sql::command(this->sql, query, variable{":sid", 0}, variable{":cldbid", client_db_id}, variable{":value", prop.value()}).executeLater()
|
||||
.waitAndGetLater(LOG_SQL_CMD, {1, "failed to update general client properties"});
|
||||
});
|
||||
}
|
||||
this->_properties = serverInstance->databaseHelper()->loadClientProperties(ref_server, this->getClientDatabaseId(), this->getType());
|
||||
|
||||
this->_properties->toggleSave(false);
|
||||
for(const auto& e : copied) {
|
||||
|
||||
@@ -367,18 +367,22 @@ command_result ConnectedClient::handleCommandClientChatClosed(Command &cmd) {
|
||||
return command_result{error::ok};
|
||||
}
|
||||
|
||||
//start=0 duration=10
|
||||
//pattern=%asd%
|
||||
|
||||
struct ClientDbArgs {
|
||||
shared_ptr<VirtualServer> server;
|
||||
int index = 0;
|
||||
int offset = 0;
|
||||
int resultIndex = 0;
|
||||
bool showIp = false;
|
||||
bool largeInfo = false;
|
||||
Command *result = nullptr;
|
||||
};
|
||||
constexpr static auto kDBListQuery{R"(
|
||||
SELECT `clients`.*, `properties`.`value` as `client_description` FROM (
|
||||
SELECT
|
||||
`clients_server`.`client_database_id`,
|
||||
`clients_server`.`client_unique_id`,
|
||||
`clients_server`.`client_nickname`,
|
||||
`clients_server`.`client_ip`,
|
||||
`clients_server`.`client_created`,
|
||||
`clients_server`.`client_last_connected`,
|
||||
`clients_server`.`client_total_connections`,
|
||||
`clients`.`client_login_name` FROM `clients_server`
|
||||
INNER JOIN `clients` ON `clients`.`client_database_id` = `clients_server`.`client_database_id`
|
||||
WHERE `server_id` = :serverId LIMIT :offset, :limit
|
||||
) AS `clients`
|
||||
LEFT JOIN `properties` ON `properties`.serverId = :serverId AND `properties`.key = 'client_description' AND `properties`.`id` = `clients`.`client_database_id`
|
||||
)"};
|
||||
|
||||
command_result ConnectedClient::handleCommandClientDbList(Command &cmd) {
|
||||
CMD_REQ_SERVER;
|
||||
@@ -386,86 +390,79 @@ command_result ConnectedClient::handleCommandClientDbList(Command &cmd) {
|
||||
CMD_CHK_AND_INC_FLOOD_POINTS(25);
|
||||
ACTION_REQUIRES_GLOBAL_PERMISSION(permission::b_virtualserver_client_dblist, 1);
|
||||
|
||||
Command notify(this->getExternalType() == CLIENT_TEAMSPEAK ? "notifyclientdblist" : "");
|
||||
size_t offset = cmd[0].has("start") ? cmd["start"].as<size_t>() : 0;
|
||||
size_t limit = cmd[0].has("duration") ? cmd["duration"].as<int>() : 0;
|
||||
if(limit > 2000 || limit < 1)
|
||||
limit = 2000;
|
||||
|
||||
if (!cmd[0].has("start"))
|
||||
cmd["start"] = 0;
|
||||
if (!cmd[0].has("duration"))
|
||||
cmd["duration"] = 20;
|
||||
if (cmd[0]["duration"].as<int>() > 2000) cmd["duration"] = 2000;
|
||||
if (cmd[0]["duration"].as<int>() < 1) cmd["duration"] = 1;
|
||||
ts::command_builder result{this->notify_response_command("notifyclientdblist")};
|
||||
result.reserve_bulks(limit);
|
||||
|
||||
auto maxIndex = cmd["start"].as<uint32_t>() + cmd["duration"].as<uint32_t>();
|
||||
ClientDbArgs args;
|
||||
args.server = this->server;
|
||||
args.offset = cmd["start"].as<uint32_t>();
|
||||
args.result = ¬ify;
|
||||
args.resultIndex = 0;
|
||||
args.index = 0;
|
||||
args.showIp = permission::v2::permission_granted(1, this->calculate_permission(permission::b_client_remoteaddress_view, 0));
|
||||
args.largeInfo = cmd.hasParm("details");
|
||||
size_t command_index{0}, set_index{0};
|
||||
|
||||
(LOG_SQL_CMD)(sql::command(this->server->getSql(), "SELECT * FROM `clients` WHERE `serverId` = :sid ORDER BY `cldbid` ASC" + (maxIndex > 0 ? " LIMIT " + to_string(maxIndex) : ""), variable{":sid", this->server->getServerId()}).query(
|
||||
[](ClientDbArgs *pArgs, int length, char **values, char **column) {
|
||||
pArgs->index++;
|
||||
if (pArgs->offset < pArgs->index) {
|
||||
ClientDbId id = 0;
|
||||
string uid, name, ip;
|
||||
string created = "0", lastConnected = "0", connections = "0";
|
||||
for (int index = 0; index < length; index++) {
|
||||
string key = column[index];
|
||||
if (key == "cldbid")
|
||||
id = stoll(values[index]);
|
||||
else if (key == "clientUid")
|
||||
uid = values[index];
|
||||
else if (key == "firstConnect")
|
||||
created = values[index];
|
||||
else if (key == "lastConnect")
|
||||
lastConnected = values[index];
|
||||
else if (key == "connections")
|
||||
connections = values[index];
|
||||
else if (key == "lastName")
|
||||
name = values[index];
|
||||
}
|
||||
auto show_ip = permission::v2::permission_granted(1, this->calculate_permission(permission::b_client_remoteaddress_view, 0));
|
||||
|
||||
pArgs->result->operator[](pArgs->resultIndex)["cldbid"] = id;
|
||||
pArgs->result->operator[](pArgs->resultIndex)["client_unique_identifier"] = uid;
|
||||
pArgs->result->operator[](pArgs->resultIndex)["client_nickname"] = name;
|
||||
pArgs->result->operator[](pArgs->resultIndex)["client_created"] = created;
|
||||
pArgs->result->operator[](pArgs->resultIndex)["client_lastconnected"] = lastConnected;
|
||||
pArgs->result->operator[](pArgs->resultIndex)["client_totalconnections"] = connections;
|
||||
pArgs->result->operator[](pArgs->resultIndex)["client_description"] = "";
|
||||
auto sqlResult = sql::command{this->server->getSql(), kDBListQuery, variable{":serverId", this->getServerId()}, variable{":offset", offset}, variable{":limit", limit}}
|
||||
.query([&](int length, std::string* values, std::string* names) {
|
||||
set_index++;
|
||||
auto bulk = result.bulk(command_index++);
|
||||
bulk.reserve(300);
|
||||
|
||||
auto props = serverInstance->databaseHelper()->loadClientProperties(pArgs->server, id, ClientType::CLIENT_TEAMSPEAK);
|
||||
if (props) {
|
||||
pArgs->result->operator[](pArgs->resultIndex)["client_lastip"] = (*props)[property::CONNECTION_CLIENT_IP].as<string>();
|
||||
pArgs->result->operator[](pArgs->resultIndex)["client_description"] = (*props)[property::CLIENT_DESCRIPTION].as<string>();
|
||||
auto index{0};
|
||||
try {
|
||||
assert(names[index] == "client_database_id");
|
||||
bulk.put_unchecked("cldbid", values[index++]);
|
||||
|
||||
if (pArgs->largeInfo) {
|
||||
pArgs->result->operator[](pArgs->resultIndex)["client_badges"] = (*props)[property::CLIENT_BADGES].as<string>();
|
||||
pArgs->result->operator[](pArgs->resultIndex)["client_version"] = (*props)[property::CLIENT_VERSION].as<string>();
|
||||
pArgs->result->operator[](pArgs->resultIndex)["client_platform"] = (*props)[property::CLIENT_PLATFORM].as<string>();
|
||||
pArgs->result->operator[](pArgs->resultIndex)["client_hwid"] = (*props)[property::CLIENT_HARDWARE_ID].as<string>();
|
||||
}
|
||||
}
|
||||
if (!pArgs->showIp)
|
||||
pArgs->result->operator[](pArgs->resultIndex)["client_lastip"] = "hidden";
|
||||
pArgs->resultIndex++;
|
||||
}
|
||||
return 0;
|
||||
}, &args));
|
||||
assert(names[index] == "client_unique_id");
|
||||
bulk.put_unchecked("client_unique_identifier", values[index++]);
|
||||
|
||||
assert(names[index] == "client_nickname");
|
||||
bulk.put_unchecked("client_nickname", values[index++]);
|
||||
|
||||
assert(names[index] == "client_ip");
|
||||
bulk.put_unchecked("client_lastip", show_ip ? values[index++] : "hidden");
|
||||
|
||||
assert(names[index] == "client_created");
|
||||
bulk.put_unchecked("client_lastconnected", values[index++]);
|
||||
|
||||
assert(names[index] == "client_last_connected");
|
||||
bulk.put_unchecked("client_created", values[index++]);
|
||||
|
||||
assert(names[index] == "client_total_connections");
|
||||
bulk.put_unchecked("client_totalconnections", values[index++]);
|
||||
|
||||
assert(names[index] == "client_login_name");
|
||||
bulk.put_unchecked("client_login_name", values[index++]);
|
||||
|
||||
assert(names[index] == "client_description");
|
||||
bulk.put_unchecked("client_description", values[index++]);
|
||||
|
||||
assert(index == length);
|
||||
} catch (std::exception& ex) {
|
||||
command_index--;
|
||||
logError(this->getServerId(), "Failed to parse client base properties at index {}: {}. Offset: {} Limit: {} Set: {}",
|
||||
index - 1,
|
||||
ex.what(),
|
||||
offset,
|
||||
limit,
|
||||
set_index - 1
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
if (command_index == 0)
|
||||
return command_result{error::database_empty_result};
|
||||
|
||||
if (args.resultIndex == 0) return command_result{error::database_empty_result};
|
||||
if (cmd.hasParm("count")) {
|
||||
size_t result = 0;
|
||||
sql::command(this->server->getSql(), "SELECT COUNT(*) AS `count` FROM `clients` WHERE `serverId` = :sid", variable{":sid", this->server->getServerId()}).query([](size_t *ptr, int, char **v, char **) {
|
||||
*ptr = static_cast<size_t>(stoll(v[0]));
|
||||
return 0;
|
||||
}, &result);
|
||||
notify[0]["count"] = result;
|
||||
size_t count{0};
|
||||
sql::command(this->server->getSql(), "SELECT COUNT(`client_database_id`) AS `count` FROM `clients_server` WHERE `server_id` = :sid", variable{":sid", this->server->getServerId()})
|
||||
.query([&](int, std::string* v, std::string*) {
|
||||
count = stoll(v[0]);
|
||||
});
|
||||
result.put_unchecked(0, "count", count);
|
||||
}
|
||||
this->sendCommand(notify);
|
||||
|
||||
this->sendCommand(result);
|
||||
return command_result{error::ok};
|
||||
}
|
||||
|
||||
@@ -869,8 +866,9 @@ command_result ConnectedClient::handleCommandClientGetDBIDfromUID(Command &cmd)
|
||||
CMD_RESET_IDLE;
|
||||
|
||||
deque<string> unique_ids;
|
||||
for(int index = 0; index < cmd.bulkCount(); index++)
|
||||
unique_ids.push_back(cmd[index]["cluid"].as<string>());
|
||||
for(int index = 0; index < cmd.bulkCount(); index++) {
|
||||
unique_ids.push_back(cmd[index]["cluid"]);
|
||||
}
|
||||
|
||||
auto res = serverInstance->databaseHelper()->queryDatabaseInfoByUid(this->server, unique_ids);
|
||||
if (res.empty()) return command_result{error::database_empty_result};
|
||||
@@ -878,8 +876,8 @@ command_result ConnectedClient::handleCommandClientGetDBIDfromUID(Command &cmd)
|
||||
Command result(this->getExternalType() == CLIENT_TEAMSPEAK ? "notifyclientdbidfromuid" : "");
|
||||
int result_index = 0;
|
||||
for(auto& info : res) {
|
||||
result[result_index]["cluid"] = info->uniqueId;
|
||||
result[result_index]["cldbid"] = info->cldbid;
|
||||
result[result_index]["cluid"] = info->client_unique_id;
|
||||
result[result_index]["cldbid"] = info->client_database_id;
|
||||
result_index++;
|
||||
}
|
||||
this->sendCommand(result);
|
||||
@@ -900,10 +898,10 @@ command_result ConnectedClient::handleCommandClientGetNameFromDBID(Command &cmd)
|
||||
Command result(this->getExternalType() == CLIENT_TEAMSPEAK ? "notifyclientgetnamefromdbid" : "");
|
||||
int result_index = 0;
|
||||
for(auto& info : res) {
|
||||
result[result_index]["cluid"] = info->uniqueId;
|
||||
result[result_index]["cldbid"] = info->cldbid;
|
||||
result[result_index]["name"] = info->lastName;
|
||||
result[result_index]["clname"] = info->lastName;
|
||||
result[result_index]["cluid"] = info->client_unique_id;
|
||||
result[result_index]["cldbid"] = info->client_database_id;
|
||||
result[result_index]["name"] = info->client_nickname;
|
||||
result[result_index]["clname"] = info->client_nickname;
|
||||
result_index++;
|
||||
}
|
||||
this->sendCommand(result);
|
||||
@@ -924,10 +922,10 @@ command_result ConnectedClient::handleCommandClientGetNameFromUid(Command &cmd)
|
||||
Command result(this->getExternalType() == CLIENT_TEAMSPEAK ? "notifyclientnamefromuid" : "");
|
||||
int result_index = 0;
|
||||
for(auto& info : res) {
|
||||
result[result_index]["cluid"] = info->uniqueId;
|
||||
result[result_index]["cldbid"] = info->cldbid;
|
||||
result[result_index]["name"] = info->lastName;
|
||||
result[result_index]["clname"] = info->lastName;
|
||||
result[result_index]["cluid"] = info->client_unique_id;
|
||||
result[result_index]["cldbid"] = info->client_database_id;
|
||||
result[result_index]["name"] = info->client_nickname;
|
||||
result[result_index]["clname"] = info->client_nickname;
|
||||
result_index++;
|
||||
}
|
||||
this->sendCommand(result);
|
||||
@@ -1086,16 +1084,16 @@ command_result ConnectedClient::handleCommandClientDbInfo(Command &cmd) {
|
||||
|
||||
size_t index = 0;
|
||||
for(const auto& info : basic) {
|
||||
res[index]["client_base64HashClientUID"] = hex::hex(base64::validate(info->uniqueId) ? base64::decode(info->uniqueId) : info->uniqueId, 'a', 'q');
|
||||
res[index]["client_unique_identifier"] = info->uniqueId;
|
||||
res[index]["client_nickname"] = info->lastName;
|
||||
res[index]["client_database_id"] = info->cldbid;
|
||||
res[index]["client_created"] = chrono::duration_cast<chrono::seconds>(info->created.time_since_epoch()).count();
|
||||
res[index]["client_lastconnected"] = chrono::duration_cast<chrono::seconds>(info->lastjoin.time_since_epoch()).count();
|
||||
res[index]["client_totalconnections"] = info->connections;
|
||||
res[index]["client_database_id"] = info->cldbid;
|
||||
res[index]["client_base64HashClientUID"] = hex::hex(base64::validate(info->client_unique_id) ? base64::decode(info->client_unique_id) : info->client_unique_id, 'a', 'q');
|
||||
res[index]["client_unique_identifier"] = info->client_unique_id;
|
||||
res[index]["client_nickname"] = info->client_nickname;
|
||||
res[index]["client_database_id"] = info->client_database_id;
|
||||
res[index]["client_created"] = chrono::duration_cast<chrono::seconds>(info->client_created.time_since_epoch()).count();
|
||||
res[index]["client_lastconnected"] = chrono::duration_cast<chrono::seconds>(info->client_last_connected.time_since_epoch()).count();
|
||||
res[index]["client_totalconnections"] = info->client_total_connections;
|
||||
res[index]["client_database_id"] = info->client_database_id;
|
||||
|
||||
auto props = serverInstance->databaseHelper()->loadClientProperties(this->server, info->cldbid, ClientType::CLIENT_TEAMSPEAK);
|
||||
auto props = serverInstance->databaseHelper()->loadClientProperties(this->server, info->client_database_id, ClientType::CLIENT_TEAMSPEAK);
|
||||
if (allow_ip)
|
||||
res[index]["client_lastip"] = (*props)[property::CONNECTION_CLIENT_IP].as<string>();
|
||||
else
|
||||
@@ -1136,13 +1134,6 @@ command_result ConnectedClient::handleCommandClientDBDelete(Command &cmd) {
|
||||
return command_result{error::ok};
|
||||
}
|
||||
|
||||
struct DBFindArgs {
|
||||
int index = 0;
|
||||
bool full = false;
|
||||
bool ip = false;
|
||||
Command cmd{""};
|
||||
};
|
||||
|
||||
command_result ConnectedClient::handleCommandClientDBFind(Command &cmd) {
|
||||
CMD_REQ_SERVER;
|
||||
CMD_RESET_IDLE;
|
||||
@@ -1152,48 +1143,73 @@ command_result ConnectedClient::handleCommandClientDBFind(Command &cmd) {
|
||||
bool uid = cmd.hasParm("uid");
|
||||
string pattern = cmd["pattern"];
|
||||
|
||||
DBFindArgs args{};
|
||||
args.cmd = Command(this->getType() == CLIENT_QUERY ? "" : "notifyclientdbfind");
|
||||
args.full = cmd.hasParm("details");
|
||||
args.ip = permission::v2::permission_granted(1, this->calculate_permission(permission::b_client_remoteaddress_view, 0));
|
||||
auto res = sql::command(this->sql, "SELECT * FROM `clients` WHERE `serverId` = :sid AND `" + std::string{uid ? "clientUid" : "lastName"} + "` LIKE :pattern LIMIT 50",
|
||||
variable{":sid", this->server->getServerId()},
|
||||
variable{":pattern", pattern}).query(
|
||||
[&](DBFindArgs *ptr, int len, char **values, char **names) {
|
||||
for (int index = 0; index < len; index++)
|
||||
if (strcmp(names[index], "cldbid") == 0)
|
||||
ptr->cmd[ptr->index]["cldbid"] = values[index];
|
||||
else if (strcmp(names[index], "clientUid") == 0 && ptr->full)
|
||||
ptr->cmd[ptr->index]["client_unique_identifier"] = values[index];
|
||||
else if (strcmp(names[index], "lastConnect") == 0 && ptr->full)
|
||||
ptr->cmd[ptr->index]["client_lastconnected"] = values[index];
|
||||
else if (strcmp(names[index], "connections") == 0 && ptr->full)
|
||||
ptr->cmd[ptr->index]["client_totalconnections"] = values[index];
|
||||
else if (strcmp(names[index], "lastName") == 0 && ptr->full)
|
||||
ptr->cmd[ptr->index]["client_nickname"] = values[index];
|
||||
if (ptr->full) {
|
||||
auto props = serverInstance->databaseHelper()->loadClientProperties(this->server, ptr->cmd[ptr->index]["cldbid"], ClientType::CLIENT_TEAMSPEAK);
|
||||
if (props) {
|
||||
if (ptr->ip) {
|
||||
ptr->cmd[ptr->index]["client_lastip"] = (*props)[property::CONNECTION_CLIENT_IP].as<string>();
|
||||
} else {
|
||||
ptr->cmd[ptr->index]["client_lastip"] = "hidden";
|
||||
}
|
||||
ptr->cmd[ptr->index]["client_badges"] = (*props)[property::CLIENT_BADGES].as<string>();
|
||||
ptr->cmd[ptr->index]["client_version"] = (*props)[property::CLIENT_VERSION].as<string>();
|
||||
ptr->cmd[ptr->index]["client_platform"] = (*props)[property::CLIENT_PLATFORM].as<string>();
|
||||
ptr->cmd[ptr->index]["client_hwid"] = (*props)[property::CLIENT_HARDWARE_ID].as<string>();
|
||||
}
|
||||
const auto detailed = cmd.hasParm("details");
|
||||
const auto show_ip = permission::v2::permission_granted(1, this->calculate_permission(permission::b_client_remoteaddress_view, 0));
|
||||
|
||||
size_t command_index{0};
|
||||
ts::command_builder result{this->notify_response_command("notifyclientdbfind")};
|
||||
result.reserve_bulks(50);
|
||||
|
||||
constexpr static auto kBaseCommand{"SELECT `client_database_id`, `client_unique_id`, `client_nickname`, `client_ip`, `client_last_connected`, `client_total_connections` FROM `clients_server` WHERE "};
|
||||
|
||||
auto sql_result = sql::command{this->sql, std::string{kBaseCommand} + "`server_id` = :sid AND " + (uid ? "`client_unique_id`" : "`client_nickname`") + " LIKE :pattern LIMIT 50", variable{":sid", this->getServerId()}, variable{":pattern", pattern}}
|
||||
.query([&](int length, std::string* values, std::string* names) {
|
||||
auto bulk = result.bulk(command_index++);
|
||||
bulk.reserve(300);
|
||||
|
||||
auto index{0};
|
||||
ClientDbId client_database_id;
|
||||
try {
|
||||
assert(names[index] == "client_database_id");
|
||||
client_database_id = std::stoull(values[index]);
|
||||
bulk.put_unchecked("cldbid", values[index++]);
|
||||
|
||||
assert(names[index] == "client_unique_id");
|
||||
bulk.put_unchecked("client_unique_identifier", values[index++]);
|
||||
|
||||
assert(names[index] == "client_nickname");
|
||||
bulk.put_unchecked("client_nickname", values[index++]);
|
||||
|
||||
assert(names[index] == "client_ip");
|
||||
if(detailed) {
|
||||
bulk.put_unchecked("client_lastip", show_ip ? values[index++] : "hidden");
|
||||
} else {
|
||||
index++;
|
||||
}
|
||||
ptr->index++;
|
||||
return 0;
|
||||
}, &args);
|
||||
auto pf = LOG_SQL_CMD;
|
||||
pf(res);
|
||||
if (args.index == 0) return command_result{error::database_empty_result};
|
||||
|
||||
this->sendCommand(args.cmd);
|
||||
assert(names[index] == "client_last_connected");
|
||||
bulk.put_unchecked("client_lastconnected", values[index++]);
|
||||
|
||||
assert(names[index] == "client_total_connections");
|
||||
bulk.put_unchecked("client_totalconnections", values[index++]);
|
||||
|
||||
assert(index == length);
|
||||
} catch (std::exception& ex) {
|
||||
command_index--;
|
||||
logError(this->getServerId(), "Failed to parse client base properties at index {}: {}. Search pattern: {}",
|
||||
index - 1,
|
||||
ex.what(),
|
||||
pattern
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
if(detailed) {
|
||||
auto props = serverInstance->databaseHelper()->loadClientProperties(this->server, client_database_id, ClientType::CLIENT_TEAMSPEAK);
|
||||
if (props) {
|
||||
auto& properties = *props;
|
||||
bulk.put_unchecked("client_badges", properties[property::CLIENT_BADGES].as<std::string>());
|
||||
bulk.put_unchecked("client_version", properties[property::CLIENT_VERSION].as<std::string>());
|
||||
bulk.put_unchecked("client_platform", properties[property::CLIENT_PLATFORM].as<std::string>());
|
||||
bulk.put_unchecked("client_hwid", properties[property::CLIENT_HARDWARE_ID].as<std::string>());
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
if(command_index == 0)
|
||||
return command_result{error::database_empty_result};
|
||||
|
||||
this->sendCommand(result);
|
||||
return command_result{error::ok};
|
||||
}
|
||||
|
||||
|
||||
@@ -714,7 +714,7 @@ command_result ConnectedClient::handleCommandBanAdd(Command &cmd) {
|
||||
return command_result{permission::b_client_ban_create};
|
||||
}
|
||||
|
||||
auto max_ban_time = server->calculate_permission(permission::i_client_ban_max_bantime, this->getClientDatabaseId(), this->getType(), 0);
|
||||
auto max_ban_time = this->calculate_permission(permission::i_client_ban_max_bantime, this->getClientDatabaseId(), this->getType(), 0);
|
||||
if(!max_ban_time.has_value) return command_result{permission::i_client_ban_max_bantime};
|
||||
if (!max_ban_time.has_infinite_power()) {
|
||||
if (max_ban_time.value < time)
|
||||
@@ -1884,10 +1884,10 @@ command_result ConnectedClient::handleCommandComplainList(Command &cmd) {
|
||||
result[index]["timestamp"] = chrono::duration_cast<chrono::seconds>(elm->created.time_since_epoch()).count();
|
||||
|
||||
for (const auto &e : dbInfo) {
|
||||
if (e->cldbid == elm->target)
|
||||
result[index]["tname"] = e->lastName;
|
||||
if (e->cldbid == elm->invoker)
|
||||
result[index]["fname"] = e->lastName;
|
||||
if (e->client_database_id == elm->target)
|
||||
result[index]["tname"] = e->client_nickname;
|
||||
if (e->client_database_id == elm->invoker)
|
||||
result[index]["fname"] = e->client_nickname;
|
||||
}
|
||||
index++;
|
||||
}
|
||||
@@ -2351,7 +2351,7 @@ command_result ConnectedClient::handleCommandQueryCreate(ts::Command &cmd) {
|
||||
auto info = serverInstance->databaseHelper()->queryDatabaseInfo(server, {cmd["cldbid"].as<ClientDbId>()});
|
||||
if(info.empty())
|
||||
return command_result{error::database_empty_result};
|
||||
uid = info[0]->uniqueId;
|
||||
uid = info[0]->client_unique_id;
|
||||
} else {
|
||||
if(server) {
|
||||
if(!permission::v2::permission_granted(1, server->calculate_permission(permission::b_client_query_create_own, this->getClientDatabaseId(), this->getType(), 0)))
|
||||
|
||||
@@ -98,7 +98,7 @@ void MusicClient::replay_song(const shared_ptr<music::PlayableSong> &entry, cons
|
||||
auto info_list = serverInstance->databaseHelper()->queryDatabaseInfo(self->getServer(), {entry->getInvoker()});
|
||||
if(!info_list.empty()) {
|
||||
auto info = info_list.front();
|
||||
invoker = "[URL=client://0/" + info->uniqueId + "~WolverinDEV]" + info->lastName + "[/URL]";
|
||||
invoker = "[URL=client://0/" + info->client_unique_id + "~WolverinDEV]" + info->client_nickname + "[/URL]";
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -102,7 +102,7 @@ command_result QueryClient::handleCommand(Command& cmd) {
|
||||
case string_hash("bindinglist"):
|
||||
return this->handleCommandBindingList(cmd);
|
||||
case string_hash("serversnapshotdeploy"): {
|
||||
#if 1
|
||||
#if 0
|
||||
return this->handleCommandServerSnapshotDeploy(cmd);
|
||||
#else
|
||||
auto cmd_str = cmd.build();
|
||||
@@ -911,33 +911,30 @@ command_result QueryClient::handleCommandServerSnapshotDeployNew(const ts::comma
|
||||
if(this->server) {
|
||||
return command_result{error::not_implemented};
|
||||
ACTION_REQUIRES_GLOBAL_PERMISSION(permission::b_virtualserver_snapshot_deploy, 1);
|
||||
//host = this->server->properties()[property::VIRTUALSERVER_HOST].as<string>();
|
||||
//port = this->server->properties()[property::VIRTUALSERVER_PORT].as<uint16_t>();
|
||||
} else {
|
||||
ACTION_REQUIRES_INSTANCE_PERMISSION(permission::b_virtualserver_snapshot_deploy, 1);
|
||||
}
|
||||
|
||||
std::string error{};
|
||||
unique_lock server_create_lock(serverInstance->getVoiceServerManager()->server_create_lock);
|
||||
//TODO: Create a server if no exists
|
||||
auto result = serverInstance->getVoiceServerManager()->deploy_snapshot(error, this->server, command);
|
||||
|
||||
{ /* TODO: Only check if we're creating a new server */
|
||||
auto instances = serverInstance->getVoiceServerManager()->serverInstances();
|
||||
if(config::server::max_virtual_server != -1 && instances.size() > config::server::max_virtual_server)
|
||||
using SnapshotDeployResult = VirtualServerManager::SnapshotDeployResult;
|
||||
switch (result) {
|
||||
case SnapshotDeployResult::SUCCESS:
|
||||
break;
|
||||
|
||||
case SnapshotDeployResult::REACHED_SERVER_ID_LIMIT:
|
||||
return command_result{error::server_max_vs_reached, "You cant create anymore virtual servers. (Server ID limit reached)"};
|
||||
|
||||
case SnapshotDeployResult::REACHED_SOFTWARE_SERVER_LIMIT:
|
||||
return command_result{error::server_max_vs_reached, "You cant create anymore virtual servers. (Software limit reached)"};
|
||||
|
||||
case SnapshotDeployResult::REACHED_CONFIG_SERVER_LIMIT:
|
||||
return command_result{error::server_max_vs_reached, "You reached the via config.yml enabled virtual server limit."};
|
||||
|
||||
if(instances.size() >= 65535)
|
||||
return command_result{error::server_max_vs_reached, "You cant create anymore virtual servers. (Software limit reached)"};
|
||||
case SnapshotDeployResult::CUSTOM_ERROR:
|
||||
return command_result{error::vs_critical, error};
|
||||
}
|
||||
|
||||
server_create_lock.unlock();
|
||||
|
||||
//TODO: Stop the server completely
|
||||
if(!serverInstance->getVoiceServerManager()->deploy_snapshot(error, 111, command)) {
|
||||
//TODO: Delete server is it was new
|
||||
return command_result{error::vs_critical, error};
|
||||
}
|
||||
|
||||
return command_result{error::ok};
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user