Teaspeak-Server/server/src/channel/ClientChannelView.cpp

440 lines
18 KiB
C++

#include <misc/sassert.h>
#include <log/LogUtils.h>
#include <misc/memtracker.h>
#include "src/client/ConnectedClient.h"
#include "ClientChannelView.h"
using namespace std;
using namespace ts;
using namespace ts::server;
ViewEntry::ViewEntry(const std::shared_ptr<ts::BasicChannel> &handle, bool editable) : handle(handle), editable(editable) {
memtrack::allocated<ViewEntry>(this);
assert(handle);
this->view_timestamp = chrono::system_clock::now();
this->previous_channel = handle->channelOrder();
this->cached_channel_id = handle->channelId();
this->cached_parent_id = handle->hasParent() ? handle->parent()->channelId() : 0;
}
ViewEntry::~ViewEntry() {
memtrack::freed<ViewEntry>(this);
}
ChannelId ts::ViewEntry::channelId() const {
if(this->cached_channel_id != 0) return this->cached_channel_id; //We've already got a channel id
//TODO cached_channel_id should be > 0 every time?
auto channel = this->handle.lock();
if(!channel) {
logCritical(LOG_GENERAL, "ViewEntry::channelId() called without a valid handle and cached_channel_id == 0!");
return 0;
}
return channel->channelId();
}
ChannelId ViewEntry::parentId() const {
if(this->cached_parent_id != 0) return this->cached_parent_id;
auto channel = this->handle.lock();
return channel && channel->parent() ? channel->parent()->channelId() : 0;
}
ChannelId ts::ViewEntry::previousChannelId() const {
return previous_channel;
}
void ts::ViewEntry::setPreviousChannelId(ts::ChannelId id) {
assert(this->editable);
this->previous_channel = id;
}
void ts::ViewEntry::setParentChannelId(ts::ChannelId id) {
assert(this->editable);
this->cached_parent_id = id;
}
ClientChannelView::ClientChannelView(server::ConnectedClient* handle) : owner(handle) {
memtrack::allocated<ClientChannelView>(this);
}
ClientChannelView::~ClientChannelView() {
memtrack::freed<ClientChannelView>(this);
}
ServerId ClientChannelView::getServerId() {
return owner ? owner->getServerId() : 0;
}
std::deque<std::shared_ptr<BasicChannel>> ClientChannelView::channels(const std::shared_ptr<ts::BasicChannel> &head,
int deep) {
std::deque<std::shared_ptr<BasicChannel>> result;
for(const auto& entry : this->entries(head ? make_shared<ViewEntry>(head) : nullptr, deep)) {
auto channel = dynamic_pointer_cast<ViewEntry>(entry)->handle.lock();
if(channel)
result.push_back(channel);
}
return result;
}
bool ClientChannelView::channel_visible(const std::shared_ptr<ts::BasicChannel> &channel,
const std::shared_ptr<ts::BasicChannel> &head,
int deep) {
if(!channel) return true; //I thing the void is kind of visible :D
return this->has_entry(make_shared<ViewEntry>(channel), head ? make_shared<ViewEntry>(head) : nullptr, deep);
}
std::shared_ptr<ViewEntry> ClientChannelView::find_channel(ts::ChannelId id) {
return dynamic_pointer_cast<ViewEntry>(this->find_entry(id));
}
std::shared_ptr<ViewEntry> ClientChannelView::find_channel(const std::shared_ptr<ts::BasicChannel> &channel) {
if(!channel) return nullptr;
deque<shared_ptr<BasicChannel>> heads{channel};
shared_ptr<LinkedTreeEntry> head = nullptr;
bool deep_search = false;
while(heads.front()) {
auto parent = heads.front()->parent();
if(!parent && heads.front()->properties()[property::CHANNEL_PID].as<ChannelId>() != 0) {
head = this->find_linked_entry(channel->channelId(), nullptr);//We're searching for a deleted head! So lets iterate over everything
deep_search = true;
break;
}
heads.push_front(parent);
}
if(!deep_search) {
heads.pop_front();
while(heads.size() > 1) {
auto front = move(heads.front());
heads.pop_front();
head = this->find_linked_entry(front->channelId(), head, 1);
if(!head) return nullptr; //Channel tree not visible!
}
head = this->find_linked_entry(channel->channelId(), head, 1);
}
return head ? static_pointer_cast<ViewEntry>(head->entry) : nullptr;
}
std::deque<std::shared_ptr<ViewEntry>> ClientChannelView::insert_channels(shared_ptr<TreeView::LinkedTreeEntry> head, bool test_permissions, bool first_only, std::shared_ptr<server::CalculateCache> cache) {
std::deque<std::shared_ptr<ViewEntry>> result;
if(!cache && test_permissions) cache = make_shared<CalculateCache>();
bool has_perm = !test_permissions || owner->permissionGranted(permission::PERMTEST_ORDERED, permission::b_channel_ignore_view_power, 1, nullptr, true, cache);
bool first = true;
while(head) {
if(!first && first_only) break;
first = false;
auto channel = dynamic_pointer_cast<BasicChannel>(head->entry);
if(this->channel_visible(channel)) {
if(head->child_head) {
for(const auto& sub : this->insert_channels(head->child_head, test_permissions, false, cache))
result.push_back(sub);
}
head = head->next;
continue;
}
if(!has_perm) {
if(!channel->permission_granted(permission::i_channel_needed_view_power, this->owner->calculate_permission_value(permission::i_channel_view_power, channel->channelId()), false)) {
head = head->next;
debugMessage(this->getServerId(), "{}[CHANNEL] Dropping channel {} ({}) (No permissions)", CLIENT_STR_LOG_PREFIX_(this->owner), channel->channelId(), channel->name());
continue;
}
}
auto entry = make_shared<ViewEntry>(dynamic_pointer_cast<BasicChannel>(head->entry), true);
std::shared_ptr<ViewEntry> parent, previous;
if(head->parent.lock()) {
auto remote_parent = head->parent.lock();
parent = dynamic_pointer_cast<ViewEntry>(this->find_entry(remote_parent->entry->channelId()));
sassert(parent);
}
auto remote_previous = head->previous; //Get our first thing
while(remote_previous && !(previous = dynamic_pointer_cast<ViewEntry>(this->find_entry(remote_previous->entry->channelId())))) {
remote_previous = remote_previous->previous;
}
auto previous_channel = previous ? previous->channel() : nullptr;
if(!this->insert_entry(entry, parent, previous)) {
logError(this->getServerId(), "Failed to insert channel into client view!");
head = head->next;
continue;
};
debugMessage(this->getServerId(), "{}[CHANNELS] Insert channel {} ({} => order {}) after {} ({})",
CLIENT_STR_LOG_PREFIX_(this->owner),
channel->channelId(), channel->name(), entry->previousChannelId(),
previous ? previous->channelId() : 0, previous_channel ? previous_channel->name() : ""
);
result.push_back(entry);
if(head->child_head) {
for(const auto& sub : this->insert_channels(head->child_head, test_permissions, false, cache))
result.push_back(sub);
}
head = head->next;
}
return result;
}
std::deque<std::shared_ptr<ViewEntry>> ClientChannelView::show_channel(std::shared_ptr<ts::TreeView::LinkedTreeEntry> l_channel, bool& success) {
success = true;
if(this->channel_visible(dynamic_pointer_cast<BasicChannel>(l_channel->entry))) return {};
std::deque<std::shared_ptr<ViewEntry>> result;
deque<shared_ptr<TreeView::LinkedTreeEntry>> parents = {l_channel};
while(parents.front()) {
auto parent = parents.front()->parent.lock();
if(parent && !this->channel_visible(dynamic_pointer_cast<BasicChannel>(parent->entry))) {
parents.push_front(parent);
} else {
parents.push_front(nullptr);
break;
}
}
parents.pop_front();
for(const auto& root_channel : parents) {
auto channel = dynamic_pointer_cast<BasicChannel>(root_channel->entry);
auto entry = make_shared<ViewEntry>(channel, true);
std::shared_ptr<ViewEntry> parent, previous;
if(root_channel->parent.lock()) {
auto remote_parent = root_channel->parent.lock();
parent = dynamic_pointer_cast<ViewEntry>(this->find_entry(remote_parent->entry->channelId()));
sassert(parent);
}
auto remote_previous = root_channel->previous; //Get our first thing
while(remote_previous && !(previous = dynamic_pointer_cast<ViewEntry>(this->find_entry(remote_previous->entry->channelId())))) {
remote_previous = remote_previous->previous;
}
auto previous_channel = previous ? previous->channel() : nullptr; //weak could be may nullptr
debugMessage(this->getServerId(), "{}[CHANNELS] Insert channel {} ({}) after {} ({})",
CLIENT_STR_LOG_PREFIX_(this->owner),
channel->channelId(), channel->name(),
previous ? previous->channelId() : 0, previous_channel ? previous_channel->name() : ""
);
if(!this->insert_entry(entry, parent, previous)) {
logError(this->getServerId(), "Failed to insert channel into client view! (Aborting root)");
success = false;
break;
};
result.push_back(entry);
}
return result;
}
std::deque<std::shared_ptr<ViewEntry>> ClientChannelView::test_channel(std::shared_ptr<ts::TreeView::LinkedTreeEntry> l_old,
std::shared_ptr<ts::TreeView::LinkedTreeEntry> channel_new, shared_ptr<CalculateCache> cache) {
if(!cache) cache = make_shared<CalculateCache>();
std::deque<std::shared_ptr<ViewEntry>> result;
bool has_perm = owner->permissionGranted(permission::PERMTEST_ORDERED, permission::b_channel_ignore_view_power, 1, nullptr, true, cache);
if(has_perm) return {};
deque<shared_ptr<TreeView::LinkedTreeEntry>> parents = {l_old};
while(parents.front()) {
auto parent = parents.front();
auto current = channel_new;
while(current && current != parent) current = current->parent.lock();
if(current == parent) {
//parents.push_front(nullptr);
break;
}
parents.push_front(parent->parent.lock());
}
parents.pop_front();
for(const auto& l_entry : parents) {
auto entry = this->find_entry(l_entry->entry->channelId());
if(!entry) break; //Already cut out!
auto channel = dynamic_pointer_cast<BasicChannel>(l_entry->entry);
sassert(entry->channelId() == channel->channelId());
if(!channel->permission_granted(permission::i_channel_needed_view_power, this->owner->calculate_permission_value(permission::i_channel_view_power, channel->channelId()), false)) {
for(const auto& te : this->delete_entry(entry))
result.push_back(dynamic_pointer_cast<ViewEntry>(te));
debugMessage(this->getServerId(), "{}[CHANNEL] Moving channel tree out of view. Root: {} ({}) (No permissions)", CLIENT_STR_LOG_PREFIX_(this->owner), channel->channelId(), channel->name());
break;
}
}
return result;
}
std::deque<std::pair<bool, std::shared_ptr<ViewEntry>>> ClientChannelView::update_channel(
std::shared_ptr<ts::TreeView::LinkedTreeEntry> l_channel, std::shared_ptr<ts::TreeView::LinkedTreeEntry> l_own, shared_ptr<CalculateCache> cache) {
return update_channel_path(std::move(l_channel), std::move(l_own), std::move(cache), 1);
}
std::deque<std::pair<bool, std::shared_ptr<ViewEntry>>> ClientChannelView::update_channel_path(std::shared_ptr<ts::TreeView::LinkedTreeEntry> l_channel, std::shared_ptr<ts::TreeView::LinkedTreeEntry> l_own, shared_ptr<CalculateCache> cache, ssize_t length) {
if(!cache) cache = make_shared<CalculateCache>();
std::deque<std::pair<bool, std::shared_ptr<ViewEntry>>> result;
bool has_perm = owner->permissionGranted(permission::PERMTEST_ORDERED, permission::b_channel_ignore_view_power, 1, nullptr, true, cache);
while(l_channel && length-- != 0) {
auto b_channel = dynamic_pointer_cast<BasicChannel>(l_channel->entry);
sassert(b_channel);
auto visible = this->channel_visible(b_channel);
if(!visible) {
if(l_channel->parent.lock() && !this->channel_visible(dynamic_pointer_cast<BasicChannel>(l_channel->parent.lock()->entry))) {
l_channel = l_channel->next;
continue; /* all subchannels had been checked, because parent isnt visible */
}
//Test if channel comes visible again!
visible = true;
if(!has_perm) {
if(!b_channel->permission_granted(permission::i_channel_needed_view_power, this->owner->calculate_permission_value(permission::i_channel_view_power, b_channel->channelId()), false)) {
visible = false;
}
}
if(visible) {
for(const auto& entry : this->show_channel(l_channel, visible))
result.emplace_back(true, entry);
for(const auto& entry : this->insert_channels(l_channel->child_head, true, false, cache))
result.emplace_back(true, entry);
}
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);
}
//Root node is okey, test children
if(l_channel->child_head) {
auto entries = this->update_channel_path(l_channel->child_head, l_own, cache, -1);
result.insert(result.end(), entries.begin(), entries.end());
}
l_channel = l_channel->next;
}
return result;
}
std::deque<std::pair<ClientChannelView::ChannelAction, std::shared_ptr<ViewEntry>>> ClientChannelView::change_order(const shared_ptr<LinkedTreeEntry> &channel, const std::shared_ptr<LinkedTreeEntry> &parent, const shared_ptr<LinkedTreeEntry> &head) {
std::deque<std::pair<ClientChannelView::ChannelAction, std::shared_ptr<ViewEntry>>> result;
auto l_entry = this->find_linked_entry(channel->entry->channelId());
auto l_parent = parent ? this->find_linked_entry(parent->entry->channelId()) : nullptr;
if(!l_entry) { //Channel not visible yet
if(!l_parent && parent) return {}; //The invisible channel was moved into an invisible tree
bool has_perm = owner->permissionGranted(permission::PERMTEST_ORDERED, permission::b_channel_ignore_view_power, 1, nullptr);
if(!has_perm) {
has_perm = dynamic_pointer_cast<BasicChannel>(channel->entry)->permission_granted(permission::i_channel_needed_view_power, this->owner->calculate_permission_value(permission::i_channel_view_power, dynamic_pointer_cast<BasicChannel>(channel->entry)->channelId()), false);
}
if(!has_perm) return {}; //Channel wasn't visible and he still has no permission for that :)
std::shared_ptr<ViewEntry> previous;
auto remote_previous = head; //Get our first thing
while(remote_previous && !(previous = dynamic_pointer_cast<ViewEntry>(this->find_entry(remote_previous->entry->channelId())))) {
remote_previous = remote_previous->previous;
}
for(const auto& shown : this->insert_channels(channel, true, true))
result.push_back({ClientChannelView::ENTER_VIEW, shown});
return result; //An invisible channel became visible
}
//Channel visible!
if(!l_parent && parent) { //Channel was visible and moved to invisible tree
auto remove = this->delete_entry(l_entry->entry);
for(const auto& entry : remove)
result.push_back({ClientChannelView::DELETE_VIEW, dynamic_pointer_cast<ViewEntry>(entry)});
return result;
}
//We have just to readjust the order or the parent
std::shared_ptr<ViewEntry> previous;
auto remote_previous = head; //Get our first thing
while(remote_previous && !(previous = dynamic_pointer_cast<ViewEntry>(this->find_entry(remote_previous->entry->channelId())))) {
remote_previous = remote_previous->previous;
}
auto parent_switch = l_parent != l_entry->parent.lock();
if(!this->move_entry(l_entry->entry, l_parent ? l_parent->entry : nullptr, previous)) return {};
return {{parent_switch ? ClientChannelView::MOVE : ClientChannelView::REORDER, dynamic_pointer_cast<ViewEntry>(l_entry->entry)}};
}
std::shared_ptr<ViewEntry> ClientChannelView::add_channel(const std::shared_ptr<ts::TreeView::LinkedTreeEntry>& l_channel) {
auto l_parent_channel = l_channel->parent.lock();
auto parent_channel = l_parent_channel ? this->find_linked_entry(l_parent_channel->entry->channelId()) : nullptr;
if(!parent_channel && l_parent_channel) return nullptr; //Tree not visible!
shared_ptr<ViewEntry> previous;
auto remote_previous = l_channel->previous; //Get our first thing
while(remote_previous && !(previous = dynamic_pointer_cast<ViewEntry>(this->find_entry(remote_previous->entry->channelId())))) {
remote_previous = remote_previous->previous;
}
auto entry = make_shared<ViewEntry>(dynamic_pointer_cast<BasicChannel>(l_channel->entry), true);
if(!this->insert_entry(entry, parent_channel ? parent_channel->entry : nullptr, previous)) return nullptr;
return entry;
}
bool ClientChannelView::remove_channel(ts::ChannelId channelId) {
auto entry = this->find_entry(channelId);
if(!entry)
return false;
auto result = this->delete_entry(entry);
if(result.size() != 1) {
logError(this->owner->getServerId(), "ClientChannelView::remove_channel(...) returned more than one channel! ({})", result.size());
for(const auto& entry : result)
debugMessage(this->owner->getServerId(), " - {}", entry->channelId());
}
return true;
}
std::deque<ChannelId> ClientChannelView::delete_channel_root(const std::shared_ptr<ts::BasicChannel> &channel){
auto linked = this->find_channel(channel);
if(!linked) return {};
std::deque<ChannelId> result;
for(const auto& channel : this->delete_entry(linked))
result.push_back(channel->channelId());
return result;
}
void ClientChannelView::reset() {
while(this->head)
this->delete_entry(this->head->entry);
}
void ClientChannelView::print() {
debugMessage(this->owner->getServerId(), "{}'s channel tree: ", this->owner->getDisplayName());
this->print_tree([&](const std::shared_ptr<TreeEntry>& entry, int deep) {
string prefix;
while(deep > 0) {
prefix += " ";
deep--;
}
debugMessage(this->owner->getServerId(), "{} - {} ({})", prefix, entry->channelId(), entry->previousChannelId());
});
}