silcd: handle empty LIST command reply
[silc.git] / apps / silcd / command.c
index f5c3824fa0fede2c7f7405a0d4e7427a1fcad8d7..7999b3286d2ee9db7105d4d4c4f21f8e44a6d4b1 100644 (file)
@@ -4,7 +4,7 @@
 
   Author: Pekka Riikonen <priikone@silcnet.org>
 
-  Copyright (C) 1997 - 2005, 2007 Pekka Riikonen
+  Copyright (C) 1997 - 2014 Pekka Riikonen
 
   This program is free software; you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published by
@@ -16,7 +16,6 @@
   GNU General Public License for more details.
 
 */
-/* $Id$ */
 
 #include "serverincludes.h"
 #include "server_internal.h"
@@ -86,6 +85,27 @@ SilcServerCommand silc_command_list[] =
   { NULL, 0 },
 };
 
+/* Returns TRUE if the connection is registered. Unregistered connections
+   usually cannot send commands hence the check. */
+
+static int silc_server_is_registered(SilcServer server,
+                                    SilcPacketStream sock,
+                                    SilcServerCommandContext cmd,
+                                    SilcCommand command)
+{
+  SilcIDListData idata = silc_packet_get_context(sock);
+
+  if (!idata)
+    return FALSE;
+
+  if (idata->status & SILC_IDLIST_STATUS_REGISTERED)
+    return TRUE;
+
+  silc_server_command_send_status_reply(cmd, command,
+                                       SILC_STATUS_ERR_NOT_REGISTERED, 0);
+  return FALSE;
+}
+
 /* Performs several checks to the command. It first checks whether this
    command was called as pending command callback. If it was then it checks
    whether error occurred in the command reply where the pending command
@@ -103,6 +123,13 @@ do {                                                                            \
     return;                                                                 \
   }                                                                         \
                                                                             \
+  if (context2 &&                                                           \
+      !silc_server_is_registered(cmd->server, cmd->sock, cmd, command)) {    \
+    SILC_LOG_DEBUG(("Not registered, command not called"));                 \
+    silc_server_command_free(cmd);                                          \
+    return;                                                                 \
+  }                                                                         \
+                                                                            \
   _argc = silc_argument_get_arg_num(cmd->args);                                     \
   if (_argc < min) {                                                        \
     SILC_LOG_DEBUG(("Not enough parameters in command"));                   \
@@ -122,27 +149,6 @@ do {                                                                            \
   }                                                                         \
 } while(0)
 
-/* Returns TRUE if the connection is registered. Unregistered connections
-   usually cannot send commands hence the check. */
-
-static int silc_server_is_registered(SilcServer server,
-                                    SilcPacketStream sock,
-                                    SilcServerCommandContext cmd,
-                                    SilcCommand command)
-{
-  SilcIDListData idata = silc_packet_get_context(sock);
-
-  if (!idata)
-    return FALSE;
-
-  if (idata->status & SILC_IDLIST_STATUS_REGISTERED)
-    return TRUE;
-
-  silc_server_command_send_status_reply(cmd, command,
-                                       SILC_STATUS_ERR_NOT_REGISTERED, 0);
-  return FALSE;
-}
-
 /* Internal context to hold data when executed command with timeout. */
 typedef struct {
   SilcServerCommandContext ctx;
@@ -203,6 +209,8 @@ void silc_server_command_process(SilcServer server,
   /* Allocate command context. This must be free'd by the
      command routine receiving it. */
   ctx = silc_server_command_alloc();
+  if (!ctx)
+    return;
   ctx->server = server;
   ctx->sock = sock;
   ctx->packet = packet; /* Save original packet */
@@ -246,6 +254,15 @@ void silc_server_command_process(SilcServer server,
     int fast;
 
     timeout = silc_calloc(1, sizeof(*timeout));
+    if (!timeout) {
+      silc_server_command_send_status_reply(
+                                       ctx, command,
+                                       SILC_STATUS_ERR_OPERATION_ALLOWED, 0);
+      silc_packet_free(packet);
+      silc_packet_stream_unref(ctx->sock);
+      silc_free(ctx);
+      return;
+    }
     timeout->ctx = ctx;
     timeout->cmd = cmd;
 
@@ -294,7 +311,8 @@ void silc_server_command_process(SilcServer server,
 SilcServerCommandContext silc_server_command_alloc()
 {
   SilcServerCommandContext ctx = silc_calloc(1, sizeof(*ctx));
-  ctx->users++;
+  if (ctx)
+    ctx->users++;
   return ctx;
 }
 
@@ -339,10 +357,12 @@ SILC_TASK_CALLBACK(silc_server_command_pending_timeout)
   SilcBuffer tmpreply;
   int i;
 
-  SILC_LOG_DEBUG(("Timeout pending command"));
+  SILC_LOG_DEBUG(("Timeout pending command %p", reply));
 
   /* Allocate temporary and bogus command reply context */
   cmdr = silc_calloc(1, sizeof(*cmdr));
+  if (!cmdr)
+    return;
   cmdr->server = server;
   cmdr->ident = reply->ident;
 
@@ -350,6 +370,10 @@ SILC_TASK_CALLBACK(silc_server_command_pending_timeout)
   cmdr->callbacks =
     silc_server_command_pending_check(server, reply->reply_cmd,
                                      reply->ident, &cmdr->callbacks_count);
+  if (!cmdr->callbacks) {
+    silc_free(cmdr);
+    return;
+  }
 
   /* Create bogus command reply with an error inside */
   tmpreply =
@@ -393,11 +417,11 @@ SilcBool silc_server_command_pending(SilcServer server,
    commands.  If the `timeout' is zero default timeout is used. */
 
 SilcBool silc_server_command_pending_timed(SilcServer server,
-                                      SilcCommand reply_cmd,
-                                      SilcUInt16 ident,
-                                      SilcCommandCb callback,
-                                      void *context,
-                                      SilcUInt16 timeout)
+                                          SilcCommand reply_cmd,
+                                          SilcUInt16 ident,
+                                          SilcCommandCb callback,
+                                          void *context,
+                                          SilcUInt16 timeout)
 {
   SilcServerCommandPending *reply;
 
@@ -412,6 +436,8 @@ SilcBool silc_server_command_pending_timed(SilcServer server,
   }
 
   reply = silc_calloc(1, sizeof(*reply));
+  if (!reply)
+    return FALSE;
   reply->reply_cmd = reply_cmd;
   reply->ident = ident;
   reply->context = context;
@@ -447,7 +473,8 @@ void silc_server_command_pending_del(SilcServer server,
 }
 
 /* Checks for pending commands and marks callbacks to be called from
-   the command reply function. Returns TRUE if there were pending command. */
+   the command reply function. Returns the pending callbacks if there were
+   any or NULL if there weren't. */
 
 SilcServerCommandPendingCallbacks
 silc_server_command_pending_check(SilcServer server,
@@ -464,6 +491,8 @@ silc_server_command_pending_check(SilcServer server,
     if ((r->reply_cmd == command || r->reply_cmd == SILC_COMMAND_NONE)
        && r->ident == ident) {
       callbacks = silc_realloc(callbacks, sizeof(*callbacks) * (i + 1));
+      if (!callbacks)
+       return NULL;
       callbacks[i].context = r->context;
       callbacks[i].callback = r->callback;
       r->reply_check = TRUE;
@@ -599,7 +628,7 @@ SILC_SERVER_CMD_FUNC(whois)
 {
   SilcServerCommandContext cmd = (SilcServerCommandContext)context;
   SILC_SERVER_COMMAND_CHECK(SILC_COMMAND_WHOIS, cmd, 1, 256);
-  silc_server_query_command(cmd->server, SILC_COMMAND_WHOIS, cmd);
+  silc_server_query_command(cmd->server, SILC_COMMAND_WHOIS, cmd, NULL);
   silc_server_command_free(cmd);
 }
 
@@ -609,7 +638,7 @@ SILC_SERVER_CMD_FUNC(whowas)
 {
   SilcServerCommandContext cmd = (SilcServerCommandContext)context;
   SILC_SERVER_COMMAND_CHECK(SILC_COMMAND_WHOWAS, cmd, 1, 2);
-  silc_server_query_command(cmd->server, SILC_COMMAND_WHOWAS, cmd);
+  silc_server_query_command(cmd->server, SILC_COMMAND_WHOWAS, cmd, NULL);
   silc_server_command_free(cmd);
 }
 
@@ -619,7 +648,7 @@ SILC_SERVER_CMD_FUNC(identify)
 {
   SilcServerCommandContext cmd = (SilcServerCommandContext)context;
   SILC_SERVER_COMMAND_CHECK(SILC_COMMAND_IDENTIFY, cmd, 1, 256);
-  silc_server_query_command(cmd->server, SILC_COMMAND_IDENTIFY, cmd);
+  silc_server_query_command(cmd->server, SILC_COMMAND_IDENTIFY, cmd, NULL);
   silc_server_command_free(cmd);
 }
 
@@ -768,7 +797,7 @@ silc_server_command_list_send_reply(SilcServerCommandContext cmd,
       valid_rcount++;
   }
 
-  if (!lch_count && !gch_count) {
+  if (!valid_lcount && !valid_rcount) {
     silc_server_command_send_status_reply(cmd, SILC_COMMAND_LIST,
                                          SILC_STATUS_OK, 0);
     return;
@@ -1051,7 +1080,8 @@ SILC_SERVER_CMD_FUNC(invite)
   SilcBuffer list, tmp2;
   SilcBufferStruct alist;
   unsigned char *tmp, *atype = NULL;
-  SilcUInt32 len, type, len2;
+  SilcUInt32 len, len2, ttype;
+  void *type;
   SilcUInt16 argc = 0, ident = silc_command_get_ident(cmd->payload);
 
   SILC_SERVER_COMMAND_CHECK(SILC_COMMAND_INVITE, cmd, 1, 4);
@@ -1163,7 +1193,7 @@ SILC_SERVER_CMD_FUNC(invite)
     tmp = silc_argument_get_arg_type(cmd->args, 2, &len);
     silc_hash_table_list(channel->invite_list, &htl);
     while (silc_hash_table_get(&htl, (void *)&type, (void *)&tmp2)) {
-      if (type == 3 && !memcmp(tmp2->data, tmp, len)) {
+      if (SILC_PTR_TO_32(type) == 3 && !memcmp(tmp2->data, tmp, len)) {
        tmp = NULL;
        break;
       }
@@ -1252,7 +1282,8 @@ SILC_SERVER_CMD_FUNC(invite)
     silc_hash_table_list(channel->invite_list, &htl);
     while (silc_hash_table_get(&htl, (void *)&type, (void *)&tmp2))
       list = silc_argument_payload_encode_one(list, tmp2->data,
-                                             silc_buffer_len(tmp2), type);
+                                             silc_buffer_len(tmp2),
+                                             SILC_PTR_TO_32(type));
     silc_hash_table_list_reset(&htl);
   }
 
@@ -1287,21 +1318,21 @@ SILC_SERVER_CMD_FUNC(invite)
 
   /* Send invite list back only if the list was modified, or no arguments
      was given. */
-  type = 0;
+  ttype = 0;
   argc = silc_argument_get_arg_num(cmd->args);
   if (argc == 1)
-    type = 1;
+    ttype = 1;
   if (silc_argument_get_arg_type(cmd->args, 3, &len))
-    type = 1;
+    ttype = 1;
 
   /* Send command reply */
   tmp = silc_argument_get_arg_type(cmd->args, 1, &len);
   silc_server_send_command_reply(server, cmd->sock, SILC_COMMAND_INVITE,
                                 SILC_STATUS_OK, 0, ident, 2,
                                 2, tmp, len,
-                                3, type && list ?
+                                3, ttype && list ?
                                 list->data : NULL,
-                                type && list ? silc_buffer_len(list) : 0);
+                                ttype && list ? silc_buffer_len(list) : 0);
   silc_buffer_free(list);
 
  out:
@@ -1325,9 +1356,8 @@ SILC_TASK_CALLBACK(silc_server_command_quit_cb)
   if (client) {
     /* Free all client specific data, such as client entry and entires
        on channels this client may be on. */
-    silc_server_free_client_data(server, q->sock, client,
-                                TRUE, q->signoff);
-    silc_packet_set_context(q->sock, NULL);
+    silc_server_free_sock_user_data(server, q->sock, q->signoff);
+    silc_server_close_connection(server, q->sock);
   }
 
   silc_packet_stream_unref(q->sock);
@@ -1358,6 +1388,8 @@ SILC_SERVER_CMD_FUNC(quit)
     tmp = NULL;
 
   q = silc_calloc(1, sizeof(*q));
+  if (!q)
+    goto out;
   q->sock = sock;
   q->signoff = tmp ? strdup(tmp) : NULL;
   silc_packet_stream_ref(q->sock);
@@ -1438,7 +1470,7 @@ SILC_SERVER_CMD_FUNC(kill)
   comment = silc_argument_get_arg_type(cmd->args, 2, &tmp_len2);
   if (comment && tmp_len2 > 128) {
     tmp_len2 = 128;
-    comment[127] = '\0';
+    comment[tmp_len2 - 1] = '\0';
   }
 
   /* If authentication data is provided then verify that killing is
@@ -1471,13 +1503,19 @@ SILC_SERVER_CMD_FUNC(kill)
 
     /* Do normal signoff for the destination client */
     sock = remote_client->connection;
+
+    if (sock)
+      silc_packet_stream_ref(sock);
+
     silc_server_remove_from_channels(server, NULL, remote_client,
                                     TRUE, (char *)"Killed", TRUE, TRUE);
-    silc_server_free_client_data(server, NULL, remote_client, TRUE,
-                                comment ? comment :
-                                (unsigned char *)"Killed");
-    if (sock)
+    silc_server_free_sock_user_data(server, sock, comment ? comment :
+                                   (unsigned char *)"Killed");
+    if (sock) {
+      silc_packet_set_context(sock, NULL);
       silc_server_close_connection(server, sock);
+      silc_packet_stream_unref(sock);
+    }
   } else {
     /* Router operator killing */
 
@@ -1557,12 +1595,13 @@ SILC_SERVER_CMD_FUNC(info)
     char info_string[256];
 
     memset(info_string, 0, sizeof(info_string));
-    snprintf(info_string, sizeof(info_string),
-            "location: %s server: %s admin: %s <%s>",
-            server->config->server_info->location,
-            server->config->server_info->server_type,
-            server->config->server_info->admin,
-            server->config->server_info->email);
+    silc_snprintf(info_string, sizeof(info_string),
+                 "location: %s server: %s admin: %s <%s> version: %s",
+                 server->config->server_info->location,
+                 server->config->server_info->server_type,
+                 server->config->server_info->admin,
+                 server->config->server_info->email,
+                 silc_dist_version);
 
     server_info = info_string;
     entry = server->id_entry;
@@ -1829,6 +1868,7 @@ static void silc_server_command_join_channel(SilcServer server,
   SilcBuffer user_list, mode_list, invite_list, ban_list;
   SilcUInt16 ident = silc_command_get_ident(cmd->payload);
   char check[512], check2[512];
+  void *plen;
   SilcBool founder = FALSE;
   SilcBool resolve;
   SilcBuffer fkey = NULL, chpklist = NULL;
@@ -2097,6 +2137,8 @@ static void silc_server_command_join_channel(SilcServer server,
      Add also the channel to client entry's channels list for fast cross-
      referencing. */
   chl = silc_calloc(1, sizeof(*chl));
+  if (!chl)
+    goto out;
   chl->mode = umode;
   chl->client = client;
   chl->channel = channel;
@@ -2145,10 +2187,11 @@ static void silc_server_command_join_channel(SilcServer server,
                       SILC_STR_END);
 
     silc_hash_table_list(channel->invite_list, &htl);
-    while (silc_hash_table_get(&htl, (void *)&tmp_len, (void *)&reply))
+    while (silc_hash_table_get(&htl, (void *)&plen, (void *)&reply))
       invite_list = silc_argument_payload_encode_one(invite_list,
                                                     reply->data,
-                                                    silc_buffer_len(reply), tmp_len);
+                                                    silc_buffer_len(reply),
+                                                    SILC_PTR_TO_32(plen));
     silc_hash_table_list_reset(&htl);
   }
 
@@ -2164,10 +2207,11 @@ static void silc_server_command_join_channel(SilcServer server,
                       SILC_STR_END);
 
     silc_hash_table_list(channel->ban_list, &htl);
-    while (silc_hash_table_get(&htl, (void *)&tmp_len, (void *)&reply))
+    while (silc_hash_table_get(&htl, (void *)&plen, (void *)&reply))
       ban_list = silc_argument_payload_encode_one(ban_list,
                                                  reply->data,
-                                                 silc_buffer_len(reply), tmp_len);
+                                                 silc_buffer_len(reply),
+                                                 SILC_PTR_TO_32(plen));
     silc_hash_table_list_reset(&htl);
   }
 
@@ -2295,6 +2339,38 @@ static void silc_server_command_join_channel(SilcServer server,
 /* Server side of command JOIN. Joins client into requested channel. If
    the channel does not exist it will be created. */
 
+void silc_server_command_join_connected(SilcServer server,
+                                       SilcServerEntry server_entry,
+                                       void *context)
+{
+  SilcServerCommandContext cmd = (SilcServerCommandContext)context;
+
+  if (!server_entry) {
+    SilcUInt32 tmp_len;
+    unsigned char *tmp = silc_argument_get_arg_type(cmd->args, 1, &tmp_len);
+    char serv[256 + 1];
+
+    SILC_LOG_DEBUG(("Connecting to router failed"));
+    silc_parse_userfqdn(tmp, NULL, 0, serv, sizeof(serv));
+
+    if (serv[0]) {
+      silc_server_command_send_status_data(cmd, SILC_COMMAND_JOIN,
+                                          SILC_STATUS_ERR_NO_SUCH_SERVER, 0,
+                                          2, serv, strlen(serv));
+    } else {
+      silc_server_command_send_status_data(cmd, SILC_COMMAND_JOIN,
+                                          SILC_STATUS_ERR_NO_SUCH_CHANNEL, 0,
+                                          2, tmp, tmp_len);
+    }
+    silc_server_command_free(cmd);
+    return;
+  }
+
+  /* Reprocess command */
+  SILC_LOG_DEBUG(("Reprocess JOIN after connecting to router"));
+  silc_server_command_join(cmd, NULL);
+}
+
 SILC_SERVER_CMD_FUNC(join)
 {
   SilcServerCommandContext cmd = (SilcServerCommandContext)context;
@@ -2303,6 +2379,7 @@ SILC_SERVER_CMD_FUNC(join)
   unsigned char *auth, *cauth;
   SilcUInt32 tmp_len, auth_len, cauth_len;
   char *tmp, *channel_name, *channel_namec = NULL, *cipher, *hmac;
+  char parsed[256 + 1], serv[256 + 1];
   SilcChannelEntry channel;
   SilcUInt32 umode = 0;
   SilcBool created = FALSE, create_key = TRUE;
@@ -2321,15 +2398,61 @@ SILC_SERVER_CMD_FUNC(join)
 
   /* Truncate over long channel names */
   if (tmp_len > 256) {
-    tmp[tmp_len - 1] = '\0';
     tmp_len = 256;
+    tmp[tmp_len - 1] = '\0';
+  }
+
+  /* Parse server name from the channel name */
+  silc_parse_userfqdn(tmp, parsed, sizeof(parsed), serv,
+                     sizeof(serv));
+  channel_name = parsed;
+
+  if (server->config->dynamic_server) {
+    /* If server name is not specified but local channels is FALSE then the
+       channel will be global, based on our router name. */
+    if (!serv[0] && !server->config->local_channels) {
+      if (!server->standalone) {
+       silc_snprintf(serv, sizeof(serv), "%s", server->router->server_name);
+      } else {
+       SilcServerConfigRouter *router;
+       router = silc_server_config_get_primary_router(server);
+       if (router) {
+         /* Create connection to primary router */
+         SILC_LOG_DEBUG(("Create dynamic connection to primary router %s:%d",
+                         router->host, router->port));
+         silc_server_create_connection(server, FALSE, TRUE,
+                                       router->host, router->port,
+                                       silc_server_command_join_connected,
+                                       cmd);
+         return;
+       }
+      }
+    }
+
+    /* If server name is ours, ignore it. */
+    if (serv[0] && silc_utf8_strcasecmp(serv, server->server_name))
+      memset(serv, 0, sizeof(serv));
+
+    /* Create connection */
+    if (serv[0] && server->standalone) {
+      SilcServerConfigRouter *router;
+      router = silc_server_config_get_primary_router(server);
+      if (router) {
+       /* Create connection to primary router */
+       SILC_LOG_DEBUG(("Create dynamic connection to primary router %s:%d",
+                       router->host, router->port));
+       silc_server_create_connection(server, FALSE, TRUE,
+                                     router->host, router->port,
+                                     silc_server_command_join_connected, cmd);
+       return;
+      }
+    }
   }
-  channel_name = tmp;
 
   /* Check for valid channel name.  This is cached, the original is saved
      in the channel context. */
-  channel_namec = silc_channel_name_check(tmp, tmp_len, SILC_STRING_UTF8, 256,
-                                         NULL);
+  channel_namec = silc_channel_name_check(channel_name, strlen(channel_name),
+                                         SILC_STRING_UTF8, 256, NULL);
   if (!channel_namec) {
     silc_server_command_send_status_reply(cmd, SILC_COMMAND_JOIN,
                                          SILC_STATUS_ERR_BAD_CHANNEL, 0);
@@ -2343,7 +2466,6 @@ SILC_SERVER_CMD_FUNC(join)
                                          0);
     goto out;
   }
-  tmp = silc_argument_get_arg_type(cmd->args, 2, &tmp_len);
 
   /* Get cipher, hmac name and auth payload */
   cipher = silc_argument_get_arg_type(cmd->args, 4, NULL);
@@ -2387,10 +2509,22 @@ SILC_SERVER_CMD_FUNC(join)
          channel = silc_server_create_new_channel(server, server->id, cipher,
                                                   hmac, channel_name, TRUE);
          if (!channel) {
-           silc_server_command_send_status_data(
-                                 cmd, SILC_COMMAND_JOIN,
-                                 SILC_STATUS_ERR_UNKNOWN_ALGORITHM,
-                                 0, 2, cipher, strlen(cipher));
+           if (cipher) {
+             silc_server_command_send_status_data(
+                               cmd, SILC_COMMAND_JOIN,
+                               SILC_STATUS_ERR_UNKNOWN_ALGORITHM,
+                               0, 2, cipher, strlen(cipher));
+           } else if (hmac) {
+             silc_server_command_send_status_data(
+                               cmd, SILC_COMMAND_JOIN,
+                               SILC_STATUS_ERR_UNKNOWN_ALGORITHM,
+                               0, 2, hmac, strlen(hmac));
+           } else {
+             silc_server_command_send_status_reply(
+                               cmd, SILC_COMMAND_JOIN,
+                               SILC_STATUS_ERR_RESOURCE_LIMIT,
+                               0);
+           }
            goto out;
          }
 
@@ -2446,10 +2580,22 @@ SILC_SERVER_CMD_FUNC(join)
          channel = silc_server_create_new_channel(server, server->id, cipher,
                                                   hmac, channel_name, TRUE);
          if (!channel) {
-           silc_server_command_send_status_data(
-                                      cmd, SILC_COMMAND_JOIN,
-                                      SILC_STATUS_ERR_UNKNOWN_ALGORITHM, 0,
-                                      2, cipher, strlen(cipher));
+           if (cipher) {
+             silc_server_command_send_status_data(
+                                     cmd, SILC_COMMAND_JOIN,
+                                     SILC_STATUS_ERR_UNKNOWN_ALGORITHM,
+                                     0, 2, cipher, strlen(cipher));
+           } else if (hmac) {
+             silc_server_command_send_status_data(
+                                     cmd, SILC_COMMAND_JOIN,
+                                     SILC_STATUS_ERR_UNKNOWN_ALGORITHM,
+                                     0, 2, hmac, strlen(hmac));
+           } else {
+             silc_server_command_send_status_reply(
+                                     cmd, SILC_COMMAND_JOIN,
+                                     SILC_STATUS_ERR_RESOURCE_LIMIT,
+                                     0);
+           }
            goto out;
          }
 
@@ -2479,10 +2625,22 @@ SILC_SERVER_CMD_FUNC(join)
        channel = silc_server_create_new_channel(server, server->id, cipher,
                                                 hmac, channel_name, TRUE);
        if (!channel) {
-         silc_server_command_send_status_data(
-                                      cmd, SILC_COMMAND_JOIN,
-                                      SILC_STATUS_ERR_UNKNOWN_ALGORITHM, 0,
-                                      2, cipher, strlen(cipher));
+         if (cipher) {
+           silc_server_command_send_status_data(
+                                     cmd, SILC_COMMAND_JOIN,
+                                     SILC_STATUS_ERR_UNKNOWN_ALGORITHM,
+                                     0, 2, cipher, strlen(cipher));
+         } else if (hmac) {
+           silc_server_command_send_status_data(
+                                     cmd, SILC_COMMAND_JOIN,
+                                     SILC_STATUS_ERR_UNKNOWN_ALGORITHM,
+                                     0, 2, hmac, strlen(hmac));
+         } else {
+           silc_server_command_send_status_reply(
+                                     cmd, SILC_COMMAND_JOIN,
+                                     SILC_STATUS_ERR_RESOURCE_LIMIT,
+                                     0);
+         }
          goto out;
        }
 
@@ -2705,7 +2863,7 @@ SILC_SERVER_CMD_FUNC(umode)
   SilcServer server = cmd->server;
   SilcClientEntry client = silc_packet_get_context(cmd->sock);
   unsigned char *tmp_mask, m[4];
-  SilcUInt32 mask = 0;
+  SilcUInt32 mask = 0, tmp_len;
   SilcUInt16 ident = silc_command_get_ident(cmd->payload);
   SilcBool set_mask = FALSE;
 
@@ -2715,8 +2873,8 @@ SILC_SERVER_CMD_FUNC(umode)
   SILC_SERVER_COMMAND_CHECK(SILC_COMMAND_UMODE, cmd, 1, 2);
 
   /* Get the client's mode mask */
-  tmp_mask = silc_argument_get_arg_type(cmd->args, 2, NULL);
-  if (tmp_mask) {
+  tmp_mask = silc_argument_get_arg_type(cmd->args, 2, &tmp_len);
+  if (tmp_mask && tmp_len == 4) {
     SILC_GET32_MSB(mask, tmp_mask);
     set_mask = TRUE;
   }
@@ -2829,7 +2987,7 @@ SILC_SERVER_CMD_FUNC(cmode)
 
   /* Get the channel mode mask */
   tmp_mask = silc_argument_get_arg_type(cmd->args, 2, &tmp_len);
-  if (tmp_mask) {
+  if (tmp_mask && tmp_len == 4) {
     SILC_GET32_MSB(mode_mask, tmp_mask);
     set_mask = TRUE;
   }
@@ -3107,7 +3265,8 @@ SILC_SERVER_CMD_FUNC(cmode)
        silc_server_command_send_status_data(
                                        cmd, SILC_COMMAND_CMODE,
                                        SILC_STATUS_ERR_UNKNOWN_ALGORITHM, 0,
-                                       2, hmac, strlen(hmac));
+                                       2, hmac ? hmac : SILC_DEFAULT_HMAC,
+                                       strlen(hmac ? hmac : SILC_DEFAULT_HMAC));
        goto out;
       }
 
@@ -3939,22 +4098,47 @@ SILC_TASK_CALLBACK(silc_server_command_detach_cb)
 {
   SilcServer server = app_context;
   QuitInternal q = (QuitInternal)context;
-  SilcClientID *client_id = (SilcClientID *)q->sock;
-  SilcClientEntry client;
-  SilcPacketStream sock;
+  SilcPacketStream sock = q->sock;
+  SilcClientEntry client = silc_packet_get_context(sock);
+  SilcIDListData idata;
 
-  client = silc_idlist_find_client_by_id(server->local_list, client_id,
-                                        TRUE, NULL);
-  if (client && client->connection) {
-    sock = client->connection;
+  if (!client) {
+    silc_packet_stream_unref(sock);
+    silc_free(q);
+    return;
+  }
 
-    /* Close the connection on our side */
-    client->router = NULL;
-    client->connection = NULL;
-    silc_server_close_connection(server, sock);
+  SILC_LOG_DEBUG(("Detaching client %s",
+                 silc_id_render(client->id, SILC_ID_CLIENT)));
+
+  /* Stop rekey for the client. */
+  silc_server_stop_rekey(server, client);
+
+  /* Abort any active protocol */
+  idata = silc_packet_get_context(sock);
+  if (idata && idata->sconn && idata->sconn->op) {
+    SILC_LOG_DEBUG(("Abort active protocol"));
+    silc_async_abort(idata->sconn->op, NULL, NULL);
+    idata->sconn->op = NULL;
   }
 
-  silc_free(client_id);
+  silc_schedule_task_del_by_all(server->schedule, 0, silc_server_do_heartbeat,
+                               sock);
+
+  /* Close the connection on our side */
+  client->router = NULL;
+  client->connection = NULL;
+  silc_server_close_connection(server, sock);
+
+  /* Mark the client as locally detached. */
+  client->local_detached = TRUE;
+
+  /* Decrement the user count; we'll increment it if the user resumes on our
+     server. */
+  SILC_VERIFY(&server->stat.my_clients > 0);
+  server->stat.my_clients--;
+
+  silc_packet_stream_unref(sock);
   silc_free(q);
 }
 
@@ -4021,14 +4205,19 @@ SILC_SERVER_CMD_FUNC(detach)
                                   SILC_NOTIFY_TYPE_UMODE_CHANGE);
 
   q = silc_calloc(1, sizeof(*q));
-  q->sock = silc_id_dup(client->id, SILC_ID_CLIENT);
+  if (!q)
+    goto out;
+  q->sock = cmd->sock;
+  silc_packet_stream_ref(q->sock);
   silc_schedule_task_add_timeout(server->schedule,
                                 silc_server_command_detach_cb,
                                 q, 0, 200000);
 
   if (server->config->detach_timeout) {
     q = silc_calloc(1, sizeof(*q));
-    q->sock = silc_id_dup(client->id, SILC_ID_CLIENT);
+    if (!q)
+      goto out;
+    q->sock = (void *)silc_id_dup(client->id, SILC_ID_CLIENT);
     silc_schedule_task_add_timeout(server->schedule,
                                   silc_server_command_detach_timeout,
                                   q, server->config->detach_timeout * 60, 0);
@@ -4152,12 +4341,12 @@ SILC_SERVER_CMD_FUNC(watch)
   }
 
   if (add_nick && add_nick_len > 128) {
-    add_nick[128] = '\0';
     add_nick_len = 128;
+    add_nick[add_nick_len - 1] = '\0';
   }
   if (del_nick && del_nick_len > 128) {
-    del_nick[128] = '\0';
     del_nick_len = 128;
+    del_nick[del_nick_len - 1] = '\0';
   }
 
   /* Add new nickname to be watched in our cell */
@@ -4256,8 +4445,10 @@ SILC_SERVER_CMD_FUNC(watch)
 
     pk = silc_argument_get_next_arg(pkargs, &type, &pk_len);
     while (pk) {
-      if (!silc_public_key_payload_decode(pk, pk_len, &public_key))
+      if (!silc_public_key_payload_decode(pk, pk_len, &public_key)) {
+        pk = silc_argument_get_next_arg(pkargs, &type, &pk_len);
        continue;
+      }
       if (type == 0x03)
         type = 0x00;
 
@@ -4386,7 +4577,7 @@ SILC_SERVER_CMD_FUNC(silcoper)
   username = silc_identifier_check(username, tmp_len, SILC_STRING_UTF8, 128,
                                   &tmp_len);
   if (!username) {
-    silc_server_command_send_status_reply(cmd, SILC_COMMAND_OPER,
+    silc_server_command_send_status_reply(cmd, SILC_COMMAND_SILCOPER,
                                          SILC_STATUS_ERR_BAD_USERNAME,
                                          0);
     goto out;
@@ -4435,7 +4626,7 @@ SILC_SERVER_CMD_FUNC(silcoper)
   }
   if (!result) {
     /* Authentication failed */
-    silc_server_command_send_status_reply(cmd, SILC_COMMAND_OPER,
+    silc_server_command_send_status_reply(cmd, SILC_COMMAND_SILCOPER,
                                          SILC_STATUS_ERR_AUTH_FAILED, 0);
     goto out;
   }
@@ -4484,7 +4675,7 @@ SILC_SERVER_CMD_FUNC(ban)
   SilcUInt32 id_len, len, len2;
   SilcArgumentPayload args;
   SilcHashTableList htl;
-  SilcUInt32 type;
+  void *type;
   SilcUInt16 argc = 0, ident = silc_command_get_ident(cmd->payload);
   SilcBufferStruct blist;
 
@@ -4593,7 +4784,8 @@ SILC_SERVER_CMD_FUNC(ban)
     silc_hash_table_list(channel->ban_list, &htl);
     while (silc_hash_table_get(&htl, (void *)&type, (void *)&tmp2))
       list = silc_argument_payload_encode_one(list, tmp2->data,
-                                             silc_buffer_len(tmp2), type);
+                                             silc_buffer_len(tmp2),
+                                             SILC_PTR_TO_32(type));
     silc_hash_table_list_reset(&htl);
   }
 
@@ -5053,7 +5245,6 @@ SILC_SERVER_CMD_FUNC(service)
   SilcServer server = cmd->server;
   SilcUInt32 tmp_len, auth_len;
   unsigned char *service_name, *auth;
-  SilcBool send_list = FALSE;
   SilcUInt16 ident = silc_command_get_ident(cmd->payload);
 
   SILC_SERVER_COMMAND_CHECK(SILC_COMMAND_SERVICE, cmd, 0, 256);
@@ -5078,8 +5269,6 @@ SILC_SERVER_CMD_FUNC(service)
   }
 
 
-  send_list = TRUE;
-
   /* Send our service list back */
   silc_server_send_command_reply(server, cmd->sock, SILC_COMMAND_SERVICE,
                                 SILC_STATUS_OK, 0, ident, 0);
@@ -5138,7 +5327,7 @@ SILC_SERVER_CMD_FUNC(connect)
     SILC_GET32_MSB(port, tmp);
 
   /* Create the connection. It is done with timeout and is async. */
-  silc_server_create_connection(server, FALSE, host, port, NULL, NULL);
+  silc_server_create_connection(server, FALSE, FALSE, host, port, NULL, NULL);
 
   /* Send reply to the sender */
   silc_server_command_send_status_reply(cmd, SILC_COMMAND_PRIV_CONNECT,