Some general code cleanup

This commit is contained in:
WolverinDEV 2021-04-14 23:12:51 +02:00
parent 25b454ad0e
commit f41cfb8c30
46 changed files with 395 additions and 1106 deletions

View File

@ -45,7 +45,6 @@ set(SERVER_SOURCE_FILES
src/client/ConnectedClient.cpp
src/server/PrecomputedPuzzles.cpp
src/client/voice/VoiceClient.cpp
src/client/voice/VoiceClientHandschake.cpp
src/client/voice/VoiceClientCommandHandler.cpp
src/client/voice/VoiceClientConnectionPacketHandler.cpp
src/TS3ServerClientManager.cpp
@ -67,7 +66,6 @@ set(SERVER_SOURCE_FILES
src/VirtualServerManager.cpp
src/channel/ServerChannel.cpp
src/channel/ClientChannelView.cpp
src/Group.cpp
src/manager/BanManager.cpp
src/client/InternalClient.cpp
@ -81,7 +79,7 @@ set(SERVER_SOURCE_FILES
src/ConnectionStatistics.cpp
src/manager/TokeManager.cpp
src/manager/TokenManager.cpp
src/terminal/CommandHandler.cpp

View File

@ -52,7 +52,7 @@ void DatabaseHelper::tick() {
}
}
constexpr static std::string_view kSqlBase{"SELECT `client_unique_id`, `client_database_id`, `client_nickname`, `client_created`, `client_last_connected`, `client_total_connections` FROM `clients_server`"};
constexpr static std::string_view kSqlBase{"SELECT `client_unique_id`, `client_database_id`, `client_nickname`, `client_created`, `client_last_connected`, `client_ip`, `client_total_connections` FROM `clients_server`"};
inline std::deque<std::shared_ptr<ClientDatabaseInfo>> query_database_client_info(sql::SqlManager* sql_manager, ServerId server_id, const std::string& query, const std::vector<variable>& variables) {
std::deque<std::shared_ptr<ClientDatabaseInfo>> result{};
@ -79,6 +79,9 @@ inline std::deque<std::shared_ptr<ClientDatabaseInfo>> query_database_client_inf
assert(names[index] == "client_last_connected");
entry->client_last_connected = std::chrono::system_clock::time_point{} + std::chrono::seconds{std::stoull(values[index++])};
assert(names[index] == "client_ip");
entry->client_ip = values[index++];
assert(names[index] == "client_total_connections");
entry->client_total_connections = std::stoull(values[index++]);
@ -558,11 +561,11 @@ void DatabaseHelper::saveChannelPermissions(const std::shared_ptr<ts::server::Vi
}
std::shared_ptr<PropertyManager> DatabaseHelper::default_properties_client(std::shared_ptr<PropertyManager> properties, ClientType type){
if(!properties)
properties = make_shared<PropertyManager>();
if(!properties) {
properties = std::make_shared<PropertyManager>();
}
properties->register_property_type<property::ClientProperties>();
properties->register_property_type<property::ConnectionProperties>();
if(type == ClientType::CLIENT_MUSIC || type == ClientType::CLIENT_QUERY){
(*properties)[property::CLIENT_INPUT_HARDWARE] = true;
@ -707,12 +710,14 @@ std::shared_ptr<PropertyManager> DatabaseHelper::loadServerProperties(const std:
return;
}
auto weak_server = weak.lock();
if(!weak_server && serverId != 0) return;
if(!weak_server && serverId != 0) {
return;
}
string sql;
if(prop.hasDbReference())
if(prop.hasDbReference()) {
sql = "UPDATE `properties` SET `value` = :value WHERE `serverId` = :sid AND `type` = :type AND `id` = :id AND `key` = :key";
else {
} else {
prop.setDbReference(true);
sql = "INSERT INTO `properties` (`serverId`, `type`, `id`, `key`, `value`) VALUES (:sid, :type, :id, :key, :value)";
}
@ -783,9 +788,9 @@ std::shared_ptr<PropertyManager> DatabaseHelper::loadPlaylistProperties(const st
if(!weak_server && serverId != 0) return;
string sql;
if(prop.hasDbReference())
if(prop.hasDbReference()) {
sql = "UPDATE `properties` SET `value` = :value WHERE `serverId` = :sid AND `type` = :type AND `id` = :id AND `key` = :key";
else {
} else {
prop.setDbReference(true);
sql = "INSERT INTO `properties` (`serverId`, `type`, `id`, `key`, `value`) VALUES (:sid, :type, :id, :key, :value)";
}
@ -903,7 +908,7 @@ std::shared_ptr<PropertyManager> DatabaseHelper::loadClientProperties(const std:
}
if(entry) {
for(const auto& prop : entry->properties) {
if(prop->id == cldbid && (prop->type == property::PROP_TYPE_CLIENT || prop->type == property::PROP_TYPE_CONNECTION)) {
if(prop->id == cldbid && (prop->type == property::PROP_TYPE_CLIENT)) {
auto p = (*props)[prop->info];
p = prop->value;
p.setModified(true);
@ -915,7 +920,7 @@ std::shared_ptr<PropertyManager> DatabaseHelper::loadClientProperties(const std:
}
if(!loaded) {
auto command = sql::command(this->sql, "SELECT `key`, `value`, `type` FROM properties WHERE `serverId` = :serverId AND (`type` = :type1 OR `type` = :type2) AND `id` = :id", variable{":serverId", server ? server->getServerId() : 0}, variable{":type1", property::PropertyType::PROP_TYPE_CONNECTION}, variable{":type2", property::PropertyType::PROP_TYPE_CLIENT}, variable{":id", cldbid});
auto command = sql::command(this->sql, "SELECT `key`, `value`, `type` FROM properties WHERE `serverId` = :serverId AND `type` = :type AND `id` = :id", variable{":serverId", server ? server->getServerId() : 0}, variable{":type", property::PropertyType::PROP_TYPE_CLIENT}, variable{":id", cldbid});
deque<unique_ptr<FastPropertyEntry>> property_list;
LOG_SQL_CMD(load_properties(server ? server->getServerId() : 0, property_list, command));
@ -1005,15 +1010,6 @@ std::shared_ptr<PropertyManager> DatabaseHelper::loadClientProperties(const std:
column = "client_total_download";
break;
default:
return;
}
} else if(prop.type().type_property == property::PROP_TYPE_CONNECTION) {
switch (prop.type().property_index) {
case property::CONNECTION_CLIENT_IP:
column = "client_ip";
break;
default:
return;
}
@ -1035,6 +1031,14 @@ std::shared_ptr<PropertyManager> DatabaseHelper::loadClientProperties(const std:
return props;
}
void DatabaseHelper::updateClientIpAddress(const ServerId &server_id, ClientDbId client_database_id, const std::string &ip_address) {
sql::command(this->sql, "UPDATE `clients_server` SET `client_ip` = :value WHERE `server_id` = :serverId AND `client_database_id` = :cldbid",
variable{":serverId", server_id},
variable{":cldbid", client_database_id},
variable{":value", ip_address}
).executeLater().waitAndGetLater(LOG_SQL_CMD, {1, "future failed"});
}
void DatabaseHelper::loadStartupCache() {
this->loadStartupPermissionCache();
this->loadStartupPropertyCache();

View File

@ -18,6 +18,7 @@ namespace ts::server {
ClientDbId client_database_id;
std::string client_unique_id;
std::string client_nickname;
std::string client_ip;
std::chrono::time_point<std::chrono::system_clock> client_created;
std::chrono::time_point<std::chrono::system_clock> client_last_connected;
@ -108,6 +109,7 @@ namespace ts::server {
std::shared_ptr<PropertyManager> loadPlaylistProperties(const std::shared_ptr<VirtualServer>&, PlaylistId); //Read and write
std::shared_ptr<PropertyManager> loadChannelProperties(const std::shared_ptr<VirtualServer>&, ChannelId); //Read and write
std::shared_ptr<PropertyManager> loadClientProperties(const std::shared_ptr<VirtualServer>&, ClientDbId, ClientType);
void updateClientIpAddress(const ServerId& /* server id */, ClientDbId /* target database id */, const std::string& /* ip address */);
void deleteGroupArtifacts(ServerId, GroupId);
bool deleteChannelPermissions(const std::shared_ptr<VirtualServer>&, ChannelId);

View File

@ -1,14 +0,0 @@
#include <algorithm>
#include <utility>
#include <log/LogUtils.h>
#include <misc/memtracker.h>
#include "Group.h"
#include "VirtualServer.h"
#include "src/client/ConnectedClient.h"
#include "InstanceHandler.h"
using namespace std;
using namespace std::chrono;
using namespace ts;
using namespace ts::server;
using namespace ts::permission;

View File

@ -11,10 +11,6 @@
#include <misc/task_executor.h>
namespace ts {
namespace weblist {
class WebListManager;
}
namespace permission {
class PermissionNameMapper;
}
@ -91,7 +87,6 @@ namespace ts {
[[nodiscard]] inline std::shared_ptr<TeamSpeakLicense> getTeamSpeakLicense() { return this->teamspeak_license; }
[[nodiscard]] inline PropertyWrapper getDefaultServerProperties() { return PropertyWrapper{this->default_server_properties}; }
[[nodiscard]] inline std::shared_ptr<webio::LoopManager> getWebIoLoop() { return this->web_event_loop; }
[[nodiscard]] inline std::shared_ptr<weblist::WebListManager> getWebList() { return this->web_list; }
[[nodiscard]] inline std::shared_ptr<permission::PermissionNameMapper> getPermissionMapper() { return this->permission_mapper; }
[[nodiscard]] inline std::shared_ptr<ts::event::EventExecutor> getConversationIo() { return this->conversation_io; }
@ -130,7 +125,6 @@ namespace ts {
std::shared_ptr<ts::event::EventExecutor> conversation_io = nullptr;
std::shared_ptr<webio::LoopManager> web_event_loop = nullptr;
std::shared_ptr<weblist::WebListManager> web_list = nullptr;
std::shared_ptr<ts::PropertyManager> default_server_properties = nullptr;
std::shared_mutex default_tree_lock;

View File

@ -1,5 +1,4 @@
#include <algorithm>
#include <utility>
#include <log/LogUtils.h>
#include <misc/std_unique_ptr.h>
#include <Properties.h>
@ -16,13 +15,6 @@ using namespace ts::server;
#define PREFIX string("[SNAPSHOT] ")
inline std::string avArguments(const ts::Command& cmd, int index){
stringstream res;
for(const auto &key : cmd[index].keys())
res << key << "=" << cmd[index][key].as<std::string>() << " ";
return res.str();
}
//TeamSpeak: permid=i_channel_needed_permission_modify_power permvalue=75 permskip=0 permnegated=0
//TeaSpeak: perm=i_channel_needed_permission_modify_power value=75 granted=75 flag_skip=0 flag_negated=0
struct SnapshotPermissionEntry {
@ -32,104 +24,9 @@ struct SnapshotPermissionEntry {
bool negated = false;
bool skipped = false;
SnapshotPermissionEntry(const std::shared_ptr<permission::PermissionTypeEntry>& type, permission::PermissionValue value, permission::PermissionValue grant) : type(std::move(type)), value(value), grant(grant) {}
SnapshotPermissionEntry(const std::shared_ptr<permission::PermissionTypeEntry>& type, permission::PermissionValue value, permission::PermissionValue grant, bool negated, bool skipped) : type(type), value(value), grant(grant), negated(negated), skipped(skipped) {}
SnapshotPermissionEntry() = default;
static deque<std::unique_ptr<SnapshotPermissionEntry>> parse(const Command& cmd, int& index, permission::teamspeak::GroupType type, int version, const std::vector<std::string>& ends, bool ignore_first = false) {
deque<std::unique_ptr<SnapshotPermissionEntry>> result{};
while(true) {
bool end_reached = false;
for(const auto& e : ends)
if(cmd[index].has(e)) { end_reached = true; break; }
if(end_reached || cmd.bulkCount() < index) {
if(ignore_first) ignore_first = false;
else break;
}
if(version == 0) {
auto permission_name = cmd[index]["permid"].string();
for(const auto& mapped : permission::teamspeak::map_key(permission_name, type)) {
auto permission = permission::resolvePermissionData(mapped);
if(permission->type == permission::unknown) {
logError(0, "Failed to parse permission {}. Original: {}", mapped, permission_name);
index++;
continue;
}
bool found = false;
for(auto& e : result)
if(e->type->type == permission->type) {
found = true;
if(permission->grantName() == mapped)
e->grant = cmd[index]["permvalue"];
else {
e->value = cmd[index]["permvalue"];
e->negated = cmd[index]["permnegated"];
e->skipped = cmd[index]["permskip"];
}
}
if(!found) {
auto entry = make_unique<SnapshotPermissionEntry>();
entry->type = permission;
if(permission->grantName() == mapped)
entry->grant = cmd[index]["permvalue"];
else {
entry->value = cmd[index]["permvalue"];
entry->negated = cmd[index]["permnegated"];
entry->skipped = cmd[index]["permskip"];
}
result.push_back(std::move(entry));
}
}
} else if(version >= 1) {
auto permission_name = cmd[index]["perm"].string();
auto permission = permission::resolvePermissionData(permission_name);
if(permission->type == permission::unknown) {
logError(0, "Failed to parse permission {}", permission_name);
index++;
continue;
}
auto entry = make_unique<SnapshotPermissionEntry>();
entry->type = permission;
entry->value = cmd[index]["value"];
entry->grant = cmd[index]["grant"];
entry->skipped = cmd[index]["flag_skip"];
entry->negated = cmd[index]["flag_negated"];
result.push_back(std::move(entry));
} else {
logError(0, "Failed to parse snapshot permission entry! Invalid version!");
index++;
continue;
}
index++;
}
deque<std::unique_ptr<SnapshotPermissionEntry>> addings{};
for(const auto& perm : result) {
if(perm->type->type == permission::i_group_auto_update_type) { //Migrate this type
for(const auto& e : permission::update::migrate)
if(e.type == perm->value) {
auto _type = permission::resolvePermissionData(e.permission.name);
if(_type->type == permission::unknown) {
logError(0, "Invalid update permission for update group {}. Permission name: {}", e.type, e.permission.name);
} else {
addings.push_back(make_unique<SnapshotPermissionEntry>(_type, e.permission.value, e.permission.granted, e.permission.negated, e.permission.skipped));
}
}
}
}
for(auto& a : addings)
result.push_back(move(a));
return result;
}
void write(Command& cmd, int& index, permission::teamspeak::GroupType type, int version) {
if(version == 0) {
if(this->value != permNotGranted) {
@ -163,544 +60,6 @@ struct SnapshotPermissionEntry {
}
};
#if 0
std::shared_ptr<VirtualServer> VirtualServerManager::createServerFromSnapshot(shared_ptr<VirtualServer> old, std::string host,
uint16_t port, const ts::Command &arguments,
std::string &error) {
ServerId serverId = 0;
map<ClientDbId, map<ChannelId, ClientDbId>> channelGroupRelations; //cid is the new cgid
map<GroupId, GroupId> channelGroupMapping;
map<ClientDbId, vector<GroupId>> serverGroupRelations; //gid is the new gid
map<GroupId, GroupId> serverGroupMapping;
map<ClientDbId, ClientDbId> db_mapping_client_id;
deque<pair<std::string, threads::Future<sql::result>>> futures;
bool sid_success = false;
serverId = this->next_available_server_id(sid_success);
if(!sid_success) {
error = "failed to generate new sid";
return nullptr;
}
ServerId log_server_id = old ? old->getServerId() : serverId;
futures.emplace_back(
"server id register",
sql::command(this->handle->getSql(), "INSERT INTO `servers` (`serverId`, `host`, `port`) VALUES (:sid, :host, :port)", variable{":sid", serverId}, variable{":host", host}, variable{":port", port}).executeLater()
);
sql::model sql_insert_channel(this->handle->getSql(), "INSERT INTO `channels` (`serverId`, `channelId`, `type`, `parentId`) VALUES (:sid, :id, :type, :pid)", variable{":sid", serverId});
sql::model sql_insert_group_assignment(this->handle->getSql(), "INSERT INTO `assignedGroups` (`serverId`, `cldbid`, `groupId`, `channelId`, `until`) VALUES (:sid, :cldbid, :gid, :chid, :until)", variable{":sid", serverId}, variable{":until", 0});
sql::model sql_insert_permission(this->handle->getSql(), "INSERT INTO `permissions` (`serverId`, `type`, `id`, `channelId`, `permId`, `value`, `grant`, `flag_skip`, `flag_negate`) VALUES (:serverId, :type, :id, :chId, :permId, :value, :grant, :flag_skip, :flag_negate)",
variable{":serverId", serverId});
sql::model sql_update_permission_grant(this->handle->getSql(), "UPDATE `permissions` SET `grant` = :grant WHERE `serverId` = :sid AND `type` = :type AND `id` = :id AND `channelId` = :chid AND `permId` = :key", variable{":sid", serverId});
sql::model sql_insert_property(this->handle->getSql(), "INSERT INTO `properties` (`serverId`, `type`, `id`, `key`, `value`) VALUES (:sid, :type, :id, :key, :value)", variable{":sid", serverId});
sql::model sql_insert_bot(this->handle->getSql(), "INSERT INTO `musicbots` (`serverId`, `botId`, `uniqueId`, `owner`) VALUES (:server_id, :bot_id, :unique_id, :owner)", variable{":server_id", serverId});
sql::model sql_insert_playlist(this->handle->getSql(), "INSERT INTO `playlists` (`serverId`, `playlist_id`) VALUES (:server_id, :playlist_id)", variable{":server_id", serverId});
sql::model sql_insert_playlist_song(this->handle->getSql(), "INSERT INTO `playlist_songs` (`serverId`, `playlist_id`, `song_id`, `order_id`, `invoker_dbid`, `url`, `url_loader`, `loaded`, `metadata`) VALUES (:server_id, :playlist_id, :song_id, :order_id, :invoker_dbid, :url, :url_loader, :loaded, :metadata)", variable{":server_id", serverId});
int index = 0;
auto snapshot_version = arguments[index].has("snapshot_version") ? arguments[index++]["snapshot_version"] : 0;
debugMessage(0, "Got server snapshot with version {}", snapshot_version);
while(true){
for(const auto &key : arguments[index].keys()){
if(key == "end_virtualserver") continue;
if(key == "begin_virtualserver") continue;
if(snapshot_version == 0) {
if(key == "virtualserver_download_quota" || key == "virtualserver_upload_quota" || key == "virtualserver_max_download_total_bandwidth" || key == "virtualserver_max_upload_total_bandwidth") {
auto value = arguments[index][key].string();
try {
arguments[index][key] = to_string((int64_t) std::stoull(value));
} catch(const std::exception& ex) {
continue;
}
}
}
debugMessage(LOG_GENERAL, PREFIX + " Having server key {} = {}", key, arguments[index][key].as<std::string>());
futures.emplace_back(
"server property import proprty=" + key,
sql_insert_property.command().values(
variable{":type", property::PropertyType::PROP_TYPE_SERVER},
variable{":id", 0},
variable{":key", key},
variable{":value", arguments[index][key].string()}
).executeLater()
);
}
if(arguments[index].has("end_virtualserver")) break;
index++;
}
//`serverId` INT NOT NULL, `playlist_id` INT, `song_id` INT, `order_id` INT, `invoker_dbid` INT, `url` TEXT, `url_loader` TEXT
/*
* sql::command(this->handle->getSql(), "INSERT INTO `playlists` (`serverId`, `playlist_id`) VALUES (:server_id, :playlist_id)",
variable{":server_id", this->ref_server()->getServerId()},
variable{":playlist_id", playlist_id}
)
*/
index++;
for(;index < arguments.bulkCount(); index++) {
if (arguments[index].has("begin_channels")) {
while (true) {
if (arguments[index].has("end_channels")) break;
auto channel_id = arguments[index]["channel_id"].string();
futures.emplace_back(
"channel register cid=" + channel_id,
sql_insert_channel.command().values(variable{":id", channel_id}, variable{":type", -1}, variable{":pid", arguments[index]["channel_pid"].string()}).executeLater()
); //TODO findout type!
for (const auto &key : arguments[index].keys()) {
if(key == "channel_id") continue;
if(key == "channel_pid") continue;
if(key == "channel_security_salt") continue;
if(key == "channel_filepath") continue; //TODO implement channel_filepath?
if(key == "begin_channels") continue;
debugMessage(log_server_id, PREFIX + "Having channel key " + key + "=" + arguments[index][key].as<std::string>() + " for channel " + arguments[index]["channel_id"].as<std::string>());
futures.push_back({
"channel prop register cid=" + channel_id + " property=" + key,
sql_insert_property.command().values(variable{":sid", serverId},
variable{":type", property::PropertyType::PROP_TYPE_CHANNEL},
variable{":id", channel_id},
variable{":key", key},
variable{":value", arguments[index][key].as<std::string>()}
).executeLater()
});
}
index++;
}
} else if (arguments[index].has("begin_clients")) {
while (true) {
if (arguments[index].has("end_clients")) break;
debugMessage(log_server_id, PREFIX + "Create client {}/{} -> end: {}",arguments[index]["client_id"].as<std::string>(), arguments[index]["client_nickname"].as<std::string>(), to_string(arguments[index].has("end_clients")));
ClientDbId oldId = 0;
auto res = sql::command(this->handle->getSql(), "SELECT `cldbid` FROM `clients` WHERE `clientUid` = :uid AND `serverId` = 0", variable{":uid", arguments[index]["client_unique_id"].as<std::string>()}).query([](ClientDbId* ptr, int, char** v, char**){ *ptr = stoll(v[0]); return 0;}, &oldId);
LOG_SQL_CMD(res);
if(oldId == 0){
res = sql::command(this->handle->getSql(), "SELECT `cldbid` FROM `clients` WHERE `serverId` = 0 ORDER BY `cldbid` DESC LIMIT 1").query([](ClientDbId* ptr, int length, char** values, char** names){
*ptr = static_cast<ClientDbId>(stoll(values[0]));
return 0;
}, &oldId);
LOG_SQL_CMD(res);
oldId += 1;
//Create a new client
sql::command(this->handle->getSql(), "INSERT INTO `clients` (`serverId`, `cldbid`, `clientUid`, `firstConnect`, `lastConnect`, `connections`, `lastName`) VALUES (:sid, :cldbid, :cluid, :firstConnect, :lastConnect, :connections, :name)",
variable{":sid", 0}, variable{":cldbid", oldId}, variable{":cluid", arguments[index]["client_unique_id"].as<std::string>()},
variable{":firstConnect", arguments[index]["client_created"].as<std::string>()},
variable{":lastConnect", "0"}, variable{":connections", "0"}, variable{":name", arguments[index]["client_nickname"].as<std::string>()}).execute(); //TODO log error
}
db_mapping_client_id[arguments[index]["client_id"].as<ClientDbId>()] = oldId;
sql::command(this->handle->getSql(), "INSERT INTO `clients` (`serverId`, `cldbid`, `clientUid`, `firstConnect`, `lastConnect`, `connections`, `lastName`) VALUES (:sid, :cldbid, :cluid, :firstConnect, :lastConnect, :connections, :name)",
variable{":sid", serverId}, variable{":cldbid", oldId}, variable{":cluid", arguments[index]["client_unique_id"].as<std::string>()},
variable{":firstConnect", arguments[index]["client_created"].as<std::string>()},
variable{":lastConnect", "0"}, variable{":connections", "0"}, variable{":name", arguments[index]["client_nickname"].as<std::string>()}).execute(); //TODO log error
//TODO here
index++;
}
} else if (arguments[index].has("begin_permissions")) {
while (true) {
index++;
if (arguments[index].has("end_permissions")) break;
else if (arguments[index].has("server_groups")) {
GroupId currentGroupId = 1;
while (true) {
if (arguments[index].has("end_groups")) break;
if(!arguments[index].has("id")){ //new group
logError(0, "Invalid server group permission entry! Missing group id!");
index++;
continue;
}
currentGroupId = static_cast<GroupId>(GroupManager::generateGroupId(this->handle->getSql()));
debugMessage(log_server_id, PREFIX + "Insert group: " + to_string(currentGroupId) + " - " + arguments[index]["name"].as<string>());
serverGroupMapping[arguments[index]["id"].as<GroupId>()] = currentGroupId;
LOG_SQL_CMD(sql::command(this->handle->getSql(), "INSERT INTO `groups` (`serverId`, `groupId`, `target`, `type`, `displayName`) VALUES (:sid, :gid, :target, :type, :name)",
variable{":sid", serverId},
variable{":gid", currentGroupId},
variable{":target", GROUPTARGET_SERVER},
variable{":type", GroupType::GROUP_TYPE_NORMAL},
variable{":name", arguments[index]["name"].as<string>()})
.execute());
auto permissions = SnapshotPermissionEntry::parse(arguments, index, permission::teamspeak::SERVER, snapshot_version, {"end_group"});
for(const auto& entry : permissions) {
futures.push_back({
"server group permission sgid=" + to_string(currentGroupId) + " permId=" + entry->type->name,
sql_insert_permission.command().values(
variable{":id", currentGroupId},
variable{":type", permission::SQL_PERM_GROUP},
variable{":chId", 0},
variable{":permId", entry->type->name},
variable{":value", entry->value},
variable{":grant", entry->grant},
variable{":flag_skip", entry->skipped},
variable{":flag_negate", entry->negated}
).executeLater()
});
}
index++;
}
//Relations for sgroups after list
index++;
while (true) {
if (arguments[index].has("end_relations")) break;
serverGroupRelations[db_mapping_client_id[arguments[index]["cldbid"].as<ClientDbId>()]].push_back(serverGroupMapping[arguments[index]["gid"].as<GroupId>()]);
futures.push_back({
"server group relation",
sql_insert_group_assignment.command().values(variable{":cldbid", db_mapping_client_id[arguments[index]["cldbid"].as<ClientDbId>()]}, variable{":gid", serverGroupMapping[arguments[index]["gid"].as<GroupId>()]}, variable{":chid", "0"}).executeLater()
});
index++;
}
} else if(arguments[index].has("channel_groups")){
GroupId currentGroupId = 1;
while (true) {
if (arguments[index].has("end_groups")) break;
if(!arguments[index].has("id")){ //new group
logError(0, "Invalid server group permission entry! Missing group id!");
index++;
continue;
}
currentGroupId = static_cast<GroupId>(GroupManager::generateGroupId(this->handle->getSql()));
debugMessage(log_server_id, PREFIX + "Insert channel group: " + to_string(currentGroupId) + " - " + arguments[index]["name"].as<string>());
channelGroupMapping[arguments[index]["id"].as<GroupId>()] = currentGroupId;
LOG_SQL_CMD(sql::command(this->handle->getSql(), "INSERT INTO `groups` (`serverId`, `groupId`, `target`, `type`, `displayName`) VALUES (:sid, :gid, :target, :type, :name)",
variable{":sid", serverId},
variable{":gid", currentGroupId},
variable{":target", GROUPTARGET_CHANNEL},
variable{":type", GroupType::GROUP_TYPE_NORMAL},
variable{":name", arguments[index]["name"].as<string>()})
.execute());
auto permissions = SnapshotPermissionEntry::parse(arguments, index, permission::teamspeak::SERVER, snapshot_version, {"end_group"});
for(const auto& entry : permissions) {
futures.push_back({
"channel group property insert cgid=" + to_string(currentGroupId) + " property=" + entry->type->name,
sql_insert_permission.command().values(
variable{":id", currentGroupId},
variable{":type", permission::SQL_PERM_GROUP},
variable{":chId", 0},
variable{":permId", entry->type->name},
variable{":value", entry->value},
variable{":grant", entry->grant},
variable{":flag_skip", entry->skipped},
variable{":flag_negate", entry->negated}
).executeLater()
});
}
index++;
}
//Relations for sgroups after list
ChannelId chId = 0;
index++;
while (true) {
if (arguments[index].has("end_relations")) break;
if(arguments[index].has("iid")) chId = arguments[index]["iid"];
channelGroupRelations[db_mapping_client_id[arguments[index]["cldbid"].as<ClientDbId>()]][chId] = channelGroupMapping[arguments[index]["gid"].as<GroupId>()];
futures.push_back({
"channel group relation",
sql_insert_group_assignment.command().values(variable{":cldbid", db_mapping_client_id[arguments[index]["cldbid"].as<ClientDbId>()]}, variable{":gid", channelGroupMapping[arguments[index]["gid"].as<GroupId>()]}, variable{":chid", chId}).executeLater()
});
index++;
}
} else if (arguments[index].has("client_flat")) {
ClientDbId currentId = 0;
while (true) {
if (arguments[index].has("end_flat")) break;
if(arguments[index].has("id1"))
currentId = arguments[index]["id1"];
auto permissions = SnapshotPermissionEntry::parse(arguments, index, permission::teamspeak::CLIENT, snapshot_version, {"id1", "end_flat"}, true);
for(const auto& entry : permissions) {
futures.push_back({
"client permission insert clientId=" + to_string(currentId) + " permId=" + entry->type->name,
sql_insert_permission.command().values(
variable{":id", currentId},
variable{":type", permission::SQL_PERM_USER},
variable{":chId", 0},
variable{":permId", entry->type->name},
variable{":value", entry->value},
variable{":grant", entry->grant},
variable{":flag_skip", entry->skipped},
variable{":flag_negate", entry->negated}
).executeLater()
});
}
}
} else if (arguments[index].has("channel_flat")) {
ChannelId currentChannelId = 0;
while (true) {
if (arguments[index].has("end_flat")) break;
if (arguments[index].has("id1"))
currentChannelId = arguments[index]["id1"]; //We also have id2 unknown for what this is. Maybe parent?
auto permissions = SnapshotPermissionEntry::parse(arguments, index, permission::teamspeak::CHANNEL, snapshot_version, {"id1", "end_flat"}, true);
for(const auto& entry : permissions) {
futures.push_back({
"channel permission insert channelId=" + to_string(currentChannelId) + " permId=" + entry->type->name,
sql_insert_permission.command().values(
variable{":id", 0},
variable{":type", permission::SQL_PERM_CHANNEL},
variable{":chId", currentChannelId},
variable{":permId", entry->type->name},
variable{":value", entry->value},
variable{":grant", entry->grant},
variable{":flag_skip", entry->skipped},
variable{":flag_negate", entry->negated}
).executeLater()
});
}
}
} else if (arguments[index].has("channel_client_flat")) {
ClientDbId currentClientId = 0;
ChannelId currentChannelId = 0;
while (true) {
if (arguments[index].has("end_flat")) break;
//id1 = channel id
//id2 = client id
if(arguments[index].has("id1"))
currentChannelId = arguments[index]["id1"];
if(arguments[index].has("id2"))
currentClientId = db_mapping_client_id[arguments[index]["id2"]];
if(currentChannelId > 0 && currentClientId > 0){
auto permissions = SnapshotPermissionEntry::parse(arguments, index, permission::teamspeak::CLIENT, snapshot_version, {"id1", "id2", "end_flat"}, true);
for(const auto& entry : permissions) {
futures.push_back({
"client channel permission insert",
sql_insert_permission.command().values(
variable{":id", currentClientId},
variable{":type", permission::SQL_PERM_CHANNEL},
variable{":chId", currentChannelId},
variable{":permId", entry->type->name},
variable{":value", entry->value},
variable{":grant", entry->grant},
variable{":flag_skip", entry->skipped},
variable{":flag_negate", entry->negated}
).executeLater()
});
}
} else index++;
}
} else {
error = "Invalid tag in permissions. Available: " + avArguments(arguments, index);
return nullptr;
}
}
} else if(arguments[index].has("begin_music")) {
PlaylistId playlist_index = 0;
PlaylistId playlist_error = 0xFFFFFFFF;
map<PlaylistId, PlaylistId> db_mapping_playlist;
/*
//gather static info (not needed because its a new server)
{
auto sql_result = sql::command(this->handle->getSql(), "SELECT `playlist_id` FROM `playlists` WHERE `serverId` = :server_id ORDER BY `playlist_id` DESC", variable{":server_id", serverId}).query([&](int length, string* values, string* names){
if(length != 1) return;
try {
playlist_index = (PlaylistId) stoll(values[0]);
} catch(const std::exception& ex) {
error = "Failed to parse playlist id from database. ID: " + values[0];
return;
}
});
LOG_SQL_CMD(sql_result);
if(playlist_index == 0) {
error = "failed to gather info";
return nullptr;
}
}
*/
while(!arguments[index].has("end_music")) {
if(arguments[index].has("begin_bots")) {
while(!arguments[index].has("end_bots")) {
ClientDbId bot_id = arguments[index]["bot_id"];
if(db_mapping_client_id.find(bot_id) == db_mapping_client_id.end()) {
logError(log_server_id, PREFIX + "Failed to insert music bot within id {}. (Mapping not found)", bot_id);
index++;
continue;
}
ClientDbId new_bot_id = db_mapping_client_id[bot_id];
//begin_bots
auto sql_result = sql_insert_bot.command().values(
variable{":bot_id", new_bot_id},
variable{":unique_id", arguments[index]["bot_unique_id"].string()},
variable{":owner", arguments[index]["bot_owner_id"].string()}
).execute();
if(!sql_result) {
logError(log_server_id, PREFIX + "Failed to insert music bot with id {} (old: {}). ({})", new_bot_id, bot_id, sql_result.fmtStr());
index++;
continue;
}
for(const auto& key : arguments[index].keys()) {
if(key == "begin_music") continue;
if(key == "begin_bots") continue;
if(key == "bot_unique_id") continue;
if(key == "bot_owner_id") continue;
if(key == "bot_id") continue;
const auto& property = property::find<property::ClientProperties>(key);
if(property.is_undefined()) {
debugMessage(log_server_id, PREFIX + "Failed to parse give music bot property {} for bot {} (old: {}). Value: {}", key, new_bot_id, bot_id, arguments[index][key].string());
continue;
}
futures.push_back({
"music bot insert",
sql_insert_property.command().values(
variable{":type", property::PropertyType::PROP_TYPE_CLIENT},
variable{":id", new_bot_id},
variable{":key", key},
variable{":value", arguments[index][key].string()}
).executeLater()
});
}
index++;
}
} else if(arguments[index].has("begin_playlist")) {
while(!arguments[index].has("end_playlist")) {
PlaylistId playlist_id = arguments[index]["playlist_id"];
db_mapping_playlist[++playlist_index] = playlist_id;
auto sql_result = sql_insert_playlist.command().values(variable{":playlist_id", playlist_index}).execute();
if(!sql_result) {
db_mapping_playlist[playlist_index] = playlist_error;
logError(log_server_id, PREFIX + "Failed to insert playlist with id {} (old: {}). ({})", playlist_index, playlist_id, sql_result.fmtStr());
index++;
continue;
}
for(const auto& key : arguments[index].keys()) {
if(key == "begin_music") continue;
if(key == "begin_playlist") continue;
if(key == "playlist_id") continue;
const auto& property = property::find<property::ClientProperties>(key);
if(property.is_undefined()) {
debugMessage(log_server_id, PREFIX + "Failed to parse given playlist property {} for playlist {} (old: {}). Value: {}", key, playlist_index, playlist_id, arguments[index][key].string());
continue;
}
futures.push_back({
"playlist insert",
sql_insert_property.command().values(
variable{":type", property::PropertyType::PROP_TYPE_PLAYLIST},
variable{":id", playlist_index},
variable{":key", key},
variable{":value", arguments[index][key].string()}
).executeLater()
});
}
index++;
}
} else if(arguments[index].has("begin_playlist_songs")) {
PlaylistId current_playlist = 0;
PlaylistId current_playlist_mapped = 0;
while(!arguments[index].has("end_playlist_songs")) {
if(arguments[index].has("song_playlist_id")) {
current_playlist = arguments[index]["song_playlist_id"];
current_playlist_mapped = (PlaylistId) db_mapping_client_id[current_playlist];
if(current_playlist_mapped == 0) {
logError(log_server_id, PREFIX + "Failed to insert playlist song entry {}. Playlist id {} hasn't been mapped", arguments[index]["song_id"].value(), current_playlist);
current_playlist_mapped = playlist_error;
}
}
if(current_playlist_mapped == playlist_error) { /* current list is an error */
index++;
continue;
}
auto sql_future = sql_insert_playlist_song.command().values(
variable{":playlist_id", current_playlist_mapped},
variable{":song_id", arguments[index]["song_id"].value()},
variable{":order_id", arguments[index]["song_order"].value()},
variable{":invoker_dbid", arguments[index]["song_invoker"].value()},
variable{":url", arguments[index]["song_url"].value()},
variable{":url_loader", arguments[index]["song_url_loader"].value()},
variable{":loaded", arguments[index]["song_loaded"].value()},
variable{":metadata", arguments[index]["song_metadata"].value()}
).executeLater();
futures.push_back({
"song insert",
sql_future
});
index++;
}
}
index++;
}
} else {
error = "Invalid root tag. Available: " + avArguments(arguments, index);
return nullptr;
}
}
debugMessage(serverId, "Wait for success!");
for(const auto& future : futures) {
auto result = future.second.waitAndGet({-1, "timeout"}, chrono::system_clock::now() + chrono::seconds(5));
if(!result) {
logError(serverId, "Failed to execute snapshotdeploy command {}: {}", future.first, result.fmtStr());
}
}
if(old)
this->deleteServer(old);
//Now we load the server
auto server = make_shared<VirtualServer>(serverId, this->handle->getSql());
server->self = server;
if(!server->initialize(false)) {
//FIXME Error handling!
}
server->properties()[property::VIRTUALSERVER_HOST] = host;
server->properties()[property::VIRTUALSERVER_PORT] = port;
server->properties()[property::VIRTUALSERVER_ASK_FOR_PRIVILEGEKEY] = false;
{
threads::MutexLock l(this->instanceLock);
this->instances.push_back(server);
}
this->adjust_executor_threads();
server->properties()[property::VIRTUALSERVER_DEFAULT_SERVER_GROUP] = serverGroupMapping[server->properties()[property::VIRTUALSERVER_DEFAULT_SERVER_GROUP].as<GroupId>()];
server->properties()[property::VIRTUALSERVER_DEFAULT_CHANNEL_GROUP] = channelGroupMapping[server->properties()[property::VIRTUALSERVER_DEFAULT_CHANNEL_GROUP].as<GroupId>()];
server->properties()[property::VIRTUALSERVER_DEFAULT_CHANNEL_ADMIN_GROUP] = channelGroupMapping[server->properties()[property::VIRTUALSERVER_DEFAULT_CHANNEL_ADMIN_GROUP].as<GroupId>()];
server->ensureValidDefaultGroups();
return server;
}
#endif
struct CommandTuple {
Command& cmd;
int& index;
int version;
};
struct PermissionCommandTuple {
Command& cmd;
int& index;

View File

@ -45,6 +45,10 @@ void ts::server::shutdownInstance(const std::string& message) {
mainThreadActive = false;
}
bool ts::server::isShuttingDown() {
return shuttingDown;
}
std::shared_ptr<server::ShutdownData> currentShutdown = nullptr;
std::shared_ptr<server::ShutdownData> server::scheduledShutdown() { return currentShutdown; }

View File

@ -5,7 +5,7 @@
namespace ts {
namespace server {
extern void shutdownInstance(const std::string& reason = ts::config::messages::applicationStopped);
extern bool isShuttingDown();
struct ShutdownData {
std::string reason;

View File

@ -41,6 +41,10 @@ void print_current_exception() {
extern bool mainThreadDone;
#ifdef BREAKPAD_EXCEPTION_HANDLER
static bool dumpCallback(const google_breakpad::MinidumpDescriptor& descriptor, void* context, bool succeeded) {
if(ts::server::isShuttingDown()) {
return true;
}
logCritical(LOG_GENERAL, "The server crashed!");
try {
if(!fs::exists(fs::u8path(ts::config::crash_path)))

View File

@ -17,10 +17,10 @@ using namespace ts::buffer;
inline void banClientFlood(VirtualServer* server, const shared_ptr<ConnectedClient>& cl, time_point<system_clock> until){
auto time = until.time_since_epoch().count() == 0 ? 0L : chrono::ceil<chrono::seconds>(until - system_clock::now()).count();
std::string reason = "You're flooding too much";
std::string reason{"You're flooding too much"};
serverInstance->banManager()->registerBan(server->getServerId(), cl->getClientDatabaseId(), reason, cl->getUid(), cl->getLoggingPeerIp(), "", "", until);
for(const auto &client : server->findClientsByUid(cl->getUid())) {
for(const auto &client : server->findClientsByUid(cl->getUid())) {
server->notify_client_ban(client, server->getServerRoot(), reason, time);
client->close_connection(system_clock::now() + seconds(1));
}
@ -32,29 +32,27 @@ timing_end = system_clock::now(); \
variable = duration_cast<decltype(variable)>(timing_end - timing_begin);
void VirtualServer::executeServerTick() {
threads::MutexTryLock l(this->stateLock); //Should not attempt to shutdown or start the server while ticking
if(!l) {
if(this->running())
logError(this->getServerId(), "Failed to lock tick mutex!");
std::shared_lock state_lock{this->state_mutex};
if(!this->running()) {
return;
}
if(!this->running()) return;
auto tick_timestamp = std::chrono::system_clock::now();
try {
if(lastTick.time_since_epoch().count() != 0) {
auto delay = system_clock::now() - lastTick;
auto delay_ms = duration_cast<milliseconds>(delay).count();
if(this->lastTick.time_since_epoch().count() > 0) {
auto delay = tick_timestamp - this->lastTick;
auto delay_ms = std::chrono::duration_cast<std::chrono::milliseconds>(delay).count();
if(delay_ms > 510) {
if(delay_ms < 750) {
logWarning(this->getServerId(),
"Found varianzes within the server tick! (Supposed: 500ms Hold: {}ms)", delay_ms);
"Found variances within the server tick! (Supposed: 500ms Hold: {}ms)", delay_ms);
} else {
logError(this->getServerId(),
"Found varianzes within the server tick! This long delay could be an issue. (Supposed: 500ms Hold: {}ms)", delay_ms);
"Found variances within the server tick! This long delay could be an issue. (Supposed: 500ms Hold: {}ms)", delay_ms);
}
}
}
lastTick = system_clock::now();
this->lastTick = tick_timestamp;
system_clock::time_point timing_begin, timing_end;
milliseconds timing_update_states, timing_client_tick, timing_channel, timing_statistic, timing_groups, timing_ccache, music_manager;
@ -64,8 +62,8 @@ void VirtualServer::executeServerTick() {
{
BEGIN_TIMINGS();
size_t clientOnline = 0;
size_t queryOnline = 0;
size_t clientOnline{0};
size_t queryOnline{0};
for(const auto& conn : client_list){
switch (conn->getType()){
case ClientType::CLIENT_TEAMSPEAK:
@ -73,6 +71,7 @@ void VirtualServer::executeServerTick() {
case ClientType::CLIENT_WEB:
clientOnline++;
break;
case ClientType::CLIENT_QUERY:
case ClientType::CLIENT_MUSIC:
queryOnline++;
@ -85,13 +84,14 @@ void VirtualServer::executeServerTick() {
}
}
properties()[property::VIRTUALSERVER_UPTIME] = std::chrono::duration_cast<std::chrono::seconds>(std::chrono::system_clock::now() - this->startTimestamp).count();
properties()[property::VIRTUALSERVER_UPTIME] = std::chrono::duration_cast<std::chrono::seconds>(tick_timestamp - this->startTimestamp).count();
properties()[property::VIRTUALSERVER_CLIENTS_ONLINE] = clientOnline + queryOnline;
properties()[property::VIRTUALSERVER_QUERYCLIENTS_ONLINE] = queryOnline;
if(clientOnline + queryOnline == 0) {
//We don't need to tick, when server is empty!
return;
}
properties()[property::VIRTUALSERVER_CHANNELS_ONLINE] = this->channelTree->channel_count();
properties()[property::VIRTUALSERVER_TOTAL_PING] = this->generate_network_report().average_ping;
END_TIMINGS(timing_update_states);
@ -123,24 +123,30 @@ void VirtualServer::executeServerTick() {
}
continue; //Fully ha?
}
if(cl->floodPoints > flood_block){
if(!cl->ignoresFlood()) {
banClientFlood(this, cl, system_clock::now() + minutes(10));
continue;
}
}
if(cl->floodPoints > flood_decrease)
if(cl->floodPoints > flood_decrease) {
cl->floodPoints -= flood_decrease;
else cl->floodPoints = 0;
} else {
cl->floodPoints = 0;
}
cl->tick_server(tick_client_end);
auto voice = dynamic_pointer_cast<SpeakingClient>(cl);
if(flag_update_spoken && voice)
if(flag_update_spoken && voice) {
this->spoken_time += voice->takeSpokenTime();
}
tick_client_end = system_clock::now();
auto passed_time = tick_client_end - tick_client_begin;
if(passed_time > microseconds(2500)) {
if(passed_time > milliseconds(10)) {
if(passed_time > microseconds{2500}) {
if(passed_time > milliseconds{10}) {
logError(this->serverId, "Ticking of client {:1} ({:2}) needs more that 2500 microseconds! ({:3} microseconds)",
cl->getLoggingPeerIp() + ":" + to_string(cl->getPeerPort()),
cl->getDisplayName(),
@ -163,8 +169,10 @@ void VirtualServer::executeServerTick() {
debugMessage(this->serverId, "Saved client permissions for client {} ({}) in {}ms", cl->getClientDatabaseId(), cl->getDisplayName(), duration_cast<milliseconds>(end - begin).count());
}
}
if(flag_update_spoken)
if(flag_update_spoken) {
this->spoken_time_timestamp = system_clock::now();
}
END_TIMINGS(timing_client_tick);
}
@ -173,30 +181,33 @@ void VirtualServer::executeServerTick() {
{
BEGIN_TIMINGS();
unique_lock channel_lock(this->channel_tree_mutex);
std::unique_lock channel_lock{this->channel_tree_mutex};
auto channels = this->channelTree->channels();
channel_lock.unlock();
for(const auto& channel : channels) {
auto server_channel = dynamic_pointer_cast<ServerChannel>(channel);
assert(server_channel);
for(const auto& channel : channels){
if(channel->channelType() == ChannelType::temporary) {
auto server_channel = dynamic_pointer_cast<ServerChannel>(channel);
assert(server_channel);
if(server_channel->client_count() > 0 || !this->getClientsByChannelRoot(channel, true).empty())
if(server_channel->client_count() > 0 || !this->isChannelRootEmpty(channel, false)) {
continue;
}
seconds deleteTimeout{channel->properties()[property::CHANNEL_DELETE_DELAY].as_or<uint64_t>(0)};
/* seconds */
auto channel_delete_timeout = channel->properties()[property::CHANNEL_DELETE_DELAY].as_or<uint64_t>(0);
auto empty_seconds = channel->empty_seconds();
auto last_left = time_point<system_clock>() + milliseconds(
channel->properties()[property::CHANNEL_LAST_LEFT].as_or<int64_t>(0));
auto channel_created = channel->createdTimestamp();
if(empty_seconds > channel_delete_timeout) {
this->delete_channel(server_channel, this->serverRoot, "temporary auto delete", channel_lock, true);
if(!channel_lock.owns_lock()) {
channel_lock.lock();
}
if(system_clock::now() - last_left < deleteTimeout) continue; //One second stay
if(system_clock::now() - channel_created < deleteTimeout + seconds(1)) continue; //One second stay
this->delete_channel(server_channel, this->serverRoot, "temporary auto delete", channel_lock, true);
if(channel_lock.owns_lock())
channel_lock.unlock();
/* no need to tick the channel any more since it has been deleted */
continue;
}
}
{
auto permission_manager = channel->permissions();
if(permission_manager->require_db_updates()) {
@ -216,18 +227,13 @@ void VirtualServer::executeServerTick() {
this->server_statistics_->tick();
{
lock_guard<threads::Mutex> lock(this->join_attempts_lock);
if(system_clock::now() > this->join_last_decrease + seconds(5)) {
for(auto& elm : this->join_attempts)
if(elm.second > 0) elm.second--;
lock_guard lock{this->join_attempts_lock};
if(tick_timestamp > this->join_last_decrease + seconds(5)) {
std::erase_if(this->join_attempts, [](auto& entry) {
return --entry.second <= 0;
});
auto copy = this->join_attempts;
for(const auto& elm : copy)
if(elm.second == 0){
auto found = find(this->join_attempts.begin(), this->join_attempts.end(), elm);
if(found != this->join_attempts.end()) this->join_attempts.erase(found);
}
this->join_last_decrease = system_clock::now();
this->join_last_decrease = tick_timestamp;
}
}
END_TIMINGS(timing_statistic);
@ -257,7 +263,7 @@ void VirtualServer::executeServerTick() {
if(system_clock::now() - lastTick > milliseconds(100)) {
//milliseconds timing_update_states, timing_client_tick, timing_channel, timing_statistic;
logError(this->serverId, "Server tick tooks to long ({}ms => Status updates: {}ms Client tick: {}ms, Channel tick: {}ms, Statistic tick: {}ms, Groups: {}ms, Conversation cache: {}ms)",
logError(this->serverId, "Server tick took to long ({}ms => Status updates: {}ms Client tick: {}ms, Channel tick: {}ms, Statistic tick: {}ms, Groups: {}ms, Conversation cache: {}ms)",
duration_cast<milliseconds>(system_clock::now() - lastTick).count(),
timing_update_states.count(),
timing_client_tick.count(),

View File

@ -448,7 +448,7 @@ inline vector<string> split_hosts(const std::string& message, char delimiter) {
bool VirtualServer::start(std::string& error) {
{
threads::Mutex lock(this->stateLock);
std::unique_lock state_lock{this->state_mutex};
if(this->state != ServerState::OFFLINE){
error = "Server isn't offline";
return false;
@ -591,7 +591,7 @@ bool VirtualServer::start(std::string& error) {
this->music_manager_->connectBots();
{
threads::MutexLock lock(this->stateLock);
std::unique_lock state_lock{this->state_mutex};
this->state = ServerState::ONLINE;
}
return true;
@ -610,8 +610,10 @@ bool VirtualServer::running() {
void VirtualServer::preStop(const std::string& reason) {
{
threads::MutexLock lock(this->stateLock);
if(!this->running() && this->state != ServerState::SUSPENDING) return;
std::unique_lock state_lock{this->state_mutex};
if(!this->running() && this->state != ServerState::SUSPENDING) {
return;
}
this->state = ServerState::SUSPENDING;
}
@ -634,7 +636,7 @@ void VirtualServer::stop(const std::string& reason, bool disconnect_query) {
auto self_lock = this->self.lock();
assert(self_lock);
{
threads::MutexLock lock(this->stateLock);
std::unique_lock state_lock{this->state_mutex};
if(!this->running() && this->state != ServerState::SUSPENDING) return;
this->state = ServerState::SUSPENDING;
}
@ -681,7 +683,7 @@ void VirtualServer::stop(const std::string& reason, bool disconnect_query) {
properties()[property::VIRTUALSERVER_UPTIME] = 0;
{
threads::MutexLock lock(this->stateLock);
std::unique_lock state_lock{this->state_mutex};
this->state = ServerState::OFFLINE;
}
this->serverRoot->server = nullptr;
@ -961,8 +963,7 @@ vector<pair<ts::permission::PermissionType, ts::permission::v2::PermissionFlagge
ClientDbId client_dbid,
ClientType client_type,
ChannelId channel_id,
bool calculate_granted,
std::shared_ptr<CalculateCache> cache) {
bool calculate_granted) {
ClientPermissionCalculator calculator{this->ref(), client_dbid, client_type, channel_id};
return calculator.calculate_permissions(permissions, calculate_granted);
}
@ -972,9 +973,8 @@ permission::v2::PermissionFlaggedValue VirtualServer::calculate_permission(
ClientDbId cldbid,
ClientType type,
ChannelId channel,
bool granted,
std::shared_ptr<CalculateCache> cache) {
auto result = this->calculate_permissions({permission}, cldbid, type, channel, granted, cache);
bool granted) {
auto result = this->calculate_permissions({permission}, cldbid, type, channel, granted);
if(result.empty()) return {0, false};
return result.front().second;

View File

@ -15,7 +15,7 @@
#include "manager/BanManager.h"
#include "Definitions.h"
#include "ConnectionStatistics.h"
#include "manager/TokeManager.h"
#include "manager/TokenManager.h"
#include "manager/ComplainManager.h"
#include "DatabaseHelper.h"
#include "manager/LetterManager.h"
@ -226,8 +226,7 @@ namespace ts {
ClientDbId,
ClientType type,
ChannelId channel,
bool granted = false,
std::shared_ptr<CalculateCache> cache = nullptr
bool granted = false
);
std::vector<std::pair<permission::PermissionType, permission::v2::PermissionFlaggedValue>> calculate_permissions(
@ -235,8 +234,7 @@ namespace ts {
ClientDbId,
ClientType type,
ChannelId channel,
bool granted = false,
std::shared_ptr<CalculateCache> cache = nullptr
bool granted = false
);
bool verifyServerPassword(std::string, bool hashed = false);
@ -301,8 +299,8 @@ namespace ts {
std::weak_ptr<VirtualServer> self;
//Locks by tick, start and stop
threads::Mutex stateLock;
ServerState::value state = ServerState::OFFLINE;
std::shared_mutex state_mutex{};
ServerState::value state{ServerState::OFFLINE};
task_id tick_task_id{};
std::chrono::system_clock::time_point lastTick;
@ -348,9 +346,8 @@ namespace ts {
std::shared_ptr<ConnectedClient> serverRoot = nullptr;
std::shared_ptr<ConnectedClient> serverAdmin = nullptr;
threads::Mutex join_attempts_lock;
std::mutex join_attempts_lock;
std::map<std::string, uint16_t > join_attempts;
threads::Mutex join_lock;
std::chrono::system_clock::time_point join_last_decrease;
std::chrono::milliseconds spoken_time{0};

View File

@ -412,7 +412,7 @@ bool VirtualServerManager::deleteServer(shared_ptr<VirtualServer> server) {
}
}
{
threads::MutexLock locK(server->stateLock);
std::unique_lock state_lock{server->state_mutex};
server->state = ServerState::DELETING;
}

View File

@ -123,11 +123,10 @@ std::shared_ptr<ViewEntry> ClientChannelView::find_channel(const std::shared_ptr
return head ? static_pointer_cast<ViewEntry>(head->entry) : nullptr;
}
std::deque<std::shared_ptr<ViewEntry>> ClientChannelView::insert_channels(shared_ptr<TreeView::LinkedTreeEntry> head, bool test_permissions, bool first_only, std::shared_ptr<server::CalculateCache> cache) {
std::deque<std::shared_ptr<ViewEntry>> ClientChannelView::insert_channels(shared_ptr<TreeView::LinkedTreeEntry> head, bool test_permissions, bool first_only) {
std::deque<std::shared_ptr<ViewEntry>> result;
if(!cache && test_permissions) cache = make_shared<CalculateCache>();
bool has_perm = !test_permissions || permission::v2::permission_granted(1, owner->calculate_permission(permission::b_channel_ignore_view_power, 0, false, cache));
bool has_perm = !test_permissions || permission::v2::permission_granted(1, owner->calculate_permission(permission::b_channel_ignore_view_power, 0, false));
bool first = true;
while(head) {
if(!first && first_only) break;
@ -136,7 +135,7 @@ std::deque<std::shared_ptr<ViewEntry>> ClientChannelView::insert_channels(shared
auto channel = dynamic_pointer_cast<BasicChannel>(head->entry);
if(this->channel_visible(channel)) {
if(head->child_head) {
for(const auto& sub : this->insert_channels(head->child_head, test_permissions, false, cache))
for(const auto& sub : this->insert_channels(head->child_head, test_permissions, false))
result.push_back(sub);
}
@ -183,7 +182,7 @@ std::deque<std::shared_ptr<ViewEntry>> ClientChannelView::insert_channels(shared
result.push_back(entry);
if(head->child_head) {
for(const auto& sub : this->insert_channels(head->child_head, test_permissions, false, cache))
for(const auto& sub : this->insert_channels(head->child_head, test_permissions, false))
result.push_back(sub);
}
head = head->next;
@ -244,11 +243,9 @@ std::deque<std::shared_ptr<ViewEntry>> ClientChannelView::show_channel(std::shar
}
std::deque<std::shared_ptr<ViewEntry>> ClientChannelView::test_channel(std::shared_ptr<ts::TreeView::LinkedTreeEntry> l_old,
std::shared_ptr<ts::TreeView::LinkedTreeEntry> channel_new, shared_ptr<CalculateCache> cache) {
if(!cache) cache = make_shared<CalculateCache>();
std::shared_ptr<ts::TreeView::LinkedTreeEntry> channel_new) {
std::deque<std::shared_ptr<ViewEntry>> result;
bool has_perm = permission::v2::permission_granted(1, owner->calculate_permission(permission::b_channel_ignore_view_power, 0, false, cache));
bool has_perm = permission::v2::permission_granted(1, owner->calculate_permission(permission::b_channel_ignore_view_power, 0, false));
if(has_perm) return {};
deque<shared_ptr<TreeView::LinkedTreeEntry>> parents = {l_old};
@ -284,14 +281,13 @@ std::deque<std::shared_ptr<ViewEntry>> ClientChannelView::test_channel(std::shar
}
std::deque<std::pair<bool, std::shared_ptr<ViewEntry>>> ClientChannelView::update_channel(
std::shared_ptr<ts::TreeView::LinkedTreeEntry> l_channel, std::shared_ptr<ts::TreeView::LinkedTreeEntry> l_own, shared_ptr<CalculateCache> cache) {
return update_channel_path(std::move(l_channel), std::move(l_own), std::move(cache), 1);
std::shared_ptr<ts::TreeView::LinkedTreeEntry> l_channel, std::shared_ptr<ts::TreeView::LinkedTreeEntry> l_own) {
return update_channel_path(std::move(l_channel), std::move(l_own), 1);
}
std::deque<std::pair<bool, std::shared_ptr<ViewEntry>>> ClientChannelView::update_channel_path(std::shared_ptr<ts::TreeView::LinkedTreeEntry> l_channel, std::shared_ptr<ts::TreeView::LinkedTreeEntry> l_own, shared_ptr<CalculateCache> cache, ssize_t length) {
if(!cache) cache = make_shared<CalculateCache>();
std::deque<std::pair<bool, std::shared_ptr<ViewEntry>>> ClientChannelView::update_channel_path(std::shared_ptr<ts::TreeView::LinkedTreeEntry> l_channel, std::shared_ptr<ts::TreeView::LinkedTreeEntry> l_own, ssize_t length) {
std::deque<std::pair<bool, std::shared_ptr<ViewEntry>>> result;
bool has_perm = permission::v2::permission_granted(1, owner->calculate_permission(permission::b_channel_ignore_view_power, 0, false, cache));
bool has_perm = permission::v2::permission_granted(1, owner->calculate_permission(permission::b_channel_ignore_view_power, 0, false));
while(l_channel && length-- != 0) {
auto b_channel = dynamic_pointer_cast<BasicChannel>(l_channel->entry);
@ -313,20 +309,20 @@ std::deque<std::pair<bool, std::shared_ptr<ViewEntry>>> ClientChannelView::updat
if(visible) {
for(const auto& entry : this->show_channel(l_channel, visible))
result.emplace_back(true, entry);
for(const auto& entry : this->insert_channels(l_channel->child_head, true, false, cache))
for(const auto& entry : this->insert_channels(l_channel->child_head, true, false))
result.emplace_back(true, entry);
}
l_channel = l_channel->next;
continue; /* all subchannels had been checked */
} else if(visible && !has_perm) {
for(const auto& entry : this->test_channel(l_channel, l_own, cache))
for(const auto& entry : this->test_channel(l_channel, l_own))
result.emplace_back(false, entry);
}
//Root node is okey, test children
if(l_channel->child_head) {
auto entries = this->update_channel_path(l_channel->child_head, l_own, cache, -1);
auto entries = this->update_channel_path(l_channel->child_head, l_own, -1);
result.insert(result.end(), entries.begin(), entries.end());
}

View File

@ -56,8 +56,7 @@ namespace ts {
std::deque<std::shared_ptr<ViewEntry>> insert_channels(
std::shared_ptr<TreeView::LinkedTreeEntry> /* head */,
bool test_permissions,
bool first_only,
std::shared_ptr<server::CalculateCache> cache = nullptr
bool first_only
);
/* shows the specific channel and their parents */
@ -69,22 +68,19 @@ namespace ts {
/* remove invalid channel */
std::deque<std::shared_ptr<ViewEntry>> test_channel(
std::shared_ptr<TreeView::LinkedTreeEntry> /* old channel */,
std::shared_ptr<TreeView::LinkedTreeEntry> /* new channel */,
std::shared_ptr<server::CalculateCache> cache = nullptr
std::shared_ptr<TreeView::LinkedTreeEntry> /* new channel */
);
/* [{ add := true | delete := false, channel}] */
std::deque<std::pair<bool, std::shared_ptr<ViewEntry>>> update_channel(
std::shared_ptr<TreeView::LinkedTreeEntry> /* channel */,
std::shared_ptr<TreeView::LinkedTreeEntry> /* own channel */,
std::shared_ptr<server::CalculateCache> cache = nullptr
std::shared_ptr<TreeView::LinkedTreeEntry> /* own channel */
);
/* [{ add := true | delete := false, channel}] */
std::deque<std::pair<bool, std::shared_ptr<ViewEntry>>> update_channel_path(
std::shared_ptr<TreeView::LinkedTreeEntry> /* channel */,
std::shared_ptr<TreeView::LinkedTreeEntry> /* own channel */,
std::shared_ptr<server::CalculateCache> cache = nullptr,
ssize_t length = -1
);

View File

@ -72,7 +72,7 @@ bool ConnectedClient::loadDataForCurrentServer() {
return false;
}
this->properties()[property::CONNECTION_CLIENT_IP] = this->getLoggingPeerIp();
serverInstance->databaseHelper()->updateClientIpAddress(this->getServerId(), this->getClientDatabaseId(), this->getLoggingPeerIp());
return true;
}
@ -1162,7 +1162,6 @@ permission::PermissionType ConnectedClient::calculate_and_get_join_state(const s
}
auto channel_id = channel->channelId();
auto permission_cache = make_shared<CalculateCache>();
switch(channel->channelType()) {
case ChannelType::permanent:
if(!permission::v2::permission_granted(1, this->calculate_permission(permission::b_channel_join_permanent, channel_id))) {

View File

@ -380,8 +380,9 @@ bool ConnectedClient::notifyConnectionInfo(const shared_ptr<ConnectedClient> &ta
}
if(info) {
for(const auto& [key, value] : info->properties)
for(const auto& [key, value] : info->properties) {
bulk.put(key, value);
}
} else {
//Fill in what we can, else we trust the client
if(target->getType() == ClientType::CLIENT_TEASPEAK || target->getType() == ClientType::CLIENT_TEAMSPEAK || target->getType() == ClientType::CLIENT_WEB) {
@ -403,13 +404,15 @@ bool ConnectedClient::notifyConnectionInfo(const shared_ptr<ConnectedClient> &ta
bulk.put(property::CONNECTION_PACKETS_SENT_SPEECH, stats.connection_packets_sent[stats::ConnectionStatistics::category::VOICE]);
}
}
if(auto vc = dynamic_pointer_cast<VoiceClient>(target); vc) {
bulk.put(property::CONNECTION_PING, floor<milliseconds>(vc->current_ping()).count());
bulk.put(property::CONNECTION_PING_DEVIATION, vc->current_ping_deviation());
}
#ifdef COMPILE_WEB_CLIENT
else if(dynamic_pointer_cast<WebClient>(target))
else if(dynamic_pointer_cast<WebClient>(target)) {
bulk.put(property::CONNECTION_PING, floor<milliseconds>(dynamic_pointer_cast<WebClient>(target)->client_ping()).count());
}
#endif
if(auto vc = dynamic_pointer_cast<VoiceClient>(target); vc){
@ -823,7 +826,7 @@ bool ConnectedClient::notifyChannelSubscribed(const deque<shared_ptr<BasicChanne
Command notify("notifychannelsubscribed");
int index = 0;
for (const auto& ch : channels) {
notify[index]["es"] = this->server->getClientsByChannel(ch).empty() ? ch->emptySince() : 0;
notify[index]["es"] = this->server->getClientsByChannel(ch).empty() ? ch->empty_seconds() : 0;
notify[index++]["cid"] = ch->channelId();
}
this->sendCommand(notify);
@ -834,7 +837,7 @@ bool ConnectedClient::notifyChannelUnsubscribed(const deque<shared_ptr<BasicChan
Command notify("notifychannelunsubscribed");
int index = 0;
for (const auto& ch : channels) {
notify[index]["es"] = this->server->getClientsByChannel(ch).empty() ? ch->emptySince() : 0;
notify[index]["es"] = this->server->getClientsByChannel(ch).empty() ? ch->empty_seconds() : 0;
notify[index++]["cid"] = ch->channelId();
}
this->sendCommand(notify);

View File

@ -152,15 +152,14 @@ bool DataClient::loadDataForCurrentServer() {
std::vector<std::pair<permission::PermissionType, permission::v2::PermissionFlaggedValue>> DataClient::calculate_permissions(
const std::deque<permission::PermissionType> &permissions,
ChannelId channel,
bool granted,
std::shared_ptr<CalculateCache>) {
bool granted) {
ts::server::ClientPermissionCalculator calculator{this, channel};
return calculator.calculate_permissions(permissions, granted);
}
permission::v2::PermissionFlaggedValue DataClient::calculate_permission(
permission::PermissionType permission, ChannelId channel, bool granted, std::shared_ptr<CalculateCache>) {
permission::PermissionType permission, ChannelId channel, bool granted) {
ts::server::ClientPermissionCalculator calculator{this, channel};
return calculator.calculate_permission(permission, granted);
}

View File

@ -43,14 +43,12 @@ namespace ts {
* This method can be called from everywhere without any locking needed.
* @param channel
* @param granted
* @param cache
* @return
*/
permission::v2::PermissionFlaggedValue calculate_permission(
permission::PermissionType,
ChannelId channel,
bool granted = false,
std::shared_ptr<CalculateCache> cache = nullptr
bool granted = false
);
/**
@ -58,14 +56,12 @@ namespace ts {
* This method can be called from everywhere without any locking needed.
* @param channel
* @param granted
* @param cache
* @return
*/
std::vector<std::pair<permission::PermissionType, permission::v2::PermissionFlaggedValue>> calculate_permissions(
const std::deque<permission::PermissionType>&,
ChannelId channel,
bool granted = false,
std::shared_ptr<CalculateCache> cache = nullptr
bool granted = false
);
virtual std::vector<std::shared_ptr<groups::ServerGroup>> assignedServerGroups();

View File

@ -100,7 +100,7 @@ command_result SpeakingClient::handleCommandClientInit(Command& cmd) {
TIMING_START(timings);
{
lock_guard<threads::Mutex> lock(this->server->join_attempts_lock);
lock_guard lock(this->server->join_attempts_lock);
auto client_address = this->getPeerIp();
auto& client_join_attempts = this->server->join_attempts[client_address];
auto& general_join_attempts = this->server->join_attempts["_"];
@ -252,9 +252,11 @@ command_result SpeakingClient::handleCommandClientInit(Command& cmd) {
}
TIMING_STEP(timings, "valid hw ip");
if(!permission::v2::permission_granted(1, permissions[permission::b_virtualserver_join_ignore_password]))
if(!this->server->verifyServerPassword(cmd["client_server_password"].string(), true))
if(!permission::v2::permission_granted(1, permissions[permission::b_virtualserver_join_ignore_password])) {
if(!this->server->verifyServerPassword(cmd["client_server_password"].string(), true)) {
return command_result{error::server_invalid_password};
}
}
if(!config::server::clients::ignore_max_clone_permissions) {
size_t clones_uid = 0;
@ -300,7 +302,7 @@ command_result SpeakingClient::handleCommandClientInit(Command& cmd) {
string fullReason = string() + "You are banned " + (banEntry->serverId == 0 ? "globally" : "from this server") + ". Reason: \"" + banEntry->reason + "\". Ban expires ";
string time;
if(banEntry->until.time_since_epoch().count() != 0){
if(banEntry->until.time_since_epoch().count() != 0) {
time += "in ";
auto seconds = chrono::ceil<chrono::seconds>(banEntry->until - chrono::system_clock::now()).count();
tm p{};
@ -310,30 +312,43 @@ command_result SpeakingClient::handleCommandClientInit(Command& cmd) {
p.tm_year++;
seconds -= 365 * 24 * 60 * 60;
}
while(seconds >= 24 * 60 * 60){
p.tm_yday++;
seconds -= 24 * 60 * 60;
}
while(seconds >= 60 * 60){
p.tm_hour++;
seconds -= 60 * 60;
}
while(seconds >= 60){
p.tm_min++;
seconds -= 60;
}
p.tm_sec = (int) seconds;
if(p.tm_year > 0)
if(p.tm_year > 0) {
time += to_string(p.tm_year) + " years, ";
if(p.tm_yday > 0)
}
if(p.tm_yday > 0) {
time += to_string(p.tm_yday) + " days, ";
if(p.tm_hour > 0)
}
if(p.tm_hour > 0) {
time += to_string(p.tm_hour) + " hours, ";
if(p.tm_min > 0)
}
if(p.tm_min > 0) {
time += to_string(p.tm_min) + " minutes, ";
if(p.tm_sec > 0)
}
if(p.tm_sec > 0) {
time += to_string(p.tm_sec) + " seconds, ";
}
if(time.empty()) time = "now, ";
time = time.substr(0, time.length() - 2);
} else time = "never";
@ -344,11 +359,12 @@ command_result SpeakingClient::handleCommandClientInit(Command& cmd) {
TIMING_STEP(timings, "ban resolve");
size_t count = 0;
{
for(const auto &cl : this->server->getClients())
if((cl->getType() == CLIENT_TEAMSPEAK || cl->getType() == CLIENT_WEB || cl->getType() == CLIENT_TEASPEAK || cl->getType() == CLIENT_MUSIC))
if(cl->connectionState() <= ConnectionState::CONNECTED && cl->connectionState() >= ConnectionState::INIT_HIGH)
count++;
for(const auto &cl : this->server->getClients()) {
if((cl->getType() == CLIENT_TEAMSPEAK || cl->getType() == CLIENT_WEB || cl->getType() == CLIENT_TEASPEAK || cl->getType() == CLIENT_MUSIC)) {
if(cl->connectionState() <= ConnectionState::CONNECTED && cl->connectionState() >= ConnectionState::INIT_HIGH) {
count++;
}
}
}
auto maxClients = this->server->properties()[property::VIRTUALSERVER_MAXCLIENTS].as_or<size_t>(0);
@ -356,15 +372,17 @@ command_result SpeakingClient::handleCommandClientInit(Command& cmd) {
bool allowReserved = permission::v2::permission_granted(1, permissions[permission::b_client_use_reserved_slot]);
if(reserved > maxClients){
if(!allowReserved)
if(!allowReserved) {
return command_result{error::server_maxclients_reached};
} else if(maxClients - (allowReserved ? 0 : reserved) <= count)
}
} else if(maxClients - (allowReserved ? 0 : reserved) <= count) {
return command_result{error::server_maxclients_reached};
}
TIMING_STEP(timings, "max clients");
auto old_last_connected = this->properties()[property::CLIENT_LASTCONNECTED].as_or<int64_t>(0);
this->properties()[property::CONNECTION_CLIENT_IP] = this->getLoggingPeerIp();
serverInstance->databaseHelper()->updateClientIpAddress(this->getServerId(), this->getClientDatabaseId(), this->getLoggingPeerIp());
this->properties()[property::CLIENT_LASTCONNECTED] = std::chrono::duration_cast<std::chrono::seconds>(std::chrono::system_clock::now().time_since_epoch()).count();
this->properties()[property::CLIENT_TOTALCONNECTIONS].increment_by<uint64_t>(1);
{

View File

@ -2167,7 +2167,7 @@ command_result ConnectedClient::handleCommandChannelInfo(Command &cmd) {
for (const auto &prop : channel->properties()->list_properties(property::FLAG_CHANNEL_VIEW | property::FLAG_CHANNEL_VARIABLE, this->getType() == CLIENT_TEAMSPEAK ? property::FLAG_NEW : (uint16_t) 0))
res[prop.type().name] = prop.value();
res["seconds_empty"] = channel->emptySince();
res["seconds_empty"] = channel->empty_seconds();
res["pid"] = res["cpid"].string();
this->sendCommand(res);

View File

@ -513,7 +513,10 @@ command_result ConnectedClient::handleCommandClientEdit(Command &cmd, const std:
logError(this->getServerId(), R"([{}] Tried to change a client property to an invalid value for {}. (Key: "{}", Value: "{}"))", CLIENT_STR_LOG_PREFIX, CLIENT_STR_LOG_PREFIX_(client), key, cmd[key].string());
continue;
}
if(client->properties()[&info].as_unchecked<string>() == cmd[key].as<string>()) continue;
if(client->properties()[&info].as_unchecked<string>() == cmd[key].as<string>()) {
continue;
}
if (info == property::CLIENT_DESCRIPTION) {
if (self) {
@ -836,7 +839,7 @@ command_result ConnectedClient::handleCommandClientList(Command &cmd) {
if (cmd.hasParm("country"))
result[index]["client_country"] = client->properties()[property::CLIENT_COUNTRY].as_unchecked<string>();
if (cmd.hasParm("ip"))
result[index]["connection_client_ip"] = allow_ip ? client->properties()[property::CONNECTION_CLIENT_IP].as_unchecked<string>() : "hidden";
result[index]["connection_client_ip"] = allow_ip ? client->getPeerIp() : "hidden";
if (cmd.hasParm("icon"))
result[index]["client_icon_id"] = client->properties()[property::CLIENT_ICON_ID].as_unchecked<string>();
@ -1098,7 +1101,7 @@ command_result ConnectedClient::handleCommandClientDbInfo(Command &cmd) {
auto props = serverInstance->databaseHelper()->loadClientProperties(this->server, info->client_database_id, ClientType::CLIENT_TEAMSPEAK);
if(allow_ip) {
bulk.put_unchecked("client_lastip", (*props)[property::CONNECTION_CLIENT_IP].as_unchecked<string>());
bulk.put_unchecked("client_lastip", info->client_ip);
} else {
bulk.put_unchecked("client_lastip", "hidden");
}
@ -1244,22 +1247,56 @@ command_result ConnectedClient::handleCommandClientInfo(Command &cmd) {
continue;
}
for (const auto &key : client->properties()->list_properties(property::FLAG_CLIENT_VIEW | property::FLAG_CLIENT_VARIABLE | property::FLAG_CLIENT_INFO, this->getType() == CLIENT_TEAMSPEAK ? property::FLAG_NEW : (uint16_t) 0))
for (const auto &key : client->properties()->list_properties(property::FLAG_CLIENT_VIEW | property::FLAG_CLIENT_VARIABLE | property::FLAG_CLIENT_INFO, this->getType() == CLIENT_TEAMSPEAK ? property::FLAG_NEW : (uint16_t) 0)) {
res[result_index][std::string{key.type().name}] = key.value();
if(view_remote)
res[result_index]["connection_client_ip"] = client->properties()[property::CONNECTION_CLIENT_IP].value();
else
res[result_index]["connection_client_ip"] = "hidden";
res[result_index]["client_idle_time"] = duration_cast<milliseconds>(system_clock::now() - client->idleTimestamp).count();
res[result_index]["connection_connected_time"] = duration_cast<milliseconds>(system_clock::now() - client->connectTimestamp).count();
{
auto channel = client->currentChannel;
if(channel)
res[result_index]["cid"] = channel->channelId();
else
res[result_index]["cid"] = 0;
}
res[result_index]["cid"] = client->getChannelId();
if(view_remote) {
res[result_index][property::CONNECTION_CLIENT_IP] = client->getPeerIp();
} else {
res[result_index][property::CONNECTION_CLIENT_IP] = "hidden";
}
res[result_index][property::CONNECTION_CONNECTED_TIME] = duration_cast<milliseconds>(system_clock::now() - client->connectTimestamp).count();
auto total_stats = this->getConnectionStatistics()->total_stats();
res["connection_packets_sent_total"] = std::accumulate(total_stats.connection_packets_sent.begin(), total_stats.connection_packets_sent.end(), (size_t) 0U);
res["connection_bytes_sent_total"] = std::accumulate(total_stats.connection_bytes_sent.begin(), total_stats.connection_bytes_sent.end(), (size_t) 0U);
res["connection_packets_received_total"] = std::accumulate(total_stats.connection_packets_received.begin(), total_stats.connection_packets_received.end(), (size_t) 0U);
res["connection_bytes_received_total"] = std::accumulate(total_stats.connection_bytes_received.begin(), total_stats.connection_bytes_received.end(), (size_t) 0U);
auto report_second = this->getConnectionStatistics()->second_stats();
auto report_minute = this->getConnectionStatistics()->minute_stats();
res["connection_bandwidth_sent_last_second_total"] = std::accumulate(report_second.connection_bytes_sent.begin(), report_second.connection_bytes_sent.end(), (size_t) 0U);
res["connection_bandwidth_sent_last_minute_total"] = std::accumulate(report_minute.connection_bytes_sent.begin(), report_minute.connection_bytes_sent.end(), (size_t) 0U);
res["connection_bandwidth_received_last_second_total"] = std::accumulate(report_second.connection_bytes_received.begin(), report_second.connection_bytes_received.end(), (size_t) 0U);
res["connection_bandwidth_received_last_minute_total"] = std::accumulate(report_minute.connection_bytes_received.begin(), report_minute.connection_bytes_received.end(), (size_t) 0U);
res["connection_filetransfer_bandwidth_sent"] = report_second.file_bytes_sent;
res["connection_filetransfer_bandwidth_received"] = report_second.file_bytes_received;
res["connection_filetransfer_bytes_sent_total"] = total_stats.file_bytes_sent;
res["connection_filetransfer_bytes_received_total"] = total_stats.file_bytes_received;
float server2client_packetloss{0};
float client2server_packetloss{0}; /* TODO: Parse from the client connect parameters? */
if(auto vc = dynamic_pointer_cast<VoiceClient>(this->ref()); vc) {
server2client_packetloss = vc->current_packet_loss();
}
if(auto data{this->connection_info.data}; data) {
try {
client2server_packetloss = std::stof(data->properties["connection_server2client_packetloss_total"]);
} catch(std::exception&) {}
}
res["connection_packetloss_total"] = (server2client_packetloss + client2server_packetloss) / 2;
res["connection_server2client_packetloss_total"] = server2client_packetloss;
res["connection_client2server_packetloss_total"] = client2server_packetloss;
/* TODO: Is this really right? It might be property::CONNECTION_IDLE_TIME. It might also be that CONNECTION_IDLE_TIME should be client_idle_time */
res[result_index]["client_idle_time"] = duration_cast<milliseconds>(system_clock::now() - client->idleTimestamp).count();
result_index++;
}

View File

@ -4,7 +4,7 @@
/* use this for any action which will do something with the server */
#define ACTION_REQUIRES_GLOBAL_PERMISSION_CACHED(required_permission_type, required_value, cache) \
do { \
if(!permission::v2::permission_granted(required_value, this->calculate_permission(required_permission_type, 0, false, cache))) \
if(!permission::v2::permission_granted(required_value, this->calculate_permission(required_permission_type, 0, false))) \
return command_result{required_permission_type}; \
} while(0)
@ -56,76 +56,10 @@ auto channel_id = command.as<ChannelId>(); \
auto l_channel = channel_id ? channel_tree->findLinkedChannel(command.as<ChannelId>()) : nullptr; \
if (!l_channel && (channel_id != 0 || force)) return command_result{error::channel_invalid_id, "Cant resolve channel"}; \
//TODO: Map permsid!
#define PARSE_PERMISSION(cmd) \
permission::PermissionType permType = permission::PermissionType::unknown; \
bool grant = false; \
if (cmd[index].has("permid")) { \
permType = cmd[index]["permid"].as<permission::PermissionType>(); \
if ((permType & PERM_ID_GRANT) != 0) { \
grant = true; \
permType &= ~PERM_ID_GRANT; \
} \
} else if (cmd[index].has("permsid")) { \
auto resv = permission::resolvePermissionData(cmd[index]["permsid"].as<string>()); \
permType = resv->type; \
if (resv->grantName() == cmd[index]["permsid"].as<string>()) grant = true; \
} \
if (permission::resolvePermissionData(permType)->type == permission::PermissionType::unknown) { \
if (conOnError) continue; \
return ts::command_result{error::parameter_invalid}; \
}
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wswitch-enum"
inline bool permission_require_granted_value(ts::permission::PermissionType type) {
using namespace ts;
/*
switch (type) {
case permission::i_permission_modify_power:
case permission::i_channel_group_member_add_power:
case permission::i_channel_group_member_remove_power:
case permission::i_channel_group_modify_power:
case permission::i_channel_group_needed_member_add_power:
case permission::i_channel_group_needed_member_remove_power:
case permission::i_channel_group_needed_modify_power:
case permission::i_server_group_member_add_power:
case permission::i_server_group_member_remove_power:
case permission::i_server_group_modify_power:
case permission::i_server_group_needed_member_add_power:
case permission::i_server_group_needed_member_remove_power:
case permission::i_server_group_needed_modify_power:
case permission::i_displayed_group_member_add_power:
case permission::i_displayed_group_member_remove_power:
case permission::i_displayed_group_modify_power:
case permission::i_displayed_group_needed_member_add_power:
case permission::i_displayed_group_needed_member_remove_power:
case permission::i_displayed_group_needed_modify_power:
case permission::i_channel_permission_modify_power:
case permission::i_channel_needed_permission_modify_power:
case permission::i_client_permission_modify_power:
case permission::i_client_needed_permission_modify_power:
case permission::i_client_needed_kick_from_server_power:
case permission::i_client_needed_kick_from_channel_power:
case permission::i_client_kick_from_channel_power:
case permission::i_client_kick_from_server_power:
return true;
default:
return false;
}
*/
switch (type) {
case permission::i_icon_id:

View File

@ -3056,7 +3056,7 @@ command_result ConnectedClient::handleCommandDummy_IpChange(ts::Command &cmd) {
}
}
this->properties()[property::CONNECTION_CLIENT_IP] = this->getLoggingPeerIp();
serverInstance->databaseHelper()->updateClientIpAddress(this->getServerId(), this->getClientDatabaseId(), this->getLoggingPeerIp());
return command_result{error::ok};
}

View File

@ -71,7 +71,6 @@ command_result ConnectedClient::handleCommandServerEdit(Command &cmd) {
ServerId serverId = target_server ? target_server->serverId : 0;
auto group_manager = target_server ? target_server->group_manager() : serverInstance->group_manager();
auto cache = make_shared<CalculateCache>();
map<string, string> toApplay;
for (auto &key : cmd[0].keys()) {
if (key == "sid") continue;

View File

@ -66,15 +66,13 @@ void QueryClient::initialize_weak_reference(const std::shared_ptr<ConnectedClien
std::make_unique<QueryClientCommandHandler>(dynamic_pointer_cast<QueryClient>(self))
);
this->event_read = event_new(this->handle->event_io_loop, this->client_file_descriptor, EV_READ | EV_PERSIST, [](int a, short b, void* c){
((QueryClient *) c)->handle_event_read(a, b, c); }, this);
this->event_write = event_new(this->handle->event_io_loop, this->client_file_descriptor, EV_WRITE, [](int a, short b, void* c){
((QueryClient *) c)->handle_event_write(a, b, c); }, this);
this->event_read = event_new(this->handle->event_io_loop, this->client_file_descriptor, EV_READ | EV_PERSIST, QueryClient::handle_event_read, this);
this->event_write = event_new(this->handle->event_io_loop, this->client_file_descriptor, EV_WRITE, QueryClient::handle_event_write, this);
}
QueryClient::~QueryClient() {
if(this->line_buffer) {
free(this->line_buffer);
::free(this->line_buffer);
this->line_buffer = nullptr;
}
@ -259,12 +257,14 @@ void QueryClient::enqueue_write_buffer(const std::string_view &message) {
buffer->unref();
}
void QueryClient::handle_event_write(int fd, short, void *) {
void QueryClient::handle_event_write(int fd, short, void *ptr_client) {
auto client = (QueryClient*) ptr_client;
NetworkBuffer* write_buffer{nullptr};
{
std::lock_guard buffer_lock{this->network_mutex};
if(this->write_buffer_head) {
write_buffer = this->write_buffer_head->ref();
std::lock_guard buffer_lock{client->network_mutex};
if(client->write_buffer_head) {
write_buffer = client->write_buffer_head->ref();
}
}
@ -274,17 +274,17 @@ void QueryClient::handle_event_write(int fd, short, void *) {
write_buffer->unref();
if (errno == EINTR || errno == EAGAIN) {
std::lock_guard event_lock{this->network_mutex};
if(this->event_write) {
event_add(this->event_write, nullptr);
std::lock_guard event_lock{client->network_mutex};
if(client->event_write) {
event_add(client->event_write, nullptr);
}
} else {
logError(LOG_QUERY, "{} Failed to send message ({}/{}). Closing connection.", CLIENT_STR_LOG_PREFIX, errno, strerror(errno));
this->close_connection(std::chrono::system_clock::time_point{});
logError(LOG_QUERY, "{} Failed to send message ({}/{}). Closing connection.", client->getLoggingPrefix(), errno, strerror(errno));
client->close_connection(std::chrono::system_clock::time_point{});
{
std::unique_lock event_lock{this->network_mutex};
auto event_write_ = std::exchange(this->event_write, nullptr);
std::unique_lock event_lock{client->network_mutex};
auto event_write_ = std::exchange(client->event_write, nullptr);
event_lock.unlock();
if(event_write_) {
@ -315,23 +315,23 @@ void QueryClient::handle_event_write(int fd, short, void *) {
/* Buffer finished, sending next one */
{
std::lock_guard buffer_lock{this->network_mutex};
if(this->write_buffer_head == write_buffer) {
std::lock_guard buffer_lock{client->network_mutex};
if(client->write_buffer_head == write_buffer) {
/* Buffer successfully send. Sending the next one. */
cleanup_buffer = this->write_buffer_head;
cleanup_buffer = client->write_buffer_head;
this->write_buffer_head = this->write_buffer_head->next_buffer;
if(this->write_buffer_head) {
client->write_buffer_head = client->write_buffer_head->next_buffer;
if(client->write_buffer_head) {
/* we've a next buffer */
write_buffer = this->write_buffer_head->ref();
write_buffer = client->write_buffer_head->ref();
} else {
assert(this->write_buffer_tail == &write_buffer->next_buffer);
assert(client->write_buffer_tail == &write_buffer->next_buffer);
write_buffer = nullptr;
this->write_buffer_tail = &this->write_buffer_head;
client->write_buffer_tail = &client->write_buffer_head;
}
} else if(this->write_buffer_head) {
} else if(client->write_buffer_head) {
/* Our buffer got dropped (who knows why). Just send the next one. */
write_buffer = this->write_buffer_head->ref();
write_buffer = client->write_buffer_head->ref();
} else {
/*
* Nothing more to write.
@ -349,15 +349,17 @@ void QueryClient::handle_event_write(int fd, short, void *) {
/* This state should only be reached when no more messages are pending to write */
assert(!write_buffer);
if(this->state == ConnectionState::DISCONNECTING) {
this->handle->enqueue_query_connection_close(dynamic_pointer_cast<QueryClient>(this->ref()));
if(client->state == ConnectionState::DISCONNECTING) {
client->handle->enqueue_query_connection_close(dynamic_pointer_cast<QueryClient>(client->ref()));
}
}
void QueryClient::handle_event_read(int fd, short, void *) {
void QueryClient::handle_event_read(int fd, short, void *ptr_client) {
static const size_t kReadBufferLength = 1024 * 8;
uint8_t buffer[kReadBufferLength];
auto client = (QueryClient*) ptr_client;
auto length = read(fd, buffer, kReadBufferLength);
if(length <= 0){
if(errno == EINTR || errno == EAGAIN) {
@ -366,15 +368,15 @@ void QueryClient::handle_event_read(int fd, short, void *) {
}
if(length == 0 && errno == 0) {
logMessage(LOG_QUERY, "{} Connection closed. Client disconnected.", CLIENT_STR_LOG_PREFIX);
logMessage(LOG_QUERY, "{} Connection closed. Client disconnected.", client->getLoggingPrefix());
} else {
logMessage(LOG_QUERY, "{} Failed to received message ({}/{}). Closing connection.", CLIENT_STR_LOG_PREFIX, errno, strerror(errno));
logMessage(LOG_QUERY, "{} Failed to received message ({}/{}). Closing connection.", client->getLoggingPrefix(), errno, strerror(errno));
}
this->close_connection(std::chrono::system_clock::time_point{});
client->close_connection(std::chrono::system_clock::time_point{});
{
std::unique_lock network_lock{this->network_mutex};
auto event_read_ = std::exchange(this->event_read, nullptr);
std::unique_lock network_lock{client->network_mutex};
auto event_read_ = std::exchange(client->event_read, nullptr);
network_lock.unlock();
if(event_read_) {
@ -387,7 +389,7 @@ void QueryClient::handle_event_read(int fd, short, void *) {
return;
}
this->handle_message_read(std::string_view{(const char *) buffer, (size_t) length});
client->handle_message_read(std::string_view{(const char *) buffer, (size_t) length});
}
inline bool is_ssl_handshake_header(const std::string_view& buffer) {

View File

@ -74,12 +74,12 @@ namespace ts::server {
void tick_query();
/* Methods will be called within the io loop (single thread) */
void handle_event_read(int, short, void*);
static void handle_event_read(int, short, void*);
void handle_message_read(const std::string_view& /* message */);
void handle_decoded_message(const std::string_view& /* message */);
/* Methods will be called within the io loop (single thread) */
void handle_event_write(int, short, void*);
static void handle_event_write(int, short, void*);
void send_message(const std::string_view&);
void enqueue_write_buffer(const std::string_view& /* message */);
private:

View File

@ -610,7 +610,7 @@ command_result QueryClient::handleCommandChannelList(Command& cmd) {
channel->properties()[property::CHANNEL_TOPIC].as_unchecked<string>());
}
if(cmd.hasParm("times") || cmd.hasParm("secondsempty")){
result.put_unchecked(index, "seconds_empty", channel_clients == 0 ? channel->emptySince() : 0);
result.put_unchecked(index, "seconds_empty", channel_clients == 0 ? channel->empty_seconds() : 0);
}
index++;
}

View File

@ -15,8 +15,9 @@ using namespace ts::connection;
using namespace ts::server::server::udp;
inline void generate_random(uint8_t *destination, size_t length) {
while(length-- > 0)
while(length-- > 0) {
*(destination++) = (uint8_t) rand();
}
}
CryptSetupHandler::CryptSetupHandler(VoiceClientConnection *connection) : connection{connection} { }
@ -97,8 +98,9 @@ CryptSetupHandler::CommandResult CryptSetupHandler::handleCommandClientInitIv(co
{
std::unique_lock server_channel_lock(client->server->get_channel_tree_lock()); /* we cant get moved if this is locked! */
if(client->currentChannel)
if(client->currentChannel) {
client->server->client_move(client->ref(), nullptr, nullptr, config::messages::timeout::connection_reinitialized, ViewReasonId::VREASON_TIMEOUT, false, server_channel_lock);
}
}
client->finalDisconnect();
@ -132,8 +134,9 @@ CryptSetupHandler::CommandResult CryptSetupHandler::handleCommandClientInitIv(co
/* normal TeamSpeak handling */
this->seed_client = base64::decode(cmd.value("alpha"));
if(this->seed_client.length() != 10)
if(this->seed_client.length() != 10) {
return ts::command_result{error::parameter_invalid, "alpha"};
}
std::string clientOmega = base64::decode(cmd.value("omega")); //The identity public key
std::string ip = cmd.value("ip");

View File

@ -43,15 +43,11 @@ void PacketEncoder::reset() {
}
while(whead) {
auto next = whead->next;
whead->unref();
whead = next;
std::exchange(whead, whead->next)->unref();
}
while(rhead) {
auto next = rhead->next;
rhead->unref();
rhead = next;
std::exchange(rhead, rhead->next)->unref();
}
}
@ -96,8 +92,7 @@ void PacketEncoder::send_packet_acknowledge(uint16_t pid, bool low) {
#define MAX_COMMAND_PACKET_PAYLOAD_LENGTH (487)
void PacketEncoder::send_command(const std::string_view &command, bool low, std::unique_ptr<std::function<void(bool)>> ack_listener) {
bool own_data_buffer{false};
void* own_data_buffer_ptr; /* immutable! */
std::optional<void*> temp_data_buffer{};
const char* data_buffer{command.data()};
size_t data_length{command.length()};
@ -121,10 +116,9 @@ void PacketEncoder::send_command(const std::string_view &command, bool low, std:
/* we don't need to make the command longer than it is */
if(compressed_size < command.length()) {
own_data_buffer = true;
data_buffer = (char*) compressed_buffer;
own_data_buffer_ptr = compressed_buffer;
data_length = compressed_size;
data_buffer = (char*) compressed_buffer;
temp_data_buffer = std::make_optional(compressed_buffer);
head_pflags |= protocol::PacketFlag::Compressed;
} else {
::free(compressed_buffer);
@ -151,6 +145,7 @@ void PacketEncoder::send_command(const std::string_view &command, bool low, std:
packet->type_and_flags_ |= protocol::PacketFlag::Fragmented;
break;
}
data_buffer += bytes;
}
packets_head->type_and_flags_ |= protocol::PacketFlag::Fragmented;
@ -220,39 +215,29 @@ void PacketEncoder::send_command(const std::string_view &command, bool low, std:
}
this->callback_request_write(this->callback_data);
if(own_data_buffer) {
::free(own_data_buffer_ptr);
if(temp_data_buffer.has_value()) {
::free(*temp_data_buffer);
}
}
void PacketEncoder::encrypt_pending_packets() {
protocol::OutgoingServerPacket* packets_head;
protocol::OutgoingServerPacket *packets_head, **packets_tail;
{
std::lock_guard wlock{this->write_queue_mutex};
packets_head = this->encrypt_queue_head;
this->encrypt_queue_head = nullptr;
this->encrypt_queue_tail = &this->encrypt_queue_head;
packets_head = std::exchange(this->encrypt_queue_head, nullptr);
packets_tail = std::exchange(this->encrypt_queue_tail, &this->encrypt_queue_head);
}
if(!packets_head) {
return;
}
auto packet = packets_head;
auto packet_tail = packet;
while(packet) {
this->encrypt_outgoing_packet(packet);
packet = packet->next;
if(packet) {
packet_tail = packet;
}
auto current_packet{packets_head};
while(current_packet) {
this->encrypt_outgoing_packet(current_packet);
current_packet = current_packet->next;
}
{
std::lock_guard wlock{this->write_queue_mutex};
*this->send_queue_tail = packets_head;
this->send_queue_tail = &packet_tail->next;
this->send_queue_tail = packets_tail;
}
}
@ -282,16 +267,19 @@ bool PacketEncoder::encrypt_outgoing_packet(ts::protocol::OutgoingServerPacket *
crypt_key, crypt_nonce,
error
);
if(!crypt_result) {
this->callback_crypt_error(this->callback_data, CryptError::KEY_GENERATION_FAILED, error);
return false;
}
}
return true;
}
PacketEncoder::BufferPopResult PacketEncoder::pop_write_buffer(protocol::OutgoingServerPacket *&result) {
bool need_encrypt{false}, more_packets;
{
std::lock_guard wlock{this->write_queue_mutex};
if(this->send_queue_head) {
@ -314,10 +302,12 @@ PacketEncoder::BufferPopResult PacketEncoder::pop_write_buffer(protocol::Outgoin
this->encrypt_queue_head = nullptr;
this->encrypt_queue_tail = &this->encrypt_queue_head;
}
need_encrypt = true;
} else {
return BufferPopResult::DRAINED;
}
result->next = nullptr;
more_packets = this->send_queue_head != nullptr || this->encrypt_queue_head != nullptr;
}

View File

@ -100,7 +100,7 @@ command_result VoiceClient::handleCommandClientInit(Command &cmd) {
auto requiredLevel = this->getServer()->properties()[property::VIRTUALSERVER_NEEDED_IDENTITY_SECURITY_LEVEL].as_unchecked<uint8_t>();
if(security_level < requiredLevel) {
return command_result{error::client_could_not_validate_identity, to_string(requiredLevel)};
return command_result{error::client_could_not_validate_identity, std::to_string(requiredLevel)};
}
}

View File

@ -1,16 +0,0 @@
#include <tommath.h>
#include <tomcrypt.h>
#include <misc/digest.h>
#include <misc/base64.h>
#include <openssl/sha.h>
#include <log/LogUtils.h>
#include <src/client/SpeakingClient.h>
#include "VoiceClient.h"
#include "../../InstanceHandler.h"
using namespace std;
using namespace std::chrono;
using namespace ts::server;
using namespace ts::protocol;
using namespace ts::connection;

View File

@ -7,7 +7,7 @@
#include <random>
#include <misc/rnd.h>
#include <log/LogUtils.h>
#include "TokeManager.h"
#include "TokenManager.h"
using namespace std;
using namespace std::chrono;

View File

@ -1,8 +1,6 @@
#include "PlayablePlaylist.h"
#include "src/VirtualServer.h"
#include "src/client/music/MusicClient.h"
#include "src/client/ConnectedClient.h"
#include "MusicPlaylist.h"
#include <log/LogUtils.h>
using namespace ts;

View File

@ -18,29 +18,29 @@ struct LibCallbackData {
std::weak_ptr<SpeakingClient> weak_ref;
};
#define log(...) \
switch(level) { \
case 0: \
logTrace(__VA_ARGS__); \
break; \
case 1: \
debugMessage(__VA_ARGS__); \
break; \
case 2: \
logMessage(__VA_ARGS__); \
break; \
case 3: \
logWarning(__VA_ARGS__); \
break; \
case 4: \
logError(__VA_ARGS__); \
break; \
case 5: \
logCritical(__VA_ARGS__); \
break; \
default: \
debugMessage(__VA_ARGS__); \
break; \
#define log(...) \
switch(level) { \
case 0: \
logTrace(__VA_ARGS__); \
break; \
case 1: \
debugMessage(__VA_ARGS__); \
break; \
case 2: \
logMessage(__VA_ARGS__); \
break; \
case 3: \
logWarning(__VA_ARGS__); \
break; \
case 4: \
logError(__VA_ARGS__); \
break; \
case 5: \
logCritical(__VA_ARGS__); \
break; \
default: \
debugMessage(__VA_ARGS__); \
break; \
}
void librtc_callback_log(uint8_t level, const void* callback_data_ptr, const char* message_ptr, uint32_t length) {

View File

@ -1,15 +1,23 @@
#include "./PrecomputedPuzzles.h"
#include "src/Configuration.h"
#include "..//Configuration.h"
#include <tomcrypt.h>
using namespace std;
using namespace ts::server::udp;
Puzzle::Puzzle() {
mp_init_multi(&this->x, &this->n, &this->result, nullptr);
}
Puzzle::~Puzzle() {
mp_clear_multi(&this->x, &this->n, &this->result, nullptr);
}
PuzzleManager::PuzzleManager() = default;
PuzzleManager::~PuzzleManager() = default;
size_t PuzzleManager::precomputed_puzzle_count() {
std::lock_guard lock{this->cache_lock};
std::lock_guard lock{this->cache_mutex};
return this->cached_puzzles.size();
}
@ -18,14 +26,16 @@ bool PuzzleManager::precompute_puzzles(size_t amount) {
std::mt19937 mt{rd()};
amount = 5;
while(this->precomputed_puzzle_count() < amount)
while(this->precomputed_puzzle_count() < amount) {
this->generate_puzzle(mt);
}
return this->precomputed_puzzle_count() > 0;
}
std::shared_ptr<Puzzle> PuzzleManager::next_puzzle() {
{
std::lock_guard lock{this->cache_lock};
std::lock_guard lock{this->cache_mutex};
auto it = this->cached_puzzles.begin() + (this->cache_index++ % this->cached_puzzles.size());
if((*it)->fail_count > 2) {
this->cached_puzzles.erase(it);
@ -44,8 +54,9 @@ inline void random_number(std::mt19937& generator, mp_int *result, int length){
std::uniform_int_distribution<uint8_t> dist{};
uint8_t buffer[length];
for(auto& byte : buffer)
for(auto& byte : buffer) {
byte = dist(generator);
}
mp_zero(result);
mp_read_unsigned_bin(result, buffer, length);
@ -56,7 +67,6 @@ inline bool solve_puzzle(Puzzle *puzzle) {
mp_init(&exp);
mp_2expt(&exp, puzzle->level);
if (mp_exptmod(&puzzle->x, &exp, &puzzle->n, &puzzle->result) != CRYPT_OK) { //Sometimes it fails (unknown why :D)
mp_clear(&exp);
return false;
@ -68,57 +78,51 @@ inline bool solve_puzzle(Puzzle *puzzle) {
inline bool write_bin_data(mp_int& data, uint8_t* result, size_t length) {
ulong n{length};
if(auto err = mp_to_unsigned_bin_n(&data, result, &n); err)
if(auto err = mp_to_unsigned_bin_n(&data, result, &n); err) {
return false;
}
if(n != length) {
auto off = length - n;
memcpy(result + off, result, n);
memmove(result + off, result, n);
memset(result, 0, off);
}
return true;
}
void PuzzleManager::generate_puzzle(std::mt19937& random_generator) {
auto puzzle = new Puzzle{};
auto puzzle = std::make_shared<Puzzle>();
puzzle->level = ts::config::voice::RsaPuzzleLevel;
mp_init_multi(&puzzle->x, &puzzle->n, &puzzle->result, nullptr);
generate_new:
static int x{0};
if(x++ == 0 || true) {
mp_set(&puzzle->x, 1);
mp_set(&puzzle->n, 1);
//random_number(random_generator, &puzzle->x, 64);
//random_number(random_generator, &puzzle->n, 64);
} else {
const static std::string_view n_{"\x01\x7a\xc5\x8d\x28\x7a\x61\x58\xf6\xe3\x98\x60\x2f\x81\x9c\x8a\x48\xc9\x20\xd1\x59\xe0\x24\x75\x91\x27\x9f\x52\x1e\x2c\x24\x85\xa9\xdc\x74\xfa\x0b\x36\xf9\x6c\x77\xa3\x7c\xf9\xbb\xf7\x04\xad\xa3\x84\x0d\x97\x25\x54\x19\x72\x4f\x8f\xfc\x66\xbe\x41\xda\x95"};
const static std::string_view x_{"\xd1\xef\xf0\x16\x34\x48\x56\x53\x15\x97\xa0\x28\xbd\x13\xce\xbf\xc2\xd6\x79\x9d\x21\x81\x83\x37\x8c\xe8\xee\xee\xa1\x22\xa4\xf5\x63\x33\x53\x0c\x38\x2f\x0a\x00\x53\x20\xc7\x93\x52\xa9\xd0\xc2\xfb\xbc\xc5\xc4\xc3\x54\xad\xcb\x49\x52\xc0\xd8\x97\x32\x94\xee"};
mp_read_unsigned_bin(&puzzle->x, (unsigned char*) x_.data(), x_.length());
mp_read_unsigned_bin(&puzzle->n, (unsigned char*) n_.data(), n_.length());
while(true) {
random_number(random_generator, &puzzle->x, 64);
random_number(random_generator, &puzzle->n, 64);
if(!solve_puzzle(&*puzzle)) {
continue;
}
auto valid_x = mp_unsigned_bin_size(&puzzle->x) <= 64;
auto valid_n = mp_unsigned_bin_size(&puzzle->n) <= 64;
auto valid_result = mp_unsigned_bin_size(&puzzle->result) <= 64;
if(!valid_n || !valid_x || !valid_result) {
continue;
}
if(!write_bin_data(puzzle->x, puzzle->data_x, 64)) {
continue;
}
if(!write_bin_data(puzzle->n, puzzle->data_n, 64)) {
continue;
}
if(!write_bin_data(puzzle->result, puzzle->data_result, 64)) {
continue;
}
/* everything seems to be good */
break;
}
if(!solve_puzzle(puzzle))
goto generate_new;
auto valid_x = mp_unsigned_bin_size(&puzzle->x) <= 64;
auto valid_n = mp_unsigned_bin_size(&puzzle->n) <= 64;
auto valid_result = mp_unsigned_bin_size(&puzzle->result) <= 64;
if(!valid_n || !valid_x || !valid_result)
goto generate_new;
if(!write_bin_data(puzzle->x, puzzle->data_x, 64))
goto generate_new;
if(!write_bin_data(puzzle->n, puzzle->data_n, 64))
goto generate_new;
if(!write_bin_data(puzzle->result, puzzle->data_result, 64))
goto generate_new;
this->cached_puzzles.push_back(shared_ptr<Puzzle>(puzzle, [](Puzzle* elm){
mp_clear_multi(&elm->n, &elm->x, &elm->result, nullptr);
delete elm;
}));
this->cached_puzzles.push_back(std::move(puzzle));
}

View File

@ -2,6 +2,7 @@
#include <tommath.h>
#include <memory>
#include <mutex>
#include <vector>
#include <misc/spin_mutex.h>
#include <random>
@ -19,6 +20,9 @@ namespace ts::server::udp {
uint8_t data_result[64]{0};
size_t fail_count{0};
Puzzle();
~Puzzle();
};
class PuzzleManager {
@ -35,7 +39,7 @@ namespace ts::server::udp {
void generate_puzzle(std::mt19937&);
size_t cache_index{0};
spin_mutex cache_lock{};
std::mutex cache_mutex{};
std::vector<std::shared_ptr<Puzzle>> cached_puzzles{};
};
}

View File

@ -669,7 +669,7 @@ void QueryServer::tick_executor() {
auto pending_closes = std::move(this->tick_pending_connection_close);
if(!this->tick_active) {
if(this->tick_pending_connection_close.empty() && this->tick_pending_connection_close.empty()) {
if(pending_disconnects.empty() && pending_closes.empty()) {
/* We're done with our work */
break;
}

View File

@ -23,12 +23,6 @@ namespace ts {
class VirtualServer;
class QueryClient;
struct QueryLoginCredentials {
std::string username;
std::string password;
ClientUid uniqueId;
};
class QueryAccount {
public:
std::string username;

View File

@ -5,7 +5,6 @@
#include "VoiceIOManager.h"
#include "VoiceServer.h"
#include "src/client/voice/VoiceClient.h"
#include "protocol/buffers.h"
using namespace std;
using namespace std::chrono;

View File

@ -104,20 +104,22 @@ void WebControlServer::on_client_receive(int _server_file_descriptor, short ev,
int file_descriptor = accept(_server_file_descriptor, (struct sockaddr *) &remote_address, &address_length);
if (file_descriptor < 0) {
if(errno == EAGAIN)
if(errno == EAGAIN) {
return;
}
if(errno == EMFILE || errno == ENFILE) {
if(errno == EMFILE)
if(errno == EMFILE) {
logError(this->handle->getServerId(), "[Web] Server ran out file descriptors. Please increase the process file descriptor limit.");
else
} else {
logError(this->handle->getServerId(), "[Web] Server ran out file descriptors. Please increase the process and system-wide file descriptor limit.");
}
bool tmp_close_success = false;
{
lock_guard reserve_fd_lock(server_reserve_fd_lock);
if(this->server_reserve_fd > 0) {
debugMessage(this->handle->getServerId(), "[Web] Trying to accept client with the reserved file descriptor to close the incomming connection.");
debugMessage(this->handle->getServerId(), "[Web] Trying to accept client with the reserved file descriptor to close the incoming connection.");
auto _ = [&]{
if(close(this->server_reserve_fd) < 0) {
debugMessage(this->handle->getServerId(), "[Web] Failed to close reserved file descriptor");
@ -170,7 +172,7 @@ void WebControlServer::on_client_receive(int _server_file_descriptor, short ev,
}
shared_ptr<WebClient> client = std::make_shared<WebClient>(this, file_descriptor);
auto client = std::make_shared<WebClient>(this, file_descriptor);
memcpy(&client->remote_address, &remote_address, sizeof(remote_address));
client->initialize_weak_reference(client);
client->initialize();
@ -219,12 +221,12 @@ void WebControlServer::stop() {
void WebControlServer::unregisterConnection(const std::shared_ptr<WebClient>& connection) {
//TODO may checks?
{
threads::MutexLock l(this->clientLock);
for(const auto& cl : this->clients) {
if(cl == connection) {
clients.erase(std::find(this->clients.begin(), this->clients.end(), cl));
break;
}
std::lock_guard lock{this->clientLock};
auto index = std::find(this->clients.begin(), this->clients.end(), connection);
if(index == this->clients.end()) {
return;
}
this->clients.erase(index);
}
}

View File

@ -44,8 +44,8 @@ namespace ts {
bool _running = false;
std::deque<std::shared_ptr<Binding>> bindings;
std::mutex server_reserve_fd_lock;
int server_reserve_fd = -1; /* -1 = unset | 0 = in use | > 0 ready to use */
std::mutex server_reserve_fd_lock{};
int server_reserve_fd{-1}; /* -1 = unset | 0 = in use | > 0 ready to use */
//IO stuff
std::chrono::system_clock::time_point accept_event_deleted;

View File

@ -179,8 +179,9 @@ bool process_next_command() {
append_write_buffer("ok\n");
}
for(const auto& line : handle.response)
for(const auto& line : handle.response) {
append_write_buffer(line + "\n");
}
append_write_buffer("\r\n");
append_write_buffer("\r\n");

View File

@ -1,23 +0,0 @@
permission calculation
channel group inheritance
general server & channel groups
- assignments
- removement
- temporary groups
Notified channel & server member add/remove powers
Group edit if the notify<server/channel> group triggers
Icon IDs seem to be buggy with groups
group permission copy dosn't work
permission reset
servergroupclientlist
server default group edit
channelgroupclientlist
temporary group/channel assignments
Test with `kCacheAllClients` and without `kCacheAllClients`
TODO:
- Delete the group header

2
shared

@ -1 +1 @@
Subproject commit 8fb93d1d9d938b78dcf34e7f7953e3d41504d00f
Subproject commit 88158742e873a9ecc076091a76b52af85ca32687