Implemented new chat system

This commit is contained in:
WolverinDEV 2019-07-23 10:37:56 +02:00
parent c720a80bc0
commit af4106614c
21 changed files with 451 additions and 177 deletions

View File

@ -419,7 +419,7 @@ int main(int argc, char** argv) {
}
/*
* 2][OUT] (188.225.34.225:9988) ftinitdownload clientftfid=4096 name=\/icon_166694597 cid=0 cpw seekpos=0 proto=1 return_code=
[02][OUT] (188.225.34.225:9988) ftinitdownload clientftfid=4096 name=\/icon_166694597 cid=0 cpw seekpos=0 proto=1 return_code=
[02][OUT] (188.225.34.225:9988) ftinitdownload clientftfid=4095 name=\/icon_4113966246 cid=0 cpw seekpos=0 proto=1 return_code=
[02][OUT] (188.225.34.225:9988) ftinitdownload clientftfid=4094 name=\/icon_3002705295 cid=0 cpw seekpos=0 proto=1 return_code=
[02][OUT] (188.225.34.225:9988) ftinitdownload clientftfid=4093 name=\/icon_494035633 cid=0 cpw seekpos=0 proto=1 return_code=

View File

@ -960,8 +960,8 @@ std::deque<std::shared_ptr<EntryBinding>> config::create_bindings() {
BIND_GROUP(web);
{
CREATE_BINDING("default_host", 0);
BIND_STRING(config::binding::DefaultWebHost, "0.0.0.0");
ADD_NOTE("Multibinding like the voice server isnt supported yet!");
BIND_STRING(config::binding::DefaultWebHost, "0.0.0.0,[::]");
ADD_NOTE("Multibinding supported here! Host delimiter is \",\"");
}
}
{

View File

@ -394,7 +394,7 @@ void DatabaseHelper::saveClientPermissions(const std::shared_ptr<ts::server::TSS
sql::command(this->sql, query,
variable{":serverId", server ? server->getServerId() : 0},
variable{":id", client_dbid},
variable{":chId", 0},
variable{":chId", update.channel_id},
variable{":type", permission::SQL_PERM_USER},
variable{":permId", permission_data->name},
@ -1081,7 +1081,7 @@ std::shared_ptr<Properties> DatabaseHelper::loadClientProperties(const std::shar
}
if(!prop.isModified()) return;
if((prop.type().flags & property::FLAG_SAVE) == 0 && (prop.type().flags & property::FLAG_SAVE_MUSIC) == 0) {
if((prop.type().flags & property::FLAG_SAVE) == 0 && (type != ClientType::CLIENT_MUSIC || (prop.type().flags & property::FLAG_SAVE_MUSIC) == 0)) {
logTrace(server ? server->getServerId() : 0, "[Property] Not saving property '" + prop.type().name + "', changed for " + to_string(cldbid) + " (New value: " + prop.value() + ")");
return;
}

View File

@ -502,6 +502,9 @@ std::deque<property::ClientProperties> GroupManager::update_server_group_propert
}
}
if(!changed.empty())
client->join_state_id++; /* groups have changed :) */
return changed;
}

View File

