Update copyright year.
[umurmur.git] / src / messages.c
index 3014f88ca34f19b882f1776b080cf891ec95803e..893708acec63a04ffeb40e4990dec1a3345821b5 100644 (file)
@@ -1,5 +1,5 @@
-/* Copyright (C) 2009, Martin Johansson <martin@fatbob.nu>
-   Copyright (C) 2005-2009, Thorvald Natvig <thorvald@natvig.com>
+/* Copyright (C) 2009-2012, Martin Johansson <martin@fatbob.nu>
+   Copyright (C) 2005-2012, Thorvald Natvig <thorvald@natvig.com>
 
    All rights reserved.
 
 #include "pds.h"
 #include "log.h"
 
+#define PREAMBLE_SIZE 6
 
-void dumpmsg(uint8_t *data, int size);
+static message_t *Msg_create_nopayload(messageType_t messageType);
 
-void Msg_addPreamble(uint8_t *buffer, uint16_t type, uint32_t len)
-{
-       uint16_t *msgType = (uint16_t *) &buffer[0];
-       uint32_t *msgLen = (uint32_t *) &buffer[2];
+static void Msg_addPreamble(uint8_t *buffer, uint16_t type, uint32_t len)
+{      
+       buffer[1] = (type) & 0xff;
+       buffer[0] = (type >> 8) & 0xff;
        
-       *msgType = htons(type);
-       *msgLen = htonl(len);
+       buffer[5] = (len) & 0xff;
+       buffer[4] = (len >> 8) & 0xff;
+       buffer[3] = (len >> 16) & 0xff;
+       buffer[2] = (len >> 24) & 0xff; 
 }
 
 static void Msg_getPreamble(uint8_t *buffer, int *type, int *len)
 {
-       uint16_t *msgType = (uint16_t *) &buffer[0];
-       uint32_t *msgLen = (uint32_t *) &buffer[2];
+       uint16_t msgType;
+       uint32_t msgLen;
        
-       *type = (int)ntohs(*msgType);
-       *len = (int)ntohl(*msgLen);
+       msgType = buffer[1] | (buffer[0] << 8);
+       msgLen = buffer[5] | (buffer[4] << 8) | (buffer[3] << 16) | (buffer[2] << 24);
+       *type = (int)msgType;
+       *len = (int)msgLen;
 }
 
-#define MAX_MSGSIZE (BUFSIZE - 6)
+#define MAX_MSGSIZE (BUFSIZE - PREAMBLE_SIZE)
 int Msg_messageToNetwork(message_t *msg, uint8_t *buffer)
 {
        int len;
-       uint8_t *bufptr = buffer + 6;
+       uint8_t *bufptr = buffer + PREAMBLE_SIZE;
                
        Log_debug("To net: msg type %d", msg->messageType);
        switch (msg->messageType) {
@@ -77,14 +82,14 @@ int Msg_messageToNetwork(message_t *msg, uint8_t *buffer)
                Msg_addPreamble(buffer, msg->messageType, len);
                mumble_proto__version__pack(msg->payload.version, bufptr);
                break;
-       case UDPTunnel:
-               len = mumble_proto__udptunnel__get_packed_size(msg->payload.UDPTunnel);
-               if (len > MAX_MSGSIZE) {
+       case UDPTunnel: /* Non-standard handling of tunneled voice traffic. */
+               if (msg->payload.UDPTunnel->packet.len > MAX_MSGSIZE) {
                        Log_warn("Too big tx message. Discarding");
                        break;
                }
-               Msg_addPreamble(buffer, msg->messageType, len);
-               mumble_proto__udptunnel__pack(msg->payload.UDPTunnel, bufptr);          
+               len = msg->payload.UDPTunnel->packet.len;
+               Msg_addPreamble(buffer, msg->messageType, msg->payload.UDPTunnel->packet.len);
+               memcpy(bufptr, msg->payload.UDPTunnel->packet.data, msg->payload.UDPTunnel->packet.len);
                break;
        case Authenticate:
                len = mumble_proto__authenticate__get_packed_size(msg->payload.authenticate);
