NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
+#include <limits.h>
#include "log.h"
#include "list.h"
#include "client.h"
#include "conf.h"
-static int nextchanId;
static channel_t *rootChan;
channel_t *defaultChan;
declare_list(channels); /* A flat list of the channels */
return ch;
}
+static int findFreeId()
+{
+ int id = 0;
+ channel_t *ch_itr = NULL;
+ for (id = 0; id < INT_MAX; id++) {
+ ch_itr = NULL;
+ while ((ch_itr = Chan_iterate(&ch_itr)) != NULL) {
+ if (ch_itr->id == id)
+ break;
+ }
+ if (ch_itr == NULL) /* Found free id */
+ return id;
+ }
+ return -1;
+}
+
#if 0
-/* Might be used when tree travesal becomes neccessary */
+/* Might be used when tree traversal becomes neccessary */
static channel_t *first_subchannel(channel_t *ch)
{
if (list_empty(&ch->subs))
}
#endif
-void Chan_iterate(channel_t **channelpptr)
+channel_t *Chan_iterate(channel_t **channelpptr)
{
channel_t *ch = *channelpptr;
}
*channelpptr = ch;
+ return ch;
+}
+
+channel_t *Chan_iterate_siblings(channel_t *parent, channel_t **channelpptr)
+{
+ channel_t *ch = *channelpptr;
+
+ if (!list_empty(&parent->subs)) {
+ if (ch == NULL)
+ ch = list_get_entry(list_get_first(&parent->subs), channel_t, node);
+ else {
+ if (list_get_next(&ch->node) == &parent->subs)
+ ch = NULL;
+ else
+ ch = list_get_entry(list_get_next(&ch->node), channel_t, node);
+ }
+ }
+
+ *channelpptr = ch;
+ return ch;
}
void Chan_init()
ch_dst = ch_itr;
list_add_tail(&ch_dst->link_node, &ch_src->channel_links);
- Log_debug("Adding channel link %s -> %s", ch_src->name, ch_dst->name);
+ Log_info("Adding channel link %s -> %s", ch_src->name, ch_dst->name);
}
}
channel_t *Chan_createChannel(const char *name, const char *desc)
{
- /* Get an ID */
- nextchanId += 1;
- return createChannel(nextchanId, name, desc);
+ int id = findFreeId();
+ if (id < 0)
+ Log_fatal("No free channel ID found");
+ return createChannel(id, name, desc);
}
void Chan_freeChannel(channel_t *ch)
}
-void Chan_playerJoin(channel_t *ch, client_t *client)
+int Chan_playerJoin(channel_t *ch, client_t *client)
{
+ channel_t *leaving = NULL;
+ int leaving_id = -1;
+
/* Only allowed in one channel at a time */
Log_debug("Add player %s to channel %s", client->playerName, ch->name);
- if (client->channel)
+ if (client->channel) {
list_del(&client->chan_node);
+ leaving = (channel_t *)client->channel;
+ if (leaving->temporary && list_empty(&leaving->clients)) {
+ leaving_id = leaving->id;
+ Chan_freeChannel(leaving);
+ }
+ }
list_add_tail(&client->chan_node, &ch->clients);
client->channel = (void *)ch;
-
+ return leaving_id;
}
-void Chan_playerJoin_id(int channelid, client_t *client)
+int Chan_playerJoin_id(int channelid, client_t *client)
{
channel_t *ch_itr = NULL;
do {
Chan_iterate(&ch_itr);
} while (ch_itr != NULL && ch_itr->id != channelid);
- if (ch_itr == NULL)
+ if (ch_itr == NULL) {
Log_warn("Channel id %d not found - ignoring.", channelid);
+ return -1;
+ }
else
- Chan_playerJoin(ch_itr, client);
-
+ return Chan_playerJoin(ch_itr, client);
}
+#if 0
void Chan_addChannel_id(int parentId, channel_t *ch)
{
channel_t *ch_itr = NULL;
else
list_add_tail(&ch->node, &ch_itr->subs);
}
+#endif
channel_t *Chan_fromId(int channelid)
{
char name[MAX_TEXT];
char desc[MAX_TEXT];
struct channel *parent;
+ bool_t temporary;
struct dlist node;
struct dlist subs;
struct dlist clients;
void Chan_removeChannel(channel_t *c);
void Chan_addClient(channel_t *c, client_t *client);
void Chan_removeClient(channel_t *c, client_t *client);
-void Chan_playerJoin(channel_t *ch, client_t *client);
-void Chan_playerJoin_id(int channelid, client_t *client);
-void Chan_iterate(channel_t **channelpptr);
+int Chan_playerJoin(channel_t *ch, client_t *client);
+int Chan_playerJoin_id(int channelid, client_t *client);
+channel_t *Chan_iterate(channel_t **channelpptr);
+channel_t *Chan_iterate_siblings(channel_t *parent, channel_t **channelpptr);
channel_t *Chan_createChannel(const char *name, const char *desc);
channel_t *Chan_fromId(int channelid);
void Chan_freeChannel(channel_t *ch);
void Mh_handle_message(client_t *client, message_t *msg)
{
- message_t *sendmsg;
+ message_t *sendmsg = NULL;
channel_t *ch_itr = NULL;
client_t *client_itr;
-
+
+ if (!client->authenticated && !(msg->messageType == Authenticate ||
+ msg->messageType == Version)) {
+ goto out;
+ }
switch (msg->messageType) {
case Authenticate:
/*
client->mute = msg->payload.userState->self_mute;
}
if (msg->payload.userState->has_channel_id) {
- Chan_playerJoin_id(msg->payload.userState->channel_id, client);
+ int leave_id;
+ leave_id = Chan_playerJoin_id(msg->payload.userState->channel_id, client);
+ if (leave_id > 0) {
+ /* XXX - need to send update to remove channel if temporary */
+ Log_debug("Removing channel ID %d", leave_id);
+ sendmsg = Msg_create(ChannelRemove);
+ sendmsg->payload.channelRemove->channel_id = leave_id;
+ }
}
if (msg->payload.userState->plugin_context != NULL) {
if (client->context)
msg->payload.userState->has_actor = true;
msg->payload.userState->actor = client->sessionId;
Client_send_message_except(NULL, msg);
+
+ /* Need to send remove channel message _after_ UserState message */
+ if (sendmsg != NULL)
+ Client_send_message_except(NULL, sendmsg);
break;
case TextMessage:
msg->payload.textMessage->has_actor = true;
msg->payload.textMessage->actor = client->sessionId;
- /* XXX - Allow HTML stuff? */
-
+ /* XXX - HTML is allowed and can't be turned off */
if (msg->payload.textMessage->n_tree_id > 0) {
sendPermissionDenied(client, "Tree message not supported");
break;
case UDPTunnel:
Client_voiceMsg(client, msg->payload.UDPTunnel->packet.data, msg->payload.UDPTunnel->packet.len);
break;
+ case ChannelState:
+ {
+ channel_t *ch_itr, *parent, *newchan;
+
+ /* Don't allow any changes to existing channels */
+ if (msg->payload.channelState->has_channel_id) {
+ sendPermissionDenied(client, "Not supported by uMurmur");
+ break;
+ }
+ /* Must have parent */
+ if (!msg->payload.channelState->has_parent) {
+ sendPermissionDenied(client, "Not supported by uMurmur");
+ break;
+ }
+ /* Must have name */
+ if (msg->payload.channelState->name == NULL) {
+ sendPermissionDenied(client, "Not supported by uMurmur");
+ break;
+ }
+ /* Must be temporary channel */
+ if (msg->payload.channelState->temporary != true) {
+ sendPermissionDenied(client, "Only temporary channels are supported by uMurmur");
+ break;
+ }
+ /* Check channel name is OK */
+ if (strlen(msg->payload.channelState->name) > MAX_TEXT) {
+ sendPermissionDenied(client, "Channel name too long");
+ break;
+ }
+
+ parent = Chan_fromId(msg->payload.channelState->parent);
+ if (parent == NULL)
+ break;
+ ch_itr = NULL;
+ while (Chan_iterate_siblings(parent, &ch_itr) != NULL) {
+ if (strcmp(ch_itr->name, msg->payload.channelState->name) == 0) {
+ sendPermissionDenied(client, "Channel already exists");
+ break;
+ }
+ }
+ /* XXX - Murmur looks for "\\w" and sends perm denied if not found.
+ * I don't know why so I don't do that here...
+ */
+
+ /* Create the channel */
+ newchan = Chan_createChannel(msg->payload.channelState->name,
+ msg->payload.channelState->description);
+ newchan->temporary = true;
+ Chan_addChannel(parent, newchan);
+ msg->payload.channelState->has_channel_id = true;
+ msg->payload.channelState->channel_id = newchan->id;
+ Msg_inc_ref(msg);
+ Client_send_message_except(NULL, msg);
+
+ /* Join the creating user */
+ sendmsg = Msg_create(UserState);
+ sendmsg->payload.userState->has_session = true;
+ sendmsg->payload.userState->session = client->sessionId;
+ sendmsg->payload.userState->has_channel_id = true;
+ sendmsg->payload.userState->channel_id = newchan->id;
+ Client_send_message_except(NULL, sendmsg);
+ Chan_playerJoin(newchan, client);
+ }
+ break;
+
/* Permission denied for all these messages. Not implemented. */
case ChannelRemove:
- case ChannelState:
case ContextAction:
case ContextActionAdd:
case ACL:
Log_warn("Message %d not handled", msg->messageType);
break;
}
+out:
Msg_free(msg);
return;
+
disconnect:
Msg_free(msg);
Client_close(client);
Msg_addPreamble(buffer, msg->messageType, len);
mumble_proto__permission_query__pack(msg->payload.permissionQuery, bufptr);
break;
+ case ChannelRemove:
+ len = mumble_proto__channel_remove__get_packed_size(msg->payload.channelRemove);
+ if (len > MAX_MSGSIZE) {
+ Log_warn("Too big tx message. Discarding");
+ break;
+ }
+ Msg_addPreamble(buffer, msg->messageType, len);
+ mumble_proto__channel_remove__pack(msg->payload.channelRemove, bufptr);
+ break;
default:
Log_warn("Unsupported message %d", msg->messageType);
msg->payload.userState = malloc(sizeof(MumbleProto__UserState));
mumble_proto__user_state__init(msg->payload.userState);
break;
+ case ChannelState:
+ msg->payload.channelState = malloc(sizeof(MumbleProto__ChannelState));
+ mumble_proto__channel_state__init(msg->payload.channelState);
+ break;
case UserRemove:
msg->payload.userRemove = malloc(sizeof(MumbleProto__UserRemove));
mumble_proto__user_remove__init(msg->payload.userRemove);
msg->payload.codecVersion = malloc(sizeof(MumbleProto__CodecVersion));
mumble_proto__codec_version__init(msg->payload.codecVersion);
break;
- case ChannelState:
- msg->payload.channelState = malloc(sizeof(MumbleProto__ChannelState));
- mumble_proto__channel_state__init(msg->payload.channelState);
- break;
case PermissionQuery:
msg->payload.permissionQuery = malloc(sizeof(MumbleProto__PermissionQuery));
mumble_proto__permission_query__init(msg->payload.permissionQuery);
break;
+ case ChannelRemove:
+ msg->payload.channelRemove = malloc(sizeof(MumbleProto__ChannelRemove));
+ mumble_proto__channel_remove__init(msg->payload.channelRemove);
+ break;
default:
Log_warn("Msg_create: Unsupported message %d", msg->messageType);
free(msg->payload.userState);
}
break;
+ case ChannelState:
+ if (msg->unpacked)
+ mumble_proto__channel_state__free_unpacked(msg->payload.channelState, NULL);
+ else {
+ free(msg->payload.channelState->name);
+ free(msg->payload.channelState->description);
+ free(msg->payload.channelState);
+ }
+ break;
case UserRemove:
if (msg->unpacked)
mumble_proto__user_remove__free_unpacked(msg->payload.userRemove, NULL);
free(msg->payload.codecVersion);
}
break;
- case ChannelState:
+ case PermissionQuery:
if (msg->unpacked)
- mumble_proto__channel_state__free_unpacked(msg->payload.channelState, NULL);
+ mumble_proto__permission_query__free_unpacked(msg->payload.permissionQuery, NULL);
else {
- if (msg->payload.channelState->description)
- free(msg->payload.channelState->description);
- free(msg->payload.channelState->name);
- free(msg->payload.channelState);
+ free(msg->payload.permissionQuery);
}
break;
- case PermissionQuery:
+ case ChannelRemove:
if (msg->unpacked)
- mumble_proto__permission_query__free_unpacked(msg->payload.permissionQuery, NULL);
+ mumble_proto__channel_remove__free_unpacked(msg->payload.channelRemove, NULL);
else {
- free(msg->payload.permissionQuery);
+ free(msg->payload.channelRemove);
}
break;
msg->payload.userState = mumble_proto__user_state__unpack(NULL, msgLen, msgData);
break;
}
+ case ChannelState:
+ {
+ msg = Msg_create_nopayload(ChannelState);
+ msg->unpacked = true;
+ msg->payload.channelState = mumble_proto__channel_state__unpack(NULL, msgLen, msgData);
+ break;
+ }
case VoiceTarget:
{
msg = Msg_create_nopayload(VoiceTarget);
msg->payload.permissionQuery = mumble_proto__permission_query__unpack(NULL, msgLen, msgData);
break;
}
+ case ChannelRemove:
+ {
+ msg = Msg_create_nopayload(ChannelRemove);
+ msg->unpacked = true;
+ msg->payload.channelRemove = mumble_proto__channel_remove__unpack(NULL, msgLen, msgData);
+ break;
+ }
default:
Log_warn("Unsupported message %d", messageType);
#define PERM_CACHED 0x8000000
#define PERM_ALL 0xf07ff
-#define PERM_DEFAULT (PERM_TRAVERSE | PERM_ENTER | PERM_SPEAK | PERM_WHISPER | PERM_TEXTMESSAGE)
+#define PERM_DEFAULT (PERM_TRAVERSE | PERM_ENTER | PERM_SPEAK | PERM_WHISPER | PERM_TEXTMESSAGE | PERM_MAKETEMPCHANNEL)
typedef enum {
Version,