Some general code cleanup
This commit is contained in:
parent
25b454ad0e
commit
f41cfb8c30
@ -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
|
||||
|
||||
|
@ -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();
|
||||
|
@ -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);
|
||||
|
@ -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;
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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; }
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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)))
|
||||
|
@ -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(),
|
||||
|
@ -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;
|
||||
|
@ -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};
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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());
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
);
|
||||
|
||||
|
@ -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))) {
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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();
|
||||
|
@ -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);
|
||||
{
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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++;
|
||||
}
|
||||
|
||||
|
@ -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:
|
||||
|
@ -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};
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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) {
|
||||
|
@ -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:
|
||||
|
@ -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++;
|
||||
}
|
||||
|
@ -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");
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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)};
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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;
|
@ -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;
|
@ -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;
|
||||
|
@ -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) {
|
||||
|
@ -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));
|
||||
}
|
@ -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{};
|
||||
};
|
||||
}
|
@ -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;
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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");
|
||||
|
@ -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
2
shared
@ -1 +1 @@
|
||||
Subproject commit 8fb93d1d9d938b78dcf34e7f7953e3d41504d00f
|
||||
Subproject commit 88158742e873a9ecc076091a76b52af85ca32687
|
Loading…
Reference in New Issue
Block a user