@@ -167,6 +172,15 @@ int Msg_messageToNetwork(message_t *msg, uint8_t *buffer)
                Msg_addPreamble(buffer, msg->messageType, len);
                mumble_proto__user_state__pack(msg->payload.userState, bufptr);
                break;
+       case UserRemove:
+               len = mumble_proto__user_remove__get_packed_size(msg->payload.userRemove);
+               if (len > MAX_MSGSIZE) {
+                       Log_warn("Too big tx message. Discarding");
+                       break;
+                       }
+               Msg_addPreamble(buffer, msg->messageType, len);
+               mumble_proto__user_remove__pack(msg->payload.userRemove, bufptr);
+               break;
        case ChannelState:
                len = mumble_proto__channel_state__get_packed_size(msg->payload.channelState);
                if (len > MAX_MSGSIZE) {
@@ -194,15 +208,53 @@ int Msg_messageToNetwork(message_t *msg, uint8_t *buffer)
                Msg_addPreamble(buffer, msg->messageType, len);
                mumble_proto__codec_version__pack(msg->payload.codecVersion, bufptr);
                break;
+       case PermissionQuery:
+               len = mumble_proto__permission_query__get_packed_size(msg->payload.permissionQuery);
+               if (len > MAX_MSGSIZE) {
+                       Log_warn("Too big tx message. Discarding");
+                       break;
+                       }
+               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;
+       case UserStats:
+       {               
+               len = mumble_proto__user_stats__get_packed_size(msg->payload.userStats);
+               if (len > MAX_MSGSIZE) {
+                       Log_warn("Too big tx message. Discarding");
+                       break;
+                       }
+               Msg_addPreamble(buffer, msg->messageType, len);
+               mumble_proto__user_stats__pack(msg->payload.userStats, bufptr);
+               break;
+       }
+       case ServerConfig:
+               len = mumble_proto__server_config__get_packed_size(msg->payload.serverConfig);
+               if (len > MAX_MSGSIZE) {
+                       Log_warn("Too big tx message. Discarding");
+                       break;
+                       }
+               Msg_addPreamble(buffer, msg->messageType, len);
+               mumble_proto__server_config__pack(msg->payload.serverConfig, bufptr);
+               break;
 
        default:
-               Log_warn("Unsupported message %d", msg->messageType);
+               Log_warn("Msg_MessageToNetwork: Unsupported message %d", msg->messageType);
                return 0;
        }
-       return len + 6;
+       return len + PREAMBLE_SIZE;
 }
 
-message_t *Msg_create(messageType_t messageType)
+static message_t *Msg_create_nopayload(messageType_t messageType)
 {
        message_t *msg = malloc(sizeof(message_t));
 
@@ -212,6 +264,12 @@ message_t *Msg_create(messageType_t messageType)
        msg->refcount = 1;
        msg->messageType = messageType;
        init_list_entry(&msg->node);
+       return msg;
+}
+
+message_t *Msg_create(messageType_t messageType)
+{
+       message_t *msg = Msg_create_nopayload(messageType);
        
        switch (messageType) {
        case Version:
@@ -258,6 +316,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);
@@ -270,9 +332,34 @@ 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);
+       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;
+       case UserStats:
+               msg->payload.userStats = malloc(sizeof(MumbleProto__UserStats));
+               mumble_proto__user_stats__init(msg->payload.userStats);
+               
+               msg->payload.userStats->from_client = malloc(sizeof(MumbleProto__UserStats__Stats));
+               mumble_proto__user_stats__stats__init(msg->payload.userStats->from_client);
+
+               msg->payload.userStats->from_server = malloc(sizeof(MumbleProto__UserStats__Stats));
+               mumble_proto__user_stats__stats__init(msg->payload.userStats->from_server);
+
+               msg->payload.userStats->version = malloc(sizeof(MumbleProto__Version));
+               mumble_proto__version__init(msg->payload.userStats->version);
+               
+               if (!msg->payload.userStats || !msg->payload.userStats->from_client ||
+                       !msg->payload.userStats->from_server || !msg->payload.userStats->version)
+                       Log_fatal("Out of memory");
+               break;
+       case ServerConfig:
+               msg->payload.serverConfig = malloc(sizeof(MumbleProto__ServerConfig));
+               mumble_proto__server_config__init(msg->payload.serverConfig);
                break;
 
        default:
