#include #include #include #include #include "misc/rnd.h" #include "src/VirtualServer.h" #include "src/client/ConnectedClient.h" #include "src/InstanceHandler.h" #include "../manager/ConversationManager.h" using namespace std; using namespace ts; using namespace ts::server; ServerChannel::ServerChannel(uint32_t rtc_channel_id, ChannelId parentId, ChannelId channelId) : BasicChannel(parentId, channelId), rtc_channel_id{rtc_channel_id} { memtrack::allocated(this); } ServerChannel::~ServerChannel() { memtrack::freed(this); } void ServerChannel::register_client(const std::shared_ptr &client) { unique_lock lock(this->client_lock); for(const auto& _client : this->clients) { if(_client.lock() == client) { return; } } this->clients.push_back(client); } void ServerChannel::unregister_client(const std::shared_ptr &client) { unique_lock lock(this->client_lock); this->clients.erase(remove_if(this->clients.begin(), this->clients.end(), [client](const auto& weak) { auto locked = weak.lock(); if(!locked || client == locked) return true; return false; }), this->clients.end()); } size_t ServerChannel::client_count() { shared_lock lock(this->client_lock); size_t result = 0; for(const auto& weak_entry : this->clients) { if(auto entry = weak_entry.lock()) { auto current_channel = entry->getChannel(); if(current_channel && current_channel.get() == this) result++; } } return result; } void ServerChannel::setProperties(const std::shared_ptr &ptr) { BasicChannel::setProperties(ptr); } ServerChannelTree::ServerChannelTree(const std::shared_ptr& server, sql::SqlManager* sql) : sql(sql), server_ref(server) { } ServerChannelTree::~ServerChannelTree() { } void ServerChannelTree::deleteSemiPermanentChannels() { loop: for(const auto& ch : this->channels()) { if(ch->channelType() == ChannelType::semipermanent || ch->channelType() == ChannelType::temporary){ //We also delete private channels this->delete_channel_root(ch); goto loop; } } } ChannelId ServerChannelTree::generateChannelId() { ChannelId channelId = 0; auto res = sql::command(this->sql, "SELECT `channelId` FROM `channels` WHERE `serverId` = :sid ORDER BY `channelId` DESC LIMIT 1", variable{":sid", this->getServerId()}).query([](ChannelId* num, int, char** value, char**){ *num = (ChannelId) stoll(value[0]); return 0; }, &channelId); auto pf = LOG_SQL_CMD; pf(res); if(!res) return 0; return channelId + 1; } std::shared_ptr ServerChannelTree::createChannel(ChannelId parentId, ChannelId orderId, const string &name) { std::shared_ptr channel = BasicChannelTree::createChannel(parentId, orderId, name); if(!channel) return channel; auto properties = serverInstance->databaseHelper()->loadChannelProperties(this->server_ref.lock(), channel->channelId()); for(const auto& prop : channel->properties().list_properties()) { if(prop.isModified()) { //Copy the already set properties (*properties)[prop.type()] = prop.value(); } } static_pointer_cast(channel)->setProperties(properties); static_pointer_cast(channel)->setPermissionManager(serverInstance->databaseHelper()->loadChannelPermissions(this->server_ref.lock(), channel->channelId())); channel->properties()[property::CHANNEL_CREATED_AT] = chrono::duration_cast(chrono::system_clock::now().time_since_epoch()).count(); channel->properties()[property::CHANNEL_LAST_LEFT] = chrono::duration_cast(chrono::system_clock::now().time_since_epoch()).count(); auto result = sql::command(this->sql, "INSERT INTO `channels` (`serverId`, `channelId`, `parentId`) VALUES(:sid, :chid, :parent);", variable{":sid", this->getServerId()}, variable{":chid", channel->channelId()}, variable{":parent", channel->parent() ? channel->parent()->channelId() : 0}).execute(); auto pf = LOG_SQL_CMD; pf(result); return channel; } inline std::shared_ptr findChannelByPool(const std::deque>& pool, size_t chId){ for(const auto& elm : pool) if(elm->entry->channelId() == chId) return dynamic_pointer_cast(elm->entry); return nullptr; } inline std::shared_ptr findLinkedChannelByPool(const std::deque>& pool, size_t chId){ for(const auto& elm : pool) if(elm->entry->channelId() == chId) return elm; return nullptr; } ServerId ServerChannelTree::getServerId() { auto s = this->server_ref.lock(); return s ? s->getServerId() : 0UL; } bool ServerChannelTree::initializeTempParents() { auto channelList = this->tmpChannelList; for(const auto& linked_channel : channelList) { auto channel = dynamic_pointer_cast(linked_channel->entry); assert(channel); if(channel->properties().hasProperty(property::CHANNEL_PID) && channel->properties()[property::CHANNEL_PID].as() != 0){ if(!channel->parent()) linked_channel->parent = findLinkedChannelByPool(this->tmpChannelList, channel->properties()[property::CHANNEL_PID]); if(!channel->parent()){ logError(this->getServerId(), "Invalid channel parent (Channel does not exists). Channel id: {} ({}) Missing parent id: {}", channel->channelId(), channel->name(), channel->properties()[property::CHANNEL_PID].as()); logError(this->getServerId(), "Resetting parent"); channel->properties()[property::CHANNEL_PID] = 0; } } } return true; } typedef std::deque> ChannelPool; inline ChannelPool remoteTopChannels(const std::shared_ptr& parent, ChannelPool& pool){ ChannelPool result; for(const auto& elm : pool) if(elm->parent.lock() == parent) result.push_back(elm); for(const auto& elm : result) pool.erase(std::find(pool.begin(), pool.end(), elm)); return result; } inline ChannelPool resolveChannelHeads(ServerId serverId, ChannelPool pool) { ChannelPool result; while(!pool.empty()) { auto element = pool.front(); if(!element) continue; ChannelPool tmp_pool = pool; auto f = find(tmp_pool.begin(), tmp_pool.end(), element); if(f != tmp_pool.end()) tmp_pool.erase(f); while(element->previous) { auto found = find(tmp_pool.begin(), tmp_pool.end(), element->previous); if(found == tmp_pool.end()) { logError(serverId, "Found recursive channel heads! Cutting"); if(element->previous && element->previous->next == element) element->previous->next = nullptr; element->previous = nullptr; break; } tmp_pool.erase(found); element = element->previous; }; //Find the head result.push_back(element); auto last = element; while(element) { auto it = find(pool.begin(), pool.end(), element); if(it == pool.end()) { logError(serverId, "Circular tail. Cutting previus."); if(last != element) { if(element->previous == last) element->previous = nullptr; if(last->next == element) last->next = nullptr; } break; } else pool.erase(it); last = element; element = element->next; } } return result; } inline std::shared_ptr buildChannelTree(ServerId serverId, const std::shared_ptr& parent, ChannelPool& channel_pool){ auto top_channels = remoteTopChannels(parent, channel_pool); if(top_channels.empty()) return nullptr; bool brokenTree = false; for(const auto& linked_channel : top_channels){ auto channel = dynamic_pointer_cast(linked_channel->entry); assert(channel); if(channel->channelOrder() != 0) { if(channel->channelOrder() == channel->channelId()) { brokenTree = true; logError(serverId, "Channel order refers to itself! (Resetting)"); channel->properties()[property::CHANNEL_ORDER] = 0; continue; } auto previous_linked = findLinkedChannelByPool(top_channels, channel->channelOrder()); if(!previous_linked) { brokenTree = true; logError(serverId, "Failed to resolve previous channel for channel {} ({}). Previous channel id: {} (Resetting order)", channel->channelId(), channel->name(), channel->channelOrder()); channel->properties()[property::CHANNEL_ORDER] = 0; continue; } if(previous_linked->next) { if(previous_linked->next != linked_channel) { brokenTree = true; logError(serverId, "Previous channel {} ({}) of channel {} ({}) is already linked with another channel {} ({}).", previous_linked->entry->channelId(), dynamic_pointer_cast(previous_linked->entry)->name(), channel->channelId(), channel->name(), previous_linked->next->entry->channelId(), dynamic_pointer_cast(previous_linked->next->entry)->name() ); logError(serverId, "Inserting channel anyway!"); previous_linked->next->previous = linked_channel; linked_channel->next = previous_linked->next; } } previous_linked->next = linked_channel; linked_channel->previous = previous_linked; } } for(const auto& elm : top_channels){ if(elm->next) { if(elm->next->previous != elm) { brokenTree = true; logError(serverId, "Test 'elm->next->previous != elm' failed! Assigning elm->next->previous to elm!"); elm->next->previous = elm; } } if(elm->previous) { if(elm->previous->next != elm) { brokenTree = true; logError(serverId, "Test 'elm->previous->next != elm' failed! Assigning elm->previous->next to elm!"); elm->previous->next = elm; } } } /* Get the heads and merge them */ auto heads = resolveChannelHeads(serverId, top_channels); if(heads.size() > 1){ brokenTree = true; logError(serverId, "Got multiple channel heads! (Count {})", heads.size()); logError(serverId, "Try to appending them", heads.size()); debugMessage(serverId, "Got head:"); //FIXME print head for(int index = 1; index < heads.size(); index++) { auto tail = heads[0]; while(tail->next) tail = tail->next; tail->next = heads[index]; tail->next->previous = tail; } } /* Testing tree */ if(brokenTree) { auto new_heads = resolveChannelHeads(serverId, top_channels); if(new_heads.size() != 1) { logCritical(serverId, "Failed to merge channel heads (Got {} heads)! Unknown reason!", new_heads.size()); logCritical(serverId, "Dropping a part!"); } } brokenTree = true; if(brokenTree){ auto entry = heads[0]; while(entry) { auto channel = dynamic_pointer_cast(entry->entry); assert(channel); auto evaluated_order_id = entry->previous ? entry->previous->entry->channelId() : 0; if(evaluated_order_id != channel->previousChannelId()) { channel->setPreviousChannelId(evaluated_order_id); debugMessage(serverId, "Fixed order id for channel {} ({}). New previous channel {}", entry->entry->channelId(), channel->name(), channel->channelOrder()); } auto evaluated_parent_id = channel->parent() ? channel->parent()->channelId() : 0; if(evaluated_parent_id != channel->properties()[property::CHANNEL_PID].as()) { debugMessage(serverId, "Fixed parent id for channel {} ({}). New parent channel {}", entry->entry->channelId(), channel->name(), evaluated_parent_id); channel->properties()[property::CHANNEL_PID] = evaluated_parent_id; } entry = entry->next; } } { /* building sub trees */ auto entry = heads[0]; while(entry) { entry->child_head = buildChannelTree(serverId, entry, channel_pool); entry = entry->next; } } return heads[0]; } bool ServerChannelTree::buildChannelTreeFromTemp() { if(this->tmpChannelList.empty()) return true; this->head = buildChannelTree(this->getServerId(), nullptr, this->tmpChannelList); assert(tmpChannelList.empty()); return true; } inline void walk_tree(const std::shared_ptr& parent, std::shared_ptr head) { auto parent_id = parent ? parent->entry->channelId() : 0; std::shared_ptr previous{nullptr}; while(head) { head->entry->setParentChannelId(parent_id); if(head->previous != previous) { logCritical(0, "Detect broken channel tree!"); } else if(previous) { head->entry->setPreviousChannelId(previous->entry->channelId()); } else { head->entry->setPreviousChannelId(0); } if(head->child_head) walk_tree(head, head->child_head); previous = head; head = head->next; } } bool ServerChannelTree::updateOrderIds() { walk_tree(nullptr, this->head); return true; } inline ssize_t count_characters(const std::string& in) { size_t index = 0; size_t count = 0; while(index < in.length()){ count++; auto current = (uint8_t) in[index]; if(current >= 128) { //UTF8 check if(current >= 192 && (current <= 193 || current >= 245)) { return -1; } else if(current >= 194 && current <= 223) { if(in.length() - index <= 1) return -1; else if((uint8_t) in[index + 1] >= 128 && (uint8_t) in[index + 1] <= 191) index += 1; //Valid else return -1; } else if(current >= 224 && current <= 239) { if(in.length() - index <= 2) return -1; else if((uint8_t) in[index + 1] >= 128 && (uint8_t) in[index + 1] <= 191 && (uint8_t) in[index + 2] >= 128 && (uint8_t) in[index + 2] <= 191) index += 2; //Valid else return -1; } else if(current >= 240 && current <= 244) { if(in.length() - index <= 3) return -1; else if((uint8_t) in[index + 1] >= 128 && (uint8_t) in[index + 1] <= 191 && (uint8_t) in[index + 2] >= 128 && (uint8_t) in[index + 2] <= 191 && (uint8_t) in[index + 3] >= 128 && (uint8_t) in[index + 3] <= 191) index += 3; //Valid else return -1; } else { return -1; } } index++; } return count; } bool ServerChannelTree::validateChannelNames() { /* if (count_characters(cmd["channel_name"]) < 1) return {findError("channel_name_inuse"), "Invalid channel name (too short)"}; if (count_characters(cmd["channel_name"]) > 40) return {findError("channel_name_inuse"), "Invalid channel name (too long)"}; */ /* for(const auto &channel : this->channels()){ mainSearch: for(const auto& ref : this->channels(channel->parent(), 1)) if(ref->name() == channel->name() && ref != channel) { logError(lstream << "Duplicated channel name '" << channel->name() << "'. Fixing it by appending '1'" << endl); channel->properties()["channel_name"] = channel->name() + "1"; goto mainSearch; } if(channel->name().length() > 40) { channel->properties()["channel_name"] = channel->name().substr(0, 35) + rnd_string(5); goto mainSearch; } } */ for(const auto &channel : this->channels()){ auto name_length = count_characters(channel->name()); if(name_length > 40) { logError(this->getServerId(), "Channel {} loaded an invalid name from the database (name to long). Cutting channel name"); channel->properties()[property::CHANNEL_NAME] = channel->name().substr(0, 40); //FIXME count UTF8 } else if(name_length < 1) { logError(this->getServerId(), "Channel {} loaded an invalid name from the database (empty name). Resetting channel name"); channel->properties()[property::CHANNEL_NAME] = "undefined"; } } function &)> test_level; test_level = [&](const std::shared_ptr &head) { map> used_names; auto it = head; while(it) { auto channel = dynamic_pointer_cast(it->entry); auto name = channel->name(); if(used_names.count(name) > 0) { auto taken_channel = used_names[name]; assert(taken_channel); size_t index = 1; while(true) { auto _name = name + to_string(index); if(_name.length() > 40) //FIXME count UTF8 _name = _name.substr(_name.length() - 40); if(used_names[_name]) index++; else { name = _name; break; } } channel->properties()[property::CHANNEL_NAME] = name; logError(this->getServerId(), "Channel {} has the same name as channel {}. Name: {}. Changing name to {} by appending an index.", channel->channelId(), taken_channel->channelId(), taken_channel->name(), name ); } used_names[name] = channel; if(it->child_head) test_level(it->child_head); it = it->next; } }; test_level(this->tree_head()); return true; } bool ServerChannelTree::validateChannelIcons() { for(const auto &channel : this->channels()) { auto iconId = (IconId) channel->properties()[property::CHANNEL_ICON_ID]; #if 0 if(iconId != 0 && !serverInstance->getFileServer()->iconExists(this->server.lock(), iconId)) { logMessage(this->getServerId(), "[FILE] Missing channel icon (" + to_string(iconId) + ")."); if(config::server::delete_missing_icon_permissions) { channel->properties()[property::CHANNEL_ICON_ID] = 0; channel->permissions()->set_permission(permission::i_icon_id, {0, 0}, permission::v2::PermissionUpdateType::set_value, permission::v2::PermissionUpdateType::do_nothing); } } #endif } return true; } void ServerChannelTree::loadChannelsFromDatabase() { auto res = sql::command(this->sql, "SELECT `channelId`, `parentId` FROM `channels` WHERE `serverId` = :sid", variable{":sid", this->getServerId()}).query(&ServerChannelTree::loadChannelFromData, this); (LOG_SQL_CMD)(res); if(!res){ logError(this->getServerId(), "Could not load channel tree from database"); return; } logMessage(this->getServerId(), "Loaded {} saved channels. Assembling...", this->tmpChannelList.size()); this->initializeTempParents(); this->buildChannelTreeFromTemp(); this->updateOrderIds(); this->validateChannelNames(); this->validateChannelIcons(); //this->printChannelTree(); } int ServerChannelTree::loadChannelFromData(int argc, char **data, char **column) { ChannelId channelId = 0; ChannelId parentId = 0; int index = 0; try { for(index = 0; index < argc; index++){ if(strcmp(column[index], "channelId") == 0) channelId = static_cast(stoll(data[index])); else if(strcmp(column[index], "parentId") == 0) parentId = static_cast(stoll(data[index])); else logError(this->getServerId(), "ServerChannelTree::loadChannelFromData called with invalid column from sql \"{}\"", column[index]); } } catch (std::exception& ex) { logError(this->getServerId(), "Failed to load channel. Got exception {}. Exception was thrown at parsing row {} with data \"{}\"", ex.what(), column[index], data[index]); return 0; } //assert(type != 0xFF); assert(channelId != 0); if(channelId == 0) return 0; auto server = this->server_ref.lock(); std::shared_ptr channel; if(server) { auto rtc_channel_id = server->rtc_server().create_channel(); channel = std::make_shared(rtc_channel_id, parentId, channelId); } else { channel = std::make_shared(0, parentId, channelId); } static_pointer_cast(channel)->setProperties(serverInstance->databaseHelper()->loadChannelProperties(server, channelId)); static_pointer_cast(channel)->setPermissionManager(serverInstance->databaseHelper()->loadChannelPermissions(server, channel->channelId())); auto entry = make_shared(channel); channel->setLinkedHandle(entry); this->tmpChannelList.push_back(entry); return 0; } deque ServerChannelTree::deleteChannelRoot(const std::shared_ptr &channel) { auto server = this->server_ref.lock(); auto channels = this->delete_channel_root(channel); deque channel_ids; for(const auto& channel : channels) { channel_ids.push_back(channel->channelId()); } return channel_ids; } void ServerChannelTree::on_channel_entry_deleted(const shared_ptr &channel) { BasicChannelTree::on_channel_entry_deleted(channel); auto server_channel = dynamic_pointer_cast(channel); assert(server_channel); auto server = this->server_ref.lock(); if(server) { server->getGroupManager()->handleChannelDeleted(channel->channelId()); server->conversation_manager()->delete_conversation(channel->channelId()); server->rtc_server().destroy_channel(server_channel->rtc_channel_id); } else { serverInstance->getGroupManager()->handleChannelDeleted(channel->channelId()); } auto sql_result = sql::command(this->sql, "DELETE FROM `channels` WHERE `serverId` = '" + to_string(this->getServerId()) + "' AND `channelId` = '" + to_string(channel->channelId()) + "'").execute(); LOG_SQL_CMD(sql_result); sql_result = sql::command(this->sql, "DELETE FROM `properties` WHERE `serverId` = '" + to_string(this->getServerId()) + "' AND `id` = '" + to_string(channel->channelId()) + "' AND `type` = " + to_string(property::PropertyType::PROP_TYPE_CHANNEL)).execute(); LOG_SQL_CMD(sql_result); serverInstance->databaseHelper()->deleteChannelPermissions(this->server_ref.lock(), channel->channelId()); sql_result = sql::command(this->sql, "DELETE FROM `assignedGroups` WHERE `serverId` = '" + to_string(this->getServerId()) + "' AND `channelId` = '" + to_string(channel->channelId()) + "'").execute(); LOG_SQL_CMD(sql_result); } std::shared_ptr ServerChannelTree::allocateChannel(const shared_ptr &parent, ChannelId channelId) { auto server = this->server_ref.lock(); if(server) { auto rtc_channel_id = server->rtc_server().create_channel(); return std::make_shared(rtc_channel_id, parent->channelId(), channelId); } else { return std::make_shared(0, parent->channelId(), channelId); } }