mumble-voip_mumble/src/Channel.cpp

320 lines
6.7 KiB
C++

// Copyright 2007-2023 The Mumble Developers. All rights reserved.
// Use of this source code is governed by a BSD-style license
// that can be found in the LICENSE file at the root of the
// Mumble source tree or at <https://www.mumble.info/LICENSE>.
#include "Channel.h"
#include "ACL.h"
#include "Group.h"
#include "User.h"
#include <QtCore/QStack>
#ifdef MUMBLE
# include <queue>
# include "PluginManager.h"
# include "Global.h"
# include "Database.h"
# include "ServerHandler.h"
QHash< unsigned int, Channel * > Channel::c_qhChannels;
QReadWriteLock Channel::c_qrwlChannels;
#endif
Channel::Channel(unsigned int id, const QString &name, QObject *p) : QObject(p) {
iId = id;
iPosition = 0;
qsName = name;
bInheritACL = true;
uiMaxUsers = 0;
bTemporary = false;
cParent = qobject_cast< Channel * >(p);
if (cParent)
cParent->addChannel(this);
#ifdef MUMBLE
uiPermissions = 0;
m_filterMode = ChannelFilterMode::NORMAL;
hasEnterRestrictions.store(false);
localUserCanEnter.store(true);
#endif // MUMBLE
}
Channel::~Channel() {
if (cParent)
cParent->removeChannel(this);
foreach (Channel *c, qlChannels)
delete c;
foreach (ChanACL *acl, qlACL)
delete acl;
foreach (Group *g, qhGroups)
delete g;
foreach (Channel *l, qhLinks.keys())
unlink(l);
Q_ASSERT(qlChannels.count() == 0);
Q_ASSERT(children().count() == 0);
}
#ifdef MUMBLE
Channel *Channel::get(unsigned int id) {
QReadLocker lock(&c_qrwlChannels);
return c_qhChannels.value(id);
}
Channel *Channel::add(unsigned int id, const QString &name) {
QWriteLocker lock(&c_qrwlChannels);
if (c_qhChannels.contains(id))
return nullptr;
Channel *c = new Channel(id, name, nullptr);
c_qhChannels.insert(id, c);
// We have to use a direct connection here in order to make sure that the user object that gets passed to the
// callback does not get invalidated or deleted while the callback is running.
QObject::connect(c, &Channel::channelEntered, Global::get().pluginManager, &PluginManager::on_channelEntered,
Qt::DirectConnection);
QObject::connect(c, &Channel::channelExited, Global::get().pluginManager, &PluginManager::on_channelExited,
Qt::DirectConnection);
return c;
}
void Channel::remove(Channel *c) {
QWriteLocker lock(&c_qrwlChannels);
c_qhChannels.remove(c->iId);
}
void Channel::setFilterMode(ChannelFilterMode filterMode) {
m_filterMode = filterMode;
ServerHandlerPtr sh = Global::get().sh;
if (sh) {
Global::get().db->setChannelFilterMode(sh->qbaDigest, iId, m_filterMode);
}
}
void Channel::clearFilterMode() {
if (m_filterMode != ChannelFilterMode::NORMAL) {
setFilterMode(ChannelFilterMode::NORMAL);
}
}
bool Channel::isFiltered() const {
if (!Global::get().s.bFilterActive) {
// Channel filtering is disabled
return false;
}
const Channel *userChannel = nullptr;
if (Global::get().uiSession != 0) {
const ClientUser *user = ClientUser::get(Global::get().uiSession);
if (user) {
userChannel = user->cChannel;
}
}
bool hasUser = false;
bool hasHide = false;
bool hasPin = false;
// Iterate tree down
std::queue< const Channel * > channelSearch;
channelSearch.push(this);
while (!channelSearch.empty()) {
const Channel *c = channelSearch.front();
channelSearch.pop();
// Never hide channel, if user resides in this channel or a subchannel
if (userChannel && c == userChannel) {
return false;
}
if (c->m_filterMode == ChannelFilterMode::PIN) {
hasPin = true;
}
if (!c->qlUsers.isEmpty()) {
hasUser = true;
}
for (const Channel *currentSubChannel : c->qlChannels) {
channelSearch.push(currentSubChannel);
}
}
// Iterate tree up
const Channel *c = this;
while (c) {
if (c->m_filterMode == ChannelFilterMode::HIDE) {
hasHide = true;
break;
}
c = c->cParent;
}
if (hasPin) {
return false;
}
if (hasHide) {
return true;
}
return Global::get().s.bFilterHidesEmptyChannels && !hasUser;
}
#endif // MUMBLE
bool Channel::lessThan(const Channel *first, const Channel *second) {
if ((first->iPosition != second->iPosition) && (first->cParent == second->cParent))
return first->iPosition < second->iPosition;
else
return QString::localeAwareCompare(first->qsName, second->qsName) < 0;
}
bool Channel::isLinked(Channel *l) const {
return ((l == this) || qhLinks.contains(l));
}
void Channel::link(Channel *l) {
if (qsPermLinks.contains(l))
return;
qsPermLinks.insert(l);
qhLinks[l]++;
l->qsPermLinks.insert(this);
l->qhLinks[this]++;
}
void Channel::unlink(Channel *l) {
if (l) {
qsPermLinks.remove(l);
qhLinks.remove(l);
l->qsPermLinks.remove(this);
l->qhLinks.remove(this);
} else {
foreach (Channel *c, qhLinks.keys())
unlink(c);
}
}
QSet< Channel * > Channel::allLinks() {
QSet< Channel * > seen;
seen.insert(this);
if (qhLinks.isEmpty())
return seen;
QStack< Channel * > stack;
stack.push(this);
while (!stack.isEmpty()) {
Channel *lnk = stack.pop();
foreach (Channel *l, lnk->qhLinks.keys()) {
if (!seen.contains(l)) {
seen.insert(l);
stack.push(l);
}
}
}
return seen;
}
QSet< Channel * > Channel::allChildren() {
QSet< Channel * > seen;
if (!qlChannels.isEmpty()) {
QStack< Channel * > stack;
stack.push(this);
while (!stack.isEmpty()) {
Channel *c = stack.pop();
foreach (Channel *chld, c->qlChannels) {
seen.insert(chld);
if (!chld->qlChannels.isEmpty())
stack.append(chld);
}
}
}
return seen;
}
void Channel::addChannel(Channel *c) {
c->cParent = this;
c->setParent(this);
qlChannels << c;
}
void Channel::removeChannel(Channel *c) {
c->cParent = nullptr;
c->setParent(nullptr);
qlChannels.removeAll(c);
}
void Channel::addUser(User *p) {
Channel *prevChannel = p->cChannel;
if (prevChannel)
prevChannel->removeUser(p);
p->cChannel = this;
qlUsers << p;
emit channelEntered(this, prevChannel, p);
}
void Channel::removeUser(User *p) {
qlUsers.removeAll(p);
emit channelExited(this, p);
}
Channel::operator QString() const {
return QString::fromLatin1("%1[%2:%3%4]")
.arg(qsName, QString::number(iId), QString::number(cParent ? static_cast< int >(cParent->iId) : -1),
bTemporary ? QLatin1String("*") : QLatin1String(""));
}
size_t Channel::getLevel() const {
size_t i = 0;
const Channel *c = this;
while (c->cParent) {
c = c->cParent;
++i;
}
return i;
}
size_t Channel::getDepth() const {
if (qlChannels.empty()) {
return 0;
}
size_t result = 0;
foreach (Channel *child, qlChannels) { result = qMax(result, child->getDepth() + 1); }
return result;
}
QString Channel::getPath() const {
QString out;
const Channel *tmp = this;
while (tmp->cParent) {
// Skip the root channel.
if (tmp->iId == 0) {
break;
}
out.prepend(QString::fromLatin1("/"));
out.prepend(tmp->qsName);
tmp = tmp->cParent;
}
return out;
}