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;