Implemented new chat system
This commit is contained in:
parent
c720a80bc0
commit
af4106614c
@ -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=
|
||||
|
@ -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 \",\"");
|
||||
}
|
||||
}
|
||||
{
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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:
|
||||
|
@ -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);
|
||||
}
|
@ -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&);
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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);
|
||||
};
|
||||
|
||||
}
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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) {
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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&);
|
||||
|
@ -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 */
|
||||
/*
|
||||
|
@ -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) {
|
||||
|
@ -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>&);
|
||||
};
|
||||
}
|
||||
|
@ -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
2
shared
@ -1 +1 @@
|
||||
Subproject commit a0cca36eca11da410626a340dbe4377067d59c1b
|
||||
Subproject commit 9f2181c18b4579de2957347c3b788f81a4de987a
|
Loading…
Reference in New Issue
Block a user