Add banlist message handling. Bans can now be edited, added and removed via the banli...
[umurmur.git] / src / messagehandler.c
index 4b6c39659dc75a75477b2ae8cf53d43c553a8f5b..f07e3ca186de5099adfdb2cee5869e43508c22c3 100644 (file)
@@ -1,5 +1,5 @@
-/* Copyright (C) 2009-2010, Martin Johansson <martin@fatbob.nu>
-   Copyright (C) 2005-2010, 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.
 
@@ -40,6 +40,7 @@
 #include "channel.h"
 #include "conf.h"
 #include "voicetarget.h"
+#include "ban.h"
 
 #define MAX_TEXT 512
 #define MAX_USERNAME 128
@@ -68,11 +69,32 @@ static void sendPermissionDenied(client_t *client, const char *reason)
        Client_send_message(client, msg);
 }
 
+static void addTokens(client_t *client, message_t *msg)
+{
+       int i;
+       if (client->tokencount + msg->payload.authenticate->n_tokens < MAX_TOKENS) {
+               /* Check lengths first */
+               for (i = 0; i < msg->payload.authenticate->n_tokens; i++) {
+                       if (strlen(msg->payload.authenticate->tokens[i]) > MAX_TOKENSIZE - 1) {
+                               sendPermissionDenied(client, "Too long token");
+                               return;
+                       }
+               }
+               
+               for (i = 0; i < msg->payload.authenticate->n_tokens; i++) {
+                       Log_debug("Adding token '%s' to client '%s'", msg->payload.authenticate->tokens[i], client->username);
+                       Client_token_add(client, msg->payload.authenticate->tokens[i]);
+               }
+       }
+       else
+               sendPermissionDenied(client, "Too many tokens");
+}
+
 void Mh_handle_message(client_t *client, message_t *msg)
 {
        message_t *sendmsg = NULL;
        channel_t *ch_itr = NULL;
-       client_t *client_itr;
+       client_t *client_itr, *target;
 
        if (!client->authenticated && !(msg->messageType == Authenticate ||
                                                                        msg->messageType == Version)) {
@@ -96,13 +118,23 @@ void Mh_handle_message(client_t *client, message_t *msg)
                Log_debug("Authenticate message received");
                
                if (IS_AUTH(client) || !msg->payload.authenticate->username) {
-                       /* Authenticate message might be sent when a token is set by the user.*/
+                       /* Authenticate message might be sent when a tokens are changed by the user.*/
+                       Client_token_free(client); /* Clear the token list */
                        if (msg->payload.authenticate->n_tokens > 0) {
-                               Log_debug("Tokens in auth message from %s", client->username);
+                               Log_debug("Tokens in auth message from '%s'. n_tokens = %d", client->username,
+                                         msg->payload.authenticate->n_tokens);
+                               addTokens(client, msg);                         
                        }
                        break;
                }
                
+               if (SSLi_getSHA1Hash(client->ssl, client->hash) && Ban_isBanned(client)) {
+                       char hexhash[41];
+                       SSLi_hash2hex(client->hash, hexhash);
+                       Log_info("Client with hash '%s' is banned. Disconnecting", hexhash);
+                       goto disconnect;
+               }
+               
                client->authenticated = true;
                
                client_itr = NULL;
@@ -145,8 +177,19 @@ void Mh_handle_message(client_t *client, message_t *msg)
                        goto disconnect;
                }
                
-               /* Name & password */
+               /* Name */
                client->username = strdup(msg->payload.authenticate->username);                         
+
+               /* Tokens */
+               if (msg->payload.authenticate->n_tokens > 0)
+                       addTokens(client, msg);
+               
+               /* Check if admin PW among tokens */
+               if (strlen(getStrConf(ADMIN_PASSPHRASE)) > 0 &&
+                   Client_token_match(client, getStrConf(ADMIN_PASSPHRASE))) {
+                       client->isAdmin = true;
+                       Log_info_client(client, "User provided admin password");
+               }
                
                /* Setup UDP encryption */
                CryptState_init(&client->cryptState);
@@ -348,25 +391,53 @@ void Mh_handle_message(client_t *client, message_t *msg)
                }
                break;
        case UserState:
