From dd433dd7c2060728c6f51c15c9e822aa26f503d8 Mon Sep 17 00:00:00 2001 From: fatbob313 Date: Sun, 24 Jan 2010 15:21:56 +0000 Subject: [PATCH] Added creation of temporary channels. --- src/channel.c | 77 +++++++++++++++++++++++++++++------- src/channel.h | 8 ++-- src/messagehandler.c | 92 +++++++++++++++++++++++++++++++++++++++++--- src/messages.c | 59 +++++++++++++++++++++------- src/messages.h | 2 +- 5 files changed, 201 insertions(+), 37 deletions(-) diff --git a/src/channel.c b/src/channel.c index 1c0b160..942c348 100644 --- a/src/channel.c +++ b/src/channel.c @@ -28,6 +28,7 @@ NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ +#include #include "log.h" #include "list.h" #include "client.h" @@ -35,7 +36,6 @@ #include "conf.h" -static int nextchanId; static channel_t *rootChan; channel_t *defaultChan; declare_list(channels); /* A flat list of the channels */ @@ -59,8 +59,24 @@ static channel_t *createChannel(int id, const char *name, const char *desc) 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)) @@ -78,7 +94,7 @@ static channel_t *next_channel(channel_t *ch) } #endif -void Chan_iterate(channel_t **channelpptr) +channel_t *Chan_iterate(channel_t **channelpptr) { channel_t *ch = *channelpptr; @@ -94,6 +110,26 @@ void Chan_iterate(channel_t **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() @@ -168,7 +204,7 @@ 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); } } @@ -184,9 +220,10 @@ void Chan_free() 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) @@ -204,31 +241,42 @@ void Chan_addChannel(channel_t *parent, 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; @@ -240,6 +288,7 @@ void Chan_addChannel_id(int parentId, channel_t *ch) else list_add_tail(&ch->node, &ch_itr->subs); } +#endif channel_t *Chan_fromId(int channelid) { diff --git a/src/channel.h b/src/channel.h index d63d532..a7f31d5 100644 --- a/src/channel.h +++ b/src/channel.h @@ -40,6 +40,7 @@ typedef struct channel { char name[MAX_TEXT]; char desc[MAX_TEXT]; struct channel *parent; + bool_t temporary; struct dlist node; struct dlist subs; struct dlist clients; @@ -54,9 +55,10 @@ void Chan_addChannel(channel_t *parent, channel_t *sub); 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); diff --git a/src/messagehandler.c b/src/messagehandler.c index ceff99b..ebd602f 100644 --- a/src/messagehandler.c +++ b/src/messagehandler.c @@ -72,10 +72,14 @@ static void sendPermissionDenied(client_t *client, const char *reason) 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: /* @@ -321,7 +325,14 @@ void Mh_handle_message(client_t *client, message_t *msg) 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) @@ -338,13 +349,16 @@ void Mh_handle_message(client_t *client, message_t *msg) 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; @@ -447,9 +461,73 @@ void Mh_handle_message(client_t *client, message_t *msg) 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: @@ -463,8 +541,10 @@ void Mh_handle_message(client_t *client, message_t *msg) Log_warn("Message %d not handled", msg->messageType); break; } +out: Msg_free(msg); return; + disconnect: Msg_free(msg); Client_close(client); diff --git a/src/messages.c b/src/messages.c index b1e76f9..2f85b9a 100644 --- a/src/messages.c +++ b/src/messages.c @@ -212,6 +212,15 @@ int Msg_messageToNetwork(message_t *msg, uint8_t *buffer) 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); @@ -282,6 +291,10 @@ message_t *Msg_create(messageType_t 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); @@ -294,14 +307,14 @@ message_t *Msg_create(messageType_t messageType) 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); @@ -410,6 +423,15 @@ void Msg_free(message_t *msg) 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); @@ -431,21 +453,18 @@ void Msg_free(message_t *msg) 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; @@ -569,6 +588,13 @@ message_t *Msg_networkToMessage(uint8_t *data, int size) 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); @@ -590,6 +616,13 @@ message_t *Msg_networkToMessage(uint8_t *data, int size) 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); diff --git a/src/messages.h b/src/messages.h index 4899a08..732cb87 100644 --- a/src/messages.h +++ b/src/messages.h @@ -64,7 +64,7 @@ #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, -- 2.30.2