2019-07-17 19:37:18 +02:00
# 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 ) {
2020-01-24 02:57:58 +01:00
memtrack : : allocated < ViewEntry > ( this ) ;
2019-07-17 19:37:18 +02:00
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 ( ) {
2020-01-24 02:57:58 +01:00
memtrack : : freed < ViewEntry > ( this ) ;
2019-07-17 19:37:18 +02:00
}
ChannelId ts : : ViewEntry : : channelId ( ) const {
2020-01-24 02:57:58 +01:00
if ( this - > cached_channel_id ! = 0 ) return this - > cached_channel_id ; //We've already got a channel id
2019-07-17 19:37:18 +02:00
2020-01-24 02:57:58 +01:00
//TODO cached_channel_id should be > 0 every time?
2019-07-17 19:37:18 +02:00
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 {
2020-01-24 02:57:58 +01:00
if ( this - > cached_parent_id ! = 0 ) return this - > cached_parent_id ;
2019-07-17 19:37:18 +02:00
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 ) ;
2020-01-24 02:57:58 +01:00
this - > cached_parent_id = id ;
2019-07-17 19:37:18 +02:00
}
ClientChannelView : : ClientChannelView ( server : : ConnectedClient * handle ) : owner ( handle ) {
2020-01-24 02:57:58 +01:00
memtrack : : allocated < ClientChannelView > ( this ) ;
2019-07-17 19:37:18 +02:00
}
ClientChannelView : : ~ ClientChannelView ( ) {
2020-01-24 02:57:58 +01:00
memtrack : : freed < ClientChannelView > ( this ) ;
2019-07-17 19:37:18 +02:00
}
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 ) ) {
2020-01-24 02:57:58 +01:00
auto channel = dynamic_pointer_cast < ViewEntry > ( entry ) - > handle . lock ( ) ;
if ( channel )
result . push_back ( channel ) ;
2019-07-17 19:37:18 +02:00
}
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 ) {
2020-01-24 02:57:58 +01:00
if ( ! channel ) return nullptr ;
2019-07-17 19:37:18 +02:00
2020-01-24 02:57:58 +01:00
deque < shared_ptr < BasicChannel > > heads { channel } ;
shared_ptr < LinkedTreeEntry > head = nullptr ;
bool deep_search = false ;
2019-07-17 19:37:18 +02:00
2020-01-24 02:57:58 +01:00
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 ) ;
}
2019-07-17 19:37:18 +02:00
2020-01-24 02:57:58 +01:00
if ( ! deep_search ) {
heads . pop_front ( ) ;
2019-07-17 19:37:18 +02:00
2020-01-24 02:57:58 +01:00
while ( heads . size ( ) > 1 ) {
auto front = move ( heads . front ( ) ) ;
heads . pop_front ( ) ;
2019-07-17 19:37:18 +02:00
2020-01-24 02:57:58 +01:00
head = this - > find_linked_entry ( front - > channelId ( ) , head , 1 ) ;
if ( ! head ) return nullptr ; //Channel tree not visible!
}
2019-07-17 19:37:18 +02:00
2020-01-24 02:57:58 +01:00
head = this - > find_linked_entry ( channel - > channelId ( ) , head , 1 ) ;
}
2019-07-17 19:37:18 +02:00
2020-01-24 02:57:58 +01:00
return head ? static_pointer_cast < ViewEntry > ( head - > entry ) : nullptr ;
2019-07-17 19:37:18 +02:00
}
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 ) {
2020-01-24 02:57:58 +01:00
if ( ! first & & first_only ) break ;
first = false ;
2019-07-17 19:37:18 +02:00
auto channel = dynamic_pointer_cast < BasicChannel > ( head - > entry ) ;
if ( this - > channel_visible ( channel ) ) {
2020-01-24 02:57:58 +01:00
if ( head - > child_head ) {
for ( const auto & sub : this - > insert_channels ( head - > child_head , test_permissions , false , cache ) )
result . push_back ( sub ) ;
}
2019-07-17 19:37:18 +02:00
head = head - > next ;
continue ;
}
if ( ! has_perm ) {
2020-01-24 02:57:58 +01:00
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 ;
}
2019-07-17 19:37:18 +02:00
}
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 ;
} ;
2020-01-24 02:57:58 +01:00
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 ( ) : " "
) ;
2019-07-17 19:37:18 +02:00
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 ;
}
2020-01-24 02:57:58 +01:00
auto previous_channel = previous ? previous - > channel ( ) : nullptr ; //weak could be may nullptr
2019-07-17 19:37:18 +02:00
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 ) {
2020-01-24 02:57:58 +01:00
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 ) {
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 ;
2019-07-17 19:37:18 +02:00
}
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 ) {
2020-01-24 02:57:58 +01:00
std : : deque < std : : pair < ClientChannelView : : ChannelAction , std : : shared_ptr < ViewEntry > > > result ;
2019-07-17 19:37:18 +02:00
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 ) {
2020-01-24 02:57:58 +01:00
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 ) ;
2019-07-17 19:37:18 +02:00
}
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 ) )
2020-01-24 02:57:58 +01:00
result . push_back ( { ClientChannelView : : ENTER_VIEW , shown } ) ;
2019-07-17 19:37:18 +02:00
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 ) {
2020-01-24 02:57:58 +01:00
auto linked = this - > find_channel ( channel ) ;
if ( ! linked ) return { } ;
2019-07-17 19:37:18 +02:00
2020-01-24 02:57:58 +01:00
std : : deque < ChannelId > result ;
for ( const auto & channel : this - > delete_entry ( linked ) )
result . push_back ( channel - > channelId ( ) ) ;
return result ;
2019-07-17 19:37:18 +02:00
}
void ClientChannelView : : reset ( ) {
2020-01-24 02:57:58 +01:00
while ( this - > head )
2019-07-17 19:37:18 +02:00
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 ( ) ) ;
} ) ;
}