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=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=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= [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); BIND_GROUP(web);
{ {
CREATE_BINDING("default_host", 0); CREATE_BINDING("default_host", 0);
BIND_STRING(config::binding::DefaultWebHost, "0.0.0.0"); BIND_STRING(config::binding::DefaultWebHost, "0.0.0.0,[::]");
ADD_NOTE("Multibinding like the voice server isnt supported yet!"); 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, sql::command(this->sql, query,
variable{":serverId", server ? server->getServerId() : 0}, variable{":serverId", server ? server->getServerId() : 0},
variable{":id", client_dbid}, variable{":id", client_dbid},
variable{":chId", 0}, variable{":chId", update.channel_id},
variable{":type", permission::SQL_PERM_USER}, variable{":type", permission::SQL_PERM_USER},
variable{":permId", permission_data->name}, variable{":permId", permission_data->name},
@ -1081,7 +1081,7 @@ std::shared_ptr<Properties> DatabaseHelper::loadClientProperties(const std::shar
} }
if(!prop.isModified()) return; 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() + ")"); logTrace(server ? server->getServerId() : 0, "[Property] Not saving property '" + prop.type().name + "', changed for " + to_string(cldbid) + " (New value: " + prop.value() + ")");
return; 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; return changed;
} }

View File

@ -372,26 +372,37 @@ bool TSServer::start(std::string& error) {
} }
if(ts::config::web::activated && serverInstance->sslManager()->web_ssl_options()) { if(ts::config::web::activated && serverInstance->sslManager()->web_ssl_options()) {
string webHostname = this->properties()[property::VIRTUALSERVER_WEB_HOST]; string web_host_string = this->properties()[property::VIRTUALSERVER_WEB_HOST];
if(webHostname.empty()) webHostname = this->properties()[property::VIRTUALSERVER_HOST].as<string>(); 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(); startTimestamp = std::chrono::system_clock::now();
#ifdef COMPILE_WEB_CLIENT #ifdef COMPILE_WEB_CLIENT
webControlServer = new WebControlServer(self.lock()); 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; error = "could not start web server. Message: " + error;
this->stop("failed to start"); this->stop("failed to start");
return false; return false;
@ -735,7 +746,7 @@ void TSServer::broadcastMessage(std::shared_ptr<ConnectedClient> invoker, std::s
return; return;
} }
this->forEachClient([&](shared_ptr<ConnectedClient> cl){ 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; l_channel = l_channel->next;
continue; /* all subchannels had been checked */ continue; /* all subchannels had been checked */
} else if(visible && !has_perm) { } else if(visible && !has_perm) {
//Test if tree comes invisible
for(const auto& entry : this->test_channel(l_channel, l_own, cache)) for(const auto& entry : this->test_channel(l_channel, l_own, cache))
result.emplace_back(false, entry); result.emplace_back(false, entry);
} }

View File

@ -23,8 +23,9 @@ namespace ts {
bool subscribed = false; bool subscribed = false;
bool editable = false; bool editable = false;
bool _deleted = false; bool joinable = false; /* used within notify text message */
ChannelId previous_channel = 0; 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::weak_ptr<BasicChannel> handle;
std::chrono::system_clock::time_point view_timestamp; std::chrono::system_clock::time_point view_timestamp;
private: private:

View File

@ -966,4 +966,55 @@ permission::v2::PermissionFlaggedValue ConnectedClient::calculate_permission_val
auto value = this->permissionValue(permission::PERMTEST_ORDERED, permission, nullptr); auto value = this->permissionValue(permission::PERMTEST_ORDERED, permission, nullptr);
return {value, value != permNotGranted}; 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 //Group manager chat
virtual bool notifyClientChatComposing(const std::shared_ptr<ConnectedClient> &); virtual bool notifyClientChatComposing(const std::shared_ptr<ConnectedClient> &);
virtual bool notifyClientChatClosed(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){ 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 //Group Client Groups
virtual bool notifyServerGroupClientAdd(const std::shared_ptr<ConnectedClient> &invoker, const std::shared_ptr<ConnectedClient> &client, const std::shared_ptr<Group> &group); 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; permission::PermissionValue channels_ignore_view = permNotGranted;
bool subscribeToAll = false; 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> selectedBot;
std::weak_ptr<MusicClient> subscribed_bot; std::weak_ptr<MusicClient> subscribed_bot;
@ -396,7 +398,7 @@ namespace ts {
CommandResult handleCommandClientUpdate(Command&); CommandResult handleCommandClientUpdate(Command&);
CommandResult handleCommandClientEdit(Command&); CommandResult handleCommandClientEdit(Command&);
CommandResult handleCommandClientEdit(Command&, const std::shared_ptr<ConnectedClient>& /* target */); CommandResult handleCommandClientEdit(Command&, const std::shared_ptr<ConnectedClient>& /* target */);
CommandResult handleCommandClientMove(Command&); //TODO: Use cached permission values CommandResult handleCommandClientMove(Command&);
CommandResult handleCommandClientGetVariables(Command&); CommandResult handleCommandClientGetVariables(Command&);
CommandResult handleCommandClientKick(Command&); CommandResult handleCommandClientKick(Command&);
CommandResult handleCommandClientPoke(Command&); CommandResult handleCommandClientPoke(Command&);

View File

@ -1186,7 +1186,8 @@ CommandResult ConnectedClient::handleCommandChannelGroupAddPerm(Command &cmd) {
if (cl->channelGroupAssigned(channelGroup, cl->getChannel())) { if (cl->channelGroupAssigned(channelGroup, cl->getChannel())) {
if(cl->update_cached_permissions()) if(cl->update_cached_permissions())
cl->sendNeededPermissions(false); /* update the needed 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->channelGroupAssigned(channelGroup, cl->getChannel())) {
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 */ 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,17 +1313,18 @@ CommandResult ConnectedClient::handleCommandClientMove(Command &cmd) {
if(!this->permissionGranted(permission::PERMTEST_ORDERED, permission::b_channel_ignore_join_power, 1, channel, true, permission_cache)) { 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); 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>();
auto val = this->permissionValue(permission::PERMTEST_ORDERED, permission::b_client_is_sticky, this->currentChannel, permission_cache_current);
if (val != permNotGranted && val > 0) {
auto st = this->permissionValue(permission::PERMTEST_ORDERED, permission::b_client_ignore_sticky, this->currentChannel, permission_cache_current);
if (st != 1)
return CommandResultPermissionError{permission::b_client_is_sticky};
}
}
} }
if (target_client == this) {
auto permission_cache_current = make_shared<CalculateCache>();
auto val = this->permissionValue(permission::PERMTEST_ORDERED, permission::b_client_is_sticky, this->currentChannel, permission_cache_current);
if (val != permNotGranted && val > 0) {
auto st = this->permissionValue(permission::PERMTEST_ORDERED, permission::b_client_ignore_sticky, this->currentChannel, permission_cache_current);
if (st != 1)
return CommandResultPermissionError{permission::b_client_is_sticky};
}
}
if (target_client != this) { 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_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); 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,29 +2397,31 @@ CommandResult ConnectedClient::handleCommandChannelAddPerm(Command &cmd) {
for(const auto& client : this->server->getClientsByChannel(channel)) { for(const auto& client : this->server->getClientsByChannel(channel)) {
/* let them lock the server channel tree as well (read lock so does not matter) */ /* let them lock the server channel tree as well (read lock so does not matter) */
client->updateChannelClientProperties(true, true); 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) {
this->server->forEachClient([&](const shared_ptr<ConnectedClient>& cl) { auto l_source = this->server->channelTree->findLinkedChannel(channel->channelId());
/* server tree read lock still active */ this->server->forEachClient([&](const shared_ptr<ConnectedClient>& cl) {
auto l_source = cl->server->channelTree->findLinkedChannel(channel->channelId()); /* server tree read lock still active */
auto l_target = !cl->currentChannel ? nullptr : cl->server->channelTree->findLinkedChannel(cl->currentChannel->channelId()); auto l_target = !cl->currentChannel ? nullptr : cl->server->channelTree->findLinkedChannel(cl->currentChannel->channelId());
sassert(l_source); sassert(l_source);
if(cl->currentChannel) sassert(l_target); if(cl->currentChannel) sassert(l_target);
{ {
unique_lock client_channel_lock(cl->channel_lock); unique_lock client_channel_lock(cl->channel_lock);
deque<ChannelId> deleted; deque<ChannelId> deleted;
for(const auto& update_entry : cl->channels->update_channel(l_source, l_target)) { for(const auto& update_entry : cl->channels->update_channel(l_source, l_target)) {
if(update_entry.first) if(update_entry.first)
cl->notifyChannelShow(update_entry.second->channel(), update_entry.second->previous_channel); cl->notifyChannelShow(update_entry.second->channel(), update_entry.second->previous_channel);
else deleted.push_back(update_entry.second->channelId()); else deleted.push_back(update_entry.second->channelId());
} }
if(!deleted.empty()) if(!deleted.empty())
cl->notifyChannelHide(deleted, false); cl->notifyChannelHide(deleted, false);
} }
}); });
}
return command_result; return command_result;
} }
@ -2468,8 +2473,10 @@ CommandResult ConnectedClient::handleCommandChannelDelPerm(Command &cmd) {
if(updateClients && this->server) if(updateClients && this->server)
this->server->forEachClient([&](std::shared_ptr<ConnectedClient> cl) { this->server->forEachClient([&](std::shared_ptr<ConnectedClient> cl) {
if(cl->currentChannel == channel) if(cl->currentChannel == channel) {
cl->updateChannelClientProperties(true, true); 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) { if(update_view && this->server) {
this->server->forEachClient([&](std::shared_ptr<ConnectedClient> cl) { 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 */ cl->sendNeededPermissions(false); /* cached permissions had changed, notify the client */
if (checkTp) if (checkTp)
cl->updateChannelClientProperties(true, true); cl->updateChannelClientProperties(true, true);
cl->join_state_id++; /* join permission may changed, all channels need to be recalculate dif needed */
} }
}); });
}).detach(); }).detach();
@ -2935,6 +2943,7 @@ CommandResult ConnectedClient::handleCommandServerGroupDelPerm(Command &cmd) {
cl->sendNeededPermissions(false); /* cached permissions had changed, notify the client */ cl->sendNeededPermissions(false); /* cached permissions had changed, notify the client */
if (checkTp) if (checkTp)
cl->updateChannelClientProperties(true, true); cl->updateChannelClientProperties(true, true);
cl->join_state_id++; /* join permission may changed, all channels need to be recalculate dif needed */
} }
}); });
}).detach(); }).detach();
@ -3021,10 +3030,13 @@ CommandResult ConnectedClient::handleCommandServerGroupAutoAddPerm(ts::Command&
server->forEachClient([groups, checkTp](shared_ptr<ConnectedClient> cl) { server->forEachClient([groups, checkTp](shared_ptr<ConnectedClient> cl) {
for(const auto& serverGroup : groups) { for(const auto& serverGroup : groups) {
if (cl->serverGroupAssigned(serverGroup)) { 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 */ cl->sendNeededPermissions(false); /* cached permissions had changed, notify the client */
if (checkTp) }
cl->updateChannelClientProperties(true, true); if (checkTp) {
cl->updateChannelClientProperties(true, true);
}
cl->join_state_id++; /* join permission may changed, all channels need to be recalculate if needed */
break; break;
} }
} }
@ -3104,6 +3116,7 @@ CommandResult ConnectedClient::handleCommandServerGroupAutoDelPerm(ts::Command&
cl->sendNeededPermissions(false); /* cached permissions had changed, notify the client */ cl->sendNeededPermissions(false); /* cached permissions had changed, notify the client */
if (checkTp) if (checkTp)
cl->updateChannelClientProperties(true, true); cl->updateChannelClientProperties(true, true);
cl->join_state_id++; /* join permission may changed, all channels need to be recalculate dif needed */
break; break;
} }
} }
@ -3228,25 +3241,57 @@ CommandResult ConnectedClient::handleCommandSendTextMessage(Command &cmd) {
} }
if(this->handleTextMessage(ChatMessageMode::TEXTMODE_PRIVATE, cmd["msg"], target)) return CommandResult::Success; if(this->handleTextMessage(ChatMessageMode::TEXTMODE_PRIVATE, cmd["msg"], target)) return CommandResult::Success;
target->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(), cmd["msg"].string()); this->notifyTextMessage(ChatMessageMode::TEXTMODE_PRIVATE, _this.lock(), target->getClientId(), 0, cmd["msg"].string());
} else if (cmd["targetmode"] == ChatMessageMode::TEXTMODE_CHANNEL) { } else if (cmd["targetmode"] == ChatMessageMode::TEXTMODE_CHANNEL) {
CMD_REQ_CHANNEL;
CACHED_PERM_CHECK(permission::b_client_channel_textmessage_send, 1, false); 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; if(this->handleTextMessage(ChatMessageMode::TEXTMODE_CHANNEL, cmd["msg"], nullptr))
for (auto &cl : this->server->getClientsByChannel(this->currentChannel)) return CommandResult::Success;
cl->notifyTextMessage(ChatMessageMode::TEXTMODE_CHANNEL, _this.lock(), this->getClientId(), cmd["msg"].string()); }
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 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()); conversation->register_message(this->getClientDatabaseId(), this->getUid(), this->getDisplayName(), cmd["msg"].string());
} else if (cmd["targetmode"] == ChatMessageMode::TEXTMODE_SERVER) { } else if (cmd["targetmode"] == ChatMessageMode::TEXTMODE_SERVER) {
CACHED_PERM_CHECK(permission::b_client_server_textmessage_send, 1); CACHED_PERM_CHECK(permission::b_client_server_textmessage_send, 1);
if(this->handleTextMessage(ChatMessageMode::TEXTMODE_SERVER, cmd["msg"], nullptr)) return CommandResult::Success; if(this->handleTextMessage(ChatMessageMode::TEXTMODE_SERVER, cmd["msg"], nullptr)) return CommandResult::Success;
this->server->forEachClient([&](shared_ptr<ConnectedClient> client) { 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"}; } else return {findError("parameter_invalid"), "invalid target mode"};
@ -4568,7 +4613,7 @@ CommandResult ConnectedClient::handleCommandClientMute(Command &cmd) {
} }
if (config::voice::notifyMuted) 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; return CommandResult::Success;
} }
@ -4589,7 +4634,7 @@ CommandResult ConnectedClient::handleCommandClientUnmute(Command &cmd) {
} }
if (config::voice::notifyMuted) 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; return CommandResult::Success;
} }
@ -4879,6 +4924,7 @@ CommandResult ConnectedClient::handleCommandClientAddPerm(Command &cmd) {
elm->sendNeededPermissions(false); /* cached permissions had changed, notify the client */ elm->sendNeededPermissions(false); /* cached permissions had changed, notify the client */
if(update_channels) if(update_channels)
elm->updateChannelClientProperties(true, true); elm->updateChannelClientProperties(true, true);
elm->join_state_id++; /* join permission may changed, all channels need to be recalculate dif needed */
} }
return CommandResult::Success; return CommandResult::Success;
@ -4917,6 +4963,7 @@ CommandResult ConnectedClient::handleCommandClientDelPerm(Command &cmd) {
elm->sendNeededPermissions(false); /* cached permissions had changed, notify the client */ elm->sendNeededPermissions(false); /* cached permissions had changed, notify the client */
if(update_channel) if(update_channel)
elm->updateChannelClientProperties(true, true); elm->updateChannelClientProperties(true, true);
elm->join_state_id++; /* join permission may changed, all channels need to be recalculate dif needed */
} }
return CommandResult::Success; return CommandResult::Success;
} }
@ -4938,9 +4985,9 @@ CommandResult ConnectedClient::handleCommandChannelClientPermList(Command &cmd)
CMD_RESET_IDLE; CMD_RESET_IDLE;
CMD_CHK_AND_INC_FLOOD_POINTS(5); CMD_CHK_AND_INC_FLOOD_POINTS(5);
PERM_CHECKR(permission::b_virtualserver_channelclient_permission_list, 1, true); PERM_CHECKR(permission::b_virtualserver_channelclient_permission_list, 1, true);
RESOLVE_CHANNEL_R(cmd["cid"], true);
std::shared_ptr<BasicChannel> channel = this->server->channelTree->findChannel(cmd["cid"].as<ChannelId>()); auto channel = dynamic_pointer_cast<ServerChannel>(l_channel->entry);
if (!channel) return {findError("channel_invalid_id"), "Cant resolve channel"}; if(!channel) return {ErrorType::VSError};
if(!serverInstance->databaseHelper()->validClientDatabaseId(this->server, cmd["cldbid"])) return {findError("client_invalid_id"), "invalid client id"}; 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>()); auto mgr = serverInstance->databaseHelper()->loadClientPermissionManager(this->server, cmd["cldbid"].as<ClientDbId>());
@ -4987,21 +5034,21 @@ CommandResult ConnectedClient::handleCommandChannelClientPermList(Command &cmd)
return CommandResult::Success; return CommandResult::Success;
} }
//TODO: Update this specific channel visibility?
CommandResult ConnectedClient::handleCommandChannelClientDelPerm(Command &cmd) { CommandResult ConnectedClient::handleCommandChannelClientDelPerm(Command &cmd) {
CMD_REQ_SERVER; CMD_REF_SERVER(server_ref);
CMD_RESET_IDLE; CMD_RESET_IDLE;
CMD_CHK_AND_INC_FLOOD_POINTS(5); CMD_CHK_AND_INC_FLOOD_POINTS(5);
if (!serverInstance->databaseHelper()->validClientDatabaseId(this->server, cmd["cldbid"])) return {findError("parameter_invalid"), "Invalid manager db id"}; 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>()); auto mgr = serverInstance->databaseHelper()->loadClientPermissionManager(this->server, cmd["cldbid"]);
if (!channel) return {findError("channel_invalid_id"), "Cant resolve channel"}; 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);
auto mgr = serverInstance->databaseHelper()->loadClientPermissionManager(this->server, cmd["cldbid"]); RESOLVE_CHANNEL_R(cmd["cid"], true);
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); 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 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"]); auto cll = this->server->findClientsByCldbId(cmd["cldbid"]);
for (int index = 0; index < cmd.bulkCount(); index++) { for (int index = 0; index < cmd.bulkCount(); index++) {
PARSE_PERMISSION(cmd); 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); mgr->set_channel_permission(permType, channel->channelId(), permission::v2::empty_permission_values, permission::v2::do_nothing, permission::v2::delete_value);
} else { } else {
mgr->set_channel_permission(permType, channel->channelId(), permission::v2::empty_permission_values, permission::v2::delete_value, permission::v2::do_nothing); 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()) { if (!cll.empty()) {
for (const auto &cl : cll) { for (const auto &elm : cll) {
if(cl->update_cached_permissions()) /* update cached calculated permissions */ if(elm->update_cached_permissions()) /* update cached calculated permissions */
cl->sendNeededPermissions(false); /* cached permissions had changed, notify the client */ elm->sendNeededPermissions(false); /* cached permissions had changed, notify the client */
}
for (const auto &elm : cll) if(elm->currentChannel == channel) {
if (elm->currentChannel == channel) {
elm->updateChannelClientProperties(true, true); 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; return CommandResult::Success;
} }
//TODO: Update this specific channel visibility?
CommandResult ConnectedClient::handleCommandChannelClientAddPerm(Command &cmd) { CommandResult ConnectedClient::handleCommandChannelClientAddPerm(Command &cmd) {
CMD_REQ_SERVER; CMD_REF_SERVER(server_ref);
CMD_RESET_IDLE; CMD_RESET_IDLE;
CMD_CHK_AND_INC_FLOOD_POINTS(5); CMD_CHK_AND_INC_FLOOD_POINTS(5);
if (!serverInstance->databaseHelper()->validClientDatabaseId(this->server, cmd["cldbid"])) return {findError("parameter_invalid"), "Invalid manager db id"}; 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"]); 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); 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>()); RESOLVE_CHANNEL_R(cmd["cid"], true);
if (!channel) return {findError("channel_invalid_id"), "Cant resolve channel"}; 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); 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 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");
auto onlineClientInstances = this->server->findClientsByCldbId(cmd["cldbid"]); auto onlineClientInstances = this->server->findClientsByCldbId(cmd["cldbid"]);
bool update_view = false;
for (int index = 0; index < cmd.bulkCount(); index++) { for (int index = 0; index < cmd.bulkCount(); index++) {
PARSE_PERMISSION(cmd); 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); mgr->set_channel_permission(permType, channel->channelId(), {0, cmd[index]["permvalue"]}, permission::v2::do_nothing, permission::v2::set_value);
} else { } 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); 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()) if (!onlineClientInstances.empty())
@ -5070,9 +5136,24 @@ CommandResult ConnectedClient::handleCommandChannelClientAddPerm(Command &cmd) {
if (elm->update_cached_permissions()) /* update cached calculated permissions */ if (elm->update_cached_permissions()) /* update cached calculated permissions */
elm->sendNeededPermissions(false); /* cached permissions had changed, notify the client */ elm->sendNeededPermissions(false); /* cached permissions had changed, notify the client */
if (elm->currentChannel == channel) { if(elm->currentChannel == channel) {
elm->updateChannelClientProperties(true, true); 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; return CommandResult::Success;
@ -7337,12 +7418,14 @@ CommandResult ConnectedClient::handleCommandConversationHistory(ts::Command &com
size_t index = 0; size_t index = 0;
size_t length = 0; size_t length = 0;
bool merge = command.hasParm("merge"); bool merge = command.hasParm("merge");
for(auto& message : messages) {
for(auto it = messages.rbegin(); it != messages.rend(); it++) {
if(index == 0) { if(index == 0) {
notify[index]["cid"] = conversation_id; notify[index]["cid"] = conversation_id;
notify[index]["flag_volatile"] = conversation->volatile_only(); 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]["timestamp"] = duration_cast<milliseconds>(message->message_timestamp.time_since_epoch()).count();
notify[index]["sender_database_id"] = message->sender_database_id; notify[index]["sender_database_id"] = message->sender_database_id;
notify[index]["sender_unique_id"] = message->sender_unique_id; notify[index]["sender_unique_id"] = message->sender_unique_id;

View File

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

View File

@ -122,15 +122,15 @@ bool ConnectedClient::handleTextMessage(ChatMessageMode mode, std::string text,
handle_text_command_fn_t function; handle_text_command_fn_t function;
if(mode == ChatMessageMode::TEXTMODE_SERVER) { if(mode == ChatMessageMode::TEXTMODE_SERVER) {
function = [&](const shared_ptr<ConnectedClient>& sender, const string& message) { 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) { } else if(mode == ChatMessageMode::TEXTMODE_CHANNEL) {
function = [&](const shared_ptr<ConnectedClient>& sender, const string& message) { 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) { } else if(mode == ChatMessageMode::TEXTMODE_PRIVATE) {
function = [&, target](const shared_ptr<ConnectedClient>& sender, const string& message) { 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 notifyPluginCmd(std::string name, std::string msg,std::shared_ptr<ConnectedClient>) override;
bool notifyClientChatComposing(const std::shared_ptr<ConnectedClient> &ptr) override; bool notifyClientChatComposing(const std::shared_ptr<ConnectedClient> &ptr) override;
bool notifyClientChatClosed(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 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; 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); 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); 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_CHANNEL) CHK_EVENT(QEVENTGROUP_CHAT, QEVENTSPECIFIER_CHAT_MESSAGE_CHANNEL);
else if(mode == ChatMessageMode::TEXTMODE_SERVER) CHK_EVENT(QEVENTGROUP_CHAT, QEVENTSPECIFIER_CHAT_MESSAGE_SERVER); 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) { 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::server;
using namespace ts::protocol; 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); memtrack::allocated<WebClient>(this);
assert(server->getTS()); assert(server->getTS());
memcpy(&this->remote_address, &addr, sizeof(sockaddr_in));
this->state = ConnectionState::INIT_LOW; this->state = ConnectionState::INIT_LOW;
this->file_descriptor = fd; this->file_descriptor = fd;
} }

View File

@ -21,7 +21,7 @@ namespace ts {
class WebClient : public SpeakingClient { class WebClient : public SpeakingClient {
friend class WebControlServer; friend class WebControlServer;
public: public:
WebClient(WebControlServer*, int socketFd, const sockaddr_in&); WebClient(WebControlServer*, int socketFd);
~WebClient() override; ~WebClient() override;
void sendJson(const Json::Value&); 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++) { 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 */ if((*it)->message_timestamp > end_timestamp) /* message has been send after the search timestamp */
continue; continue;
if(begin_timestamp.time_since_epoch().count() != 0 && (*it)->message_timestamp < begin_timestamp)
return result;
result.push_back(*it); result.push_back(*it);
if(--message_count == 0) if(--message_count == 0)
return result; 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) if(begin_timestamp.time_since_epoch().count() != 0 && rmid->timestamp < begin_timestamp)
return result; return result;
if(rmid->timestamp >= timestamp) 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 */ 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(const std::shared_ptr<TSServer>& handle) : handle(handle) {}
WebControlServer::~WebControlServer() = default; WebControlServer::~WebControlServer() = default;
bool WebControlServer::start(sockaddr_in* addr, std::string& errorMessage) { bool WebControlServer::start(const std::deque<std::shared_ptr<WebControlServer::Binding>>& bindings, std::string& error) {
if(this->running()) return false; if(this->running()) {
this->_running = true; error = "server already running";
boundAddress = new sockaddr_in; return false;
memcpy(boundAddress, addr, sizeof(*addr)); }
this->_running = true;
this->serverSocket = socket(AF_INET, SOCK_STREAM | SOCK_NONBLOCK, 0); /* reserve backup file descriptor in case that the max file descriptors have been reached */
if (serverSocket < 0) { {
logCritical(this->handle->getServerId(), "Cant create server socket for server"); this->server_reserve_fd = dup(1);
return false; 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; for(auto& binding : bindings) {
if (setsockopt(serverSocket, SOL_SOCKET, SO_REUSEADDR, &enable, sizeof(int)) < 0) binding->file_descriptor = socket(binding->address.ss_family, SOCK_STREAM | SOCK_NONBLOCK, 0);
logError("setsockopt(SO_REUSEADDR) failed"); if(binding->file_descriptor < 0) {
if(setsockopt(serverSocket, IPPROTO_TCP, TCP_NOPUSH, &disabled, sizeof disabled) < 0) logError(this->handle->getServerId(), "[Web] Failed to bind server to {}. (Failed to create socket: {} | {})", binding->as_string(), errno, strerror(errno));
logError("Cant disable nopush! Error: "+to_string(errno)+" / "+strerror(errno)); continue;
setsockopt(serverSocket, SOL_SOCKET, SO_KEEPALIVE, &enable, sizeof(enable)); }
if(fcntl(serverSocket, F_SETFD, FD_CLOEXEC) < 0) int enable = 1, disabled = 0;
logError(this->handle->getServerId(), "Failed to enable FD_CLOEXEC for {} (WebControlServer)", serverSocket);
if (bind(serverSocket, (struct sockaddr *) boundAddress, sizeof(*boundAddress)) < 0) { if (setsockopt(binding->file_descriptor, SOL_SOCKET, SO_REUSEADDR, &enable, sizeof(int)) < 0)
errorMessage = string() + "Cant bind server socket (" + strerror(errno) + ")"; logWarning(this->handle->getServerId(), "[Web] Failed to activate SO_REUSEADDR for binding {} ({} | {})", binding->as_string(), errno, strerror(errno));
return false; 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(listen(serverSocket, 255) < 0){ if(binding->address.ss_family == AF_INET6) {
errorMessage = string() + "Cant listen on server socket (" + strerror(errno) + ")"; if(setsockopt(binding->file_descriptor, IPPROTO_IPV6, IPV6_V6ONLY, &enable, sizeof(int)) < 0)
return false; 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));
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);
return true; 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);
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) { #define CLOSE_CONNECTION \
sockaddr_in remoteAddr{}; if(shutdown(file_descriptor, SHUT_RDWR) < 0) { \
memset(&remoteAddr, 0, sizeof(sockaddr_in)); debugMessage(LOG_FT, "[{}] Failed to shutdown socket ({} | {}).", logging_address(remote_address), errno, strerror(errno)); \
socklen_t addrLength = sizeof(remoteAddr); } \
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); inline std::string logging_address(const sockaddr_storage& address) {
if (acceptedSocketFd < 0) { if(config::server::disable_ip_saving)
if(errno == EAGAIN) //No manager return "X.X.X.X" + to_string(net::port(address));
return; return net::to_string(address, true);
}
logError(this->handle->getServerId(), lstream << "Having an error while accepting a new manager. ("+to_string(errno)+"/"+string(strerror(errno))+")"); void WebControlServer::on_client_receive(int _server_file_descriptor, short ev, void *arg) {
return; sockaddr_storage remote_address{};
} memset(&remote_address, 0, sizeof(remote_address));
socklen_t address_length = sizeof(remote_address);
shared_ptr<WebClient> client = std::make_shared<WebClient>(this, acceptedSocketFd, remoteAddr); int file_descriptor = accept(_server_file_descriptor, (struct sockaddr *) &remote_address, &address_length);
client->applySelfLock(client); if (file_descriptor < 0) {
client->initialize(); if(errno == EAGAIN)
memcpy(&client->remote_address, &remoteAddr, sizeof(sockaddr_in)); return;
this->clientLock.lock(); if(errno == EMFILE || errno == ENFILE) {
this->clients.push_back(client); if(errno == EMFILE)
this->clientLock.unlock(); logError(this->handle->getServerId(), "[Web] Server ran out file descriptors. Please increase the process file descriptor limit.");
event_add(client->readEvent, nullptr); else
logMessage(this->handle->getServerId(), lstream << "[Web] Got new manager from " << client->getLoggingPeerIp() << ":" << client->getPeerPort() << endl); 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, file_descriptor);
memcpy(&client->remote_address, &remote_address, sizeof(remote_address));
client->applySelfLock(client);
client->initialize();
this->clientLock.lock();
this->clients.push_back(client);
this->clientLock.unlock();
event_add(client->readEvent, nullptr);
logMessage(this->handle->getServerId(), "[Web] Got new client from {}:{}", client->getLoggingPeerIp(), client->getPeerPort());
} }
void WebControlServer::stop() { void WebControlServer::stop() {
@ -94,19 +192,28 @@ void WebControlServer::stop() {
e->closeConnection(system_clock::now()); e->closeConnection(system_clock::now());
} }
if(this->acceptEvent) {
event_del(this->acceptEvent); for(auto& binding : this->bindings) {
event_free(this->acceptEvent); if(binding->event_accept) {
event_del_block(binding->event_accept);
event_free(binding->event_accept);
binding->event_accept = nullptr;
}
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();
if(this->serverSocket > 0){ if(this->server_reserve_fd > 0) {
if(shutdown(this->serverSocket, SHUT_RDWR) < 0) logError(this->handle->getServerId(), "Could not shutdown WebSocketServer socket!"); if(close(this->server_reserve_fd) < 0)
if(close(this->serverSocket) < 0) logError(this->handle->getServerId(), "Could not close WebSocketServer socket!"); logError(this->handle->getServerId(), "[Web] Failed to close backup file descriptor ({} | {})", errno, strerror(errno));
} }
this->serverSocket = -1; this->server_reserve_fd = -1;
delete this->boundAddress;
this->boundAddress = nullptr;
} }
void WebControlServer::unregisterConnection(const std::shared_ptr<WebClient>& connection) { void WebControlServer::unregisterConnection(const std::shared_ptr<WebClient>& connection) {

View File

@ -3,21 +3,29 @@
#include <ThreadPool/Mutex.h> #include <ThreadPool/Mutex.h>
#include <ThreadPool/Thread.h> #include <ThreadPool/Thread.h>
#include <event.h> #include <event.h>
#include <misc/net.h>
namespace ts { namespace ts {
namespace server { namespace server {
class TSServer; class TSServer;
class WebClient; class WebClient;
class WebRTCServer;
class WebControlServer { class WebControlServer {
friend class WebClient; friend class WebClient;
public: 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>&); explicit WebControlServer(const std::shared_ptr<TSServer>&);
~WebControlServer(); ~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; } inline bool running(){ return _running; }
void stop(); void stop();
std::shared_ptr<TSServer> getTS(){ return this->handle; } std::shared_ptr<TSServer> getTS(){ return this->handle; }
@ -34,11 +42,15 @@ namespace ts {
std::deque<std::shared_ptr<WebClient>> clients; std::deque<std::shared_ptr<WebClient>> clients;
bool _running = false; bool _running = false;
sockaddr_in* boundAddress = nullptr; std::deque<std::shared_ptr<Binding>> bindings;
int serverSocket = 0;
::event* acceptEvent = nullptr; 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: 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>&); void unregisterConnection(const std::shared_ptr<WebClient>&);
}; };
} }

View File

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

2
shared

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