// Copyright 2007-2022 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 . #include "Channel.h" #include "ACL.h" #include "Group.h" #include "User.h" #include #ifdef MUMBLE # include "PluginManager.h" # include "Global.h" QHash< int, Channel * > Channel::c_qhChannels; QReadWriteLock Channel::c_qrwlChannels; #endif Channel::Channel(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; bFiltered = false; hasEnterRestrictions.store(false); localUserCanEnter.store(true); #endif } 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(int id) { QReadLocker lock(&c_qrwlChannels); return c_qhChannels.value(id); } Channel *Channel::add(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); } #endif 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 ? 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; }