#include #include #include #include "src/client/ConnectedClient.h" #include "ClientChannelView.h" using namespace std; using namespace ts; using namespace ts::server; ViewEntry::ViewEntry(const std::shared_ptr &handle, bool editable) : handle(handle), editable(editable) { memtrack::allocated(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(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(this); } ClientChannelView::~ClientChannelView() { memtrack::freed(this); } ServerId ClientChannelView::getServerId() { return owner ? owner->getServerId() : 0; } std::deque> ClientChannelView::channels(const std::shared_ptr &head, int deep) { std::deque> result; for(const auto& entry : this->entries(head ? make_shared(head) : nullptr, deep)) { auto channel = dynamic_pointer_cast(entry)->handle.lock(); if(channel) result.push_back(channel); } return result; } bool ClientChannelView::channel_visible(const std::shared_ptr &channel, const std::shared_ptr &head, int deep) { if(!channel) return true; //I thing the void is kind of visible :D return this->has_entry(make_shared(channel), head ? make_shared(head) : nullptr, deep); } std::shared_ptr ClientChannelView::find_channel(ts::ChannelId id) { return dynamic_pointer_cast(this->find_entry(id)); } std::shared_ptr ClientChannelView::find_channel(const std::shared_ptr &channel) { if(!channel) return nullptr; deque> heads{channel}; shared_ptr head = nullptr; bool deep_search = false; while(heads.front()) { auto parent = heads.front()->parent(); if(!parent && heads.front()->properties()[property::CHANNEL_PID].as() != 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(head->entry) : nullptr; } std::deque> ClientChannelView::insert_channels(shared_ptr head, bool test_permissions, bool first_only, std::shared_ptr cache) { std::deque> result; if(!cache && test_permissions) cache = make_shared(); 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(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(dynamic_pointer_cast(head->entry), true); std::shared_ptr parent, previous; if(head->parent.lock()) { auto remote_parent = head->parent.lock(); parent = dynamic_pointer_cast(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(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> ClientChannelView::show_channel(std::shared_ptr l_channel, bool& success) { success = true; if(this->channel_visible(dynamic_pointer_cast(l_channel->entry))) return {}; std::deque> result; deque> parents = {l_channel}; while(parents.front()) { auto parent = parents.front()->parent.lock(); if(parent && !this->channel_visible(dynamic_pointer_cast(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(root_channel->entry); auto entry = make_shared(channel, true); std::shared_ptr parent, previous; if(root_channel->parent.lock()) { auto remote_parent = root_channel->parent.lock(); parent = dynamic_pointer_cast(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(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> ClientChannelView::test_channel(std::shared_ptr l_old, std::shared_ptr channel_new, shared_ptr cache) { if(!cache) cache = make_shared(); std::deque> result; bool has_perm = owner->permissionGranted(permission::PERMTEST_ORDERED, permission::b_channel_ignore_view_power, 1, nullptr, true, cache); if(has_perm) return {}; deque> 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(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(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>> ClientChannelView::update_channel( std::shared_ptr l_channel, std::shared_ptr l_own, shared_ptr cache) { return update_channel_path(std::move(l_channel), std::move(l_own), std::move(cache), 1); } std::deque>> ClientChannelView::update_channel_path(std::shared_ptr l_channel, std::shared_ptr l_own, shared_ptr cache, ssize_t length) { if(!cache) cache = make_shared(); std::deque>> 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(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(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>> ClientChannelView::change_order(const shared_ptr &channel, const std::shared_ptr &parent, const shared_ptr &head) { std::deque>> 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(channel->entry)->permission_granted(permission::i_channel_needed_view_power, this->owner->calculate_permission_value(permission::i_channel_view_power, dynamic_pointer_cast(channel->entry)->channelId()), false); } if(!has_perm) return {}; //Channel wasn't visible and he still has no permission for that :) std::shared_ptr previous; auto remote_previous = head; //Get our first thing while(remote_previous && !(previous = dynamic_pointer_cast(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(entry)}); return result; } //We have just to readjust the order or the parent std::shared_ptr previous; auto remote_previous = head; //Get our first thing while(remote_previous && !(previous = dynamic_pointer_cast(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(l_entry->entry)}}; } std::shared_ptr ClientChannelView::add_channel(const std::shared_ptr& 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 previous; auto remote_previous = l_channel->previous; //Get our first thing while(remote_previous && !(previous = dynamic_pointer_cast(this->find_entry(remote_previous->entry->channelId())))) { remote_previous = remote_previous->previous; } auto entry = make_shared(dynamic_pointer_cast(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 ClientChannelView::delete_channel_root(const std::shared_ptr &channel){ auto linked = this->find_channel(channel); if(!linked) return {}; std::deque 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& entry, int deep) { string prefix; while(deep > 0) { prefix += " "; deep--; } debugMessage(this->owner->getServerId(), "{} - {} ({})", prefix, entry->channelId(), entry->previousChannelId()); }); }