Added creation of temporary channels.
authorfatbob313 <martin@fatbob.nu>
Sun, 24 Jan 2010 15:21:56 +0000 (15:21 +0000)
committerfatbob313 <martin@fatbob.nu>
Sun, 24 Jan 2010 15:21:56 +0000 (15:21 +0000)
src/channel.c
src/channel.h
src/messagehandler.c
src/messages.c
src/messages.h

index 1c0b1604cc59e49fa0e590cd767a5292f5e86888..942c34847debc64c6589c31aa311ca0e08b2de98 100644 (file)
@@ -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 <limits.h>
 #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)
 {
index d63d53262c21d9299ad63fc9eef68183847de14e..a7f31d55f90ea75842c00e179c2b77f4a8032082 100644 (file)
@@ -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);
index ceff99b6c7a0f73e4cde3a5363d592bd6e5616b6..ebd602f357a8407826b3e0239f4e6f9b9c65f546 100644 (file)
@@ -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);
index b1e76f9b908c08846ba347f73ca279abf47edf8e..2f85b9a3ba0b93a06fe47e37cbfd76f6b1ff0a0e 100644 (file)
@@ -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);
index 4899a085d0d5756882e8ce4a08394d288252d848..732cb87f48b445f1c0d818724be453591d8e054f 100644 (file)
@@ -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,