diff --git a/src/ACL.cpp b/src/ACL.cpp index 08df8e2c7..1a5e031c5 100644 --- a/src/ACL.cpp +++ b/src/ACL.cpp @@ -174,6 +174,9 @@ QString ChanACL::whatsThis(Perm p) { case MakeChannel: return tr("This represents the permission to make sub-channels. The user making the sub-channel will be added to the " "admin group of the sub-channel."); + case MakeTempChannel: + return tr("This represents the permission to make a temporary subchannel. The user making the sub-channel will be added to the " + "admin group of the sub-channel. Temporary channels are not stored and disappear when the last user leaves."); case LinkChannel: return tr("This represents the permission to link channels. Users in linked channels hear each other, as long as " "the speaking user has the speak privilege in the channel of the listener. You need the link " @@ -223,6 +226,8 @@ QString ChanACL::permName(Perm p) { return tr("Move"); case MakeChannel: return tr("Make channel"); + case MakeTempChannel: + return tr("Make temporary channel"); case LinkChannel: return tr("Link channel"); case TextMessage: diff --git a/src/ACL.h b/src/ACL.h index 8e0a6a77e..6fe9d3d14 100644 --- a/src/ACL.h +++ b/src/ACL.h @@ -54,6 +54,7 @@ class ChanACL : public QObject { LinkChannel = 0x80, Whisper = 0x100, TextMessage = 0x200, + MakeTempChannel = 0x400, // Root channel only Kick = 0x10000, diff --git a/src/Channel.cpp b/src/Channel.cpp index 27e6118f2..2ec89605e 100644 --- a/src/Channel.cpp +++ b/src/Channel.cpp @@ -42,6 +42,7 @@ Channel::Channel(int id, const QString &name, QObject *p) : QObject(p) { iId = id; qsName = name; bInheritACL = true; + bTemporary = false; cParent = qobject_cast(p); if (cParent) cParent->addChannel(this); @@ -177,5 +178,5 @@ void Channel::removeUser(User *p) { } Channel::operator const QString() const { - return QString::fromLatin1("%1[%2:%3]").arg(qsName).arg(iId).arg(cParent ? cParent->iId : -1); + return QString::fromLatin1("%1[%2:%3%4]").arg(qsName).arg(iId).arg(cParent ? cParent->iId : -1).arg(bTemporary ? QLatin1String("*") : QLatin1String("")); } diff --git a/src/Channel.h b/src/Channel.h index caa40570b..87c7c452c 100644 --- a/src/Channel.h +++ b/src/Channel.h @@ -47,6 +47,7 @@ class Channel : public QObject { QSet qsUnseen; public: int iId; + bool bTemporary; Channel *cParent; QString qsName; QString qsDesc; diff --git a/src/Mumble.proto b/src/Mumble.proto index 72c3ae224..0747e4f10 100644 --- a/src/Mumble.proto +++ b/src/Mumble.proto @@ -66,6 +66,7 @@ message ChannelState { optional string description = 5; repeated uint32 links_add = 6; repeated uint32 links_remove = 7; + optional bool temporary = 8 [default = false]; } message UserRemove { diff --git a/src/mumble/Messages.cpp b/src/mumble/Messages.cpp index 5f0d5488a..9390a3919 100644 --- a/src/mumble/Messages.cpp +++ b/src/mumble/Messages.cpp @@ -321,6 +321,7 @@ void MainWindow::msgChannelState(const MumbleProto::ChannelState &msg) { if (!c) { if (msg.has_parent() && p && msg.has_name()) { c = pmModel->addChannel(msg.channel_id(), p, u8(msg.name())); + c->bTemporary = msg.temporary(); p = NULL; } else { return; diff --git a/src/murmur/DBus.cpp b/src/murmur/DBus.cpp index 2f741b57f..1c047240f 100644 --- a/src/murmur/DBus.cpp +++ b/src/murmur/DBus.cpp @@ -412,7 +412,7 @@ void MurmurDBus::removeChannel(int id, const QDBusMessage &msg) { qdbc.send(msg.createErrorReply("net.sourceforge.mumble.Error.channel", "Invalid channel id")); return; } - server->removeChannel(cChannel, NULL); + server->removeChannel(cChannel); } void MurmurDBus::getChannelState(int id, const QDBusMessage &msg, ChannelInfo &state) { diff --git a/src/murmur/Messages.cpp b/src/murmur/Messages.cpp index c26e6bae7..3a8041e50 100644 --- a/src/murmur/Messages.cpp +++ b/src/murmur/Messages.cpp @@ -598,12 +598,23 @@ void Server::msgChannelState(ServerUser *uSource, MumbleProto::ChannelState &msg if (! c) { if (! p || qsName.isNull()) return; - if (! hasPermission(uSource, p, ChanACL::MakeChannel)) { - PERM_DENIED(uSource, p, ChanACL::MakeChannel); + + ChanACL::Perm perm = msg.temporary() ? ChanACL::MakeTempChannel : ChanACL::MakeChannel; + if (! hasPermission(uSource, p, perm)) { + PERM_DENIED(uSource, p, perm); return; } + + Channel *tmp = p; + while (tmp) { + if (tmp->bTemporary) { + PERM_DENIED(uSource, tmp, perm); + return; + } + tmp = tmp->cParent; + } - c = addChannel(p, qsName); + c = addChannel(p, qsName, msg.temporary()); c->qsDesc = qsDesc; if (uSource->iId >= 0) { Group *g = new Group(c, "admin"); @@ -614,6 +625,15 @@ void Server::msgChannelState(ServerUser *uSource, MumbleProto::ChannelState &msg msg.set_channel_id(c->iId); log(uSource, QString("Added channel %1 under %2").arg(*c).arg(*p)); emit channelCreated(c); + sendAll(msg); + + if (c->bTemporary) { + MumbleProto::UserState mpus; + mpus.set_session(uSource->uiSession); + mpus.set_channel_id(c->iId); + sendAll(mpus); + userEnterChannel(uSource, c); + } } else { if (! qsName.isNull()) { if (! hasPermission(uSource, c, ChanACL::Write) || (c->iId == 0)) { @@ -713,9 +733,8 @@ void Server::msgChannelState(ServerUser *uSource, MumbleProto::ChannelState &msg updateChannel(c); emit channelStateChanged(c); + sendAll(msg); } - - sendAll(msg); } void Server::msgChannelRemove(ServerUser *uSource, MumbleProto::ChannelRemove &msg) { @@ -732,7 +751,7 @@ void Server::msgChannelRemove(ServerUser *uSource, MumbleProto::ChannelRemove &m log(uSource, QString("Removed channel %1").arg(*c)); - removeChannel(c, uSource); + removeChannel(c); } void Server::msgTextMessage(ServerUser *uSource, MumbleProto::TextMessage &msg) { diff --git a/src/murmur/Murmur.ice b/src/murmur/Murmur.ice index c48106b3f..c73078aad 100644 --- a/src/murmur/Murmur.ice +++ b/src/murmur/Murmur.ice @@ -75,6 +75,8 @@ module Murmur IntList links; /** Description of channel. Shown as tooltip for this channel. */ string description; + /** Channel is temporary, and will be removed when the last user leaves it. */ + bool temporary; }; /** A group. Groups are defined per channel, and can inherit members from parent channels. @@ -108,14 +110,23 @@ module Murmur const int PermissionWhisper = 0x100; /** Mute and deafen other users in this channel. */ const int PermissionMuteDeafen = 0x10; - /** Move and Kick users from channel. You need this permission in both the source and destination channel to move another user. - * If you have this privilege on the root channel, you can ban users. - */ - const int PermissionMoveKick = 0x20; + /** Move users from channel. You need this permission in both the source and destination channel to move another user. */ + const int PermissionMove = 0x20; /** Make new channel as a subchannel of this channel. */ const int PermissionMakeChannel = 0x40; + /** Make new temporary channel as a subchannel of this channel. */ + const int PermissionMakeTempChannel = 0x400; /** Link this channel. You need this permission in both the source and destination channel to link channels, or in either channel to unlink them. */ const int PermissionLinkChannel = 0x80; + /** Send text message to channel. */ + const int PermissionTextMessage = 0x200; + /** Kick user from server. Only valid on root channel. */ + const int PermissionKick = 0x10000; + /** Ban user from server. Only valid on root channel. */ + const int PermissionBan = 0x20000; + /** Register and unregister users. Only valid on root channel. */ + const int PermissionRegister = 0x40000; + /** Access Control List for a channel. ACLs are defined per channel, and can be inherited from parent channels. **/ diff --git a/src/murmur/MurmurIce.cpp b/src/murmur/MurmurIce.cpp index 5f0712280..819dc2f43 100644 --- a/src/murmur/MurmurIce.cpp +++ b/src/murmur/MurmurIce.cpp @@ -114,6 +114,7 @@ static void channelToChannel(const ::Channel *c, Murmur::Channel &mc) { mc.description = u8(c->qsDesc); foreach(::Channel *chn, c->qsPermLinks) mc.links.push_back(chn->iId); + mc.temporary = c->bTemporary; } static void ACLtoACL(const ::ChanACL *acl, Murmur::ACL &ma) { @@ -1014,7 +1015,7 @@ static void impl_Server_removeChannel(const ::Murmur::AMD_Server_removeChannelPt if (!channel->cParent) { cb->ice_exception(::Murmur::InvalidChannelException()); } else { - server->removeChannel(channel, NULL); + server->removeChannel(channel); cb->ice_response(); } } diff --git a/src/murmur/Server.cpp b/src/murmur/Server.cpp index 5a4f80349..13d944720 100644 --- a/src/murmur/Server.cpp +++ b/src/murmur/Server.cpp @@ -1015,7 +1015,7 @@ void Server::sendProtoExcept(ServerUser *u, const ::google::protobuf::Message &m usr->sendMessage(msg, msgType, cache); } -void Server::removeChannel(Channel *chan, User *src, Channel *dest) { +void Server::removeChannel(Channel *chan, Channel *dest) { Channel *c; User *p; @@ -1025,7 +1025,7 @@ void Server::removeChannel(Channel *chan, User *src, Channel *dest) { chan->unlink(NULL); foreach(c, chan->qlChannels) { - removeChannel(c, src, dest); + removeChannel(c, dest); } foreach(p, chan->qlUsers) { @@ -1036,7 +1036,7 @@ void Server::removeChannel(Channel *chan, User *src, Channel *dest) { mpus.set_channel_id(dest->iId); sendAll(mpus); - userEnterChannel(p, dest); + userEnterChannel(p, dest, true); } MumbleProto::ChannelRemove mpcr; @@ -1054,7 +1054,7 @@ void Server::removeChannel(Channel *chan, User *src, Channel *dest) { delete chan; } -void Server::userEnterChannel(User *p, Channel *c, bool quiet) { +void Server::userEnterChannel(User *p, Channel *c, bool quiet, bool ignoretemp) { clearACLCache(p); if (quiet && (p->cChannel == c)) @@ -1067,7 +1067,9 @@ void Server::userEnterChannel(User *p, Channel *c, bool quiet) { if (quiet) return; - + + Channel *old = p->cChannel; + setLastChannel(p); bool mayspeak = hasPermission(static_cast(p), c, ChanACL::Speak); @@ -1085,6 +1087,10 @@ void Server::userEnterChannel(User *p, Channel *c, bool quiet) { } } emit userStateChanged(p); + + if (old->bTemporary && old->qlUsers.isEmpty() && ! ignoretemp) { + removeChannel(old); + } } bool Server::hasPermission(ServerUser *p, Channel *c, QFlags perm) { diff --git a/src/murmur/Server.h b/src/murmur/Server.h index b9fbb9537..cacae5154 100644 --- a/src/murmur/Server.h +++ b/src/murmur/Server.h @@ -268,8 +268,8 @@ class Server : public QThread { void log(const QString &); void log(ServerUser *u, const QString &); - void removeChannel(Channel *c, User *src, Channel *dest = NULL); - void userEnterChannel(User *u, Channel *c, bool quiet = false); + void removeChannel(Channel *c, Channel *dest = NULL); + void userEnterChannel(User *u, Channel *c, bool quiet = false, bool ignoretemp = false); Server(int snum, QObject *parent = NULL); ~Server(); @@ -308,7 +308,7 @@ class Server : public QThread { // Database / DBus functions. Implementation in ServerDB.cpp void initialize(); int authenticate(QString &name, const QString &pw, const QStringList &emails = QStringList(), const QString &certhash = QString(), bool bStrongCert = false); - Channel *addChannel(Channel *c, const QString &name); + Channel *addChannel(Channel *c, const QString &name, bool temporary = false); void removeChannel(const Channel *c); void readChannels(Channel *p = NULL); void readLinks(); diff --git a/src/murmur/ServerDB.cpp b/src/murmur/ServerDB.cpp index ba4224d82..940b7db49 100644 --- a/src/murmur/ServerDB.cpp +++ b/src/murmur/ServerDB.cpp @@ -337,7 +337,7 @@ ServerDB::ServerDB() { SQLDO("INSERT INTO `%1user_info` SELECT `server_id`,`player_id`,'email',email FROM `%1players_old` WHERE `email` IS NOT NULL"); if (version == 3) { - SQLDO("INSERT INTO `%1channel_info` SELECT `server_id`,`channel_id`,'description',description FROM `%1channels_old` WHERE `description` IS NOT NULL"); + SQLDO("INSERT INTO `%1channel_info` SELECT `server_id`,`channel_id`,'description',`description` FROM `%1channels_old` WHERE `description` IS NOT NULL"); } qWarning("Removing old tables..."); @@ -999,6 +999,8 @@ QByteArray Server::getUserTexture(int id) { } void Server::addLink(Channel *c, Channel *l) { + if (c->bTemporary || l->bTemporary) + return; TransactionHolder th; QSqlQuery &query = *th.qsqQuery; @@ -1017,6 +1019,8 @@ void Server::addLink(Channel *c, Channel *l) { } void Server::removeLink(Channel *c, Channel *l) { + if (c->bTemporary || l->bTemporary) + return; TransactionHolder th; QSqlQuery &query = *th.qsqQuery; @@ -1045,7 +1049,7 @@ void Server::removeLink(Channel *c, Channel *l) { } } -Channel *Server::addChannel(Channel *p, const QString &name) { +Channel *Server::addChannel(Channel *p, const QString &name, bool temporary) { TransactionHolder th; QSqlQuery &query = *th.qsqQuery; @@ -1056,31 +1060,41 @@ Channel *Server::addChannel(Channel *p, const QString &name) { int id = 0; if (query.next()) id = query.value(0).toInt(); - - - SQLPREP("INSERT INTO `%1channels` (`server_id`, `parent_id`, `channel_id`, `name`) VALUES (?,?,?,?)"); - query.addBindValue(iServerNum); - query.addBindValue(p->iId); - query.addBindValue(id); - query.addBindValue(name); - SQLEXEC(); + + // Temporary channels might "complicate" this somewhat. + while (qhChannels.contains(id)) + ++id; + + if (! temporary) { + SQLPREP("INSERT INTO `%1channels` (`server_id`, `parent_id`, `channel_id`, `name`) VALUES (?,?,?,?)"); + query.addBindValue(iServerNum); + query.addBindValue(p->iId); + query.addBindValue(id); + query.addBindValue(name); + SQLEXEC(); + } Channel *c = new Channel(id, name, p); + c->bTemporary = temporary; qhChannels.insert(id, c); return c; } void Server::removeChannel(const Channel *c) { - TransactionHolder th; + if (! c->bTemporary) { + TransactionHolder th; - QSqlQuery &query = *th.qsqQuery; - SQLPREP("DELETE FROM `%1channels` WHERE `server_id` = ? AND `channel_id` = ?"); - query.addBindValue(iServerNum); - query.addBindValue(c->iId); - SQLEXEC(); + QSqlQuery &query = *th.qsqQuery; + SQLPREP("DELETE FROM `%1channels` WHERE `server_id` = ? AND `channel_id` = ?"); + query.addBindValue(iServerNum); + query.addBindValue(c->iId); + SQLEXEC(); + } qhChannels.remove(c->iId); } void Server::updateChannel(const Channel *c) { + if (c->bTemporary) + return; TransactionHolder th; Group *g; ChanACL *acl; @@ -1274,9 +1288,11 @@ void Server::readLinks() { } void Server::setLastChannel(const User *p) { - if (p->iId < 0) return; + + if (p->cChannel->bTemporary) + return; TransactionHolder th; QSqlQuery &query = *th.qsqQuery;