@ -372,26 +372,37 @@ bool TSServer::start(std::string& error) {
}
if(ts::config::web::activated && serverInstance->sslManager()->web_ssl_options()) {
string webHostname = this->properties()[property::VIRTUALSERVER_WEB_HOST];
if(webHostname.empty()) webHostname = this->properties()[property::VIRTUALSERVER_HOST].as<string>();
string web_host_string = this->properties()[property::VIRTUALSERVER_WEB_HOST];
if(web_host_string.empty())
web_host_string = this->properties()[property::VIRTUALSERVER_HOST].as<string>();
auto web_port = this->properties()[property::VIRTUALSERVER_WEB_PORT].as<uint16_t>();
if(web_port == 0)
web_port = this->properties()[property::VIRTUALSERVER_PORT].as<uint16_t>();
sockaddr_in webAddress{};
memset(&webAddress, 0, sizeof(webAddress));
webAddress.sin_family = AF_INET;
if(!evaluateAddress4(webHostname, webAddress.sin_addr)) {
error = "could not resolve host (WebServer)";
this->stop("Could not start web server");
return false;
}
auto port = this->properties()[property::VIRTUALSERVER_WEB_PORT].as<uint16_t>();
if(port == 0) port = this->properties()[property::VIRTUALSERVER_PORT].as<uint16_t>();
webAddress.sin_port = htons(port);
logMessage(this->serverId, "Starting web server on " + webHostname + ":" + to_string(port));
startTimestamp = std::chrono::system_clock::now();
#ifdef COMPILE_WEB_CLIENT
webControlServer = new WebControlServer(self.lock());
if(!webControlServer->start(&webAddress, error)) {
auto web_bindings = net::resolve_bindings(web_host_string, web_port);
deque<shared_ptr<WebControlServer::Binding>> bindings;
for(auto& binding : web_bindings) {
if(!get<2>(binding).empty()) {
logError(this->serverId, "[Web] Failed to resolve binding for {}: {}", get<0>(binding), get<2>(binding));
continue;
}
auto entry = make_shared<WebControlServer::Binding>();
memcpy(&entry->address, &get<1>(binding), sizeof(sockaddr_storage));
entry->file_descriptor = -1;
entry->event_accept = nullptr;
bindings.push_back(entry);
}
logMessage(this->serverId, "[Web] Starting server on {}:{}", web_host_string, web_port);
if(!webControlServer->start(bindings, error)) {
error = "could not start web server. Message: " + error;
this->stop("failed to start");
return false;
@ -735,7 +746,7 @@ void TSServer::broadcastMessage(std::shared_ptr<ConnectedClient> invoker, std::s
return;
}
this->forEachClient([&](shared_ptr<ConnectedClient> cl){
cl->notifyTextMessage(ChatMessageMode::TEXTMODE_SERVER, invoker, 0, message);
cl->notifyTextMessage(ChatMessageMode::TEXTMODE_SERVER, invoker, 0, 0, message);
});
}

View File

@ -317,7 +317,6 @@ std::deque<std::pair<bool, std::shared_ptr<ViewEntry>>> ClientChannelView::updat
l_channel = l_channel->next;
continue; /* all subchannels had been checked */
} else if(visible && !has_perm) {
//Test if tree comes invisible
for(const auto& entry : this->test_channel(l_channel, l_own, cache))
result.emplace_back(false, entry);
}

View File

@ -23,8 +23,9 @@ namespace ts {
bool subscribed = false;
bool editable = false;
bool _deleted = false;
bool joinable = false; /* used within notify text message */
ChannelId previous_channel = 0;
uint16_t join_state_id = 0; /* the calculation id for the flag joinable. If this does not match with the join_state_id within the client the flag needs to be recalculated */
std::weak_ptr<BasicChannel> handle;
std::chrono::system_clock::time_point view_timestamp;
private:

View File

@ -967,3 +967,54 @@ permission::v2::PermissionFlaggedValue ConnectedClient::calculate_permission_val
auto value = this->permissionValue(permission::PERMTEST_ORDERED, permission, nullptr);
return {value, value != permNotGranted};
}
#define RESULT(flag) \
do { \
ventry->join_state_id = this->join_state_id; \
ventry->joinable = (flag); \
return flag; \
} while(0)
bool ConnectedClient::calculate_and_get_join_state(const std::shared_ptr<BasicChannel>& channel) {
shared_ptr<ViewEntry> ventry;
{
shared_lock view_lock(this->channel_lock);
ventry = this->channel_view()->find_channel(channel);
if(!ventry)
return false;
}
if(ventry->join_state_id == this->join_state_id)
return ventry->joinable;
auto permission_cache = make_shared<CalculateCache>();
switch(channel->channelType()) {
case ChannelType::permanent:
if(!this->permissionGranted(permission::PERMTEST_ORDERED, permission::b_channel_join_permanent, 1, channel, true, permission_cache))
RESULT(false);
break;
case ChannelType::semipermanent:
if(!this->permissionGranted(permission::PERMTEST_ORDERED, permission::b_channel_join_semi_permanent, 1, channel, true, permission_cache))
RESULT(false);
break;
case ChannelType::temporary:
if(!this->permissionGranted(permission::PERMTEST_ORDERED, permission::b_channel_join_temporary, 1, channel, true, permission_cache))
RESULT(false);
break;
}
if(!this->permissionGranted(permission::PERMTEST_ORDERED, permission::b_channel_ignore_join_power, 1, channel, true, permission_cache)) {
auto result = this->server->calculatePermissions2(this->getClientDatabaseId(), {permission::i_channel_join_power}, this->getType(), channel->channelId(), false, permission_cache);
if(result.empty())
RESULT(false);
if(!channel->permission_granted(permission::i_channel_needed_join_power, result.back().second, false))
RESULT(false);
}
auto val = this->permissionValue(permission::PERMTEST_ORDERED, permission::b_client_is_sticky, this->currentChannel, permission_cache);
if (val != permNotGranted && val > 0) {
auto st = this->permissionValue(permission::PERMTEST_ORDERED, permission::b_client_ignore_sticky, this->currentChannel, permission_cache);
if (st != 1)
RESULT(false);
}
RESULT(true);
}

View File

@ -176,9 +176,9 @@ namespace ts {
//Group manager chat
virtual bool notifyClientChatComposing(const std::shared_ptr<ConnectedClient> &);
virtual bool notifyClientChatClosed(const std::shared_ptr<ConnectedClient> &);
virtual bool notifyTextMessage(ChatMessageMode mode, const std::shared_ptr<ConnectedClient> &sender, uint64_t targetId, const std::string &textMessage);
virtual bool notifyTextMessage(ChatMessageMode mode, const std::shared_ptr<ConnectedClient> &sender, uint64_t targetId, ChannelId channel_id, const std::string &textMessage);
inline void sendChannelMessage(const std::shared_ptr<ConnectedClient>& sender, const std::string& textMessage){
this->notifyTextMessage(ChatMessageMode::TEXTMODE_CHANNEL, sender, this->currentChannel ? this->currentChannel->channelId() : 0, textMessage);
this->notifyTextMessage(ChatMessageMode::TEXTMODE_CHANNEL, sender, this->currentChannel ? this->currentChannel->channelId() : 0, 0, textMessage);
}
//Group Client Groups
virtual bool notifyServerGroupClientAdd(const std::shared_ptr<ConnectedClient> &invoker, const std::shared_ptr<ConnectedClient> &client, const std::shared_ptr<Group> &group);
@ -369,6 +369,8 @@ namespace ts {
permission::PermissionValue channels_ignore_view = permNotGranted;
bool subscribeToAll = false;
uint16_t join_state_id = 1; /* default channel value is 0 and by default we need to calculate at least once, so we use 1 */
bool calculate_and_get_join_state(const std::shared_ptr<BasicChannel>&);
std::weak_ptr<MusicClient> selectedBot;
std::weak_ptr<MusicClient> subscribed_bot;
@ -396,7 +398,7 @@ namespace ts {
CommandResult handleCommandClientUpdate(Command&);
CommandResult handleCommandClientEdit(Command&);
CommandResult handleCommandClientEdit(Command&, const std::shared_ptr<ConnectedClient>& /* target */);
CommandResult handleCommandClientMove(Command&); //TODO: Use cached permission values
CommandResult handleCommandClientMove(Command&);
CommandResult handleCommandClientGetVariables(Command&);
CommandResult handleCommandClientKick(Command&);
CommandResult handleCommandClientPoke(Command&);

View File

@ -1186,7 +1186,8 @@ CommandResult ConnectedClient::handleCommandChannelGroupAddPerm(Command &cmd) {
if (cl->channelGroupAssigned(channelGroup, cl->getChannel())) {
if(cl->update_cached_permissions())
cl->sendNeededPermissions(false); /* update the needed permissions */
cl->updateChannelClientProperties(true, true);
cl->updateChannelClientProperties(false, true);
cl->join_state_id++; /* join permission may changed, all channels need to be recalculate dif needed */
}
});
}
@ -1236,7 +1237,8 @@ CommandResult ConnectedClient::handleCommandChannelGroupDelPerm(Command &cmd) {
if (cl->channelGroupAssigned(channelGroup, cl->getChannel())) {
if(cl->update_cached_permissions()) /* update cached calculated permissions */
cl->sendNeededPermissions(false); /* cached permissions had changed, notify the client */
cl->updateChannelClientProperties(true, false);
cl->updateChannelClientProperties(false, false);
cl->join_state_id++; /* join permission may changed, all channels need to be recalculate dif needed */
}
});
}
@ -1311,6 +1313,7 @@ CommandResult ConnectedClient::handleCommandClientMove(Command &cmd) {
if(!this->permissionGranted(permission::PERMTEST_ORDERED, permission::b_channel_ignore_join_power, 1, channel, true, permission_cache)) {
CHANNEL_PERMISSION_TEST(permission::i_channel_join_power, permission::i_channel_needed_join_power, channel, false);
}
if (target_client == this) {
auto permission_cache_current = make_shared<CalculateCache>();
@ -1321,7 +1324,7 @@ CommandResult ConnectedClient::handleCommandClientMove(Command &cmd) {
return CommandResultPermissionError{permission::b_client_is_sticky};
}
}
}
if (target_client != this) {
PERM_CHECK_CHANNELR(permission::i_client_move_power, target_client->permissionValue(permission::PERMTEST_ORDERED, permission::i_client_needed_move_power, target_client->getChannel()), target_client->getChannel(), true);
PERM_CHECK_CHANNEL_CR(permission::i_client_move_power, target_client->permissionValue(permission::PERMTEST_ORDERED, permission::i_client_needed_move_power, channel), channel, true, permission_cache);
@ -2394,12 +2397,13 @@ CommandResult ConnectedClient::handleCommandChannelAddPerm(Command &cmd) {
for(const auto& client : this->server->getClientsByChannel(channel)) {
/* let them lock the server channel tree as well (read lock so does not matter) */
client->updateChannelClientProperties(true, true);
client->join_state_id++; /* join permission may changed, all channels need to be recalculate dif needed */
}
if(update_view && this->server)
if(update_view && this->server) {
auto l_source = this->server->channelTree->findLinkedChannel(channel->channelId());
this->server->forEachClient([&](const shared_ptr<ConnectedClient>& cl) {
/* server tree read lock still active */
auto l_source = cl->server->channelTree->findLinkedChannel(channel->channelId());
auto l_target = !cl->currentChannel ? nullptr : cl->server->channelTree->findLinkedChannel(cl->currentChannel->channelId());
sassert(l_source);
if(cl->currentChannel) sassert(l_target);
@ -2417,6 +2421,7 @@ CommandResult ConnectedClient::handleCommandChannelAddPerm(Command &cmd) {
cl->notifyChannelHide(deleted, false);
}
});
}
return command_result;
}
@ -2468,8 +2473,10 @@ CommandResult ConnectedClient::handleCommandChannelDelPerm(Command &cmd) {
if(updateClients && this->server)
this->server->forEachClient([&](std::shared_ptr<ConnectedClient> cl) {
if(cl->currentChannel == channel)
if(cl->currentChannel == channel) {
cl->updateChannelClientProperties(true, true);
cl->join_state_id++; /* join permission may changed, all channels need to be recalculate dif needed */
}
});
if(update_view && this->server) {
this->server->forEachClient([&](std::shared_ptr<ConnectedClient> cl) {
@ -2866,6 +2873,7 @@ CommandResult ConnectedClient::handleCommandServerGroupAddPerm(Command &cmd) {
cl->sendNeededPermissions(false); /* cached permissions had changed, notify the client */
if (checkTp)
cl->updateChannelClientProperties(true, true);
cl->join_state_id++; /* join permission may changed, all channels need to be recalculate dif needed */
}
});
}).detach();
@ -2935,6 +2943,7 @@ CommandResult ConnectedClient::handleCommandServerGroupDelPerm(Command &cmd) {
cl->sendNeededPermissions(false); /* cached permissions had changed, notify the client */
if (checkTp)
cl->updateChannelClientProperties(true, true);
cl->join_state_id++; /* join permission may changed, all channels need to be recalculate dif needed */
}
});
}).detach();
@ -3021,10 +3030,13 @@ CommandResult ConnectedClient::handleCommandServerGroupAutoAddPerm(ts::Command&
server->forEachClient([groups, checkTp](shared_ptr<ConnectedClient> cl) {
for(const auto& serverGroup : groups) {
if (cl->serverGroupAssigned(serverGroup)) {
if(cl->update_cached_permissions()) /* update cached calculated permissions */
if(cl->update_cached_permissions()) {/* update cached calculated permissions */
cl->sendNeededPermissions(false); /* cached permissions had changed, notify the client */
if (checkTp)
}
if (checkTp) {
cl->updateChannelClientProperties(true, true);
}
cl->join_state_id++; /* join permission may changed, all channels need to be recalculate if needed */
break;
}
}
@ -3104,6 +3116,7 @@ CommandResult ConnectedClient::handleCommandServerGroupAutoDelPerm(ts::Command&
cl->sendNeededPermissions(false); /* cached permissions had changed, notify the client */
if (checkTp)
cl->updateChannelClientProperties(true, true);
cl->join_state_id++; /* join permission may changed, all channels need to be recalculate dif needed */
break;
}
}
@ -3228,25 +3241,57 @@ CommandResult ConnectedClient::handleCommandSendTextMessage(Command &cmd) {
}
if(this->handleTextMessage(ChatMessageMode::TEXTMODE_PRIVATE, cmd["msg"], target)) return CommandResult::Success;
target->notifyTextMessage(ChatMessageMode::TEXTMODE_PRIVATE, _this.lock(), target->getClientId(), cmd["msg"].string());
this->notifyTextMessage(ChatMessageMode::TEXTMODE_PRIVATE, _this.lock(), target->getClientId(), cmd["msg"].string());
target->notifyTextMessage(ChatMessageMode::TEXTMODE_PRIVATE, _this.lock(), target->getClientId(), 0, cmd["msg"].string());
this->notifyTextMessage(ChatMessageMode::TEXTMODE_PRIVATE, _this.lock(), target->getClientId(), 0, cmd["msg"].string());
} else if (cmd["targetmode"] == ChatMessageMode::TEXTMODE_CHANNEL) {
CMD_REQ_CHANNEL;
CACHED_PERM_CHECK(permission::b_client_channel_textmessage_send, 1, false);
if(!cmd[0].has("cid"))
cmd["cid"] = 0;
RESOLVE_CHANNEL_R(cmd["cid"], false);
auto channel = l_channel ? dynamic_pointer_cast<BasicChannel>(l_channel->entry) : nullptr;
if(!channel) {
CMD_REQ_CHANNEL;
channel = this->currentChannel;
channel_id = this->currentChannel->channelId();
if(this->handleTextMessage(ChatMessageMode::TEXTMODE_CHANNEL, cmd["msg"], nullptr)) return CommandResult::Success;
for (auto &cl : this->server->getClientsByChannel(this->currentChannel))
cl->notifyTextMessage(ChatMessageMode::TEXTMODE_CHANNEL, _this.lock(), this->getClientId(), cmd["msg"].string());
if(this->handleTextMessage(ChatMessageMode::TEXTMODE_CHANNEL, cmd["msg"], nullptr))
return CommandResult::Success;
}
auto message = cmd["msg"].string();
auto _this = this->_this.lock();
auto client_id = this->getClientId();
auto flag_password = channel->properties()[property::CHANNEL_FLAG_PASSWORD].as<bool>();
for(const auto& client : this->server->getClients()) {
if(client->connectionState() != ConnectionState::CONNECTED)
continue;
auto type = client->getType();
if(type == ClientType::CLIENT_INTERNAL || type == ClientType::CLIENT_MUSIC)
continue;
auto own_channel = client->currentChannel == this->currentChannel;
if(type != ClientType::CLIENT_TEAMSPEAK || own_channel) {
if(!own_channel && &*client != this) {
if(flag_password)
continue; /* TODO: Send notification about new message. The client then could request messages via message history */
if(!client->calculate_and_get_join_state(channel))
continue;
}
client->notifyTextMessage(ChatMessageMode::TEXTMODE_CHANNEL, _this, client_id, channel_id, message);
}
}
auto conversations = this->server->conversation_manager();
auto conversation = conversations->get_or_create(this->currentChannel->channelId());
auto conversation = conversations->get_or_create(channel->channelId());
conversation->register_message(this->getClientDatabaseId(), this->getUid(), this->getDisplayName(), cmd["msg"].string());
} else if (cmd["targetmode"] == ChatMessageMode::TEXTMODE_SERVER) {
CACHED_PERM_CHECK(permission::b_client_server_textmessage_send, 1);
if(this->handleTextMessage(ChatMessageMode::TEXTMODE_SERVER, cmd["msg"], nullptr)) return CommandResult::Success;
this->server->forEachClient([&](shared_ptr<ConnectedClient> client) {
client->notifyTextMessage(ChatMessageMode::TEXTMODE_SERVER, _this.lock(), this->getClientId(), cmd["msg"].string());
client->notifyTextMessage(ChatMessageMode::TEXTMODE_SERVER, _this.lock(), this->getClientId(), 0, cmd["msg"].string());
});
} else return {findError("parameter_invalid"), "invalid target mode"};
@ -4568,7 +4613,7 @@ CommandResult ConnectedClient::handleCommandClientMute(Command &cmd) {
}
if (config::voice::notifyMuted)
client->notifyTextMessage(ChatMessageMode::TEXTMODE_PRIVATE, _this.lock(), client->getClientId(), config::messages::mute_notify_message);
client->notifyTextMessage(ChatMessageMode::TEXTMODE_PRIVATE, _this.lock(), client->getClientId(), 0, config::messages::mute_notify_message);
return CommandResult::Success;
}
@ -4589,7 +4634,7 @@ CommandResult ConnectedClient::handleCommandClientUnmute(Command &cmd) {
}
if (config::voice::notifyMuted)
client->notifyTextMessage(ChatMessageMode::TEXTMODE_PRIVATE, _this.lock(), client->getClientId(), config::messages::unmute_notify_message);
client->notifyTextMessage(ChatMessageMode::TEXTMODE_PRIVATE, _this.lock(), client->getClientId(), 0, config::messages::unmute_notify_message);
return CommandResult::Success;
}
@ -4879,6 +4924,7 @@ CommandResult ConnectedClient::handleCommandClientAddPerm(Command &cmd) {
elm->sendNeededPermissions(false); /* cached permissions had changed, notify the client */
if(update_channels)
elm->updateChannelClientProperties(true, true);
elm->join_state_id++; /* join permission may changed, all channels need to be recalculate dif needed */
}
return CommandResult::Success;
@ -4917,6 +4963,7 @@ CommandResult ConnectedClient::handleCommandClientDelPerm(Command &cmd) {
elm->sendNeededPermissions(false); /* cached permissions had changed, notify the client */
if(update_channel)
elm->updateChannelClientProperties(true, true);
elm->join_state_id++; /* join permission may changed, all channels need to be recalculate dif needed */
}
return CommandResult::Success;
}
@ -4938,9 +4985,9 @@ CommandResult ConnectedClient::handleCommandChannelClientPermList(Command &cmd)
CMD_RESET_IDLE;
CMD_CHK_AND_INC_FLOOD_POINTS(5);
PERM_CHECKR(permission::b_virtualserver_channelclient_permission_list, 1, true);
std::shared_ptr<BasicChannel> channel = this->server->channelTree->findChannel(cmd["cid"].as<ChannelId>());
if (!channel) return {findError("channel_invalid_id"), "Cant resolve channel"};
RESOLVE_CHANNEL_R(cmd["cid"], true);
auto channel = dynamic_pointer_cast<ServerChannel>(l_channel->entry);
if(!channel) return {ErrorType::VSError};
if(!serverInstance->databaseHelper()->validClientDatabaseId(this->server, cmd["cldbid"])) return {findError("client_invalid_id"), "invalid client id"};
auto mgr = serverInstance->databaseHelper()->loadClientPermissionManager(this->server, cmd["cldbid"].as<ClientDbId>());
@ -4987,21 +5034,21 @@ CommandResult ConnectedClient::handleCommandChannelClientPermList(Command &cmd)
return CommandResult::Success;
}
//TODO: Update this specific channel visibility?
CommandResult ConnectedClient::handleCommandChannelClientDelPerm(Command &cmd) {
CMD_REQ_SERVER;
CMD_REF_SERVER(server_ref);
CMD_RESET_IDLE;
CMD_CHK_AND_INC_FLOOD_POINTS(5);
if (!serverInstance->databaseHelper()->validClientDatabaseId(this->server, cmd["cldbid"])) return {findError("parameter_invalid"), "Invalid manager db id"};
std::shared_ptr<BasicChannel> channel = this->server->channelTree->findChannel(cmd["cid"].as<ChannelId>());
if (!channel) return {findError("channel_invalid_id"), "Cant resolve channel"};
auto mgr = serverInstance->databaseHelper()->loadClientPermissionManager(this->server, cmd["cldbid"]);
PERM_CHECKR(permission::i_client_permission_modify_power, this->server->calculatePermission(permission::PERMTEST_ORDERED, cmd["cldbid"], permission::i_client_needed_permission_modify_power, ClientType::CLIENT_TEAMSPEAK, nullptr), true);
RESOLVE_CHANNEL_R(cmd["cid"], true);
auto channel = dynamic_pointer_cast<ServerChannel>(l_channel->entry);
if(!channel) return {ErrorType::VSError};
bool ignoreGrant = this->permissionGranted(permission::PERMTEST_ORDERED, permission::b_permission_modify_power_ignore, 1, this->currentChannel);
bool conOnError = cmd[0].has("continueonerror");
bool conOnError = cmd[0].has("continueonerror"), update_view = false;
auto cll = this->server->findClientsByCldbId(cmd["cldbid"]);
for (int index = 0; index < cmd.bulkCount(); index++) {
PARSE_PERMISSION(cmd);
@ -5013,27 +5060,42 @@ CommandResult ConnectedClient::handleCommandChannelClientDelPerm(Command &cmd) {
mgr->set_channel_permission(permType, channel->channelId(), permission::v2::empty_permission_values, permission::v2::do_nothing, permission::v2::delete_value);
} else {
mgr->set_channel_permission(permType, channel->channelId(), permission::v2::empty_permission_values, permission::v2::delete_value, permission::v2::do_nothing);
update_view = permType == permission::b_channel_ignore_view_power || permType == permission::i_channel_view_power;
}
}
if (!cll.empty()) {
for (const auto &cl : cll) {
if(cl->update_cached_permissions()) /* update cached calculated permissions */
cl->sendNeededPermissions(false); /* cached permissions had changed, notify the client */
for (const auto &elm : cll) {
if(elm->update_cached_permissions()) /* update cached calculated permissions */
elm->sendNeededPermissions(false); /* cached permissions had changed, notify the client */
if(elm->currentChannel == channel) {
elm->updateChannelClientProperties(true, true);
} else if(update_view) {
unique_lock client_channel_lock(this->channel_lock);
auto elm_channel = elm->currentChannel;
if(elm_channel) {
deque<ChannelId> deleted;
for(const auto& update_entry : elm->channels->update_channel_path(l_channel, this->server->channelTree->findLinkedChannel(elm->currentChannel->channelId()))) {
if(update_entry.first)
elm->notifyChannelShow(update_entry.second->channel(), update_entry.second->previous_channel);
else deleted.push_back(update_entry.second->channelId());
}
if(!deleted.empty())
elm->notifyChannelHide(deleted, false); /* we've locked the tree before */
}
}
for (const auto &elm : cll)
if (elm->currentChannel == channel) {
elm->updateChannelClientProperties(true, true);
elm->join_state_id++; /* join permission may changed, all channels need to be recalculate dif needed */
}
}
return CommandResult::Success;
}
//TODO: Update this specific channel visibility?
CommandResult ConnectedClient::handleCommandChannelClientAddPerm(Command &cmd) {
CMD_REQ_SERVER;
CMD_REF_SERVER(server_ref);
CMD_RESET_IDLE;
CMD_CHK_AND_INC_FLOOD_POINTS(5);
if (!serverInstance->databaseHelper()->validClientDatabaseId(this->server, cmd["cldbid"])) return {findError("parameter_invalid"), "Invalid manager db id"};
@ -5041,13 +5103,16 @@ CommandResult ConnectedClient::handleCommandChannelClientAddPerm(Command &cmd) {
auto mgr = serverInstance->databaseHelper()->loadClientPermissionManager(this->server, cmd["cldbid"]);
PERM_CHECKR(permission::i_client_permission_modify_power, this->server->calculatePermission(permission::PERMTEST_ORDERED, cmd["cldbid"], permission::i_client_needed_permission_modify_power, ClientType::CLIENT_TEAMSPEAK, nullptr), true);
std::shared_ptr<BasicChannel> channel = this->server->channelTree->findChannel(cmd["cid"].as<ChannelId>());
if (!channel) return {findError("channel_invalid_id"), "Cant resolve channel"};
RESOLVE_CHANNEL_R(cmd["cid"], true);
auto channel = dynamic_pointer_cast<ServerChannel>(l_channel->entry);
if(!channel) return {ErrorType::VSError};
auto maxValue = this->getPermissionGrantValue(permission::PERMTEST_ORDERED, permission::i_permission_modify_power, this->currentChannel);
bool ignoreGrant = this->permissionGranted(permission::PERMTEST_ORDERED, permission::b_permission_modify_power_ignore, 1, this->currentChannel);
bool conOnError = cmd[0].has("continueonerror");
auto onlineClientInstances = this->server->findClientsByCldbId(cmd["cldbid"]);
bool update_view = false;
for (int index = 0; index < cmd.bulkCount(); index++) {
PARSE_PERMISSION(cmd);
@ -5063,6 +5128,7 @@ CommandResult ConnectedClient::handleCommandChannelClientAddPerm(Command &cmd) {
mgr->set_channel_permission(permType, channel->channelId(), {0, cmd[index]["permvalue"]}, permission::v2::do_nothing, permission::v2::set_value);
} else {
mgr->set_channel_permission(permType, channel->channelId(), {cmd[index]["permvalue"], 0}, permission::v2::set_value, permission::v2::do_nothing, cmd[index]["permskip"] ? 1 : 0, cmd[index]["permnegated"] ? 1 : 0);
update_view = permType == permission::b_channel_ignore_view_power || permType == permission::i_channel_view_power;
}
}
if (!onlineClientInstances.empty())
@ -5070,9 +5136,24 @@ CommandResult ConnectedClient::handleCommandChannelClientAddPerm(Command &cmd) {
if (elm->update_cached_permissions()) /* update cached calculated permissions */
elm->sendNeededPermissions(false); /* cached permissions had changed, notify the client */
if (elm->currentChannel == channel) {
if(elm->currentChannel == channel) {
elm->updateChannelClientProperties(true, true);
} else if(update_view) {
unique_lock client_channel_lock(this->channel_lock);
auto elm_channel = elm->currentChannel;
if(elm_channel) {
deque<ChannelId> deleted;
for(const auto& update_entry : elm->channels->update_channel_path(l_channel, this->server->channelTree->findLinkedChannel(elm->currentChannel->channelId()))) {
if(update_entry.first)
elm->notifyChannelShow(update_entry.second->channel(), update_entry.second->previous_channel);
else deleted.push_back(update_entry.second->channelId());
}
if(!deleted.empty())
elm->notifyChannelHide(deleted, false); /* we've locked the tree before */
}
}
elm->join_state_id++; /* join permission may changed, all channels need to be recalculate dif needed */
}
return CommandResult::Success;
@ -7337,12 +7418,14 @@ CommandResult ConnectedClient::handleCommandConversationHistory(ts::Command &com
size_t index = 0;
size_t length = 0;
bool merge = command.hasParm("merge");
for(auto& message : messages) {
for(auto it = messages.rbegin(); it != messages.rend(); it++) {
if(index == 0) {
notify[index]["cid"] = conversation_id;
notify[index]["flag_volatile"] = conversation->volatile_only();
}
auto& message = *it;
notify[index]["timestamp"] = duration_cast<milliseconds>(message->message_timestamp.time_since_epoch()).count();
notify[index]["sender_database_id"] = message->sender_database_id;
notify[index]["sender_unique_id"] = message->sender_unique_id;

View File

@ -178,13 +178,15 @@ bool ConnectedClient::notifyChannelGroupList() {
return true;
}
bool ConnectedClient::notifyTextMessage(ChatMessageMode mode, const shared_ptr<ConnectedClient> &invoker, uint64_t targetId, const string &textMessage) {
bool ConnectedClient::notifyTextMessage(ChatMessageMode mode, const shared_ptr<ConnectedClient> &invoker, uint64_t targetId, ChannelId channel_id, const string &textMessage) {
//notifytextmessage targetmode=1 msg=asdasd target=2 invokerid=1 invokername=WolverinDEV invokeruid=xxjnc14LmvTk+Lyrm8OOeo4tOqw=
Command cmd("notifytextmessage");
INVOKER(cmd, invoker);
cmd["targetmode"] = mode;
cmd["target"] = targetId;
cmd["msg"] = textMessage;
if(this->getType() != ClientType::CLIENT_TEAMSPEAK)
cmd["cid"] = channel_id;
this->sendCommand(cmd);
return true;
}

View File

@ -122,15 +122,15 @@ bool ConnectedClient::handleTextMessage(ChatMessageMode mode, std::string text,
handle_text_command_fn_t function;
if(mode == ChatMessageMode::TEXTMODE_SERVER) {
function = [&](const shared_ptr<ConnectedClient>& sender, const string& message) {
this->notifyTextMessage(ChatMessageMode::TEXTMODE_SERVER, sender, 0, message);
this->notifyTextMessage(ChatMessageMode::TEXTMODE_SERVER, sender, 0, 0, message);
};
} else if(mode == ChatMessageMode::TEXTMODE_CHANNEL) {
function = [&](const shared_ptr<ConnectedClient>& sender, const string& message) {
this->notifyTextMessage(ChatMessageMode::TEXTMODE_CHANNEL, sender, 0, message);
this->notifyTextMessage(ChatMessageMode::TEXTMODE_CHANNEL, sender, 0, 0, message);
};
} else if(mode == ChatMessageMode::TEXTMODE_PRIVATE) {
function = [&, target](const shared_ptr<ConnectedClient>& sender, const string& message) {
this->notifyTextMessage(ChatMessageMode::TEXTMODE_PRIVATE, target, this->getClientId(), message);
this->notifyTextMessage(ChatMessageMode::TEXTMODE_PRIVATE, target, this->getClientId(), 0, message);
};
}

View File

@ -102,7 +102,7 @@ namespace ts {
bool notifyPluginCmd(std::string name, std::string msg,std::shared_ptr<ConnectedClient>) override;
bool notifyClientChatComposing(const std::shared_ptr<ConnectedClient> &ptr) override;
bool notifyClientChatClosed(const std::shared_ptr<ConnectedClient> &ptr) override;
bool notifyTextMessage(ChatMessageMode mode, const std::shared_ptr<ConnectedClient> &sender, uint64_t targetId, const std::string &textMessage) override;
bool notifyTextMessage(ChatMessageMode mode, const std::shared_ptr<ConnectedClient> &sender, uint64_t targetId, ChannelId channel_id, const std::string &textMessage) override;
bool notifyServerGroupClientAdd(const std::shared_ptr<ConnectedClient> &invoker, const std::shared_ptr<ConnectedClient> &client, const std::shared_ptr<Group> &group) override;
bool notifyServerGroupClientRemove(std::shared_ptr<ConnectedClient> invoker, std::shared_ptr<ConnectedClient> client, std::shared_ptr<Group> group) override;

View File

@ -67,11 +67,11 @@ bool QueryClient::notifyClientChatClosed(const shared_ptr<ConnectedClient> &ptr)
return ConnectedClient::notifyClientChatClosed(ptr);
}
bool QueryClient::notifyTextMessage(ChatMessageMode mode, const shared_ptr<ConnectedClient> &sender, uint64_t targetId, const string &textMessage) {
bool QueryClient::notifyTextMessage(ChatMessageMode mode, const shared_ptr<ConnectedClient> &sender, uint64_t targetId, ChannelId channel_id, const string &textMessage) {
if(mode == ChatMessageMode::TEXTMODE_PRIVATE) CHK_EVENT(QEVENTGROUP_CHAT, QEVENTSPECIFIER_CHAT_MESSAGE_PRIVATE);
else if(mode == ChatMessageMode::TEXTMODE_CHANNEL) CHK_EVENT(QEVENTGROUP_CHAT, QEVENTSPECIFIER_CHAT_MESSAGE_CHANNEL);
else if(mode == ChatMessageMode::TEXTMODE_SERVER) CHK_EVENT(QEVENTGROUP_CHAT, QEVENTSPECIFIER_CHAT_MESSAGE_SERVER);
return ConnectedClient::notifyTextMessage(mode, sender, targetId, textMessage);
return ConnectedClient::notifyTextMessage(mode, sender, targetId, channel_id, textMessage);
}
bool QueryClient::notifyServerGroupClientAdd(const shared_ptr<ConnectedClient> &invoker, const shared_ptr<ConnectedClient> &client, const shared_ptr<Group> &group) {

View File

@ -22,11 +22,10 @@ using namespace ts;
using namespace ts::server;
using namespace ts::protocol;
WebClient::WebClient(WebControlServer* server, int fd, const sockaddr_in& addr) : SpeakingClient(server->getTS()->getSql(), server->getTS()), handle(server) {
WebClient::WebClient(WebControlServer* server, int fd) : SpeakingClient(server->getTS()->getSql(), server->getTS()), handle(server) {
memtrack::allocated<WebClient>(this);
assert(server->getTS());
memcpy(&this->remote_address, &addr, sizeof(sockaddr_in));
this->state = ConnectionState::INIT_LOW;
this->file_descriptor = fd;
}

View File

@ -21,7 +21,7 @@ namespace ts {
class WebClient : public SpeakingClient {
friend class WebControlServer;
public:
WebClient(WebControlServer*, int socketFd, const sockaddr_in&);
WebClient(WebControlServer*, int socketFd);
~WebClient() override;
void sendJson(const Json::Value&);

View File

@ -1025,6 +1025,9 @@ std::deque<std::shared_ptr<ConversationEntry>> Conversation::message_history(con
for(auto it = this->_last_messages.rbegin(); it != this->_last_messages.rend(); it++) {
if((*it)->message_timestamp > end_timestamp) /* message has been send after the search timestamp */
continue;
if(begin_timestamp.time_since_epoch().count() != 0 && (*it)->message_timestamp < begin_timestamp)
return result;
result.push_back(*it);
if(--message_count == 0)
return result;
@ -1102,6 +1105,7 @@ std::deque<std::shared_ptr<ConversationEntry>> Conversation::message_history(con
if(begin_timestamp.time_since_epoch().count() != 0 && rmid->timestamp < begin_timestamp)
return result;
if(rmid->timestamp >= timestamp)
continue; /* for some reason we got a message from the index of before where we are. This could happen for "orphaned" blocks which point to a valid block within the future block */
/*

View File

@ -18,70 +18,168 @@ using namespace ts::server;
WebControlServer::WebControlServer(const std::shared_ptr<TSServer>& handle) : handle(handle) {}
WebControlServer::~WebControlServer() = default;
bool WebControlServer::start(sockaddr_in* addr, std::string& errorMessage) {
if(this->running()) return false;
bool WebControlServer::start(const std::deque<std::shared_ptr<WebControlServer::Binding>>& bindings, std::string& error) {
if(this->running()) {
error = "server already running";
return false;
}
this->_running = true;
boundAddress = new sockaddr_in;
memcpy(boundAddress, addr, sizeof(*addr));
this->serverSocket = socket(AF_INET, SOCK_STREAM | SOCK_NONBLOCK, 0);
if (serverSocket < 0) {
logCritical(this->handle->getServerId(), "Cant create server socket for server");
return false;
/* reserve backup file descriptor in case that the max file descriptors have been reached */
{
this->server_reserve_fd = dup(1);
if(this->server_reserve_fd < 0)
logWarning(this->handle->getServerId(), "Failed to reserve a backup accept file descriptor. ({} | {})", errno, strerror(errno));
}
int enable = 1;
int disabled = 0;
if (setsockopt(serverSocket, SOL_SOCKET, SO_REUSEADDR, &enable, sizeof(int)) < 0)
logError("setsockopt(SO_REUSEADDR) failed");
if(setsockopt(serverSocket, IPPROTO_TCP, TCP_NOPUSH, &disabled, sizeof disabled) < 0)
logError("Cant disable nopush! Error: "+to_string(errno)+" / "+strerror(errno));
setsockopt(serverSocket, SOL_SOCKET, SO_KEEPALIVE, &enable, sizeof(enable));
if(fcntl(serverSocket, F_SETFD, FD_CLOEXEC) < 0)
logError(this->handle->getServerId(), "Failed to enable FD_CLOEXEC for {} (WebControlServer)", serverSocket);
if (bind(serverSocket, (struct sockaddr *) boundAddress, sizeof(*boundAddress)) < 0) {
errorMessage = string() + "Cant bind server socket (" + strerror(errno) + ")";
return false;
{
for(auto& binding : bindings) {
binding->file_descriptor = socket(binding->address.ss_family, SOCK_STREAM | SOCK_NONBLOCK, 0);
if(binding->file_descriptor < 0) {
logError(this->handle->getServerId(), "[Web] Failed to bind server to {}. (Failed to create socket: {} | {})", binding->as_string(), errno, strerror(errno));
continue;
}
if(listen(serverSocket, 255) < 0){
errorMessage = string() + "Cant listen on server socket (" + strerror(errno) + ")";
return false;
int enable = 1, disabled = 0;
if (setsockopt(binding->file_descriptor, SOL_SOCKET, SO_REUSEADDR, &enable, sizeof(int)) < 0)
logWarning(this->handle->getServerId(), "[Web] Failed to activate SO_REUSEADDR for binding {} ({} | {})", binding->as_string(), errno, strerror(errno));
if(setsockopt(binding->file_descriptor, IPPROTO_TCP, TCP_NOPUSH, &disabled, sizeof disabled) < 0)
logWarning(this->handle->getServerId(), "[Web] Failed to deactivate TCP_NOPUSH for binding {} ({} | {})", binding->as_string(), errno, strerror(errno));
if(binding->address.ss_family == AF_INET6) {
if(setsockopt(binding->file_descriptor, IPPROTO_IPV6, IPV6_V6ONLY, &enable, sizeof(int)) < 0)
logWarning(this->handle->getServerId(), "[Web] Failed to activate IPV6_V6ONLY for IPv6 binding {} ({} | {})", binding->as_string(), errno, strerror(errno));
}
if(fcntl(binding->file_descriptor, F_SETFD, FD_CLOEXEC) < 0)
logWarning(this->handle->getServerId(), "[Web] Failed to set flag FD_CLOEXEC for binding {} ({} | {})", binding->as_string(), errno, strerror(errno));
if (bind(binding->file_descriptor, (struct sockaddr *) &binding->address, sizeof(binding->address)) < 0) {
logError(this->handle->getServerId(), "[Web] Failed to bind server to {}. (Failed to bind socket: {} | {})", binding->as_string(), errno, strerror(errno));
close(binding->file_descriptor);
continue;
}
if (listen(binding->file_descriptor, SOMAXCONN) < 0) {
logError(this->handle->getServerId(), "[Web] Failed to bind server to {}. (Failed to listen: {} | {})", binding->as_string(), errno, strerror(errno));
close(binding->file_descriptor);
continue;
}
auto io_base = serverInstance->getWebIoLoop()->next_loop();
assert(io_base);
this->acceptEvent = event_new(io_base->loop, this->serverSocket, EV_READ | EV_PERSIST, [](int a, short b, void* c){ ((WebControlServer*) c)->onClientAccept(a, b, c); }, this);
event_add(this->acceptEvent, nullptr);
binding->event_accept = event_new(io_base->loop, binding->file_descriptor, EV_READ | EV_PERSIST, [](int a, short b, void* c){ ((WebControlServer *) c)->on_client_receive(a, b, c); }, this);
event_add(binding->event_accept, nullptr);
this->bindings.push_back(binding);
}
if(this->bindings.empty()) {
this->stop();
error = "failed to bind to any address";
return false;
}
}
return true;
}
void WebControlServer::onClientAccept(int fd, short ev, void *arg) {
sockaddr_in remoteAddr{};
memset(&remoteAddr, 0, sizeof(sockaddr_in));
socklen_t addrLength = sizeof(remoteAddr);
#define CLOSE_CONNECTION \
if(shutdown(file_descriptor, SHUT_RDWR) < 0) { \
debugMessage(LOG_FT, "[{}] Failed to shutdown socket ({} | {}).", logging_address(remote_address), errno, strerror(errno)); \
} \
if(close(file_descriptor) < 0) { \
debugMessage(LOG_FT, "[{}] Failed to close socket ({} | {}).", logging_address(remote_address), errno, strerror(errno)); \
}
int acceptedSocketFd = accept(this->serverSocket, (struct sockaddr *) &remoteAddr, &addrLength);
if (acceptedSocketFd < 0) {
if(errno == EAGAIN) //No manager
inline std::string logging_address(const sockaddr_storage& address) {
if(config::server::disable_ip_saving)
return "X.X.X.X" + to_string(net::port(address));
return net::to_string(address, true);
}
void WebControlServer::on_client_receive(int _server_file_descriptor, short ev, void *arg) {
sockaddr_storage remote_address{};
memset(&remote_address, 0, sizeof(remote_address));
socklen_t address_length = sizeof(remote_address);
int file_descriptor = accept(_server_file_descriptor, (struct sockaddr *) &remote_address, &address_length);
if (file_descriptor < 0) {
if(errno == EAGAIN)
return;
logError(this->handle->getServerId(), lstream << "Having an error while accepting a new manager. ("+to_string(errno)+"/"+string(strerror(errno))+")");
if(errno == EMFILE || errno == ENFILE) {
if(errno == EMFILE)
logError(this->handle->getServerId(), "[Web] Server ran out file descriptors. Please increase the process file descriptor limit.");
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.");
auto _ = [&]{
if(close(this->server_reserve_fd) < 0) {
debugMessage(this->handle->getServerId(), "[Web] Failed to close reserved file descriptor");
tmp_close_success = false;
return;
}
this->server_reserve_fd = 0;
errno = 0;
file_descriptor = accept(_server_file_descriptor, (struct sockaddr *) &remote_address, &address_length);
if(file_descriptor < 0) {
if(errno == EMFILE || errno == ENFILE)
debugMessage(this->handle->getServerId(), "[Web] [{}] Even with freeing the reserved descriptor accept failed. Attempting to reclaim reserved file descriptor", logging_address(remote_address));
else if(errno == EAGAIN);
else {
debugMessage(this->handle->getServerId(), "[Web] [{}] Failed to accept client with reserved file descriptor. ({} | {})", logging_address(remote_address), errno, strerror(errno));
}
this->server_reserve_fd = dup(1);
if(this->server_reserve_fd < 0)
debugMessage(this->handle->getServerId(), "[Web] [{}] Failed to reclaim reserved file descriptor. Future clients cant be accepted!", logging_address(remote_address));
else
tmp_close_success = true;
return;
}
debugMessage(this->handle->getServerId(), "[Web] [{}] Successfully accepted client via reserved descriptor (fd: {}). Disconnecting client.", logging_address(remote_address), file_descriptor);
CLOSE_CONNECTION
this->server_reserve_fd = dup(1);
if(this->server_reserve_fd < 0)
debugMessage(this->handle->getServerId(), "[Web] Failed to reclaim reserved file descriptor. Future clients cant be accepted!");
else
tmp_close_success = true;
logMessage(this->handle->getServerId(), "[Web] [{}] Dropping file transfer connection attempt because of too many open file descriptors.", logging_address(remote_address));
};
_();
}
}
if(!tmp_close_success) {
debugMessage(this->handle->getServerId(), "[Web] Sleeping two seconds because we're currently having no resources for this user. (Removing the accept event)");
for(auto& binding : this->bindings)
event_del_noblock(binding->event_accept);
accept_event_deleted = system_clock::now();
return;
}
return;
}
logMessage(this->handle->getServerId(), "[Web] Got an error while accepting a new client. (errno: {}, message: {})", errno, strerror(errno));
return;
}
shared_ptr<WebClient> client = std::make_shared<WebClient>(this, acceptedSocketFd, remoteAddr);
shared_ptr<WebClient> client = std::make_shared<WebClient>(this, file_descriptor);
memcpy(&client->remote_address, &remote_address, sizeof(remote_address));
client->applySelfLock(client);
client->initialize();
memcpy(&client->remote_address, &remoteAddr, sizeof(sockaddr_in));
this->clientLock.lock();
this->clients.push_back(client);
this->clientLock.unlock();
event_add(client->readEvent, nullptr);
logMessage(this->handle->getServerId(), lstream << "[Web] Got new manager from " << client->getLoggingPeerIp() << ":" << client->getPeerPort() << endl);
logMessage(this->handle->getServerId(), "[Web] Got new client from {}:{}", client->getLoggingPeerIp(), client->getPeerPort());
}
void WebControlServer::stop() {
@ -94,19 +192,28 @@ void WebControlServer::stop() {
e->closeConnection(system_clock::now());
}
if(this->acceptEvent) {
event_del(this->acceptEvent);
event_free(this->acceptEvent);
}
if(this->serverSocket > 0){
if(shutdown(this->serverSocket, SHUT_RDWR) < 0) logError(this->handle->getServerId(), "Could not shutdown WebSocketServer socket!");
if(close(this->serverSocket) < 0) logError(this->handle->getServerId(), "Could not close WebSocketServer socket!");
for(auto& binding : this->bindings) {
if(binding->event_accept) {
event_del_block(binding->event_accept);
event_free(binding->event_accept);
binding->event_accept = nullptr;
}
this->serverSocket = -1;
if(binding->file_descriptor > 0) {
if(shutdown(binding->file_descriptor, SHUT_RDWR) < 0)
logWarning(this->handle->getServerId(), "[Web] Failed to shutdown socket for binding {} ({} | {}).", binding->as_string(), errno, strerror(errno));
if(close(binding->file_descriptor) < 0)
logError(this->handle->getServerId(), "[Web] Failed to close socket for binding {} ({} | {}).", binding->as_string(), errno, strerror(errno));
binding->file_descriptor = -1;
}
}
this->bindings.clear();
delete this->boundAddress;
this->boundAddress = nullptr;
if(this->server_reserve_fd > 0) {
if(close(this->server_reserve_fd) < 0)
logError(this->handle->getServerId(), "[Web] Failed to close backup file descriptor ({} | {})", errno, strerror(errno));
}
this->server_reserve_fd = -1;
}
void WebControlServer::unregisterConnection(const std::shared_ptr<WebClient>& connection) {

View File

@ -3,20 +3,28 @@
#include <ThreadPool/Mutex.h>
#include <ThreadPool/Thread.h>
#include <event.h>
#include <misc/net.h>
namespace ts {
namespace server {
class TSServer;
class WebClient;
class WebRTCServer;
class WebControlServer {
friend class WebClient;
public:
struct Binding {
sockaddr_storage address{};
int file_descriptor = 0;
::event* event_accept = nullptr;
inline std::string as_string() { return net::to_string(address, true); }
};
explicit WebControlServer(const std::shared_ptr<TSServer>&);
~WebControlServer();
bool start(sockaddr_in*, std::string&);
bool start(const std::deque<std::shared_ptr<Binding>>& /* bindings */, std::string& /* error */);
inline bool running(){ return _running; }
void stop();
@ -34,11 +42,15 @@ namespace ts {
std::deque<std::shared_ptr<WebClient>> clients;
bool _running = false;
sockaddr_in* boundAddress = nullptr;
int serverSocket = 0;
::event* acceptEvent = nullptr;
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 */
//IO stuff
std::chrono::system_clock::time_point accept_event_deleted;
private:
void onClientAccept(int fd, short ev, void *arg);
void on_client_receive(int fd, short ev, void *arg);
void unregisterConnection(const std::shared_ptr<WebClient>&);
};
}

View File

@ -223,7 +223,7 @@ namespace terminal {
return false;
}
for(const auto &cl : server->getClientsByChannel(channel))
cl->notifyTextMessage(ChatMessageMode::TEXTMODE_CHANNEL, server->getServerRoot(), cl->getClientId(), message);
cl->notifyTextMessage(ChatMessageMode::TEXTMODE_CHANNEL, server->getServerRoot(), cl->getClientId(), 0, message);
}
break;
case ChatMessageMode::TEXTMODE_PRIVATE:
@ -233,7 +233,7 @@ namespace terminal {
logError("Cloud not find manager from clid");
return false;
}
client->notifyTextMessage(ChatMessageMode::TEXTMODE_CHANNEL, server->getServerRoot(), client->getClientId(), message);
client->notifyTextMessage(ChatMessageMode::TEXTMODE_CHANNEL, server->getServerRoot(), client->getClientId(), 0, message);
}
break;
default:

2
shared

@ -1 +1 @@
Subproject commit a0cca36eca11da410626a340dbe4377067d59c1b
Subproject commit 9f2181c18b4579de2957347c3b788f81a4de987a