@@ -294,12 +381,17 @@ void Msg_free(message_t *msg)
        if (msg->refcount > 0)
                return;
 
-       /* XXX - add free for locally generated messages too */
        switch (msg->messageType) {
        case Version:
                if (msg->unpacked)
                        mumble_proto__version__free_unpacked(msg->payload.version, NULL);
                else {
+                       if (msg->payload.version->release)
+                               free(msg->payload.version->release);
+                       if (msg->payload.version->os)
+                               free(msg->payload.version->os);
+                       if (msg->payload.version->os_version)
+                               free(msg->payload.version->os_version);
                        free(msg->payload.version);
                }
                break;
@@ -314,6 +406,8 @@ void Msg_free(message_t *msg)
        case Authenticate:
                if (msg->unpacked)
                        mumble_proto__authenticate__free_unpacked(msg->payload.authenticate, NULL);
+               else
+                       free(msg->payload.authenticate);
                break;
        case Ping:
                if (msg->unpacked)
@@ -342,6 +436,14 @@ void Msg_free(message_t *msg)
                if (msg->unpacked)
                        mumble_proto__text_message__free_unpacked(msg->payload.textMessage, NULL);
                else {
+                       if (msg->payload.textMessage->message)
+                               free(msg->payload.textMessage->message);
+                       if (msg->payload.textMessage->session)
+                               free(msg->payload.textMessage->session);
+                       if (msg->payload.textMessage->channel_id)
+                               free(msg->payload.textMessage->channel_id);
+                       if (msg->payload.textMessage->tree_id)
+                               free(msg->payload.textMessage->tree_id);
                        free(msg->payload.textMessage);
                }
                break;
@@ -375,6 +477,19 @@ 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 {
+                       if (msg->payload.channelState->name)
+                               free(msg->payload.channelState->name);
+                       if (msg->payload.channelState->description)
+                               free(msg->payload.channelState->description);
+                       if (msg->payload.channelState->links)
+                               free(msg->payload.channelState->links);
+                       free(msg->payload.channelState);
+               }
+               break;
        case UserRemove:
                if (msg->unpacked)
                        mumble_proto__user_remove__free_unpacked(msg->payload.userRemove, NULL);
@@ -396,14 +511,56 @@ 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 ChannelRemove:
+               if (msg->unpacked)
+                       mumble_proto__channel_remove__free_unpacked(msg->payload.channelRemove, NULL);
+               else {
+                       free(msg->payload.channelRemove);
+               }
+               break;
+       case UserStats:
+               if (msg->unpacked)
+                       mumble_proto__user_stats__free_unpacked(msg->payload.userStats, NULL);
+               else {
+                       if (msg->payload.userStats->from_client)
+                               free(msg->payload.userStats->from_client);
+                       if (msg->payload.userStats->from_server)
+                               free(msg->payload.userStats->from_server);
+                       if (msg->payload.userStats->version) {
+                               if (msg->payload.userStats->version->release)
+                                       free(msg->payload.userStats->version->release);
+                               if (msg->payload.userStats->version->os)
+                                       free(msg->payload.userStats->version->os);
+                               if (msg->payload.userStats->version->os_version)
+                                       free(msg->payload.userStats->version->os_version);
+
+                               free(msg->payload.userStats->version);
+                       }
+                       if (msg->payload.userStats->celt_versions)
+                               free(msg->payload.userStats->celt_versions);
+                       if (msg->payload.userStats->certificates) {
+                               if (msg->payload.userStats->certificates->data)
+                                       free(msg->payload.userStats->certificates->data);
+                               free(msg->payload.userStats->certificates);
+                       }
+                       if (msg->payload.userStats->address.data)
+                               free(msg->payload.userStats->address.data);
+
+                       free(msg->payload.userStats);
+               }
+               break;
+       case ServerConfig:
+               if (msg->unpacked)
+                       mumble_proto__server_config__free_unpacked(msg->payload.serverConfig, NULL);
+               else {
+                       free(msg->payload.serverConfig);
                }
                break;
 
@@ -414,21 +571,21 @@ void Msg_free(message_t *msg)
        free(msg);
 }
 
