383 lines
16 KiB
C++
383 lines
16 KiB
C++
#pragma once
|
|
|
|
#include <deque>
|
|
#include <memory>
|
|
#include <functional>
|
|
#include <ThreadPool/ThreadPool.h>
|
|
#include <arpa/inet.h>
|
|
#include <BasicChannel.h>
|
|
#include <sqlite3.h>
|
|
#include <sql/SqlQuery.h>
|
|
#include "Group.h"
|
|
#include "Properties.h"
|
|
#include "query/Command.h"
|
|
#include "channel/ServerChannel.h"
|
|
#include "manager/BanManager.h"
|
|
#include "Definitions.h"
|
|
#include "ConnectionStatistics.h"
|
|
#include "manager/TokenManager.h"
|
|
#include "manager/ComplainManager.h"
|
|
#include "DatabaseHelper.h"
|
|
#include "manager/LetterManager.h"
|
|
#include "Configuration.h"
|
|
#include "protocol/ringbuffer.h"
|
|
#include "absl/btree/map.h"
|
|
#include <misc/task_executor.h>
|
|
|
|
#include <tomcrypt.h>
|
|
#undef byte
|
|
|
|
#ifdef COMPILE_WEB_CLIENT
|
|
#include "server/WebServer.h"
|
|
#endif
|
|
|
|
template<typename T, typename _Tp>
|
|
inline bool operator==(T* elm, const std::shared_ptr<_Tp>& __a) noexcept { return elm == __a.get(); }
|
|
|
|
template<typename T, typename _Tp>
|
|
inline bool operator==(const std::shared_ptr<_Tp>& __a, T* elm) noexcept { return elm == __a.get(); }
|
|
|
|
template<typename T, typename _Tp>
|
|
inline bool operator!=(T* elm, const std::shared_ptr<_Tp>& __a) noexcept { return elm != __a.get(); }
|
|
|
|
template<typename T, typename _Tp>
|
|
inline bool operator!=(const std::shared_ptr<_Tp>& __a, T* elm) noexcept { return elm != __a.get(); }
|
|
|
|
namespace ts {
|
|
class ServerChannelTree;
|
|
|
|
namespace music {
|
|
class MusicBotManager;
|
|
}
|
|
|
|
namespace rtc {
|
|
class Server;
|
|
}
|
|
|
|
namespace server {
|
|
class ConnectedClient;
|
|
class VoiceClient;
|
|
class QueryClient;
|
|
class WebClient;
|
|
class InternalClient;
|
|
|
|
class InstanceHandler;
|
|
class VoiceServer;
|
|
class QueryServer;
|
|
class SpeakingClient;
|
|
|
|
class WebControlServer;
|
|
|
|
namespace conversation {
|
|
class ConversationManager;
|
|
}
|
|
|
|
namespace groups {
|
|
class ServerGroup;
|
|
class ChannelGroup;
|
|
class GroupManager;
|
|
}
|
|
|
|
struct ServerState {
|
|
enum value {
|
|
OFFLINE,
|
|
BOOTING,
|
|
ONLINE,
|
|
SUSPENDING,
|
|
DELETING
|
|
};
|
|
|
|
inline static std::string string(value state) {
|
|
switch (state) {
|
|
case ServerState::OFFLINE:
|
|
return "offline";
|
|
case ServerState::BOOTING:
|
|
return "booting";
|
|
case ServerState::ONLINE:
|
|
return "online";
|
|
case ServerState::SUSPENDING:
|
|
return "suspending";
|
|
case ServerState::DELETING:
|
|
return "deleting";
|
|
default:
|
|
return "unknown";
|
|
}
|
|
}
|
|
};
|
|
|
|
struct ServerSlotUsageReport {
|
|
size_t server_count{0};
|
|
|
|
size_t max_clients{0};
|
|
size_t reserved_clients{0};
|
|
|
|
size_t clients_teamspeak{0};
|
|
size_t clients_teaspeak{0};
|
|
size_t clients_teaweb{0};
|
|
size_t queries{0};
|
|
size_t music_bots{0};
|
|
|
|
size_t online_channels{0};
|
|
|
|
[[nodiscard]] inline size_t voice_clients() const {
|
|
return this->clients_teaspeak + this->clients_teamspeak + this->clients_teaweb;
|
|
}
|
|
|
|
inline ServerSlotUsageReport& operator+=(const ServerSlotUsageReport& other) {
|
|
this->server_count += other.server_count;
|
|
this->max_clients += other.max_clients;
|
|
this->reserved_clients += other.reserved_clients;
|
|
this->clients_teamspeak += other.clients_teamspeak;
|
|
this->clients_teaspeak += other.clients_teaspeak;
|
|
this->clients_teaweb += other.clients_teaweb;
|
|
this->queries += other.queries;
|
|
this->music_bots += other.music_bots;
|
|
this->online_channels += other.online_channels;
|
|
return *this;
|
|
}
|
|
};
|
|
|
|
struct CalculateCache {};
|
|
|
|
class VirtualServer {
|
|
friend class WebClient;
|
|
friend class DataClient;
|
|
friend class VoiceClient;
|
|
friend class MusicClient;
|
|
friend class ConnectedClient;
|
|
friend class InternalClient;
|
|
friend class QueryServer;
|
|
friend class QueryClient;
|
|
friend class SpeakingClient;
|
|
friend class music::MusicBotManager;
|
|
friend class InstanceHandler;
|
|
friend class VirtualServerManager;
|
|
public:
|
|
struct NetworkReport {
|
|
float average_ping{0};
|
|
float average_loss{0};
|
|
};
|
|
|
|
VirtualServer(ServerId serverId, sql::SqlManager*);
|
|
~VirtualServer();
|
|
|
|
bool initialize(bool test_properties);
|
|
|
|
bool start(std::string& error);
|
|
bool running();
|
|
void preStop(const std::string&);
|
|
void stop(const std::string& reason, bool /* disconnect query */);
|
|
|
|
ServerSlotUsageReport onlineStats();
|
|
std::shared_ptr<ConnectedClient> find_client_by_id(ClientId /* client id */);
|
|
std::deque<std::shared_ptr<ConnectedClient>> findClientsByCldbId(ClientDbId cldbId);
|
|
std::deque<std::shared_ptr<ConnectedClient>> findClientsByUid(ClientUid uid);
|
|
std::shared_ptr<ConnectedClient> findClient(std::string name, bool ignoreCase = true);
|
|
bool forEachClient(std::function<void(std::shared_ptr<ConnectedClient>)>);
|
|
//bool forEachClient(std::function<std::shared_ptr<VoiceClient>>, bool executeLaterIfLocked = true);
|
|
|
|
std::vector<std::shared_ptr<ConnectedClient>> getClients();
|
|
std::deque<std::shared_ptr<ConnectedClient>> getClientsByChannel(std::shared_ptr<BasicChannel>);
|
|
std::deque<std::shared_ptr<ConnectedClient>> getClientsByChannelRoot(const std::shared_ptr<BasicChannel> &, bool lock_channel_tree);
|
|
[[nodiscard]] size_t countChannelRootClients(const std::shared_ptr<BasicChannel> &, size_t /* limit */, bool /* lock the channel tree */);
|
|
[[nodiscard]] bool isChannelRootEmpty(const std::shared_ptr<BasicChannel> &, bool lock_channel_tree);
|
|
|
|
template <typename ClType>
|
|
std::vector<std::shared_ptr<ClType>> getClientsByChannel(const std::shared_ptr<BasicChannel>& ch) {
|
|
std::vector<std::shared_ptr<ClType>> result;
|
|
for(const auto& cl : this->getClientsByChannel(ch))
|
|
if(std::dynamic_pointer_cast<ClType>(cl))
|
|
result.push_back(std::dynamic_pointer_cast<ClType>(cl));
|
|
return result;
|
|
}
|
|
|
|
ecc_key* serverKey(){ return _serverKey; }
|
|
std::string publicServerKey();
|
|
|
|
inline PropertyWrapper properties() { return PropertyWrapper{this->_properties}; }
|
|
inline const PropertyWrapper properties() const { return PropertyWrapper{this->_properties}; }
|
|
|
|
inline sql::SqlManager * getSql(){ return this->sql; }
|
|
sql::AsyncSqlPool* getSqlPool(){ return this->sql->pool; }
|
|
|
|
inline ServerId getServerId(){ return this->serverId; }
|
|
inline ServerChannelTree* getChannelTree(){ return this->channelTree; }
|
|
inline rtc::Server& rtc_server() { return *this->rtc_server_; }
|
|
|
|
[[nodiscard]] inline auto& getTokenManager() {
|
|
return *this->tokenManager;
|
|
}
|
|
|
|
[[nodiscard]] inline auto group_manager() { return this->groups_manager_; }
|
|
[[nodiscard]] std::shared_ptr<groups::ServerGroup> default_server_group();
|
|
[[nodiscard]] std::shared_ptr<groups::ChannelGroup> default_channel_group();
|
|
|
|
bool notifyServerEdited(std::shared_ptr<ConnectedClient>, std::deque<std::string> keys);
|
|
bool notifyClientPropertyUpdates(std::shared_ptr<ConnectedClient>, const std::deque<const property::PropertyDescription*>& keys, bool selfNotify = true); /* execute only with at least channel tree read lock! */
|
|
inline bool notifyClientPropertyUpdates(const std::shared_ptr<ConnectedClient>& client, const std::deque<property::ClientProperties>& keys, bool selfNotify = true) {
|
|
if(keys.empty()) {
|
|
return false;
|
|
}
|
|
|
|
std::deque<const property::PropertyDescription*> _keys{};
|
|
for(const auto& key : keys) {
|
|
_keys.push_back(&property::describe(key));
|
|
}
|
|
|
|
return this->notifyClientPropertyUpdates(client, _keys, selfNotify);
|
|
};
|
|
|
|
void broadcastMessage(std::shared_ptr<ConnectedClient>, std::string message);
|
|
|
|
#ifndef __deprecated
|
|
#define __deprecated __attribute__((deprecated))
|
|
#endif
|
|
__deprecated void registerInternalClient(std::shared_ptr<ConnectedClient>);
|
|
__deprecated void unregisterInternalClient(std::shared_ptr<ConnectedClient>);
|
|
|
|
std::shared_ptr<ConnectedClient> getServerRoot(){ return this->serverRoot; }
|
|
|
|
std::string getDisplayName(){ return properties()[property::VIRTUALSERVER_NAME]; }
|
|
|
|
std::shared_ptr<stats::ConnectionStatistics> getServerStatistics(){ return server_statistics_; }
|
|
|
|
std::shared_ptr<VoiceServer> getVoiceServer(){ return this->udpVoiceServer; }
|
|
WebControlServer* getWebServer(){ return this->webControlServer; }
|
|
|
|
/* calculate permissions for an client in this server */
|
|
permission::v2::PermissionFlaggedValue calculate_permission(
|
|
permission::PermissionType,
|
|
ClientDbId,
|
|
ClientType type,
|
|
ChannelId channel,
|
|
bool granted = false
|
|
);
|
|
|
|
std::vector<std::pair<permission::PermissionType, permission::v2::PermissionFlaggedValue>> calculate_permissions(
|
|
const std::deque<permission::PermissionType>&,
|
|
ClientDbId,
|
|
ClientType type,
|
|
ChannelId channel,
|
|
bool granted = false
|
|
);
|
|
|
|
[[nodiscard]] bool verifyServerPassword(std::string /* password */, bool /* hashed */);
|
|
|
|
void testBanStateChange(const std::shared_ptr<ConnectedClient>& invoker);
|
|
|
|
[[nodiscard]] NetworkReport generate_network_report();
|
|
|
|
bool resetPermissions(std::string&);
|
|
void ensureValidDefaultGroups();
|
|
|
|
ServerState::value getState() { return this->state; }
|
|
|
|
inline std::shared_ptr<VirtualServer> ref() { return this->self.lock(); }
|
|
inline bool disable_ip_saving() { return this->_disable_ip_saving; }
|
|
inline std::chrono::system_clock::time_point start_timestamp() { return this->startTimestamp; };
|
|
|
|
/* Note: Use only this method to disconnect the client and notify everybody else that he has been banned! */
|
|
void notify_client_ban(const std::shared_ptr<ConnectedClient>& /* client */, const std::shared_ptr<ConnectedClient>& /* invoker */, const std::string& /* reason */, size_t /* length */);
|
|
void notify_client_kick(
|
|
const std::shared_ptr<ConnectedClient>& /* client */,
|
|
const std::shared_ptr<ConnectedClient>& /* invoker */,
|
|
const std::string& /* reason */,
|
|
const std::shared_ptr<BasicChannel>& /* target channel */
|
|
);
|
|
|
|
void client_move(
|
|
const std::shared_ptr<ConnectedClient>& /* client */,
|
|
std::shared_ptr<BasicChannel> /* target channel */,
|
|
const std::shared_ptr<ConnectedClient>& /* invoker */,
|
|
const std::string& /* reason */,
|
|
ViewReasonId /* reason id */,
|
|
bool /* notify the client */,
|
|
std::unique_lock<std::shared_mutex>& /* tree lock */
|
|
);
|
|
|
|
void delete_channel(
|
|
std::shared_ptr<ServerChannel> /* target channel */,
|
|
const std::shared_ptr<ConnectedClient>& /* invoker */,
|
|
const std::string& /* kick message */,
|
|
std::unique_lock<std::shared_mutex>& /* tree lock */,
|
|
bool temporary_auto_delete
|
|
);
|
|
|
|
void send_text_message(const std::shared_ptr<BasicChannel>& /* channel */, const std::shared_ptr<ConnectedClient>& /* sender */, const std::string& /* message */);
|
|
|
|
inline int voice_encryption_mode() { return this->_voice_encryption_mode; }
|
|
inline std::shared_ptr<conversation::ConversationManager> conversation_manager() { return this->conversation_manager_; }
|
|
|
|
inline auto& get_channel_tree_lock() { return this->channel_tree_mutex; }
|
|
|
|
void update_channel_from_permissions(const std::shared_ptr<BasicChannel>& /* channel */, const std::shared_ptr<ConnectedClient>& /* issuer */);
|
|
|
|
inline void enqueue_notify_channel_group_list() { this->task_notify_channel_group_list.enqueue(); }
|
|
inline void enqueue_notify_server_group_list() { this->task_notify_server_group_list.enqueue(); }
|
|
protected:
|
|
bool registerClient(std::shared_ptr<ConnectedClient>);
|
|
bool unregisterClient(std::shared_ptr<ConnectedClient>, std::string, std::unique_lock<std::shared_mutex>& channel_tree_lock);
|
|
bool assignDefaultChannel(const std::shared_ptr<ConnectedClient>&, bool join);
|
|
|
|
private:
|
|
std::weak_ptr<VirtualServer> self;
|
|
|
|
//Locks by tick, start and stop
|
|
std::shared_mutex state_mutex{};
|
|
ServerState::value state{ServerState::OFFLINE};
|
|
|
|
task_id tick_task_id{};
|
|
std::chrono::system_clock::time_point lastTick;
|
|
void executeServerTick();
|
|
|
|
std::shared_ptr<VoiceServer> udpVoiceServer = nullptr;
|
|
WebControlServer* webControlServer = nullptr;
|
|
token::TokenManager* tokenManager = nullptr;
|
|
ComplainManager* complains = nullptr;
|
|
letter::LetterManager* letters = nullptr;
|
|
std::shared_ptr<music::MusicBotManager> music_manager_;
|
|
std::shared_ptr<stats::ConnectionStatistics> server_statistics_;
|
|
std::shared_ptr<conversation::ConversationManager> conversation_manager_;
|
|
std::unique_ptr<rtc::Server> rtc_server_;
|
|
|
|
sql::SqlManager* sql;
|
|
|
|
uint16_t serverId = 1;
|
|
|
|
std::chrono::system_clock::time_point startTimestamp;
|
|
std::chrono::system_clock::time_point fileStatisticsTimestamp;
|
|
std::chrono::system_clock::time_point conversation_cache_cleanup_timestamp;
|
|
|
|
//The client list
|
|
std::mutex clients_mutex{};
|
|
btree::map<ClientId, std::shared_ptr<ConnectedClient>> clients{};
|
|
|
|
std::recursive_mutex client_nickname_lock;
|
|
|
|
//General server properties
|
|
ecc_key* _serverKey = nullptr;
|
|
std::shared_ptr<PropertyManager> _properties;
|
|
int _voice_encryption_mode = 2; /* */
|
|
|
|
ServerChannelTree* channelTree = nullptr;
|
|
std::shared_mutex channel_tree_mutex; /* lock if access channel tree! */
|
|
|
|
std::shared_ptr<groups::GroupManager> groups_manager_{};
|
|
|
|
std::shared_ptr<ConnectedClient> serverRoot = nullptr;
|
|
std::shared_ptr<ConnectedClient> serverAdmin = nullptr;
|
|
|
|
std::mutex join_attempts_lock;
|
|
std::map<std::string, uint16_t > join_attempts;
|
|
std::chrono::system_clock::time_point join_last_decrease;
|
|
|
|
std::chrono::milliseconds spoken_time{0};
|
|
std::chrono::system_clock::time_point spoken_time_timestamp;
|
|
|
|
multi_shot_task task_notify_channel_group_list{};
|
|
multi_shot_task task_notify_server_group_list{};
|
|
|
|
bool _disable_ip_saving = false;
|
|
};
|
|
}
|
|
} |