First draft of the new snapshot system and database changes

This commit is contained in:
WolverinDEV
2020-07-30 20:25:45 +02:00
parent c60119af00
commit 72abd7e20e
20 changed files with 1558 additions and 961 deletions
+165 -149
View File
@@ -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 = &notify;
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};
}
+6 -6
View File
@@ -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)))