-               /* Only allow state changes for for the self user */
+               target = NULL;
+               /* Only allow state changes for for the self user unless an admin is issuing */
                if (msg->payload.userState->has_session &&
-                       msg->payload.userState->session != client->sessionId) {
+                   msg->payload.userState->session != client->sessionId && !client->isAdmin) {
                        sendPermissionDenied(client, "Permission denied");
                        break;
                }
-               if (msg->payload.userState->has_user_id || msg->payload.userState->has_mute ||
-                       msg->payload.userState->has_deaf || msg->payload.userState->has_suppress ||
-                       msg->payload.userState->has_texture) {
-                       
+               if (msg->payload.userState->has_session && msg->payload.userState->session != client->sessionId) {
+                       while (Client_iterate(&target) != NULL) {
+                               if (target->sessionId == msg->payload.userState->session)
+                                       break;
+                       }
+                       if (target == NULL) {
+                               Log_warn("Client with sessionID %d not found", msg->payload.userState->session);
+                               break;
+                       }
+               }
+
+               if (msg->payload.userState->has_user_id || msg->payload.userState->has_suppress ||
+                   msg->payload.userState->has_priority_speaker || msg->payload.userState->has_texture) {
                        sendPermissionDenied(client, "Not supported by uMurmur");
                        break;
                }
+
+               if (target == NULL)
+                       target = client;
                
                msg->payload.userState->has_session = true;
-               msg->payload.userState->session = client->sessionId;
+               msg->payload.userState->session = target->sessionId;
                msg->payload.userState->has_actor = true;
                msg->payload.userState->actor = client->sessionId;
 
+               if (msg->payload.userState->has_deaf) {
+                       target->deaf = msg->payload.userState->deaf;
+                       if (target->deaf) {
+                               msg->payload.userState->has_mute = true;
+                               msg->payload.userState->mute = true;
+                       }
+               }
+               if (msg->payload.userState->has_mute) {
+                       target->mute = msg->payload.userState->mute;
+                       if (!target->mute) {
+                               msg->payload.userState->has_deaf = true;
+                               msg->payload.userState->deaf = false;
+                               client->deaf = false;
+                       }
+               }
                if (msg->payload.userState->has_self_deaf) {
                        client->deaf = msg->payload.userState->self_deaf;
                }
@@ -404,8 +475,15 @@ void Mh_handle_message(client_t *client, message_t *msg)
                }
                if (msg->payload.userState->has_channel_id) {
                        int leave_id;
-                       if (!Chan_userJoin_id_test(msg->payload.userState->channel_id))
+                       channelJoinResult_t chjoin_rc = Chan_userJoin_id_test(msg->payload.userState->channel_id, client);
+                       
+                       if (chjoin_rc != CHJOIN_OK) {
+                               if (chjoin_rc == CHJOIN_WRONGPW) {
+                                       sendPermissionDenied(client, "Wrong channel password");
+                               }
                                break;
+                       }
+                       
                        leave_id = Chan_userJoin_id(msg->payload.userState->channel_id, client);
                        if (leave_id > 0) {
                                Log_debug("Removing channel ID %d", leave_id);
@@ -435,6 +513,8 @@ void Mh_handle_message(client_t *client, message_t *msg)
                break;
                
        case TextMessage:
+               if (!getBoolConf(ALLOW_TEXTMESSAGE))
+                       break;
                msg->payload.textMessage->has_actor = true;
                msg->payload.textMessage->actor = client->sessionId;
 
@@ -540,7 +620,16 @@ void Mh_handle_message(client_t *client, message_t *msg)
        case PermissionQuery:
                Msg_inc_ref(msg); /* Re-use message */
                msg->payload.permissionQuery->has_permissions = true;
-               msg->payload.permissionQuery->permissions = PERM_DEFAULT;
+               
+               if (client->isAdmin)
+                       msg->payload.permissionQuery->permissions = PERM_ADMIN;
+               else
+                       msg->payload.permissionQuery->permissions = PERM_DEFAULT;
+               
+               if (!getBoolConf(ALLOW_TEXTMESSAGE))
+                       msg->payload.permissionQuery->permissions &= ~PERM_TEXTMESSAGE;
+               if (!getBoolConf(ENABLE_BAN))
+                       msg->payload.permissionQuery->permissions &= ~PERM_BAN;
                
                Client_send_message(client, msg);
                break;
@@ -714,6 +803,7 @@ void Mh_handle_message(client_t *client, message_t *msg)
                        memset(sendmsg->payload.userStats->address.data, 0, 16);
                        /* ipv4 representation as ipv6 address. Supposedly correct. */
                        memcpy(&sendmsg->payload.userStats->address.data[12], &target->remote_tcp.sin_addr, 4);
+                       memset(&sendmsg->payload.userStats->address.data[10], 0xff, 2); /* IPv4 */
                        sendmsg->payload.userStats->address.len = 16;
                }
                /* BW */
@@ -729,12 +819,66 @@ void Mh_handle_message(client_t *client, message_t *msg)
                Client_send_message(client, sendmsg);
        }
        break;
