Fix double free in silcd.
[silc.git] / apps / silcd / command.c
index 7a4d7aa7e45236952d2371d2f1a0b8776564062b..d8d105c535a76a72de9518fe9c1853fe17c48765 100644 (file)
@@ -2,15 +2,14 @@
 
   command.c
 
-  Author: Pekka Riikonen <priikone@poseidon.pspt.fi>
+  Author: Pekka Riikonen <priikone@silcnet.org>
 
-  Copyright (C) 1997 - 2001 Pekka Riikonen
+  Copyright (C) 1997 - 2008 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
-  the Free Software Foundation; either version 2 of the License, or
-  (at your option) any later version.
-  
+  the Free Software Foundation; version 2 of the License.
+
   This program is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 #include "server_internal.h"
 
 static int silc_server_is_registered(SilcServer server,
-                                    SilcSocketConnection sock,
+                                    SilcPacketStream sock,
                                     SilcServerCommandContext cmd,
                                     SilcCommand command);
-static void 
+static void
 silc_server_command_send_status_reply(SilcServerCommandContext cmd,
                                      SilcCommand command,
-                                     SilcCommandStatus status);
-static void 
+                                     SilcStatus status,
+                                     SilcStatus error);
+static void
 silc_server_command_send_status_data(SilcServerCommandContext cmd,
                                     SilcCommand command,
-                                    SilcCommandStatus status,
-                                    uint32 arg_type,
+                                    SilcStatus status,
+                                    SilcStatus error,
+                                    SilcUInt32 arg_type,
                                     const unsigned char *arg,
-                                    uint32 arg_len);
+                                    SilcUInt32 arg_len);
 static bool
 silc_server_command_pending_error_check(SilcServerCommandContext cmd,
                                        SilcServerCommandReplyContext cmdr,
@@ -56,8 +57,7 @@ SilcServerCommand silc_command_list[] =
   SILC_SERVER_CMD(quit, QUIT, SILC_CF_LAG | SILC_CF_REG),
   SILC_SERVER_CMD(kill, KILL, SILC_CF_LAG_STRICT | SILC_CF_REG | SILC_CF_OPER),
   SILC_SERVER_CMD(info, INFO, SILC_CF_LAG | SILC_CF_REG),
-  SILC_SERVER_CMD(connect, CONNECT, 
-                 SILC_CF_LAG | SILC_CF_REG | SILC_CF_OPER),
+  SILC_SERVER_CMD(stats, STATS, SILC_CF_LAG | SILC_CF_REG),
   SILC_SERVER_CMD(ping, PING, SILC_CF_LAG | SILC_CF_REG),
   SILC_SERVER_CMD(oper, OPER, SILC_CF_LAG | SILC_CF_REG | SILC_CF_OPER),
   SILC_SERVER_CMD(join, JOIN, SILC_CF_LAG_STRICT | SILC_CF_REG),
@@ -67,15 +67,21 @@ SilcServerCommand silc_command_list[] =
   SILC_SERVER_CMD(cumode, CUMODE, SILC_CF_LAG | SILC_CF_REG),
   SILC_SERVER_CMD(kick, KICK, SILC_CF_LAG_STRICT | SILC_CF_REG),
   SILC_SERVER_CMD(ban, BAN, SILC_CF_LAG_STRICT | SILC_CF_REG),
-  SILC_SERVER_CMD(close, CLOSE,
-                 SILC_CF_LAG | SILC_CF_REG | SILC_CF_OPER),
-  SILC_SERVER_CMD(shutdown, SHUTDOWN, SILC_CF_LAG | SILC_CF_REG | 
-                 SILC_CF_OPER),
+  SILC_SERVER_CMD(detach, DETACH, SILC_CF_LAG_STRICT | SILC_CF_REG),
+  SILC_SERVER_CMD(watch, WATCH, SILC_CF_LAG | SILC_CF_REG),
   SILC_SERVER_CMD(silcoper, SILCOPER,
                  SILC_CF_LAG | SILC_CF_REG | SILC_CF_SILC_OPER),
   SILC_SERVER_CMD(leave, LEAVE, SILC_CF_LAG_STRICT | SILC_CF_REG),
   SILC_SERVER_CMD(users, USERS, SILC_CF_LAG | SILC_CF_REG),
   SILC_SERVER_CMD(getkey, GETKEY, SILC_CF_LAG | SILC_CF_REG),
+  SILC_SERVER_CMD(service, SERVICE, SILC_CF_LAG_STRICT | SILC_CF_REG),
+
+  SILC_SERVER_CMD(connect, PRIV_CONNECT,
+                 SILC_CF_LAG | SILC_CF_REG | SILC_CF_OPER),
+  SILC_SERVER_CMD(close, PRIV_CLOSE,
+                 SILC_CF_LAG | SILC_CF_REG | SILC_CF_OPER),
+  SILC_SERVER_CMD(shutdown, PRIV_SHUTDOWN, SILC_CF_LAG | SILC_CF_REG |
+                 SILC_CF_OPER),
 
   { NULL, 0 },
 };
@@ -83,45 +89,48 @@ SilcServerCommand silc_command_list[] =
 /* 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
-   callback was called. 
+   callback was called.
 
    It also checks that the requested command includes correct amount
    of arguments. */
-#define SILC_SERVER_COMMAND_CHECK(command, context, min, max)                \
-do {                                                                         \
-  uint32 _argc;                                                                      \
-                                                                             \
-  SILC_LOG_DEBUG(("Start"));                                                 \
-                                                                             \
-  if (silc_server_command_pending_error_check(cmd, context2, command)) {      \
-    silc_server_command_free(cmd);                                           \
-    return;                                                                  \
-  }                                                                          \
-                                                                             \
-  _argc = silc_argument_get_arg_num(cmd->args);                                      \
-  if (_argc < min) {                                                         \
-    silc_server_command_send_status_reply(cmd, command,                              \
-                                         SILC_STATUS_ERR_NOT_ENOUGH_PARAMS); \
-    silc_server_command_free(cmd);                                           \
-    return;                                                                  \
-  }                                                                          \
-  if (_argc > max) {                                                         \
-    silc_server_command_send_status_reply(cmd, command,                              \
-                                         SILC_STATUS_ERR_TOO_MANY_PARAMS);   \
-    silc_server_command_free(cmd);                                           \
-    return;                                                                  \
-  }                                                                          \
+#define SILC_SERVER_COMMAND_CHECK(command, context, min, max)               \
+do {                                                                        \
+  SilcUInt32 _argc;                                                         \
+                                                                            \
+  if (silc_server_command_pending_error_check(cmd, context2, command)) {     \
+    SILC_LOG_DEBUG(("Error occurred in command reply, 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"));                   \
+    silc_server_command_send_status_reply(cmd, command,                             \
+                                         SILC_STATUS_ERR_NOT_ENOUGH_PARAMS, \
+                                         0);                                \
+    silc_server_command_free(cmd);                                          \
+    return;                                                                 \
+  }                                                                         \
+  if (_argc > max) {                                                        \
+    SILC_LOG_DEBUG(("Too many parameters in command"));                             \
+    silc_server_command_send_status_reply(cmd, command,                             \
+                                         SILC_STATUS_ERR_TOO_MANY_PARAMS,   \
+                                         0);                                \
+    silc_server_command_free(cmd);                                          \
+    return;                                                                 \
+  }                                                                         \
 } 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,
-                                    SilcSocketConnection sock,
+                                    SilcPacketStream sock,
                                     SilcServerCommandContext cmd,
                                     SilcCommand command)
 {
-  SilcIDListData idata = (SilcIDListData)sock->user_data;
+  SilcIDListData idata = silc_packet_get_context(sock);
 
   if (!idata)
     return FALSE;
@@ -130,7 +139,7 @@ static int silc_server_is_registered(SilcServer server,
     return TRUE;
 
   silc_server_command_send_status_reply(cmd, command,
-                                       SILC_STATUS_ERR_NOT_REGISTERED);
+                                       SILC_STATUS_ERR_NOT_REGISTERED, 0);
   return FALSE;
 }
 
@@ -146,25 +155,33 @@ typedef struct {
 SILC_TASK_CALLBACK(silc_server_command_process_timeout)
 {
   SilcServerCommandTimeout timeout = (SilcServerCommandTimeout)context;
-  SilcClientEntry client = (SilcClientEntry)timeout->ctx->sock->user_data;
+  SilcClientEntry client = silc_packet_get_context(timeout->ctx->sock);
 
   if (!client) {
+    SILC_LOG_DEBUG(("Client entry is invalid"));
     silc_server_command_free(timeout->ctx);
     silc_free(timeout);
+    return;
   }
 
   /* Update access time */
   client->last_command = time(NULL);
 
-  if (!(timeout->cmd->flags & SILC_CF_REG))
+  if (!(timeout->cmd->flags & SILC_CF_REG)) {
+    SILC_LOG_DEBUG(("Calling %s command",
+                   silc_get_command_name(timeout->cmd->cmd)));
     timeout->cmd->cb(timeout->ctx, NULL);
-  else if (silc_server_is_registered(timeout->ctx->server, 
-                                    timeout->ctx->sock, 
-                                    timeout->ctx, 
-                                    timeout->cmd->cmd))
+  } else if (silc_server_is_registered(timeout->ctx->server,
+                                      timeout->ctx->sock,
+                                      timeout->ctx,
+                                      timeout->cmd->cmd)) {
+    SILC_LOG_DEBUG(("Calling %s command",
+                   silc_get_command_name(timeout->cmd->cmd)));
     timeout->cmd->cb(timeout->ctx, NULL);
-  else
+  } else {
+    SILC_LOG_DEBUG(("Client is not registered"));
     silc_server_command_free(timeout->ctx);
+  }
 
   silc_free(timeout);
 }
@@ -172,28 +189,32 @@ SILC_TASK_CALLBACK(silc_server_command_process_timeout)
 /* Processes received command packet. */
 
 void silc_server_command_process(SilcServer server,
-                                SilcSocketConnection sock,
-                                SilcPacketContext *packet)
+                                SilcPacketStream sock,
+                                SilcPacket packet)
 {
+  SilcIDListData idata = silc_packet_get_context(sock);
   SilcServerCommandContext ctx;
   SilcServerCommand *cmd;
   SilcCommand command;
 
+  if (!idata)
+    return;
+
   /* Allocate command context. This must be free'd by the
      command routine receiving it. */
   ctx = silc_server_command_alloc();
   ctx->server = server;
-  ctx->sock = silc_socket_dup(sock);
-  ctx->packet = silc_packet_context_dup(packet); /* Save original packet */
-  
+  ctx->sock = sock;
+  ctx->packet = packet; /* Save original packet */
+  silc_packet_stream_ref(sock);
+
   /* Parse the command payload in the packet */
-  ctx->payload = silc_command_payload_parse(packet->buffer->data,
-                                           packet->buffer->len);
+  ctx->payload = silc_command_payload_parse(packet->buffer.data,
+                                           silc_buffer_len(&packet->buffer));
   if (!ctx->payload) {
-    SILC_LOG_ERROR(("Bad command payload, packet dropped"));
-    silc_buffer_free(packet->buffer);
-    silc_packet_context_free(packet);
-    silc_socket_free(ctx->sock);
+    SILC_LOG_ERROR(("Bad command payload"));
+    silc_packet_free(packet);
+    silc_packet_stream_unref(ctx->sock);
     silc_free(ctx);
     return;
   }
@@ -205,10 +226,13 @@ void silc_server_command_process(SilcServer server,
     if (cmd->cmd == command)
       break;
 
-  if (cmd == NULL) {
+  if (!cmd || !cmd->cb) {
+    SILC_LOG_DEBUG(("Unknown command %d", command));
     silc_server_command_send_status_reply(ctx, command,
-                                         SILC_STATUS_ERR_UNKNOWN_COMMAND);
-    silc_server_command_free(ctx);
+                                         SILC_STATUS_ERR_UNKNOWN_COMMAND, 0);
+    silc_packet_free(packet);
+    silc_packet_stream_unref(ctx->sock);
+    silc_free(ctx);
     return;
   }
 
@@ -216,11 +240,12 @@ void silc_server_command_process(SilcServer server,
      executed with zero (0) timeout but if client is sending command more
      frequently than once in 2 seconds, then the timeout may be 0 to 2
      seconds. */
-  if (sock->type == SILC_SOCKET_TYPE_CLIENT) {
-    SilcClientEntry client = (SilcClientEntry)sock->user_data;
-    SilcServerCommandTimeout timeout = silc_calloc(1, sizeof(*timeout));
+  if (idata->conn_type == SILC_CONN_CLIENT) {
+    SilcClientEntry client = silc_packet_get_context(sock);
+    SilcServerCommandTimeout timeout;
     int fast;
 
+    timeout = silc_calloc(1, sizeof(*timeout));
     timeout->ctx = ctx;
     timeout->cmd = cmd;
 
@@ -228,34 +253,40 @@ void silc_server_command_process(SilcServer server,
       client->fast_command++;
       fast = FALSE;
     } else {
-      client->fast_command = ((client->fast_command - 1) <= 0 ? 0 : 
-                             client->fast_command--);
+      if (client->fast_command - 2 <= 0)
+       client->fast_command = 0;
+      else
+       client->fast_command -= 2;
       fast = TRUE;
     }
 
     if (!fast && ((cmd->flags & SILC_CF_LAG_STRICT) ||
                  (client->fast_command > 5 && cmd->flags & SILC_CF_LAG)))
-      silc_schedule_task_add(server->schedule, sock->sock, 
-                        silc_server_command_process_timeout,
-                        (void *)timeout, 
-                        2 - (time(NULL) - client->last_command), 0,
-                        SILC_TASK_TIMEOUT, SILC_TASK_PRI_NORMAL);
+      silc_schedule_task_add_timeout(
+                           server->schedule,
+                           silc_server_command_process_timeout, timeout,
+                           (client->fast_command < 3 ? 0 :
+                            2 - (time(NULL) - client->last_command)),
+                           (client->fast_command < 3 ? 200000 : 0));
     else
-      silc_schedule_task_add(server->schedule, sock->sock, 
-                        silc_server_command_process_timeout,
-                        (void *)timeout, 0, 1,
-                        SILC_TASK_TIMEOUT, SILC_TASK_PRI_NORMAL);
+      silc_schedule_task_add_timeout(server->schedule,
+                                    silc_server_command_process_timeout,
+                                    timeout, 0, 0);
     return;
   }
 
   /* Execute for server */
 
-  if (!(cmd->flags & SILC_CF_REG))
+  if (!(cmd->flags & SILC_CF_REG)) {
+    SILC_LOG_DEBUG(("Calling %s command", silc_get_command_name(cmd->cmd)));
     cmd->cb(ctx, NULL);
-  else if (silc_server_is_registered(server, sock, ctx, cmd->cmd))
+  } else if (silc_server_is_registered(server, sock, ctx, cmd->cmd)) {
+    SILC_LOG_DEBUG(("Calling %s command", silc_get_command_name(cmd->cmd)));
     cmd->cb(ctx, NULL);
-  else
+  } else {
+    SILC_LOG_DEBUG(("Server is not registered"));
     silc_server_command_free(ctx);
+  }
 }
 
 /* Allocate Command Context */
@@ -278,9 +309,9 @@ void silc_server_command_free(SilcServerCommandContext ctx)
     if (ctx->payload)
       silc_command_payload_free(ctx->payload);
     if (ctx->packet)
-      silc_packet_context_free(ctx->packet);
+      silc_packet_free(ctx->packet);
     if (ctx->sock)
-      silc_socket_free(ctx->sock); /* Decrease reference counter */
+      silc_packet_stream_unref(ctx->sock);
     silc_free(ctx);
   }
 }
@@ -288,7 +319,7 @@ void silc_server_command_free(SilcServerCommandContext ctx)
 /* Duplicate Command Context by adding reference counter. The context won't
    be free'd untill it hits zero. */
 
-SilcServerCommandContext 
+SilcServerCommandContext
 silc_server_command_dup(SilcServerCommandContext ctx)
 {
   ctx->users++;
@@ -297,6 +328,48 @@ silc_server_command_dup(SilcServerCommandContext ctx)
   return ctx;
 }
 
+/* Timeout for pending command.  If reply to pending command never arrives
+   this is called to free resources. */
+
+SILC_TASK_CALLBACK(silc_server_command_pending_timeout)
+{
+  SilcServer server = app_context;
+  SilcServerCommandPending *reply = context;
+  SilcServerCommandReplyContext cmdr;
+  SilcBuffer tmpreply;
+  int i;
+
+  SILC_LOG_DEBUG(("Timeout pending command"));
+
+  /* Allocate temporary and bogus command reply context */
+  cmdr = silc_calloc(1, sizeof(*cmdr));
+  cmdr->server = server;
+  cmdr->ident = reply->ident;
+
+  /* Check for pending commands and mark to be exeucted */
+  cmdr->callbacks =
+    silc_server_command_pending_check(server, reply->reply_cmd,
+                                     reply->ident, &cmdr->callbacks_count);
+
+  /* Create bogus command reply with an error inside */
+  tmpreply =
+    silc_command_reply_payload_encode_va(reply->reply_cmd ? reply->reply_cmd :
+                                        SILC_COMMAND_RESERVED,
+                                        SILC_STATUS_ERR_TIMEDOUT, 0,
+                                        reply->ident, 0);
+  cmdr->payload = silc_command_payload_parse(tmpreply->data,
+                                            silc_buffer_len(tmpreply));
+  silc_buffer_free(tmpreply);
+
+  /* Call all callbacks. Same as SILC_SERVER_PENDING_EXEC macro. */
+  for (i = 0; i < cmdr->callbacks_count; i++)
+    if (cmdr->callbacks[i].callback)
+      (*cmdr->callbacks[i].callback)(cmdr->callbacks[i].context, cmdr);
+
+  silc_server_command_pending_del(server, reply->reply_cmd, reply->ident);
+  silc_server_command_reply_free(cmdr);
+}
+
 /* Add new pending command to be executed when reply to a command has been
    received. The `reply_cmd' is the command that will call the `callback'
    with `context' when reply has been received.  It can be SILC_COMMAND_NONE
@@ -306,11 +379,25 @@ silc_server_command_dup(SilcServerCommandContext ctx)
    specified command, ident, callback and context this function has no
    effect. */
 
-bool silc_server_command_pending(SilcServer server,
+SilcBool silc_server_command_pending(SilcServer server,
                                 SilcCommand reply_cmd,
-                                uint16 ident,
+                                SilcUInt16 ident,
                                 SilcCommandCb callback,
                                 void *context)
+{
+  return silc_server_command_pending_timed(server, reply_cmd, ident, callback,
+                                          context, 0);
+}
+
+/* Same as silc_server_command_pending with specific timeout for pending
+   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)
 {
   SilcServerCommandPending *reply;
 
@@ -329,6 +416,10 @@ bool silc_server_command_pending(SilcServer server,
   reply->ident = ident;
   reply->context = context;
   reply->callback = callback;
+  reply->timeout =
+    silc_schedule_task_add_timeout(server->schedule,
+                                  silc_server_command_pending_timeout, reply,
+                                  timeout ? timeout : 12, 0);
   silc_dlist_add(server->pending_commands, reply);
 
   return TRUE;
@@ -338,15 +429,19 @@ bool silc_server_command_pending(SilcServer server,
 
 void silc_server_command_pending_del(SilcServer server,
                                     SilcCommand reply_cmd,
-                                    uint16 ident)
+                                    SilcUInt16 ident)
 {
   SilcServerCommandPending *r;
 
   silc_dlist_start(server->pending_commands);
   while ((r = silc_dlist_get(server->pending_commands)) != SILC_LIST_END) {
-    if (r->reply_cmd == reply_cmd && r->ident == ident) {
+    if ((r->reply_cmd == reply_cmd || (r->reply_cmd == SILC_COMMAND_NONE &&
+                                       r->reply_check))
+        && r->ident == ident) {
       silc_dlist_del(server->pending_commands, r);
-      break;
+      if (r->timeout)
+       silc_schedule_task_del(server->schedule, r->timeout);
+      silc_free(r);
     }
   }
 }
@@ -356,10 +451,9 @@ void silc_server_command_pending_del(SilcServer server,
 
 SilcServerCommandPendingCallbacks
 silc_server_command_pending_check(SilcServer server,
-                                 SilcServerCommandReplyContext ctx,
-                                 SilcCommand command, 
-                                 uint16 ident,
-                                 uint32 *callbacks_count)
+                                 SilcCommand command,
+                                 SilcUInt16 ident,
+                                 SilcUInt32 *callbacks_count)
 {
   SilcServerCommandPending *r;
   SilcServerCommandPendingCallbacks callbacks = NULL;
@@ -372,7 +466,7 @@ silc_server_command_pending_check(SilcServer server,
       callbacks = silc_realloc(callbacks, sizeof(*callbacks) * (i + 1));
       callbacks[i].context = r->context;
       callbacks[i].callback = r->callback;
-      ctx->ident = ident;
+      r->reply_check = TRUE;
       i++;
     }
   }
@@ -383,53 +477,91 @@ silc_server_command_pending_check(SilcServer server,
 
 /* Sends simple status message as command reply packet */
 
-static void 
+static void
 silc_server_command_send_status_reply(SilcServerCommandContext cmd,
                                      SilcCommand command,
-                                     SilcCommandStatus status)
+                                     SilcStatus status,
+                                     SilcStatus error)
 {
   SilcBuffer buffer;
 
+  /* Statistics */
+  cmd->server->stat.commands_sent++;
+
   SILC_LOG_DEBUG(("Sending command status %d", status));
 
-  buffer = 
-    silc_command_reply_payload_encode_va(command, status, 
+  buffer =
+    silc_command_reply_payload_encode_va(command, status, error,
                                         silc_command_get_ident(cmd->payload),
                                         0);
   silc_server_packet_send(cmd->server, cmd->sock,
-                         SILC_PACKET_COMMAND_REPLY, 0, 
-                         buffer->data, buffer->len, FALSE);
+                         SILC_PACKET_COMMAND_REPLY, 0,
+                         buffer->data, silc_buffer_len(buffer));
   silc_buffer_free(buffer);
 }
 
 /* Sends command status reply with one extra argument. The argument
    type must be sent as argument. */
 
-static void 
+static void
 silc_server_command_send_status_data(SilcServerCommandContext cmd,
                                     SilcCommand command,
-                                    SilcCommandStatus status,
-                                    uint32 arg_type,
+                                    SilcStatus status,
+                                    SilcStatus error,
+                                    SilcUInt32 arg_type,
                                     const unsigned char *arg,
-                                    uint32 arg_len)
+                                    SilcUInt32 arg_len)
 {
   SilcBuffer buffer;
 
+  /* Statistics */
+  cmd->server->stat.commands_sent++;
+
   SILC_LOG_DEBUG(("Sending command status %d", status));
 
-  buffer = 
-    silc_command_reply_payload_encode_va(command, status, 
+  buffer =
+    silc_command_reply_payload_encode_va(command, status, 0,
                                         silc_command_get_ident(cmd->payload),
                                         1, arg_type, arg, arg_len);
   silc_server_packet_send(cmd->server, cmd->sock,
-                         SILC_PACKET_COMMAND_REPLY, 0, 
-                         buffer->data, buffer->len, FALSE);
+                         SILC_PACKET_COMMAND_REPLY, 0,
+                         buffer->data, silc_buffer_len(buffer));
+  silc_buffer_free(buffer);
+}
+
+static void
+silc_server_command_send_status_data2(SilcServerCommandContext cmd,
+                                     SilcCommand command,
+                                     SilcStatus status,
+                                     SilcStatus error,
+                                     SilcUInt32 arg_type1,
+                                     const unsigned char *arg1,
+                                     SilcUInt32 arg_len1,
+                                     SilcUInt32 arg_type2,
+                                     const unsigned char *arg2,
+                                     SilcUInt32 arg_len2)
+{
+  SilcBuffer buffer;
+
+  /* Statistics */
+  cmd->server->stat.commands_sent++;
+
+  SILC_LOG_DEBUG(("Sending command status %d", status));
+
+  buffer =
+    silc_command_reply_payload_encode_va(command, status, 0,
+                                        silc_command_get_ident(cmd->payload),
+                                        2, arg_type1, arg1, arg_len1,
+                                        arg_type2, arg2, arg_len2);
+  silc_server_packet_send(cmd->server, cmd->sock,
+                         SILC_PACKET_COMMAND_REPLY, 0,
+                         buffer->data, silc_buffer_len(buffer));
   silc_buffer_free(buffer);
 }
 
 /* This function can be called to check whether in the command reply
    an error occurred. This function has no effect if this is called
-   when the command function was not called as pending command callback. 
+   when the command function was not called as pending command callback.
    This returns TRUE if error had occurred. */
 
 static bool
@@ -437,25 +569,23 @@ silc_server_command_pending_error_check(SilcServerCommandContext cmd,
                                        SilcServerCommandReplyContext cmdr,
                                        SilcCommand command)
 {
-  SilcCommandStatus status;
-
   if (!cmd->pending || !cmdr)
     return FALSE;
 
-  SILC_GET16_MSB(status, silc_argument_get_arg_type(cmdr->args, 1, NULL));
-  if (status != SILC_STATUS_OK &&
-      status != SILC_STATUS_LIST_START &&
-      status != SILC_STATUS_LIST_ITEM &&
-      status != SILC_STATUS_LIST_END) {
+  if (!silc_command_get_status(cmdr->payload, NULL, NULL)) {
     SilcBuffer buffer;
 
+    /* Statistics */
+    cmd->server->stat.commands_sent++;
+
     /* Send the same command reply payload */
-    silc_command_set_ident(cmdr->payload, 
+    silc_command_set_command(cmdr->payload, silc_command_get(cmd->payload));
+    silc_command_set_ident(cmdr->payload,
                           silc_command_get_ident(cmd->payload));
     buffer = silc_command_payload_encode_payload(cmdr->payload);
     silc_server_packet_send(cmd->server, cmd->sock,
-                           SILC_PACKET_COMMAND_REPLY, 0, 
-                           buffer->data, buffer->len, FALSE);
+                           SILC_PACKET_COMMAND_REPLY, 0,
+                           buffer->data, silc_buffer_len(buffer));
     silc_buffer_free(buffer);
     return TRUE;
   }
@@ -463,2526 +593,1217 @@ silc_server_command_pending_error_check(SilcServerCommandContext cmd,
   return FALSE;
 }
 
-/******************************************************************************
+/* Server side of command WHOIS. */
 
-                              WHOIS Functions
+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, NULL);
+  silc_server_command_free(cmd);
+}
 
-******************************************************************************/
+/* Server side of command WHOWAS. */
 
-static int
-silc_server_command_whois_parse(SilcServerCommandContext cmd,
-                               SilcClientID ***client_id,
-                               uint32 *client_id_count,
-                               char **nickname,
-                               char **server_name,
-                               int *count,
-                               SilcCommand command)
+SILC_SERVER_CMD_FUNC(whowas)
 {
-  unsigned char *tmp;
-  uint32 len;
-  uint32 argc = silc_argument_get_arg_num(cmd->args);
-  int i, k;
-
-  /* If client ID is in the command it must be used instead of nickname */
-  tmp = silc_argument_get_arg_type(cmd->args, 3, &len);
-  if (!tmp) {
-    /* No ID, get the nickname@server string and parse it. */
-    tmp = silc_argument_get_arg_type(cmd->args, 1, NULL);
-    if (tmp) {
-      silc_parse_userfqdn(tmp, nickname, server_name);
-    } else {
-      silc_server_command_send_status_reply(cmd, command,
-                                           SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
-      return FALSE;
-    }
-  } else {
-    /* Command includes ID, we must use that.  Also check whether the command
-       has more than one ID set - take them all. */
-
-    *client_id = silc_calloc(1, sizeof(**client_id));
-    (*client_id)[0] = silc_id_payload_parse_id(tmp, len);
-    if ((*client_id)[0] == NULL) {
-      silc_free(*client_id);
-      silc_server_command_send_status_reply(cmd, command,
-                                           SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
-      return FALSE;
-    }
-    *client_id_count = 1;
-
-    /* Take all ID's from the command packet */
-    if (argc > 1) {
-      for (k = 1, i = 1; i < argc; i++) {
-       tmp = silc_argument_get_arg_type(cmd->args, i + 3, &len);
-       if (tmp) {
-         *client_id = silc_realloc(*client_id, sizeof(**client_id) *
-                                   (*client_id_count + 1));
-         (*client_id)[k] = silc_id_payload_parse_id(tmp, len);
-         if ((*client_id)[k] == NULL) {
-           /* Cleanup all and fail */
-           for (i = 0; i < *client_id_count; i++)
-             silc_free((*client_id)[i]);
-           silc_free(*client_id);
-           silc_server_command_send_status_reply(
-                                        cmd, command,
-                                        SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
-           return FALSE;
-         }
-         (*client_id_count)++;
-         k++;
-       }
-      }
-    }
-  }
+  SilcServerCommandContext cmd = (SilcServerCommandContext)context;
+  SILC_SERVER_COMMAND_CHECK(SILC_COMMAND_WHOWAS, cmd, 1, 2);
+  silc_server_query_command(cmd->server, SILC_COMMAND_WHOWAS, cmd, NULL);
+  silc_server_command_free(cmd);
+}
 
-  /* Get the max count of reply messages allowed */
-  tmp = silc_argument_get_arg_type(cmd->args, 2, NULL);
-  if (tmp)
-    *count = atoi(tmp);
-  else
-    *count = 0;
+/* Server side of command IDENTIFY. */
 
-  return TRUE;
+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, NULL);
+  silc_server_command_free(cmd);
 }
 
-/* Resolve context used by both WHOIS and IDENTIFY commands */
-typedef struct {
-  SilcServerEntry router;
-  uint16 ident;
-  unsigned char **res_argv;
-  uint32 *res_argv_lens;
-  uint32 *res_argv_types;
-  uint32 res_argc;
-} *SilcServerResolveContext;
+/* Server side of command NICK. Sets nickname for user. Setting
+   nickname causes generation of a new client ID for the client. The
+   new client ID is sent to the client after changing the nickname. */
 
-static bool
-silc_server_command_whois_check(SilcServerCommandContext cmd,
-                               SilcClientEntry *clients,
-                               uint32 clients_count)
+SILC_SERVER_CMD_FUNC(nick)
 {
+  SilcServerCommandContext cmd = (SilcServerCommandContext)context;
+  SilcClientEntry client = silc_packet_get_context(cmd->sock);
   SilcServer server = cmd->server;
-  SilcClientEntry entry;
-  SilcServerResolveContext resolve = NULL, r = NULL;
-  uint32 resolve_count = 0;
-  int i, k;
-  bool no_res = TRUE;
+  SilcBuffer nidp, oidp = NULL;
+  SilcClientID *new_id;
+  SilcUInt32 nick_len;
+  unsigned char *nick, *nickc = NULL;
+  SilcUInt16 ident = silc_command_get_ident(cmd->payload);
 
-  SILC_LOG_DEBUG(("Start"));
+  if (client->data.conn_type != SILC_CONN_CLIENT || !client)
+    goto out;
 
-  for (i = 0; i < clients_count; i++) {
-    entry = clients[i];
-    if (!entry)
-      continue;
+  SILC_SERVER_COMMAND_CHECK(SILC_COMMAND_NICK, cmd, 1, 1);
 
-    if ((entry->nickname && entry->username && entry->userinfo) ||
-       !(entry->data.status & SILC_IDLIST_STATUS_REGISTERED)) {
-      if (!entry->router)
-       continue;
+  /* Get nickname */
+  nick = silc_argument_get_arg_type(cmd->args, 1, &nick_len);
+  if (!nick) {
+    silc_server_command_send_status_reply(cmd, SILC_COMMAND_NICK,
+                                         SILC_STATUS_ERR_BAD_NICKNAME, 0);
+    goto out;
+  }
 
-      /* If we are normal server, and we've not resolved this client from
-        router and it is global client, we'll check whether it is on some
-        channel.  If not then we cannot be sure about its validity, and
-        we'll resolve it from router. */
-      if (cmd->server->server_type != SILC_SERVER || cmd->pending ||
-         entry->connection || silc_hash_table_count(entry->channels))
-       continue;
-    }
+  /* Truncate over long nicks */
+  if (nick_len > 128) {
+    nick_len = 128;
+    nick[nick_len - 1] = '\0';
+  }
 
-    /* We need to resolve this entry since it is not complete */
+  /* Check for valid nickname string.  This is cached, original is saved
+     in the client context. */
+  nickc = silc_identifier_check(nick, nick_len, SILC_STRING_UTF8, 128, NULL);
+  if (!nickc) {
+    silc_server_command_send_status_reply(cmd, SILC_COMMAND_NICK,
+                                         SILC_STATUS_ERR_BAD_NICKNAME, 0);
+    goto out;
+  }
 
-    if (!cmd->pending && entry->data.status & SILC_IDLIST_STATUS_RESOLVING) {
-      /* The entry is being resolved (and we are not the resolver) so attach
-        to the command reply and we're done with this one. */
-      silc_server_command_pending(server, SILC_COMMAND_NONE, 
-                                 entry->resolve_cmd_ident,
-                                 silc_server_command_whois,
-                                 silc_server_command_dup(cmd));
-      no_res = FALSE;
-    } else {
-      if (entry->data.status & SILC_IDLIST_STATUS_RESOLVING) {
-       /* We've resolved this and it still is not ready.  We'll return
-          and are that this will be handled again after it is resolved. */
-       for (i = 0; i < resolve_count; i++) {
-         for (k = 0; k < r->res_argc; k++)
-           silc_free(r->res_argv[k]);
-         silc_free(r->res_argv);
-         silc_free(r->res_argv_lens);
-         silc_free(r->res_argv_types);
-       }
-       silc_free(resolve);
-       return FALSE;
-      } else {
-       /* We'll resolve this client */
-       SilcBuffer idp;
+  /* Check for same nickname */
+  if (strlen(client->nickname) == nick_len &&
+      !memcmp(client->nickname, nick, nick_len)) {
+    nidp = silc_id_payload_encode(client->id, SILC_ID_CLIENT);
+    silc_free(nickc);
+    goto send_reply;
+  }
 
-       r = NULL;
-       for (k = 0; k < resolve_count; k++) {
-         if (resolve[k].router == entry->router) {
-           r = &resolve[k];
-           break;
-         }
-       }
+  /* Create new Client ID */
+  if (!silc_id_create_client_id(cmd->server, cmd->server->id,
+                               cmd->server->rng,
+                               cmd->server->md5hash,
+                               nickc, strlen(nickc), &new_id)) {
+    silc_server_command_send_status_reply(cmd, SILC_COMMAND_NICK,
+                                         SILC_STATUS_ERR_BAD_NICKNAME, 0);
+    silc_free(nickc);
+    goto out;
+  }
 
-       if (!r) {
-         resolve = silc_realloc(resolve, sizeof(*resolve) * 
-                                (resolve_count + 1));
-         r = &resolve[resolve_count];
-         memset(r, 0, sizeof(*r));
-         r->router = entry->router;
-         r->ident = ++server->cmd_ident;
-         resolve_count++;
-       }
+  /* Send notify about nickname change to our router. We send the new
+     ID and ask to replace it with the old one. If we are router the
+     packet is broadcasted. Send NICK_CHANGE notify. */
+  silc_server_send_notify_nick_change(server, SILC_PRIMARY_ROUTE(server),
+                                     SILC_BROADCAST(server), client->id,
+                                     new_id, nick);
 
-       r->res_argv = silc_realloc(r->res_argv, sizeof(*r->res_argv) *
-                                  (r->res_argc + 1));
-       r->res_argv_lens = silc_realloc(r->res_argv_lens, 
-                                       sizeof(*r->res_argv_lens) *
-                                       (r->res_argc + 1));
-       r->res_argv_types = silc_realloc(r->res_argv_types, 
-                                        sizeof(*r->res_argv_types) *
-                                        (r->res_argc + 1));
-       idp = silc_id_payload_encode(entry->id, SILC_ID_CLIENT);
-       r->res_argv[r->res_argc] = silc_calloc(idp->len, 
-                                              sizeof(**r->res_argv));
-       memcpy(r->res_argv[r->res_argc], idp->data, idp->len);
-       r->res_argv_lens[r->res_argc] = idp->len;
-       r->res_argv_types[r->res_argc] = r->res_argc + 3;
-       r->res_argc++;
-       silc_buffer_free(idp);
-
-       entry->resolve_cmd_ident = r->ident;
-       entry->data.status |= SILC_IDLIST_STATUS_RESOLVING;
-       entry->data.status &= ~SILC_IDLIST_STATUS_RESOLVED;
-      }
-    }
-  }
+  /* Check if anyone is watching the old nickname */
+  if (server->server_type == SILC_ROUTER)
+    silc_server_check_watcher_list(server, client, nick,
+                                  SILC_NOTIFY_TYPE_NICK_CHANGE);
 
-  /* Do the resolving */
-  for (i = 0; i < resolve_count; i++) {
-    SilcBuffer res_cmd;
+  oidp = silc_id_payload_encode(client->id, SILC_ID_CLIENT);
 
-    r = &resolve[i];
+  /* Update client entry */
+  silc_idcache_update_by_context(server->local_list->clients, client,
+                                new_id, nickc, TRUE);
+  silc_free(new_id);
+  silc_free(client->nickname);
+  client->nickname = strdup(nick);
 
-    /* Send WHOIS request. We send WHOIS since we're doing the requesting
-       now anyway so make it a good one. */
-    res_cmd = silc_command_payload_encode(SILC_COMMAND_WHOIS,
-                                         r->res_argc, r->res_argv, 
-                                         r->res_argv_lens,
-                                         r->res_argv_types, 
-                                         r->ident);
-    silc_server_packet_send(server, r->router->connection,
-                           SILC_PACKET_COMMAND, cmd->packet->flags,
-                           res_cmd->data, res_cmd->len, FALSE);
+  nidp = silc_id_payload_encode(client->id, SILC_ID_CLIENT);
 
-    /* Reprocess this packet after received reply */
-    silc_server_command_pending(server, SILC_COMMAND_WHOIS, 
-                               r->ident,
-                               silc_server_command_whois,
-                               silc_server_command_dup(cmd));
-    cmd->pending = TRUE;
+  /* Send NICK_CHANGE notify to the client's channels */
+  silc_server_send_notify_on_channels(server, NULL, client,
+                                     SILC_NOTIFY_TYPE_NICK_CHANGE, 3,
+                                     oidp->data, silc_buffer_len(oidp),
+                                     nidp->data, silc_buffer_len(nidp),
+                                     client->nickname,
+                                     strlen(client->nickname));
+
+  /* Check if anyone is watching the new nickname */
+  if (server->server_type == SILC_ROUTER)
+    silc_server_check_watcher_list(server, client, NULL,
+                                  SILC_NOTIFY_TYPE_NICK_CHANGE);
 
-    silc_buffer_free(res_cmd);
-    for (k = 0; k < r->res_argc; k++)
-      silc_free(r->res_argv[k]);
-    silc_free(r->res_argv);
-    silc_free(r->res_argv_lens);
-    silc_free(r->res_argv_types);
-    no_res = FALSE;
-  }
-  silc_free(resolve);
+ send_reply:
+  /* Send the new Client ID as reply command back to client */
+  silc_server_send_command_reply(cmd->server, cmd->sock,
+                                SILC_COMMAND_NICK,
+                                SILC_STATUS_OK, 0, ident, 2,
+                                2, nidp->data, silc_buffer_len(nidp),
+                                3, nick, nick_len);
+  silc_buffer_free(nidp);
+  if (oidp)
+    silc_buffer_free(oidp);
 
-  return no_res;
+ out:
+  silc_server_command_free(cmd);
 }
 
+/* Sends the LIST command reply */
+
 static void
-silc_server_command_whois_send_reply(SilcServerCommandContext cmd,
-                                    SilcClientEntry *clients,
-                                    uint32 clients_count,
-                                    int count,
-                                    const char *nickname,
-                                    SilcClientID **client_ids)
+silc_server_command_list_send_reply(SilcServerCommandContext cmd,
+                                   SilcChannelEntry *lch,
+                                   SilcUInt32 lch_count,
+                                   SilcChannelEntry *gch,
+                                   SilcUInt32 gch_count)
 {
-  SilcServer server = cmd->server;
-  char *tmp;
-  int i, k, len, valid_count;
-  SilcBuffer packet, idp, channels;
-  SilcClientEntry entry;
-  SilcCommandStatus status;
-  uint16 ident = silc_command_get_ident(cmd->payload);
-  char nh[256], uh[256];
-  unsigned char idle[4], mode[4];
-  unsigned char *fingerprint;
-  SilcSocketConnection hsock;
-
-  /* Process only valid clients and ignore those that are not registered. */
-  valid_count = 0;
-  for (i = 0; i < clients_count; i++) {
-    if (clients[i]->data.status & SILC_IDLIST_STATUS_REGISTERED)
-      valid_count++;
+  int i, k;
+  SilcBuffer idp;
+  SilcChannelEntry entry;
+  SilcStatus status;
+  SilcUInt16 ident = silc_command_get_ident(cmd->payload);
+  char *topic;
+  unsigned char usercount[4];
+  SilcUInt32 users;
+  int valid_lcount = 0, valid_rcount = 0;
+
+  for (i = 0; i < lch_count; i++) {
+    if (lch[i]->mode & SILC_CHANNEL_MODE_SECRET)
+      lch[i] = NULL;
     else
-      clients[i] = NULL;
-  }
-
-  if (!valid_count) {
-    /* No valid clients found, send error reply */
-    if (nickname) {
-      silc_server_command_send_status_data(cmd, SILC_COMMAND_WHOIS,
-                                          SILC_STATUS_ERR_NO_SUCH_NICK,
-                                          3, nickname, strlen(nickname));
-    } else if (client_ids && client_ids[0]) {
-      SilcBuffer idp = silc_id_payload_encode(client_ids[0], SILC_ID_CLIENT);
-      silc_server_command_send_status_data(cmd, SILC_COMMAND_WHOIS,
-                                          SILC_STATUS_ERR_NO_SUCH_CLIENT_ID,
-                                          2, idp->data, idp->len);
-      silc_buffer_free(idp);
-    }
+      valid_lcount++;
+  }
+  for (i = 0; i < gch_count; i++) {
+    if (gch[i]->mode & SILC_CHANNEL_MODE_SECRET)
+      gch[i] = NULL;
+    else
+      valid_rcount++;
+  }
+
+  if (!lch_count && !gch_count) {
+    silc_server_command_send_status_reply(cmd, SILC_COMMAND_LIST,
+                                         SILC_STATUS_OK, 0);
     return;
   }
 
-  /* Start processing found clients. */
-  if (valid_count > 1)
+  status = SILC_STATUS_OK;
+  if ((lch_count + gch_count) > 1)
     status = SILC_STATUS_LIST_START;
-  else
-    status = SILC_STATUS_OK;
 
-  for (i = 0, k = 0; i < clients_count; i++) {
-    entry = clients[i];
+  /* Local list */
+  for (i = 0, k = 0; i < lch_count; i++) {
+    entry = lch[i];
     if (!entry)
       continue;
 
     if (k >= 1)
       status = SILC_STATUS_LIST_ITEM;
-    if (valid_count > 1 && k == valid_count - 1)
-      status = SILC_STATUS_LIST_END;
-    if (count && k - 1 == count)
+    if (valid_lcount > 1 && k == valid_lcount - 1 && !valid_rcount)
       status = SILC_STATUS_LIST_END;
 
-    /* Send WHOIS reply */
-    idp = silc_id_payload_encode(entry->id, SILC_ID_CLIENT);
-    tmp = silc_argument_get_first_arg(cmd->args, NULL);
-    
-    memset(uh, 0, sizeof(uh));
-    memset(nh, 0, sizeof(nh));
-    memset(idle, 0, sizeof(idle));
-    
-    strncat(nh, entry->nickname, strlen(entry->nickname));
-    if (!strchr(entry->nickname, '@')) {
-      strncat(nh, "@", 1);
-      if (entry->servername) {
-       strncat(nh, entry->servername, strlen(entry->servername));
-      } else {
-       len = entry->router ? strlen(entry->router->server_name) :
-         strlen(server->server_name);
-       strncat(nh, entry->router ? entry->router->server_name :
-               server->server_name, len);
-      }
-    }
-      
-    strncat(uh, entry->username, strlen(entry->username));
-    if (!strchr(entry->username, '@')) {
-      strncat(uh, "@", 1);
-      hsock = (SilcSocketConnection)entry->connection;
-      len = strlen(hsock->hostname);
-      strncat(uh, hsock->hostname, len);
+    idp = silc_id_payload_encode(entry->id, SILC_ID_CHANNEL);
+
+    if (entry->mode & SILC_CHANNEL_MODE_PRIVATE) {
+      topic = "*private*";
+      memset(usercount, 0, sizeof(usercount));
+    } else {
+      topic = entry->topic;
+      users = silc_hash_table_count(entry->user_list);
+      SILC_PUT32_MSB(users, usercount);
     }
 
-    channels = silc_server_get_client_channel_list(server, entry);
+    /* Send the reply */
+    silc_server_send_command_reply(cmd->server, cmd->sock, SILC_COMMAND_LIST,
+                                  status, 0, ident, 4,
+                                  2, idp->data, silc_buffer_len(idp),
+                                  3, entry->channel_name,
+                                  strlen(entry->channel_name),
+                                  4, topic, topic ? strlen(topic) : 0,
+                                  5, usercount, 4);
+    silc_buffer_free(idp);
+    k++;
+  }
 
-    if (entry->data.fingerprint[0] != 0 && entry->data.fingerprint[1] != 0)
-      fingerprint = entry->data.fingerprint;
-    else
-      fingerprint = NULL;
-      
-    SILC_PUT32_MSB(entry->mode, mode);
+  /* Global list */
+  for (i = 0, k = 0; i < gch_count; i++) {
+    entry = gch[i];
+    if (!entry)
+      continue;
+
+    if (k >= 1)
+      status = SILC_STATUS_LIST_ITEM;
+    if (valid_rcount > 1 && k == valid_rcount - 1)
+      status = SILC_STATUS_LIST_END;
+
+    idp = silc_id_payload_encode(entry->id, SILC_ID_CHANNEL);
 
-    if (entry->connection) {
-      SILC_PUT32_MSB((time(NULL) - entry->data.last_receive), idle);
+    if (entry->mode & SILC_CHANNEL_MODE_PRIVATE) {
+      topic = "*private*";
+      memset(usercount, 0, sizeof(usercount));
+    } else {
+      topic = entry->topic;
+      users = entry->user_count;
+      SILC_PUT32_MSB(users, usercount);
     }
 
-    packet = 
-      silc_command_reply_payload_encode_va(SILC_COMMAND_WHOIS,
-                                          status, ident, 8, 
-                                          2, idp->data, idp->len,
-                                          3, nh, strlen(nh),
-                                          4, uh, strlen(uh),
-                                          5, entry->userinfo, 
-                                          strlen(entry->userinfo),
-                                          6, channels ? channels->data : NULL,
-                                          channels ? channels->len : 0,
-                                          7, mode, 4,
-                                          8, idle, 4,
-                                          9, fingerprint,
-                                          fingerprint ? 20 : 0);
-
-    silc_server_packet_send(server, cmd->sock, SILC_PACKET_COMMAND_REPLY,
-                           0, packet->data, packet->len, FALSE);
-    
-    silc_buffer_free(packet);
+    /* Send the reply */
+    silc_server_send_command_reply(cmd->server, cmd->sock, SILC_COMMAND_LIST,
+                                  status, 0, ident, 4,
+                                  2, idp->data, silc_buffer_len(idp),
+                                  3, entry->channel_name,
+                                  strlen(entry->channel_name),
+                                  4, topic, topic ? strlen(topic) : 0,
+                                  5, usercount, 4);
     silc_buffer_free(idp);
-    if (channels)
-      silc_buffer_free(channels);
-
     k++;
   }
 }
 
-static void 
-silc_server_command_whois_send_router(SilcServerCommandContext cmd)
-{
-  SilcServer server = cmd->server;
-  SilcBuffer tmpbuf;
-  uint16 old_ident;
-
-  old_ident = silc_command_get_ident(cmd->payload);
-  silc_command_set_ident(cmd->payload, ++server->cmd_ident);
-  tmpbuf = silc_command_payload_encode_payload(cmd->payload);
-
-  /* Send WHOIS command to our router */
-  silc_server_packet_send(server, (SilcSocketConnection)
-                         server->router->connection,
-                         SILC_PACKET_COMMAND, cmd->packet->flags,
-                         tmpbuf->data, tmpbuf->len, TRUE);
-
-  /* Reprocess this packet after received reply from router */
-  silc_server_command_pending(server, SILC_COMMAND_WHOIS, 
-                             silc_command_get_ident(cmd->payload),
-                             silc_server_command_whois,
-                             silc_server_command_dup(cmd));
-  cmd->pending = TRUE;
-  silc_command_set_ident(cmd->payload, old_ident);
-  silc_buffer_free(tmpbuf);
-}
+/* Server side of LIST command. This lists the channel of the requested
+   server. Secret channels are not listed. */
 
-static int
-silc_server_command_whois_process(SilcServerCommandContext cmd)
+SILC_SERVER_CMD_FUNC(list)
 {
+  SilcServerCommandContext cmd = (SilcServerCommandContext)context;
   SilcServer server = cmd->server;
-  char *nick = NULL, *server_name = NULL;
-  int count = 0;
-  SilcClientEntry *clients = NULL, entry;
-  SilcClientID **client_id = NULL;
-  uint32 client_id_count = 0, clients_count = 0;
-  int i, ret = 0;
-  bool check_global = FALSE;
-
-  /* Parse the whois request */
-  if (!silc_server_command_whois_parse(cmd, &client_id, &client_id_count, 
-                                      &nick, &server_name, &count,
-                                      SILC_COMMAND_WHOIS))
-    return 0;
-
-  /* Send the WHOIS request to the router only if it included nickname.
-     Since nicknames can be expanded into many clients we need to send it
-     to router.  If the WHOIS included only client ID's we will check them
-     first locally since we just might have them. */
-  if (nick && !client_id_count && cmd->sock->type == SILC_SOCKET_TYPE_CLIENT &&
-      server->server_type == SILC_SERVER && !cmd->pending &&
+  SilcID id;
+  SilcChannelID *channel_id = NULL;
+  SilcChannelEntry *lchannels = NULL, *gchannels = NULL;
+  SilcUInt32 lch_count = 0, gch_count = 0;
+
+  SILC_SERVER_COMMAND_CHECK(SILC_COMMAND_LIST, cmd, 0, 1);
+
+  /* If we are normal server, send the command to router, since we
+     want to know all channels in the network. */
+  if (!cmd->pending && server->server_type != SILC_ROUTER &&
       !server->standalone) {
-    silc_server_command_whois_send_router(cmd);
-    ret = -1;
+    SilcBuffer tmpbuf;
+    SilcUInt16 old_ident;
+
+    /* Statistics */
+    cmd->server->stat.commands_sent++;
+
+    old_ident = silc_command_get_ident(cmd->payload);
+    silc_command_set_ident(cmd->payload, ++server->cmd_ident);
+    tmpbuf = silc_command_payload_encode_payload(cmd->payload);
+    silc_server_packet_send(server, SILC_PRIMARY_ROUTE(server),
+                           SILC_PACKET_COMMAND, cmd->packet->flags,
+                           tmpbuf->data, silc_buffer_len(tmpbuf));
+
+    /* Reprocess this packet after received reply from router */
+    silc_server_command_pending(server, SILC_COMMAND_LIST,
+                               silc_command_get_ident(cmd->payload),
+                               silc_server_command_list,
+                               silc_server_command_dup(cmd));
+    cmd->pending = TRUE;
+    silc_command_set_ident(cmd->payload, old_ident);
+    silc_buffer_free(tmpbuf);
     goto out;
   }
 
-  if (cmd->sock->type == SILC_SOCKET_TYPE_CLIENT)
-    check_global = TRUE;
-  else if (server->server_type != SILC_SERVER)
-    check_global = TRUE;
-
-  /* Get all clients matching that ID or nickname from local list */
-  if (client_id_count) {
-    /* Check all Client ID's received in the command packet */
-    for (i = 0; i < client_id_count; i++) {
-      entry = silc_idlist_find_client_by_id(server->local_list, 
-                                           client_id[i], TRUE, NULL);
-      if (!entry && check_global)
-       entry = silc_idlist_find_client_by_id(server->global_list, 
-                                             client_id[i], TRUE, NULL);
-      if (entry) {
-       clients = silc_realloc(clients, sizeof(*clients) * 
-                              (clients_count + 1));
-       clients[clients_count++] = entry;
-      } else {
-       /* If we are normal server and did not send the request first to router
-          do it now, since we do not have the Client ID information. */
-       if (cmd->sock->type == SILC_SOCKET_TYPE_CLIENT &&
-           server->server_type == SILC_SERVER && !cmd->pending && 
-           !server->standalone) {
-         silc_server_command_whois_send_router(cmd);
-         ret = -1;
-         goto out;
-       }
-      }
-    }
-  } else {
-    /* Find by nickname */
-    if (!silc_idlist_get_clients_by_hash(server->local_list, 
-                                        nick, server->md5hash,
-                                        &clients, &clients_count))
-      silc_idlist_get_clients_by_nickname(server->local_list, 
-                                         nick, server_name,
-                                         &clients, &clients_count);
-    if (check_global) {
-      if (!silc_idlist_get_clients_by_hash(server->global_list, 
-                                          nick, server->md5hash,
-                                          &clients, &clients_count))
-       silc_idlist_get_clients_by_nickname(server->global_list, 
-                                           nick, server_name,
-                                           &clients, &clients_count);
-    }
-  }
-  
-  if (!clients) {
-    /* If we are normal server and did not send the request first to router
-       do it now, since we do not have the information. */
-    if (cmd->sock->type == SILC_SOCKET_TYPE_CLIENT &&
-       server->server_type == SILC_SERVER && !cmd->pending && 
-       !server->standalone) {
-      silc_server_command_whois_send_router(cmd);
-      ret = -1;
-      goto out;
-    }
+  /* Get Channel ID */
+  if (silc_argument_get_decoded(cmd->args, 1, SILC_ARGUMENT_ID, &id, NULL))
+    channel_id = SILC_ID_GET_ID(id);
 
-    /* Such client(s) really does not exist in the SILC network. */
-    if (!client_id_count) {
-      silc_server_command_send_status_data(cmd, SILC_COMMAND_WHOIS,
-                                          SILC_STATUS_ERR_NO_SUCH_NICK,
-                                          3, nick, strlen(nick));
-    } else {
-      SilcBuffer idp = silc_id_payload_encode(client_id[0], SILC_ID_CLIENT);
-      silc_server_command_send_status_data(cmd, SILC_COMMAND_WHOIS,
-                                          SILC_STATUS_ERR_NO_SUCH_CLIENT_ID,
-                                          2, idp->data, idp->len);
-      silc_buffer_free(idp);
-    }
-    goto out;
-  }
+  /* Get the channels from local list */
+  lchannels = silc_idlist_get_channels(server->local_list, channel_id,
+                                      &lch_count);
 
-  /* Router always finds the client entry if it exists in the SILC network.
-     However, it might be incomplete entry and does not include all the
-     mandatory fields that WHOIS command reply requires. Check for these and
-     make query from the server who owns the client if some fields are 
-     missing. */
-  if (!silc_server_command_whois_check(cmd, clients, clients_count)) {
-    ret = -1;
-    goto out;
-  }
+  /* Get the channels from global list */
+  gchannels = silc_idlist_get_channels(server->global_list, channel_id,
+                                      &gch_count);
 
-  /* Send the command reply */
-  silc_server_command_whois_send_reply(cmd, clients, clients_count,
-                                      count, nick, client_id);
+  /* Send the reply */
+  silc_server_command_list_send_reply(cmd, lchannels, lch_count,
+                                     gchannels, gch_count);
 
- out:
-  if (client_id_count) {
-    for (i = 0; i < client_id_count; i++)
-      silc_free(client_id[i]);
-    silc_free(client_id);
-  }
-  silc_free(clients);
-  silc_free(nick);
-  silc_free(server_name);
+  silc_free(lchannels);
+  silc_free(gchannels);
 
-  return ret;
+ out:
+  silc_server_command_free(cmd);
 }
 
-/* Server side of command WHOIS. Processes user's query and sends found 
-   results as command replies back to the client. */
+/* Server side of TOPIC command. Sets topic for channel and/or returns
+   current topic to client. */
 
-SILC_SERVER_CMD_FUNC(whois)
+SILC_SERVER_CMD_FUNC(topic)
 {
   SilcServerCommandContext cmd = (SilcServerCommandContext)context;
-  int ret = 0;
-
-  SILC_SERVER_COMMAND_CHECK(SILC_COMMAND_WHOIS, cmd, 1, 3328);
-
-  ret = silc_server_command_whois_process(cmd);
-  silc_server_command_free(cmd);
-}
-
-/******************************************************************************
+  SilcServer server = cmd->server;
+  SilcClientEntry client = silc_packet_get_context(cmd->sock);
+  SilcID id;
+  SilcChannelEntry channel;
+  SilcChannelClientEntry chl;
+  SilcBuffer idp;
+  unsigned char *tmp;
+  SilcUInt32 argc, tmp_len;
+  SilcUInt16 ident = silc_command_get_ident(cmd->payload);
 
-                              WHOWAS Functions
+  if (client->data.conn_type != SILC_CONN_CLIENT || !client)
+    goto out;
 
-******************************************************************************/
+  SILC_SERVER_COMMAND_CHECK(SILC_COMMAND_TOPIC, cmd, 1, 2);
 
-static int
-silc_server_command_whowas_parse(SilcServerCommandContext cmd,
-                                char **nickname,
-                                char **server_name,
-                                int *count)
-{
-  unsigned char *tmp;
-  uint32 len;
+  argc = silc_argument_get_arg_num(cmd->args);
 
-  tmp = silc_argument_get_arg_type(cmd->args, 1, &len);
-  if (!tmp) {
-    silc_server_command_send_status_reply(cmd, SILC_COMMAND_WHOWAS,
-                                         SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
-    return FALSE;
+  /* Get Channel ID */
+  if (!silc_argument_get_decoded(cmd->args, 1, SILC_ARGUMENT_ID, &id, NULL)) {
+    silc_server_command_send_status_reply(cmd, SILC_COMMAND_TOPIC,
+                                         SILC_STATUS_ERR_NO_CHANNEL_ID, 0);
+    goto out;
   }
 
-  /* Get the nickname@server string and parse it. */
-  silc_parse_userfqdn(tmp, nickname, server_name);
-
-  /* Get the max count of reply messages allowed */
-  tmp = silc_argument_get_arg_type(cmd->args, 2, NULL);
-  if (tmp)
-    *count = atoi(tmp);
-  else
-    *count = 0;
+  /* Check whether the channel exists */
+  channel = silc_idlist_find_channel_by_id(server->local_list,
+                                          SILC_ID_GET_ID(id), NULL);
+  if (!channel) {
+    channel = silc_idlist_find_channel_by_id(server->global_list,
+                                            SILC_ID_GET_ID(id), NULL);
+    if (!channel) {
+      tmp = silc_argument_get_arg_type(cmd->args, 1, &tmp_len);
+      silc_server_command_send_status_data(cmd, SILC_COMMAND_TOPIC,
+                                          SILC_STATUS_ERR_NO_SUCH_CHANNEL_ID,
+                                          0, 2, tmp, tmp_len);
+      goto out;
+    }
+  }
 
-  return TRUE;
-}
+  if (argc > 1) {
+    /* Get the topic */
+    tmp = silc_argument_get_arg_type(cmd->args, 2, &tmp_len);
+    if (!tmp) {
+      silc_server_command_send_status_reply(cmd, SILC_COMMAND_TOPIC,
+                                           SILC_STATUS_ERR_NOT_ENOUGH_PARAMS,
+                                           0);
+      goto out;
+    }
+    if (tmp_len > 256) {
+      tmp_len = 256;
+      tmp[tmp_len - 1] = '\0';
+    }
 
-static char
-silc_server_command_whowas_check(SilcServerCommandContext cmd,
-                                SilcClientEntry *clients,
-                                uint32 clients_count)
-{
-  SilcServer server = cmd->server;
-  int i;
-  SilcClientEntry entry;
+    if (!silc_utf8_valid(tmp, tmp_len)) {
+      silc_server_command_send_status_reply(cmd, SILC_COMMAND_TOPIC,
+                                           SILC_STATUS_ERR_NOT_ENOUGH_PARAMS,
+                                           0);
+      goto out;
+    }
 
-  for (i = 0; i < clients_count; i++) {
-    entry = clients[i];
+    /* See whether the client is on channel and has rights to change topic */
+    if (!silc_server_client_on_channel(client, channel, &chl)) {
+      tmp = silc_argument_get_arg_type(cmd->args, 1, &tmp_len);
+      silc_server_command_send_status_data(cmd, SILC_COMMAND_TOPIC,
+                                          SILC_STATUS_ERR_NOT_ON_CHANNEL,
+                                          0, 2, tmp, tmp_len);
+      goto out;
+    }
 
-    if (!entry->nickname || !entry->username) {
-      SilcBuffer tmpbuf;
-      uint16 old_ident;
+    if (channel->mode & SILC_CHANNEL_MODE_TOPIC &&
+       !(chl->mode & SILC_CHANNEL_UMODE_CHANOP) &&
+       !(chl->mode & SILC_CHANNEL_UMODE_CHANFO)) {
+      tmp = silc_argument_get_arg_type(cmd->args, 1, &tmp_len);
+      silc_server_command_send_status_data(cmd, SILC_COMMAND_TOPIC,
+                                          SILC_STATUS_ERR_NO_CHANNEL_PRIV,
+                                          0, 2, tmp, tmp_len);
+      goto out;
+    }
 
-      if (!entry->router)
-       continue;
-      
-      old_ident = silc_command_get_ident(cmd->payload);
-      silc_command_set_ident(cmd->payload, ++server->cmd_ident);
-      tmpbuf = silc_command_payload_encode_payload(cmd->payload);
+    if (!channel->topic || strcmp(channel->topic, tmp)) {
+      /* Set the topic for channel */
+      silc_free(channel->topic);
+      channel->topic = strdup(tmp);
 
-      /* Send WHOWAS command */
-      silc_server_packet_send(server, entry->router->connection,
-                             SILC_PACKET_COMMAND, cmd->packet->flags,
-                             tmpbuf->data, tmpbuf->len, TRUE);
-      
-      /* Reprocess this packet after received reply */
-      silc_server_command_pending(server, SILC_COMMAND_WHOWAS, 
-                                 silc_command_get_ident(cmd->payload),
-                                 silc_server_command_whowas, 
-                                 silc_server_command_dup(cmd));
-      cmd->pending = TRUE;
-      silc_command_set_ident(cmd->payload, old_ident);
+      /* Send TOPIC_SET notify type to the network */
+      silc_server_send_notify_topic_set(server, SILC_PRIMARY_ROUTE(server),
+                                       SILC_BROADCAST(server), channel,
+                                       client->id, SILC_ID_CLIENT,
+                                       channel->topic);
 
-      silc_buffer_free(tmpbuf);
-      return FALSE;
+      /* Send notify about topic change to all clients on the channel */
+      idp = silc_id_payload_encode(client->id, SILC_ID_CLIENT);
+      silc_server_send_notify_to_channel(server, NULL, channel, FALSE, TRUE,
+                                        SILC_NOTIFY_TYPE_TOPIC_SET, 2,
+                                        idp->data, silc_buffer_len(idp),
+                                        channel->topic,
+                                        strlen(channel->topic));
+      silc_buffer_free(idp);
     }
   }
 
-  return TRUE;
+  /* Send the topic to client as reply packet */
+  idp = silc_id_payload_encode(SILC_ID_GET_ID(id), SILC_ID_CHANNEL);
+  silc_server_send_command_reply(cmd->server, cmd->sock, SILC_COMMAND_TOPIC,
+                                SILC_STATUS_OK, 0, ident, 2,
+                                2, idp->data, silc_buffer_len(idp),
+                                3, channel->topic,
+                                channel->topic ?
+                                strlen(channel->topic) : 0);
+  silc_buffer_free(idp);
+
+ out:
+  silc_server_command_free(cmd);
 }
 
-static void
-silc_server_command_whowas_send_reply(SilcServerCommandContext cmd,
-                                     SilcClientEntry *clients,
-                                     uint32 clients_count)
+/* Server side of INVITE command. Invites some client to join some channel.
+   This command is also used to manage the invite list of the channel. */
+
+SILC_SERVER_CMD_FUNC(invite)
 {
+  SilcServerCommandContext cmd = (SilcServerCommandContext)context;
   SilcServer server = cmd->server;
-  char *tmp;
-  int i, k, count = 0, len;
-  SilcBuffer packet, idp;
-  SilcClientEntry entry = NULL;
-  SilcCommandStatus status;
-  uint16 ident = silc_command_get_ident(cmd->payload);
-  char nh[256], uh[256];
-  int valid_count;
+  SilcPacketStream sock = cmd->sock, dest_sock;
+  SilcChannelClientEntry chl;
+  SilcClientEntry sender, dest;
+  SilcChannelEntry channel;
+  SilcID id, id2;
+  SilcIDListData idata;
+  SilcArgumentPayload args;
+  SilcHashTableList htl;
+  SilcBuffer list, tmp2;
+  SilcBufferStruct alist;
+  unsigned char *tmp, *atype = NULL;
+  SilcUInt32 len, len2, ttype;
+  void *type;
+  SilcUInt16 argc = 0, ident = silc_command_get_ident(cmd->payload);
 
-  status = SILC_STATUS_OK;
+  SILC_SERVER_COMMAND_CHECK(SILC_COMMAND_INVITE, cmd, 1, 4);
 
-  /* Process only entries that are not registered anymore. */
-  valid_count = 0;
-  for (i = 0; i < clients_count; i++) {
-    if (clients[i]->data.status & SILC_IDLIST_STATUS_REGISTERED)
-      clients[i] = NULL;
-    else
-      valid_count++;
+  /* Get Channel ID */
+  if (!silc_argument_get_decoded(cmd->args, 1, SILC_ARGUMENT_ID, &id, NULL)) {
+    silc_server_command_send_status_reply(cmd, SILC_COMMAND_INVITE,
+                                         SILC_STATUS_ERR_NO_CHANNEL_ID, 0);
+    goto out;
   }
 
-  if (!valid_count) {
-    /* No valid entries found at all, just send error */
-    unsigned char *tmp;
-    
-    tmp = silc_argument_get_arg_type(cmd->args, 1, NULL);
-    if (tmp)
-      silc_server_command_send_status_data(cmd, SILC_COMMAND_WHOWAS,
-                                          SILC_STATUS_ERR_NO_SUCH_NICK,
-                                          3, tmp, strlen(tmp));
-    return;
+  /* Get the channel entry */
+  channel = silc_idlist_find_channel_by_id(server->local_list,
+                                          SILC_ID_GET_ID(id), NULL);
+  if (!channel) {
+    channel = silc_idlist_find_channel_by_id(server->global_list,
+                                            SILC_ID_GET_ID(id), NULL);
+    if (!channel) {
+      tmp = silc_argument_get_arg_type(cmd->args, 1, &len);
+      silc_server_command_send_status_data(cmd, SILC_COMMAND_INVITE,
+                                          SILC_STATUS_ERR_NO_SUCH_CHANNEL_ID,
+                                          0, 2, tmp, len);
+      goto out;
+    }
   }
 
-  if (valid_count > 1)
-    status = SILC_STATUS_LIST_START;
+  /* Check whether the sender of this command is on the channel. */
+  sender = silc_packet_get_context(sock);
+  if (!sender || !silc_server_client_on_channel(sender, channel, &chl)) {
+    tmp = silc_argument_get_arg_type(cmd->args, 1, &len);
+    silc_server_command_send_status_data(cmd, SILC_COMMAND_INVITE,
+                                        SILC_STATUS_ERR_NOT_ON_CHANNEL, 0,
+                                        2, tmp, len);
+    goto out;
+  }
 
-  for (i = 0, k = 0; i < clients_count; i++) {
-    entry = clients[i];
-    if (!entry)
-      continue;
+  /* Check whether the channel is invite-only channel. If yes then the
+     sender of this command must be at least channel operator. */
+  if (channel->mode & SILC_CHANNEL_MODE_INVITE &&
+      !(chl->mode & SILC_CHANNEL_UMODE_CHANOP) &&
+      !(chl->mode & SILC_CHANNEL_UMODE_CHANFO)) {
+    tmp = silc_argument_get_arg_type(cmd->args, 1, &len);
+    silc_server_command_send_status_data(cmd, SILC_COMMAND_INVITE,
+                                        SILC_STATUS_ERR_NO_CHANNEL_PRIV,
+                                        0, 2, tmp, len);
+    goto out;
+  }
 
-    if (k >= 1)
-      status = SILC_STATUS_LIST_ITEM;
-    if (valid_count > 1 && k == valid_count - 1)
-      status = SILC_STATUS_LIST_END;
-    if (count && k - 1 == count)
-      status = SILC_STATUS_LIST_END;
-    if (count && k - 1 > count)
-      break;
+  /* Get destination client ID */
+  if (silc_argument_get_decoded(cmd->args, 2, SILC_ARGUMENT_ID, &id2, NULL)) {
+    SilcBool resolve;
 
-    /* Send WHOWAS reply */
-    idp = silc_id_payload_encode(entry->id, SILC_ID_CLIENT);
-    tmp = silc_argument_get_first_arg(cmd->args, NULL);
-    memset(uh, 0, sizeof(uh));
-    memset(nh, 0, sizeof(nh));
-
-    strncat(nh, entry->nickname, strlen(entry->nickname));
-    if (!strchr(entry->nickname, '@')) {
-      strncat(nh, "@", 1);
-      if (entry->servername) {
-       strncat(nh, entry->servername, strlen(entry->servername));
-      } else {
-       len = entry->router ? strlen(entry->router->server_name) :
-         strlen(server->server_name);
-       strncat(nh, entry->router ? entry->router->server_name :
-               server->server_name, len);
+    /* Get the client entry */
+    dest = silc_server_query_client(server, SILC_ID_GET_ID(id2),
+                                   FALSE, &resolve);
+    if (!dest) {
+      if (server->server_type != SILC_SERVER || !resolve || cmd->pending) {
+       tmp = silc_argument_get_arg_type(cmd->args, 2, &len);
+       silc_server_command_send_status_data(
+                                       cmd, SILC_COMMAND_INVITE,
+                                       SILC_STATUS_ERR_NO_SUCH_CLIENT_ID, 0,
+                                       2, tmp, len);
+       goto out;
       }
+
+      /* The client info is being resolved. Reprocess this packet after
+        receiving the reply to the query. */
+      silc_server_command_pending(server, SILC_COMMAND_WHOIS,
+                                 server->cmd_ident,
+                                 silc_server_command_invite,
+                                 silc_server_command_dup(cmd));
+      cmd->pending = TRUE;
+      goto out;
     }
-      
-    strncat(uh, entry->username, strlen(entry->username));
-    if (!strchr(entry->username, '@')) {
-      strncat(uh, "@", 1);
-      strcat(uh, "*private*");
-    }
-      
-    packet = 
-      silc_command_reply_payload_encode_va(SILC_COMMAND_WHOWAS,
-                                          status, ident, 4, 
-                                          2, idp->data, idp->len,
-                                          3, nh, strlen(nh),
-                                          4, uh, strlen(uh),
-                                          5, entry->userinfo, 
-                                          entry->userinfo ? 
-                                          strlen(entry->userinfo) : 0);
-    silc_server_packet_send(server, cmd->sock, SILC_PACKET_COMMAND_REPLY,
-                           0, packet->data, packet->len, FALSE);
-    
-    silc_buffer_free(packet);
-    silc_buffer_free(idp);
 
-    k++;
-  }
-}
+    /* Check whether the requested client is already on the channel. */
+    if (silc_server_client_on_channel(dest, channel, NULL)) {
+      tmp = silc_argument_get_arg_type(cmd->args, 2, &len);
+      atype = silc_argument_get_arg_type(cmd->args, 1, &len2);
+      silc_server_command_send_status_data2(cmd, SILC_COMMAND_INVITE,
+                                           SILC_STATUS_ERR_USER_ON_CHANNEL,
+                                           0, 2, tmp, len,
+                                           3, atype, len2);
+      goto out;
+    }
 
-static int
-silc_server_command_whowas_process(SilcServerCommandContext cmd)
-{
-  SilcServer server = cmd->server;
-  char *nick = NULL, *server_name = NULL;
-  int count = 0;
-  SilcClientEntry *clients = NULL;
-  uint32 clients_count = 0;
-  int ret = 0;
-  bool check_global = FALSE;
-
-  /* Protocol dictates that we must always send the received WHOWAS request
-     to our router if we are normal server, so let's do it now unless we
-     are standalone. We will not send any replies to the client until we
-     have received reply from the router. */
-  if (cmd->sock->type == SILC_SOCKET_TYPE_CLIENT &&
-      server->server_type == SILC_SERVER && !cmd->pending && 
-      !server->standalone) {
-    SilcBuffer tmpbuf;
-    uint16 old_ident;
+    /* Get route to the client */
+    dest_sock = silc_server_get_client_route(server, NULL, 0,
+                                            SILC_ID_GET_ID(id2),
+                                            &idata, NULL);
+    if (!dest_sock) {
+      tmp = silc_argument_get_arg_type(cmd->args, 2, &len);
+      silc_server_command_send_status_data(cmd, SILC_COMMAND_INVITE,
+                                          SILC_STATUS_ERR_NO_SUCH_CLIENT_ID,
+                                          0, 2, tmp, len);
+      goto out;
+    }
 
-    old_ident = silc_command_get_ident(cmd->payload);
-    silc_command_set_ident(cmd->payload, ++server->cmd_ident);
-    tmpbuf = silc_command_payload_encode_payload(cmd->payload);
+    /* Add the client to the invite list */
 
-    /* Send WHOWAS command to our router */
-    silc_server_packet_send(server, (SilcSocketConnection)
-                           server->router->connection,
-                           SILC_PACKET_COMMAND, cmd->packet->flags,
-                           tmpbuf->data, tmpbuf->len, TRUE);
+    /* Allocate hash table for invite list if it doesn't exist yet */
+    if (!channel->invite_list)
+      channel->invite_list =
+       silc_hash_table_alloc(0, silc_hash_ptr,
+                             NULL, NULL, NULL,
+                             silc_server_inviteban_destruct, channel, TRUE);
+
+    /* Check if the ID is in the list already */
+    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 (SILC_PTR_TO_32(type) == 3 && !memcmp(tmp2->data, tmp, len)) {
+       tmp = NULL;
+       break;
+      }
+    }
+    silc_hash_table_list_reset(&htl);
 
-    /* Reprocess this packet after received reply from router */
-    silc_server_command_pending(server, SILC_COMMAND_WHOWAS, 
-                               silc_command_get_ident(cmd->payload),
-                               silc_server_command_whowas,
-                               silc_server_command_dup(cmd));
-    cmd->pending = TRUE;
-    silc_command_set_ident(cmd->payload, old_ident);
+    /* Add new Client ID to invite list */
+    if (tmp) {
+      list = silc_buffer_alloc_size(len);
+      silc_buffer_put(list, tmp, len);
+      silc_hash_table_add(channel->invite_list, (void *)3, list);
+    }
 
-    silc_buffer_free(tmpbuf);
-    ret = -1;
-    goto out;
+    if (!(dest->mode & SILC_UMODE_BLOCK_INVITE)) {
+      /* Send notify to the client that is invited to the channel */
+      SilcBuffer idp, idp2;
+      idp = silc_id_payload_encode(SILC_ID_GET_ID(id), SILC_ID_CHANNEL);
+      idp2 = silc_id_payload_encode(sender->id, SILC_ID_CLIENT);
+      silc_server_send_notify_dest(server, dest_sock, FALSE,
+                                  SILC_ID_GET_ID(id2), SILC_ID_CLIENT,
+                                  SILC_NOTIFY_TYPE_INVITE, 3,
+                                  idp->data, silc_buffer_len(idp),
+                                  channel->channel_name,
+                                  strlen(channel->channel_name),
+                                  idp2->data, silc_buffer_len(idp2));
+      silc_buffer_free(idp);
+      silc_buffer_free(idp2);
+    }
   }
 
-  /* We are ready to process the command request. Let's search for the
-     requested client and send reply to the requesting client. */
-
-  if (cmd->sock->type == SILC_SOCKET_TYPE_CLIENT)
-    check_global = TRUE;
-  else if (server->server_type != SILC_SERVER)
-    check_global = TRUE;
-
-  /* Parse the whowas request */
-  if (!silc_server_command_whowas_parse(cmd, &nick, &server_name, &count))
-    return 0;
-
-  /* Get all clients matching that nickname from local list */
-  if (!silc_idlist_get_clients_by_nickname(server->local_list, 
-                                          nick, server_name,
-                                          &clients, &clients_count))
-    silc_idlist_get_clients_by_hash(server->local_list, 
-                                   nick, server->md5hash,
-                                   &clients, &clients_count);
-  
-  /* Check global list as well */
-  if (check_global) {
-    if (!silc_idlist_get_clients_by_nickname(server->global_list, 
-                                            nick, server_name,
-                                            &clients, &clients_count))
-      silc_idlist_get_clients_by_hash(server->global_list, 
-                                     nick, server->md5hash,
-                                     &clients, &clients_count);
-  }
-  
-  if (!clients) {
-    /* Such a client really does not exist in the SILC network. */
-    silc_server_command_send_status_data(cmd, SILC_COMMAND_WHOWAS,
-                                        SILC_STATUS_ERR_NO_SUCH_NICK,
-                                        3, nick, strlen(nick));
-    goto out;
-  }
+  /* Get the invite information */
+  tmp = silc_argument_get_arg_type(cmd->args, 4, &len2);
+  if (tmp && len2 > 2) {
+    /* Parse the arguments to see they are constructed correctly */
+    SILC_GET16_MSB(argc, tmp);
+    args = silc_argument_payload_parse(tmp + 2, len2 - 2, argc);
+    if (!args) {
+      silc_server_command_send_status_reply(cmd, SILC_COMMAND_INVITE,
+                                           SILC_STATUS_ERR_NOT_ENOUGH_PARAMS,
+                                           0);
+      goto out;
+    }
 
-  if (!silc_server_command_whowas_check(cmd, clients, clients_count)) {
-    ret = -1;
-    goto out;
+    /* Get the type of action */
+    atype = silc_argument_get_arg_type(cmd->args, 3, &len);
+    if (atype && len == 1) {
+      if (atype[0] == 0x00) {
+       /* Allocate hash table for invite list if it doesn't exist yet */
+       if (!channel->invite_list)
+         channel->invite_list =
+           silc_hash_table_alloc(0, silc_hash_ptr,
+                                 NULL, NULL, NULL,
+                                 silc_server_inviteban_destruct, channel,
+                                 TRUE);
+
+       /* Check for resource limit */
+       if (silc_hash_table_count(channel->invite_list) > 64) {
+         silc_server_command_send_status_reply(cmd, SILC_COMMAND_INVITE,
+                                               SILC_STATUS_ERR_RESOURCE_LIMIT,
+                                               0);
+         goto out;
+       }
+      }
+
+      /* Now add or delete the information. */
+      if (!silc_server_inviteban_process(server, channel->invite_list,
+                                        (SilcUInt8)atype[0], args)) {
+       silc_server_command_send_status_reply(
+                                   cmd, SILC_COMMAND_INVITE,
+                                   SILC_STATUS_ERR_NOT_ENOUGH_PARAMS,
+                                   0);
+       goto out;
+      }
+    }
+    silc_argument_payload_free(args);
+  }
+
+  /* Encode invite list */
+  list = NULL;
+  if (channel->invite_list && silc_hash_table_count(channel->invite_list)) {
+    list = silc_buffer_alloc_size(2);
+    silc_buffer_format(list,
+                      SILC_STR_UI_SHORT(silc_hash_table_count(
+                                         channel->invite_list)),
+                      SILC_STR_END);
+    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),
+                                             SILC_PTR_TO_32(type));
+    silc_hash_table_list_reset(&htl);
+  }
+
+  /* The notify is sent to local servers (not clients), and to network. */
+  if (atype && tmp && len2) {
+    silc_buffer_set(&alist, tmp, len2);
+
+    /* Send to local servers if we are router */
+    if (server->server_type == SILC_ROUTER) {
+      SilcBuffer idp, idp2;
+      idp = silc_id_payload_encode(SILC_ID_GET_ID(id), SILC_ID_CHANNEL);
+      idp2 = silc_id_payload_encode(sender->id, SILC_ID_CLIENT);
+      silc_server_send_notify_to_channel(server, NULL, channel, FALSE, FALSE,
+                                         SILC_NOTIFY_TYPE_INVITE, 5,
+                                        idp->data, silc_buffer_len(idp),
+                                        channel->channel_name,
+                                        strlen(channel->channel_name),
+                                        idp2->data, silc_buffer_len(idp2),
+                                        atype, 1,
+                                        tmp ? alist.data : NULL,
+                                        tmp ? silc_buffer_len(&alist) : 0);
+      silc_buffer_free(idp);
+      silc_buffer_free(idp2);
+    }
+
+    /* Send to network */
+    silc_server_send_notify_invite(server, SILC_PRIMARY_ROUTE(server),
+                                  SILC_BROADCAST(server), channel,
+                                  sender->id, atype,
+                                  tmp ? &alist : NULL);
   }
 
-  /* Send the command reply to the client */
-  silc_server_command_whowas_send_reply(cmd, clients, clients_count);
+  /* Send invite list back only if the list was modified, or no arguments
+     was given. */
+  ttype = 0;
+  argc = silc_argument_get_arg_num(cmd->args);
+  if (argc == 1)
+    ttype = 1;
+  if (silc_argument_get_arg_type(cmd->args, 3, &len))
+    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, ttype && list ?
+                                list->data : NULL,
+                                ttype && list ? silc_buffer_len(list) : 0);
+  silc_buffer_free(list);
 
  out:
-  silc_free(clients);
-  silc_free(nick);
-  silc_free(server_name);
-  return ret;
+  silc_server_command_free(cmd);
 }
 
-/* Server side of command WHOWAS. */
+typedef struct {
+  SilcPacketStream sock;
+  char *signoff;
+} *QuitInternal;
 
-SILC_SERVER_CMD_FUNC(whowas)
+/* Quits connection to client. This gets called if client won't
+   close the connection even when it has issued QUIT command. */
+
+SILC_TASK_CALLBACK(silc_server_command_quit_cb)
 {
-  SilcServerCommandContext cmd = (SilcServerCommandContext)context;
-  int ret = 0;
+  SilcServer server = app_context;
+  QuitInternal q = (QuitInternal)context;
+  SilcClientEntry client = silc_packet_get_context(q->sock);
 
-  SILC_SERVER_COMMAND_CHECK(SILC_COMMAND_WHOWAS, cmd, 1, 2);
+  if (client) {
+    /* Free all client specific data, such as client entry and entires
+       on channels this client may be on. */
+    silc_server_free_sock_user_data(server, q->sock, q->signoff);
+    silc_server_close_connection(server, q->sock);
+  }
 
-  ret = silc_server_command_whowas_process(cmd);
-  silc_server_command_free(cmd);
+  silc_packet_stream_unref(q->sock);
+  silc_free(q->signoff);
+  silc_free(q);
 }
 
-/******************************************************************************
-
-                              IDENTIFY Functions
-
-******************************************************************************/
+/* Quits SILC session. This is the normal way to disconnect client. */
 
-static void 
-silc_server_command_identify_send_router(SilcServerCommandContext cmd)
+SILC_SERVER_CMD_FUNC(quit)
 {
+  SilcServerCommandContext cmd = (SilcServerCommandContext)context;
   SilcServer server = cmd->server;
-  SilcBuffer tmpbuf;
-  uint16 old_ident;
-
-  old_ident = silc_command_get_ident(cmd->payload);
-  silc_command_set_ident(cmd->payload, ++server->cmd_ident);
-  tmpbuf = silc_command_payload_encode_payload(cmd->payload);
-
-  /* Send IDENTIFY command to our router */
-  silc_server_packet_send(server, (SilcSocketConnection)
-                         server->router->connection,
-                         SILC_PACKET_COMMAND, cmd->packet->flags,
-                         tmpbuf->data, tmpbuf->len, TRUE);
-
-  /* Reprocess this packet after received reply from router */
-  silc_server_command_pending(server, SILC_COMMAND_IDENTIFY, 
-                             silc_command_get_ident(cmd->payload),
-                             silc_server_command_identify,
-                             silc_server_command_dup(cmd));
-  cmd->pending = TRUE;
-  silc_command_set_ident(cmd->payload, old_ident);
-  silc_buffer_free(tmpbuf);
-}
+  SilcPacketStream sock = cmd->sock;
+  SilcClientEntry client = silc_packet_get_context(sock);
+  QuitInternal q;
+  unsigned char *tmp = NULL;
+  SilcUInt32 len = 0;
 
-static int
-silc_server_command_identify_parse(SilcServerCommandContext cmd,
-                                  SilcClientEntry **clients,
-                                  uint32 *clients_count,
-                                  SilcServerEntry **servers,
-                                  uint32 *servers_count,
-                                  SilcChannelEntry **channels,
-                                  uint32 *channels_count,
-                                  uint32 *count)
-{
-  SilcServer server = cmd->server;
-  unsigned char *tmp;
-  uint32 len;
-  uint32 argc = silc_argument_get_arg_num(cmd->args);
-  SilcIDPayload idp;
-  bool check_global = FALSE;
-  void *entry;
-  int i;
-  bool error = FALSE;
+  SILC_SERVER_COMMAND_CHECK(SILC_COMMAND_QUIT, cmd, 0, 1);
 
-  if (cmd->sock->type == SILC_SOCKET_TYPE_CLIENT)
-    check_global = TRUE;
-  else if (server->server_type != SILC_SERVER)
-    check_global = TRUE;
+  if (client->data.conn_type != SILC_CONN_CLIENT)
+    goto out;
 
-  /* If ID Payload is in the command it must be used instead of names */
-  tmp = silc_argument_get_arg_type(cmd->args, 5, &len);
-  if (!tmp) {
-    /* No ID, get the names. */
-
-    /* If we are normal server and have not resolved information from
-       router yet, do so now. */
-    if (cmd->sock->type == SILC_SOCKET_TYPE_CLIENT && 
-       server->server_type == SILC_SERVER && !cmd->pending && 
-       !server->standalone) {
-      silc_server_command_identify_send_router(cmd);
-      return -1;
-    }
+  /* Get message */
+  tmp = silc_argument_get_arg_type(cmd->args, 1, &len);
+  if (len > 128)
+    tmp = NULL;
 
-    /* Try to get nickname@server. */
-    tmp = silc_argument_get_arg_type(cmd->args, 1, NULL);
-    if (tmp) {
-      char *nick = NULL;
-      char *nick_server = NULL;
-
-      silc_parse_userfqdn(tmp, &nick, &nick_server);
-
-      if (!silc_idlist_get_clients_by_hash(server->local_list, 
-                                          nick, server->md5hash,
-                                          clients, clients_count))
-       silc_idlist_get_clients_by_nickname(server->local_list, 
-                                           nick, nick_server,
-                                           clients, clients_count);
-      if (check_global) {
-       if (!silc_idlist_get_clients_by_hash(server->global_list, 
-                                            nick, server->md5hash,
-                                            clients, clients_count))
-         silc_idlist_get_clients_by_nickname(server->global_list, 
-                                             nick, nick_server,
-                                             clients, clients_count);
-      }
-
-      silc_free(nick);
-      silc_free(nick_server);
-
-      if (!(*clients)) {
-       /* the nickname does not exist, send error reply */
-       silc_server_command_send_status_data(cmd, SILC_COMMAND_IDENTIFY,
-                                            SILC_STATUS_ERR_NO_SUCH_NICK,
-                                            3, tmp, strlen(tmp));
-       return 0;
-      }
-    }
+  q = silc_calloc(1, sizeof(*q));
+  q->sock = sock;
+  q->signoff = tmp ? strdup(tmp) : NULL;
+  silc_packet_stream_ref(q->sock);
 
-    /* Try to get server name */
-    tmp = silc_argument_get_arg_type(cmd->args, 2, NULL);
-    if (tmp) {
-      entry = silc_idlist_find_server_by_name(server->local_list,
-                                             tmp, TRUE, NULL);
-      if (!entry && check_global)
-       entry = silc_idlist_find_server_by_name(server->global_list,
-                                               tmp, TRUE, NULL);
-      if (entry) {
-       *servers = silc_realloc(*servers, sizeof(**servers) * 
-                               (*servers_count + 1));
-       (*servers)[(*servers_count)++] = entry;
-      }
+  /* We quit the connection with little timeout */
+  silc_schedule_task_add_timeout(server->schedule,
+                                silc_server_command_quit_cb, (void *)q,
+                                0, 200000);
 
-      if (!(*servers)) {
-       /* the server does not exist, send error reply */
-       silc_server_command_send_status_data(cmd, SILC_COMMAND_IDENTIFY,
-                                            SILC_STATUS_ERR_NO_SUCH_SERVER,
-                                            3, tmp, strlen(tmp));
-       return 0;
-      }
-    }
+ out:
+  silc_server_command_free(cmd);
+}
 
-    /* Try to get channel name */
-    tmp = silc_argument_get_arg_type(cmd->args, 3, NULL);
-    if (tmp) {
-      entry = silc_idlist_find_channel_by_name(server->local_list,
-                                              tmp, NULL);
-      if (!entry && check_global)
-       entry = silc_idlist_find_channel_by_name(server->global_list,
-                                                tmp, NULL);
-      if (entry) {
-       *channels = silc_realloc(*channels, sizeof(**channels) * 
-                                (*channels_count + 1));
-       (*channels)[(*channels_count)++] = entry;
-      }
+/* Server side of command KILL. This command is used by router operator
+   to remove an client from the SILC Network temporarily. */
 
-      if (!(*channels)) {
-       /* The channel does not exist, send error reply */
-       silc_server_command_send_status_data(cmd, SILC_COMMAND_IDENTIFY,
-                                            SILC_STATUS_ERR_NO_SUCH_CHANNEL,
-                                            3, tmp, strlen(tmp));
-       return 0;
-      }
-    }
+SILC_SERVER_CMD_FUNC(kill)
+{
+  SilcServerCommandContext cmd = (SilcServerCommandContext)context;
+  SilcServer server = cmd->server;
+  SilcClientEntry client = silc_packet_get_context(cmd->sock);
+  SilcClientEntry remote_client;
+  SilcID id;
+  unsigned char *tmp, *comment, *auth;
+  SilcUInt32 tmp_len, tmp_len2, auth_len;
 
-    if (!(*clients) && !(*servers) && !(*channels)) {
-      silc_server_command_send_status_reply(cmd, SILC_COMMAND_IDENTIFY,
-                                           SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
-      return 0;
-    }
-  } else {
-    /* Command includes ID, we must use that.  Also check whether the command
-       has more than one ID set - take them all. */
+  SILC_SERVER_COMMAND_CHECK(SILC_COMMAND_KILL, cmd, 1, 3);
 
-    /* Take all ID's from the command packet */
-    for (i = 0; i < argc; i++) {
-      void *id;
+  if (client->data.conn_type != SILC_CONN_CLIENT || !client)
+    goto out;
 
-      tmp = silc_argument_get_arg_type(cmd->args, i + 5, &len);
-      if (!tmp)
-       continue;
-      
-      idp = silc_id_payload_parse(tmp, len);
-      if (!idp) {
-       silc_free(*clients);
-       silc_free(*servers);
-       silc_free(*channels);
-       silc_server_command_send_status_reply(
-                                      cmd, SILC_COMMAND_IDENTIFY,
-                                      SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
-       return 0;
-      }
+  /* Get authentication payload if present */
+  auth = silc_argument_get_arg_type(cmd->args, 3, &auth_len);
 
-      id = silc_id_payload_get_id(idp);
-      
-      switch (silc_id_payload_get_type(idp)) {
-       
-      case SILC_ID_CLIENT:
-       entry = (void *)silc_idlist_find_client_by_id(server->local_list, 
-                                                     id, TRUE, NULL);
-       if (!entry && check_global)
-         entry = (void *)silc_idlist_find_client_by_id(server->global_list, 
-                                                       id, TRUE, NULL);
-       if (entry) {
-         *clients = silc_realloc(*clients, sizeof(**clients) * 
-                                 (*clients_count + 1));
-         (*clients)[(*clients_count)++] = (SilcClientEntry)entry;
-       } else {
-         /* If we are normal server and have not resolved information from
-            router yet, do so now. */
-         if (cmd->sock->type == SILC_SOCKET_TYPE_CLIENT && 
-             server->server_type == SILC_SERVER && !cmd->pending && 
-             !server->standalone) {
-           silc_server_command_identify_send_router(cmd);
-           silc_free(*clients);
-           silc_free(*servers);
-           silc_free(*channels);
-           return -1;
-         } else {
-           silc_server_command_send_status_data(
-                                       cmd, SILC_COMMAND_IDENTIFY,
-                                       SILC_STATUS_ERR_NO_SUCH_CLIENT_ID,
-                                       2, tmp, len);
-           error = TRUE;
-         }
-       }
+  if (!auth) {
+    /* Router operator killing */
 
-       break;
-       
-      case SILC_ID_SERVER:
-       entry = (void *)silc_idlist_find_server_by_id(server->local_list, 
-                                                     id, TRUE, NULL);
-       if (!entry && check_global)
-         entry = (void *)silc_idlist_find_server_by_id(server->global_list, 
-                                                       id, TRUE, NULL);
-       if (entry) {
-         *servers = silc_realloc(*servers, sizeof(**servers) * 
-                                 (*servers_count + 1));
-         (*servers)[(*servers_count)++] = (SilcServerEntry)entry;
-       } else {
-         /* If we are normal server and have not resolved information from
-            router yet, do so now. */
-         if (cmd->sock->type == SILC_SOCKET_TYPE_CLIENT && 
-             server->server_type == SILC_SERVER && !cmd->pending && 
-             !server->standalone) {
-           silc_server_command_identify_send_router(cmd);
-           silc_free(*clients);
-           silc_free(*servers);
-           silc_free(*channels);
-           return -1;
-         } else {
-           silc_server_command_send_status_data(
-                                        cmd, SILC_COMMAND_IDENTIFY,
-                                        SILC_STATUS_ERR_NO_SUCH_SERVER_ID,
-                                        2, tmp, len);
-           error = TRUE;
-         }
-       }
-       break;
-       
-      case SILC_ID_CHANNEL:
-       entry = (void *)silc_idlist_find_channel_by_id(server->local_list, 
-                                                      id, NULL);
-       if (!entry && check_global)
-         entry = (void *)silc_idlist_find_channel_by_id(server->global_list, 
-                                                        id, NULL);
-       if (entry) {
-         *channels = silc_realloc(*channels, sizeof(**channels) * 
-                                  (*channels_count + 1));
-         (*channels)[(*channels_count)++] = (SilcChannelEntry)entry;
-       } else {
-         /* If we are normal server and have not resolved information from
-            router yet, do so now. */
-         if (cmd->sock->type == SILC_SOCKET_TYPE_CLIENT && 
-             server->server_type == SILC_SERVER && !cmd->pending && 
-             !server->standalone) {
-           silc_server_command_identify_send_router(cmd);
-           silc_free(*clients);
-           silc_free(*servers);
-           silc_free(*channels);
-           return -1;
-         } else {
-           silc_server_command_send_status_data(
-                                        cmd, SILC_COMMAND_IDENTIFY,
-                                        SILC_STATUS_ERR_NO_SUCH_CHANNEL_ID,
-                                        2, tmp, len);
-           error = TRUE;
-         }
-       }
-       break;
-      }
+    /* KILL command works only on router */
+    if (server->server_type != SILC_ROUTER) {
+      silc_server_command_send_status_reply(cmd, SILC_COMMAND_KILL,
+                                           SILC_STATUS_ERR_NO_ROUTER_PRIV, 0);
+      goto out;
+    }
 
-      silc_free(id);
+    /* Check whether client has the permissions. */
+    if (!(client->mode & SILC_UMODE_ROUTER_OPERATOR)) {
+      silc_server_command_send_status_reply(cmd, SILC_COMMAND_KILL,
+                                           SILC_STATUS_ERR_NO_ROUTER_PRIV, 0);
+      goto out;
     }
   }
 
-  if (error) {
-    silc_free(*clients);
-    silc_free(*servers);
-    silc_free(*channels);
-    return FALSE;
+  /* Get the client ID */
+  if (!silc_argument_get_decoded(cmd->args, 1, SILC_ARGUMENT_ID, &id, NULL)) {
+    silc_server_command_send_status_reply(cmd, SILC_COMMAND_KILL,
+                                         SILC_STATUS_ERR_NO_CLIENT_ID, 0);
+    goto out;
   }
-  
-  /* Get the max count of reply messages allowed */
-  tmp = silc_argument_get_arg_type(cmd->args, 4, NULL);
-  if (tmp)
-    *count = atoi(tmp);
-  else
-    *count = 0;
-
-  return 1;
-}
-
-/* Checks that all mandatory fields in client entry are present. If not
-   then send WHOIS request to the server who owns the client. We use
-   WHOIS because we want to get as much information as possible at once. */
-
-static bool
-silc_server_command_identify_check_client(SilcServerCommandContext cmd,
-                                         SilcClientEntry *clients,
-                                         uint32 clients_count)
-{
-  SilcServer server = cmd->server;
-  SilcClientEntry entry;
-  SilcServerResolveContext resolve = NULL, r = NULL;
-  uint32 resolve_count = 0;
-  int i, k;
-  bool no_res = TRUE;
-
-  for (i = 0; i < clients_count; i++) {
-    entry = clients[i];
-    if (!entry)
-      continue;
-
-    if (entry->nickname || 
-       !(entry->data.status & SILC_IDLIST_STATUS_REGISTERED)) {
-      if (!entry->router)
-       continue;
 
-      /* If we are normal server, and we've not resolved this client from
-        router and it is global client, we'll check whether it is on some
-        channel.  If not then we cannot be sure about its validity, and
-        we'll resolve it from router. */
-      if (cmd->server->server_type != SILC_SERVER || cmd->pending ||
-         entry->connection || silc_hash_table_count(entry->channels))
-       continue;
+  /* Get the client entry */
+  remote_client = silc_idlist_find_client_by_id(server->local_list,
+                                               SILC_ID_GET_ID(id),
+                                               TRUE, NULL);
+  if (!remote_client) {
+    remote_client = silc_idlist_find_client_by_id(server->global_list,
+                                                 SILC_ID_GET_ID(id),
+                                                 TRUE, NULL);
+    if (!remote_client) {
+      tmp = silc_argument_get_arg_type(cmd->args, 1, &tmp_len);
+      silc_server_command_send_status_data(cmd, SILC_COMMAND_KILL,
+                                          SILC_STATUS_ERR_NO_SUCH_CLIENT_ID,
+                                          0, 2, tmp, tmp_len);
+      goto out;
     }
+  }
 
-    /* We need to resolve this entry since it is not complete */
-
-    if (!cmd->pending && entry->data.status & SILC_IDLIST_STATUS_RESOLVING) {
-      /* The entry is being resolved (and we are not the resolver) so attach
-        to the command reply and we're done with this one. */
-      silc_server_command_pending(server, SILC_COMMAND_NONE, 
-                                 entry->resolve_cmd_ident,
-                                 silc_server_command_identify,
-                                 silc_server_command_dup(cmd));
-      no_res = FALSE;
-    } else {
-      if (entry->data.status & SILC_IDLIST_STATUS_RESOLVING) {
-       /* We've resolved this and it still is not ready.  We'll return
-          and are that this will be handled again after it is resolved. */
-       for (i = 0; i < resolve_count; i++) {
-         for (k = 0; k < r->res_argc; k++)
-           silc_free(r->res_argv[k]);
-         silc_free(r->res_argv);
-         silc_free(r->res_argv_lens);
-         silc_free(r->res_argv_types);
-       }
-       silc_free(resolve);
-       return FALSE;
-      } else {
-       /* We'll resolve this client */
-       SilcBuffer idp;
-
-       r = NULL;
-       for (k = 0; k < resolve_count; k++) {
-         if (resolve[k].router == entry->router) {
-           r = &resolve[k];
-           break;
-         }
-       }
+  /* Get comment */
+  comment = silc_argument_get_arg_type(cmd->args, 2, &tmp_len2);
+  if (comment && tmp_len2 > 128) {
+    tmp_len2 = 128;
+    comment[tmp_len2 - 1] = '\0';
+  }
 
-       if (!r) {
-         resolve = silc_realloc(resolve, sizeof(*resolve) * 
-                                (resolve_count + 1));
-         r = &resolve[resolve_count];
-         memset(r, 0, sizeof(*r));
-         r->router = entry->router;
-         r->ident = ++server->cmd_ident;
-         resolve_count++;
-       }
+  /* If authentication data is provided then verify that killing is
+     actually allowed */
+  if (auth && auth_len) {
+    SilcPacketStream sock;
 
-       r->res_argv = silc_realloc(r->res_argv, sizeof(*r->res_argv) *
-                                  (r->res_argc + 1));
-       r->res_argv_lens = silc_realloc(r->res_argv_lens, 
-                                       sizeof(*r->res_argv_lens) *
-                                       (r->res_argc + 1));
-       r->res_argv_types = silc_realloc(r->res_argv_types, 
-                                        sizeof(*r->res_argv_types) *
-                                        (r->res_argc + 1));
-       idp = silc_id_payload_encode(entry->id, SILC_ID_CLIENT);
-       r->res_argv[r->res_argc] = silc_calloc(idp->len, 
-                                              sizeof(**r->res_argv));
-       memcpy(r->res_argv[r->res_argc], idp->data, idp->len);
-       r->res_argv_lens[r->res_argc] = idp->len;
-       r->res_argv_types[r->res_argc] = r->res_argc + 3;
-       r->res_argc++;
-       silc_buffer_free(idp);
-
-       entry->resolve_cmd_ident = r->ident;
-       entry->data.status |= SILC_IDLIST_STATUS_RESOLVING;
-       entry->data.status &= ~SILC_IDLIST_STATUS_RESOLVED;
-      }
+    if (!SILC_IS_LOCAL(remote_client) || !remote_client->data.public_key) {
+      silc_server_command_send_status_reply(cmd, SILC_COMMAND_KILL,
+                                           SILC_STATUS_ERR_OPERATION_ALLOWED,
+                                           0);
+      goto out;
     }
-  }
 
-  /* Do the resolving */
-  for (i = 0; i < resolve_count; i++) {
-    SilcBuffer res_cmd;
+    /* Verify the signature */
+    if (!silc_auth_verify_data(auth, auth_len, SILC_AUTH_PUBLIC_KEY,
+                              remote_client->data.public_key, 0,
+                              server->sha1hash, remote_client->id,
+                              SILC_ID_CLIENT)) {
+      silc_server_command_send_status_reply(cmd, SILC_COMMAND_KILL,
+                                           SILC_STATUS_ERR_AUTH_FAILED, 0);
+      goto out;
+    }
 
-    r = &resolve[i];
+    /* Send reply to the sender */
+    tmp = silc_argument_get_arg_type(cmd->args, 1, &tmp_len);
+    silc_server_command_send_status_data(cmd, SILC_COMMAND_KILL,
+                                        SILC_STATUS_OK, 0,
+                                        2, tmp, tmp_len);
+
+    /* 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_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 */
 
-    /* Send WHOIS request. We send WHOIS since we're doing the requesting
-       now anyway so make it a good one. */
-    res_cmd = silc_command_payload_encode(SILC_COMMAND_WHOIS,
-                                         r->res_argc, r->res_argv, 
-                                         r->res_argv_lens,
-                                         r->res_argv_types, 
-                                         r->ident);
-    silc_server_packet_send(server, r->router->connection,
-                           SILC_PACKET_COMMAND, cmd->packet->flags,
-                           res_cmd->data, res_cmd->len, FALSE);
+    /* Send reply to the sender */
+    tmp = silc_argument_get_arg_type(cmd->args, 1, &tmp_len);
+    silc_server_command_send_status_data(cmd, SILC_COMMAND_KILL,
+                                        SILC_STATUS_OK, 0,
+                                        2, tmp, tmp_len);
 
-    /* Reprocess this packet after received reply */
-    silc_server_command_pending(server, SILC_COMMAND_WHOIS, 
-                               r->ident,
-                               silc_server_command_identify,
-                               silc_server_command_dup(cmd));
-    cmd->pending = TRUE;
+    /* Check if anyone is watching this nickname */
+    if (server->server_type == SILC_ROUTER)
+      silc_server_check_watcher_list(server, client, NULL,
+                                    SILC_NOTIFY_TYPE_KILLED);
 
-    silc_buffer_free(res_cmd);
-    for (k = 0; k < r->res_argc; k++)
-      silc_free(r->res_argv[k]);
-    silc_free(r->res_argv);
-    silc_free(r->res_argv_lens);
-    silc_free(r->res_argv_types);
-    no_res = FALSE;
+    /* Now do the killing */
+    silc_server_kill_client(server, remote_client, comment, client->id,
+                           SILC_ID_CLIENT);
   }
-  silc_free(resolve);
 
-  return no_res;
+ out:
+  silc_server_command_free(cmd);
 }
 
-static void
-silc_server_command_identify_send_reply(SilcServerCommandContext cmd,
-                                       SilcClientEntry *clients,
-                                       uint32 clients_count,
-                                       SilcServerEntry *servers,
-                                       uint32 servers_count,
-                                       SilcChannelEntry *channels,
-                                       uint32 channels_count,
-                                       int count)
+/* Server side of command INFO. This sends information about us to
+   the client. If client requested specific server we will send the
+   command to that server. */
+
+SILC_SERVER_CMD_FUNC(info)
 {
+  SilcServerCommandContext cmd = (SilcServerCommandContext)context;
   SilcServer server = cmd->server;
-  int i, k, len, valid_count;
-  SilcBuffer packet, idp;
-  SilcCommandStatus status;
-  uint16 ident = silc_command_get_ident(cmd->payload);
-  char nh[256], uh[256];
-  SilcSocketConnection hsock;
+  SilcBuffer idp;
+  unsigned char *tmp;
+  SilcUInt32 tmp_len;
+  char *dest_server = NULL, *server_info = NULL, *server_name;
+  SilcUInt16 ident = silc_command_get_ident(cmd->payload);
+  SilcServerEntry entry = NULL;
+  SilcID id;
 
-  status = SILC_STATUS_OK;
+  SILC_SERVER_COMMAND_CHECK(SILC_COMMAND_INFO, cmd, 0, 2);
 
-  if (clients) {
-    SilcClientEntry entry;
+  /* Get server name */
+  dest_server = silc_argument_get_arg_type(cmd->args, 1, NULL);
+  if (dest_server) {
+    /* Check server name. */
+    dest_server = silc_identifier_check(dest_server, strlen(dest_server),
+                                       SILC_STRING_UTF8, 256, &tmp_len);
+    if (!dest_server) {
+      silc_server_command_send_status_reply(cmd, SILC_COMMAND_INFO,
+                                           SILC_STATUS_ERR_BAD_SERVER, 0);
+      goto out;
+    }
+  }
 
-    /* Process only valid entries. */
-    valid_count = 0;
-    for (i = 0; i < clients_count; i++) {
-      if (clients[i]->data.status & SILC_IDLIST_STATUS_REGISTERED)
-       valid_count++;
-      else
-       clients[i] = NULL;
+  /* Get Server ID */
+  if (silc_argument_get_decoded(cmd->args, 2, SILC_ARGUMENT_ID, &id, NULL)) {
+    /* Check whether we have this server cached */
+    entry = silc_idlist_find_server_by_id(server->local_list,
+                                         SILC_ID_GET_ID(id), TRUE, NULL);
+    if (!entry) {
+      entry = silc_idlist_find_server_by_id(server->global_list,
+                                           SILC_ID_GET_ID(id), TRUE, NULL);
+      if (!entry && server->server_type != SILC_SERVER) {
+       tmp = silc_argument_get_arg_type(cmd->args, 2, &tmp_len);
+       silc_server_command_send_status_data(cmd, SILC_COMMAND_INFO,
+                                            SILC_STATUS_ERR_NO_SUCH_SERVER_ID,
+                                            0, 2, tmp, tmp_len);
+       goto out;
+      }
     }
+  }
 
-    if (!valid_count) {
-      /* No valid entries found at all, just send error */
-      unsigned char *tmp;
+  if ((!dest_server && !entry) || (entry && entry == server->id_entry) ||
+      (dest_server && !cmd->pending &&
+       !memcmp(dest_server, server->server_name, strlen(dest_server)))) {
+    /* Send our reply */
+    char info_string[256];
 
-      tmp = silc_argument_get_arg_type(cmd->args, 1, NULL);
-      if (tmp) {
-       silc_server_command_send_status_data(cmd, SILC_COMMAND_IDENTIFY,
-                                            SILC_STATUS_ERR_NO_SUCH_NICK,
-                                            3, tmp, strlen(tmp));
-      } else {
-       tmp = silc_argument_get_arg_type(cmd->args, 5, (uint32 *)&len);
-       silc_server_command_send_status_data(cmd, SILC_COMMAND_IDENTIFY,
-                                            SILC_STATUS_ERR_NO_SUCH_CLIENT_ID,
-                                            2, tmp, len);
+    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);
+
+    server_info = info_string;
+    entry = server->id_entry;
+  } else {
+    /* Check whether we have this server cached */
+    if (!entry && dest_server) {
+      entry = silc_idlist_find_server_by_name(server->global_list,
+                                             dest_server, TRUE, NULL);
+      if (!entry) {
+       entry = silc_idlist_find_server_by_name(server->local_list,
+                                               dest_server, TRUE, NULL);
       }
-      return;
     }
 
-    /* Process all valid client entries and send command replies */
-
-    if (valid_count > 1)
-      status = SILC_STATUS_LIST_START;
+    if (!cmd->pending &&
+       server->server_type != SILC_SERVER && entry && !entry->server_info) {
+      /* Send to the server */
+      SilcBuffer tmpbuf;
+      SilcUInt16 old_ident;
 
-    for (i = 0, k = 0; i < clients_count; i++) {
-      entry = clients[i];
-      if (!entry)
-       continue;
+      /* Statistics */
+      cmd->server->stat.commands_sent++;
 
-      if (k >= 1)
-       status = SILC_STATUS_LIST_ITEM;
-      if (valid_count > 1 && k == valid_count - 1 
-         && !servers_count && !channels_count)
-       status = SILC_STATUS_LIST_END;
-      if (count && k - 1 == count)
-       status = SILC_STATUS_LIST_END;
-      if (count && k - 1 > count)
-       break;
+      old_ident = silc_command_get_ident(cmd->payload);
+      silc_command_set_ident(cmd->payload, ++server->cmd_ident);
+      tmpbuf = silc_command_payload_encode_payload(cmd->payload);
 
-      /* Send IDENTIFY reply */
-
-      idp = silc_id_payload_encode(entry->id, SILC_ID_CLIENT);
-      memset(uh, 0, sizeof(uh));
-      memset(nh, 0, sizeof(nh));
-      strncat(nh, entry->nickname, strlen(entry->nickname));
-      if (!strchr(entry->nickname, '@')) {
-       strncat(nh, "@", 1);
-       if (entry->servername) {
-         strncat(nh, entry->servername, strlen(entry->servername));
-       } else {
-         len = entry->router ? strlen(entry->router->server_name) :
-           strlen(server->server_name);
-         strncat(nh, entry->router ? entry->router->server_name :
-                 server->server_name, len);
-       }
-      }
+      silc_server_packet_send(server, entry->connection,
+                             SILC_PACKET_COMMAND, cmd->packet->flags,
+                             tmpbuf->data, silc_buffer_len(tmpbuf));
 
-      if (!entry->username) {
-       packet = silc_command_reply_payload_encode_va(SILC_COMMAND_IDENTIFY,
-                                                     status, ident, 2,
-                                                     2, idp->data, idp->len, 
-                                                     3, nh, strlen(nh));
-      } else {
-       strncat(uh, entry->username, strlen(entry->username));
-       if (!strchr(entry->username, '@')) {
-         strncat(uh, "@", 1);
-         hsock = (SilcSocketConnection)entry->connection;
-         len = strlen(hsock->hostname);
-         strncat(uh, hsock->hostname, len);
-       }
-       
-       packet = silc_command_reply_payload_encode_va(SILC_COMMAND_IDENTIFY,
-                                                     status, ident, 3,
-                                                     2, idp->data, idp->len, 
-                                                     3, nh, strlen(nh),
-                                                     4, uh, strlen(uh));
-      }
-      
-      silc_server_packet_send(server, cmd->sock, SILC_PACKET_COMMAND_REPLY,
-                             0, packet->data, packet->len, FALSE);
-      
-      silc_buffer_free(packet);
-      silc_buffer_free(idp);
-      
-      k++;
+      /* Reprocess this packet after received reply from router */
+      silc_server_command_pending(server, SILC_COMMAND_INFO,
+                                 silc_command_get_ident(cmd->payload),
+                                 silc_server_command_info,
+                                 silc_server_command_dup(cmd));
+      cmd->pending = TRUE;
+      silc_command_set_ident(cmd->payload, old_ident);
+      silc_buffer_free(tmpbuf);
+      goto out;
     }
-  }
 
-  if (servers) {
-    SilcServerEntry entry;
+    if (!entry && !cmd->pending && !server->standalone) {
+      /* Send to the primary router */
+      SilcBuffer tmpbuf;
+      SilcUInt16 old_ident;
 
-    if (status == SILC_STATUS_OK && servers_count > 1)
-      status = SILC_STATUS_LIST_START;
-
-    for (i = 0, k = 0; i < servers_count; i++) {
-      entry = servers[i];
-      
-      if (k >= 1)
-       status = SILC_STATUS_LIST_ITEM;
-      if (servers_count > 1 && k == servers_count - 1 && !channels_count)
-       status = SILC_STATUS_LIST_END;
-      if (count && k - 1 == count)
-       status = SILC_STATUS_LIST_END;
-      if (count && k - 1 > count)
-       break;
-      
-      /* Send IDENTIFY reply */
-      idp = silc_id_payload_encode(entry->id, SILC_ID_SERVER);
-      packet = 
-       silc_command_reply_payload_encode_va(SILC_COMMAND_IDENTIFY,
-                                            status, ident, 2,
-                                            2, idp->data, idp->len, 
-                                            3, entry->server_name, 
-                                            entry->server_name ? 
-                                            strlen(entry->server_name) : 0);
-      silc_server_packet_send(server, cmd->sock, SILC_PACKET_COMMAND_REPLY,
-                             0, packet->data, packet->len, FALSE);
-      
-      silc_buffer_free(packet);
-      silc_buffer_free(idp);
-      
-      k++;
-    }
-  }
+      /* Statistics */
+      cmd->server->stat.commands_sent++;
 
-  if (channels) {
-    SilcChannelEntry entry;
+      old_ident = silc_command_get_ident(cmd->payload);
+      silc_command_set_ident(cmd->payload, ++server->cmd_ident);
+      tmpbuf = silc_command_payload_encode_payload(cmd->payload);
 
-    if (status == SILC_STATUS_OK && channels_count > 1)
-      status = SILC_STATUS_LIST_START;
+      silc_server_packet_send(server, SILC_PRIMARY_ROUTE(server),
+                             SILC_PACKET_COMMAND, cmd->packet->flags,
+                             tmpbuf->data, silc_buffer_len(tmpbuf));
 
-    for (i = 0, k = 0; i < channels_count; i++) {
-      entry = channels[i];
-      
-      if (k >= 1)
-       status = SILC_STATUS_LIST_ITEM;
-      if (channels_count > 1 && k == channels_count - 1)
-       status = SILC_STATUS_LIST_END;
-      if (count && k - 1 == count)
-       status = SILC_STATUS_LIST_END;
-      if (count && k - 1 > count)
-       break;
-      
-      /* Send IDENTIFY reply */
-      idp = silc_id_payload_encode(entry->id, SILC_ID_CHANNEL);
-      packet = 
-       silc_command_reply_payload_encode_va(SILC_COMMAND_IDENTIFY,
-                                            status, ident, 2,
-                                            2, idp->data, idp->len, 
-                                            3, entry->channel_name, 
-                                            entry->channel_name ? 
-                                            strlen(entry->channel_name): 0);
-      silc_server_packet_send(server, cmd->sock, SILC_PACKET_COMMAND_REPLY,
-                             0, packet->data, packet->len, FALSE);
-      
-      silc_buffer_free(packet);
-      silc_buffer_free(idp);
-      
-      k++;
+      /* Reprocess this packet after received reply from router */
+      silc_server_command_pending(server, SILC_COMMAND_INFO,
+                                 silc_command_get_ident(cmd->payload),
+                                 silc_server_command_info,
+                                 silc_server_command_dup(cmd));
+      cmd->pending = TRUE;
+      silc_command_set_ident(cmd->payload, old_ident);
+      silc_buffer_free(tmpbuf);
+      goto out;
     }
   }
-}
 
-static int
-silc_server_command_identify_process(SilcServerCommandContext cmd)
-{
-  uint32 count = 0;
-  int ret = 0;
-  SilcClientEntry *clients = NULL;
-  SilcServerEntry *servers = NULL;
-  SilcChannelEntry *channels = NULL;
-  uint32 clients_count = 0, servers_count = 0, channels_count = 0;
-
-  /* Parse the IDENTIFY request */
-  ret = silc_server_command_identify_parse(cmd,
-                                          &clients, &clients_count,
-                                          &servers, &servers_count,
-                                          &channels, &channels_count,
-                                          &count);
-  if (ret < 1)
-    return ret;
-  ret = 0;
-
-  /* Check that all mandatory fields are present and request those data
-     from the server who owns the client if necessary. */
-  if (clients && !silc_server_command_identify_check_client(cmd, clients, 
-                                                           clients_count)) {
-    ret = -1;
+  if (!entry) {
+    if (dest_server) {
+      silc_free(dest_server);
+      dest_server = silc_argument_get_arg_type(cmd->args, 1, NULL);
+      silc_server_command_send_status_data(cmd, SILC_COMMAND_INFO,
+                                          SILC_STATUS_ERR_NO_SUCH_SERVER, 0,
+                                          2, dest_server,
+                                          strlen(dest_server));
+      dest_server = NULL;
+    }
     goto out;
   }
 
-  /* Send the command reply to the client */
-  silc_server_command_identify_send_reply(cmd, 
-                                         clients, clients_count,
-                                         servers, servers_count,
-                                         channels, channels_count, 
-                                         count);
+  idp = silc_id_payload_encode(entry->id, SILC_ID_SERVER);
+  if (!server_info)
+    server_info = entry->server_info;
+  server_name = entry->server_name;
+
+  /* Send the reply */
+  silc_server_send_command_reply(server, cmd->sock, SILC_COMMAND_INFO,
+                                SILC_STATUS_OK, 0, ident, 3,
+                                2, idp->data, silc_buffer_len(idp),
+                                3, server_name,
+                                strlen(server_name),
+                                4, server_info,
+                                server_info ?
+                                strlen(server_info) : 0);
+  silc_buffer_free(idp);
 
  out:
-  silc_free(clients);
-  silc_free(servers);
-  silc_free(channels);
-  return ret;
+  silc_free(dest_server);
+  silc_server_command_free(cmd);
 }
 
-SILC_SERVER_CMD_FUNC(identify)
+/* Server side of command PING. This just replies to the ping. */
+
+SILC_SERVER_CMD_FUNC(ping)
 {
   SilcServerCommandContext cmd = (SilcServerCommandContext)context;
-  int ret = 0;
+  SilcServer server = cmd->server;
+  SilcUInt32 tmp_len;
+  unsigned char *tmp;
+  SilcID id;
+
+  SILC_SERVER_COMMAND_CHECK(SILC_COMMAND_PING, cmd, 1, 1);
+
+  /* Get Server ID */
+  if (!silc_argument_get_decoded(cmd->args, 1, SILC_ARGUMENT_ID, &id, NULL)) {
+    silc_server_command_send_status_reply(cmd, SILC_COMMAND_PING,
+                                         SILC_STATUS_ERR_NOT_ENOUGH_PARAMS,
+                                         0);
+    goto out;
+  }
 
-  SILC_SERVER_COMMAND_CHECK(SILC_COMMAND_IDENTIFY, cmd, 1, 3328);
+  if (SILC_ID_SERVER_COMPARE(SILC_ID_GET_ID(id), server->id)) {
+    /* Send our reply */
+    silc_server_command_send_status_reply(cmd, SILC_COMMAND_PING,
+                                         SILC_STATUS_OK, 0);
+  } else {
+    tmp = silc_argument_get_arg_type(cmd->args, 1, &tmp_len);
+    silc_server_command_send_status_data(cmd, SILC_COMMAND_PING,
+                                        SILC_STATUS_ERR_NO_SUCH_SERVER_ID, 0,
+                                        2, tmp, tmp_len);
+    goto out;
+  }
 
-  ret = silc_server_command_identify_process(cmd);
+ out:
   silc_server_command_free(cmd);
 }
 
-/* Server side of command NICK. Sets nickname for user. Setting
-   nickname causes generation of a new client ID for the client. The
-   new client ID is sent to the client after changing the nickname. */
+/* Server side of command STATS. */
 
-SILC_SERVER_CMD_FUNC(nick)
+SILC_SERVER_CMD_FUNC(stats)
 {
   SilcServerCommandContext cmd = (SilcServerCommandContext)context;
-  SilcClientEntry client = (SilcClientEntry)cmd->sock->user_data;
   SilcServer server = cmd->server;
-  SilcBuffer packet, nidp, oidp = NULL;
-  SilcClientID *new_id;
-  uint32 nick_len;
-  char *nick;
-  uint16 ident = silc_command_get_ident(cmd->payload);
-  int nickfail = 0;
+  SilcID id;
+  unsigned char *tmp;
+  SilcUInt32 tmp_len;
+  SilcBuffer packet, stats;
+  SilcUInt16 ident = silc_command_get_ident(cmd->payload);
+  SilcUInt32 uptime;
 
-  if (cmd->sock->type != SILC_SOCKET_TYPE_CLIENT)
+  SILC_SERVER_COMMAND_CHECK(SILC_COMMAND_STATS, cmd, 1, 1);
+
+  /* Get Server ID */
+  if (!silc_argument_get_decoded(cmd->args, 1, SILC_ARGUMENT_ID, &id, NULL)) {
+    silc_server_command_send_status_reply(cmd, SILC_COMMAND_STATS,
+                                         SILC_STATUS_ERR_NO_SERVER_ID, 0);
     goto out;
+  }
 
-  SILC_SERVER_COMMAND_CHECK(SILC_COMMAND_NICK, cmd, 1, 1);
+  SILC_LOG_DEBUG(("id %s", silc_id_render(SILC_ID_GET_ID(id),
+                                         id.type)));
 
-  /* Check nickname */
-  nick = silc_argument_get_arg_type(cmd->args, 1, &nick_len);
-  if (nick_len > 128)
-    nick[128] = '\0';
-  if (silc_server_name_bad_chars(nick, nick_len) == TRUE) {
-    silc_server_command_send_status_reply(cmd, SILC_COMMAND_NICK,
-                                         SILC_STATUS_ERR_BAD_NICKNAME);
+  /* The ID must be ours */
+  if (!SILC_ID_SERVER_COMPARE(server->id, SILC_ID_GET_ID(id))) {
+    tmp = silc_argument_get_arg_type(cmd->args, 1, &tmp_len);
+    silc_server_command_send_status_data(cmd, SILC_COMMAND_STATS,
+                                        SILC_STATUS_ERR_NO_SUCH_SERVER_ID, 0,
+                                        2, tmp, tmp_len);
     goto out;
   }
 
-  /* Check for same nickname */
-  if (!strcmp(client->nickname, nick)) {
-    nidp = silc_id_payload_encode(client->id, SILC_ID_CLIENT);
-    goto send_reply;
-  }
-
-  /* Create new Client ID */
-  while (!silc_id_create_client_id(cmd->server, cmd->server->id, 
-                                  cmd->server->rng, 
-                                  cmd->server->md5hash, nick,
-                                  &new_id)) {
-    nickfail++;
-    snprintf(&nick[strlen(nick) - 1], 1, "%d", nickfail);
-  }
-
-  /* Send notify about nickname change to our router. We send the new
-     ID and ask to replace it with the old one. If we are router the
-     packet is broadcasted. Send NICK_CHANGE notify. */
-  if (!server->standalone)
-    silc_server_send_notify_nick_change(server, server->router->connection, 
-                                       server->server_type == SILC_SERVER ? 
-                                       FALSE : TRUE, client->id,
-                                       new_id);
-
-  oidp = silc_id_payload_encode(client->id, SILC_ID_CLIENT);
-
-  /* Remove old cache entry */
-  silc_idcache_del_by_context(server->local_list->clients, client);
-
-  /* Free old ID */
-  silc_free(client->id);
-
-  /* Save the nickname as this client is our local client */
-  silc_free(client->nickname);
-
-  client->nickname = strdup(nick);
-  client->id = new_id;
-
-  /* Update client cache */
-  silc_idcache_add(server->local_list->clients, client->nickname, 
-                  client->id, (void *)client, 0, NULL);
-
-  nidp = silc_id_payload_encode(client->id, SILC_ID_CLIENT);
-
-  /* Send NICK_CHANGE notify to the client's channels */
-  silc_server_send_notify_on_channels(server, NULL, client, 
-                                     SILC_NOTIFY_TYPE_NICK_CHANGE, 2,
-                                     oidp->data, oidp->len, 
-                                     nidp->data, nidp->len);
-
- send_reply:
-  /* Send the new Client ID as reply command back to client */
-  packet = silc_command_reply_payload_encode_va(SILC_COMMAND_NICK, 
-                                               SILC_STATUS_OK, ident, 1, 
-                                               2, nidp->data, nidp->len);
-  silc_server_packet_send(cmd->server, cmd->sock, SILC_PACKET_COMMAND_REPLY,
-                         0, packet->data, packet->len, FALSE);
-
-  silc_buffer_free(packet);
-  silc_buffer_free(nidp);
-  if (oidp)
-    silc_buffer_free(oidp);
-  
- out:
-  silc_server_command_free(cmd);
-}
-
-/* Sends the LIST command reply */
-
-static void
-silc_server_command_list_send_reply(SilcServerCommandContext cmd,
-                                   SilcChannelEntry *lch, 
-                                   uint32 lch_count,
-                                   SilcChannelEntry *gch,
-                                   uint32 gch_count)
-{
-  int i, k;
-  SilcBuffer packet, idp;
-  SilcChannelEntry entry;
-  SilcCommandStatus status;
-  uint16 ident = silc_command_get_ident(cmd->payload);
-  char *topic;
-  unsigned char usercount[4];
-  uint32 users;
-  int valid_lcount = 0, valid_rcount = 0;
-
-  for (i = 0; i < lch_count; i++) {
-    if (lch[i]->mode & SILC_CHANNEL_MODE_SECRET)
-      lch[i] = NULL;
-    else
-      valid_lcount++;
-  }
-  for (i = 0; i < gch_count; i++) {
-    if (gch[i]->mode & SILC_CHANNEL_MODE_SECRET)
-      gch[i] = NULL;
-    else
-      valid_rcount++;
-  }
-
-  status = SILC_STATUS_OK;
-  if ((lch_count + gch_count) > 1)
-    status = SILC_STATUS_LIST_START;
-
-  /* Local list */
-  for (i = 0, k = 0; i < lch_count; i++) {
-    entry = lch[i];
-    if (!entry)
-      continue;
-
-    if (k >= 1)
-      status = SILC_STATUS_LIST_ITEM;
-    if (valid_lcount > 1 && k == valid_lcount - 1 && !valid_rcount)
-      status = SILC_STATUS_LIST_END;
-
-    idp = silc_id_payload_encode(entry->id, SILC_ID_CHANNEL);
-
-    if (entry->mode & SILC_CHANNEL_MODE_PRIVATE) {
-      topic = "*private*";
-      memset(usercount, 0, sizeof(usercount));
-    } else {
-      topic = entry->topic;
-      users = silc_hash_table_count(entry->user_list);
-      SILC_PUT32_MSB(users, usercount);
-    }
-
-    /* Send the reply */
-    packet = 
-      silc_command_reply_payload_encode_va(SILC_COMMAND_LIST, 
-                                          status, ident, 4,
-                                          2, idp->data, idp->len,
-                                          3, entry->channel_name, 
-                                          strlen(entry->channel_name),
-                                          4, topic, topic ? strlen(topic) : 0,
-                                          5, usercount, 4);
-    silc_server_packet_send(cmd->server, cmd->sock, 
-                           SILC_PACKET_COMMAND_REPLY, 0, packet->data, 
-                           packet->len, FALSE);
-    silc_buffer_free(packet);
-    silc_buffer_free(idp);
-    k++;
-  }
-
-  /* Global list */
-  for (i = 0, k = 0; i < gch_count; i++) {
-    entry = gch[i];
-    if (!entry)
-      continue;
-
-    if (k >= 1)
-      status = SILC_STATUS_LIST_ITEM;
-    if (valid_rcount > 1 && k == valid_rcount - 1)
-      status = SILC_STATUS_LIST_END;
-
-    idp = silc_id_payload_encode(entry->id, SILC_ID_CHANNEL);
-
-    if (entry->mode & SILC_CHANNEL_MODE_PRIVATE) {
-      topic = "*private*";
-      memset(usercount, 0, sizeof(usercount));
-    } else {
-      topic = entry->topic;
-      users = entry->user_count;
-      SILC_PUT32_MSB(users, usercount);
-    }
-
-    /* Send the reply */
-    packet = 
-      silc_command_reply_payload_encode_va(SILC_COMMAND_LIST, 
-                                          status, ident, 4,
-                                          2, idp->data, idp->len,
-                                          3, entry->channel_name, 
-                                          strlen(entry->channel_name),
-                                          4, topic, topic ? strlen(topic) : 0,
-                                          5, usercount, 4);
-    silc_server_packet_send(cmd->server, cmd->sock, 
-                           SILC_PACKET_COMMAND_REPLY, 0, packet->data, 
-                           packet->len, FALSE);
-    silc_buffer_free(packet);
-    silc_buffer_free(idp);
-    k++;
-  }
-}
-
-/* Server side of LIST command. This lists the channel of the requested
-   server. Secret channels are not listed. */
-
-SILC_SERVER_CMD_FUNC(list)
-{
-  SilcServerCommandContext cmd = (SilcServerCommandContext)context;
-  SilcServer server = cmd->server;
-  SilcChannelID *channel_id = NULL;
-  unsigned char *tmp;
-  uint32 tmp_len;
-  SilcChannelEntry *lchannels = NULL, *gchannels = NULL;
-  uint32 lch_count = 0, gch_count = 0;
-
-  SILC_SERVER_COMMAND_CHECK(SILC_COMMAND_LIST, cmd, 0, 1);
-
-  /* If we are normal server, send the command to router, since we
-     want to know all channels in the network. */
-  if (!cmd->pending && server->server_type == SILC_SERVER && 
-      !server->standalone) {
-    SilcBuffer tmpbuf;
-    uint16 old_ident;
-    
-    old_ident = silc_command_get_ident(cmd->payload);
-    silc_command_set_ident(cmd->payload, ++server->cmd_ident);
-    tmpbuf = silc_command_payload_encode_payload(cmd->payload);
-    silc_server_packet_send(server, server->router->connection,
-                           SILC_PACKET_COMMAND, cmd->packet->flags,
-                           tmpbuf->data, tmpbuf->len, TRUE);
-
-    /* Reprocess this packet after received reply from router */
-    silc_server_command_pending(server, SILC_COMMAND_LIST, 
-                               silc_command_get_ident(cmd->payload),
-                               silc_server_command_list, 
-                               silc_server_command_dup(cmd));
-    cmd->pending = TRUE;
-    silc_command_set_ident(cmd->payload, old_ident);
-    silc_buffer_free(tmpbuf);
-    goto out;
-  }
-
-  /* Get Channel ID */
-  tmp = silc_argument_get_arg_type(cmd->args, 1, &tmp_len);
-  if (tmp) {
-    channel_id = silc_id_payload_parse_id(tmp, tmp_len);
-    if (!channel_id) {
-      silc_server_command_send_status_reply(cmd, SILC_COMMAND_LIST,
-                                           SILC_STATUS_ERR_NO_CHANNEL_ID);
-      goto out;
-    }
-  }
-
-  /* Get the channels from local list */
-  lchannels = silc_idlist_get_channels(server->local_list, channel_id,
-                                      &lch_count);
-  
-  /* Get the channels from global list */
-  gchannels = silc_idlist_get_channels(server->global_list, channel_id,
-                                      &gch_count);
-
-  /* Send the reply */
-  silc_server_command_list_send_reply(cmd, lchannels, lch_count, 
-                                     gchannels, gch_count);
-
-  silc_free(lchannels);
-  silc_free(gchannels);
-
- out:
-  silc_server_command_free(cmd);
-}
-
-/* Server side of TOPIC command. Sets topic for channel and/or returns
-   current topic to client. */
-
-SILC_SERVER_CMD_FUNC(topic)
-{
-  SilcServerCommandContext cmd = (SilcServerCommandContext)context;
-  SilcServer server = cmd->server;
-  SilcClientEntry client = (SilcClientEntry)cmd->sock->user_data;
-  SilcChannelID *channel_id;
-  SilcChannelEntry channel;
-  SilcChannelClientEntry chl;
-  SilcBuffer packet, idp;
-  unsigned char *tmp;
-  uint32 argc, tmp_len;
-  uint16 ident = silc_command_get_ident(cmd->payload);
-
-  SILC_SERVER_COMMAND_CHECK(SILC_COMMAND_TOPIC, cmd, 1, 2);
-
-  argc = silc_argument_get_arg_num(cmd->args);
-
-  /* Get Channel ID */
-  tmp = silc_argument_get_arg_type(cmd->args, 1, &tmp_len);
-  if (!tmp) {
-    silc_server_command_send_status_reply(cmd, SILC_COMMAND_TOPIC,
-                                         SILC_STATUS_ERR_NO_CHANNEL_ID);
-    goto out;
-  }
-  channel_id = silc_id_payload_parse_id(tmp, tmp_len);
-  if (!channel_id) {
-    silc_server_command_send_status_reply(cmd, SILC_COMMAND_TOPIC,
-                                         SILC_STATUS_ERR_NO_CHANNEL_ID);
-    goto out;
-  }
-
-  /* Check whether the channel exists */
-  channel = silc_idlist_find_channel_by_id(server->local_list, 
-                                          channel_id, NULL);
-  if (!channel) {
-    channel = silc_idlist_find_channel_by_id(server->global_list, 
-                                            channel_id, NULL);
-    if (!channel) {
-      silc_server_command_send_status_reply(cmd, SILC_COMMAND_TOPIC,
-                                           SILC_STATUS_ERR_NO_SUCH_CHANNEL);
-      goto out;
-    }
-  }
-
-  if (argc > 1) {
-    /* Get the topic */
-    tmp = silc_argument_get_arg_type(cmd->args, 2, NULL);
-    if (!tmp) {
-      silc_server_command_send_status_reply(cmd, SILC_COMMAND_TOPIC,
-                                           SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
-      goto out;
-    }
-
-    if (strlen(tmp) > 256) {
-      silc_server_command_send_status_reply(cmd, SILC_COMMAND_TOPIC,
-                                           SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
-      goto out;
-    }
-
-    /* See whether the client is on channel and has rights to change topic */
-    if (!silc_hash_table_find(channel->user_list, client, NULL, 
-                             (void *)&chl)) {
-      silc_server_command_send_status_reply(cmd, SILC_COMMAND_TOPIC,
-                                           SILC_STATUS_ERR_NOT_ON_CHANNEL);
-      goto out;
-    }
-
-    if (chl->mode == SILC_CHANNEL_UMODE_NONE) {
-      if (channel->mode & SILC_CHANNEL_MODE_TOPIC) {
-       silc_server_command_send_status_reply(cmd, SILC_COMMAND_TOPIC,
-                                             SILC_STATUS_ERR_NO_CHANNEL_PRIV);
-       goto out;
-      }
-    }
-
-    /* Set the topic for channel */
-    silc_free(channel->topic);
-    channel->topic = strdup(tmp);
-
-    /* Send TOPIC_SET notify type to the network */
-    if (!server->standalone)
-      silc_server_send_notify_topic_set(server, server->router->connection,
-                                       server->server_type == SILC_ROUTER ?
-                                       TRUE : FALSE, channel, 
-                                       client->id, SILC_ID_CLIENT,
-                                       channel->topic);
-
-    idp = silc_id_payload_encode(client->id, SILC_ID_CLIENT);
-
-    /* Send notify about topic change to all clients on the channel */
-    silc_server_send_notify_to_channel(server, NULL, channel, FALSE, 
-                                      SILC_NOTIFY_TYPE_TOPIC_SET, 2,
-                                      idp->data, idp->len,
-                                      channel->topic, strlen(channel->topic));
-    silc_buffer_free(idp);
-  }
-
-  /* Send the topic to client as reply packet */
-  idp = silc_id_payload_encode(channel_id, SILC_ID_CHANNEL);
-  packet = silc_command_reply_payload_encode_va(SILC_COMMAND_TOPIC, 
-                                               SILC_STATUS_OK, ident, 2, 
-                                               2, idp->data, idp->len,
-                                               3, channel->topic, 
-                                               channel->topic ? 
-                                               strlen(channel->topic) : 0);
-  silc_server_packet_send(cmd->server, cmd->sock, SILC_PACKET_COMMAND_REPLY,
-                         0, packet->data, packet->len, FALSE);
-
-  silc_buffer_free(packet);
-  silc_buffer_free(idp);
-  silc_free(channel_id);
-
- out:
-  silc_server_command_free(cmd);
-}
-
-/* Server side of INVITE command. Invites some client to join some channel. 
-   This command is also used to manage the invite list of the channel. */
-
-SILC_SERVER_CMD_FUNC(invite)
-{
-  SilcServerCommandContext cmd = (SilcServerCommandContext)context;
-  SilcServer server = cmd->server;
-  SilcSocketConnection sock = cmd->sock, dest_sock;
-  SilcChannelClientEntry chl;
-  SilcClientEntry sender, dest;
-  SilcClientID *dest_id = NULL;
-  SilcChannelEntry channel;
-  SilcChannelID *channel_id = NULL;
-  SilcIDListData idata;
-  SilcBuffer idp, idp2, packet;
-  unsigned char *tmp, *add, *del;
-  uint32 len;
-  uint16 ident = silc_command_get_ident(cmd->payload);
-
-  SILC_SERVER_COMMAND_CHECK(SILC_COMMAND_INVITE, cmd, 1, 4);
-
-  /* Get Channel ID */
-  tmp = silc_argument_get_arg_type(cmd->args, 1, &len);
-  if (!tmp) {
-    silc_server_command_send_status_reply(cmd, SILC_COMMAND_INVITE,
-                                         SILC_STATUS_ERR_NO_CHANNEL_ID);
-    goto out;
-  }
-  channel_id = silc_id_payload_parse_id(tmp, len);
-  if (!channel_id) {
-    silc_server_command_send_status_reply(cmd, SILC_COMMAND_INVITE,
-                                         SILC_STATUS_ERR_NO_CHANNEL_ID);
-    goto out;
-  }
-
-  /* Get the channel entry */
-  channel = silc_idlist_find_channel_by_id(server->local_list, 
-                                          channel_id, NULL);
-  if (!channel) {
-    channel = silc_idlist_find_channel_by_id(server->global_list, 
-                                            channel_id, NULL);
-    if (!channel) {
-      silc_server_command_send_status_reply(cmd, SILC_COMMAND_INVITE,
-                                           SILC_STATUS_ERR_NO_SUCH_CHANNEL);
-      goto out;
-    }
-  }
-
-  /* Check whether the sender of this command is on the channel. */
-  sender = (SilcClientEntry)sock->user_data;
-  if (!silc_server_client_on_channel(sender, channel)) {
-    silc_server_command_send_status_reply(cmd, SILC_COMMAND_INVITE,
-                                         SILC_STATUS_ERR_NOT_ON_CHANNEL);
-    goto out;
-  }
-
-  /* Check whether the channel is invite-only channel. If yes then the
-     sender of this command must be at least channel operator. */
-  if (channel->mode & SILC_CHANNEL_MODE_INVITE) {
-    silc_hash_table_find(channel->user_list, sender, NULL, (void *)&chl);
-    if (chl->mode == SILC_CHANNEL_UMODE_NONE) {
-      silc_server_command_send_status_reply(cmd, SILC_COMMAND_INVITE,
-                                           SILC_STATUS_ERR_NO_CHANNEL_PRIV);
-      goto out;
-    }
-  }
-
-  /* Get destination client ID */
-  tmp = silc_argument_get_arg_type(cmd->args, 2, &len);
-  if (tmp) {
-    char invite[512];
-    bool resolve;
-
-    dest_id = silc_id_payload_parse_id(tmp, len);
-    if (!dest_id) {
-      silc_server_command_send_status_reply(cmd, SILC_COMMAND_INVITE,
-                                           SILC_STATUS_ERR_NO_CLIENT_ID);
-      goto out;
-    }
-
-    /* Get the client entry */
-    dest = silc_server_get_client_resolve(server, dest_id, &resolve);
-    if (!dest) {
-      if (server->server_type != SILC_SERVER || !resolve) {
-       silc_server_command_send_status_reply(
-                                       cmd, SILC_COMMAND_INVITE,
-                                       SILC_STATUS_ERR_NO_SUCH_CLIENT_ID);
-       goto out;
-      }
-      
-      /* The client info is being resolved. Reprocess this packet after
-        receiving the reply to the query. */
-      silc_server_command_pending(server, SILC_COMMAND_WHOIS, 
-                                 server->cmd_ident,
-                                 silc_server_command_invite, 
-                                 silc_server_command_dup(cmd));
-      cmd->pending = TRUE;
-      silc_free(channel_id);
-      silc_free(dest_id);
-      goto out;
-    }
-
-    /* Check whether the requested client is already on the channel. */
-    if (silc_server_client_on_channel(dest, channel)) {
-      silc_server_command_send_status_reply(cmd, SILC_COMMAND_INVITE,
-                                           SILC_STATUS_ERR_USER_ON_CHANNEL);
-      goto out;
-    }
-    
-    /* Get route to the client */
-    dest_sock = silc_server_get_client_route(server, NULL, 0, dest_id, &idata);
-    if (!dest_sock) {
-      silc_server_command_send_status_reply(cmd, SILC_COMMAND_INVITE,
-                                           SILC_STATUS_ERR_NO_SUCH_CLIENT_ID);
-      goto out;
-    }
-
-    memset(invite, 0, sizeof(invite));
-    strncat(invite, dest->nickname, strlen(dest->nickname));
-    strncat(invite, "!", 1);
-    strncat(invite, dest->username, strlen(dest->username));
-    if (!strchr(dest->username, '@')) {
-      strncat(invite, "@", 1);
-      strncat(invite, cmd->sock->hostname, strlen(cmd->sock->hostname));
-    }
-
-    len = strlen(invite);
-    if (!channel->invite_list)
-      channel->invite_list = silc_calloc(len + 2, 
-                                        sizeof(*channel->invite_list));
-    else
-      channel->invite_list = silc_realloc(channel->invite_list, 
-                                         sizeof(*channel->invite_list) * 
-                                         (len + 
-                                          strlen(channel->invite_list) + 2));
-    strncat(channel->invite_list, invite, len);
-    strncat(channel->invite_list, ",", 1);
-
-    /* Send notify to the client that is invited to the channel */
-    idp = silc_id_payload_encode(channel_id, SILC_ID_CHANNEL);
-    idp2 = silc_id_payload_encode(sender->id, SILC_ID_CLIENT);
-    silc_server_send_notify_dest(server, dest_sock, FALSE, dest_id, 
-                                SILC_ID_CLIENT,
-                                SILC_NOTIFY_TYPE_INVITE, 3, 
-                                idp->data, idp->len, 
-                                channel->channel_name, 
-                                strlen(channel->channel_name),
-                                idp2->data, idp2->len);
-    silc_buffer_free(idp);
-    silc_buffer_free(idp2);
-  }
-
-  /* Add the client to the invite list of the channel */
-  add = silc_argument_get_arg_type(cmd->args, 3, &len);
-  if (add) {
-    if (!channel->invite_list)
-      channel->invite_list = silc_calloc(len + 2, 
-                                        sizeof(*channel->invite_list));
-    else
-      channel->invite_list = silc_realloc(channel->invite_list, 
-                                         sizeof(*channel->invite_list) * 
-                                         (len + 
-                                          strlen(channel->invite_list) + 2));
-    if (add[len - 1] == ',')
-      add[len - 1] = '\0';
-    
-    strncat(channel->invite_list, add, len);
-    strncat(channel->invite_list, ",", 1);
-  }
-
-  /* Get the invite to be removed and remove it from the list */
-  del = silc_argument_get_arg_type(cmd->args, 4, &len);
-  if (del && channel->invite_list) {
-    char *start, *end, *n;
-
-    if (!strncmp(channel->invite_list, del, 
-                strlen(channel->invite_list) - 1)) {
-      silc_free(channel->invite_list);
-      channel->invite_list = NULL;
-    } else {
-      start = strstr(channel->invite_list, del);
-      if (start && strlen(start) >= len) {
-       end = start + len;
-       n = silc_calloc(strlen(channel->invite_list) - len, sizeof(*n));
-       strncat(n, channel->invite_list, start - channel->invite_list);
-       strncat(n, end + 1, ((channel->invite_list + 
-                             strlen(channel->invite_list)) - end) - 1);
-       silc_free(channel->invite_list);
-       channel->invite_list = n;
-      }
-    }
-  }
-
-  /* Send notify to the primary router */
-  if (!server->standalone)
-    silc_server_send_notify_invite(server, server->router->connection,
-                                  server->server_type == SILC_ROUTER ?
-                                  TRUE : FALSE, channel,
-                                  sender->id, add, del);
-
-  /* Send command reply */
-  tmp = silc_argument_get_arg_type(cmd->args, 1, &len);
-
-  if (add || del)
-    packet = 
-      silc_command_reply_payload_encode_va(SILC_COMMAND_INVITE,
-                                          SILC_STATUS_OK, ident, 2,
-                                          2, tmp, len,
-                                          3, channel->invite_list,
-                                          channel->invite_list ?
-                                          strlen(channel->invite_list) : 0);
-  else
-    packet = 
-      silc_command_reply_payload_encode_va(SILC_COMMAND_INVITE,
-                                          SILC_STATUS_OK, ident, 1,
-                                          2, tmp, len);
-  silc_server_packet_send(server, cmd->sock, SILC_PACKET_COMMAND_REPLY, 0, 
-                         packet->data, packet->len, FALSE);
-  silc_buffer_free(packet);
-
- out:
-  silc_free(dest_id);
-  silc_free(channel_id);
-  silc_server_command_free(cmd);
-}
-
-typedef struct {
-  SilcServer server;
-  SilcSocketConnection sock;
-  char *signoff;
-} *QuitInternal;
-
-/* Quits connection to client. This gets called if client won't
-   close the connection even when it has issued QUIT command. */
-
-SILC_TASK_CALLBACK(silc_server_command_quit_cb)
-{
-  QuitInternal q = (QuitInternal)context;
-
-  /* Free all client specific data, such as client entry and entires
-     on channels this client may be on. */
-  silc_server_free_client_data(q->server, q->sock, q->sock->user_data,
-                              TRUE, q->signoff);
-  q->sock->user_data = NULL;
-
-  /* Close the connection on our side */
-  silc_server_close_connection(q->server, q->sock);
-
-  silc_free(q->signoff);
-  silc_free(q);
-}
-
-/* Quits SILC session. This is the normal way to disconnect client. */
-SILC_SERVER_CMD_FUNC(quit)
-{
-  SilcServerCommandContext cmd = (SilcServerCommandContext)context;
-  SilcServer server = cmd->server;
-  SilcSocketConnection sock = cmd->sock;
-  QuitInternal q;
-  unsigned char *tmp = NULL;
-  uint32 len = 0;
-
-  SILC_SERVER_COMMAND_CHECK(SILC_COMMAND_QUIT, cmd, 0, 1);
-
-  if (cmd->sock->type != SILC_SOCKET_TYPE_CLIENT)
-    goto out;
-
-  /* Get destination ID */
-  tmp = silc_argument_get_arg_type(cmd->args, 1, &len);
-  if (len > 128)
-    tmp = NULL;
-
-  q = silc_calloc(1, sizeof(*q));
-  q->server = server;
-  q->sock = sock;
-  q->signoff = tmp ? strdup(tmp) : NULL;
-
-  /* We quit the connection with little timeout */
-  silc_schedule_task_add(server->schedule, sock->sock,
-                    silc_server_command_quit_cb, (void *)q,
-                    0, 200000, SILC_TASK_TIMEOUT, SILC_TASK_PRI_LOW);
-
- out:
-  silc_server_command_free(cmd);
-}
-
-/* Server side of command KILL. This command is used by router operator
-   to remove an client from the SILC Network temporarily. */
-
-SILC_SERVER_CMD_FUNC(kill)
-{
-  SilcServerCommandContext cmd = (SilcServerCommandContext)context;
-  SilcServer server = cmd->server;
-  SilcClientEntry client = (SilcClientEntry)cmd->sock->user_data;
-  SilcClientEntry remote_client;
-  SilcClientID *client_id;
-  unsigned char *tmp, *comment;
-  uint32 tmp_len, tmp_len2;
-  bool local;
-
-  SILC_SERVER_COMMAND_CHECK(SILC_COMMAND_KILL, cmd, 1, 2);
-
-  if (!client || cmd->sock->type != SILC_SOCKET_TYPE_CLIENT)
-    goto out;
-
-  /* KILL command works only on router */
-  if (server->server_type != SILC_ROUTER) {
-    silc_server_command_send_status_reply(cmd, SILC_COMMAND_KILL,
-                                         SILC_STATUS_ERR_NO_ROUTER_PRIV);
-    goto out;
-  }
-
-  /* Check whether client has the permissions. */
-  if (!(client->mode & SILC_UMODE_ROUTER_OPERATOR)) {
-    silc_server_command_send_status_reply(cmd, SILC_COMMAND_KILL,
-                                         SILC_STATUS_ERR_NO_ROUTER_PRIV);
-    goto out;
-  }
-
-  /* Get the client ID */
-  tmp = silc_argument_get_arg_type(cmd->args, 1, &tmp_len);
-  if (!tmp) {
-    silc_server_command_send_status_reply(cmd, SILC_COMMAND_KILL,
-                                         SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
-    goto out;
-  }
-  client_id = silc_id_payload_parse_id(tmp, tmp_len);
-  if (!client_id) {
-    silc_server_command_send_status_reply(cmd, SILC_COMMAND_KILL,
-                                         SILC_STATUS_ERR_NO_SUCH_CLIENT_ID);
-    goto out;
-  }
-
-  /* Get the client entry */
-  remote_client = silc_idlist_find_client_by_id(server->local_list, 
-                                               client_id, TRUE, NULL);
-  local = TRUE;
-  if (!remote_client) {
-    remote_client = silc_idlist_find_client_by_id(server->global_list, 
-                                                 client_id, TRUE, NULL);
-    local = FALSE;
-    if (!remote_client) {
-      silc_server_command_send_status_reply(cmd, SILC_COMMAND_KILL,
-                                           SILC_STATUS_ERR_NO_SUCH_CLIENT_ID);
-      goto out;
-    }
-  }
-
-  /* Get comment */
-  comment = silc_argument_get_arg_type(cmd->args, 2, &tmp_len2);
-  if (tmp_len2 > 128)
-    comment = NULL;
-
-  /* Send reply to the sender */
-  silc_server_command_send_status_reply(cmd, SILC_COMMAND_KILL,
-                                       SILC_STATUS_OK);
-
-  /* Send the KILL notify packets. First send it to the channel, then
-     to our primary router and then directly to the client who is being
-     killed right now. */
-
-  /* Send KILLED notify to the channels. It is not sent to the client
-     as it will be sent differently destined directly to the client and not
-     to the channel. */
-  silc_server_send_notify_on_channels(server, remote_client, 
-                                     remote_client, SILC_NOTIFY_TYPE_KILLED,
-                                     comment ? 2 : 1,
-                                     tmp, tmp_len,
-                                     comment, comment ? tmp_len2 : 0);
-
-  /* Send KILLED notify to primary route */
-  if (!server->standalone)
-    silc_server_send_notify_killed(server, server->router->connection, TRUE,
-                                  remote_client->id, comment);
-
-  /* Send KILLED notify to the client directly */
-  silc_server_send_notify_killed(server, remote_client->connection ? 
-                                remote_client->connection : 
-                                remote_client->router->connection, FALSE,
-                                remote_client->id, comment);
-
-  /* Remove the client from all channels. This generates new keys to the
-     channels as well. */
-  silc_server_remove_from_channels(server, NULL, remote_client, FALSE, 
-                                  NULL, TRUE);
-
-  /* Remove the client entry, If it is locally connected then we will also
-     disconnect the client here */
-  if (remote_client->connection) {
-    /* Remove locally conneted client */
-    SilcSocketConnection sock = remote_client->connection;
-    silc_server_free_client_data(server, sock, remote_client, FALSE, NULL);
-    silc_server_close_connection(server, sock);
-  } else {
-    /* Update statistics */
-    if (remote_client->connection)
-      server->stat.my_clients--;
-    if (server->server_type == SILC_ROUTER)
-      server->stat.cell_clients--;
-    SILC_OPER_STATS_UPDATE(remote_client, server, SILC_UMODE_SERVER_OPERATOR);
-    SILC_OPER_STATS_UPDATE(remote_client, router, SILC_UMODE_ROUTER_OPERATOR);
-
-    /* Remove remote client */
-    silc_idlist_del_client(local ? server->local_list :
-                          server->global_list, remote_client);
-  }
-
- out:
-  silc_server_command_free(cmd);
-}
-
-/* Server side of command INFO. This sends information about us to 
-   the client. If client requested specific server we will send the 
-   command to that server. */
-
-SILC_SERVER_CMD_FUNC(info)
-{
-  SilcServerCommandContext cmd = (SilcServerCommandContext)context;
-  SilcServer server = cmd->server;
-  SilcBuffer packet, idp;
-  unsigned char *tmp;
-  uint32 tmp_len;
-  char *dest_server, *server_info = NULL, *server_name;
-  uint16 ident = silc_command_get_ident(cmd->payload);
-  SilcServerEntry entry = NULL;
-  SilcServerID *server_id = NULL;
-
-  SILC_SERVER_COMMAND_CHECK(SILC_COMMAND_INFO, cmd, 0, 2);
-
-  /* Get server name */
-  dest_server = silc_argument_get_arg_type(cmd->args, 1, NULL);
-
-  /* Get Server ID */
-  tmp = silc_argument_get_arg_type(cmd->args, 2, &tmp_len);
-  if (tmp) {
-    server_id = silc_id_payload_parse_id(tmp, tmp_len);
-    if (!server_id) {
-      silc_server_command_send_status_reply(cmd, SILC_COMMAND_INFO,
-                                           SILC_STATUS_ERR_NO_SERVER_ID);
-      goto out;
-    }
-  }
-
-  if (server_id) {
-    /* Check whether we have this server cached */
-    entry = silc_idlist_find_server_by_id(server->local_list,
-                                         server_id, TRUE, NULL);
-    if (!entry) {
-      entry = silc_idlist_find_server_by_id(server->global_list,
-                                           server_id, TRUE, NULL);
-      if (!entry && server->server_type != SILC_SERVER) {
-       silc_server_command_send_status_reply(cmd, SILC_COMMAND_INFO,
-                                             SILC_STATUS_ERR_NO_SUCH_SERVER);
-       goto out;
-      }
-    }
-  }
-
-  /* Some buggy servers has sent request to router about themselves. */
-  if (server->server_type != SILC_SERVER && cmd->sock->user_data == entry)
-    goto out;
-
-  if ((!dest_server && !server_id && !entry) || (entry && 
-                                                entry == server->id_entry) ||
-      (dest_server && !cmd->pending && 
-       !strncasecmp(dest_server, server->server_name, strlen(dest_server)))) {
-    /* Send our reply */
-    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);
-
-    server_info = info_string;
-    entry = server->id_entry;
-  } else {
-    /* Check whether we have this server cached */
-    if (!entry && dest_server) {
-      entry = silc_idlist_find_server_by_name(server->global_list,
-                                             dest_server, TRUE, NULL);
-      if (!entry) {
-       entry = silc_idlist_find_server_by_name(server->local_list,
-                                               dest_server, TRUE, NULL);
-      }
-    }
-
-    if (!cmd->pending &&
-       server->server_type != SILC_SERVER && entry && !entry->server_info) {
-      /* Send to the server */
-      SilcBuffer tmpbuf;
-      uint16 old_ident;
-
-      old_ident = silc_command_get_ident(cmd->payload);
-      silc_command_set_ident(cmd->payload, ++server->cmd_ident);
-      tmpbuf = silc_command_payload_encode_payload(cmd->payload);
-
-      silc_server_packet_send(server, entry->connection,
-                             SILC_PACKET_COMMAND, cmd->packet->flags,
-                             tmpbuf->data, tmpbuf->len, TRUE);
-
-      /* Reprocess this packet after received reply from router */
-      silc_server_command_pending(server, SILC_COMMAND_INFO, 
-                                 silc_command_get_ident(cmd->payload),
-                                 silc_server_command_info,
-                                 silc_server_command_dup(cmd));
-      cmd->pending = TRUE;
-      silc_command_set_ident(cmd->payload, old_ident);
-      silc_buffer_free(tmpbuf);
-      goto out;
-    }
-
-    if (!entry && !cmd->pending && !server->standalone) {
-      /* Send to the primary router */
-      SilcBuffer tmpbuf;
-      uint16 old_ident;
-
-      old_ident = silc_command_get_ident(cmd->payload);
-      silc_command_set_ident(cmd->payload, ++server->cmd_ident);
-      tmpbuf = silc_command_payload_encode_payload(cmd->payload);
-
-      silc_server_packet_send(server, server->router->connection,
-                             SILC_PACKET_COMMAND, cmd->packet->flags,
-                             tmpbuf->data, tmpbuf->len, TRUE);
-
-      /* Reprocess this packet after received reply from router */
-      silc_server_command_pending(server, SILC_COMMAND_INFO, 
-                                 silc_command_get_ident(cmd->payload),
-                                 silc_server_command_info,
-                                 silc_server_command_dup(cmd));
-      cmd->pending = TRUE;
-      silc_command_set_ident(cmd->payload, old_ident);
-      silc_buffer_free(tmpbuf);
-      goto out;
-    }
-  }
-
-  silc_free(server_id);
-
-  if (!entry) {
-    silc_server_command_send_status_reply(cmd, SILC_COMMAND_INFO,
-                                         SILC_STATUS_ERR_NO_SUCH_SERVER);
-    goto out;
-  }
-
-  idp = silc_id_payload_encode(entry->id, SILC_ID_SERVER);
-  if (!server_info)
-    server_info = entry->server_info;
-  server_name = entry->server_name;
-
-  /* Send the reply */
-  packet = silc_command_reply_payload_encode_va(SILC_COMMAND_INFO,
-                                               SILC_STATUS_OK, ident, 3,
-                                               2, idp->data, idp->len,
-                                               3, server_name, 
-                                               strlen(server_name),
-                                               4, server_info, 
-                                               server_info ? 
-                                               strlen(server_info) : 0);
-  silc_server_packet_send(server, cmd->sock, SILC_PACKET_COMMAND_REPLY, 0, 
-                         packet->data, packet->len, FALSE);
-    
-  silc_buffer_free(packet);
-  silc_buffer_free(idp);
-
- out:
-  silc_server_command_free(cmd);
-}
-
-/* Server side of command PING. This just replies to the ping. */
-
-SILC_SERVER_CMD_FUNC(ping)
-{
-  SilcServerCommandContext cmd = (SilcServerCommandContext)context;
-  SilcServer server = cmd->server;
-  SilcServerID *id;
-  uint32 len;
-  unsigned char *tmp;
-
-  SILC_SERVER_COMMAND_CHECK(SILC_COMMAND_INFO, cmd, 1, 2);
+  /* If we are router then just send everything we got. If we are normal
+     server then we'll send this to our router to get all the latest
+     statistical information. */
+  if (!cmd->pending && server->server_type != SILC_ROUTER &&
+      !server->standalone) {
+    SilcBuffer idp;
+
+    /* Statistics */
+    cmd->server->stat.commands_sent++;
+
+    /* Send request to our router */
+    idp = silc_id_payload_encode(server->router->id,
+                                SILC_ID_SERVER);
+    packet = silc_command_payload_encode_va(SILC_COMMAND_STATS,
+                                           ++server->cmd_ident, 1,
+                                           1, idp->data,
+                                           silc_buffer_len(idp));
+    silc_server_packet_send(server, SILC_PRIMARY_ROUTE(server),
+                           SILC_PACKET_COMMAND, 0, packet->data,
+                           silc_buffer_len(packet));
 
-  /* Get Server ID */
-  tmp = silc_argument_get_arg_type(cmd->args, 1, &len);
-  if (!tmp) {
-    silc_server_command_send_status_reply(cmd, SILC_COMMAND_PING,
-                                         SILC_STATUS_ERR_NO_SERVER_ID);
+    /* Reprocess this packet after received reply from router */
+    silc_server_command_pending(server, SILC_COMMAND_STATS,
+                               server->cmd_ident,
+                               silc_server_command_stats,
+                               silc_server_command_dup(cmd));
+    cmd->pending = TRUE;
+    silc_buffer_free(packet);
+    silc_buffer_free(idp);
     goto out;
   }
-  id = silc_id_str2id(tmp, len, SILC_ID_SERVER);
-  if (!id)
-    goto out;
 
-  if (SILC_ID_SERVER_COMPARE(id, server->id)) {
-    /* Send our reply */
-    silc_server_command_send_status_reply(cmd, SILC_COMMAND_PING,
-                                         SILC_STATUS_OK);
-  } else {
-    silc_server_command_send_status_reply(cmd, SILC_COMMAND_PING,
-                                         SILC_STATUS_ERR_NO_SUCH_SERVER);
-    goto out;
-  }
+  /* Send our reply to sender */
+  uptime = time(NULL) - server->starttime;
+
+  stats = silc_buffer_alloc_size(60);
+  silc_buffer_format(stats,
+                    SILC_STR_UI_INT(server->starttime),
+                    SILC_STR_UI_INT(uptime),
+                    SILC_STR_UI_INT(server->stat.my_clients),
+                    SILC_STR_UI_INT(server->stat.my_channels),
+                    SILC_STR_UI_INT(server->stat.my_server_ops),
+                    SILC_STR_UI_INT(server->stat.my_router_ops),
+                    SILC_STR_UI_INT(server->stat.cell_clients),
+                    SILC_STR_UI_INT(server->stat.cell_channels),
+                    SILC_STR_UI_INT(server->stat.cell_servers),
+                    SILC_STR_UI_INT(server->stat.clients),
+                    SILC_STR_UI_INT(server->stat.channels),
+                    SILC_STR_UI_INT(server->stat.servers),
+                    SILC_STR_UI_INT(server->stat.routers),
+                    SILC_STR_UI_INT(server->stat.server_ops),
+                    SILC_STR_UI_INT(server->stat.router_ops),
+                    SILC_STR_END);
 
-  silc_free(id);
+  tmp = silc_argument_get_arg_type(cmd->args, 1, &tmp_len);
+  silc_server_send_command_reply(server, cmd->sock, SILC_COMMAND_STATS,
+                                SILC_STATUS_OK, 0, ident, 2,
+                                2, tmp, tmp_len,
+                                3, stats->data, silc_buffer_len(stats));
+  silc_buffer_free(stats);
 
  out:
   silc_server_command_free(cmd);
@@ -2992,59 +1813,92 @@ SILC_SERVER_CMD_FUNC(ping)
    has been either created or resolved from ID lists. This joins the sent
    client to the channel. */
 
-static void silc_server_command_join_channel(SilcServer server, 
+static void silc_server_command_join_channel(SilcServer server,
                                             SilcServerCommandContext cmd,
                                             SilcChannelEntry channel,
                                             SilcClientID *client_id,
-                                            bool created,
-                                            bool create_key,
-                                            uint32 umode,
+                                            SilcBool created,
+                                            SilcBool create_key,
+                                            SilcUInt32 umode,
                                             const unsigned char *auth,
-                                            uint32 auth_len)
+                                            SilcUInt32 auth_len,
+                                            const unsigned char *cauth,
+                                            SilcUInt32 cauth_len)
 {
-  SilcSocketConnection sock = cmd->sock;
+  SilcPacketStream sock = cmd->sock;
+  SilcIDListData idata = silc_packet_get_context(sock);
   unsigned char *tmp;
-  uint32 tmp_len, user_count;
-  unsigned char *passphrase = NULL, mode[4], tmp2[4], tmp3[4];
+  SilcUInt32 tmp_len, user_count;
+  unsigned char *passphrase = NULL, mode[4], tmp2[4], tmp3[4], ulimit[4];
   SilcClientEntry client;
   SilcChannelClientEntry chl;
-  SilcBuffer reply, chidp, clidp, keyp = NULL, user_list, mode_list;
-  uint16 ident = silc_command_get_ident(cmd->payload);
+  SilcBuffer reply, chidp, clidp, keyp = NULL;
+  SilcBuffer user_list, mode_list, invite_list, ban_list;
+  SilcUInt16 ident = silc_command_get_ident(cmd->payload);
   char check[512], check2[512];
-  bool founder = FALSE;
-  bool resolve;
+  void *plen;
+  SilcBool founder = FALSE;
+  SilcBool resolve;
+  SilcBuffer fkey = NULL, chpklist = NULL;
+  const char *cipher, *hostname, *ip;
 
-  SILC_LOG_DEBUG(("Start"));
+  SILC_LOG_DEBUG(("Joining client to channel"));
 
   if (!channel)
     return;
 
+  silc_socket_stream_get_info(silc_packet_stream_get_stream(sock),
+                             NULL, &hostname, &ip, NULL);
+
   /* Get the client entry */
-  if (cmd->sock->type == SILC_SOCKET_TYPE_CLIENT) {
-    client = (SilcClientEntry)sock->user_data;
+  if (idata->conn_type == SILC_CONN_CLIENT) {
+    client = (SilcClientEntry)idata;
+    if (!client)
+      return;
   } else {
-    client = silc_server_get_client_resolve(server, client_id, &resolve);
+    client = silc_server_query_client(server, client_id, FALSE,
+                                     &resolve);
     if (!client) {
-      if (cmd->pending)
-       goto out;
-
-      if (!resolve) {
-       silc_server_command_send_status_reply(
+      if (!resolve || cmd->pending) {
+       tmp = silc_argument_get_arg_type(cmd->args, 2, &tmp_len);
+       silc_server_command_send_status_data(
                                         cmd, SILC_COMMAND_JOIN,
-                                        SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
+                                        SILC_STATUS_ERR_NO_SUCH_CLIENT_ID, 0,
+                                        2, tmp, tmp_len);
        goto out;
       }
 
       /* The client info is being resolved. Reprocess this packet after
         receiving the reply to the query. */
-      silc_server_command_pending(server, SILC_COMMAND_WHOIS, 
+      silc_server_command_pending(server, SILC_COMMAND_WHOIS,
                                  server->cmd_ident,
-                                 silc_server_command_join, 
+                                 silc_server_command_join,
                                  silc_server_command_dup(cmd));
       cmd->pending = TRUE;
       goto out;
     }
 
+    if (!client->data.public_key &&
+       (auth || cauth || channel->ban_list ||
+        (channel->mode & SILC_CHANNEL_MODE_INVITE))) {
+      if (cmd->pending == 2)
+       goto out;
+
+      /* We must retrieve the client's public key by sending
+        GETKEY command. Reprocess this packet after receiving the key */
+      clidp = silc_id_payload_encode(client_id, SILC_ID_CLIENT);
+      silc_server_send_command(server, cmd->sock,
+                              SILC_COMMAND_GETKEY, ++server->cmd_ident,
+                              1, 1, clidp->data, silc_buffer_len(clidp));
+      silc_buffer_free(clidp);
+      silc_server_command_pending(server, SILC_COMMAND_GETKEY,
+                                 server->cmd_ident,
+                                 silc_server_command_join,
+                                 silc_server_command_dup(cmd));
+      cmd->pending = 2;
+      goto out;
+    }
+
     cmd->pending = FALSE;
   }
 
@@ -3055,20 +1909,29 @@ static void silc_server_command_join_channel(SilcServer server,
    */
   if (auth && auth_len && channel->mode & SILC_CHANNEL_MODE_FOUNDER_AUTH) {
     SilcIDListData idata = (SilcIDListData)client;
+    SilcChannelClientEntry chl2;
+    SilcHashTableList htl;
 
     if (channel->founder_key && idata->public_key &&
-       silc_pkcs_public_key_compare(channel->founder_key, 
+       silc_pkcs_public_key_compare(channel->founder_key,
                                     idata->public_key)) {
-      void *auth_data = (channel->founder_method == SILC_AUTH_PASSWORD ?
-                        (void *)channel->founder_passwd : 
-                        (void *)channel->founder_key);
-      uint32 auth_data_len = (channel->founder_method == SILC_AUTH_PASSWORD ?
-                             channel->founder_passwd_len : 0);
-
       /* Check whether the client is to become founder */
-      if (silc_auth_verify_data(auth, auth_len, channel->founder_method, 
-                               auth_data, auth_data_len,
-                               idata->hash, client->id, SILC_ID_CLIENT)) {
+      if (silc_auth_verify_data(auth, auth_len, SILC_AUTH_PUBLIC_KEY,
+                               channel->founder_key, 0, server->sha1hash,
+                               client->id, SILC_ID_CLIENT)) {
+
+       /* There cannot be anyone else as founder on the channel now.  This
+          client is definitely the founder due to this authentication */
+       silc_hash_table_list(channel->user_list, &htl);
+       while (silc_hash_table_get(&htl, NULL, (void *)&chl2))
+         if (chl2->mode & SILC_CHANNEL_UMODE_CHANFO) {
+           chl2->mode &= ~SILC_CHANNEL_UMODE_CHANFO;
+           silc_server_force_cumode_change(server, NULL, channel, chl2,
+                                           chl2->mode);
+           break;
+         }
+       silc_hash_table_list_reset(&htl);
+
        umode = (SILC_CHANNEL_UMODE_CHANOP | SILC_CHANNEL_UMODE_CHANFO);
        founder = TRUE;
       }
@@ -3082,33 +1945,56 @@ static void silc_server_command_join_channel(SilcServer server,
   if (!umode) {
     memset(check, 0, sizeof(check));
     memset(check2, 0, sizeof(check2));
-    strncat(check, client->nickname, strlen(client->nickname));
-    strncat(check, "!", 1);
-    strncat(check, client->username, strlen(client->username));
+    silc_strncat(check, sizeof(check),
+                client->nickname, strlen(client->nickname));
+    silc_strncat(check, sizeof(check), "!", 1);
+    silc_strncat(check, sizeof(check),
+                client->username, strlen(client->username));
     if (!strchr(client->username, '@')) {
-      strncat(check, "@", 1);
-      strncat(check, cmd->sock->hostname, strlen(cmd->sock->hostname));
+      silc_strncat(check, sizeof(check), "@", 1);
+      silc_strncat(check, sizeof(check),
+                  hostname, strlen(hostname));
     }
 
-    strncat(check2, client->nickname, strlen(client->nickname));
+    silc_strncat(check2, sizeof(check2),
+                client->nickname, strlen(client->nickname));
     if (!strchr(client->nickname, '@')) {
-      strncat(check2, "@", 1);
-      strncat(check2, server->server_name, strlen(server->server_name));
+      silc_strncat(check2, sizeof(check2), "@", 1);
+      silc_strncat(check2, sizeof(check2),
+                  SILC_IS_LOCAL(client) ? server->server_name :
+                  client->router->server_name,
+                  SILC_IS_LOCAL(client) ? strlen(server->server_name) :
+                  strlen(client->router->server_name));
     }
-    strncat(check2, "!", 1);
-    strncat(check2, client->username, strlen(client->username));
+    silc_strncat(check2, sizeof(check2), "!", 1);
+    silc_strncat(check2, sizeof(check2),
+                client->username, strlen(client->username));
     if (!strchr(client->username, '@')) {
-      strncat(check2, "@", 1);
-      strncat(check2, cmd->sock->hostname, strlen(cmd->sock->hostname));
+      silc_strncat(check2, sizeof(check2), "@", 1);
+      silc_strncat(check2, sizeof(check2),
+                  hostname, strlen(hostname));
     }
-    
+
     /* Check invite list if channel is invite-only channel */
     if (channel->mode & SILC_CHANNEL_MODE_INVITE) {
       if (!channel->invite_list ||
-         (!silc_string_match(channel->invite_list, check) &&
-          !silc_string_match(channel->invite_list, check2))) {
-       silc_server_command_send_status_reply(cmd, SILC_COMMAND_JOIN,
-                                             SILC_STATUS_ERR_NOT_INVITED);
+         !silc_hash_table_count(channel->invite_list) ||
+         (!silc_server_inviteban_match(server, channel->invite_list,
+                                       3, client->id) &&
+          !silc_server_inviteban_match(server, channel->invite_list,
+                                       2, client->data.public_key) &&
+          !silc_server_inviteban_match(server, channel->invite_list,
+                                       1, client->nickname) &&
+          !silc_server_inviteban_match(server, channel->invite_list,
+                                       1, check) &&
+          !silc_server_inviteban_match(server, channel->invite_list,
+                                       1, check2))) {
+       chidp = silc_id_payload_encode(channel->id, SILC_ID_CHANNEL);
+       silc_server_command_send_status_data(cmd, SILC_COMMAND_JOIN,
+                                            SILC_STATUS_ERR_NOT_INVITED, 0,
+                                            2, chidp->data,
+                                            silc_buffer_len(chidp));
+       silc_buffer_free(chidp);
        goto out;
       }
     }
@@ -3116,22 +2002,38 @@ static void silc_server_command_join_channel(SilcServer server,
     /* Check ban list if it exists. If the client's nickname, server,
        username and/or hostname is in the ban list the access to the
        channel is denied. */
-    if (channel->ban_list) {
-      if (silc_string_match(channel->ban_list, check) ||
-         silc_string_match(channel->ban_list, check2)) {
-       silc_server_command_send_status_reply(
+    if (channel->ban_list && silc_hash_table_count(channel->ban_list)) {
+      if (silc_server_inviteban_match(server, channel->ban_list,
+                                     3, client->id) ||
+         silc_server_inviteban_match(server, channel->ban_list,
+                                     2, client->data.public_key) ||
+         silc_server_inviteban_match(server, channel->ban_list,
+                                     1, client->nickname) ||
+         silc_server_inviteban_match(server, channel->ban_list,
+                                     1, check) ||
+         silc_server_inviteban_match(server, channel->ban_list,
+                                     1, check2)) {
+       chidp = silc_id_payload_encode(channel->id, SILC_ID_CHANNEL);
+       silc_server_command_send_status_data(
                                      cmd, SILC_COMMAND_JOIN,
-                                     SILC_STATUS_ERR_BANNED_FROM_CHANNEL);
+                                     SILC_STATUS_ERR_BANNED_FROM_CHANNEL, 0,
+                                     2, chidp->data,
+                                     silc_buffer_len(chidp));
+       silc_buffer_free(chidp);
        goto out;
       }
     }
-    
+
     /* Check user count limit if set. */
     if (channel->mode & SILC_CHANNEL_MODE_ULIMIT) {
-      if (silc_hash_table_count(channel->user_list) + 1 > 
+      if (silc_hash_table_count(channel->user_list) + 1 >
          channel->user_limit) {
-       silc_server_command_send_status_reply(cmd, SILC_COMMAND_JOIN,
-                                             SILC_STATUS_ERR_CHANNEL_IS_FULL);
+       chidp = silc_id_payload_encode(channel->id, SILC_ID_CHANNEL);
+       silc_server_command_send_status_data(cmd, SILC_COMMAND_JOIN,
+                                            SILC_STATUS_ERR_CHANNEL_IS_FULL,
+                                            0, 2, chidp->data,
+                                            silc_buffer_len(chidp));
+       silc_buffer_free(chidp);
        goto out;
       }
     }
@@ -3141,15 +2043,28 @@ static void silc_server_command_join_channel(SilcServer server,
   if (channel->mode & SILC_CHANNEL_MODE_PASSPHRASE) {
     /* Get passphrase */
     tmp = silc_argument_get_arg_type(cmd->args, 3, &tmp_len);
-    if (tmp) {
-      passphrase = silc_calloc(tmp_len, sizeof(*passphrase));
-      memcpy(passphrase, tmp, tmp_len);
-    }
-  
+    if (tmp)
+      passphrase = silc_memdup(tmp, tmp_len);
+
     if (!passphrase || !channel->passphrase ||
+       strlen(channel->passphrase) != strlen(passphrase) ||
         memcmp(passphrase, channel->passphrase, strlen(channel->passphrase))) {
+      chidp = silc_id_payload_encode(channel->id, SILC_ID_CHANNEL);
+      silc_server_command_send_status_data(cmd, SILC_COMMAND_JOIN,
+                                          SILC_STATUS_ERR_BAD_PASSWORD, 0,
+                                          2, chidp->data,
+                                          silc_buffer_len(chidp));
+      silc_buffer_free(chidp);
+      goto out;
+    }
+  }
+
+  /* Verify channel authentication with channel public keys if set. */
+  if (channel->mode & SILC_CHANNEL_MODE_CHANNEL_AUTH) {
+    if (!silc_server_verify_channel_auth(server, channel, client->id,
+                                        cauth, cauth_len)) {
       silc_server_command_send_status_reply(cmd, SILC_COMMAND_JOIN,
-                                           SILC_STATUS_ERR_BAD_PASSWORD);
+                                           SILC_STATUS_ERR_PERM_DENIED, 0);
       goto out;
     }
   }
@@ -3159,9 +2074,17 @@ static void silc_server_command_join_channel(SilcServer server,
    */
 
   /* Check whether the client already is on the channel */
-  if (silc_server_client_on_channel(client, channel)) {
-    silc_server_command_send_status_reply(cmd, SILC_COMMAND_JOIN,
-                                         SILC_STATUS_ERR_USER_ON_CHANNEL);
+  if (silc_server_client_on_channel(client, channel, NULL)) {
+    clidp = silc_id_payload_encode(client_id, SILC_ID_CLIENT);
+    chidp = silc_id_payload_encode(channel->id, SILC_ID_CHANNEL);
+    silc_server_command_send_status_data2(cmd, SILC_COMMAND_JOIN,
+                                         SILC_STATUS_ERR_USER_ON_CHANNEL, 0,
+                                         2, clidp->data,
+                                         silc_buffer_len(clidp),
+                                         3, chidp->data,
+                                         silc_buffer_len(chidp));
+    silc_buffer_free(clidp);
+    silc_buffer_free(chidp);
     goto out;
   }
 
@@ -3173,8 +2096,8 @@ static void silc_server_command_join_channel(SilcServer server,
     /* Send the channel key. This is broadcasted to the channel but is not
        sent to the client who is joining to the channel. */
     if (!(channel->mode & SILC_CHANNEL_MODE_PRIVKEY))
-      silc_server_send_channel_key(server, NULL, channel, 
-                                  server->server_type == SILC_ROUTER ? 
+      silc_server_send_channel_key(server, NULL, channel,
+                                  server->server_type == SILC_ROUTER ?
                                   FALSE : !server->standalone);
   }
 
@@ -3188,6 +2111,7 @@ static void silc_server_command_join_channel(SilcServer server,
   silc_hash_table_add(channel->user_list, client, chl);
   silc_hash_table_add(client->channels, channel, chl);
   channel->user_count++;
+  channel->disabled = FALSE;
 
   /* Get users on the channel */
   silc_server_get_users_on_channel(server, channel, &user_list, &mode_list,
@@ -3201,34 +2125,85 @@ static void silc_server_command_join_channel(SilcServer server,
   SILC_PUT32_MSB(channel->mode, mode);
   SILC_PUT32_MSB(created, tmp2);
   SILC_PUT32_MSB(user_count, tmp3);
+  if (channel->mode & SILC_CHANNEL_MODE_ULIMIT)
+    SILC_PUT32_MSB(channel->user_limit, ulimit);
 
   if (!(channel->mode & SILC_CHANNEL_MODE_PRIVKEY)) {
-    tmp = silc_id_id2str(channel->id, SILC_ID_CHANNEL);
-    keyp = silc_channel_key_payload_encode(SILC_ID_CHANNEL_LEN, tmp, 
-                                          strlen(channel->channel_key->
-                                                 cipher->name),
-                                          channel->channel_key->cipher->name,
+    unsigned char cid[32];
+    SilcUInt32 cid_len;
+    silc_id_id2str(channel->id, SILC_ID_CHANNEL, cid, sizeof(cid), &cid_len);
+    cipher = silc_cipher_get_name(channel->send_key);
+    keyp = silc_channel_key_payload_encode(cid_len, cid,
+                                          strlen(cipher), cipher,
                                           channel->key_len / 8, channel->key);
-    silc_free(tmp);
   }
 
-  reply = 
+  if (channel->founder_key)
+    fkey = silc_public_key_payload_encode(channel->founder_key);
+
+  /* Encode invite list */
+  invite_list = NULL;
+  if (channel->invite_list && silc_hash_table_count(channel->invite_list)) {
+    SilcHashTableList htl;
+
+    invite_list = silc_buffer_alloc_size(2);
+    silc_buffer_format(invite_list,
+                      SILC_STR_UI_SHORT(silc_hash_table_count(
+                                         channel->invite_list)),
+                      SILC_STR_END);
+
+    silc_hash_table_list(channel->invite_list, &htl);
+    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),
+                                                    SILC_PTR_TO_32(plen));
+    silc_hash_table_list_reset(&htl);
+  }
+
+  /* Encode ban list */
+  ban_list = NULL;
+  if (channel->ban_list && silc_hash_table_count(channel->ban_list)) {
+    SilcHashTableList htl;
+
+    ban_list = silc_buffer_alloc_size(2);
+    silc_buffer_format(ban_list,
+                      SILC_STR_UI_SHORT(silc_hash_table_count(
+                                         channel->ban_list)),
+                      SILC_STR_END);
+
+    silc_hash_table_list(channel->ban_list, &htl);
+    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),
+                                                 SILC_PTR_TO_32(plen));
+    silc_hash_table_list_reset(&htl);
+  }
+
+  if (channel->channel_pubkeys)
+    chpklist = silc_server_get_channel_pk_list(server, channel, FALSE, FALSE);
+
+  reply =
     silc_command_reply_payload_encode_va(SILC_COMMAND_JOIN,
-                                        SILC_STATUS_OK, ident, 13,
+                                        SILC_STATUS_OK, 0, ident, 16,
                                         2, channel->channel_name,
                                         strlen(channel->channel_name),
-                                        3, chidp->data, chidp->len,
-                                        4, clidp->data, clidp->len,
+                                        3, chidp->data,
+                                        silc_buffer_len(chidp),
+                                        4, clidp->data,
+                                        silc_buffer_len(clidp),
                                         5, mode, 4,
                                         6, tmp2, 4,
-                                        7, keyp ? keyp->data : NULL, 
-                                        keyp ? keyp->len : 0,
-                                        8, channel->ban_list, 
-                                        channel->ban_list ?
-                                        strlen(channel->ban_list) : 0,
-                                        9, channel->invite_list,
-                                        channel->invite_list ?
-                                        strlen(channel->invite_list) : 0,
+                                        7, keyp ? keyp->data : NULL,
+                                        keyp ? silc_buffer_len(keyp) : 0,
+                                        8, ban_list ? ban_list->data : NULL,
+                                        ban_list ?
+                                        silc_buffer_len(ban_list): 0,
+                                        9, invite_list ? invite_list->data :
+                                        NULL,
+                                        invite_list ?
+                                        silc_buffer_len(invite_list) : 0,
                                         10, channel->topic,
                                         channel->topic ?
                                         strlen(channel->topic) : 0,
@@ -3236,13 +2211,27 @@ static void silc_server_command_join_channel(SilcServer server,
                                         strlen(silc_hmac_get_name(channel->
                                                                   hmac)),
                                         12, tmp3, 4,
-                                        13, user_list->data, user_list->len,
-                                        14, mode_list->data, 
-                                        mode_list->len);
+                                        13, user_list->data,
+                                        silc_buffer_len(user_list),
+                                        14, mode_list->data,
+                                        silc_buffer_len(mode_list),
+                                        15, fkey ? fkey->data : NULL,
+                                        fkey ? silc_buffer_len(fkey) : 0,
+                                        16, chpklist ? chpklist->data : NULL,
+                                        chpklist ? silc_buffer_len(chpklist) : 0,
+                                        17, (channel->mode &
+                                             SILC_CHANNEL_MODE_ULIMIT ?
+                                             ulimit : NULL),
+                                        (channel->mode &
+                                         SILC_CHANNEL_MODE_ULIMIT ?
+                                         sizeof(ulimit) : 0));
 
   /* Send command reply */
-  silc_server_packet_send(server, sock, SILC_PACKET_COMMAND_REPLY, 0, 
-                         reply->data, reply->len, FALSE);
+  silc_server_packet_send(server, sock, SILC_PACKET_COMMAND_REPLY, 0,
+                         reply->data, silc_buffer_len(reply));
+
+  /* Statistics */
+  cmd->server->stat.commands_sent++;
 
   /* Send JOIN notify to locally connected clients on the channel. If
      we are normal server then router will send or have sent JOIN notify
@@ -3250,162 +2239,296 @@ static void silc_server_command_join_channel(SilcServer server,
      we'll ignore it (in packet_receive.c) so we must send it here. If
      we are router then this will send it to local clients and local
      servers. */
-  silc_server_send_notify_to_channel(server, NULL, channel, FALSE, 
+  SILC_LOG_DEBUG(("Send JOIN notify to channel"));
+  silc_server_send_notify_to_channel(server, NULL, channel, FALSE, TRUE,
                                     SILC_NOTIFY_TYPE_JOIN, 2,
-                                    clidp->data, clidp->len,
-                                    chidp->data, chidp->len);
+                                    clidp->data, silc_buffer_len(clidp),
+                                    chidp->data, silc_buffer_len(chidp));
+
+  /* Update statistics */
+  server->stat.my_chanclients++;
+  if (server->server_type == SILC_ROUTER) {
+    server->stat.cell_chanclients++;
+    server->stat.chanclients++;
+  }
 
   if (!cmd->pending) {
     /* Send JOIN notify packet to our primary router */
-    if (!server->standalone)
-      silc_server_send_notify_join(server, server->router->connection,
-                                  server->server_type == SILC_ROUTER ?
-                                  TRUE : FALSE, channel, client->id);
+    silc_server_send_notify_join(server, SILC_PRIMARY_ROUTE(server),
+                                SILC_BROADCAST(server), channel, client->id);
 
     if (keyp)
       /* Distribute the channel key to all backup routers. */
       silc_server_backup_send(server, NULL, SILC_PACKET_CHANNEL_KEY, 0,
-                             keyp->data, keyp->len, FALSE, TRUE);
-  }
-
-  /* If client became founder by providing correct founder auth data
-     notify the mode change to the channel. */
-  if (founder) {
-    SILC_PUT32_MSB(chl->mode, mode);
-    silc_server_send_notify_to_channel(server, NULL, channel, FALSE, 
-                                      SILC_NOTIFY_TYPE_CUMODE_CHANGE, 3,
-                                      clidp->data, clidp->len,
-                                      mode, 4, clidp->data, clidp->len);
-      
-    /* Set CUMODE notify type to network */
-    if (!server->standalone)
-      silc_server_send_notify_cumode(server, server->router->connection,
-                                    server->server_type == SILC_ROUTER ? 
-                                    TRUE : FALSE, channel,
-                                    chl->mode, client->id, SILC_ID_CLIENT,
-                                    client->id);
+                             keyp->data, silc_buffer_len(keyp), FALSE, TRUE);
+
+    /* If client became founder by providing correct founder auth data
+       notify the mode change to the channel. */
+    if (founder) {
+      SILC_PUT32_MSB(chl->mode, mode);
+      SILC_LOG_DEBUG(("Send CUMODE_CHANGE notify to channel"));
+      silc_server_send_notify_to_channel(server, NULL, channel, FALSE, TRUE,
+                                        SILC_NOTIFY_TYPE_CUMODE_CHANGE, 4,
+                                        clidp->data,
+                                        silc_buffer_len(clidp),
+                                        mode, 4, clidp->data,
+                                        silc_buffer_len(clidp),
+                                        fkey ? fkey->data : NULL,
+                                        fkey ? silc_buffer_len(fkey) : 0);
+    }
   }
 
+  /* Set CUMODE notify type to network */
+  if (founder)
+    silc_server_send_notify_cumode(server, SILC_PRIMARY_ROUTE(server),
+                                  SILC_BROADCAST(server), channel,
+                                  chl->mode, client->id, SILC_ID_CLIENT,
+                                  client->id, channel->founder_key);
+
   silc_buffer_free(reply);
   silc_buffer_free(clidp);
   silc_buffer_free(chidp);
   silc_buffer_free(keyp);
   silc_buffer_free(user_list);
   silc_buffer_free(mode_list);
+  silc_buffer_free(fkey);
+  silc_buffer_free(chpklist);
+  silc_buffer_free(invite_list);
+  silc_buffer_free(ban_list);
 
  out:
+  if (passphrase)
+    memset(passphrase, 0, strlen(passphrase));
   silc_free(passphrase);
 }
 
-/* Server side of command JOIN. Joins client into requested channel. If 
+/* 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;
   SilcServer server = cmd->server;
-  unsigned char *auth;
-  uint32 tmp_len, auth_len;
-  char *tmp, *channel_name = NULL, *cipher, *hmac;
+  SilcIDListData idata = silc_packet_get_context(cmd->sock);
+  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;
-  uint32 umode = 0;
-  bool created = FALSE, create_key = TRUE;
-  SilcClientID *client_id;
+  SilcUInt32 umode = 0;
+  SilcBool created = FALSE, create_key = TRUE;
+  SilcID id;
 
-  SILC_SERVER_COMMAND_CHECK(SILC_COMMAND_JOIN, cmd, 2, 6);
+  SILC_SERVER_COMMAND_CHECK(SILC_COMMAND_JOIN, cmd, 2, 7);
 
   /* Get channel name */
   tmp = silc_argument_get_arg_type(cmd->args, 1, &tmp_len);
   if (!tmp) {
     silc_server_command_send_status_reply(cmd, SILC_COMMAND_JOIN,
-                                         SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
+                                         SILC_STATUS_ERR_NOT_ENOUGH_PARAMS,
+                                         0);
     goto out;
   }
-  channel_name = tmp;
 
-  if (tmp_len > 256)
-    channel_name[255] = '\0';
+  /* Truncate over long channel names */
+  if (tmp_len > 256) {
+    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 (silc_server_name_bad_chars(channel_name, tmp_len) == TRUE) {
-    silc_server_command_send_status_reply(cmd, SILC_COMMAND_JOIN,
-                                         SILC_STATUS_ERR_BAD_CHANNEL);
-    goto out;
+  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), 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;
+      }
+    }
   }
 
-  /* Get Client ID of the client who is joining to the channel */
-  tmp = silc_argument_get_arg_type(cmd->args, 2, &tmp_len);
-  if (!tmp) {
+  /* Check for valid channel name.  This is cached, the original is saved
+     in the channel context. */
+  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_NOT_ENOUGH_PARAMS);
+                                         SILC_STATUS_ERR_BAD_CHANNEL, 0);
     goto out;
   }
-  client_id = silc_id_payload_parse_id(tmp, tmp_len);
-  if (!client_id) {
+
+  /* Get Client ID of the client who is joining to the channel */
+  if (!silc_argument_get_decoded(cmd->args, 2, SILC_ARGUMENT_ID, &id, NULL)) {
     silc_server_command_send_status_reply(cmd, SILC_COMMAND_JOIN,
-                                         SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
+                                         SILC_STATUS_ERR_NO_CLIENT_ID,
+                                         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);
   hmac = silc_argument_get_arg_type(cmd->args, 5, NULL);
   auth = silc_argument_get_arg_type(cmd->args, 6, &auth_len);
+  cauth = silc_argument_get_arg_type(cmd->args, 7, &cauth_len);
 
   /* See if the channel exists */
-  channel = silc_idlist_find_channel_by_name(server->local_list, 
-                                            channel_name, NULL);
+  channel = silc_idlist_find_channel_by_name(server->local_list,
+                                            channel_namec, NULL);
+
+  if (idata->conn_type == SILC_CONN_CLIENT) {
+    SilcClientEntry entry = (SilcClientEntry)idata;
+    if (!entry) {
+      silc_server_command_send_status_reply(cmd, SILC_COMMAND_JOIN,
+                                           SILC_STATUS_ERR_NOT_ENOUGH_PARAMS,
+                                           0);
+      goto out;
+    }
 
-  if (cmd->sock->type == SILC_SOCKET_TYPE_CLIENT) {
-    SilcClientEntry entry = (SilcClientEntry)cmd->sock->user_data;
-    client_id = silc_id_dup(entry->id, SILC_ID_CLIENT);
+#ifndef SILC_DIST_INPLACE
+    /* Limit how many channels client can join */
+    if (!cmd->pending && entry->channels &&
+       silc_hash_table_count(entry->channels) >=
+       server->config->param.chlimit) {
+      silc_server_command_send_status_reply(cmd, SILC_COMMAND_JOIN,
+                                           SILC_STATUS_ERR_RESOURCE_LIMIT,
+                                           0);
+      goto out;
+    }
+#endif /* SILC_DIST_INPLACE */
 
-    if (!channel || channel->disabled) {
-      /* Channel not found */
+    if (!channel ||
+       (channel->disabled && server->server_type != SILC_ROUTER)) {
+      /* Channel not found or not valid */
 
-      /* If we are standalone server we don't have a router, we just create 
-        the channel by ourselves. */
+      /* If we are standalone server we don't have a router, we just create
+        the channel by ourselves (unless it existed). */
       if (server->standalone) {
-       channel = silc_server_create_new_channel(server, server->id, cipher, 
-                                                hmac, channel_name, TRUE);
        if (!channel) {
-         silc_server_command_send_status_reply(
-                                        cmd, SILC_COMMAND_JOIN,
-                                        SILC_STATUS_ERR_UNKNOWN_ALGORITHM);
-         goto out;
+         channel = silc_server_create_new_channel(server, server->id, cipher,
+                                                  hmac, channel_name, TRUE);
+         if (!channel) {
+           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;
+         }
+
+         umode = (SILC_CHANNEL_UMODE_CHANOP | SILC_CHANNEL_UMODE_CHANFO);
+         created = TRUE;
+         create_key = FALSE;
        }
-       
-       umode = (SILC_CHANNEL_UMODE_CHANOP | SILC_CHANNEL_UMODE_CHANFO);
-       created = TRUE;
-       create_key = FALSE;
-       
       } else {
 
-       /* The channel does not exist on our server. If we are normal server 
+       /* The channel does not exist on our server. If we are normal server
           we will send JOIN command to our router which will handle the
-          joining procedure (either creates the channel if it doesn't exist 
+          joining procedure (either creates the channel if it doesn't exist
           or joins the client to it). */
        if (server->server_type != SILC_ROUTER) {
          SilcBuffer tmpbuf;
-         uint16 old_ident;
+         SilcUInt16 old_ident;
 
          /* If this is pending command callback then we've resolved
             it and it didn't work, return since we've notified the
             client already in the command reply callback. */
          if (cmd->pending)
            goto out;
-         
+
+         /* Statistics */
+         cmd->server->stat.commands_sent++;
+
          old_ident = silc_command_get_ident(cmd->payload);
          silc_command_set_ident(cmd->payload, ++server->cmd_ident);
          tmpbuf = silc_command_payload_encode_payload(cmd->payload);
-         
+
          /* Send JOIN command to our router */
-         silc_server_packet_send(server, (SilcSocketConnection)
-                                 server->router->connection,
+         silc_server_packet_send(server, SILC_PRIMARY_ROUTE(server),
                                  SILC_PACKET_COMMAND, cmd->packet->flags,
-                                 tmpbuf->data, tmpbuf->len, TRUE);
-         
+                                 tmpbuf->data, silc_buffer_len(tmpbuf));
+
          /* Reprocess this packet after received reply from router */
-         silc_server_command_pending(server, SILC_COMMAND_JOIN, 
+         silc_server_command_pending(server, SILC_COMMAND_JOIN,
                                      silc_command_get_ident(cmd->payload),
                                      silc_server_command_join,
                                      silc_server_command_dup(cmd));
@@ -3414,18 +2537,32 @@ SILC_SERVER_CMD_FUNC(join)
          silc_buffer_free(tmpbuf);
          goto out;
        }
-       
+
        /* We are router and the channel does not seem exist so we will check
           our global list as well for the channel. */
-       channel = silc_idlist_find_channel_by_name(server->global_list, 
-                                                  channel_name, NULL);
+       channel = silc_idlist_find_channel_by_name(server->global_list,
+                                                  channel_namec, NULL);
        if (!channel) {
          /* Channel really does not exist, create it */
-         channel = silc_server_create_new_channel(server, server->id, cipher, 
+         channel = silc_server_create_new_channel(server, server->id, cipher,
                                                   hmac, channel_name, TRUE);
          if (!channel) {
-           silc_server_command_send_status_reply(cmd, SILC_COMMAND_JOIN,
-                                      SILC_STATUS_ERR_UNKNOWN_ALGORITHM);
+           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;
          }
 
@@ -3442,21 +2579,35 @@ SILC_SERVER_CMD_FUNC(join)
       /* If the command came from router and we are normal server then
         something went wrong with the joining as the channel was not found.
         We can't do anything else but ignore this. */
-      if (cmd->sock->type == SILC_SOCKET_TYPE_ROUTER ||
+      if (idata->conn_type == SILC_CONN_ROUTER ||
          server->server_type != SILC_ROUTER)
        goto out;
-      
+
       /* We are router and the channel does not seem exist so we will check
         our global list as well for the channel. */
-      channel = silc_idlist_find_channel_by_name(server->global_list, 
-                                                channel_name, NULL);
+      channel = silc_idlist_find_channel_by_name(server->global_list,
+                                                channel_namec, NULL);
       if (!channel) {
        /* Channel really does not exist, create it */
-       channel = silc_server_create_new_channel(server, server->id, cipher, 
+       channel = silc_server_create_new_channel(server, server->id, cipher,
                                                 hmac, channel_name, TRUE);
        if (!channel) {
-         silc_server_command_send_status_reply(cmd, SILC_COMMAND_JOIN,
-                                      SILC_STATUS_ERR_UNKNOWN_ALGORITHM);
+         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;
        }
 
@@ -3469,34 +2620,45 @@ SILC_SERVER_CMD_FUNC(join)
 
   /* Check whether the channel was created by our router */
   if (cmd->pending && context2) {
-    SilcServerCommandReplyContext reply = 
-      (SilcServerCommandReplyContext)context2;
+    SilcServerCommandReplyContext reply = context2;
 
     if (silc_command_get(reply->payload) == SILC_COMMAND_JOIN) {
       tmp = silc_argument_get_arg_type(reply->args, 6, NULL);
       SILC_GET32_MSB(created, tmp);
       if (silc_argument_get_arg_type(reply->args, 7, NULL))
        create_key = FALSE;     /* Router returned the key already */
+
+      if (silc_command_get_status(reply->payload, NULL, NULL) &&
+         channel->mode & SILC_CHANNEL_MODE_PASSPHRASE) {
+       /* Save channel passphrase, if user provided it successfully */
+       unsigned char *pa;
+       SilcUInt32 pa_len;
+       pa = silc_argument_get_arg_type(cmd->args, 3, &pa_len);
+       if (pa) {
+         silc_free(channel->passphrase);
+         channel->passphrase = silc_memdup(pa, pa_len);
+       }
+      }
     }
 
     if (silc_command_get(reply->payload) == SILC_COMMAND_WHOIS &&
-       !silc_hash_table_count(channel->user_list))
+       !channel->disabled && !silc_hash_table_count(channel->user_list))
       created = TRUE;
   }
 
   /* If the channel does not have global users and is also empty the client
      will be the channel founder and operator. */
-  if (!channel->global_users && !silc_hash_table_count(channel->user_list))
+  if (!channel->disabled &&
+      !channel->global_users && !silc_hash_table_count(channel->user_list))
     umode = (SILC_CHANNEL_UMODE_CHANOP | SILC_CHANNEL_UMODE_CHANFO);
 
   /* Join to the channel */
-  silc_server_command_join_channel(server, cmd, channel, client_id,
+  silc_server_command_join_channel(server, cmd, channel, SILC_ID_GET_ID(id),
                                   created, create_key, umode,
-                                  auth, auth_len);
-
-  silc_free(client_id);
+                                  auth, auth_len, cauth, cauth_len);
 
  out:
+  silc_free(channel_namec);
   silc_server_command_free(cmd);
 }
 
@@ -3507,22 +2669,33 @@ SILC_SERVER_CMD_FUNC(motd)
 {
   SilcServerCommandContext cmd = (SilcServerCommandContext)context;
   SilcServer server = cmd->server;
-  SilcBuffer packet, idp;
-  char *motd, *dest_server;
-  uint32 motd_len;
-  uint16 ident = silc_command_get_ident(cmd->payload);
-  
+  SilcBuffer idp;
+  char *motd, *dest_server = NULL;
+  SilcUInt32 motd_len;
+  SilcUInt16 ident = silc_command_get_ident(cmd->payload);
+
   SILC_SERVER_COMMAND_CHECK(SILC_COMMAND_MOTD, cmd, 1, 1);
 
   /* Get server name */
   dest_server = silc_argument_get_arg_type(cmd->args, 1, NULL);
   if (!dest_server) {
     silc_server_command_send_status_reply(cmd, SILC_COMMAND_MOTD,
-                                         SILC_STATUS_ERR_NO_SUCH_SERVER);
+                                         SILC_STATUS_ERR_NOT_ENOUGH_PARAMS,
+                                         0);
+    goto out;
+  }
+
+  /* Check server name */
+  dest_server = silc_identifier_check(dest_server, strlen(dest_server),
+                                     SILC_STRING_UTF8, 256, NULL);
+  if (!dest_server) {
+    silc_server_command_send_status_reply(cmd, SILC_COMMAND_MOTD,
+                                         SILC_STATUS_ERR_BAD_SERVER,
+                                         0);
     goto out;
   }
 
-  if (!strncasecmp(dest_server, server->server_name, strlen(dest_server))) {
+  if (!memcmp(dest_server, server->server_name, strlen(dest_server))) {
     /* Send our MOTD */
 
     idp = silc_id_payload_encode(server->id_entry->id, SILC_ID_SERVER);
@@ -3530,25 +2703,27 @@ SILC_SERVER_CMD_FUNC(motd)
     if (server->config && server->config->server_info &&
        server->config->server_info->motd_file) {
       /* Send motd */
-      motd = silc_file_readfile(server->config->server_info->motd_file, &motd_len);
-      if (!motd)
+      motd = silc_file_readfile(server->config->server_info->motd_file,
+                               &motd_len);
+      if (!motd) {
+       /* No motd */
+       silc_server_send_command_reply(server, cmd->sock, SILC_COMMAND_MOTD,
+                                      SILC_STATUS_OK, 0, ident, 1,
+                                      2, idp->data, silc_buffer_len(idp));
        goto out;
-      
+      }
+
       motd[motd_len] = 0;
-      packet = silc_command_reply_payload_encode_va(SILC_COMMAND_MOTD,
-                                                   SILC_STATUS_OK, ident, 2,
-                                                   2, idp, idp->len,
-                                                   3, motd, motd_len);
+      silc_server_send_command_reply(server, cmd->sock, SILC_COMMAND_MOTD,
+                                    SILC_STATUS_OK, 0, ident, 2,
+                                    2, idp->data, silc_buffer_len(idp),
+                                    3, motd, motd_len);
     } else {
       /* No motd */
-      packet = silc_command_reply_payload_encode_va(SILC_COMMAND_MOTD,
-                                                   SILC_STATUS_OK, ident, 1,
-                                                   2, idp, idp->len);
+      silc_server_send_command_reply(server, cmd->sock, SILC_COMMAND_MOTD,
+                                    SILC_STATUS_OK, 0, ident, 1,
+                                    2, idp->data, silc_buffer_len(idp));
     }
-
-    silc_server_packet_send(server, cmd->sock, SILC_PACKET_COMMAND_REPLY, 0, 
-                           packet->data, packet->len, FALSE);
-    silc_buffer_free(packet);
     silc_buffer_free(idp);
   } else {
     SilcServerEntry entry;
@@ -3561,11 +2736,14 @@ SILC_SERVER_CMD_FUNC(motd)
                                              dest_server, TRUE, NULL);
     }
 
-    if (server->server_type != SILC_SERVER && !cmd->pending && 
+    if (server->server_type != SILC_SERVER && !cmd->pending &&
        entry && !entry->motd) {
       /* Send to the server */
       SilcBuffer tmpbuf;
-      uint16 old_ident;
+      SilcUInt16 old_ident;
+
+      /* Statistics */
+      cmd->server->stat.commands_sent++;
 
       old_ident = silc_command_get_ident(cmd->payload);
       silc_command_set_ident(cmd->payload, ++server->cmd_ident);
@@ -3573,10 +2751,10 @@ SILC_SERVER_CMD_FUNC(motd)
 
       silc_server_packet_send(server, entry->connection,
                              SILC_PACKET_COMMAND, cmd->packet->flags,
-                             tmpbuf->data, tmpbuf->len, TRUE);
+                             tmpbuf->data, silc_buffer_len(tmpbuf));
 
       /* Reprocess this packet after received reply from router */
-      silc_server_command_pending(server, SILC_COMMAND_MOTD, 
+      silc_server_command_pending(server, SILC_COMMAND_MOTD,
                                  silc_command_get_ident(cmd->payload),
                                  silc_server_command_motd,
                                  silc_server_command_dup(cmd));
@@ -3586,21 +2764,27 @@ SILC_SERVER_CMD_FUNC(motd)
       goto out;
     }
 
-    if (!entry && !cmd->pending && !server->standalone) {
+    /* Send to primary router only if we don't know the server
+     * the client requested or if the server is not locally connected */
+    if ((!entry || !(entry->data.status & SILC_IDLIST_STATUS_LOCAL))
+       && !cmd->pending && !server->standalone) {
       /* Send to the primary router */
       SilcBuffer tmpbuf;
-      uint16 old_ident;
+      SilcUInt16 old_ident;
+
+      /* Statistics */
+      cmd->server->stat.commands_sent++;
 
       old_ident = silc_command_get_ident(cmd->payload);
       silc_command_set_ident(cmd->payload, ++server->cmd_ident);
       tmpbuf = silc_command_payload_encode_payload(cmd->payload);
 
-      silc_server_packet_send(server, server->router->connection,
+      silc_server_packet_send(server, SILC_PRIMARY_ROUTE(server),
                              SILC_PACKET_COMMAND, cmd->packet->flags,
-                             tmpbuf->data, tmpbuf->len, TRUE);
+                             tmpbuf->data, silc_buffer_len(tmpbuf));
 
       /* Reprocess this packet after received reply from router */
-      silc_server_command_pending(server, SILC_COMMAND_MOTD, 
+      silc_server_command_pending(server, SILC_COMMAND_MOTD,
                                  silc_command_get_ident(cmd->payload),
                                  silc_server_command_motd,
                                  silc_server_command_dup(cmd));
@@ -3611,1051 +2795,1676 @@ SILC_SERVER_CMD_FUNC(motd)
     }
 
     if (!entry) {
-      silc_server_command_send_status_reply(cmd, SILC_COMMAND_INFO,
-                                           SILC_STATUS_ERR_NO_SUCH_SERVER);
+      silc_free(dest_server);
+      dest_server = silc_argument_get_arg_type(cmd->args, 1, NULL);
+      silc_server_command_send_status_data(cmd, SILC_COMMAND_MOTD,
+                                          SILC_STATUS_ERR_NO_SUCH_SERVER, 0,
+                                          2, dest_server,
+                                          strlen(dest_server));
+      dest_server = NULL;
       goto out;
     }
 
-    idp = silc_id_payload_encode(server->id_entry->id, SILC_ID_SERVER);
-    packet = silc_command_reply_payload_encode_va(SILC_COMMAND_MOTD,
-                                                 SILC_STATUS_OK, ident, 2,
-                                                 2, idp, idp->len,
-                                                 3, entry->motd,
-                                                 entry->motd ? 
-                                                 strlen(entry->motd) : 0);
-    silc_server_packet_send(server, cmd->sock, SILC_PACKET_COMMAND_REPLY, 0, 
-                           packet->data, packet->len, FALSE);
-    silc_buffer_free(packet);
+    idp = silc_id_payload_encode(entry->id, SILC_ID_SERVER);
+    silc_server_send_command_reply(server, cmd->sock, SILC_COMMAND_MOTD,
+                                  SILC_STATUS_OK, 0, ident, 2,
+                                  2, idp->data, silc_buffer_len(idp),
+                                  3, entry->motd,
+                                  entry->motd ?
+                                  strlen(entry->motd) : 0);
     silc_buffer_free(idp);
   }
 
- out:
-  silc_server_command_free(cmd);
-}
+ out:
+  silc_free(dest_server);
+  silc_server_command_free(cmd);
+}
+
+/* Server side of command UMODE. Client can use this command to set/unset
+   user mode. Client actually cannot set itself to be as server/router
+   operator so this can be used only to unset the modes. */
+
+SILC_SERVER_CMD_FUNC(umode)
+{
+  SilcServerCommandContext cmd = (SilcServerCommandContext)context;
+  SilcServer server = cmd->server;
+  SilcClientEntry client = silc_packet_get_context(cmd->sock);
+  unsigned char *tmp_mask, m[4];
+  SilcUInt32 mask = 0;
+  SilcUInt16 ident = silc_command_get_ident(cmd->payload);
+  SilcBool set_mask = FALSE;
+
+  if (client->data.conn_type != SILC_CONN_CLIENT || !client)
+    goto out;
+
+  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) {
+    SILC_GET32_MSB(mask, tmp_mask);
+    set_mask = TRUE;
+  }
+
+  if (set_mask) {
+    /* Check that mode changing is allowed. */
+    if (!silc_server_check_umode_rights(server, client, mask)) {
+      silc_server_command_send_status_reply(cmd, SILC_COMMAND_UMODE,
+                                           SILC_STATUS_ERR_PERM_DENIED, 0);
+      goto out;
+    }
+
+    /* Anonymous mode cannot be set by client */
+    if (mask & SILC_UMODE_ANONYMOUS &&
+       !(client->mode & SILC_UMODE_ANONYMOUS)) {
+      silc_server_command_send_status_reply(cmd, SILC_COMMAND_UMODE,
+                                           SILC_STATUS_ERR_PERM_DENIED, 0);
+      goto out;
+    }
+
+    /* Update statistics */
+    if (mask & SILC_UMODE_GONE) {
+      if (!(client->mode & SILC_UMODE_GONE))
+       server->stat.my_aways++;
+    } else {
+      if (client->mode & SILC_UMODE_GONE)
+       server->stat.my_aways--;
+    }
+
+    /* If the client has anonymous mode set, preserve it. */
+    if (client->mode & SILC_UMODE_ANONYMOUS)
+      mask |= SILC_UMODE_ANONYMOUS;
+
+    /* Change the mode */
+    client->mode = mask;
+
+    /* Send UMODE change to primary router */
+    silc_server_send_notify_umode(server, SILC_PRIMARY_ROUTE(server),
+                                 SILC_BROADCAST(server), client->id,
+                                 client->mode);
+
+    /* Check if anyone is watching this nickname */
+    if (server->server_type == SILC_ROUTER)
+      silc_server_check_watcher_list(server, client, NULL,
+                                    SILC_NOTIFY_TYPE_UMODE_CHANGE);
+  }
+
+  /* Send command reply to sender */
+  SILC_PUT32_MSB(client->mode, m);
+  silc_server_send_command_reply(server, cmd->sock, SILC_COMMAND_UMODE,
+                                SILC_STATUS_OK, 0, ident, 1,
+                                2, m, sizeof(m));
+
+ out:
+  silc_server_command_free(cmd);
+}
+
+/* Server side command of CMODE. Changes channel mode */
+
+SILC_SERVER_CMD_FUNC(cmode)
+{
+  SilcServerCommandContext cmd = (SilcServerCommandContext)context;
+  SilcServer server = cmd->server;
+  SilcClientEntry client = silc_packet_get_context(cmd->sock);
+  SilcIDListData idata = (SilcIDListData)client;
+  SilcID id;
+  SilcChannelEntry channel;
+  SilcChannelClientEntry chl;
+  SilcBuffer cidp;
+  unsigned char *tmp, *tmp_id, *tmp_mask, *chpkdata = NULL;
+  char *cipher = NULL, *hmac = NULL, *passphrase = NULL, ulimit[4];
+  SilcUInt32 mode_mask = 0, old_mask = 0, tmp_len, tmp_len2, chpklen;
+  SilcUInt16 ident = silc_command_get_ident(cmd->payload);
+  SilcBool set_mask = FALSE, set_chpk = FALSE;
+  SilcPublicKey founder_key = NULL;
+  SilcBuffer fkey = NULL, chpklist = NULL;
+  SilcBufferStruct chpk;
+
+  if (!client) {
+    silc_server_command_free(cmd);
+    return;
+  }
+
+  SILC_SERVER_COMMAND_CHECK(SILC_COMMAND_CMODE, cmd, 1, 9);
+
+  /* Get Channel ID */
+  if (!silc_argument_get_decoded(cmd->args, 1, SILC_ARGUMENT_ID, &id, NULL)) {
+    silc_server_command_send_status_reply(cmd, SILC_COMMAND_CMODE,
+                                         SILC_STATUS_ERR_NO_CHANNEL_ID, 0);
+    silc_server_command_free(cmd);
+    return;
+  }
+
+  /* Get channel entry */
+  channel = silc_idlist_find_channel_by_id(server->local_list,
+                                          SILC_ID_GET_ID(id), NULL);
+  if (!channel) {
+    channel = silc_idlist_find_channel_by_id(server->global_list,
+                                            SILC_ID_GET_ID(id), NULL);
+    if (!channel) {
+      tmp_id = silc_argument_get_arg_type(cmd->args, 1, &tmp_len2);
+      silc_server_command_send_status_data(cmd, SILC_COMMAND_CMODE,
+                                          SILC_STATUS_ERR_NO_SUCH_CHANNEL_ID,
+                                          0, 2, tmp_id, tmp_len2);
+      silc_server_command_free(cmd);
+      return;
+    }
+  }
+  old_mask = channel->mode;
+
+  /* Get the channel mode mask */
+  tmp_mask = silc_argument_get_arg_type(cmd->args, 2, &tmp_len);
+  if (tmp_mask) {
+    SILC_GET32_MSB(mode_mask, tmp_mask);
+    set_mask = TRUE;
+  }
+
+  /* Check whether this client is on the channel */
+  if (!silc_server_client_on_channel(client, channel, &chl)) {
+    tmp_id = silc_argument_get_arg_type(cmd->args, 1, &tmp_len2);
+    silc_server_command_send_status_data(cmd, SILC_COMMAND_CMODE,
+                                        SILC_STATUS_ERR_NOT_ON_CHANNEL, 0,
+                                        2, tmp_id, tmp_len2);
+    goto out;
+  }
+
+  /* Check that client has rights to change any requested channel modes */
+  if (set_mask && !silc_server_check_cmode_rights(server, channel, chl,
+                                                 mode_mask)) {
+    SILC_LOG_DEBUG(("Client does not have rights to change mode"));
+    tmp_id = silc_argument_get_arg_type(cmd->args, 1, &tmp_len2);
+    silc_server_command_send_status_data(
+                            cmd, SILC_COMMAND_CMODE,
+                            (!(chl->mode & SILC_CHANNEL_UMODE_CHANOP) ?
+                             SILC_STATUS_ERR_NO_CHANNEL_PRIV :
+                             SILC_STATUS_ERR_NO_CHANNEL_FOPRIV), 0,
+                            2, tmp_id, tmp_len2);
+    goto out;
+  }
+
+  /* If mode mask was not sent as argument then merely return the current
+     mode mask, founder key and channel public key list to the sender. */
+  if (!set_mask) {
+    unsigned char m[4];
+    SILC_PUT32_MSB(channel->mode, m);
+    if (channel->founder_key)
+      fkey = silc_public_key_payload_encode(channel->founder_key);
+    if (channel->channel_pubkeys)
+      chpklist = silc_server_get_channel_pk_list(server, channel,
+                                                FALSE, FALSE);
+    tmp_id = silc_argument_get_arg_type(cmd->args, 1, &tmp_len2);
+    silc_server_send_command_reply(server, cmd->sock, SILC_COMMAND_CMODE,
+                                  SILC_STATUS_OK, 0, ident, 4,
+                                  2, tmp_id, tmp_len2,
+                                  3, m, sizeof(m),
+                                  4, fkey ? fkey->data : NULL,
+                                  fkey ? silc_buffer_len(fkey) : 0,
+                                  5, chpklist ? chpklist->data : NULL,
+                                  chpklist ? silc_buffer_len(chpklist) : 0);
+    goto out;
+  }
+
+  /*
+   * Check the modes. Modes that requires nothing special operation are
+   * not checked here.
+   */
+
+  if (mode_mask & SILC_CHANNEL_MODE_PRIVKEY) {
+    /* Channel uses private keys to protect traffic. Client(s) has set the
+       key locally they want to use, server does not know that key. */
+    /* Nothing interesting to do here */
+  } else {
+    if (channel->mode & SILC_CHANNEL_MODE_PRIVKEY) {
+      /* The mode is removed and we need to generate and distribute
+        new channel key. Clients are not using private channel keys
+        anymore after this. */
+
+      /* if we don't remove the flag from the mode
+       * silc_server_create_channel_key won't create a new key */
+      channel->mode &= ~SILC_CHANNEL_MODE_PRIVKEY;
+
+      /* Re-generate channel key */
+      if (!silc_server_create_channel_key(server, channel, 0))
+       goto out;
+
+      /* Send the channel key. This sends it to our local clients and if
+        we are normal server to our router as well. */
+      silc_server_send_channel_key(server, NULL, channel,
+                                  server->server_type == SILC_ROUTER ?
+                                  FALSE : !server->standalone);
+
+      cipher = (char *)silc_cipher_get_name(channel->send_key);
+      hmac = (char *)silc_hmac_get_name(channel->hmac);
+    }
+  }
+
+  if (mode_mask & SILC_CHANNEL_MODE_ULIMIT) {
+    /* User limit is set on channel */
+    SilcUInt32 user_limit;
+
+    /* Get user limit */
+    tmp = silc_argument_get_arg_type(cmd->args, 3, NULL);
+    if (!tmp) {
+      if (!(channel->mode & SILC_CHANNEL_MODE_ULIMIT)) {
+       silc_server_command_send_status_reply(cmd, SILC_COMMAND_CMODE,
+                                  SILC_STATUS_ERR_NOT_ENOUGH_PARAMS, 0);
+       goto out;
+      }
+    } else {
+      SILC_GET32_MSB(user_limit, tmp);
+      channel->user_limit = user_limit;
+    }
+  } else {
+    if (channel->mode & SILC_CHANNEL_MODE_ULIMIT)
+      /* User limit mode is unset. Remove user limit */
+      channel->user_limit = 0;
+  }
+
+  if (mode_mask & SILC_CHANNEL_MODE_PASSPHRASE) {
+    if (!(channel->mode & SILC_CHANNEL_MODE_PASSPHRASE)) {
+      /* Passphrase has been set to channel */
+
+      /* Get the passphrase */
+      tmp = silc_argument_get_arg_type(cmd->args, 4, NULL);
+      if (!tmp) {
+       silc_server_command_send_status_reply(cmd, SILC_COMMAND_CMODE,
+                                  SILC_STATUS_ERR_NOT_ENOUGH_PARAMS, 0);
+       goto out;
+      }
+
+      /* Save the passphrase */
+      passphrase = channel->passphrase = silc_memdup(tmp, strlen(tmp));
+    }
+  } else {
+    if (channel->mode & SILC_CHANNEL_MODE_PASSPHRASE) {
+      /* Passphrase mode is unset. remove the passphrase */
+      silc_free(channel->passphrase);
+      channel->passphrase = NULL;
+    }
+  }
+
+  if (mode_mask & SILC_CHANNEL_MODE_CIPHER) {
+    if (!(channel->mode & SILC_CHANNEL_MODE_CIPHER)) {
+      /* Cipher to use protect the traffic */
+      SilcCipher send_key, receive_key, olds, oldr;
+
+      /* Get cipher */
+      cipher = silc_argument_get_arg_type(cmd->args, 5, NULL);
+      if (!cipher) {
+       silc_server_command_send_status_reply(cmd, SILC_COMMAND_CMODE,
+                                  SILC_STATUS_ERR_NOT_ENOUGH_PARAMS, 0);
+       goto out;
+      }
+
+      /* Delete old cipher and allocate the new one */
+      if (!silc_cipher_alloc(cipher, &send_key)) {
+       silc_server_command_send_status_data(
+                                        cmd, SILC_COMMAND_CMODE,
+                                        SILC_STATUS_ERR_UNKNOWN_ALGORITHM, 0,
+                                        2, cipher, strlen(cipher));
+       goto out;
+      }
+      if (!silc_cipher_alloc(cipher, &receive_key)) {
+       silc_server_command_send_status_data(
+                                        cmd, SILC_COMMAND_CMODE,
+                                        SILC_STATUS_ERR_UNKNOWN_ALGORITHM, 0,
+                                        2, cipher, strlen(cipher));
+       goto out;
+      }
+
+      olds = channel->send_key;
+      oldr = channel->receive_key;
+      channel->send_key = send_key;
+      channel->receive_key = receive_key;
+
+      /* Re-generate channel key */
+      if (!silc_server_create_channel_key(server, channel, 0)) {
+       /* We don't have new key, revert to old one */
+       channel->send_key = olds;
+       channel->receive_key = oldr;
+       goto out;
+      }
+
+      /* Remove old channel key for good */
+      silc_cipher_free(olds);
+      silc_cipher_free(oldr);
+
+      /* Send the channel key. This sends it to our local clients and if
+        we are normal server to our router as well. */
+      silc_server_send_channel_key(server, NULL, channel,
+                                  server->server_type == SILC_ROUTER ?
+                                  FALSE : !server->standalone);
+    }
+  } else {
+    if (channel->mode & SILC_CHANNEL_MODE_CIPHER) {
+      /* Cipher mode is unset. Remove the cipher and revert back to
+        default cipher */
+      SilcCipher send_key, receive_key, olds, oldr;
+      cipher = channel->cipher;
+
+      /* Delete old cipher and allocate default one */
+      if (!silc_cipher_alloc(cipher ? cipher : SILC_DEFAULT_CIPHER,
+                            &send_key)) {
+       silc_server_command_send_status_data(
+                                     cmd, SILC_COMMAND_CMODE,
+                                     SILC_STATUS_ERR_UNKNOWN_ALGORITHM, 0,
+                                     2, cipher, strlen(cipher));
+       goto out;
+      }
+      if (!silc_cipher_alloc(cipher ? cipher : SILC_DEFAULT_CIPHER,
+                            &receive_key)) {
+       silc_server_command_send_status_data(
+                                     cmd, SILC_COMMAND_CMODE,
+                                     SILC_STATUS_ERR_UNKNOWN_ALGORITHM, 0,
+                                     2, cipher, strlen(cipher));
+       goto out;
+      }
+
+      olds = channel->send_key;
+      oldr = channel->receive_key;
+      channel->send_key = send_key;
+      channel->receive_key = receive_key;
+
+      /* Re-generate channel key */
+      if (!silc_server_create_channel_key(server, channel, 0)) {
+       /* We don't have new key, revert to old one */
+       channel->send_key = olds;
+       channel->receive_key = oldr;
+       goto out;
+      }
+
+      /* Remove old channel key for good */
+      silc_cipher_free(olds);
+      silc_cipher_free(oldr);
+
+      /* Send the channel key. This sends it to our local clients and if
+        we are normal server to our router as well. */
+      silc_server_send_channel_key(server, NULL, channel,
+                                  server->server_type == SILC_ROUTER ?
+                                  FALSE : !server->standalone);
+    }
+  }
+
+  if (mode_mask & SILC_CHANNEL_MODE_HMAC) {
+    if (!(channel->mode & SILC_CHANNEL_MODE_HMAC)) {
+      /* HMAC to use protect the traffic */
+      unsigned char hash[SILC_HASH_MAXLEN];
+      SilcHmac newhmac;
 
-/* Server side of command UMODE. Client can use this command to set/unset
-   user mode. Client actually cannot set itself to be as server/router
-   operator so this can be used only to unset the modes. */
+      /* Get hmac */
+      hmac = silc_argument_get_arg_type(cmd->args, 6, NULL);
+      if (!hmac) {
+       silc_server_command_send_status_reply(cmd, SILC_COMMAND_CMODE,
+                                  SILC_STATUS_ERR_NOT_ENOUGH_PARAMS, 0);
+       goto out;
+      }
 
-SILC_SERVER_CMD_FUNC(umode)
-{
-  SilcServerCommandContext cmd = (SilcServerCommandContext)context;
-  SilcServer server = cmd->server;
-  SilcClientEntry client = (SilcClientEntry)cmd->sock->user_data;
-  SilcBuffer packet;
-  unsigned char *tmp_mask;
-  uint32 mask;
-  uint16 ident = silc_command_get_ident(cmd->payload);
+      /* Delete old hmac and allocate the new one */
+      if (!silc_hmac_alloc(hmac, NULL, &newhmac)) {
+       silc_server_command_send_status_data(
+                                       cmd, SILC_COMMAND_CMODE,
+                                       SILC_STATUS_ERR_UNKNOWN_ALGORITHM, 0,
+                                       2, hmac, strlen(hmac));
+       goto out;
+      }
 
-  if (cmd->sock->type != SILC_SOCKET_TYPE_CLIENT)
-    goto out;
+      silc_hmac_free(channel->hmac);
+      channel->hmac = newhmac;
 
-  SILC_SERVER_COMMAND_CHECK(SILC_COMMAND_UMODE, cmd, 2, 2);
+      /* Set the HMAC key out of current channel key. The client must do
+        this locally. */
+      silc_hash_make(silc_hmac_get_hash(channel->hmac), channel->key,
+                    channel->key_len / 8, hash);
+      silc_hmac_set_key(channel->hmac, hash,
+                       silc_hash_len(silc_hmac_get_hash(channel->hmac)));
+      memset(hash, 0, sizeof(hash));
+    }
+  } else {
+    if (channel->mode & SILC_CHANNEL_MODE_HMAC) {
+      /* Hmac mode is unset. Remove the hmac and revert back to
+        default hmac */
+      SilcHmac newhmac;
+      unsigned char hash[SILC_HASH_MAXLEN];
+      hmac = channel->hmac_name;
 
-  /* Get the client's mode mask */
-  tmp_mask = silc_argument_get_arg_type(cmd->args, 2, NULL);
-  if (!tmp_mask) {
-    silc_server_command_send_status_reply(cmd, SILC_COMMAND_UMODE,
-                                         SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
-    goto out;
-  }
-  SILC_GET32_MSB(mask, tmp_mask);
+      /* Delete old hmac and allocate default one */
+      if (!silc_hmac_alloc(hmac ? hmac : SILC_DEFAULT_HMAC, NULL, &newhmac)) {
+       silc_server_command_send_status_data(
+                                       cmd, SILC_COMMAND_CMODE,
+                                       SILC_STATUS_ERR_UNKNOWN_ALGORITHM, 0,
+                                       2, hmac, strlen(hmac));
+       goto out;
+      }
 
-  /* 
-   * Change the mode 
-   */
+      silc_hmac_free(channel->hmac);
+      channel->hmac = newhmac;
 
-  if (mask & SILC_UMODE_SERVER_OPERATOR) {
-    if (!(client->mode & SILC_UMODE_SERVER_OPERATOR)) {
-      /* Cannot operator mode */
-      silc_server_command_send_status_reply(cmd, SILC_COMMAND_UMODE,
-                                           SILC_STATUS_ERR_PERM_DENIED);
-      goto out;
-    }
-  } else {
-    /* Remove the server operator rights */
-    if (client->mode & SILC_UMODE_SERVER_OPERATOR) {
-      client->mode &= ~SILC_UMODE_SERVER_OPERATOR;
-      if (client->connection)
-       server->stat.my_server_ops--;
-      if (server->server_type == SILC_ROUTER)
-       server->stat.server_ops--;
+      /* Set the HMAC key out of current channel key. The client must do
+        this locally. */
+      silc_hash_make(silc_hmac_get_hash(channel->hmac), channel->key,
+                    channel->key_len / 8,
+                    hash);
+      silc_hmac_set_key(channel->hmac, hash,
+                       silc_hash_len(silc_hmac_get_hash(channel->hmac)));
+      memset(hash, 0, sizeof(hash));
     }
   }
 
-  if (mask & SILC_UMODE_ROUTER_OPERATOR) {
-    if (!(client->mode & SILC_UMODE_ROUTER_OPERATOR)) {
-      /* Cannot operator mode */
-      silc_server_command_send_status_reply(cmd, SILC_COMMAND_UMODE,
-                                           SILC_STATUS_ERR_PERM_DENIED);
-      goto out;
+  if (mode_mask & SILC_CHANNEL_MODE_FOUNDER_AUTH) {
+    if (chl->mode & SILC_CHANNEL_UMODE_CHANFO) {
+      /* Check if the founder public key was received */
+      founder_key = idata->public_key;
+      tmp = silc_argument_get_arg_type(cmd->args, 8, &tmp_len);
+      if (tmp) {
+       if (!silc_public_key_payload_decode(tmp, tmp_len, &founder_key)) {
+         silc_server_command_send_status_reply(cmd, SILC_COMMAND_CMODE,
+                                               SILC_STATUS_ERR_AUTH_FAILED,
+                                               0);
+         goto out;
+       }
+      } else {
+       /* If key was not sent and the channel mode has already founder
+          then the key was not to be changed. */
+       if (channel->mode & SILC_CHANNEL_MODE_FOUNDER_AUTH)
+         goto has_founder;
+      }
+
+      /* Set the founder authentication */
+      tmp = silc_argument_get_arg_type(cmd->args, 7, &tmp_len);
+      if (!tmp) {
+       silc_server_command_send_status_reply(
+                                    cmd, SILC_COMMAND_CMODE,
+                                    SILC_STATUS_ERR_NOT_ENOUGH_PARAMS, 0);
+       goto out;
+      }
+
+      /* Verify the payload before setting the mode */
+      if (!silc_auth_verify_data(tmp, tmp_len, SILC_AUTH_PUBLIC_KEY,
+                                founder_key, 0, server->sha1hash,
+                                client->id, SILC_ID_CLIENT)) {
+       silc_server_command_send_status_reply(cmd, SILC_COMMAND_CMODE,
+                                             SILC_STATUS_ERR_AUTH_FAILED,
+                                             0);
+       goto out;
+      }
+
+      /* Save the public key */
+      if (channel->founder_key)
+       silc_pkcs_public_key_free(channel->founder_key);
+      if (silc_argument_get_arg_type(cmd->args, 8, NULL))
+       channel->founder_key = founder_key;
+      else
+       channel->founder_key = silc_pkcs_public_key_copy(founder_key);
+      if (!channel->founder_key) {
+       silc_server_command_send_status_reply(cmd, SILC_COMMAND_CMODE,
+                                             SILC_STATUS_ERR_AUTH_FAILED,
+                                             0);
+       goto out;
+      }
+
+      fkey = silc_public_key_payload_encode(channel->founder_key);
+      if (!fkey) {
+       silc_server_command_send_status_reply(cmd, SILC_COMMAND_CMODE,
+                                             SILC_STATUS_ERR_AUTH_FAILED,
+                                             0);
+       silc_pkcs_public_key_free(channel->founder_key);
+       channel->founder_key = NULL;
+       goto out;
+      }
     }
   } else {
-    /* Remove the router operator rights */
-    if (client->mode & SILC_UMODE_ROUTER_OPERATOR) {
-      client->mode &= ~SILC_UMODE_ROUTER_OPERATOR;
-      if (client->connection)
-       server->stat.my_router_ops--;
-      if (server->server_type == SILC_ROUTER)
-       server->stat.router_ops--;
+    if (chl->mode & SILC_CHANNEL_UMODE_CHANFO) {
+      if (channel->mode & SILC_CHANNEL_MODE_FOUNDER_AUTH) {
+       if (channel->founder_key)
+         silc_pkcs_public_key_free(channel->founder_key);
+       channel->founder_key = NULL;
+      }
     }
   }
+ has_founder:
 
-  if (mask & SILC_UMODE_GONE) {
-    client->mode |= SILC_UMODE_GONE;
-  } else {
-    if (client->mode & SILC_UMODE_GONE)
-      /* Remove the gone status */
-      client->mode &= ~SILC_UMODE_GONE;
-  }
+  if (mode_mask & SILC_CHANNEL_MODE_CHANNEL_AUTH) {
+    if (chl->mode & SILC_CHANNEL_UMODE_CHANFO) {
+      SilcStatus st;
 
-  /* Send UMODE change to primary router */
-  if (!server->standalone)
-    silc_server_send_notify_umode(server, server->router->connection, TRUE,
-                                 client->id, client->mode);
+      chpkdata = silc_argument_get_arg_type(cmd->args, 9, &chpklen);
 
-  /* Send command reply to sender */
-  packet = silc_command_reply_payload_encode_va(SILC_COMMAND_UMODE,
-                                               SILC_STATUS_OK, ident, 1,
-                                               2, tmp_mask, 4);
-  silc_server_packet_send(server, cmd->sock, SILC_PACKET_COMMAND_REPLY, 0, 
-                         packet->data, packet->len, FALSE);
-  silc_buffer_free(packet);
+      if (!chpkdata && channel->mode & SILC_CHANNEL_MODE_CHANNEL_AUTH)
+       goto has_pk_list;
 
- out:
-  silc_server_command_free(cmd);
-}
+      set_chpk = TRUE;
 
-/* Checks that client has rights to add or remove channel modes. If any
-   of the checks fails FALSE is returned. */
+      /* Process the channel public key(s) */
+      st = silc_server_set_channel_pk_list(server, NULL, channel,
+                                          chpkdata, chpklen);
+      if (st != SILC_STATUS_OK) {
+       silc_server_command_send_status_reply(cmd, SILC_COMMAND_CMODE, st, 0);
+       goto out;
+      }
+    }
+  } else {
+    if (chl->mode & SILC_CHANNEL_UMODE_CHANFO) {
+      if (channel->mode & SILC_CHANNEL_MODE_CHANNEL_AUTH) {
+       if (channel->channel_pubkeys)
+         silc_hash_table_free(channel->channel_pubkeys);
+       channel->channel_pubkeys = NULL;
+       set_chpk = TRUE;
+      }
+    }
+  }
+ has_pk_list:
 
-int silc_server_check_cmode_rights(SilcChannelEntry channel,
-                                  SilcChannelClientEntry client,
-                                  uint32 mode)
-{
-  int is_op = client->mode & SILC_CHANNEL_UMODE_CHANOP;
-  int is_fo = client->mode & SILC_CHANNEL_UMODE_CHANFO;
+  /* Finally, set the mode */
+  old_mask = channel->mode = mode_mask;
 
-  /* Check whether has rights to change anything */
-  if (!is_op && !is_fo)
-    return FALSE;
+  /* Send CMODE_CHANGE notify. */
+  cidp = silc_id_payload_encode(client->id, SILC_ID_CLIENT);
+  if (mode_mask & SILC_CHANNEL_MODE_ULIMIT)
+    SILC_PUT32_MSB(channel->user_limit, ulimit);
+  silc_server_send_notify_to_channel(server, NULL, channel, FALSE, TRUE,
+                                    SILC_NOTIFY_TYPE_CMODE_CHANGE, 8,
+                                    cidp->data, silc_buffer_len(cidp),
+                                    tmp_mask, 4,
+                                    cipher, cipher ? strlen(cipher) : 0,
+                                    hmac, hmac ? strlen(hmac) : 0,
+                                    passphrase, passphrase ?
+                                    strlen(passphrase) : 0,
+                                    fkey ? fkey->data : NULL,
+                                    fkey ? silc_buffer_len(fkey) : 0,
+                                    chpkdata ? chpkdata : NULL,
+                                    chpkdata ? chpklen : 0,
+                                    mode_mask & SILC_CHANNEL_MODE_ULIMIT ?
+                                    ulimit : NULL,
+                                    mode_mask & SILC_CHANNEL_MODE_ULIMIT ?
+                                    sizeof(ulimit) : 0);
 
-  /* Check whether has rights to change everything */
-  if (is_op && is_fo)
-    return TRUE;
+  /* Set CMODE notify type to network */
+  if (chpkdata && chpklen)
+    silc_buffer_set(&chpk, chpkdata, chpklen);
+  silc_server_send_notify_cmode(server, SILC_PRIMARY_ROUTE(server),
+                               SILC_BROADCAST(server), channel,
+                               mode_mask, client->id, SILC_ID_CLIENT,
+                               cipher, hmac, passphrase, founder_key,
+                               chpkdata ? &chpk : NULL);
 
-  /* We know that client is channel operator, check that they are not
-     changing anything that requires channel founder rights. Rest of the
-     modes are available automatically for channel operator. */
+  if (set_chpk)
+    chpklist = silc_server_get_channel_pk_list(server, channel, FALSE, FALSE);
 
-  if (mode & SILC_CHANNEL_MODE_PRIVKEY) {
-    if (!(channel->mode & SILC_CHANNEL_MODE_PRIVKEY))
-      if (is_op && !is_fo)
-       return FALSE;
-  } else {
-    if (channel->mode & SILC_CHANNEL_MODE_PRIVKEY) {
-      if (is_op && !is_fo)
-       return FALSE;
-    }
-  }
-  
-  if (mode & SILC_CHANNEL_MODE_PASSPHRASE) {
-    if (!(channel->mode & SILC_CHANNEL_MODE_PASSPHRASE))
-      if (is_op && !is_fo)
-       return FALSE;
-  } else {
-    if (channel->mode & SILC_CHANNEL_MODE_PASSPHRASE) {
-      if (is_op && !is_fo)
-       return FALSE;
-    }
-  }
+  /* Send command reply to sender */
+  tmp_id = silc_argument_get_arg_type(cmd->args, 1, &tmp_len2);
+  silc_server_send_command_reply(server, cmd->sock, SILC_COMMAND_CMODE,
+                                SILC_STATUS_OK, 0, ident, 5,
+                                2, tmp_id, tmp_len2,
+                                3, tmp_mask, 4,
+                                4, fkey ? fkey->data : NULL,
+                                fkey ? silc_buffer_len(fkey) : 0,
+                                5, chpklist ? chpklist->data :
+                                NULL, chpklist ? silc_buffer_len(chpklist)
+                                : 0,
+                                6, (mode_mask &
+                                    SILC_CHANNEL_MODE_ULIMIT ?
+                                    ulimit : NULL),
+                                (mode_mask &
+                                 SILC_CHANNEL_MODE_ULIMIT ?
+                                 sizeof(ulimit) : 0));
+  silc_buffer_free(cidp);
 
-  if (mode & SILC_CHANNEL_MODE_CIPHER) {
-    if (!(channel->mode & SILC_CHANNEL_MODE_CIPHER))
-      if (is_op && !is_fo)
-       return FALSE;
-  } else {
-    if (channel->mode & SILC_CHANNEL_MODE_CIPHER) {
-      if (is_op && !is_fo)
-       return FALSE;
-    }
-  }
-  
-  if (mode & SILC_CHANNEL_MODE_FOUNDER_AUTH) {
-    if (!(channel->mode & SILC_CHANNEL_MODE_FOUNDER_AUTH))
-      if (is_op && !is_fo)
-       return FALSE;
-  } else {
-    if (channel->mode & SILC_CHANNEL_MODE_FOUNDER_AUTH) {
-      if (is_op && !is_fo)
-       return FALSE;
-    }
-  }
-  
-  return TRUE;
+ out:
+  channel->mode = old_mask;
+  silc_buffer_free(chpklist);
+  silc_buffer_free(fkey);
+  silc_server_command_free(cmd);
 }
 
-/* Server side command of CMODE. Changes channel mode */
+/* Server side of CUMODE command. Changes client's mode on a channel. */
 
-SILC_SERVER_CMD_FUNC(cmode)
+SILC_SERVER_CMD_FUNC(cumode)
 {
   SilcServerCommandContext cmd = (SilcServerCommandContext)context;
   SilcServer server = cmd->server;
-  SilcClientEntry client = (SilcClientEntry)cmd->sock->user_data;
-  SilcIDListData idata = (SilcIDListData)client;
-  SilcChannelID *channel_id;
+  SilcClientEntry client = silc_packet_get_context(cmd->sock);
+  SilcID id, id2;
   SilcChannelEntry channel;
+  SilcClientEntry target_client;
   SilcChannelClientEntry chl;
-  SilcBuffer packet, cidp;
-  unsigned char *tmp, *tmp_id, *tmp_mask;
-  char *cipher = NULL, *hmac = NULL, *passphrase = NULL;
-  uint32 mode_mask, tmp_len, tmp_len2;
-  uint16 ident = silc_command_get_ident(cmd->payload);
-
-  SILC_SERVER_COMMAND_CHECK(SILC_COMMAND_CMODE, cmd, 2, 7);
+  SilcBuffer idp;
+  unsigned char *tmp_id, *tmp_ch_id, *tmp_mask;
+  SilcUInt32 target_mask, sender_mask = 0, tmp_len, tmp_ch_len;
+  int notify = FALSE;
+  SilcUInt16 ident = silc_command_get_ident(cmd->payload);
+  SilcPublicKey founder_key = NULL;
+  SilcBuffer fkey = NULL;
 
-  /* Get Channel ID */
-  tmp_id = silc_argument_get_arg_type(cmd->args, 1, &tmp_len2);
-  if (!tmp_id) {
-    silc_server_command_send_status_reply(cmd, SILC_COMMAND_CMODE,
-                                         SILC_STATUS_ERR_NO_CHANNEL_ID);
+  if (!client)
     goto out;
-  }
-  channel_id = silc_id_payload_parse_id(tmp_id, tmp_len2);
-  if (!channel_id) {
-    silc_server_command_send_status_reply(cmd, SILC_COMMAND_CMODE,
-                                         SILC_STATUS_ERR_NO_CHANNEL_ID);
-    goto out;
-  }
 
-  /* Get the channel mode mask */
-  tmp_mask = silc_argument_get_arg_type(cmd->args, 2, &tmp_len);
-  if (!tmp_mask) {
-    silc_server_command_send_status_reply(cmd, SILC_COMMAND_CMODE,
-                                         SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
+  SILC_SERVER_COMMAND_CHECK(SILC_COMMAND_CUMODE, cmd, 3, 4);
+
+  /* Get Channel ID */
+  if (!silc_argument_get_decoded(cmd->args, 1, SILC_ARGUMENT_ID, &id, NULL)) {
+    silc_server_command_send_status_reply(cmd, SILC_COMMAND_CUMODE,
+                                         SILC_STATUS_ERR_NO_CHANNEL_ID, 0);
     goto out;
   }
-  SILC_GET32_MSB(mode_mask, tmp_mask);
 
   /* Get channel entry */
-  channel = silc_idlist_find_channel_by_id(server->local_list, 
-                                          channel_id, NULL);
+  channel = silc_idlist_find_channel_by_id(server->local_list,
+                                          SILC_ID_GET_ID(id), NULL);
   if (!channel) {
-    channel = silc_idlist_find_channel_by_id(server->global_list, 
-                                            channel_id, NULL);
+    channel = silc_idlist_find_channel_by_id(server->global_list,
+                                            SILC_ID_GET_ID(id), NULL);
     if (!channel) {
-      silc_server_command_send_status_reply(cmd, SILC_COMMAND_CMODE,
-                                           SILC_STATUS_ERR_NO_SUCH_CHANNEL);
+      tmp_ch_id = silc_argument_get_arg_type(cmd->args, 1, &tmp_ch_len);
+      silc_server_command_send_status_data(cmd, SILC_COMMAND_CUMODE,
+                                          SILC_STATUS_ERR_NO_SUCH_CHANNEL_ID,
+                                          0, 2, tmp_ch_id, tmp_ch_len);
       goto out;
     }
   }
 
-  /* Check whether this client is on the channel */
-  if (!silc_server_client_on_channel(client, channel)) {
-    silc_server_command_send_status_reply(cmd, SILC_COMMAND_CMODE,
-                                         SILC_STATUS_ERR_NOT_ON_CHANNEL);
+  /* Check whether sender is on the channel */
+  if (!silc_server_client_on_channel(client, channel, &chl)) {
+    tmp_ch_id = silc_argument_get_arg_type(cmd->args, 1, &tmp_ch_len);
+    silc_server_command_send_status_data(cmd, SILC_COMMAND_CUMODE,
+                                        SILC_STATUS_ERR_NOT_ON_CHANNEL, 0,
+                                        2, tmp_ch_id, tmp_ch_len);
+    goto out;
+  }
+  sender_mask = chl->mode;
+
+  /* Get the target client's channel mode mask */
+  tmp_mask = silc_argument_get_arg_type(cmd->args, 2, NULL);
+  if (!tmp_mask) {
+    silc_server_command_send_status_reply(cmd, SILC_COMMAND_CUMODE,
+                                         SILC_STATUS_ERR_NOT_ENOUGH_PARAMS,
+                                         0);
+    goto out;
+  }
+  SILC_GET32_MSB(target_mask, tmp_mask);
+
+  /* Get target Client ID */
+  if (!silc_argument_get_decoded(cmd->args, 3, SILC_ARGUMENT_ID, &id2, NULL)) {
+    silc_server_command_send_status_reply(cmd, SILC_COMMAND_CUMODE,
+                                         SILC_STATUS_ERR_NO_CLIENT_ID, 0);
     goto out;
   }
 
-  /* Get entry to the channel user list */
-  silc_hash_table_find(channel->user_list, client, NULL, (void *)&chl);
+  /* Get target client's entry */
+  target_client = silc_idlist_find_client_by_id(server->local_list,
+                                               SILC_ID_GET_ID(id2),
+                                               TRUE, NULL);
+  if (!target_client)
+    target_client = silc_idlist_find_client_by_id(server->global_list,
+                                                 SILC_ID_GET_ID(id2),
+                                                 TRUE, NULL);
 
-  /* Check that client has rights to change any requested channel modes */
-  if (!silc_server_check_cmode_rights(channel, chl, mode_mask)) {
-    silc_server_command_send_status_reply(cmd, SILC_COMMAND_CMODE,
-                                         SILC_STATUS_ERR_NO_CHANNEL_PRIV);
+  if (target_client != client &&
+      !(sender_mask & SILC_CHANNEL_UMODE_CHANFO) &&
+      !(sender_mask & SILC_CHANNEL_UMODE_CHANOP)) {
+    tmp_ch_id = silc_argument_get_arg_type(cmd->args, 1, &tmp_ch_len);
+    silc_server_command_send_status_data(cmd, SILC_COMMAND_CUMODE,
+                                        SILC_STATUS_ERR_NOT_YOU, 0,
+                                        2, tmp_ch_id, tmp_ch_len);
     goto out;
   }
 
+  /* Check whether target client is on the channel */
+  if (target_client != client) {
+    if (!silc_server_client_on_channel(target_client, channel, &chl)) {
+      tmp_ch_id = silc_argument_get_arg_type(cmd->args, 1, &tmp_ch_len);
+      tmp_id = silc_argument_get_arg_type(cmd->args, 3, &tmp_len);
+      silc_server_command_send_status_data2(
+                                 cmd, SILC_COMMAND_CUMODE,
+                                 SILC_STATUS_ERR_USER_NOT_ON_CHANNEL, 0,
+                                 2, tmp_id, tmp_len,
+                                 3, tmp_ch_id, tmp_ch_len);
+      goto out;
+    }
+  }
+
   /*
-   * Check the modes. Modes that requires nothing special operation are
-   * not checked here.
+   * Change the mode
    */
 
-  if (mode_mask & SILC_CHANNEL_MODE_PRIVKEY) {
-    /* Channel uses private keys to protect traffic. Client(s) has set the
-       key locally they want to use, server does not know that key. */
-    /* Nothing interesting to do here */
+  /* If the target client is founder, no one else can change their mode
+     but themselves. */
+  if (chl->mode & SILC_CHANNEL_UMODE_CHANFO && client != target_client) {
+    tmp_ch_id = silc_argument_get_arg_type(cmd->args, 1, &tmp_ch_len);
+    silc_server_command_send_status_data(cmd, SILC_COMMAND_CUMODE,
+                                        SILC_STATUS_ERR_NO_CHANNEL_FOPRIV,
+                                        0, 2, tmp_ch_id, tmp_ch_len);
+    goto out;
+  }
+
+  if (target_mask & SILC_CHANNEL_UMODE_CHANFO) {
+    if (target_client != client) {
+      silc_server_command_send_status_reply(cmd, SILC_COMMAND_CUMODE,
+                                           SILC_STATUS_ERR_NO_CHANNEL_FOPRIV,
+                                           0);
+      goto out;
+    }
+
+    if (!(chl->mode & SILC_CHANNEL_UMODE_CHANFO)) {
+      /* The client tries to claim the founder rights. */
+      unsigned char *tmp_auth;
+      SilcUInt32 tmp_auth_len;
+      SilcChannelClientEntry chl2;
+      SilcHashTableList htl;
+
+      if (!(channel->mode & SILC_CHANNEL_MODE_FOUNDER_AUTH) ||
+         !channel->founder_key) {
+       silc_server_command_send_status_reply(cmd, SILC_COMMAND_CUMODE,
+                                             SILC_STATUS_ERR_AUTH_FAILED, 0);
+       goto out;
+      }
+
+      tmp_auth = silc_argument_get_arg_type(cmd->args, 4, &tmp_auth_len);
+      if (!tmp_auth) {
+       silc_server_command_send_status_reply(cmd, SILC_COMMAND_CUMODE,
+                                             SILC_STATUS_ERR_AUTH_FAILED, 0);
+       goto out;
+      }
+
+      /* Verify the authentication payload */
+      if (!silc_auth_verify_data(tmp_auth, tmp_auth_len, SILC_AUTH_PUBLIC_KEY,
+                                channel->founder_key, 0, server->sha1hash,
+                                client->id, SILC_ID_CLIENT)) {
+       silc_server_command_send_status_reply(cmd, SILC_COMMAND_CUMODE,
+                                             SILC_STATUS_ERR_AUTH_FAILED, 0);
+       goto out;
+      }
+
+      notify = TRUE;
+      founder_key = channel->founder_key;
+      fkey = silc_public_key_payload_encode(founder_key);
+      if (!fkey) {
+       silc_server_command_send_status_reply(cmd, SILC_COMMAND_CUMODE,
+                                             SILC_STATUS_ERR_AUTH_FAILED, 0);
+       goto out;
+      }
+
+      /* There cannot be anyone else as founder on the channel now.  This
+        client is definitely the founder due to this authentication.  This
+        is done only on router, not on server, since server cannot know
+        whether router will accept this mode change or not.  XXX This
+        probably shouldn't be done anymore at all, may cause problems in
+        router-router connections too (maybe just AUTH_FAILED error should
+        be returned). -Pekka */
+      if (server->server_type == SILC_ROUTER) {
+       silc_hash_table_list(channel->user_list, &htl);
+       while (silc_hash_table_get(&htl, NULL, (void *)&chl2))
+         if (chl2->mode & SILC_CHANNEL_UMODE_CHANFO) {
+           chl2->mode &= ~SILC_CHANNEL_UMODE_CHANFO;
+           silc_server_force_cumode_change(server, NULL, channel, chl2,
+                                           chl2->mode);
+           break;
+         }
+       silc_hash_table_list_reset(&htl);
+      }
+
+      sender_mask = chl->mode |= SILC_CHANNEL_UMODE_CHANFO;
+    }
   } else {
-    if (channel->mode & SILC_CHANNEL_MODE_PRIVKEY) {
-      /* The mode is removed and we need to generate and distribute
-        new channel key. Clients are not using private channel keys
-        anymore after this. */
-      
-      /* Re-generate channel key */
-      if (!silc_server_create_channel_key(server, channel, 0))
+    if (chl->mode & SILC_CHANNEL_UMODE_CHANFO) {
+      if (target_client == client) {
+       /* Remove channel founder rights from itself */
+       chl->mode &= ~SILC_CHANNEL_UMODE_CHANFO;
+       notify = TRUE;
+      } else {
+       silc_server_command_send_status_reply(cmd, SILC_COMMAND_CUMODE,
+                                             SILC_STATUS_ERR_NOT_YOU, 0);
        goto out;
-       
-      /* Send the channel key. This sends it to our local clients and if
-        we are normal server to our router as well. */
-      silc_server_send_channel_key(server, NULL, channel, 
-                                  server->server_type == SILC_ROUTER ? 
-                                  FALSE : !server->standalone);
-       
-      cipher = channel->channel_key->cipher->name;
-      hmac = (char *)silc_hmac_get_name(channel->hmac);
+      }
     }
   }
 
-  if (mode_mask & SILC_CHANNEL_MODE_ULIMIT) {
-    /* User limit is set on channel */
-    uint32 user_limit;
-      
-    /* Get user limit */
-    tmp = silc_argument_get_arg_type(cmd->args, 3, NULL);
-    if (!tmp) {
-      if (!(channel->mode & SILC_CHANNEL_MODE_ULIMIT)) {
-       silc_server_command_send_status_reply(cmd, SILC_COMMAND_CMODE,
-                                  SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
-       goto out;
+  if (target_mask & SILC_CHANNEL_UMODE_CHANOP) {
+    /* Promote to operator */
+    if (!(chl->mode & SILC_CHANNEL_UMODE_CHANOP)) {
+      if (!(sender_mask & SILC_CHANNEL_UMODE_CHANOP) &&
+          !(sender_mask & SILC_CHANNEL_UMODE_CHANFO)) {
+       tmp_ch_id = silc_argument_get_arg_type(cmd->args, 1, &tmp_ch_len);
+        silc_server_command_send_status_data(cmd, SILC_COMMAND_CUMODE,
+                                            SILC_STATUS_ERR_NO_CHANNEL_PRIV,
+                                            0, 2, tmp_ch_id, tmp_ch_len);
+        goto out;
       }
-    } else {
-      SILC_GET32_MSB(user_limit, tmp);
-      channel->user_limit = user_limit;
+
+      chl->mode |= SILC_CHANNEL_UMODE_CHANOP;
+      notify = TRUE;
     }
   } else {
-    if (channel->mode & SILC_CHANNEL_MODE_ULIMIT)
-      /* User limit mode is unset. Remove user limit */
-      channel->user_limit = 0;
+    if (chl->mode & SILC_CHANNEL_UMODE_CHANOP) {
+      if (!(sender_mask & SILC_CHANNEL_UMODE_CHANOP) &&
+          !(sender_mask & SILC_CHANNEL_UMODE_CHANFO)) {
+       tmp_ch_id = silc_argument_get_arg_type(cmd->args, 1, &tmp_ch_len);
+        silc_server_command_send_status_data(cmd, SILC_COMMAND_CUMODE,
+                                            SILC_STATUS_ERR_NO_CHANNEL_PRIV,
+                                            0, 2, tmp_ch_id, tmp_ch_len);
+        goto out;
+      }
+
+      /* Demote to normal user */
+      chl->mode &= ~SILC_CHANNEL_UMODE_CHANOP;
+      notify = TRUE;
+    }
   }
 
-  if (mode_mask & SILC_CHANNEL_MODE_PASSPHRASE) {
-    if (!(channel->mode & SILC_CHANNEL_MODE_PASSPHRASE)) {
-      /* Passphrase has been set to channel */
-      
-      /* Get the passphrase */
-      tmp = silc_argument_get_arg_type(cmd->args, 4, NULL);
-      if (!tmp) {
-       silc_server_command_send_status_reply(cmd, SILC_COMMAND_CMODE,
-                                  SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
-       goto out;
-      }
+  if (target_mask & SILC_CHANNEL_UMODE_BLOCK_MESSAGES) {
+    if (target_client != client) {
+      silc_server_command_send_status_reply(cmd, SILC_COMMAND_CUMODE,
+                                           SILC_STATUS_ERR_NOT_YOU, 0);
+      goto out;
+    }
 
-      /* Save the passphrase */
-      passphrase = channel->passphrase = strdup(tmp);
+    if (!(chl->mode & SILC_CHANNEL_UMODE_BLOCK_MESSAGES)) {
+      chl->mode |= SILC_CHANNEL_UMODE_BLOCK_MESSAGES;
+      notify = TRUE;
     }
   } else {
-    if (channel->mode & SILC_CHANNEL_MODE_PASSPHRASE) {
-      /* Passphrase mode is unset. remove the passphrase */
-      if (channel->passphrase) {
-       silc_free(channel->passphrase);
-       channel->passphrase = NULL;
+    if (chl->mode & SILC_CHANNEL_UMODE_BLOCK_MESSAGES) {
+      if (target_client != client) {
+       silc_server_command_send_status_reply(cmd, SILC_COMMAND_CUMODE,
+                                             SILC_STATUS_ERR_NOT_YOU, 0);
+       goto out;
       }
+
+      chl->mode &= ~SILC_CHANNEL_UMODE_BLOCK_MESSAGES;
+      notify = TRUE;
     }
   }
 
-  if (mode_mask & SILC_CHANNEL_MODE_CIPHER) {
-    if (!(channel->mode & SILC_CHANNEL_MODE_CIPHER)) {
-      /* Cipher to use protect the traffic */
-      SilcCipher newkey, oldkey;
+  if (target_mask & SILC_CHANNEL_UMODE_BLOCK_MESSAGES_USERS) {
+    if (target_client != client) {
+      silc_server_command_send_status_reply(cmd, SILC_COMMAND_CUMODE,
+                                           SILC_STATUS_ERR_NOT_YOU, 0);
+      goto out;
+    }
 
-      /* Get cipher */
-      cipher = silc_argument_get_arg_type(cmd->args, 5, NULL);
-      if (!cipher) {
-       silc_server_command_send_status_reply(cmd, SILC_COMMAND_CMODE,
-                                  SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
+    if (!(chl->mode & SILC_CHANNEL_UMODE_BLOCK_MESSAGES_USERS)) {
+      chl->mode |= SILC_CHANNEL_UMODE_BLOCK_MESSAGES_USERS;
+      notify = TRUE;
+    }
+  } else {
+    if (chl->mode & SILC_CHANNEL_UMODE_BLOCK_MESSAGES_USERS) {
+      if (target_client != client) {
+       silc_server_command_send_status_reply(cmd, SILC_COMMAND_CUMODE,
+                                             SILC_STATUS_ERR_NOT_YOU, 0);
        goto out;
       }
 
-      /* Delete old cipher and allocate the new one */
-      if (!silc_cipher_alloc(cipher, &newkey)) {
-       silc_server_command_send_status_reply(cmd, SILC_COMMAND_CMODE,
-                                      SILC_STATUS_ERR_UNKNOWN_ALGORITHM);
-       goto out;
-      }
+      chl->mode &= ~SILC_CHANNEL_UMODE_BLOCK_MESSAGES_USERS;
+      notify = TRUE;
+    }
+  }
 
-      oldkey = channel->channel_key;
-      channel->channel_key = newkey;
+  if (target_mask & SILC_CHANNEL_UMODE_BLOCK_MESSAGES_ROBOTS) {
+    if (target_client != client) {
+      silc_server_command_send_status_reply(cmd, SILC_COMMAND_CUMODE,
+                                           SILC_STATUS_ERR_NOT_YOU, 0);
+      goto out;
+    }
 
-      /* Re-generate channel key */
-      if (!silc_server_create_channel_key(server, channel, 0)) {
-       /* We don't have new key, revert to old one */
-       channel->channel_key = oldkey;
+    if (!(chl->mode & SILC_CHANNEL_UMODE_BLOCK_MESSAGES_ROBOTS)) {
+      chl->mode |= SILC_CHANNEL_UMODE_BLOCK_MESSAGES_ROBOTS;
+      notify = TRUE;
+    }
+  } else {
+    if (chl->mode & SILC_CHANNEL_UMODE_BLOCK_MESSAGES_ROBOTS) {
+      if (target_client != client) {
+       silc_server_command_send_status_reply(cmd, SILC_COMMAND_CUMODE,
+                                             SILC_STATUS_ERR_NOT_YOU, 0);
        goto out;
       }
 
-      /* Remove old channel key for good */
-      silc_cipher_free(oldkey);
+      chl->mode &= ~SILC_CHANNEL_UMODE_BLOCK_MESSAGES_ROBOTS;
+      notify = TRUE;
+    }
+  }
 
-      /* Send the channel key. This sends it to our local clients and if
-        we are normal server to our router as well. */
-      silc_server_send_channel_key(server, NULL, channel, 
-                                  server->server_type == SILC_ROUTER ? 
-                                  FALSE : !server->standalone);
+  if (target_mask & SILC_CHANNEL_UMODE_QUIET) {
+    if (!(chl->mode & SILC_CHANNEL_UMODE_QUIET)) {
+      if (client == target_client) {
+       silc_server_command_send_status_reply(cmd, SILC_COMMAND_CUMODE,
+                                             SILC_STATUS_ERR_PERM_DENIED, 0);
+       goto out;
+      }
+      chl->mode |= SILC_CHANNEL_UMODE_QUIET;
+      notify = TRUE;
     }
   } else {
-    if (channel->mode & SILC_CHANNEL_MODE_CIPHER) {
-      /* Cipher mode is unset. Remove the cipher and revert back to 
-        default cipher */
-      SilcCipher newkey, oldkey;
-      cipher = channel->cipher;
-
-      /* Delete old cipher and allocate default one */
-      if (!silc_cipher_alloc(cipher ? cipher : SILC_DEFAULT_CIPHER, &newkey)) {
-       silc_server_command_send_status_reply(cmd, SILC_COMMAND_CMODE,
-                                  SILC_STATUS_ERR_UNKNOWN_ALGORITHM);
+    if (chl->mode & SILC_CHANNEL_UMODE_QUIET) {
+      if (client == target_client) {
+       silc_server_command_send_status_reply(cmd, SILC_COMMAND_CUMODE,
+                                             SILC_STATUS_ERR_PERM_DENIED, 0);
        goto out;
       }
+      chl->mode &= ~SILC_CHANNEL_UMODE_QUIET;
+      notify = TRUE;
+    }
+  }
 
-      oldkey = channel->channel_key;
-      channel->channel_key = newkey;
+  idp = silc_id_payload_encode(client->id, SILC_ID_CLIENT);
+  tmp_id = silc_argument_get_arg_type(cmd->args, 3, &tmp_len);
+  tmp_ch_id = silc_argument_get_arg_type(cmd->args, 1, &tmp_ch_len);
 
-      /* Re-generate channel key */
-      if (!silc_server_create_channel_key(server, channel, 0)) {
-       /* We don't have new key, revert to old one */
-       channel->channel_key = oldkey;
-       goto out;
-      }
-      
-      /* Remove old channel key for good */
-      silc_cipher_free(oldkey);
+  /* Send notify to channel, notify only if mode was actually changed. */
+  if (notify) {
+    silc_server_send_notify_to_channel(server, NULL, channel, FALSE, TRUE,
+                                      SILC_NOTIFY_TYPE_CUMODE_CHANGE, 4,
+                                      idp->data, silc_buffer_len(idp),
+                                      tmp_mask, 4,
+                                      tmp_id, tmp_len,
+                                      fkey ? fkey->data : NULL,
+                                      fkey ? silc_buffer_len(fkey) : 0);
 
-      /* Send the channel key. This sends it to our local clients and if
-        we are normal server to our router as well. */
-      silc_server_send_channel_key(server, NULL, channel, 
-                                  server->server_type == SILC_ROUTER ? 
-                                  FALSE : !server->standalone);
-    }
+    /* Set CUMODE notify type to network */
+    silc_server_send_notify_cumode(server, SILC_PRIMARY_ROUTE(server),
+                                  SILC_BROADCAST(server), channel,
+                                  target_mask, client->id, SILC_ID_CLIENT,
+                                  target_client->id, founder_key);
   }
 
-  if (mode_mask & SILC_CHANNEL_MODE_HMAC) {
-    if (!(channel->mode & SILC_CHANNEL_MODE_HMAC)) {
-      /* HMAC to use protect the traffic */
-      unsigned char hash[32];
-      SilcHmac newhmac;
+  /* Send command reply to sender */
+  silc_server_send_command_reply(server, cmd->sock, SILC_COMMAND_CUMODE,
+                                SILC_STATUS_OK, 0, ident, 3,
+                                2, tmp_mask, 4,
+                                3, tmp_ch_id, tmp_ch_len,
+                                4, tmp_id, tmp_len);
+  silc_buffer_free(idp);
 
-      /* Get hmac */
-      hmac = silc_argument_get_arg_type(cmd->args, 6, NULL);
-      if (!hmac) {
-       silc_server_command_send_status_reply(cmd, SILC_COMMAND_CMODE,
-                                  SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
-       goto out;
-      }
+ out:
+  silc_buffer_free(fkey);
+  silc_server_command_free(cmd);
+}
 
-      /* Delete old hmac and allocate the new one */
-      if (!silc_hmac_alloc(hmac, NULL, &newhmac)) {
-       silc_server_command_send_status_reply(cmd, SILC_COMMAND_CMODE,
-                                      SILC_STATUS_ERR_UNKNOWN_ALGORITHM);
-       goto out;
-      }
+/* Server side of KICK command. Kicks client out of channel. */
 
-      silc_hmac_free(channel->hmac);
-      channel->hmac = newhmac;
+SILC_SERVER_CMD_FUNC(kick)
+{
+  SilcServerCommandContext cmd = (SilcServerCommandContext)context;
+  SilcServer server = cmd->server;
+  SilcClientEntry client = silc_packet_get_context(cmd->sock);
+  SilcClientEntry target_client;
+  SilcID id, id2;
+  SilcChannelEntry channel;
+  SilcChannelClientEntry chl;
+  SilcBuffer idp;
+  SilcUInt32 tmp_len, target_idp_len, clen;
+  SilcUInt16 ident = silc_command_get_ident(cmd->payload);
+  unsigned char *tmp, *comment, *target_idp;
 
-      /* Set the HMAC key out of current channel key. The client must do
-        this locally. */
-      silc_hash_make(silc_hmac_get_hash(channel->hmac), channel->key, 
-                    channel->key_len / 8, hash);
-      silc_hmac_set_key(channel->hmac, hash, 
-                       silc_hash_len(silc_hmac_get_hash(channel->hmac)));
-      memset(hash, 0, sizeof(hash));
-    }
-  } else {
-    if (channel->mode & SILC_CHANNEL_MODE_HMAC) {
-      /* Hmac mode is unset. Remove the hmac and revert back to 
-        default hmac */
-      SilcHmac newhmac;
-      unsigned char hash[32];
-      hmac = channel->hmac_name;
+  if (!client)
+    goto out;
 
-      /* Delete old hmac and allocate default one */
-      silc_hmac_free(channel->hmac);
-      if (!silc_hmac_alloc(hmac ? hmac : SILC_DEFAULT_HMAC, NULL, &newhmac)) {
-       silc_server_command_send_status_reply(cmd, SILC_COMMAND_CMODE,
-                                      SILC_STATUS_ERR_UNKNOWN_ALGORITHM);
-       goto out;
-      }
+  SILC_SERVER_COMMAND_CHECK(SILC_COMMAND_LEAVE, cmd, 1, 3);
 
-      silc_hmac_free(channel->hmac);
-      channel->hmac = newhmac;
+  /* Get Channel ID */
+  if (!silc_argument_get_decoded(cmd->args, 1, SILC_ARGUMENT_ID, &id, NULL)) {
+    silc_server_command_send_status_reply(cmd, SILC_COMMAND_KICK,
+                                         SILC_STATUS_ERR_NO_CHANNEL_ID, 0);
+    goto out;
+  }
 
-      /* Set the HMAC key out of current channel key. The client must do
-        this locally. */
-      silc_hash_make(silc_hmac_get_hash(channel->hmac), channel->key, 
-                    channel->key_len / 8, 
-                    hash);
-      silc_hmac_set_key(channel->hmac, hash, 
-                       silc_hash_len(silc_hmac_get_hash(channel->hmac)));
-      memset(hash, 0, sizeof(hash));
+  /* Get channel entry */
+  channel = silc_idlist_find_channel_by_id(server->local_list,
+                                          SILC_ID_GET_ID(id), NULL);
+  if (!channel) {
+    channel = silc_idlist_find_channel_by_id(server->local_list,
+                                            SILC_ID_GET_ID(id), NULL);
+    if (!channel) {
+      tmp = silc_argument_get_arg_type(cmd->args, 1, &tmp_len);
+      silc_server_command_send_status_data(cmd, SILC_COMMAND_KICK,
+                                          SILC_STATUS_ERR_NO_SUCH_CHANNEL_ID,
+                                          0, 2, tmp, tmp_len);
+      goto out;
     }
   }
 
-  if (mode_mask & SILC_CHANNEL_MODE_FOUNDER_AUTH) {
-    if (chl->mode & SILC_CHANNEL_UMODE_CHANFO) {
-      if (!(channel->mode & SILC_CHANNEL_MODE_FOUNDER_AUTH)) {
-       /* Set the founder authentication */
-       SilcAuthPayload auth;
-       
-       tmp = silc_argument_get_arg_type(cmd->args, 7, &tmp_len);
-       if (!tmp) {
-         silc_server_command_send_status_reply(cmd, SILC_COMMAND_CMODE,
-                                    SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
-         goto out;
-       }
+  /* Check whether sender is on the channel */
+  if (!silc_server_client_on_channel(client, channel, &chl)) {
+    tmp = silc_argument_get_arg_type(cmd->args, 1, &tmp_len);
+    silc_server_command_send_status_data(cmd, SILC_COMMAND_KICK,
+                                        SILC_STATUS_ERR_NOT_ON_CHANNEL,
+                                        0, 2, tmp, tmp_len);
+    goto out;
+  }
 
-       auth = silc_auth_payload_parse(tmp, tmp_len);
-       if (!auth) {
-         silc_server_command_send_status_reply(cmd, SILC_COMMAND_CMODE,
-                                    SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
-         goto out;
-       }
+  /* Check that the kicker is channel operator or channel founder */
+  if (!(chl->mode & SILC_CHANNEL_UMODE_CHANOP) &&
+      !(chl->mode & SILC_CHANNEL_UMODE_CHANFO)) {
+    tmp = silc_argument_get_arg_type(cmd->args, 1, &tmp_len);
+    silc_server_command_send_status_data(cmd, SILC_COMMAND_KICK,
+                                        SILC_STATUS_ERR_NO_CHANNEL_PRIV,
+                                        0, 2, tmp, tmp_len);
+    goto out;
+  }
 
-       /* Save the public key */
-       tmp = silc_pkcs_public_key_encode(idata->public_key, &tmp_len);
-       silc_pkcs_public_key_decode(tmp, tmp_len, &channel->founder_key);
-       silc_free(tmp);
-       
-       channel->founder_method = silc_auth_get_method(auth);
-
-       if (channel->founder_method == SILC_AUTH_PASSWORD) {
-         tmp = silc_auth_get_data(auth, &tmp_len);
-         channel->founder_passwd = 
-           silc_calloc(tmp_len + 1, sizeof(*channel->founder_passwd));
-         memcpy(channel->founder_passwd, tmp, tmp_len);
-         channel->founder_passwd_len = tmp_len;
-       } else {
-         /* Verify the payload before setting the mode */
-         if (!silc_auth_verify(auth, channel->founder_method, 
-                               channel->founder_key, 0, idata->hash,
-                               client->id, SILC_ID_CLIENT)) {
-           silc_server_command_send_status_reply(cmd, SILC_COMMAND_CMODE,
-                                                 SILC_STATUS_ERR_AUTH_FAILED);
-           goto out;
-         }
-       }
+  /* Get target Client ID */
+  if (!silc_argument_get_decoded(cmd->args, 2, SILC_ARGUMENT_ID, &id2, NULL)) {
+    silc_server_command_send_status_reply(cmd, SILC_COMMAND_KICK,
+                                         SILC_STATUS_ERR_NO_CLIENT_ID, 0);
+    goto out;
+  }
 
-       silc_auth_payload_free(auth);
-      }
-    }
-  } else {
-    if (chl->mode & SILC_CHANNEL_UMODE_CHANFO) {
-      if (channel->mode & SILC_CHANNEL_MODE_FOUNDER_AUTH) {
-       if (channel->founder_key)
-         silc_pkcs_public_key_free(channel->founder_key);
-       if (channel->founder_passwd) {
-         silc_free(channel->founder_passwd);
-         channel->founder_passwd = NULL;
-       }
-      }
-    }
+  /* Get target client's entry */
+  target_client = silc_idlist_find_client_by_id(server->local_list,
+                                               SILC_ID_GET_ID(id2),
+                                               TRUE, NULL);
+  if (!target_client)
+    target_client = silc_idlist_find_client_by_id(server->global_list,
+                                                 SILC_ID_GET_ID(id2),
+                                                 TRUE, NULL);
+
+  /* Check whether target client is on the channel */
+  if (!silc_server_client_on_channel(target_client, channel, &chl)) {
+    tmp = silc_argument_get_arg_type(cmd->args, 1, &tmp_len);
+    target_idp = silc_argument_get_arg_type(cmd->args, 2, &target_idp_len);
+    silc_server_command_send_status_data2(cmd, SILC_COMMAND_KICK,
+                                         SILC_STATUS_ERR_USER_NOT_ON_CHANNEL,
+                                         0, 2, target_idp, target_idp_len,
+                                         3, tmp, tmp_len);
+    goto out;
   }
 
-  /* Finally, set the mode */
-  channel->mode = mode_mask;
+  /* Check that the target client is not channel founder. Channel founder
+     cannot be kicked from the channel. */
+  if (chl->mode & SILC_CHANNEL_UMODE_CHANFO) {
+    tmp = silc_argument_get_arg_type(cmd->args, 1, &tmp_len);
+    silc_server_command_send_status_data(cmd, SILC_COMMAND_KICK,
+                                        SILC_STATUS_ERR_NO_CHANNEL_FOPRIV,
+                                        0, 2, tmp, tmp_len);
+    goto out;
+  }
 
-  /* Send CMODE_CHANGE notify. */
-  cidp = silc_id_payload_encode(client->id, SILC_ID_CLIENT);
-  silc_server_send_notify_to_channel(server, NULL, channel, FALSE,
-                                    SILC_NOTIFY_TYPE_CMODE_CHANGE, 5,
-                                    cidp->data, cidp->len, 
-                                    tmp_mask, 4,
-                                    cipher, cipher ? strlen(cipher) : 0,
-                                    hmac, hmac ? strlen(hmac) : 0,
-                                    passphrase, passphrase ? 
-                                    strlen(passphrase) : 0);
+  /* Get comment */
+  comment = silc_argument_get_arg_type(cmd->args, 3, &clen);
+  if (clen > 128)
+    comment = NULL;
+
+  /* Send the reply back to the client */
+  tmp = silc_argument_get_arg_type(cmd->args, 1, &tmp_len);
+  target_idp = silc_argument_get_arg_type(cmd->args, 2, &target_idp_len);
+  silc_server_send_command_reply(server, cmd->sock, SILC_COMMAND_KICK,
+                                SILC_STATUS_OK, 0, ident, 2,
+                                2, tmp, tmp_len,
+                                3, target_idp, target_idp_len);
+
+  /* Send KICKED notify to local clients on the channel */
+  idp = silc_id_payload_encode(client->id, SILC_ID_CLIENT);
+  silc_server_send_notify_to_channel(server, NULL, channel, FALSE, TRUE,
+                                    SILC_NOTIFY_TYPE_KICKED, 3,
+                                    target_idp, target_idp_len,
+                                    comment, comment ? strlen(comment) : 0,
+                                    idp->data, silc_buffer_len(idp));
+  silc_buffer_free(idp);
+
+  /* Send KICKED notify to primary route */
+  silc_server_send_notify_kicked(server, SILC_PRIMARY_ROUTE(server),
+                                SILC_BROADCAST(server), channel,
+                                target_client->id, client->id, comment);
+
+  /* Remove the client from channel's invite list */
+  if (channel->invite_list && silc_hash_table_count(channel->invite_list)) {
+    SilcBuffer ab =
+      silc_argument_payload_encode_one(NULL, target_idp, target_idp_len, 3);
+    SilcArgumentPayload args =
+      silc_argument_payload_parse(ab->data, silc_buffer_len(ab), 1);
+
+    silc_server_inviteban_process(server, channel->invite_list, 1, args);
+    silc_buffer_free(ab);
+    silc_argument_payload_free(args);
+  }
 
-  /* Set CMODE notify type to network */
-  if (!server->standalone)
-    silc_server_send_notify_cmode(server, server->router->connection,
-                                 server->server_type == SILC_ROUTER ? 
-                                 TRUE : FALSE, channel,
-                                 mode_mask, client->id, SILC_ID_CLIENT,
-                                 cipher, hmac, passphrase);
+  /* Remove the client from the channel. If the channel does not exist
+     after removing the client then the client kicked itself off the channel
+     and we don't have to send anything after that. */
+  if (!silc_server_remove_from_one_channel(server, NULL, channel,
+                                          target_client, FALSE))
+    goto out;
 
-  /* Send command reply to sender */
-  packet = silc_command_reply_payload_encode_va(SILC_COMMAND_CMODE,
-                                               SILC_STATUS_OK, ident, 2,
-                                               2, tmp_id, tmp_len2,
-                                               3, tmp_mask, 4);
-  silc_server_packet_send(server, cmd->sock, SILC_PACKET_COMMAND_REPLY, 0, 
-                         packet->data, packet->len, FALSE);
-    
-  silc_buffer_free(packet);
-  silc_free(channel_id);
-  silc_buffer_free(cidp);
+  if (!(channel->mode & SILC_CHANNEL_MODE_PRIVKEY)) {
+    /* Re-generate channel key */
+    if (!silc_server_create_channel_key(server, channel, 0))
+      goto out;
+
+    /* Send the channel key to the channel. The key of course is not sent
+       to the client who was kicked off the channel. */
+    silc_server_send_channel_key(server, target_client->connection, channel,
+                                server->server_type == SILC_ROUTER ?
+                                FALSE : !server->standalone);
+  }
 
  out:
   silc_server_command_free(cmd);
 }
 
-/* Server side of CUMODE command. Changes client's mode on a channel. */
+/* Server side of OPER command. Client uses this comand to obtain server
+   operator privileges to this server/router. */
 
-SILC_SERVER_CMD_FUNC(cumode)
+SILC_SERVER_CMD_FUNC(oper)
 {
   SilcServerCommandContext cmd = (SilcServerCommandContext)context;
   SilcServer server = cmd->server;
-  SilcClientEntry client = (SilcClientEntry)cmd->sock->user_data;
+  SilcClientEntry client = silc_packet_get_context(cmd->sock);
+  unsigned char *username = NULL, *auth;
+  SilcUInt32 tmp_len;
+  SilcServerConfigAdmin *admin;
   SilcIDListData idata = (SilcIDListData)client;
-  SilcChannelID *channel_id;
-  SilcClientID *client_id;
-  SilcChannelEntry channel;
-  SilcClientEntry target_client;
-  SilcChannelClientEntry chl;
-  SilcBuffer packet, idp;
-  unsigned char *tmp_id, *tmp_ch_id, *tmp_mask;
-  uint32 target_mask, sender_mask = 0, tmp_len, tmp_ch_len;
-  int notify = FALSE;
-  uint16 ident = silc_command_get_ident(cmd->payload);
+  SilcBool result = FALSE;
+  SilcPublicKey cached_key;
+  const char *hostname, *ip;
 
-  SILC_SERVER_COMMAND_CHECK(SILC_COMMAND_CUMODE, cmd, 3, 4);
+  if (client->data.conn_type != SILC_CONN_CLIENT || !client)
+    goto out;
 
-  /* Get Channel ID */
-  tmp_ch_id = silc_argument_get_arg_type(cmd->args, 1, &tmp_ch_len);
-  if (!tmp_ch_id) {
-    silc_server_command_send_status_reply(cmd, SILC_COMMAND_CUMODE,
-                                         SILC_STATUS_ERR_NO_CHANNEL_ID);
+  SILC_SERVER_COMMAND_CHECK(SILC_COMMAND_OPER, cmd, 1, 2);
+
+  silc_socket_stream_get_info(silc_packet_stream_get_stream(cmd->sock),
+                             NULL, &hostname, &ip, NULL);
+
+  /* Get the username */
+  username = silc_argument_get_arg_type(cmd->args, 1, &tmp_len);
+  if (!username) {
+    silc_server_command_send_status_reply(cmd, SILC_COMMAND_OPER,
+                                         SILC_STATUS_ERR_NOT_ENOUGH_PARAMS,
+                                         0);
     goto out;
   }
-  channel_id = silc_id_payload_parse_id(tmp_ch_id, tmp_ch_len);
-  if (!channel_id) {
-    silc_server_command_send_status_reply(cmd, SILC_COMMAND_CUMODE,
-                                         SILC_STATUS_ERR_NO_CHANNEL_ID);
+
+  /* Check username */
+  username = silc_identifier_check(username, strlen(username),
+                                  SILC_STRING_UTF8, 128, &tmp_len);
+  if (!username) {
+    silc_server_command_send_status_reply(cmd, SILC_COMMAND_OPER,
+                                         SILC_STATUS_ERR_BAD_USERNAME,
+                                         0);
     goto out;
   }
 
-  /* Get channel entry */
-  channel = silc_idlist_find_channel_by_id(server->local_list, 
-                                          channel_id, NULL);
-  if (!channel) {
-    channel = silc_idlist_find_channel_by_id(server->global_list, 
-                                            channel_id, NULL);
-    if (!channel) {
-      silc_server_command_send_status_reply(cmd, SILC_COMMAND_CUMODE,
-                                           SILC_STATUS_ERR_NO_SUCH_CHANNEL);
+  /* Get the admin configuration */
+  admin = silc_server_config_find_admin(server, (char *)ip,
+                                       username, client->nickname);
+  if (!admin) {
+    admin = silc_server_config_find_admin(server, (char *)hostname,
+                                         username, client->nickname);
+    if (!admin) {
+      silc_server_command_send_status_reply(cmd, SILC_COMMAND_OPER,
+                                           SILC_STATUS_ERR_AUTH_FAILED,
+                                           0);
+      SILC_LOG_INFO(("OPER authentication failed for username '%s' by "
+                    "nickname '%s' from %s", username,
+                    client->nickname, hostname));
       goto out;
     }
   }
 
-  /* Check whether sender is on the channel */
-  if (!silc_server_client_on_channel(client, channel)) {
-    silc_server_command_send_status_reply(cmd, SILC_COMMAND_CUMODE,
-                                         SILC_STATUS_ERR_NOT_ON_CHANNEL);
-    goto out;
-  }
-
-  /* Check that client has rights to change other's rights */
-  silc_hash_table_find(channel->user_list, client, NULL, (void *)&chl);
-  sender_mask = chl->mode;
-  
-  /* Get the target client's channel mode mask */
-  tmp_mask = silc_argument_get_arg_type(cmd->args, 2, NULL);
-  if (!tmp_mask) {
-    silc_server_command_send_status_reply(cmd, SILC_COMMAND_CUMODE,
-                                         SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
+  /* Get the authentication payload */
+  auth = silc_argument_get_arg_type(cmd->args, 2, &tmp_len);
+  if (!auth) {
+    silc_server_command_send_status_reply(cmd, SILC_COMMAND_OPER,
+                                         SILC_STATUS_ERR_NOT_ENOUGH_PARAMS,
+                                         0);
     goto out;
   }
-  SILC_GET32_MSB(target_mask, tmp_mask);
 
-  /* Get target Client ID */
-  tmp_id = silc_argument_get_arg_type(cmd->args, 3, &tmp_len);
-  if (!tmp_id) {
-    silc_server_command_send_status_reply(cmd, SILC_COMMAND_CUMODE,
-                                         SILC_STATUS_ERR_NO_CLIENT_ID);
-    goto out;
+  /* Verify the authentication data. If both passphrase and public key
+     is set then try both of them. */
+  if (admin->passphrase)
+    result = silc_auth_verify_data(auth, tmp_len, SILC_AUTH_PASSWORD,
+                                  admin->passphrase, admin->passphrase_len,
+                                  idata->hash, client->id, SILC_ID_CLIENT);
+  if (!result && admin->publickeys) {
+    cached_key =
+      silc_server_get_public_key(server,
+                                SILC_SKR_USAGE_SERVICE_AUTHORIZATION, admin);
+    if (!cached_key)
+      goto out;
+    result = silc_auth_verify_data(auth, tmp_len, SILC_AUTH_PUBLIC_KEY,
+                                  cached_key, 0, idata->hash,
+                                  client->id, SILC_ID_CLIENT);
   }
-  client_id = silc_id_payload_parse_id(tmp_id, tmp_len);
-  if (!client_id) {
-    silc_server_command_send_status_reply(cmd, SILC_COMMAND_CUMODE,
-                                         SILC_STATUS_ERR_NO_CLIENT_ID);
+  if (!result) {
+    /* Authentication failed */
+    silc_server_command_send_status_reply(cmd, SILC_COMMAND_OPER,
+                                         SILC_STATUS_ERR_AUTH_FAILED,
+                                         0);
     goto out;
   }
 
-  /* Get target client's entry */
-  target_client = silc_idlist_find_client_by_id(server->local_list, 
-                                               client_id, TRUE, NULL);
-  if (!target_client) {
-    target_client = silc_idlist_find_client_by_id(server->global_list, 
-                                                 client_id, TRUE, NULL);
-  }
+  /* Client is now server operator */
+  client->mode |= SILC_UMODE_SERVER_OPERATOR;
 
-  if (target_client != client &&
-      !(sender_mask & SILC_CHANNEL_UMODE_CHANFO) &&
-      !(sender_mask & SILC_CHANNEL_UMODE_CHANOP)) {
-    silc_server_command_send_status_reply(cmd, SILC_COMMAND_CUMODE,
-                                         SILC_STATUS_ERR_NO_CHANNEL_PRIV);
-    goto out;
-  }
+  /* Update statistics */
+  if (SILC_IS_LOCAL(client))
+    server->stat.my_server_ops++;
+  if (server->server_type == SILC_ROUTER)
+    server->stat.server_ops++;
 
-  /* Check whether target client is on the channel */
-  if (target_client != client) {
-    if (!silc_server_client_on_channel(target_client, channel)) {
-      silc_server_command_send_status_reply(cmd, SILC_COMMAND_CUMODE,
-                                SILC_STATUS_ERR_USER_NOT_ON_CHANNEL);
-      goto out;
-    }
+  /* Send UMODE change to primary router */
+  silc_server_send_notify_umode(server, SILC_PRIMARY_ROUTE(server),
+                               SILC_BROADCAST(server), client->id,
+                               client->mode);
 
-    /* Get entry to the channel user list */
-    silc_hash_table_find(channel->user_list, target_client, NULL, 
-                        (void *)&chl);
-  }
+  /* Check if anyone is watching this nickname */
+  if (server->server_type == SILC_ROUTER)
+    silc_server_check_watcher_list(server, client, NULL,
+                                  SILC_NOTIFY_TYPE_UMODE_CHANGE);
 
-  /* 
-   * Change the mode 
-   */
+  /* Send reply to the sender */
+  silc_server_command_send_status_reply(cmd, SILC_COMMAND_OPER,
+                                       SILC_STATUS_OK, 0);
 
-  /* If the target client is founder, no one else can change their mode
-     but themselves. */
-  if (chl->mode & SILC_CHANNEL_UMODE_CHANFO && client != target_client) {
-    silc_server_command_send_status_reply(cmd, SILC_COMMAND_CUMODE,
-                                         SILC_STATUS_ERR_NO_CHANNEL_PRIV);
-    goto out;
-  }
+ out:
+  silc_free(username);
+  silc_server_command_free(cmd);
+}
 
-  if (target_mask & SILC_CHANNEL_UMODE_CHANFO) {
-    if (!(chl->mode & SILC_CHANNEL_UMODE_CHANFO)) {
-      /* The client tries to claim the founder rights. */
-      unsigned char *tmp_auth;
-      uint32 tmp_auth_len, auth_len;
-      void *auth;
-      
-      if (target_client != client) {
-       silc_server_command_send_status_reply(cmd, SILC_COMMAND_CUMODE,
-                                             SILC_STATUS_ERR_NOT_YOU);
-       goto out;
-      }
+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;
 
-      if (!(channel->mode & SILC_CHANNEL_MODE_FOUNDER_AUTH) ||
-         !channel->founder_key || !idata->public_key ||
-         !silc_pkcs_public_key_compare(channel->founder_key, 
-                                       idata->public_key)) {
-       silc_server_command_send_status_reply(cmd, SILC_COMMAND_CUMODE,
-                                             SILC_STATUS_ERR_NOT_YOU);
-       goto out;
-      }
+  client = silc_idlist_find_client_by_id(server->local_list, client_id,
+                                        TRUE, NULL);
+  if (client && client->connection) {
+    sock = client->connection;
 
-      tmp_auth = silc_argument_get_arg_type(cmd->args, 4, &tmp_auth_len);
-      if (!tmp_auth) {
-       silc_server_command_send_status_reply(cmd, SILC_COMMAND_CUMODE,
-                                    SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
-       goto out;
-      }
+    /* Close the connection on our side */
+    client->router = NULL;
+    client->connection = NULL;
+    silc_server_close_connection(server, sock);
+  }
 
-      auth = (channel->founder_method == SILC_AUTH_PASSWORD ?
-             (void *)channel->founder_passwd : (void *)channel->founder_key);
-      auth_len = (channel->founder_method == SILC_AUTH_PASSWORD ?
-                 channel->founder_passwd_len : 0);
-      
-      if (!silc_auth_verify_data(tmp_auth, tmp_auth_len,
-                                channel->founder_method, auth, auth_len,
-                                idata->hash, client->id, SILC_ID_CLIENT)) {
-       silc_server_command_send_status_reply(cmd, SILC_COMMAND_CUMODE,
-                                             SILC_STATUS_ERR_AUTH_FAILED);
-       goto out;
-      }
-      
-      sender_mask = chl->mode |= SILC_CHANNEL_UMODE_CHANFO;
-      notify = TRUE;
-    }
-  } else {
-    if (chl->mode & SILC_CHANNEL_UMODE_CHANFO) {
-      if (target_client == client) {
-       /* Remove channel founder rights from itself */
-       chl->mode &= ~SILC_CHANNEL_UMODE_CHANFO;
-       notify = TRUE;
-      } else {
-       silc_server_command_send_status_reply(cmd, SILC_COMMAND_CUMODE,
-                                             SILC_STATUS_ERR_NOT_YOU);
-       goto out;
-      }
-    }
+  silc_free(client_id);
+  silc_free(q);
+}
+
+SILC_TASK_CALLBACK(silc_server_command_detach_timeout)
+{
+  SilcServer server = app_context;
+  QuitInternal q = (QuitInternal)context;
+  SilcClientID *client_id = (SilcClientID *)q->sock;
+  SilcClientEntry client;
+
+  client = silc_idlist_find_client_by_id(server->local_list, client_id,
+                                        TRUE, NULL);
+  if (client && client->mode & SILC_UMODE_DETACHED) {
+    SILC_LOG_DEBUG(("Detach timeout"));
+    silc_server_free_client_data(server, NULL, client, TRUE,
+                                "Detach timeout");
   }
 
-  if (target_mask & SILC_CHANNEL_UMODE_CHANOP) {
-    /* Promote to operator */
-    if (!(chl->mode & SILC_CHANNEL_UMODE_CHANOP)) {
-      if (!(sender_mask & SILC_CHANNEL_UMODE_CHANOP) &&
-         !(sender_mask & SILC_CHANNEL_UMODE_CHANFO)) {
-       silc_server_command_send_status_reply(cmd, SILC_COMMAND_CUMODE,
-                                             SILC_STATUS_ERR_NO_CHANNEL_PRIV);
-       goto out;
-      }
+  silc_free(client_id);
+  silc_free(q);
+}
 
-      chl->mode |= SILC_CHANNEL_UMODE_CHANOP;
-      notify = TRUE;
-    }
-  } else {
-    if (chl->mode & SILC_CHANNEL_UMODE_CHANOP) {
-      if (!(sender_mask & SILC_CHANNEL_UMODE_CHANOP) &&
-         !(sender_mask & SILC_CHANNEL_UMODE_CHANFO)) {
-       silc_server_command_send_status_reply(cmd, SILC_COMMAND_CUMODE,
-                                             SILC_STATUS_ERR_NO_CHANNEL_PRIV);
-       goto out;
-      }
+/* Server side of DETACH command.  Detached the client from the network
+   by closing the connection but preserving the session. */
 
-      /* Demote to normal user */
-      chl->mode &= ~SILC_CHANNEL_UMODE_CHANOP;
-      notify = TRUE;
-    }
+SILC_SERVER_CMD_FUNC(detach)
+{
+  SilcServerCommandContext cmd = (SilcServerCommandContext)context;
+  SilcServer server = cmd->server;
+  SilcClientEntry client = silc_packet_get_context(cmd->sock);
+  QuitInternal q;
+
+  if (server->config->detach_disabled) {
+    silc_server_command_send_status_reply(cmd, SILC_COMMAND_DETACH,
+                                         SILC_STATUS_ERR_OPERATION_ALLOWED,
+                                         0);
+    goto out;
   }
 
-  idp = silc_id_payload_encode(client->id, SILC_ID_CLIENT);
-  tmp_id = silc_argument_get_arg_type(cmd->args, 3, &tmp_len);
+  if (client->data.conn_type != SILC_CONN_CLIENT || !client)
+    goto out;
 
-  /* Send notify to channel, notify only if mode was actually changed. */
-  if (notify) {
-    silc_server_send_notify_to_channel(server, NULL, channel, FALSE, 
-                                      SILC_NOTIFY_TYPE_CUMODE_CHANGE, 3,
-                                      idp->data, idp->len,
-                                      tmp_mask, 4, 
-                                      tmp_id, tmp_len);
+  SILC_SERVER_COMMAND_CHECK(SILC_COMMAND_DETACH, cmd, 0, 0);
+
+  /* Remove operator privileges, since the client may resume in some
+     other server which to it does not have operator privileges. */
+  SILC_OPER_STATS_UPDATE(client, server, SILC_UMODE_SERVER_OPERATOR);
+  SILC_OPER_STATS_UPDATE(client, router, SILC_UMODE_ROUTER_OPERATOR);
+
+  /* Send the user mode notify to notify that client is detached */
+  client->mode |= SILC_UMODE_DETACHED;
+  client->data.status &= ~SILC_IDLIST_STATUS_RESUMED;
+  client->data.status &= ~SILC_IDLIST_STATUS_NOATTR;
+  client->last_command = 0;
+  client->fast_command = 0;
+  silc_server_send_notify_umode(server, SILC_PRIMARY_ROUTE(server),
+                               SILC_BROADCAST(server), client->id,
+                               client->mode);
+  server->stat.my_detached++;
+
+  /* Check if anyone is watching this nickname */
+  if (server->server_type == SILC_ROUTER)
+    silc_server_check_watcher_list(server, client, NULL,
+                                  SILC_NOTIFY_TYPE_UMODE_CHANGE);
 
-    /* Set CUMODE notify type to network */
-    if (!server->standalone)
-      silc_server_send_notify_cumode(server, server->router->connection,
-                                    server->server_type == SILC_ROUTER ? 
-                                    TRUE : FALSE, channel,
-                                    target_mask, client->id, 
-                                    SILC_ID_CLIENT,
-                                    target_client->id);
+  q = silc_calloc(1, sizeof(*q));
+  q->sock = silc_id_dup(client->id, SILC_ID_CLIENT);
+  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);
+    silc_schedule_task_add_timeout(server->schedule,
+                                  silc_server_command_detach_timeout,
+                                  q, server->config->detach_timeout * 60, 0);
   }
 
-  /* Send command reply to sender */
-  packet = silc_command_reply_payload_encode_va(SILC_COMMAND_CUMODE,
-                                               SILC_STATUS_OK, ident, 3,
-                                               2, tmp_mask, 4,
-                                               3, tmp_ch_id, tmp_ch_len,
-                                               4, tmp_id, tmp_len);
-  silc_server_packet_send(server, cmd->sock, SILC_PACKET_COMMAND_REPLY, 0, 
-                         packet->data, packet->len, FALSE);
-    
-  silc_buffer_free(packet);
-  silc_free(channel_id);
-  silc_free(client_id);
-  silc_buffer_free(idp);
+  /* Send reply to the sender */
+  silc_server_command_send_status_reply(cmd, SILC_COMMAND_DETACH,
+                                       SILC_STATUS_OK, 0);
 
  out:
   silc_server_command_free(cmd);
 }
 
-/* Server side of KICK command. Kicks client out of channel. */
+/* Server side of WATCH command. */
 
-SILC_SERVER_CMD_FUNC(kick)
+SILC_SERVER_CMD_FUNC(watch)
 {
   SilcServerCommandContext cmd = (SilcServerCommandContext)context;
   SilcServer server = cmd->server;
-  SilcClientEntry client = (SilcClientEntry)cmd->sock->user_data;
-  SilcClientEntry target_client;
-  SilcChannelID *channel_id;
-  SilcClientID *client_id;
-  SilcChannelEntry channel;
-  SilcChannelClientEntry chl;
-  SilcBuffer idp;
-  uint32 tmp_len, target_idp_len;
-  unsigned char *tmp, *comment, *target_idp;
+  char *add_nick, *del_nick;
+  SilcUInt32 add_nick_len, del_nick_len, tmp_len, pk_len;
+  unsigned char hash[SILC_HASH_MAXLEN], *tmp,  *pk, *nick;
+  SilcClientEntry client;
+  SilcID id;
+  SilcUInt16 old_ident;
 
-  SILC_SERVER_COMMAND_CHECK(SILC_COMMAND_LEAVE, cmd, 1, 3);
+  SILC_SERVER_COMMAND_CHECK(SILC_COMMAND_WATCH, cmd, 1, 3);
 
-  /* Get Channel ID */
-  tmp = silc_argument_get_arg_type(cmd->args, 1, &tmp_len);
-  if (!tmp) {
-    silc_server_command_send_status_reply(cmd, SILC_COMMAND_KICK,
-                                         SILC_STATUS_ERR_NO_CHANNEL_ID);
-    goto out;
+  if (server->server_type != SILC_ROUTER && !server->standalone) {
+    if (!cmd->pending) {
+      /* Send the command to router */
+      SilcBuffer tmpbuf;
+
+      /* If backup receives this from primary, handle it locally */
+      if (server->server_type == SILC_BACKUP_ROUTER &&
+         cmd->sock == SILC_PRIMARY_ROUTE(server))
+       goto process_watch;
+
+      SILC_LOG_DEBUG(("Forwarding WATCH to router"));
+
+      /* Statistics */
+      cmd->server->stat.commands_sent++;
+
+      old_ident = silc_command_get_ident(cmd->payload);
+      silc_command_set_ident(cmd->payload, ++server->cmd_ident);
+      tmpbuf = silc_command_payload_encode_payload(cmd->payload);
+
+      silc_server_packet_send(server, SILC_PRIMARY_ROUTE(server),
+                             SILC_PACKET_COMMAND, cmd->packet->flags,
+                             tmpbuf->data, silc_buffer_len(tmpbuf));
+
+      /* Reprocess this packet after received reply from router */
+      silc_server_command_pending(server, SILC_COMMAND_WATCH,
+                                 silc_command_get_ident(cmd->payload),
+                                 silc_server_command_watch,
+                                 silc_server_command_dup(cmd));
+      cmd->pending = TRUE;
+      silc_command_set_ident(cmd->payload, old_ident);
+      silc_buffer_free(tmpbuf);
+      goto out;
+    } else {
+      SilcServerCommandReplyContext reply = context2;
+      SilcStatus status;
+
+      if (!reply)
+        goto out;
+
+      silc_command_get_status(reply->payload, &status, NULL);
+
+      /* Backup router handles the WATCH command also. */
+      if (server->server_type != SILC_BACKUP_ROUTER ||
+         SILC_STATUS_IS_ERROR(status)) {
+       /* Received reply from router, just send same data to the client. */
+       SILC_LOG_DEBUG(("Received reply to WATCH from router"));
+       silc_server_command_send_status_reply(cmd, SILC_COMMAND_WATCH, status,
+                                             0);
+       goto out;
+      }
+    }
   }
-  channel_id = silc_id_payload_parse_id(tmp, tmp_len);
-  if (!channel_id) {
-    silc_server_command_send_status_reply(cmd, SILC_COMMAND_KICK,
-                                         SILC_STATUS_ERR_NO_CHANNEL_ID);
+
+  /* We are router and keep the watch list for local cell */
+ process_watch:
+
+  /* Get the client ID */
+  if (!silc_argument_get_decoded(cmd->args, 1, SILC_ARGUMENT_ID, &id, NULL)) {
+    silc_server_command_send_status_reply(cmd, SILC_COMMAND_WATCH,
+                                         SILC_STATUS_ERR_NOT_ENOUGH_PARAMS,
+                                         0);
     goto out;
   }
 
-  /* Get channel entry */
-  channel = silc_idlist_find_channel_by_id(server->local_list, 
-                                          channel_id, NULL);
-  if (!channel) {
-    channel = silc_idlist_find_channel_by_id(server->local_list, 
-                                            channel_id, NULL);
-    if (!channel) {
-      silc_server_command_send_status_reply(cmd, SILC_COMMAND_KICK,
-                                           SILC_STATUS_ERR_NO_SUCH_CHANNEL);
+  /* Get the client entry which must be in local list */
+  client = silc_idlist_find_client_by_id(server->local_list,
+                                        SILC_ID_GET_ID(id), TRUE, NULL);
+  if (!client) {
+    /* Backup checks global list also */
+    if (server->server_type == SILC_BACKUP_ROUTER)
+      client = silc_idlist_find_client_by_id(server->global_list,
+                                            SILC_ID_GET_ID(id), TRUE, NULL);
+    if (!client) {
+      tmp = silc_argument_get_arg_type(cmd->args, 1, &tmp_len);
+      silc_server_command_send_status_data(cmd, SILC_COMMAND_WATCH,
+                                          SILC_STATUS_ERR_NO_SUCH_CLIENT_ID,
+                                          0, 2, tmp, tmp_len);
       goto out;
     }
   }
 
-  /* Check whether sender is on the channel */
-  if (!silc_server_client_on_channel(client, channel)) {
-    silc_server_command_send_status_reply(cmd, SILC_COMMAND_KICK,
-                                         SILC_STATUS_ERR_NOT_ON_CHANNEL);
-    goto out;
-  }
+  /* Take public key for watching by public key */
+  pk = silc_argument_get_arg_type(cmd->args, 4, &pk_len);
 
-  /* Check that the kicker is channel operator or channel founder */
-  silc_hash_table_find(channel->user_list, client, NULL, (void *)&chl);
-  if (chl->mode == SILC_CHANNEL_UMODE_NONE) {
-    silc_server_command_send_status_reply(cmd, SILC_COMMAND_KICK,
-                                         SILC_STATUS_ERR_NO_CHANNEL_PRIV);
-    goto out;
-  }
-  
-  /* Get target Client ID */
-  target_idp = silc_argument_get_arg_type(cmd->args, 2, &target_idp_len);
-  if (!target_idp) {
-    silc_server_command_send_status_reply(cmd, SILC_COMMAND_KICK,
-                                         SILC_STATUS_ERR_NO_CLIENT_ID);
-    goto out;
-  }
-  client_id = silc_id_payload_parse_id(target_idp, target_idp_len);
-  if (!client_id) {
-    silc_server_command_send_status_reply(cmd, SILC_COMMAND_KICK,
-                                         SILC_STATUS_ERR_NO_CLIENT_ID);
+  /* Take nickname */
+  add_nick = silc_argument_get_arg_type(cmd->args, 2, &add_nick_len);
+  del_nick = silc_argument_get_arg_type(cmd->args, 3, &del_nick_len);
+  if (!add_nick && !del_nick && !pk) {
+    silc_server_command_send_status_reply(cmd, SILC_COMMAND_WATCH,
+                                         SILC_STATUS_ERR_NOT_ENOUGH_PARAMS,
+                                         0);
     goto out;
   }
 
-  /* Get target client's entry */
-  target_client = silc_idlist_find_client_by_id(server->local_list, 
-                                               client_id, TRUE, NULL);
-  if (!target_client) {
-    target_client = silc_idlist_find_client_by_id(server->global_list, 
-                                                 client_id, TRUE, NULL);
-  }
-
-  /* Check that the target client is not channel founder. Channel founder
-     cannot be kicked from the channel. */
-  silc_hash_table_find(channel->user_list, target_client, NULL, (void *)&chl);
-  if (chl->mode & SILC_CHANNEL_UMODE_CHANFO) {
-    silc_server_command_send_status_reply(cmd, SILC_COMMAND_KICK,
-                                         SILC_STATUS_ERR_NO_CHANNEL_FOPRIV);
-    goto out;
+  if (add_nick && add_nick_len > 128) {
+    add_nick_len = 128;
+    add_nick[add_nick_len - 1] = '\0';
   }
-  
-  /* Check whether target client is on the channel */
-  if (!silc_server_client_on_channel(target_client, channel)) {
-    silc_server_command_send_status_reply(cmd, SILC_COMMAND_KICK,
-                                         SILC_STATUS_ERR_USER_NOT_ON_CHANNEL);
-    goto out;
+  if (del_nick && del_nick_len > 128) {
+    del_nick_len = 128;
+    del_nick[del_nick_len - 1] = '\0';
   }
 
-  /* Get comment */
-  tmp_len = 0;
-  comment = silc_argument_get_arg_type(cmd->args, 3, &tmp_len);
-  if (tmp_len > 128)
-    comment = NULL;
+  /* Add new nickname to be watched in our cell */
+  if (add_nick) {
+    nick = silc_identifier_check(add_nick, add_nick_len, SILC_STRING_UTF8, 128,
+                                &add_nick_len);
+    if (!nick) {
+      silc_server_command_send_status_reply(cmd, SILC_COMMAND_WATCH,
+                                           SILC_STATUS_ERR_BAD_NICKNAME, 0);
+      goto out;
+    }
 
-  /* Send command reply to sender */
-  silc_server_command_send_status_reply(cmd, SILC_COMMAND_KICK, 
-                                       SILC_STATUS_OK);
+    /* Hash the nick, we have the hash saved, not nicks because we can
+       do one to one mapping to the nick from Client ID hash this way. */
+    silc_hash_make(server->md5hash, nick, add_nick_len, hash);
+
+    /* Check whether this client is already watching this nickname */
+    if (silc_hash_table_find_by_context(server->watcher_list, hash,
+                                       client, NULL)) {
+      /* Nickname is alredy being watched for this client */
+      silc_server_command_send_status_reply(cmd, SILC_COMMAND_WATCH,
+                                           SILC_STATUS_ERR_NICKNAME_IN_USE,
+                                           0);
+      silc_free(nick);
+      goto out;
+    }
 
-  /* Send KICKED notify to local clients on the channel */
-  idp = silc_id_payload_encode(client->id, SILC_ID_CLIENT);
-  silc_server_send_notify_to_channel(server, NULL, channel, FALSE,
-                                    SILC_NOTIFY_TYPE_KICKED, 3,
-                                    target_idp, target_idp_len,
-                                    comment, comment ? strlen(comment) : 0,
-                                    idp->data, idp->len);
-  silc_buffer_free(idp);
+    /* Get the nickname from the watcher list and use the same key in
+       new entries as well.  If key doesn't exist then create it. */
+    if (!silc_hash_table_find(server->watcher_list, hash, (void *)&tmp, NULL))
+      tmp = silc_memdup(hash, CLIENTID_HASH_LEN);
 
-  /* Remove the client from the channel. If the channel does not exist
-     after removing the client then the client kicked itself off the channel
-     and we don't have to send anything after that. */
-  if (!silc_server_remove_from_one_channel(server, NULL, channel, 
-                                          target_client, FALSE))
-    goto out;
+    /* Add the client to the watcher list with the specified nickname hash. */
+    silc_hash_table_add(server->watcher_list, tmp, client);
+    silc_free(nick);
+  }
 
-  /* Send KICKED notify to primary route */
-  if (!server->standalone)
-    silc_server_send_notify_kicked(server, server->router->connection,
-                                  server->server_type == SILC_ROUTER ?
-                                  TRUE : FALSE, channel,
-                                  target_client->id, client->id, comment);
+  /* Delete nickname from watch list */
+  if (del_nick) {
+    nick = silc_identifier_check(del_nick, del_nick_len, SILC_STRING_UTF8, 128,
+                                &del_nick_len);
+    if (!nick) {
+      silc_server_command_send_status_reply(cmd, SILC_COMMAND_WATCH,
+                                           SILC_STATUS_ERR_BAD_NICKNAME, 0);
+      goto out;
+    }
 
-  if (!(channel->mode & SILC_CHANNEL_MODE_PRIVKEY)) {
-    /* Re-generate channel key */
-    if (!silc_server_create_channel_key(server, channel, 0))
+    /* Hash the nick, we have the hash saved, not nicks because we can
+       do one to one mapping to the nick from Client ID hash this way. */
+    silc_hash_make(server->md5hash, nick, del_nick_len, hash);
+
+    /* Check that this client is watching for this nickname */
+    if (!silc_hash_table_find_by_context(server->watcher_list, hash,
+                                        client, (void *)&tmp)) {
+      /* Nickname is alredy being watched for this client */
+      silc_server_command_send_status_data(cmd, SILC_COMMAND_WATCH,
+                                          SILC_STATUS_ERR_NO_SUCH_NICK, 0,
+                                          2, nick, del_nick_len);
+      silc_free(nick);
       goto out;
-    
-    /* Send the channel key to the channel. The key of course is not sent
-       to the client who was kicked off the channel. */
-    silc_server_send_channel_key(server, target_client->connection, channel, 
-                                server->server_type == SILC_ROUTER ? 
-                                FALSE : !server->standalone);
+    }
+
+    /* Delete the nickname from the watcher list. */
+    silc_hash_table_del_by_context(server->watcher_list, hash, client);
+
+    /* Now check whether there still exists entries with this key, if not
+       then free the key to not leak memory. */
+    if (!silc_hash_table_find(server->watcher_list, hash, NULL, NULL))
+      silc_free(tmp);
+    silc_free(nick);
   }
 
- out:
-  silc_server_command_free(cmd);
-}
+  /* Add/del public key */
+  if (pk) {
+    SilcUInt16 pkargc;
+    SilcArgumentPayload pkargs;
+    SilcUInt32 type;
+    SilcPublicKey public_key, pkkey;
 
-/* Server side of OPER command. Client uses this comand to obtain server
-   operator privileges to this server/router. */
+    if (pk_len < 2) {
+      silc_server_command_send_status_reply(cmd, SILC_COMMAND_WATCH,
+                                           SILC_STATUS_ERR_NOT_ENOUGH_PARAMS,
+                                           0);
+      goto out;
+    }
 
-SILC_SERVER_CMD_FUNC(oper)
-{
-  SilcServerCommandContext cmd = (SilcServerCommandContext)context;
-  SilcServer server = cmd->server;
-  SilcClientEntry client = (SilcClientEntry)cmd->sock->user_data;
-  unsigned char *username, *auth;
-  uint32 tmp_len;
-  SilcServerConfigAdmin *admin;
-  SilcIDListData idata = (SilcIDListData)client;
-  bool result = FALSE;
+    /* Get the argument from the Argument List Payload */
+    SILC_GET16_MSB(pkargc, pk);
+    pkargs = silc_argument_payload_parse(pk + 2, pk_len - 2, pkargc);
+    if (!pkargs) {
+      silc_server_command_send_status_reply(cmd, SILC_COMMAND_WATCH,
+                                           SILC_STATUS_ERR_NOT_ENOUGH_PARAMS,
+                                           0);
+      goto out;
+    }
 
-  SILC_SERVER_COMMAND_CHECK(SILC_COMMAND_OPER, cmd, 1, 2);
+    pk = silc_argument_get_next_arg(pkargs, &type, &pk_len);
+    while (pk) {
+      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;
 
-  if (!client || cmd->sock->type != SILC_SOCKET_TYPE_CLIENT)
-    goto out;
+      if (type == 0x00) {
+       /* Add public key to watch list */
 
-  /* Get the username */
-  username = silc_argument_get_arg_type(cmd->args, 1, &tmp_len);
-  if (!username) {
-    silc_server_command_send_status_reply(cmd, SILC_COMMAND_OPER,
-                                         SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
-    goto out;
-  }
+       /* Check whether this client is already watching this public key */
+       if (silc_hash_table_find_by_context(server->watcher_list_pk,
+                                           public_key, client, NULL)) {
+         silc_pkcs_public_key_free(public_key);
+         silc_server_command_send_status_reply(
+                               cmd, SILC_COMMAND_WATCH,
+                               SILC_STATUS_ERR_NICKNAME_IN_USE, 0);
+         goto out;
+       }
 
-  /* Get the admin configuration */
-  admin = silc_server_config_find_admin(server, cmd->sock->ip,
-                                       username, client->nickname);
-  if (!admin) {
-    admin = silc_server_config_find_admin(server, cmd->sock->hostname,
-                                         username, client->nickname);
-    if (!admin) {
-      silc_server_command_send_status_reply(cmd, SILC_COMMAND_OPER,
-                                           SILC_STATUS_ERR_AUTH_FAILED);
-      goto out;
-    }
-  }
+       /* Get the public key from the watcher list and use the same key in
+          new entries as well.  If key doesn't exist then create it. */
+       pkkey = NULL;
+       if (!silc_hash_table_find(server->watcher_list_pk, public_key,
+                                 (void *)&pkkey, NULL))
+         pkkey = public_key;
+       else
+         silc_pkcs_public_key_free(public_key);
+
+       /* Add the client to the watcher list with the specified public
+          key. */
+       silc_hash_table_add(server->watcher_list_pk, pkkey, client);
+
+      } else if (type == 0x01) {
+       /* Delete public key from watch list */
+
+       /* Check that this client is watching this public key */
+       if (silc_hash_table_find_by_context(server->watcher_list_pk,
+                                           public_key, client,
+                                           (void *)&pkkey)) {
+         silc_pkcs_public_key_free(public_key);
+         silc_server_command_send_status_reply(
+                               cmd, SILC_COMMAND_WATCH,
+                               SILC_STATUS_ERR_NOT_ENOUGH_PARAMS, 0);
+         goto out;
+       }
 
-  /* Get the authentication payload */
-  auth = silc_argument_get_arg_type(cmd->args, 2, &tmp_len);
-  if (!auth) {
-    silc_server_command_send_status_reply(cmd, SILC_COMMAND_OPER,
-                                         SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
-    goto out;
-  }
+       /* Delete the public key from the watcher list. */
+       silc_hash_table_del_by_context(server->watcher_list_pk,
+                                      public_key, client);
 
-  /* Verify the authentication data. If both passphrase and public key
-     is set then try both of them. */
-  if (admin->passphrase)
-    result = silc_auth_verify_data(auth, tmp_len, SILC_AUTH_PASSWORD,
-                                  admin->passphrase, admin->passphrase_len,
-                                  idata->hash, client->id, SILC_ID_CLIENT);
-  if (!result && admin->publickey)
-    result = silc_auth_verify_data(auth, tmp_len, SILC_AUTH_PUBLIC_KEY,
-                                  admin->publickey, 0,
-                                  idata->hash, client->id, SILC_ID_CLIENT);
-  if (!result) {
-    /* Authentication failed */
-    silc_server_command_send_status_reply(cmd, SILC_COMMAND_OPER,
-                                         SILC_STATUS_ERR_AUTH_FAILED);
-    goto out;
+       /* Now check whether there still exists entries with this key, if
+          not then free the key to not leak memory. */
+       if (!silc_hash_table_find(server->watcher_list_pk, hash, NULL, NULL))
+         silc_pkcs_public_key_free(pkkey);
+        silc_pkcs_public_key_free(public_key);
+      }
+
+      pk = silc_argument_get_next_arg(pkargs, &type, &pk_len);
+    }
   }
 
-  /* Client is now server operator */
-  client->mode |= SILC_UMODE_SERVER_OPERATOR;
+  /* Send reply */
+  silc_server_command_send_status_reply(cmd, SILC_COMMAND_WATCH,
+                                       SILC_STATUS_OK, 0);
 
-  /* Update statistics */
-  if (client->connection)
-    server->stat.my_server_ops++;
-  if (server->server_type == SILC_ROUTER)
-    server->stat.server_ops++;
+  /* Distribute the watch list to backup routers too */
+  if (server->backup) {
+    SilcBuffer tmpbuf;
 
-  /* Send UMODE change to primary router */
-  if (!server->standalone)
-    silc_server_send_notify_umode(server, server->router->connection, TRUE,
-                                 client->id, client->mode);
+    /* Statistics */
+    cmd->server->stat.commands_sent++;
 
-  /* Send reply to the sender */
-  silc_server_command_send_status_reply(cmd, SILC_COMMAND_OPER,
-                                       SILC_STATUS_OK);
+    old_ident = silc_command_get_ident(cmd->payload);
+    silc_command_set_ident(cmd->payload, ++server->cmd_ident);
+    tmpbuf = silc_command_payload_encode_payload(cmd->payload);
+    silc_server_backup_send(server, silc_packet_get_context(cmd->sock),
+                           SILC_PACKET_COMMAND,
+                           cmd->packet->flags, tmpbuf->data,
+                           silc_buffer_len(tmpbuf),
+                           FALSE, TRUE);
+    silc_command_set_ident(cmd->payload, old_ident);
+    silc_buffer_free(tmpbuf);
+  }
 
  out:
   silc_server_command_free(cmd);
@@ -4668,21 +4477,26 @@ SILC_SERVER_CMD_FUNC(silcoper)
 {
   SilcServerCommandContext cmd = (SilcServerCommandContext)context;
   SilcServer server = cmd->server;
-  SilcClientEntry client = (SilcClientEntry)cmd->sock->user_data;
-  unsigned char *username, *auth;
-  uint32 tmp_len;
+  SilcClientEntry client = silc_packet_get_context(cmd->sock);
+  unsigned char *username = NULL, *auth;
+  SilcUInt32 tmp_len;
   SilcServerConfigAdmin *admin;
   SilcIDListData idata = (SilcIDListData)client;
-  bool result = FALSE;
+  SilcBool result = FALSE;
+  SilcPublicKey cached_key;
+  const char *hostname, *ip;
+
+  if (client->data.conn_type != SILC_CONN_CLIENT || !client)
+    goto out;
 
   SILC_SERVER_COMMAND_CHECK(SILC_COMMAND_SILCOPER, cmd, 1, 2);
 
-  if (!client || cmd->sock->type != SILC_SOCKET_TYPE_CLIENT)
-    goto out;
+  silc_socket_stream_get_info(silc_packet_stream_get_stream(cmd->sock),
+                             NULL, &hostname, &ip, NULL);
 
   if (server->server_type != SILC_ROUTER) {
     silc_server_command_send_status_reply(cmd, SILC_COMMAND_SILCOPER,
-                                         SILC_STATUS_ERR_AUTH_FAILED);
+                                         SILC_STATUS_ERR_AUTH_FAILED, 0);
     goto out;
   }
 
@@ -4690,19 +4504,33 @@ SILC_SERVER_CMD_FUNC(silcoper)
   username = silc_argument_get_arg_type(cmd->args, 1, &tmp_len);
   if (!username) {
     silc_server_command_send_status_reply(cmd, SILC_COMMAND_SILCOPER,
-                                         SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
+                                         SILC_STATUS_ERR_NOT_ENOUGH_PARAMS,
+                                         0);
+    goto out;
+  }
+
+  /* Check username */
+  username = silc_identifier_check(username, tmp_len, SILC_STRING_UTF8, 128,
+                                  &tmp_len);
+  if (!username) {
+    silc_server_command_send_status_reply(cmd, SILC_COMMAND_SILCOPER,
+                                         SILC_STATUS_ERR_BAD_USERNAME,
+                                         0);
     goto out;
   }
 
   /* Get the admin configuration */
-  admin = silc_server_config_find_admin(server, cmd->sock->ip,
+  admin = silc_server_config_find_admin(server, (char *)ip,
                                        username, client->nickname);
   if (!admin) {
-    admin = silc_server_config_find_admin(server, cmd->sock->hostname,
+    admin = silc_server_config_find_admin(server, (char *)hostname,
                                          username, client->nickname);
     if (!admin) {
       silc_server_command_send_status_reply(cmd, SILC_COMMAND_SILCOPER,
-                                           SILC_STATUS_ERR_AUTH_FAILED);
+                                           SILC_STATUS_ERR_AUTH_FAILED, 0);
+      SILC_LOG_INFO(("SILCOPER authentication failed for username '%s' by "
+                    "nickname '%s' from %s", username,
+                    client->nickname, hostname));
       goto out;
     }
   }
@@ -4711,7 +4539,8 @@ SILC_SERVER_CMD_FUNC(silcoper)
   auth = silc_argument_get_arg_type(cmd->args, 2, &tmp_len);
   if (!auth) {
     silc_server_command_send_status_reply(cmd, SILC_COMMAND_SILCOPER,
-                                         SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
+                                         SILC_STATUS_ERR_NOT_ENOUGH_PARAMS,
+                                         0);
     goto out;
   }
 
@@ -4721,14 +4550,20 @@ SILC_SERVER_CMD_FUNC(silcoper)
     result = silc_auth_verify_data(auth, tmp_len, SILC_AUTH_PASSWORD,
                                   admin->passphrase, admin->passphrase_len,
                                   idata->hash, client->id, SILC_ID_CLIENT);
-  if (!result && admin->publickey)
+  if (!result && admin->publickeys) {
+    cached_key =
+      silc_server_get_public_key(server,
+                                SILC_SKR_USAGE_SERVICE_AUTHORIZATION, admin);
+    if (!cached_key)
+      goto out;
     result = silc_auth_verify_data(auth, tmp_len, SILC_AUTH_PUBLIC_KEY,
-                                  admin->publickey, 0,
-                                  idata->hash, client->id, SILC_ID_CLIENT);
+                                  cached_key, 0, idata->hash,
+                                  client->id, SILC_ID_CLIENT);
+  }
   if (!result) {
     /* Authentication failed */
-    silc_server_command_send_status_reply(cmd, SILC_COMMAND_OPER,
-                                         SILC_STATUS_ERR_AUTH_FAILED);
+    silc_server_command_send_status_reply(cmd, SILC_COMMAND_SILCOPER,
+                                         SILC_STATUS_ERR_AUTH_FAILED, 0);
     goto out;
   }
 
@@ -4736,76 +4571,27 @@ SILC_SERVER_CMD_FUNC(silcoper)
   client->mode |= SILC_UMODE_ROUTER_OPERATOR;
 
   /* Update statistics */
-  if (client->connection)
+  if (SILC_IS_LOCAL(client))
     server->stat.my_router_ops++;
   if (server->server_type == SILC_ROUTER)
     server->stat.router_ops++;
 
   /* Send UMODE change to primary router */
-  if (!server->standalone)
-    silc_server_send_notify_umode(server, server->router->connection, TRUE,
-                                 client->id, client->mode);
-
-  /* Send reply to the sender */
-  silc_server_command_send_status_reply(cmd, SILC_COMMAND_SILCOPER,
-                                       SILC_STATUS_OK);
-
- out:
-  silc_server_command_free(cmd);
-}
+  silc_server_send_notify_umode(server, SILC_PRIMARY_ROUTE(server),
+                               SILC_BROADCAST(server), client->id,
+                               client->mode);
 
-/* Server side command of CONNECT. Connects us to the specified remote
-   server or router. */
-
-SILC_SERVER_CMD_FUNC(connect)
-{
-  SilcServerCommandContext cmd = (SilcServerCommandContext)context;
-  SilcServer server = cmd->server;
-  SilcClientEntry client = (SilcClientEntry)cmd->sock->user_data;
-  unsigned char *tmp, *host;
-  uint32 tmp_len;
-  uint32 port = SILC_PORT;
-
-  SILC_SERVER_COMMAND_CHECK(SILC_COMMAND_CONNECT, cmd, 1, 2);
-
-  if (!client || cmd->sock->type != SILC_SOCKET_TYPE_CLIENT)
-    goto out;
-
-  /* Check whether client has the permissions. */
-  if (client->mode == SILC_UMODE_NONE) {
-    silc_server_command_send_status_reply(cmd, SILC_COMMAND_CONNECT,
-                                         SILC_STATUS_ERR_NO_SERVER_PRIV);
-    goto out;
-  }
-
-  if (server->server_type == SILC_ROUTER && 
-      client->mode & SILC_UMODE_SERVER_OPERATOR) {
-    silc_server_command_send_status_reply(cmd, SILC_COMMAND_CONNECT,
-                                         SILC_STATUS_ERR_NO_ROUTER_PRIV);
-    goto out;
-  }
-
-  /* Get the remote server */
-  host = silc_argument_get_arg_type(cmd->args, 1, &tmp_len);
-  if (!host) {
-    silc_server_command_send_status_reply(cmd, SILC_COMMAND_CONNECT,
-                                         SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
-    goto out;
-  }
-
-  /* Get port */
-  tmp = silc_argument_get_arg_type(cmd->args, 2, &tmp_len);
-  if (tmp)
-    SILC_GET32_MSB(port, tmp);
-
-  /* Create the connection. It is done with timeout and is async. */
-  silc_server_create_connection(server, host, port);
+  /* Check if anyone is watching this nickname */
+  if (server->server_type == SILC_ROUTER)
+    silc_server_check_watcher_list(server, client, NULL,
+                                  SILC_NOTIFY_TYPE_UMODE_CHANGE);
 
   /* Send reply to the sender */
-  silc_server_command_send_status_reply(cmd, SILC_COMMAND_CONNECT,
-                                       SILC_STATUS_OK);
+  silc_server_command_send_status_reply(cmd, SILC_COMMAND_SILCOPER,
+                                       SILC_STATUS_OK, 0);
 
  out:
+  silc_free(username);
   silc_server_command_free(cmd);
 }
 
@@ -4816,290 +4602,220 @@ SILC_SERVER_CMD_FUNC(ban)
 {
   SilcServerCommandContext cmd = (SilcServerCommandContext)context;
   SilcServer server = cmd->server;
-  SilcClientEntry client = (SilcClientEntry)cmd->sock->user_data;
-  SilcBuffer packet;
+  SilcClientEntry client = silc_packet_get_context(cmd->sock);
+  SilcBuffer list, tmp2;
   SilcChannelEntry channel;
-  SilcChannelClientEntry chl;
-  SilcChannelID *channel_id = NULL;
-  unsigned char *id, *add, *del;
-  uint32 id_len, tmp_len;
-  uint16 ident = silc_command_get_ident(cmd->payload);
-
-  if (cmd->sock->type != SILC_SOCKET_TYPE_CLIENT)
-    goto out;
-
-  SILC_SERVER_COMMAND_CHECK(SILC_COMMAND_BAN, cmd, 0, 3);
-
-  /* Get Channel ID */
-  id = silc_argument_get_arg_type(cmd->args, 1, &id_len);
-  if (id) {
-    channel_id = silc_id_payload_parse_id(id, id_len);
-    if (!channel_id) {
-      silc_server_command_send_status_reply(cmd, SILC_COMMAND_BAN,
-                                           SILC_STATUS_ERR_NO_CHANNEL_ID);
-      goto out;
-    }
-  }
-
-  /* Get channel entry. The server must know about the channel since the
-     client is expected to be on the channel. */
-  channel = silc_idlist_find_channel_by_id(server->local_list, 
-                                          channel_id, NULL);
-  if (!channel) {
-    channel = silc_idlist_find_channel_by_id(server->global_list, 
-                                            channel_id, NULL);
-    if (!channel) {
-      silc_server_command_send_status_reply(cmd, SILC_COMMAND_BAN,
-                                           SILC_STATUS_ERR_NO_SUCH_CHANNEL);
-      goto out;
-    }
-  }
-
-  /* Check whether this client is on the channel */
-  if (!silc_server_client_on_channel(client, channel)) {
-    silc_server_command_send_status_reply(cmd, SILC_COMMAND_BAN,
-                                         SILC_STATUS_ERR_NOT_ON_CHANNEL);
-    goto out;
-  }
-
-  /* Get entry to the channel user list */
-  if (!silc_hash_table_find(channel->user_list, client, NULL, (void *)&chl)) {
-    silc_server_command_send_status_reply(cmd, SILC_COMMAND_BAN,
-                                         SILC_STATUS_ERR_NOT_ON_CHANNEL);
-    goto out;
-  }
-
-  /* The client must be at least channel operator. */
-  if (!(chl->mode & SILC_CHANNEL_UMODE_CHANOP)) {
-    silc_server_command_send_status_reply(cmd, SILC_COMMAND_BAN,
-                                         SILC_STATUS_ERR_NO_CHANNEL_PRIV);
-    goto out;
-  }
-
-  /* Get the new ban and add it to the ban list */
-  add = silc_argument_get_arg_type(cmd->args, 2, &tmp_len);
-  if (add) {
-    if (!channel->ban_list)
-      channel->ban_list = silc_calloc(tmp_len + 2, sizeof(*channel->ban_list));
-    else
-      channel->ban_list = silc_realloc(channel->ban_list, 
-                                      sizeof(*channel->ban_list) * 
-                                      (tmp_len + 
-                                       strlen(channel->ban_list) + 2));
-    if (add[tmp_len - 1] == ',')
-      add[tmp_len - 1] = '\0';
-
-    strncat(channel->ban_list, add, tmp_len);
-    strncat(channel->ban_list, ",", 1);
-  }
-
-  /* Get the ban to be removed and remove it from the list */
-  del = silc_argument_get_arg_type(cmd->args, 3, &tmp_len);
-  if (del && channel->ban_list) {
-    char *start, *end, *n;
-
-    if (!strncmp(channel->ban_list, del, strlen(channel->ban_list) - 1)) {
-      silc_free(channel->ban_list);
-      channel->ban_list = NULL;
-    } else {
-      start = strstr(channel->ban_list, del);
-      if (start && strlen(start) >= tmp_len) {
-       end = start + tmp_len;
-       n = silc_calloc(strlen(channel->ban_list) - tmp_len, sizeof(*n));
-       strncat(n, channel->ban_list, start - channel->ban_list);
-       strncat(n, end + 1, ((channel->ban_list + strlen(channel->ban_list)) - 
-                            end) - 1);
-       silc_free(channel->ban_list);
-       channel->ban_list = n;
-      }
-    }
-  }
-
-  /* Send the BAN notify type to our primary router. */
-  if (!server->standalone && (add || del))
-    silc_server_send_notify_ban(server, server->router->connection,
-                               server->server_type == SILC_ROUTER ?
-                               TRUE : FALSE, channel, add, del);
-
-  /* Send the reply back to the client */
-  packet = 
-    silc_command_reply_payload_encode_va(SILC_COMMAND_BAN,
-                                        SILC_STATUS_OK, ident, 2,
-                                        2, id, id_len,
-                                        3, channel->ban_list, 
-                                        channel->ban_list ? 
-                                        strlen(channel->ban_list) -1 : 0);
-  silc_server_packet_send(server, cmd->sock, SILC_PACKET_COMMAND_REPLY, 0, 
-                         packet->data, packet->len, FALSE);
-    
-  silc_buffer_free(packet);
-
- out:
-  silc_free(channel_id);
-  silc_server_command_free(cmd);
-}
-
-/* Server side command of CLOSE. Closes connection to a specified server. */
-SILC_SERVER_CMD_FUNC(close)
-{
-  SilcServerCommandContext cmd = (SilcServerCommandContext)context;
-  SilcServer server = cmd->server;
-  SilcClientEntry client = (SilcClientEntry)cmd->sock->user_data;
-  SilcServerEntry server_entry;
-  SilcSocketConnection sock;
-  unsigned char *tmp;
-  uint32 tmp_len;
-  unsigned char *name;
-  uint32 port = SILC_PORT;
+  SilcChannelClientEntry chl;
+  SilcID id;
+  unsigned char *tmp_id, *tmp, *atype = NULL;
+  SilcUInt32 id_len, len, len2;
+  SilcArgumentPayload args;
+  SilcHashTableList htl;
+  void *type;
+  SilcUInt16 argc = 0, ident = silc_command_get_ident(cmd->payload);
+  SilcBufferStruct blist;
+
+  if (client->data.conn_type != SILC_CONN_CLIENT || !client)
+    goto out;
 
-  SILC_SERVER_COMMAND_CHECK(SILC_COMMAND_CLOSE, cmd, 1, 2);
+  SILC_SERVER_COMMAND_CHECK(SILC_COMMAND_BAN, cmd, 0, 3);
 
-  if (!client || cmd->sock->type != SILC_SOCKET_TYPE_CLIENT)
+  /* Get Channel ID */
+  if (!silc_argument_get_decoded(cmd->args, 1, SILC_ARGUMENT_ID, &id, NULL)) {
+    silc_server_command_send_status_reply(cmd, SILC_COMMAND_BAN,
+                                         SILC_STATUS_ERR_NO_CHANNEL_ID, 0);
     goto out;
+  }
 
-  /* Check whether client has the permissions. */
-  if (client->mode == SILC_UMODE_NONE) {
-    silc_server_command_send_status_reply(cmd, SILC_COMMAND_CLOSE,
-                                         SILC_STATUS_ERR_NO_SERVER_PRIV);
-    goto out;
+  /* Get channel entry. The server must know about the channel since the
+     client is expected to be on the channel. */
+  channel = silc_idlist_find_channel_by_id(server->local_list,
+                                          SILC_ID_GET_ID(id), NULL);
+  if (!channel) {
+    channel = silc_idlist_find_channel_by_id(server->global_list,
+                                            SILC_ID_GET_ID(id), NULL);
+    if (!channel) {
+      tmp_id = silc_argument_get_arg_type(cmd->args, 1, &id_len);
+      silc_server_command_send_status_data(
+                                          cmd, SILC_COMMAND_BAN,
+                                          SILC_STATUS_ERR_NO_SUCH_CHANNEL_ID,
+                                          0, 2, tmp_id, id_len);
+      goto out;
+    }
   }
 
-  /* Get the remote server */
-  name = silc_argument_get_arg_type(cmd->args, 1, &tmp_len);
-  if (!name) {
-    silc_server_command_send_status_reply(cmd, SILC_COMMAND_CLOSE,
-                                         SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
+  /* Check whether this client is on the channel */
+  if (!silc_server_client_on_channel(client, channel, &chl)) {
+    tmp_id = silc_argument_get_arg_type(cmd->args, 1, &id_len);
+    silc_server_command_send_status_data(cmd, SILC_COMMAND_BAN,
+                                        SILC_STATUS_ERR_NOT_ON_CHANNEL, 0,
+                                        2, tmp_id, id_len);
     goto out;
   }
 
-  /* Get port */
-  tmp = silc_argument_get_arg_type(cmd->args, 2, &tmp_len);
-  if (tmp)
-    SILC_GET32_MSB(port, tmp);
-
-  server_entry = silc_idlist_find_server_by_conn(server->local_list,
-                                                name, port, FALSE, NULL);
-  if (!server_entry)
-    server_entry = silc_idlist_find_server_by_conn(server->global_list,
-                                                  name, port, FALSE, NULL);
-  if (!server_entry) {
-    silc_server_command_send_status_reply(cmd, SILC_COMMAND_CLOSE,
-                                         SILC_STATUS_ERR_NO_SERVER_ID);
+  /* The client must be at least channel operator. */
+  if (!(chl->mode & SILC_CHANNEL_UMODE_CHANOP)) {
+    tmp_id = silc_argument_get_arg_type(cmd->args, 1, &id_len);
+    silc_server_command_send_status_data(cmd, SILC_COMMAND_BAN,
+                                        SILC_STATUS_ERR_NO_CHANNEL_PRIV, 0,
+                                        2, tmp_id, id_len);
     goto out;
   }
 
-  /* Send reply to the sender */
-  silc_server_command_send_status_reply(cmd, SILC_COMMAND_CLOSE,
-                                       SILC_STATUS_OK);
+  /* Get the ban information */
+  tmp = silc_argument_get_arg_type(cmd->args, 3, &len2);
+  if (tmp && len2 > 2) {
+    /* Parse the arguments to see they are constructed correctly */
+    SILC_GET16_MSB(argc, tmp);
+    args = silc_argument_payload_parse(tmp + 2, len2 - 2, argc);
+    if (!args) {
+      silc_server_command_send_status_reply(cmd, SILC_COMMAND_BAN,
+                                           SILC_STATUS_ERR_NOT_ENOUGH_PARAMS,
+                                           0);
+      goto out;
+    }
 
-  /* Close the connection to the server */
-  sock = (SilcSocketConnection)server_entry->connection;
+    /* Get the type of action */
+    atype = silc_argument_get_arg_type(cmd->args, 2, &len);
+    if (atype && len == 1) {
+      if (atype[0] == 0x00) {
+       /* Allocate hash table for ban list if it doesn't exist yet */
+       if (!channel->ban_list)
+         channel->ban_list =
+           silc_hash_table_alloc(0, silc_hash_ptr,
+                                 NULL, NULL, NULL,
+                                 silc_server_inviteban_destruct, channel,
+                                 TRUE);
+
+       /* Check for resource limit */
+       if (silc_hash_table_count(channel->ban_list) > 64) {
+         silc_server_command_send_status_reply(cmd, SILC_COMMAND_BAN,
+                                               SILC_STATUS_ERR_RESOURCE_LIMIT,
+                                               0);
+         goto out;
+       }
+      }
 
-  /* If we shutdown primary router connection manually then don't trigger
-     any reconnect or backup router connections, by setting the router
-     to NULL here. */
-  if (server->router == server_entry) {
-    server->id_entry->router = NULL;
-    server->router = NULL;
-    server->standalone = TRUE;
+      /* Now add or delete the information. */
+      if (!silc_server_inviteban_process(server, channel->ban_list,
+                                        (SilcUInt8)atype[0], args)) {
+       silc_server_command_send_status_reply(
+                                     cmd, SILC_COMMAND_BAN,
+                                     SILC_STATUS_ERR_NOT_ENOUGH_PARAMS,
+                                     0);
+       goto out;
+      }
+    }
+    silc_argument_payload_free(args);
   }
-  silc_server_free_sock_user_data(server, sock, NULL);
-  silc_server_close_connection(server, sock);
-  
- out:
-  silc_server_command_free(cmd);
-}
 
-/* Server side command of SHUTDOWN. Shutdowns the server and closes all
-   active connections. */
-SILC_SERVER_CMD_FUNC(shutdown)
-{
-  SilcServerCommandContext cmd = (SilcServerCommandContext)context;
-  SilcServer server = cmd->server;
-  SilcClientEntry client = (SilcClientEntry)cmd->sock->user_data;
+  /* Encode ban list */
+  list = NULL;
+  if (channel->ban_list && silc_hash_table_count(channel->ban_list)) {
+    list = silc_buffer_alloc_size(2);
+    silc_buffer_format(list,
+                      SILC_STR_UI_SHORT(silc_hash_table_count(
+                                         channel->ban_list)),
+                      SILC_STR_END);
+    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),
+                                             SILC_PTR_TO_32(type));
+    silc_hash_table_list_reset(&htl);
+  }
 
-  SILC_SERVER_COMMAND_CHECK(SILC_COMMAND_SHUTDOWN, cmd, 0, 0);
+  tmp_id = silc_argument_get_arg_type(cmd->args, 1, &id_len);
 
-  if (!client || cmd->sock->type != SILC_SOCKET_TYPE_CLIENT)
-    goto out;
+  /* Send BAN notify type to local servers (but not clients) and to
+     network. */
+  if (atype && tmp && len2) {
+    silc_buffer_set(&blist, tmp, len2);
 
-  /* Check whether client has the permission. */
-  if (client->mode == SILC_UMODE_NONE) {
-    silc_server_command_send_status_reply(cmd, SILC_COMMAND_SHUTDOWN,
-                                         SILC_STATUS_ERR_NO_SERVER_PRIV);
-    goto out;
-  }
+    /* Send to local servers if we are router */
+    if (server->server_type == SILC_ROUTER)
+      silc_server_send_notify_to_channel(server, NULL, channel, FALSE, FALSE,
+                                         SILC_NOTIFY_TYPE_BAN, 3,
+                                        tmp_id, id_len,
+                                        atype, 1,
+                                        tmp ? blist.data : NULL,
+                                        tmp ? silc_buffer_len(&blist) : 0);
 
-  /* Send reply to the sender */
-  silc_server_command_send_status_reply(cmd, SILC_COMMAND_SHUTDOWN,
-                                       SILC_STATUS_OK);
+    /* Send to network. */
+    silc_server_send_notify_ban(server, SILC_PRIMARY_ROUTE(server),
+                               SILC_BROADCAST(server), channel, atype,
+                               &blist);
+  }
 
-  /* Then, gracefully, or not, bring the server down. */
-  silc_server_stop(server);
-  exit(0);
+  /* Send the reply back to the client */
+  silc_server_send_command_reply(server, cmd->sock, SILC_COMMAND_BAN,
+                                SILC_STATUS_OK, 0, ident, 2,
+                                2, tmp_id, id_len,
+                                3, list ? list->data : NULL,
+                                list ? silc_buffer_len(list) : 0);
+  silc_buffer_free(list);
 
  out:
   silc_server_command_free(cmd);
 }
+
 /* Server side command of LEAVE. Removes client from a channel. */
 
 SILC_SERVER_CMD_FUNC(leave)
 {
   SilcServerCommandContext cmd = (SilcServerCommandContext)context;
   SilcServer server = cmd->server;
-  SilcSocketConnection sock = cmd->sock;
-  SilcClientEntry id_entry = (SilcClientEntry)cmd->sock->user_data;
-  SilcChannelID *id = NULL;
+  SilcPacketStream sock = cmd->sock;
+  SilcClientEntry id_entry = silc_packet_get_context(cmd->sock);
+  SilcID id;
   SilcChannelEntry channel;
-  uint32 len;
+  SilcUInt32 len;
   unsigned char *tmp;
 
+  if (id_entry->data.conn_type != SILC_CONN_CLIENT || !id_entry)
+    goto out;
+
   SILC_SERVER_COMMAND_CHECK(SILC_COMMAND_LEAVE, cmd, 1, 2);
 
   /* Get Channel ID */
-  tmp = silc_argument_get_arg_type(cmd->args, 1, &len);
-  if (!tmp) {
+  if (!silc_argument_get_decoded(cmd->args, 1, SILC_ARGUMENT_ID, &id, NULL)) {
     silc_server_command_send_status_reply(cmd, SILC_COMMAND_LEAVE,
-                                         SILC_STATUS_ERR_NO_CHANNEL_ID);
-    goto out;
-  }
-  id = silc_id_payload_parse_id(tmp, len);
-  if (!id) {
-    silc_server_command_send_status_reply(cmd, SILC_COMMAND_LEAVE,
-                                         SILC_STATUS_ERR_NO_CHANNEL_ID);
+                                         SILC_STATUS_ERR_NO_CHANNEL_ID, 0);
     goto out;
   }
 
   /* Get channel entry */
-  channel = silc_idlist_find_channel_by_id(server->local_list, id, NULL);
+  channel = silc_idlist_find_channel_by_id(server->local_list,
+                                          SILC_ID_GET_ID(id), NULL);
   if (!channel) {
-    channel = silc_idlist_find_channel_by_id(server->global_list, id, NULL);
+    channel = silc_idlist_find_channel_by_id(server->global_list,
+                                            SILC_ID_GET_ID(id), NULL);
     if (!channel) {
-      silc_server_command_send_status_reply(cmd, SILC_COMMAND_LEAVE,
-                                           SILC_STATUS_ERR_NO_SUCH_CHANNEL);
+      tmp = silc_argument_get_arg_type(cmd->args, 1, &len);
+      silc_server_command_send_status_data(cmd, SILC_COMMAND_LEAVE,
+                                          SILC_STATUS_ERR_NO_SUCH_CHANNEL_ID,
+                                          0, 2, tmp, len);
       goto out;
     }
   }
 
   /* Check whether this client is on the channel */
-  if (!silc_server_client_on_channel(id_entry, channel)) {
-    silc_server_command_send_status_reply(cmd, SILC_COMMAND_LEAVE,
-                                         SILC_STATUS_ERR_NOT_ON_CHANNEL);
+  if (!silc_server_client_on_channel(id_entry, channel, NULL)) {
+    tmp = silc_argument_get_arg_type(cmd->args, 1, &len);
+    silc_server_command_send_status_data(cmd, SILC_COMMAND_LEAVE,
+                                        SILC_STATUS_ERR_NOT_ON_CHANNEL, 0,
+                                        2, tmp, len);
     goto out;
   }
 
   /* Notify routers that they should remove this client from their list
      of clients on the channel. Send LEAVE notify type. */
-  if (!server->standalone)
-    silc_server_send_notify_leave(server, server->router->connection,
-                                 server->server_type == SILC_ROUTER ?
-                                 TRUE : FALSE, channel, id_entry->id);
+  silc_server_send_notify_leave(server, SILC_PRIMARY_ROUTE(server),
+                               SILC_BROADCAST(server), channel, id_entry->id);
 
-  silc_server_command_send_status_reply(cmd, SILC_COMMAND_LEAVE,
-                                       SILC_STATUS_OK);
+  tmp = silc_argument_get_arg_type(cmd->args, 1, &len);
+  silc_server_command_send_status_data(cmd, SILC_COMMAND_LEAVE,
+                                      SILC_STATUS_OK, 0, 2, tmp, len);
 
   /* Remove client from channel */
   if (!silc_server_remove_from_one_channel(server, sock, channel, id_entry,
@@ -5113,13 +4829,12 @@ SILC_SERVER_CMD_FUNC(leave)
       goto out;
 
     /* Send the channel key */
-    silc_server_send_channel_key(server, NULL, channel, 
-                                server->server_type == SILC_ROUTER ? 
+    silc_server_send_channel_key(server, NULL, channel,
+                                server->server_type == SILC_ROUTER ?
                                 FALSE : !server->standalone);
   }
 
  out:
-  silc_free(id);
   silc_server_command_free(cmd);
 }
 
@@ -5131,17 +4846,18 @@ SILC_SERVER_CMD_FUNC(users)
 {
   SilcServerCommandContext cmd = (SilcServerCommandContext)context;
   SilcServer server = cmd->server;
+  SilcIDListData idata = silc_packet_get_context(cmd->sock);
   SilcChannelEntry channel;
-  SilcChannelID *id = NULL;
-  SilcBuffer packet, idp;
+  SilcID id;
+  SilcBuffer idp;
   unsigned char *channel_id;
-  uint32 channel_id_len;
+  SilcUInt32 channel_id_len;
   SilcBuffer client_id_list;
   SilcBuffer client_mode_list;
   unsigned char lc[4];
-  uint32 list_count = 0;
-  uint16 ident = silc_command_get_ident(cmd->payload);
-  char *channel_name;
+  SilcUInt32 list_count = 0;
+  SilcUInt16 ident = silc_command_get_ident(cmd->payload);
+  char *channel_name, *channel_namec = NULL;
 
   SILC_SERVER_COMMAND_CHECK(SILC_COMMAND_USERS, cmd, 1, 2);
 
@@ -5153,15 +4869,27 @@ SILC_SERVER_CMD_FUNC(users)
 
   if (!channel_id && !channel_name) {
     silc_server_command_send_status_reply(cmd, SILC_COMMAND_USERS,
-                                         SILC_STATUS_ERR_NO_CHANNEL_ID);
+                                         SILC_STATUS_ERR_NO_CHANNEL_ID, 0);
     goto out;
   }
 
-  if (channel_id) {
-    id = silc_id_payload_parse_id(channel_id, channel_id_len);
-    if (!id) {
+  /* Check channel name */
+  if (channel_name) {
+    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_USERS,
-                                           SILC_STATUS_ERR_NO_CHANNEL_ID);
+                                           SILC_STATUS_ERR_BAD_CHANNEL, 0);
+      goto out;
+    }
+  }
+
+  /* Check Channel ID */
+  if (channel_id) {
+    if (!silc_id_payload_parse_id(channel_id, channel_id_len, &id)) {
+      silc_server_command_send_status_data(cmd, SILC_COMMAND_USERS,
+                                          SILC_STATUS_ERR_BAD_CHANNEL_ID, 0,
+                                          2, channel_id, channel_id_len);
       goto out;
     }
   }
@@ -5169,96 +4897,111 @@ SILC_SERVER_CMD_FUNC(users)
   /* If we are server and we don't know about this channel we will send
      the command to our router. If we know about the channel then we also
      have the list of users already. */
-  if (id)
-    channel = silc_idlist_find_channel_by_id(server->local_list, id, NULL);
+  if (channel_id)
+    channel = silc_idlist_find_channel_by_id(server->local_list,
+                                            SILC_ID_GET_ID(id), NULL);
   else
-    channel = silc_idlist_find_channel_by_name(server->local_list, 
-                                              channel_name, NULL);
+    channel = silc_idlist_find_channel_by_name(server->local_list,
+                                              channel_namec, NULL);
 
-  if (!channel || channel->disabled) {
+  if (!channel || (!server->standalone && (channel->disabled ||
+                   !channel->users_resolved))) {
     if (server->server_type != SILC_ROUTER && !server->standalone &&
        !cmd->pending) {
       SilcBuffer tmpbuf;
-      
+
+      /* Statistics */
+      cmd->server->stat.commands_sent++;
+
       silc_command_set_ident(cmd->payload, ++server->cmd_ident);
       tmpbuf = silc_command_payload_encode_payload(cmd->payload);
-      
+
       /* Send USERS command */
-      silc_server_packet_send(server, server->router->connection,
+      silc_server_packet_send(server, SILC_PRIMARY_ROUTE(server),
                              SILC_PACKET_COMMAND, cmd->packet->flags,
-                             tmpbuf->data, tmpbuf->len, TRUE);
-      
+                             tmpbuf->data, silc_buffer_len(tmpbuf));
+
       /* Reprocess this packet after received reply */
-      silc_server_command_pending(server, SILC_COMMAND_USERS, 
+      silc_server_command_pending(server, SILC_COMMAND_USERS,
                                  silc_command_get_ident(cmd->payload),
                                  silc_server_command_users,
                                  silc_server_command_dup(cmd));
       cmd->pending = TRUE;
       silc_command_set_ident(cmd->payload, ident);
       silc_buffer_free(tmpbuf);
-      silc_free(id);
       goto out;
     }
 
     /* Check the global list as well. */
-    if (id)
-      channel = silc_idlist_find_channel_by_id(server->global_list, id, NULL);
+    if (channel_id)
+      channel = silc_idlist_find_channel_by_id(server->global_list,
+                                              SILC_ID_GET_ID(id), NULL);
     else
-      channel = silc_idlist_find_channel_by_name(server->global_list, 
-                                                channel_name, NULL);
+      channel = silc_idlist_find_channel_by_name(server->global_list,
+                                                channel_namec, NULL);
     if (!channel) {
       /* Channel really does not exist */
-      silc_server_command_send_status_reply(cmd, SILC_COMMAND_USERS,
-                                           SILC_STATUS_ERR_NO_SUCH_CHANNEL);
+      if (channel_id)
+       silc_server_command_send_status_data(
+                                   cmd, SILC_COMMAND_USERS,
+                                   SILC_STATUS_ERR_NO_SUCH_CHANNEL_ID, 0,
+                                   2, channel_id, channel_id_len);
+      else
+       silc_server_command_send_status_data(
+                                   cmd, SILC_COMMAND_USERS,
+                                   SILC_STATUS_ERR_NO_SUCH_CHANNEL, 0,
+                                   2, channel_name, strlen(channel_name));
       goto out;
     }
   }
 
   /* If the channel is private or secret do not send anything, unless the
-     user requesting this command is on the channel. */
-  if (cmd->sock->type == SILC_SOCKET_TYPE_CLIENT) {
+     user requesting this command is on the channel or is server */
+  if (idata->conn_type == SILC_CONN_CLIENT) {
     if (channel->mode & (SILC_CHANNEL_MODE_PRIVATE | SILC_CHANNEL_MODE_SECRET)
-       && !silc_server_client_on_channel(cmd->sock->user_data, channel)) {
-      silc_server_command_send_status_reply(cmd, SILC_COMMAND_USERS,
-                                           SILC_STATUS_ERR_NO_SUCH_CHANNEL);
-      goto out;
-    }
-  } else {
-    if (channel->mode & 
-       (SILC_CHANNEL_MODE_PRIVATE | SILC_CHANNEL_MODE_SECRET)) {
-      silc_server_command_send_status_reply(cmd, SILC_COMMAND_USERS,
-                                           SILC_STATUS_ERR_NO_SUCH_CHANNEL);
+       && !silc_server_client_on_channel((SilcClientEntry)idata, channel,
+                                         NULL)) {
+      silc_server_command_send_status_data(cmd, SILC_COMMAND_USERS,
+                                          SILC_STATUS_ERR_NO_SUCH_CHANNEL, 0,
+                                          2, channel->channel_name,
+                                          strlen(channel->channel_name));
       goto out;
     }
   }
 
   /* Get the users list */
-  silc_server_get_users_on_channel(server, channel, &client_id_list,
-                                  &client_mode_list, &list_count);
+  if (!silc_server_get_users_on_channel(server, channel, &client_id_list,
+                                       &client_mode_list, &list_count)) {
+    list_count = 0;
+    client_id_list = NULL;
+    client_mode_list = NULL;
+  }
 
   /* List count */
   SILC_PUT32_MSB(list_count, lc);
 
   /* Send reply */
   idp = silc_id_payload_encode(channel->id, SILC_ID_CHANNEL);
-  packet = silc_command_reply_payload_encode_va(SILC_COMMAND_USERS,
-                                               SILC_STATUS_OK, ident, 4,
-                                               2, idp->data, idp->len,
-                                               3, lc, 4,
-                                               4, client_id_list->data,
-                                               client_id_list->len,
-                                               5, client_mode_list->data,
-                                               client_mode_list->len);
-  silc_server_packet_send(server, cmd->sock, SILC_PACKET_COMMAND_REPLY, 0, 
-                         packet->data, packet->len, FALSE);
-    
+  silc_server_send_command_reply(server, cmd->sock, SILC_COMMAND_USERS,
+                                SILC_STATUS_OK, 0, ident, 4,
+                                2, idp->data, silc_buffer_len(idp),
+                                3, lc, 4,
+                                4, client_id_list ?
+                                client_id_list->data : NULL,
+                                client_id_list ?
+                                silc_buffer_len(client_id_list) : 0,
+                                5, client_mode_list ?
+                                client_mode_list->data : NULL,
+                                client_mode_list ?
+                                silc_buffer_len(client_mode_list) : 0);
   silc_buffer_free(idp);
-  silc_buffer_free(packet);
-  silc_buffer_free(client_id_list);
-  silc_buffer_free(client_mode_list);
-  silc_free(id);
+  if (client_id_list)
+    silc_buffer_free(client_id_list);
+  if (client_mode_list)
+    silc_buffer_free(client_mode_list);
 
  out:
+  silc_free(channel_namec);
   silc_server_command_free(cmd);
 }
 
@@ -5269,68 +5012,71 @@ SILC_SERVER_CMD_FUNC(getkey)
 {
   SilcServerCommandContext cmd = (SilcServerCommandContext)context;
   SilcServer server = cmd->server;
-  SilcBuffer packet;
   SilcClientEntry client;
   SilcServerEntry server_entry;
-  SilcClientID *client_id = NULL;
-  SilcServerID *server_id = NULL;
+  SilcClientID client_id;
+  SilcServerID server_id;
   SilcIDPayload idp = NULL;
-  uint16 ident = silc_command_get_ident(cmd->payload);
-  unsigned char *tmp, *pkdata;
-  uint32 tmp_len, pklen;
+  SilcUInt16 ident = silc_command_get_ident(cmd->payload);
+  unsigned char *tmp;
+  SilcUInt32 tmp_len;
   SilcBuffer pk = NULL;
   SilcIdType id_type;
   SilcPublicKey public_key;
 
-  SILC_LOG_DEBUG(("Start"));
-
   tmp = silc_argument_get_arg_type(cmd->args, 1, &tmp_len);
   if (!tmp) {
     silc_server_command_send_status_reply(cmd, SILC_COMMAND_GETKEY,
-                                         SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
+                                         SILC_STATUS_ERR_NOT_ENOUGH_PARAMS,
+                                         0);
     goto out;
   }
   idp = silc_id_payload_parse(tmp, tmp_len);
   if (!idp) {
     silc_server_command_send_status_reply(cmd, SILC_COMMAND_GETKEY,
-                                         SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
+                                         SILC_STATUS_ERR_NOT_ENOUGH_PARAMS,
+                                         0);
     goto out;
   }
 
   id_type = silc_id_payload_get_type(idp);
   if (id_type == SILC_ID_CLIENT) {
-    client_id = silc_id_payload_get_id(idp);
+    silc_id_payload_get_id(idp, &client_id, sizeof(client_id));
 
     /* If the client is not found from local list there is no chance it
        would be locally connected client so send the command further. */
-    client = silc_idlist_find_client_by_id(server->local_list, 
-                                          client_id, TRUE, NULL);
+    client = silc_idlist_find_client_by_id(server->local_list,
+                                          &client_id, TRUE, NULL);
     if (!client)
-      client = silc_idlist_find_client_by_id(server->global_list, 
-                                            client_id, TRUE, NULL);
-    
+      client = silc_idlist_find_client_by_id(server->global_list,
+                                            &client_id, TRUE, NULL);
+
     if ((!client && !cmd->pending && !server->standalone) ||
-       (client && !client->connection && !cmd->pending) ||
+       (client && !client->connection && !cmd->pending &&
+        !(client->mode & SILC_UMODE_DETACHED)) ||
        (client && !client->data.public_key && !cmd->pending)) {
       SilcBuffer tmpbuf;
-      uint16 old_ident;
-      SilcSocketConnection dest_sock;
-      
-      dest_sock = silc_server_get_client_route(server, NULL, 0, 
-                                              client_id, NULL);
+      SilcUInt16 old_ident;
+      SilcPacketStream dest_sock;
+
+      dest_sock = silc_server_get_client_route(server, NULL, 0,
+                                              &client_id, NULL, NULL);
       if (!dest_sock)
        goto out;
-      
+
+      /* Statistics */
+      cmd->server->stat.commands_sent++;
+
       old_ident = silc_command_get_ident(cmd->payload);
       silc_command_set_ident(cmd->payload, ++server->cmd_ident);
       tmpbuf = silc_command_payload_encode_payload(cmd->payload);
-      
+
       silc_server_packet_send(server, dest_sock,
                              SILC_PACKET_COMMAND, cmd->packet->flags,
-                             tmpbuf->data, tmpbuf->len, TRUE);
-      
+                             tmpbuf->data, silc_buffer_len(tmpbuf));
+
       /* Reprocess this packet after received reply from router */
-      silc_server_command_pending(server, SILC_COMMAND_GETKEY, 
+      silc_server_command_pending(server, SILC_COMMAND_GETKEY,
                                  silc_command_get_ident(cmd->payload),
                                  silc_server_command_getkey,
                                  silc_server_command_dup(cmd));
@@ -5341,42 +5087,29 @@ SILC_SERVER_CMD_FUNC(getkey)
     }
 
     if (!client) {
-      silc_server_command_send_status_reply(cmd, SILC_COMMAND_GETKEY,
-                                           SILC_STATUS_ERR_NO_SUCH_CLIENT_ID);
+      silc_server_command_send_status_data(cmd, SILC_COMMAND_GETKEY,
+                                          SILC_STATUS_ERR_NO_SUCH_CLIENT_ID,
+                                          0, 2, tmp, tmp_len);
       goto out;
     }
 
     /* The client is locally connected, just get the public key and
-       send it back. If they key does not exist then do not send it, 
+       send it back. If they key does not exist then do not send it,
        send just OK reply */
     public_key = client->data.public_key;
-    if (!public_key) {
-      pkdata = NULL;
-      pklen = 0;
-    } else {
-      tmp = silc_pkcs_public_key_encode(public_key, &tmp_len);
-      pk = silc_buffer_alloc(4 + tmp_len);
-      silc_buffer_pull_tail(pk, SILC_BUFFER_END(pk));
-      silc_buffer_format(pk,
-                        SILC_STR_UI_SHORT(tmp_len),
-                        SILC_STR_UI_SHORT(SILC_SKE_PK_TYPE_SILC),
-                        SILC_STR_UI_XNSTRING(tmp, tmp_len),
-                        SILC_STR_END);
-      silc_free(tmp);
-      pkdata = pk->data;
-      pklen = pk->len;
-    }
+    if (public_key)
+      pk = silc_public_key_payload_encode(public_key);
   } else if (id_type == SILC_ID_SERVER) {
-    server_id = silc_id_payload_get_id(idp);
+    silc_id_payload_get_id(idp, &server_id, sizeof(server_id));
 
     /* If the server is not found from local list there is no chance it
        would be locally connected server so send the command further. */
-    server_entry = silc_idlist_find_server_by_id(server->local_list, 
-                                                server_id, TRUE, NULL);
+    server_entry = silc_idlist_find_server_by_id(server->local_list,
+                                                &server_id, TRUE, NULL);
     if (!server_entry)
-      server_entry = silc_idlist_find_server_by_id(server->global_list, 
-                                                  server_id, TRUE, NULL);
-    
+      server_entry = silc_idlist_find_server_by_id(server->global_list,
+                                                  &server_id, TRUE, NULL);
+
     if (server_entry != server->id_entry &&
        ((!server_entry && !cmd->pending && !server->standalone) ||
         (server_entry && !server_entry->connection && !cmd->pending &&
@@ -5384,18 +5117,21 @@ SILC_SERVER_CMD_FUNC(getkey)
         (server_entry && !server_entry->data.public_key && !cmd->pending &&
          !server->standalone))) {
       SilcBuffer tmpbuf;
-      uint16 old_ident;
-      
+      SilcUInt16 old_ident;
+
+      /* Statistics */
+      cmd->server->stat.commands_sent++;
+
       old_ident = silc_command_get_ident(cmd->payload);
       silc_command_set_ident(cmd->payload, ++server->cmd_ident);
       tmpbuf = silc_command_payload_encode_payload(cmd->payload);
-      
-      silc_server_packet_send(server, server->router->connection,
+
+      silc_server_packet_send(server, SILC_PRIMARY_ROUTE(server),
                              SILC_PACKET_COMMAND, cmd->packet->flags,
-                             tmpbuf->data, tmpbuf->len, TRUE);
-      
+                             tmpbuf->data, silc_buffer_len(tmpbuf));
+
       /* Reprocess this packet after received reply from router */
-      silc_server_command_pending(server, SILC_COMMAND_GETKEY, 
+      silc_server_command_pending(server, SILC_COMMAND_GETKEY,
                                  silc_command_get_ident(cmd->payload),
                                  silc_server_command_getkey,
                                  silc_server_command_dup(cmd));
@@ -5406,52 +5142,258 @@ SILC_SERVER_CMD_FUNC(getkey)
     }
 
     if (!server_entry) {
-      silc_server_command_send_status_reply(cmd, SILC_COMMAND_GETKEY,
-                                           SILC_STATUS_ERR_NO_SUCH_SERVER_ID);
+      silc_server_command_send_status_data(cmd, SILC_COMMAND_GETKEY,
+                                          SILC_STATUS_ERR_NO_SUCH_SERVER_ID,
+                                          0, 2, tmp, tmp_len);
       goto out;
     }
 
     /* If they key does not exist then do not send it, send just OK reply */
-    public_key = (!server_entry->data.public_key ? 
+    public_key = (!server_entry->data.public_key ?
                  (server_entry == server->id_entry ? server->public_key :
                   NULL) : server_entry->data.public_key);
-    if (!public_key) {
-      pkdata = NULL;
-      pklen = 0;
-    } else {
-      tmp = silc_pkcs_public_key_encode(public_key, &tmp_len);
-      pk = silc_buffer_alloc(4 + tmp_len);
-      silc_buffer_pull_tail(pk, SILC_BUFFER_END(pk));
-      silc_buffer_format(pk,
-                        SILC_STR_UI_SHORT(tmp_len),
-                        SILC_STR_UI_SHORT(SILC_SKE_PK_TYPE_SILC),
-                        SILC_STR_UI_XNSTRING(tmp, tmp_len),
-                        SILC_STR_END);
-      silc_free(tmp);
-      pkdata = pk->data;
-      pklen = pk->len;
-    }
+    if (public_key)
+      pk = silc_public_key_payload_encode(public_key);
   } else {
     goto out;
   }
 
   tmp = silc_argument_get_arg_type(cmd->args, 1, &tmp_len);
-  packet = silc_command_reply_payload_encode_va(SILC_COMMAND_GETKEY,
-                                               SILC_STATUS_OK, ident, 
-                                               pkdata ? 2 : 1,
-                                               2, tmp, tmp_len,
-                                               3, pkdata, pklen);
-  silc_server_packet_send(server, cmd->sock, SILC_PACKET_COMMAND_REPLY, 0, 
-                         packet->data, packet->len, FALSE);
-  silc_buffer_free(packet);
-
-  if (pk)
-    silc_buffer_free(pk);
+  silc_server_send_command_reply(server, cmd->sock, SILC_COMMAND_GETKEY,
+                                SILC_STATUS_OK, 0, ident, 2,
+                                2, tmp, tmp_len,
+                                3, pk ? pk->data : NULL,
+                                pk ? silc_buffer_len(pk) : 0);
 
  out:
   if (idp)
     silc_id_payload_free(idp);
-  silc_free(client_id);
-  silc_free(server_id);
+  silc_buffer_free(pk);
+  silc_server_command_free(cmd);
+}
+
+/* Server side of command SERVICE. */
+/* XXX currently this just sends empty reply back */
+
+SILC_SERVER_CMD_FUNC(service)
+{
+  SilcServerCommandContext cmd = (SilcServerCommandContext)context;
+  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);
+
+  /* Get requested service */
+  service_name = silc_argument_get_arg_type(cmd->args, 1, &tmp_len);
+  if (service_name && tmp_len) {
+    /* Verify service name */
+    if (!silc_identifier_verify(service_name, tmp_len,
+                               SILC_STRING_UTF8, 256)) {
+      silc_server_command_send_status_reply(cmd, SILC_COMMAND_SERVICE,
+                                           SILC_STATUS_ERR_NOT_ENOUGH_PARAMS,
+                                           0);
+      goto out;
+    }
+  }
+
+  /* Get authentication payload if present */
+  auth = silc_argument_get_arg_type(cmd->args, 2, &auth_len);
+  if (auth) {
+    /* XXX */
+  }
+
+
+  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);
+
+ out:
+  silc_server_command_free(cmd);
+}
+
+
+/* Private range commands, specific to this implementation */
+
+/* Server side command of CONNECT. Connects us to the specified remote
+   server or router. */
+
+SILC_SERVER_CMD_FUNC(connect)
+{
+  SilcServerCommandContext cmd = (SilcServerCommandContext)context;
+  SilcServer server = cmd->server;
+  SilcClientEntry client = silc_packet_get_context(cmd->sock);
+  unsigned char *tmp, *host;
+  SilcUInt32 tmp_len;
+  SilcUInt32 port = SILC_PORT;
+
+  if (client->data.conn_type != SILC_CONN_CLIENT || !client)
+    goto out;
+
+  SILC_SERVER_COMMAND_CHECK(SILC_COMMAND_PRIV_CONNECT, cmd, 1, 2);
+
+  /* Check whether client has the permissions. */
+  if (!(client->mode & SILC_UMODE_SERVER_OPERATOR) &&
+      !(client->mode & SILC_UMODE_ROUTER_OPERATOR)) {
+    silc_server_command_send_status_reply(cmd, SILC_COMMAND_PRIV_CONNECT,
+                                         SILC_STATUS_ERR_NO_SERVER_PRIV, 0);
+    goto out;
+  }
+
+  if (server->server_type == SILC_ROUTER && !server->backup_router &&
+      client->mode & SILC_UMODE_SERVER_OPERATOR) {
+    silc_server_command_send_status_reply(cmd, SILC_COMMAND_PRIV_CONNECT,
+                                         SILC_STATUS_ERR_NO_ROUTER_PRIV, 0);
+    goto out;
+  }
+
+  /* Get the remote server */
+  host = silc_argument_get_arg_type(cmd->args, 1, &tmp_len);
+  if (!host) {
+    silc_server_command_send_status_reply(cmd, SILC_COMMAND_PRIV_CONNECT,
+                                         SILC_STATUS_ERR_NOT_ENOUGH_PARAMS,
+                                         0);
+    goto out;
+  }
+
+  /* Get port */
+  tmp = silc_argument_get_arg_type(cmd->args, 2, &tmp_len);
+  if (tmp)
+    SILC_GET32_MSB(port, tmp);
+
+  /* Create the connection. It is done with timeout and is async. */
+  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,
+                                       SILC_STATUS_OK, 0);
+
+ out:
+  silc_server_command_free(cmd);
+}
+
+/* Server side command of CLOSE. Closes connection to a specified server. */
+
+SILC_SERVER_CMD_FUNC(close)
+{
+  SilcServerCommandContext cmd = (SilcServerCommandContext)context;
+  SilcServer server = cmd->server;
+  SilcClientEntry client = silc_packet_get_context(cmd->sock);
+  SilcServerEntry server_entry;
+  SilcPacketStream sock;
+  unsigned char *tmp;
+  SilcUInt32 tmp_len;
+  unsigned char *name;
+  SilcUInt32 port = SILC_PORT;
+
+  if (client->data.conn_type != SILC_CONN_CLIENT || !client)
+    goto out;
+
+  SILC_SERVER_COMMAND_CHECK(SILC_COMMAND_PRIV_CLOSE, cmd, 1, 2);
+
+  /* Check whether client has the permissions. */
+  if (!(client->mode & SILC_UMODE_SERVER_OPERATOR) &&
+      !(client->mode & SILC_UMODE_ROUTER_OPERATOR)) {
+    silc_server_command_send_status_reply(cmd, SILC_COMMAND_PRIV_CLOSE,
+                                         SILC_STATUS_ERR_NO_SERVER_PRIV,
+                                         0);
+    goto out;
+  }
+
+  /* Get the remote server */
+  name = silc_argument_get_arg_type(cmd->args, 1, &tmp_len);
+  if (!name) {
+    silc_server_command_send_status_reply(cmd, SILC_COMMAND_PRIV_CLOSE,
+                                         SILC_STATUS_ERR_NOT_ENOUGH_PARAMS,
+                                         0);
+    goto out;
+  }
+
+  /* Get port */
+  tmp = silc_argument_get_arg_type(cmd->args, 2, &tmp_len);
+  if (tmp)
+    SILC_GET32_MSB(port, tmp);
+
+  server_entry = silc_idlist_find_server_by_conn(server->local_list,
+                                                name, port, TRUE, NULL);
+  if (!server_entry)
+    server_entry = silc_idlist_find_server_by_conn(server->global_list,
+                                                  name, port, TRUE, NULL);
+  if (!server_entry) {
+    silc_server_command_send_status_reply(cmd, SILC_COMMAND_PRIV_CLOSE,
+                                         SILC_STATUS_ERR_NO_SERVER_ID, 0);
+    goto out;
+  }
+
+  if (server_entry == server->id_entry) {
+    silc_server_command_send_status_reply(cmd, SILC_COMMAND_PRIV_CLOSE,
+                                         SILC_STATUS_ERR_NO_SERVER_ID, 0);
+    goto out;
+  }
+
+  /* Send reply to the sender */
+  silc_server_command_send_status_reply(cmd, SILC_COMMAND_PRIV_CLOSE,
+                                       SILC_STATUS_OK, 0);
+
+  /* Close the connection to the server */
+  sock = server_entry->connection;
+
+  if (server_entry->server_type == SILC_BACKUP_ROUTER) {
+    server->backup_closed = TRUE;
+    silc_server_backup_del(server, server_entry);
+  }
+
+  server->backup_noswitch = TRUE;
+  if (server->router == server_entry) {
+    server->id_entry->router = NULL;
+    server->router = NULL;
+    server->standalone = TRUE;
+  }
+  silc_server_disconnect_remote(server, sock,
+                               SILC_STATUS_ERR_BANNED_FROM_SERVER,
+                               "Closed by administrator");
+  silc_server_free_sock_user_data(server, sock, NULL);
+  server->backup_noswitch = FALSE;
+
+ out:
+  silc_server_command_free(cmd);
+}
+
+/* Server side command of SHUTDOWN. Shutdowns the server and closes all
+   active connections. */
+
+SILC_SERVER_CMD_FUNC(shutdown)
+{
+  SilcServerCommandContext cmd = (SilcServerCommandContext)context;
+  SilcServer server = cmd->server;
+  SilcClientEntry client = silc_packet_get_context(cmd->sock);
+
+  if (client->data.conn_type != SILC_CONN_CLIENT || !client)
+    goto out;
+
+  SILC_SERVER_COMMAND_CHECK(SILC_COMMAND_PRIV_SHUTDOWN, cmd, 0, 0);
+
+  /* Check whether client has the permission. */
+  if (!(client->mode & SILC_UMODE_SERVER_OPERATOR) &&
+      !(client->mode & SILC_UMODE_ROUTER_OPERATOR)) {
+    silc_server_command_send_status_reply(cmd, SILC_COMMAND_PRIV_SHUTDOWN,
+                                         SILC_STATUS_ERR_NO_SERVER_PRIV,
+                                         0);
+    goto out;
+  }
+
+  /* Send reply to the sender */
+  silc_server_command_send_status_reply(cmd, SILC_COMMAND_PRIV_SHUTDOWN,
+                                       SILC_STATUS_OK, 0);
+
+  /* Then, gracefully, or not, bring the server down. */
+  silc_server_stop(server);
+  exit(0);
+
+ out:
   silc_server_command_free(cmd);
 }