-void dumpmsg(uint8_t *data, int size)
+message_t *Msg_CreateVoiceMsg(uint8_t *data, int size)
 {
-       int i, r = 0, offset = 0;
-       char buf[512];
+       message_t *msg = NULL;
        
-       while (r * 8 + i < size) {
-               for (i = 0; i < 8 && r * 8 + i < size; i++) {
-                       offset += sprintf(buf + offset, "%x ", data[r * 8 + i]);
-               }
-               sprintf(buf + offset, "\n");
-               printf(buf);
-               offset = 0;
-               r++;
-               i = 0;
-       } 
+       msg = Msg_create_nopayload(UDPTunnel);
+       msg->unpacked = false;
+       msg->payload.UDPTunnel = malloc(sizeof(struct _MumbleProto__UDPTunnel));
+       if (msg->payload.UDPTunnel == NULL)
+               Log_fatal("Out of memory");
+       msg->payload.UDPTunnel->packet.data = malloc(size);
+       if (msg->payload.UDPTunnel->packet.data == NULL)
+               Log_fatal("Out of memory");
+       memcpy(msg->payload.UDPTunnel->packet.data, data, size);
+       msg->payload.UDPTunnel->packet.len = size;
+       return msg;
 }
 
 message_t *Msg_networkToMessage(uint8_t *data, int size)
@@ -440,98 +597,165 @@ message_t *Msg_networkToMessage(uint8_t *data, int size)
        Msg_getPreamble(data, &messageType, &msgLen);
 
        Log_debug("Message type %d size %d", messageType, msgLen);