+       case UserRemove:
+               target = NULL;
+               /* Only admin can issue this */
+               if (!client->isAdmin) {
+                       sendPermissionDenied(client, "Permission denied");
+                       break;
+               }
+               while (Client_iterate(&target) != NULL) {
+                       if (target->sessionId == msg->payload.userRemove->session)
+                               break;
+               }
+               if (target == NULL) {
+                       Log_warn("Client with sessionId %d not found", msg->payload.userRemove->session);
+                       break;
+               }
+               msg->payload.userRemove->session = target->sessionId;
+               msg->payload.userRemove->has_actor = true;
+               msg->payload.userRemove->actor = client->sessionId;
+
+               if (msg->payload.userRemove->has_ban && msg->payload.userRemove->ban) {
+                       if (!getBoolConf(ENABLE_BAN))
+                               sendPermissionDenied(client, "Permission denied");
+                       else
+                               Ban_UserBan(target, msg->payload.userRemove->reason);
+               } else {
+                       Log_info_client(target, "User kicked. Reason: '%s'",
+                                       strlen(msg->payload.userRemove->reason) == 0 ? "N/A" : msg->payload.userRemove->reason);
+               }
+               /* Re-use message */
+               Msg_inc_ref(msg);
+                               
+               Client_send_message_except(NULL, msg);
+               Client_close(target);
+               break;
+       case BanList:
+               /* Only admin can issue this */
+               if (!client->isAdmin) {
+                       sendPermissionDenied(client, "Permission denied");
+                       break;
+               }
+               if (!getBoolConf(ENABLE_BAN)) {
+                       sendPermissionDenied(client, "Permission denied");
+                       break;
+               }
+               if (msg->payload.banList->has_query && msg->payload.banList->query) {
+                       /* Create banlist message and add banentrys */
+                       sendmsg = Ban_getBanList();
+                       Client_send_message(client, sendmsg);
+               } else {
+                       /* Clear banlist and set the new one */
+                       Ban_clearBanList();
+                       Ban_putBanList(msg, msg->payload.banList->n_bans);
+               }
+               break;
+               
                /* Permission denied for all these messages. Not implemented. */
        case ChannelRemove:
        case ContextAction:
        case ContextActionAdd:
        case ACL:
-       case BanList:
        case UserList:
        case QueryUsers:
                sendPermissionDenied(client, "Not supported by uMurmur");