-       dumpmsg(data, size);
+       //dumpmsg(data, size);
        
        switch (messageType) {
        case Version:
        {
-               msg = Msg_create(Version);
+               msg = Msg_create_nopayload(Version);
                msg->unpacked = true;
                msg->payload.version = mumble_proto__version__unpack(NULL, msgLen, msgData);
+               if (msg->payload.version == NULL)
+                       goto err_out;
                break;
        }
-       case UDPTunnel:
+       case UDPTunnel: /* Non-standard handling of tunneled voice data */
        {
-               msg = Msg_create(UDPTunnel);
-               msg->unpacked = true;
-               msg->payload.UDPTunnel = mumble_proto__udptunnel__unpack(NULL, msgLen, msgData);
+               msg = Msg_CreateVoiceMsg(msgData, msgLen);
                break;
        }
        case Authenticate:
        {
-               msg = Msg_create(Authenticate);
+               msg = Msg_create_nopayload(Authenticate);
                msg->unpacked = true;
                msg->payload.authenticate = mumble_proto__authenticate__unpack(NULL, msgLen, msgData);
+               if (msg->payload.authenticate == NULL)
+                       goto err_out;
                break;
        }
        case Ping:
        {
-               msg = Msg_create(Ping);
+               msg = Msg_create_nopayload(Ping);
                msg->unpacked = true;
                msg->payload.ping = mumble_proto__ping__unpack(NULL, msgLen, msgData);
+               if (msg->payload.ping == NULL)
+                       goto err_out;
                break;
        }
        case Reject:
        {
-               msg = Msg_create(Reject);
+               msg = Msg_create_nopayload(Reject);
                msg->unpacked = true;
                msg->payload.reject = mumble_proto__reject__unpack(NULL, msgLen, msgData);
+               if (msg->payload.reject == NULL)
+                       goto err_out;
                break;
        }
        case ServerSync:
        {
-               msg = Msg_create(ServerSync);
+               msg = Msg_create_nopayload(ServerSync);
                msg->unpacked = true;
                msg->payload.serverSync = mumble_proto__server_sync__unpack(NULL, msgLen, msgData);
+               if (msg->payload.serverSync == NULL)
+                       goto err_out;
                break;
        }
        case TextMessage:
        {
-               msg = Msg_create(TextMessage);
+               msg = Msg_create_nopayload(TextMessage);
                msg->unpacked = true;
                msg->payload.textMessage = mumble_proto__text_message__unpack(NULL, msgLen, msgData);
+               if (msg->payload.textMessage == NULL)
+                       goto err_out;
                break;
        }
        case PermissionDenied:
        {
-               msg = Msg_create(PermissionDenied);
+               msg = Msg_create_nopayload(PermissionDenied);
                msg->unpacked = true;
                msg->payload.permissionDenied = mumble_proto__permission_denied__unpack(NULL, msgLen, msgData);
+               if (msg->payload.permissionDenied == NULL)
+                       goto err_out;
                break;
        }
        case CryptSetup:
        {
-               msg = Msg_create(CryptSetup);
+               msg = Msg_create_nopayload(CryptSetup);
                msg->unpacked = true;
                msg->payload.cryptSetup = mumble_proto__crypt_setup__unpack(NULL, msgLen, msgData);
+               if (msg->payload.cryptSetup == NULL)
+                       goto err_out;
                break;
        }
        case UserList:
        {
-               msg = Msg_create(UserList);
+               msg = Msg_create_nopayload(UserList);
                msg->unpacked = true;
                msg->payload.userList = mumble_proto__user_list__unpack(NULL, msgLen, msgData);
+               if (msg->payload.userList == NULL)
+                       goto err_out;
                break;
        }
        case UserState:
        {
-               msg = Msg_create(UserState);
+               msg = Msg_create_nopayload(UserState);
                msg->unpacked = true;
                msg->payload.userState = mumble_proto__user_state__unpack(NULL, msgLen, msgData);
+               if (msg->payload.userState == NULL)
+                       goto err_out;
+               break;
+       }
+       case ChannelState:
+       {
+               msg = Msg_create_nopayload(ChannelState);
+               msg->unpacked = true;
+               msg->payload.channelState = mumble_proto__channel_state__unpack(NULL, msgLen, msgData);
+               if (msg->payload.channelState == NULL)
+                       goto err_out;
                break;
        }
        case VoiceTarget:
        {
-               msg = Msg_create(VoiceTarget);
+               msg = Msg_create_nopayload(VoiceTarget);
                msg->unpacked = true;
                msg->payload.voiceTarget = mumble_proto__voice_target__unpack(NULL, msgLen, msgData);
+               if (msg->payload.voiceTarget == NULL)
+                       goto err_out;
                break;
        }
        case CodecVersion:
        {
-               msg = Msg_create(CodecVersion);
+               msg = Msg_create_nopayload(CodecVersion);
                msg->unpacked = true;
                msg->payload.codecVersion = mumble_proto__codec_version__unpack(NULL, msgLen, msgData);
+               if (msg->payload.codecVersion == NULL)
+                       goto err_out;
+               break;
+       }
+       case PermissionQuery:
+       {
+               msg = Msg_create_nopayload(PermissionQuery);
+               msg->unpacked = true;
+               msg->payload.permissionQuery = mumble_proto__permission_query__unpack(NULL, msgLen, msgData);
+               if (msg->payload.permissionQuery == NULL)
+                       goto err_out;
+               break;
+       }
+       case ChannelRemove:
+       {
+               msg = Msg_create_nopayload(ChannelRemove);
+               msg->unpacked = true;
+               msg->payload.channelRemove = mumble_proto__channel_remove__unpack(NULL, msgLen, msgData);
+               if (msg->payload.channelRemove == NULL)
+                       goto err_out;
+               break;
+       }
+       case UserStats:
+       {
+               msg = Msg_create_nopayload(UserStats);
+               msg->unpacked = true;
+               msg->payload.userStats = mumble_proto__user_stats__unpack(NULL, msgLen, msgData);
+               if (msg->payload.userStats == NULL)
+                       goto err_out;
+               break;
+       }
+       case UserRemove:
+       {
+               msg = Msg_create_nopayload(UserRemove);
+               msg->unpacked = true;
+               msg->payload.userRemove = mumble_proto__user_remove__unpack(NULL, msgLen, msgData);
+               if (msg->payload.userRemove == NULL)
+                       goto err_out;
                break;
        }
 
@@ -540,4 +764,8 @@ message_t *Msg_networkToMessage(uint8_t *data, int size)
                break;
        }
        return msg;
+       
+err_out:
+       free(msg);
+       return NULL;
 }