Merged from silc_1_0_branch.
[silc.git] / apps / silcd / command.c
index e1ab042c4f23d324bc3147c4de7a3d11b992d76c..78beef6f58c38c90c71ae045894c8c16000351e1 100644 (file)
@@ -8,8 +8,7 @@
 
   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
@@ -29,11 +28,13 @@ static int silc_server_is_registered(SilcServer server,
 static void 
 silc_server_command_send_status_reply(SilcServerCommandContext cmd,
                                      SilcCommand command,
-                                     SilcStatus status);
+                                     SilcStatus status,
+                                     SilcStatus error);
 static void 
 silc_server_command_send_status_data(SilcServerCommandContext cmd,
                                     SilcCommand command,
                                     SilcStatus status,
+                                    SilcStatus error,
                                     SilcUInt32 arg_type,
                                     const unsigned char *arg,
                                     SilcUInt32 arg_len);
@@ -91,30 +92,33 @@ SilcServerCommand silc_command_list[] =
 
    It also checks that the requested command includes correct amount
    of arguments. */
-#define SILC_SERVER_COMMAND_CHECK(command, context, min, max)                \
-do {                                                                         \
-  SilcUInt32 _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
@@ -134,7 +138,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;
 }
 
@@ -153,6 +157,7 @@ SILC_TASK_CALLBACK(silc_server_command_process_timeout)
   SilcClientEntry client = (SilcClientEntry)timeout->ctx->sock->user_data;
 
   if (!client) {
+    SILC_LOG_DEBUG(("Client entry is invalid"));
     silc_server_command_free(timeout->ctx);
     silc_free(timeout);
   }
@@ -160,15 +165,21 @@ SILC_TASK_CALLBACK(silc_server_command_process_timeout)
   /* 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);
 }
@@ -195,7 +206,6 @@ void silc_server_command_process(SilcServer server,
                                            packet->buffer->len);
   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_free(ctx);
@@ -210,8 +220,9 @@ void silc_server_command_process(SilcServer server,
       break;
 
   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_STATUS_ERR_UNKNOWN_COMMAND, 0);
     silc_server_command_free(ctx);
     return;
   }
@@ -222,9 +233,15 @@ void silc_server_command_process(SilcServer server,
      seconds. */
   if (sock->type == SILC_SOCKET_TYPE_CLIENT) {
     SilcClientEntry client = (SilcClientEntry)sock->user_data;
-    SilcServerCommandTimeout timeout = silc_calloc(1, sizeof(*timeout));
+    SilcServerCommandTimeout timeout;
     int fast;
 
+    if (!client) {
+      SILC_LOG_DEBUG(("Client entry is invalid"));
+      silc_server_command_free(ctx);
+    }
+
+    timeout = silc_calloc(1, sizeof(*timeout));
     timeout->ctx = ctx;
     timeout->cmd = cmd;
 
@@ -232,34 +249,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_server_command_process_timeout, timeout,
+                            (client->fast_command < 3 ? 0 :
+                             2 - (time(NULL) - client->last_command)),
+                            (client->fast_command < 3 ? 200000 : 0),
+                            SILC_TASK_TIMEOUT, SILC_TASK_PRI_NORMAL);
     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_server_command_process_timeout, timeout,
+                            0, 1, SILC_TASK_TIMEOUT, SILC_TASK_PRI_NORMAL);
     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 */
@@ -301,6 +324,47 @@ 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, tmpreply->len);
+  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
@@ -315,6 +379,20 @@ bool silc_server_command_pending(SilcServer server,
                                 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. */
+
+bool silc_server_command_pending_timed(SilcServer server,
+                                      SilcCommand reply_cmd,
+                                      SilcUInt16 ident,
+                                      SilcCommandCb callback,
+                                      void *context,
+                                      SilcUInt16 timeout)
 {
   SilcServerCommandPending *reply;
 
@@ -333,6 +411,11 @@ bool silc_server_command_pending(SilcServer server,
   reply->ident = ident;
   reply->context = context;
   reply->callback = callback;
+  reply->timeout =
+    silc_schedule_task_add(server->schedule, 0,
+                          silc_server_command_pending_timeout, reply,
+                          timeout ? timeout : 10, 0,
+                          SILC_TASK_TIMEOUT, SILC_TASK_PRI_LOW);
   silc_dlist_add(server->pending_commands, reply);
 
   return TRUE;
@@ -348,1642 +431,152 @@ void silc_server_command_pending_del(SilcServer server,
 
   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;
-    }
-  }
-}
-
-/* Checks for pending commands and marks callbacks to be called from
-   the command reply function. Returns TRUE if there were pending command. */
-
-SilcServerCommandPendingCallbacks
-silc_server_command_pending_check(SilcServer server,
-                                 SilcServerCommandReplyContext ctx,
-                                 SilcCommand command, 
-                                 SilcUInt16 ident,
-                                 SilcUInt32 *callbacks_count)
-{
-  SilcServerCommandPending *r;
-  SilcServerCommandPendingCallbacks callbacks = NULL;
-  int i = 0;
-
-  silc_dlist_start(server->pending_commands);
-  while ((r = silc_dlist_get(server->pending_commands)) != SILC_LIST_END) {
-    if ((r->reply_cmd == command || r->reply_cmd == SILC_COMMAND_NONE)
-       && r->ident == ident) {
-      callbacks = silc_realloc(callbacks, sizeof(*callbacks) * (i + 1));
-      callbacks[i].context = r->context;
-      callbacks[i].callback = r->callback;
-      ctx->ident = ident;
-      i++;
-    }
-  }
-
-  *callbacks_count = i;
-  return callbacks;
-}
-
-/* Sends simple status message as command reply packet */
-
-static void 
-silc_server_command_send_status_reply(SilcServerCommandContext cmd,
-                                     SilcCommand command,
-                                     SilcStatus status)
-{
-  SilcBuffer buffer;
-
-  SILC_LOG_DEBUG(("Sending command status %d", status));
-
-  buffer = 
-    silc_command_reply_payload_encode_va(command, status, 0,
-                                        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_buffer_free(buffer);
-}
-
-/* Sends command status reply with one extra argument. The argument
-   type must be sent as argument. */
-
-static void 
-silc_server_command_send_status_data(SilcServerCommandContext cmd,
-                                    SilcCommand command,
-                                    SilcStatus status,
-                                    SilcUInt32 arg_type,
-                                    const unsigned char *arg,
-                                    SilcUInt32 arg_len)
-{
-  SilcBuffer buffer;
-
-  SILC_LOG_DEBUG(("Sending command status %d", 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_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. 
-   This returns TRUE if error had occurred. */
-
-static bool
-silc_server_command_pending_error_check(SilcServerCommandContext cmd,
-                                       SilcServerCommandReplyContext cmdr,
-                                       SilcCommand command)
-{
-  if (!cmd->pending || !cmdr)
-    return FALSE;
-
-  if (!silc_command_get_status(cmdr->payload, NULL, NULL)) {
-    SilcBuffer buffer;
-
-    /* Send the same command reply 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_buffer_free(buffer);
-    return TRUE;
-  }
-
-  return FALSE;
-}
-
-/******************************************************************************
-
-                              WHOIS Functions
-
-******************************************************************************/
-
-static int
-silc_server_command_whois_parse(SilcServerCommandContext cmd,
-                               SilcClientID ***client_id,
-                               SilcUInt32 *client_id_count,
-                               char **nickname,
-                               char **server_name,
-                               int *count,
-                               SilcCommand command)
-{
-  unsigned char *tmp;
-  SilcUInt32 len;
-  SilcUInt32 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, NULL);
-    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, NULL);
-         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++;
-       }
-      }
-    }
-  }
-
-  /* Get the max count of reply messages allowed */
-  tmp = silc_argument_get_arg_type(cmd->args, 2, NULL);
-  if (tmp) {
-    SILC_GET32_MSB(*count, tmp);
-  } else {
-    *count = 0;
-  }
-
-  return TRUE;
-}
-
-/* Resolve context used by both WHOIS and IDENTIFY commands */
-typedef struct {
-  SilcServerEntry router;
-  SilcUInt16 ident;
-  unsigned char **res_argv;
-  SilcUInt32 *res_argv_lens;
-  SilcUInt32 *res_argv_types;
-  SilcUInt32 res_argc;
-} *SilcServerResolveContext;
-
-static bool
-silc_server_command_whois_check(SilcServerCommandContext cmd,
-                               SilcClientEntry *clients,
-                               SilcUInt32 clients_count)
-{
-  SilcServer server = cmd->server;
-  SilcClientEntry entry;
-  SilcServerResolveContext resolve = NULL, r = NULL;
-  SilcUInt32 resolve_count = 0;
-  int i, k;
-  bool no_res = TRUE;
-
-  SILC_LOG_DEBUG(("Start"));
-
-  for (i = 0; i < clients_count; i++) {
-    entry = clients[i];
-    if (!entry)
-      continue;
-
-    if ((entry->nickname && entry->username && entry->userinfo) ||
-       !(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;
-    }
-
-    /* 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_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;
-
-       r = NULL;
-       for (k = 0; k < resolve_count; k++) {
-         if (resolve[k].router == entry->router) {
-           r = &resolve[k];
-           break;
-         }
-       }
-
-       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++;
-       }
-
-       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;
-      }
-    }
-  }
-
-  /* Do the resolving */
-  for (i = 0; i < resolve_count; i++) {
-    SilcBuffer res_cmd;
-
-    r = &resolve[i];
-
-    /* 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);
-
-    /* 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;
-
-    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);
-
-  return no_res;
-}
-
-static void
-silc_server_command_whois_send_reply(SilcServerCommandContext cmd,
-                                    SilcClientEntry *clients,
-                                    SilcUInt32 clients_count,
-                                    int count,
-                                    const char *nickname,
-                                    SilcClientID **client_ids)
-{
-  SilcServer server = cmd->server;
-  char *tmp;
-  int i, k, len, valid_count;
-  SilcBuffer packet, idp, channels, umode_list = NULL;
-  SilcClientEntry entry;
-  SilcStatus status;
-  SilcUInt16 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++;
-    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);
-    }
-    return;
-  }
-
-  /* Start processing found clients. */
-  if (valid_count > 1)
-    status = SILC_STATUS_LIST_START;
-  else
-    status = SILC_STATUS_OK;
-
-  for (i = 0, k = 0; i < clients_count; i++) {
-    entry = clients[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)
-      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, '@') && entry->connection) {
-      strncat(uh, "@", 1);
-      hsock = (SilcSocketConnection)entry->connection;
-      len = strlen(hsock->hostname);
-      strncat(uh, hsock->hostname, len);
-    }
-
-    if (cmd->sock->type == SILC_SOCKET_TYPE_CLIENT)
-      channels = silc_server_get_client_channel_list(server, entry, FALSE, 
-                                                    FALSE, &umode_list);
-    else
-      channels = silc_server_get_client_channel_list(server, entry, TRUE, 
-                                                    TRUE, &umode_list);
-
-    if (entry->data.fingerprint[0] != 0 && entry->data.fingerprint[1] != 0)
-      fingerprint = entry->data.fingerprint;
-    else
-      fingerprint = NULL;
-      
-    SILC_PUT32_MSB(entry->mode, mode);
-
-    if (entry->connection) {
-      SILC_PUT32_MSB((time(NULL) - entry->data.last_receive), idle);
-    }
-
-    packet = 
-      silc_command_reply_payload_encode_va(SILC_COMMAND_WHOIS,
-                                          status, 0, ident, 9, 
-                                          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,
-                                          10, umode_list ? umode_list->data :
-                                          NULL, umode_list ? umode_list->len :
-                                          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);
-    if (channels)
-      silc_buffer_free(channels);
-    if (umode_list) {
-      silc_buffer_free(umode_list);
-      umode_list = NULL;
+      if (r->timeout)
+       silc_schedule_task_del(server->schedule, r->timeout);
+      silc_free(r);
     }
+  }
+}
 
-    k++;
-  }
-}
-
-static void 
-silc_server_command_whois_send_router(SilcServerCommandContext cmd)
-{
-  SilcServer server = cmd->server;
-  SilcBuffer tmpbuf;
-  SilcUInt16 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);
-}
-
-static int
-silc_server_command_whois_process(SilcServerCommandContext cmd)
-{
-  SilcServer server = cmd->server;
-  char *nick = NULL, *server_name = NULL;
-  int count = 0;
-  SilcClientEntry *clients = NULL, entry;
-  SilcClientID **client_id = NULL;
-  SilcUInt32 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 &&
-      !server->standalone) {
-    silc_server_command_whois_send_router(cmd);
-    ret = -1;
-    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;
-    }
-
-    /* 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;
-  }
-
-  /* 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;
-  }
-
-  /* Send the command reply */
-  silc_server_command_whois_send_reply(cmd, clients, clients_count,
-                                      count, nick, client_id);
-
- 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);
-
-  return ret;
-}
-
-/* Server side of command WHOIS. Processes user's query and sends found 
-   results as command replies back to the client. */
-
-SILC_SERVER_CMD_FUNC(whois)
-{
-  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);
-}
-
-/******************************************************************************
-
-                              WHOWAS Functions
-
-******************************************************************************/
-
-static int
-silc_server_command_whowas_parse(SilcServerCommandContext cmd,
-                                char **nickname,
-                                char **server_name,
-                                int *count)
-{
-  unsigned char *tmp;
-  SilcUInt32 len;
-
-  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 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) {
-    SILC_GET32_MSB(*count, tmp);
-  } else {
-    *count = 0;
-  }
-
-  return TRUE;
-}
-
-static char
-silc_server_command_whowas_check(SilcServerCommandContext cmd,
-                                SilcClientEntry *clients,
-                                SilcUInt32 clients_count)
-{
-  SilcServer server = cmd->server;
-  int i;
-  SilcClientEntry entry;
-
-  for (i = 0; i < clients_count; i++) {
-    entry = clients[i];
-
-    if (!entry->nickname || !entry->username) {
-      SilcBuffer tmpbuf;
-      SilcUInt16 old_ident;
-
-      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);
-
-      /* 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);
-
-      silc_buffer_free(tmpbuf);
-      return FALSE;
-    }
-  }
-
-  return TRUE;
-}
-
-static void
-silc_server_command_whowas_send_reply(SilcServerCommandContext cmd,
-                                     SilcClientEntry *clients,
-                                     SilcUInt32 clients_count)
-{
-  SilcServer server = cmd->server;
-  char *tmp;
-  int i, k, count = 0, len;
-  SilcBuffer packet, idp;
-  SilcClientEntry entry = NULL;
-  SilcStatus status;
-  SilcUInt16 ident = silc_command_get_ident(cmd->payload);
-  char nh[256], uh[256];
-  int valid_count;
-
-  status = SILC_STATUS_OK;
-
-  /* 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++;
-  }
-
-  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;
-  }
-
-  if (valid_count > 1)
-    status = SILC_STATUS_LIST_START;
-
-  for (i = 0, k = 0; i < clients_count; i++) {
-    entry = clients[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)
-      status = SILC_STATUS_LIST_END;
-    if (count && k - 1 > count)
-      break;
-
-    /* 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);
-      }
-    }
-      
-    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, 0, 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++;
-  }
-}
-
-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;
-  SilcUInt32 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;
-    SilcUInt16 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 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);
-
-    /* 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);
-
-    silc_buffer_free(tmpbuf);
-    ret = -1;
-    goto out;
-  }
-
-  /* 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;
-  }
-
-  if (!silc_server_command_whowas_check(cmd, clients, clients_count)) {
-    ret = -1;
-    goto out;
-  }
-
-  /* Send the command reply to the client */
-  silc_server_command_whowas_send_reply(cmd, clients, clients_count);
-
- out:
-  silc_free(clients);
-  silc_free(nick);
-  silc_free(server_name);
-  return ret;
-}
-
-/* Server side of command WHOWAS. */
-
-SILC_SERVER_CMD_FUNC(whowas)
-{
-  SilcServerCommandContext cmd = (SilcServerCommandContext)context;
-  int ret = 0;
-
-  SILC_SERVER_COMMAND_CHECK(SILC_COMMAND_WHOWAS, cmd, 1, 2);
-
-  ret = silc_server_command_whowas_process(cmd);
-  silc_server_command_free(cmd);
-}
-
-/******************************************************************************
-
-                              IDENTIFY Functions
-
-******************************************************************************/
-
-static void 
-silc_server_command_identify_send_router(SilcServerCommandContext cmd)
-{
-  SilcServer server = cmd->server;
-  SilcBuffer tmpbuf;
-  SilcUInt16 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);
-}
-
-static int
-silc_server_command_identify_parse(SilcServerCommandContext cmd,
-                                  SilcClientEntry **clients,
-                                  SilcUInt32 *clients_count,
-                                  SilcServerEntry **servers,
-                                  SilcUInt32 *servers_count,
-                                  SilcChannelEntry **channels,
-                                  SilcUInt32 *channels_count,
-                                  SilcUInt32 *count)
-{
-  SilcServer server = cmd->server;
-  unsigned char *tmp;
-  SilcUInt32 len;
-  SilcUInt32 argc = silc_argument_get_arg_num(cmd->args);
-  SilcIDPayload idp;
-  bool check_global = FALSE;
-  void *entry;
-  int i;
-  bool error = FALSE;
-
-  if (cmd->sock->type == SILC_SOCKET_TYPE_CLIENT)
-    check_global = TRUE;
-  else if (server->server_type != SILC_SERVER)
-    check_global = TRUE;
-
-  /* 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;
-    }
-
-    /* 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;
-      }
-    }
-
-    /* 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;
-      }
-
-      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;
-      }
-    }
-
-    /* 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;
-      }
-
-      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;
-      }
-    }
-
-    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. */
-
-    /* Take all ID's from the command packet */
-    for (i = 0; i < argc; i++) {
-      void *id;
-
-      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;
-      }
-
-      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;
-         }
-       }
-
-       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;
-      }
-
-      silc_free(id);
-    }
-  }
-
-  if (error) {
-    silc_free(*clients);
-    silc_free(*servers);
-    silc_free(*channels);
-    return FALSE;
-  }
-  
-  /* Get the max count of reply messages allowed */
-  tmp = silc_argument_get_arg_type(cmd->args, 4, NULL);
-  if (tmp) {
-    SILC_GET32_MSB(*count, 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,
-                                         SilcUInt32 clients_count)
-{
-  SilcServer server = cmd->server;
-  SilcClientEntry entry;
-  SilcServerResolveContext resolve = NULL, r = NULL;
-  SilcUInt32 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;
-    }
-
-    /* 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;
-         }
-       }
-
-       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++;
-       }
-
-       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;
-      }
-    }
-  }
-
-  /* Do the resolving */
-  for (i = 0; i < resolve_count; i++) {
-    SilcBuffer res_cmd;
-
-    r = &resolve[i];
-
-    /* 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);
-
-    /* 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;
-
-    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);
-
-  return no_res;
-}
-
-static void
-silc_server_command_identify_send_reply(SilcServerCommandContext cmd,
-                                       SilcClientEntry *clients,
-                                       SilcUInt32 clients_count,
-                                       SilcServerEntry *servers,
-                                       SilcUInt32 servers_count,
-                                       SilcChannelEntry *channels,
-                                       SilcUInt32 channels_count,
-                                       int count)
-{
-  SilcServer server = cmd->server;
-  int i, k, len, valid_count;
-  SilcBuffer packet, idp;
-  SilcStatus status;
-  SilcUInt16 ident = silc_command_get_ident(cmd->payload);
-  char nh[256], uh[256];
-  SilcSocketConnection hsock;
-
-  status = SILC_STATUS_OK;
+/* Checks for pending commands and marks callbacks to be called from
+   the command reply function. Returns TRUE if there were pending command. */
 
-  if (clients) {
-    SilcClientEntry entry;
+SilcServerCommandPendingCallbacks
+silc_server_command_pending_check(SilcServer server,
+                                 SilcCommand command, 
+                                 SilcUInt16 ident,
+                                 SilcUInt32 *callbacks_count)
+{
+  SilcServerCommandPending *r;
+  SilcServerCommandPendingCallbacks callbacks = NULL;
+  int i = 0;
 
-    /* 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;
+  silc_dlist_start(server->pending_commands);
+  while ((r = silc_dlist_get(server->pending_commands)) != SILC_LIST_END) {
+    if ((r->reply_cmd == command || r->reply_cmd == SILC_COMMAND_NONE)
+       && r->ident == ident) {
+      callbacks = silc_realloc(callbacks, sizeof(*callbacks) * (i + 1));
+      callbacks[i].context = r->context;
+      callbacks[i].callback = r->callback;
+      r->reply_check = TRUE;
+      i++;
     }
+  }
 
-    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_IDENTIFY,
-                                            SILC_STATUS_ERR_NO_SUCH_NICK,
-                                            3, tmp, strlen(tmp));
-      } else {
-       tmp = silc_argument_get_arg_type(cmd->args, 5, (SilcUInt32 *)&len);
-       silc_server_command_send_status_data(cmd, SILC_COMMAND_IDENTIFY,
-                                            SILC_STATUS_ERR_NO_SUCH_CLIENT_ID,
-                                            2, tmp, len);
-      }
-      return;
-    }
+  *callbacks_count = i;
+  return callbacks;
+}
 
-    /* Process all valid client entries and send command replies */
+/* Sends simple status message as command reply packet */
 
-    if (valid_count > 1)
-      status = SILC_STATUS_LIST_START;
+static void 
+silc_server_command_send_status_reply(SilcServerCommandContext cmd,
+                                     SilcCommand command,
+                                     SilcStatus status,
+                                     SilcStatus error)
+{
+  SilcBuffer buffer;
 
-    for (i = 0, k = 0; i < clients_count; i++) {
-      entry = clients[i];
-      if (!entry)
-       continue;
+  SILC_LOG_DEBUG(("Sending command status %d", status));
 
-      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;
+  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_buffer_free(buffer);
+}
 
-      /* 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);
-       }
-      }
+/* Sends command status reply with one extra argument. The argument
+   type must be sent as argument. */
 
-      if (!entry->username) {
-       packet = silc_command_reply_payload_encode_va(SILC_COMMAND_IDENTIFY,
-                                                     status, 0, ident, 2,
-                                                     2, idp->data, idp->len, 
-                                                     3, nh, strlen(nh));
-      } else {
-       strncat(uh, entry->username, strlen(entry->username));
-       if (!strchr(entry->username, '@') && entry->connection) {
-         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, 0, 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++;
-    }
-  }
+static void 
+silc_server_command_send_status_data(SilcServerCommandContext cmd,
+                                    SilcCommand command,
+                                    SilcStatus status,
+                                    SilcStatus error,
+                                    SilcUInt32 arg_type,
+                                    const unsigned char *arg,
+                                    SilcUInt32 arg_len)
+{
+  SilcBuffer buffer;
 
-  if (servers) {
-    SilcServerEntry entry;
+  SILC_LOG_DEBUG(("Sending command status %d", status));
 
-    if (status == SILC_STATUS_OK && servers_count > 1)
-      status = SILC_STATUS_LIST_START;
+  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_buffer_free(buffer);
+}
 
-    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, 0, 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++;
-    }
-  }
+/* 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. 
+   This returns TRUE if error had occurred. */
 
-  if (channels) {
-    SilcChannelEntry entry;
+static bool
+silc_server_command_pending_error_check(SilcServerCommandContext cmd,
+                                       SilcServerCommandReplyContext cmdr,
+                                       SilcCommand command)
+{
+  if (!cmd->pending || !cmdr)
+    return FALSE;
 
-    if (status == SILC_STATUS_OK && channels_count > 1)
-      status = SILC_STATUS_LIST_START;
+  if (!silc_command_get_status(cmdr->payload, NULL, NULL)) {
+    SilcBuffer buffer;
 
-    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, 0, 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++;
-    }
+    /* Send the same command reply 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_buffer_free(buffer);
+    return TRUE;
   }
+
+  return FALSE;
 }
 
-static int
-silc_server_command_identify_process(SilcServerCommandContext cmd)
+/* Server side of command WHOIS. */
+
+SILC_SERVER_CMD_FUNC(whois)
 {
-  SilcUInt32 count = 0;
-  int ret = 0;
-  SilcClientEntry *clients = NULL;
-  SilcServerEntry *servers = NULL;
-  SilcChannelEntry *channels = NULL;
-  SilcUInt32 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;
-    goto out;
-  }
+  SilcServerCommandContext cmd = (SilcServerCommandContext)context;
+  SILC_SERVER_COMMAND_CHECK(SILC_COMMAND_WHOIS, cmd, 1, 256);
+  silc_server_query_command(cmd->server, SILC_COMMAND_WHOIS, cmd);
+  silc_server_command_free(cmd);
+}
 
-  /* Send the command reply to the client */
-  silc_server_command_identify_send_reply(cmd, 
-                                         clients, clients_count,
-                                         servers, servers_count,
-                                         channels, channels_count, 
-                                         count);
+/* Server side of command WHOWAS. */
 
- out:
-  silc_free(clients);
-  silc_free(servers);
-  silc_free(channels);
-  return ret;
+SILC_SERVER_CMD_FUNC(whowas)
+{
+  SilcServerCommandContext cmd = (SilcServerCommandContext)context;
+  SILC_SERVER_COMMAND_CHECK(SILC_COMMAND_WHOWAS, cmd, 1, 2);
+  silc_server_query_command(cmd->server, SILC_COMMAND_WHOWAS, cmd);
+  silc_server_command_free(cmd);
 }
 
+/* Server side of command IDENTIFY. */
+
 SILC_SERVER_CMD_FUNC(identify)
 {
   SilcServerCommandContext cmd = (SilcServerCommandContext)context;
-  int ret = 0;
-
-  SILC_SERVER_COMMAND_CHECK(SILC_COMMAND_IDENTIFY, cmd, 1, 3328);
-
-  ret = silc_server_command_identify_process(cmd);
+  SILC_SERVER_COMMAND_CHECK(SILC_COMMAND_IDENTIFY, cmd, 1, 256);
+  silc_server_query_command(cmd->server, SILC_COMMAND_IDENTIFY, cmd);
   silc_server_command_free(cmd);
 }
 
@@ -2003,18 +596,23 @@ SILC_SERVER_CMD_FUNC(nick)
   SilcUInt16 ident = silc_command_get_ident(cmd->payload);
   int nickfail = 0;
 
-  if (cmd->sock->type != SILC_SOCKET_TYPE_CLIENT)
+  if (cmd->sock->type != SILC_SOCKET_TYPE_CLIENT || !client)
     goto out;
 
   SILC_SERVER_COMMAND_CHECK(SILC_COMMAND_NICK, cmd, 1, 1);
 
   /* Check 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 (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);
+                                         SILC_STATUS_ERR_BAD_NICKNAME, 0);
     goto out;
   }
 
@@ -2032,7 +630,7 @@ SILC_SERVER_CMD_FUNC(nick)
     nickfail++;
     if (nickfail > 9) {
       silc_server_command_send_status_reply(cmd, SILC_COMMAND_NICK,
-                                           SILC_STATUS_ERR_BAD_NICKNAME);
+                                           SILC_STATUS_ERR_BAD_NICKNAME, 0);
       goto out;
     }
     snprintf(&nick[strlen(nick) - 1], 1, "%d", nickfail);
@@ -2041,11 +639,9 @@ SILC_SERVER_CMD_FUNC(nick)
   /* 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, nick);
+  silc_server_send_notify_nick_change(server, SILC_PRIMARY_ROUTE(server),
+                                     SILC_BROADCAST(server), client->id,
+                                     new_id, nick);
 
   /* Check if anyone is watching the old nickname */
   if (server->server_type == SILC_ROUTER)
@@ -2085,8 +681,9 @@ SILC_SERVER_CMD_FUNC(nick)
  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, 0, ident, 1, 
-                                               2, nidp->data, nidp->len);
+                                               SILC_STATUS_OK, 0, ident, 2,
+                                               2, nidp->data, nidp->len,
+                                               3, nick, strlen(nick));
   silc_server_packet_send(cmd->server, cmd->sock, SILC_PACKET_COMMAND_REPLY,
                          0, packet->data, packet->len, FALSE);
 
@@ -2231,7 +828,7 @@ SILC_SERVER_CMD_FUNC(list)
 
   /* 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 && 
+  if (!cmd->pending && server->server_type != SILC_ROUTER && 
       !server->standalone) {
     SilcBuffer tmpbuf;
     SilcUInt16 old_ident;
@@ -2239,7 +836,7 @@ SILC_SERVER_CMD_FUNC(list)
     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);
 
@@ -2260,7 +857,7 @@ SILC_SERVER_CMD_FUNC(list)
     channel_id = silc_id_payload_parse_id(tmp, tmp_len, NULL);
     if (!channel_id) {
       silc_server_command_send_status_reply(cmd, SILC_COMMAND_LIST,
-                                           SILC_STATUS_ERR_NO_CHANNEL_ID);
+                                           SILC_STATUS_ERR_NO_CHANNEL_ID, 0);
       goto out;
     }
   }
@@ -2300,6 +897,9 @@ SILC_SERVER_CMD_FUNC(topic)
   SilcUInt32 argc, tmp_len;
   SilcUInt16 ident = silc_command_get_ident(cmd->payload);
 
+  if (cmd->sock->type != SILC_SOCKET_TYPE_CLIENT || !client)
+    goto out;
+
   SILC_SERVER_COMMAND_CHECK(SILC_COMMAND_TOPIC, cmd, 1, 2);
 
   argc = silc_argument_get_arg_num(cmd->args);
@@ -2308,13 +908,13 @@ SILC_SERVER_CMD_FUNC(topic)
   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);
+                                         SILC_STATUS_ERR_NO_CHANNEL_ID, 0);
     goto out;
   }
   channel_id = silc_id_payload_parse_id(tmp, tmp_len, NULL);
   if (!channel_id) {
     silc_server_command_send_status_reply(cmd, SILC_COMMAND_TOPIC,
-                                         SILC_STATUS_ERR_NO_CHANNEL_ID);
+                                         SILC_STATUS_ERR_NO_CHANNEL_ID, 0);
     goto out;
   }
 
@@ -2326,7 +926,8 @@ SILC_SERVER_CMD_FUNC(topic)
                                             channel_id, NULL);
     if (!channel) {
       silc_server_command_send_status_reply(cmd, SILC_COMMAND_TOPIC,
-                                           SILC_STATUS_ERR_NO_SUCH_CHANNEL);
+                                           SILC_STATUS_ERR_NO_SUCH_CHANNEL,
+                                           0);
       goto out;
     }
   }
@@ -2336,20 +937,23 @@ SILC_SERVER_CMD_FUNC(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);
+                                           SILC_STATUS_ERR_NOT_ENOUGH_PARAMS,
+                                           0);
       goto out;
     }
 
     if (strlen(tmp) > 256) {
       silc_server_command_send_status_reply(cmd, SILC_COMMAND_TOPIC,
-                                           SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
+                                           SILC_STATUS_ERR_NOT_ENOUGH_PARAMS,
+                                           0);
       goto out;
     }
 
     /* See whether the client is on channel and has rights to change topic */
     if (!silc_server_client_on_channel(client, channel, &chl)) {
       silc_server_command_send_status_reply(cmd, SILC_COMMAND_TOPIC,
-                                           SILC_STATUS_ERR_NOT_ON_CHANNEL);
+                                           SILC_STATUS_ERR_NOT_ON_CHANNEL,
+                                           0);
       goto out;
     }
 
@@ -2357,30 +961,31 @@ SILC_SERVER_CMD_FUNC(topic)
        !(chl->mode & SILC_CHANNEL_UMODE_CHANOP) &&
        !(chl->mode & SILC_CHANNEL_UMODE_CHANFO)) {
       silc_server_command_send_status_reply(cmd, SILC_COMMAND_TOPIC,
-                                           SILC_STATUS_ERR_NO_CHANNEL_PRIV);
+                                           SILC_STATUS_ERR_NO_CHANNEL_PRIV,
+                                           0);
       goto out;
     }
 
-    /* Set the topic for channel */
-    silc_free(channel->topic);
-    channel->topic = strdup(tmp);
+    if (!channel->topic || strcmp(channel->topic, tmp)) {
+      /* 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, 
+      /* 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);
 
-    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 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, 
+                                        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 */
@@ -2416,10 +1021,12 @@ SILC_SERVER_CMD_FUNC(invite)
   SilcChannelEntry channel;
   SilcChannelID *channel_id = NULL;
   SilcIDListData idata;
-  SilcBuffer idp, idp2, packet;
-  unsigned char *tmp, *add, *del;
-  SilcUInt32 len;
-  SilcUInt16 ident = silc_command_get_ident(cmd->payload);
+  SilcArgumentPayload args;
+  SilcHashTableList htl;
+  SilcBuffer packet, list, tmp2;
+  unsigned char *tmp;
+  SilcUInt32 len, type;
+  SilcUInt16 argc = 0, ident = silc_command_get_ident(cmd->payload);
 
   SILC_SERVER_COMMAND_CHECK(SILC_COMMAND_INVITE, cmd, 1, 4);
 
@@ -2427,13 +1034,13 @@ SILC_SERVER_CMD_FUNC(invite)
   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);
+                                         SILC_STATUS_ERR_NO_CHANNEL_ID, 0);
     goto out;
   }
   channel_id = silc_id_payload_parse_id(tmp, len, NULL);
   if (!channel_id) {
     silc_server_command_send_status_reply(cmd, SILC_COMMAND_INVITE,
-                                         SILC_STATUS_ERR_NO_CHANNEL_ID);
+                                         SILC_STATUS_ERR_NO_CHANNEL_ID, 0);
     goto out;
   }
 
@@ -2445,16 +1052,17 @@ SILC_SERVER_CMD_FUNC(invite)
                                             channel_id, NULL);
     if (!channel) {
       silc_server_command_send_status_reply(cmd, SILC_COMMAND_INVITE,
-                                           SILC_STATUS_ERR_NO_SUCH_CHANNEL);
+                                           SILC_STATUS_ERR_NO_SUCH_CHANNEL,
+                                           0);
       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, &chl)) {
+  if (!sender || !silc_server_client_on_channel(sender, channel, &chl)) {
     silc_server_command_send_status_reply(cmd, SILC_COMMAND_INVITE,
-                                         SILC_STATUS_ERR_NOT_ON_CHANNEL);
+                                         SILC_STATUS_ERR_NOT_ON_CHANNEL, 0);
     goto out;
   }
 
@@ -2464,30 +1072,30 @@ SILC_SERVER_CMD_FUNC(invite)
       !(chl->mode & SILC_CHANNEL_UMODE_CHANOP) &&
       !(chl->mode & SILC_CHANNEL_UMODE_CHANFO)) {
     silc_server_command_send_status_reply(cmd, SILC_COMMAND_INVITE,
-                                           SILC_STATUS_ERR_NO_CHANNEL_PRIV);
+                                         SILC_STATUS_ERR_NO_CHANNEL_PRIV,
+                                         0);
     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, NULL);
     if (!dest_id) {
       silc_server_command_send_status_reply(cmd, SILC_COMMAND_INVITE,
-                                           SILC_STATUS_ERR_NO_CLIENT_ID);
+                                           SILC_STATUS_ERR_NO_CLIENT_ID, 0);
       goto out;
     }
 
     /* Get the client entry */
-    dest = silc_server_get_client_resolve(server, dest_id, FALSE, &resolve);
+    dest = silc_server_query_client(server, dest_id, FALSE, &resolve);
     if (!dest) {
-      if (server->server_type != SILC_SERVER || !resolve) {
+      if (server->server_type != SILC_SERVER || !resolve || cmd->pending) {
        silc_server_command_send_status_reply(
                                        cmd, SILC_COMMAND_INVITE,
-                                       SILC_STATUS_ERR_NO_SUCH_CLIENT_ID);
+                                       SILC_STATUS_ERR_NO_SUCH_CLIENT_ID, 0);
        goto out;
       }
       
@@ -2506,7 +1114,8 @@ SILC_SERVER_CMD_FUNC(invite)
     /* Check whether the requested client is already on the channel. */
     if (silc_server_client_on_channel(dest, channel, NULL)) {
       silc_server_command_send_status_reply(cmd, SILC_COMMAND_INVITE,
-                                           SILC_STATUS_ERR_USER_ON_CHANNEL);
+                                           SILC_STATUS_ERR_USER_ON_CHANNEL,
+                                           0);
       goto out;
     }
     
@@ -2515,33 +1124,40 @@ SILC_SERVER_CMD_FUNC(invite)
                                             &idata, NULL);
     if (!dest_sock) {
       silc_server_command_send_status_reply(cmd, SILC_COMMAND_INVITE,
-                                           SILC_STATUS_ERR_NO_SUCH_CLIENT_ID);
+                                           SILC_STATUS_ERR_NO_SUCH_CLIENT_ID,
+                                           0);
       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));
-    }
+    /* Add the client to the invite list */
 
-    len = strlen(invite);
+    /* Allocate hash table for invite list if it doesn't exist yet */
     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);
+      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 */
+    silc_hash_table_list(channel->invite_list, &htl);
+    while (silc_hash_table_get(&htl, (void **)&type, (void **)&tmp2)) {
+      if (type == 3 && !memcmp(tmp2->data, tmp, len)) {
+       tmp = NULL;
+       break;
+      }
+    }
+    silc_hash_table_list_reset(&htl);
+
+    /* 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);
+    }
 
     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(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, 
@@ -2556,73 +1172,95 @@ SILC_SERVER_CMD_FUNC(invite)
     }
   }
 
-  /* 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;
+  /* Get the invite information */
+  tmp = silc_argument_get_arg_type(cmd->args, 4, &len);
+  if (tmp && len > 2) {
+    /* Parse the arguments to see they are constructed correctly */
+    SILC_GET16_MSB(argc, tmp);
+    args = silc_argument_payload_parse(tmp + 2, len - 2, argc);
+    if (!args) {
+      silc_server_command_send_status_reply(cmd, SILC_COMMAND_INVITE,
+                                           SILC_STATUS_ERR_NOT_ENOUGH_PARAMS,
+                                           0);
+      goto out;
+    }
 
-    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;
+    /* Get the type of action */
+    tmp = silc_argument_get_arg_type(cmd->args, 3, &len);
+    if (tmp && len == 1) {
+      if (tmp[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. */
+      silc_server_inviteban_process(server, channel->invite_list,
+                                   (SilcUInt8)tmp[0], args);
+    }
+    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)) {
+      if (type == 1)
+       list = silc_argument_payload_encode_one(list, (char *)tmp2,
+                                               strlen((char *)tmp2), type);
+      else
+       list = silc_argument_payload_encode_one(list, tmp2->data, tmp2->len,
+                                               type);
     }
+    silc_hash_table_list_reset(&htl);
   }
 
   /* 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);
+  silc_server_send_notify_invite(
+                        server, SILC_PRIMARY_ROUTE(server),
+                        SILC_BROADCAST(server), channel, sender->id,
+                        silc_argument_get_arg_type(cmd->args, 3, NULL),
+                        list);
+
+  /* Send invite list back only if the list was modified, or now arguments
+     was given. */
+  type = 0;
+  argc = silc_argument_get_arg_num(cmd->args);
+  if (argc == 1)
+    type = 1;
+  if (silc_argument_get_arg_type(cmd->args, 3, &len))
+    type = 1;
 
   /* 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, 0, 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, 0, ident, 1,
-                                          2, tmp, len);
+  packet = silc_command_reply_payload_encode_va(SILC_COMMAND_INVITE,
+                                               SILC_STATUS_OK, 0, ident, 2,
+                                               2, tmp, len,
+                                               3, type && list ? 
+                                               list->data : NULL,
+                                               type && list ? list->len : 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(list);
 
  out:
   silc_free(dest_id);
@@ -2631,7 +1269,6 @@ SILC_SERVER_CMD_FUNC(invite)
 }
 
 typedef struct {
-  SilcServer server;
   SilcSocketConnection sock;
   char *signoff;
 } *QuitInternal;
@@ -2641,17 +1278,19 @@ typedef struct {
 
 SILC_TASK_CALLBACK(silc_server_command_quit_cb)
 {
+  SilcServer server = app_context;
   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,
+  silc_server_free_client_data(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_server_close_connection(server, q->sock);
 
+  silc_socket_free(q->sock);
   silc_free(q->signoff);
   silc_free(q);
 }
@@ -2678,8 +1317,7 @@ SILC_SERVER_CMD_FUNC(quit)
     tmp = NULL;
 
   q = silc_calloc(1, sizeof(*q));
-  q->server = server;
-  q->sock = sock;
+  q->sock = silc_socket_dup(sock);
   q->signoff = tmp ? strdup(tmp) : NULL;
 
   /* We quit the connection with little timeout */
@@ -2700,78 +1338,128 @@ SILC_SERVER_CMD_FUNC(kill)
   SilcServer server = cmd->server;
   SilcClientEntry client = (SilcClientEntry)cmd->sock->user_data;
   SilcClientEntry remote_client;
-  SilcClientID *client_id;
-  unsigned char *tmp, *comment;
-  SilcUInt32 tmp_len, tmp_len2;
-  bool local;
+  SilcClientID *client_id = NULL;
+  unsigned char *tmp, *comment, *auth;
+  SilcUInt32 tmp_len, tmp_len2, auth_len;
 
-  SILC_SERVER_COMMAND_CHECK(SILC_COMMAND_KILL, cmd, 1, 2);
+  SILC_SERVER_COMMAND_CHECK(SILC_COMMAND_KILL, cmd, 1, 3);
 
-  if (!client || cmd->sock->type != SILC_SOCKET_TYPE_CLIENT)
+  if (cmd->sock->type != SILC_SOCKET_TYPE_CLIENT || !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;
-  }
+  /* Get authentication payload if present */
+  auth = silc_argument_get_arg_type(cmd->args, 3, &auth_len);
 
-  /* 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;
+  if (!auth) {
+    /* Router operator killing */
+
+    /* 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;
+    }
+
+    /* 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;
+    }
   }
 
   /* 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);
+                                         SILC_STATUS_ERR_NOT_ENOUGH_PARAMS,
+                                         0);
     goto out;
   }
   client_id = silc_id_payload_parse_id(tmp, tmp_len, NULL);
   if (!client_id) {
     silc_server_command_send_status_reply(cmd, SILC_COMMAND_KILL,
-                                         SILC_STATUS_ERR_NO_SUCH_CLIENT_ID);
+                                         SILC_STATUS_ERR_NO_SUCH_CLIENT_ID,
+                                         0);
     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);
+                                           SILC_STATUS_ERR_NO_SUCH_CLIENT_ID,
+                                           0);
       goto out;
     }
   }
 
   /* Get comment */
   comment = silc_argument_get_arg_type(cmd->args, 2, &tmp_len2);
-  if (tmp_len2 > 128)
+  if (comment && tmp_len2 > 128) {
     tmp_len2 = 128;
+    comment[127] = '\0';
+  }
 
-  /* Send reply to the sender */
-  silc_server_command_send_status_reply(cmd, SILC_COMMAND_KILL,
-                                       SILC_STATUS_OK);
+  /* If authentication data is provided then verify that killing is
+     actually allowed */
+  if (auth && auth_len) {
+    SilcSocketConnection sock;
 
-  /* 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);
+    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;
+    }
+
+    /* 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;
+    }
+
+    /* Send reply to the sender */
+    silc_server_command_send_status_reply(cmd, SILC_COMMAND_KILL,
+                                         SILC_STATUS_OK, 0);
+
+    /* Do normal signoff for the destination client */
+    sock = remote_client->connection;
+    silc_server_remove_from_channels(server, NULL, remote_client,
+                                    TRUE, (char *)"Killed", TRUE, TRUE);
+    silc_server_free_client_data(server, NULL, remote_client, TRUE,
+                                comment ? comment :
+                                (unsigned char *)"Killed");
+    if (sock)
+      silc_server_close_connection(server, sock);
+  } else {
+    /* Router operator killing */
+
+    /* Send reply to the sender */
+    silc_server_command_send_status_reply(cmd, SILC_COMMAND_KILL,
+                                         SILC_STATUS_OK, 0);
+
+    /* 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);
 
-  /* Now do the killing */
-  silc_server_kill_client(server, remote_client, comment, client->id,
-                         SILC_ID_CLIENT);
+    /* Now do the killing */
+    silc_server_kill_client(server, remote_client, comment, client->id,
+                           SILC_ID_CLIENT);
+  }
 
  out:
+  silc_free(client_id);
   silc_server_command_free(cmd);
 }
 
@@ -2802,7 +1490,7 @@ SILC_SERVER_CMD_FUNC(info)
     server_id = silc_id_payload_parse_id(tmp, tmp_len, NULL);
     if (!server_id) {
       silc_server_command_send_status_reply(cmd, SILC_COMMAND_INFO,
-                                           SILC_STATUS_ERR_NO_SERVER_ID);
+                                           SILC_STATUS_ERR_NO_SERVER_ID, 0);
       goto out;
     }
   }
@@ -2816,7 +1504,8 @@ SILC_SERVER_CMD_FUNC(info)
                                            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);
+                                             SILC_STATUS_ERR_NO_SUCH_SERVER,
+                                             0);
        goto out;
       }
     }
@@ -2888,7 +1577,7 @@ SILC_SERVER_CMD_FUNC(info)
       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);
 
@@ -2908,7 +1597,7 @@ SILC_SERVER_CMD_FUNC(info)
 
   if (!entry) {
     silc_server_command_send_status_reply(cmd, SILC_COMMAND_INFO,
-                                         SILC_STATUS_ERR_NO_SUCH_SERVER);
+                                         SILC_STATUS_ERR_NO_SUCH_SERVER, 0);
     goto out;
   }
 
@@ -2942,36 +1631,35 @@ SILC_SERVER_CMD_FUNC(ping)
 {
   SilcServerCommandContext cmd = (SilcServerCommandContext)context;
   SilcServer server = cmd->server;
-  SilcServerID *id;
-  SilcUInt32 len;
+  SilcUInt32 tmp_len;
   unsigned char *tmp;
+  SilcServerID *server_id = NULL;
 
-  SILC_SERVER_COMMAND_CHECK(SILC_COMMAND_INFO, cmd, 1, 2);
+  SILC_SERVER_COMMAND_CHECK(SILC_COMMAND_PING, cmd, 1, 1);
 
   /* Get Server ID */
-  tmp = silc_argument_get_arg_type(cmd->args, 1, &len);
+  tmp = silc_argument_get_arg_type(cmd->args, 1, &tmp_len);
   if (!tmp) {
     silc_server_command_send_status_reply(cmd, SILC_COMMAND_PING,
-                                         SILC_STATUS_ERR_NO_SERVER_ID);
+                                         SILC_STATUS_ERR_NO_SERVER_ID, 0);
     goto out;
   }
-  id = silc_id_str2id(tmp, len, SILC_ID_SERVER);
-  if (!id)
+  server_id = silc_id_payload_parse_id(tmp, tmp_len, NULL);
+  if (!server_id)
     goto out;
 
-  if (SILC_ID_SERVER_COMPARE(id, server->id)) {
+  if (SILC_ID_SERVER_COMPARE(server_id, server->id)) {
     /* Send our reply */
     silc_server_command_send_status_reply(cmd, SILC_COMMAND_PING,
-                                         SILC_STATUS_OK);
+                                         SILC_STATUS_OK, 0);
   } else {
     silc_server_command_send_status_reply(cmd, SILC_COMMAND_PING,
-                                         SILC_STATUS_ERR_NO_SUCH_SERVER);
+                                         SILC_STATUS_ERR_NO_SUCH_SERVER, 0);
     goto out;
   }
 
-  silc_free(id);
-
  out:
+  silc_free(server_id);
   silc_server_command_free(cmd);
 }
 
@@ -2993,8 +1681,8 @@ SILC_SERVER_CMD_FUNC(stats)
   /* Get Server ID */
   tmp = silc_argument_get_arg_type(cmd->args, 1, &tmp_len);
   if (!tmp) {
-    silc_server_command_send_status_reply(cmd, SILC_COMMAND_INFO,
-                                         SILC_STATUS_ERR_NO_SERVER_ID);
+    silc_server_command_send_status_reply(cmd, SILC_COMMAND_PING,
+                                         SILC_STATUS_ERR_NO_SERVER_ID, 0);
     goto out;
   }
   server_id = silc_id_payload_parse_id(tmp, tmp_len, NULL);
@@ -3003,8 +1691,8 @@ SILC_SERVER_CMD_FUNC(stats)
 
   /* The ID must be ours */
   if (!SILC_ID_SERVER_COMPARE(server->id, server_id)) {
-    silc_server_command_send_status_reply(cmd, SILC_COMMAND_INFO,
-                                         SILC_STATUS_ERR_NO_SUCH_SERVER);
+    silc_server_command_send_status_reply(cmd, SILC_COMMAND_PING,
+                                         SILC_STATUS_ERR_NO_SUCH_SERVER, 0);
     silc_free(server_id);
     goto out;
   }
@@ -3021,7 +1709,7 @@ SILC_SERVER_CMD_FUNC(stats)
     packet = silc_command_payload_encode_va(SILC_COMMAND_STATS, 
                                            ++server->cmd_ident, 1,
                                            1, idp->data, idp->len);
-    silc_server_packet_send(server, server->router->connection,
+    silc_server_packet_send(server, SILC_PRIMARY_ROUTE(server),
                            SILC_PACKET_COMMAND, 0, packet->data,
                            packet->len, FALSE);
 
@@ -3091,13 +1779,16 @@ static void silc_server_command_join_channel(SilcServer server,
   unsigned char *passphrase = NULL, mode[4], tmp2[4], tmp3[4];
   SilcClientEntry client;
   SilcChannelClientEntry chl;
-  SilcBuffer reply, chidp, clidp, keyp = NULL, user_list, mode_list;
+  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;
+  SilcBuffer fkey = NULL;
+  const char *cipher;
 
-  SILC_LOG_DEBUG(("Start"));
+  SILC_LOG_DEBUG(("Joining client to channel"));
 
   if (!channel)
     return;
@@ -3105,27 +1796,45 @@ static void silc_server_command_join_channel(SilcServer server,
   /* Get the client entry */
   if (cmd->sock->type == SILC_SOCKET_TYPE_CLIENT) {
     client = (SilcClientEntry)sock->user_data;
+    if (!client)
+      return;
   } else {
-    client = silc_server_get_client_resolve(server, client_id, FALSE, 
-                                           &resolve);
+    client = silc_server_query_client(server, client_id, FALSE, 
+                                     &resolve);
     if (!client) {
-      if (cmd->pending)
-       goto out;
-
-      if (!resolve) {
+      if (!resolve || cmd->pending) {
        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;
       }
 
-      /* The client info is being resolved. Reprocess this packet after
-        receiving the reply to the query. */
-      silc_server_command_pending(server, SILC_COMMAND_WHOIS, 
+      /* 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_join, 
+                                 silc_server_command_dup(cmd));
+      cmd->pending = TRUE;
+      goto out;
+    }
+
+    if (auth && auth_len && !client->data.public_key) {
+      if (cmd->pending == 2)
+       goto out;
+
+      /* We must retrieve the detached 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, clidp->len);
+      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 = TRUE;
+      cmd->pending = 2;
       goto out;
     }
 
@@ -3139,21 +1848,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, 
                                     idata->public_key)) {
-      void *auth_data = (channel->founder_method == SILC_AUTH_PASSWORD ?
-                        (void *)channel->founder_passwd : 
-                        (void *)channel->founder_key);
-      SilcUInt32 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;
       }
@@ -3167,33 +1884,47 @@ 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),
+                  cmd->sock->hostname, strlen(cmd->sock->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),
+                  server->server_name, strlen(server->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),
+                  cmd->sock->hostname, strlen(cmd->sock->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_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, check) &&
+          !silc_server_inviteban_match(server, channel->invite_list,
+                                       1, check2))) {
        silc_server_command_send_status_reply(cmd, SILC_COMMAND_JOIN,
-                                             SILC_STATUS_ERR_NOT_INVITED);
+                                             SILC_STATUS_ERR_NOT_INVITED, 0);
        goto out;
       }
     }
@@ -3201,12 +1932,18 @@ 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)) {
+    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, check) ||
+         silc_server_inviteban_match(server, channel->ban_list,
+                                     1, check2)) {
        silc_server_command_send_status_reply(
                                      cmd, SILC_COMMAND_JOIN,
-                                     SILC_STATUS_ERR_BANNED_FROM_CHANNEL);
+                                     SILC_STATUS_ERR_BANNED_FROM_CHANNEL, 0);
        goto out;
       }
     }
@@ -3216,7 +1953,8 @@ static void silc_server_command_join_channel(SilcServer server,
       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);
+                                             SILC_STATUS_ERR_CHANNEL_IS_FULL,
+                                             0);
        goto out;
       }
     }
@@ -3232,7 +1970,7 @@ static void silc_server_command_join_channel(SilcServer server,
     if (!passphrase || !channel->passphrase ||
         memcmp(passphrase, channel->passphrase, strlen(channel->passphrase))) {
       silc_server_command_send_status_reply(cmd, SILC_COMMAND_JOIN,
-                                           SILC_STATUS_ERR_BAD_PASSWORD);
+                                           SILC_STATUS_ERR_BAD_PASSWORD, 0);
       goto out;
     }
   }
@@ -3244,7 +1982,7 @@ 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, NULL)) {
     silc_server_command_send_status_reply(cmd, SILC_COMMAND_JOIN,
-                                         SILC_STATUS_ERR_USER_ON_CHANNEL);
+                                         SILC_STATUS_ERR_USER_ON_CHANNEL, 0);
     goto out;
   }
 
@@ -3271,6 +2009,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,
@@ -3287,19 +2026,73 @@ static void silc_server_command_join_channel(SilcServer server,
 
   if (!(channel->mode & SILC_CHANNEL_MODE_PRIVKEY)) {
     tmp = silc_id_id2str(channel->id, SILC_ID_CHANNEL);
+    cipher = silc_cipher_get_name(channel->channel_key);
     keyp = silc_channel_key_payload_encode(silc_id_get_len(channel->id,
                                                           SILC_ID_CHANNEL), 
                                           tmp,
-                                          strlen(channel->channel_key->
-                                                 cipher->name),
-                                          channel->channel_key->cipher->name,
+                                          strlen(cipher), cipher,
                                           channel->key_len / 8, channel->key);
     silc_free(tmp);
   }
 
+  if (channel->founder_key)
+    fkey = silc_pkcs_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 **)&tmp_len, (void **)&reply)) {
+      if (tmp_len == 1)
+       invite_list = silc_argument_payload_encode_one(invite_list,
+                                                      (char *)reply,
+                                                      strlen((char *)reply),
+                                                      tmp_len);
+      else
+       invite_list = silc_argument_payload_encode_one(invite_list,
+                                                      reply->data,
+                                                      reply->len, tmp_len);
+    }
+    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 **)&tmp_len, (void **)&reply)) {
+      if (tmp_len == 1)
+       ban_list = silc_argument_payload_encode_one(ban_list,
+                                                   (char *)reply,
+                                                   strlen((char *)reply),
+                                                   tmp_len);
+      else
+       ban_list = silc_argument_payload_encode_one(ban_list,
+                                                   reply->data,
+                                                   reply->len, tmp_len);
+    }
+    silc_hash_table_list_reset(&htl);
+  }
+
   reply = 
     silc_command_reply_payload_encode_va(SILC_COMMAND_JOIN,
-                                        SILC_STATUS_OK, 0, ident, 13,
+                                        SILC_STATUS_OK, 0, ident, 14,
                                         2, channel->channel_name,
                                         strlen(channel->channel_name),
                                         3, chidp->data, chidp->len,
@@ -3308,12 +2101,11 @@ static void silc_server_command_join_channel(SilcServer server,
                                         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,
+                                        8, ban_list ? ban_list->data : NULL,
+                                        ban_list ? ban_list->len : 0,
+                                        9, invite_list ? invite_list->data :
+                                        NULL,
+                                        invite_list ? invite_list->len : 0,
                                         10, channel->topic,
                                         channel->topic ?
                                         strlen(channel->topic) : 0,
@@ -3323,7 +2115,9 @@ static void silc_server_command_join_channel(SilcServer server,
                                         12, tmp3, 4,
                                         13, user_list->data, user_list->len,
                                         14, mode_list->data, 
-                                        mode_list->len);
+                                        mode_list->len,
+                                        15, fkey ? fkey->data : NULL,
+                                        fkey ? fkey->len : 0);
 
   /* Send command reply */
   silc_server_packet_send(server, sock, SILC_PACKET_COMMAND_REPLY, 0, 
@@ -3335,48 +2129,59 @@ 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_LOG_DEBUG(("Send JOIN notify to channel"));
   silc_server_send_notify_to_channel(server, NULL, channel, FALSE, 
                                     SILC_NOTIFY_TYPE_JOIN, 2,
                                     clidp->data, clidp->len,
                                     chidp->data, chidp->len);
 
+  /* 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);
+    /* 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, 
+                                        SILC_NOTIFY_TYPE_CUMODE_CHANGE, 4,
+                                        clidp->data, clidp->len,
+                                        mode, 4, clidp->data, clidp->len,
+                                        fkey ? fkey->data : NULL,
+                                        fkey ? fkey->len : 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(invite_list);
+  silc_buffer_free(ban_list);
 
  out:
   silc_free(passphrase);
@@ -3403,7 +2208,8 @@ SILC_SERVER_CMD_FUNC(join)
   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;
@@ -3413,7 +2219,7 @@ SILC_SERVER_CMD_FUNC(join)
 
   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);
+                                         SILC_STATUS_ERR_BAD_CHANNEL, 0);
     goto out;
   }
 
@@ -3421,13 +2227,15 @@ SILC_SERVER_CMD_FUNC(join)
   tmp = silc_argument_get_arg_type(cmd->args, 2, &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;
   }
   client_id = silc_id_payload_parse_id(tmp, tmp_len, NULL);
   if (!client_id) {
     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;
   }
 
@@ -3442,27 +2250,39 @@ SILC_SERVER_CMD_FUNC(join)
 
   if (cmd->sock->type == SILC_SOCKET_TYPE_CLIENT) {
     SilcClientEntry entry = (SilcClientEntry)cmd->sock->user_data;
+    if (!entry) {
+      silc_server_command_send_status_reply(cmd, SILC_COMMAND_JOIN,
+                                           SILC_STATUS_ERR_NOT_ENOUGH_PARAMS,
+                                           0);
+      goto out;
+    }
+
+    silc_free(client_id);
     client_id = silc_id_dup(entry->id, SILC_ID_CLIENT);
 
-    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. */
+        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;
-       }
-       
-       umode = (SILC_CHANNEL_UMODE_CHANOP | SILC_CHANNEL_UMODE_CHANFO);
-       created = TRUE;
-       create_key = FALSE;
+         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,
+                                 0);
+           silc_free(client_id);
+           goto out;
+         }
        
+         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 
@@ -3476,8 +2296,10 @@ SILC_SERVER_CMD_FUNC(join)
          /* 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)
+         if (cmd->pending) {
+           silc_free(client_id);
            goto out;
+         }
          
          old_ident = silc_command_get_ident(cmd->payload);
          silc_command_set_ident(cmd->payload, ++server->cmd_ident);
@@ -3485,7 +2307,7 @@ SILC_SERVER_CMD_FUNC(join)
          
          /* Send JOIN command to our router */
          silc_server_packet_send(server, (SilcSocketConnection)
-                                 server->router->connection,
+                                 SILC_PRIMARY_ROUTE(server),
                                  SILC_PACKET_COMMAND, cmd->packet->flags,
                                  tmpbuf->data, tmpbuf->len, TRUE);
          
@@ -3497,6 +2319,7 @@ SILC_SERVER_CMD_FUNC(join)
          cmd->pending = TRUE;
           silc_command_set_ident(cmd->payload, old_ident);
          silc_buffer_free(tmpbuf);
+         silc_free(client_id);
          goto out;
        }
        
@@ -3509,8 +2332,10 @@ SILC_SERVER_CMD_FUNC(join)
          channel = silc_server_create_new_channel(server, server->id, cipher, 
                                                   hmac, channel_name, TRUE);
          if (!channel) {
-           silc_server_command_send_status_reply(cmd, SILC_COMMAND_JOIN,
-                                      SILC_STATUS_ERR_UNKNOWN_ALGORITHM);
+           silc_server_command_send_status_reply(
+                                      cmd, SILC_COMMAND_JOIN,
+                                      SILC_STATUS_ERR_UNKNOWN_ALGORITHM, 0);
+           silc_free(client_id);
            goto out;
          }
 
@@ -3528,8 +2353,10 @@ SILC_SERVER_CMD_FUNC(join)
         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 ||
-         server->server_type != SILC_ROUTER)
+         server->server_type != SILC_ROUTER) {
+       silc_free(client_id);
        goto out;
+      }
       
       /* We are router and the channel does not seem exist so we will check
         our global list as well for the channel. */
@@ -3540,8 +2367,10 @@ SILC_SERVER_CMD_FUNC(join)
        channel = silc_server_create_new_channel(server, server->id, cipher, 
                                                 hmac, channel_name, TRUE);
        if (!channel) {
-         silc_server_command_send_status_reply(cmd, SILC_COMMAND_JOIN,
-                                      SILC_STATUS_ERR_UNKNOWN_ALGORITHM);
+         silc_server_command_send_status_reply(
+                                      cmd, SILC_COMMAND_JOIN,
+                                      SILC_STATUS_ERR_UNKNOWN_ALGORITHM, 0);
+         silc_free(client_id);
          goto out;
        }
 
@@ -3561,16 +2390,29 @@ SILC_SERVER_CMD_FUNC(join)
       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 */
@@ -3602,7 +2444,7 @@ SILC_SERVER_CMD_FUNC(motd)
   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_NO_SUCH_SERVER, 0);
     goto out;
   }
 
@@ -3614,7 +2456,8 @@ 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);
+      motd = silc_file_readfile(server->config->server_info->motd_file,
+                               &motd_len);
       if (!motd)
        goto out;
       
@@ -3681,7 +2524,7 @@ SILC_SERVER_CMD_FUNC(motd)
       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);
 
@@ -3697,8 +2540,8 @@ SILC_SERVER_CMD_FUNC(motd)
     }
 
     if (!entry) {
-      silc_server_command_send_status_reply(cmd, SILC_COMMAND_INFO,
-                                           SILC_STATUS_ERR_NO_SUCH_SERVER);
+      silc_server_command_send_status_reply(cmd, SILC_COMMAND_MOTD,
+                                           SILC_STATUS_ERR_NO_SUCH_SERVER, 0);
       goto out;
     }
 
@@ -3734,7 +2577,7 @@ SILC_SERVER_CMD_FUNC(umode)
   SilcUInt16 ident = silc_command_get_ident(cmd->payload);
   bool set_mask = FALSE;
 
-  if (cmd->sock->type != SILC_SOCKET_TYPE_CLIENT)
+  if (cmd->sock->type != SILC_SOCKET_TYPE_CLIENT || !client)
     goto out;
 
   SILC_SERVER_COMMAND_CHECK(SILC_COMMAND_UMODE, cmd, 1, 2);
@@ -3750,7 +2593,7 @@ SILC_SERVER_CMD_FUNC(umode)
     /* 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);
+                                           SILC_STATUS_ERR_PERM_DENIED, 0);
       goto out;
     }
 
@@ -3758,24 +2601,33 @@ SILC_SERVER_CMD_FUNC(umode)
     if (mask & SILC_UMODE_ANONYMOUS) {
       if (!(client->mode & SILC_UMODE_ANONYMOUS)) {
        silc_server_command_send_status_reply(cmd, SILC_COMMAND_UMODE,
-                                             SILC_STATUS_ERR_PERM_DENIED);
+                                             SILC_STATUS_ERR_PERM_DENIED, 0);
        goto out;
       }
     } else {
       if (client->mode & SILC_UMODE_ANONYMOUS) {
        silc_server_command_send_status_reply(cmd, SILC_COMMAND_UMODE,
-                                             SILC_STATUS_ERR_PERM_DENIED);
+                                             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--;
+    }
+
     /* Change the mode */
     client->mode = mask;
 
     /* Send UMODE change to primary router */
-    if (!server->standalone)
-      silc_server_send_notify_umode(server, server->router->connection, TRUE,
-                                   client->id, client->mode);
+    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)
@@ -3810,31 +2662,33 @@ SILC_SERVER_CMD_FUNC(cmode)
   SilcBuffer packet, cidp;
   unsigned char *tmp, *tmp_id, *tmp_mask;
   char *cipher = NULL, *hmac = NULL, *passphrase = NULL;
-  SilcUInt32 mode_mask = 0, tmp_len, tmp_len2;
+  SilcUInt32 mode_mask = 0, old_mask = 0, tmp_len, tmp_len2;
   SilcUInt16 ident = silc_command_get_ident(cmd->payload);
   bool set_mask = FALSE;
+  SilcPublicKey founder_key = NULL;
+  SilcBuffer fkey = NULL;
+
+  if (!client) {
+    silc_server_command_free(cmd);
+    return;
+  }
 
-  SILC_SERVER_COMMAND_CHECK(SILC_COMMAND_CMODE, cmd, 1, 7);
+  SILC_SERVER_COMMAND_CHECK(SILC_COMMAND_CMODE, cmd, 1, 8);
 
   /* 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);
-    goto out;
+                                         SILC_STATUS_ERR_NO_CHANNEL_ID, 0);
+    silc_server_command_free(cmd);
+    return;
   }
   channel_id = silc_id_payload_parse_id(tmp_id, tmp_len2, NULL);
   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_GET32_MSB(mode_mask, tmp_mask);
-    set_mask = TRUE;
+                                         SILC_STATUS_ERR_NO_CHANNEL_ID, 0);
+    silc_server_command_free(cmd);
+    return;
   }
 
   /* Get channel entry */
@@ -3845,26 +2699,38 @@ SILC_SERVER_CMD_FUNC(cmode)
                                             channel_id, NULL);
     if (!channel) {
       silc_server_command_send_status_reply(cmd, SILC_COMMAND_CMODE,
-                                           SILC_STATUS_ERR_NO_SUCH_CHANNEL);
-      goto out;
+                                           SILC_STATUS_ERR_NO_SUCH_CHANNEL,
+                                           0);
+      silc_free(channel_id);
+      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)) {
     silc_server_command_send_status_reply(cmd, SILC_COMMAND_CMODE,
-                                         SILC_STATUS_ERR_NOT_ON_CHANNEL);
+                                         SILC_STATUS_ERR_NOT_ON_CHANNEL, 0);
     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"));
     silc_server_command_send_status_reply(
                             cmd, SILC_COMMAND_CMODE,
                             (!(chl->mode & SILC_CHANNEL_UMODE_CHANOP) ? 
                              SILC_STATUS_ERR_NO_CHANNEL_PRIV :
-                             SILC_STATUS_ERR_NO_CHANNEL_FOPRIV));
+                             SILC_STATUS_ERR_NO_CHANNEL_FOPRIV), 0);
     goto out;
   }
 
@@ -3907,8 +2773,8 @@ SILC_SERVER_CMD_FUNC(cmode)
       silc_server_send_channel_key(server, NULL, channel, 
                                   server->server_type == SILC_ROUTER ? 
                                   FALSE : !server->standalone);
-       
-      cipher = channel->channel_key->cipher->name;
+
+      cipher = (char *)silc_cipher_get_name(channel->channel_key);
       hmac = (char *)silc_hmac_get_name(channel->hmac);
     }
   }
@@ -3922,7 +2788,7 @@ SILC_SERVER_CMD_FUNC(cmode)
     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);
+                                  SILC_STATUS_ERR_NOT_ENOUGH_PARAMS, 0);
        goto out;
       }
     } else {
@@ -3943,7 +2809,7 @@ SILC_SERVER_CMD_FUNC(cmode)
       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);
+                                  SILC_STATUS_ERR_NOT_ENOUGH_PARAMS, 0);
        goto out;
       }
 
@@ -3967,14 +2833,14 @@ SILC_SERVER_CMD_FUNC(cmode)
       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);
+                                  SILC_STATUS_ERR_NOT_ENOUGH_PARAMS, 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);
+                                      SILC_STATUS_ERR_UNKNOWN_ALGORITHM, 0);
        goto out;
       }
 
@@ -4007,7 +2873,7 @@ SILC_SERVER_CMD_FUNC(cmode)
       /* 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);
+                                  SILC_STATUS_ERR_UNKNOWN_ALGORITHM, 0);
        goto out;
       }
 
@@ -4042,14 +2908,14 @@ SILC_SERVER_CMD_FUNC(cmode)
       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);
+                                  SILC_STATUS_ERR_NOT_ENOUGH_PARAMS, 0);
        goto out;
       }
 
       /* 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);
+                                      SILC_STATUS_ERR_UNKNOWN_ALGORITHM, 0);
        goto out;
       }
 
@@ -4076,7 +2942,7 @@ SILC_SERVER_CMD_FUNC(cmode)
       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);
+                                      SILC_STATUS_ERR_UNKNOWN_ALGORITHM, 0);
        goto out;
       }
 
@@ -4096,96 +2962,115 @@ SILC_SERVER_CMD_FUNC(cmode)
 
   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) {
+      /* 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_pkcs_public_key_payload_decode(tmp, tmp_len, &founder_key)) {
          silc_server_command_send_status_reply(cmd, SILC_COMMAND_CMODE,
-                                    SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
+                                               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;
+      }
 
-       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;
-       }
+      /* 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;
+      }
 
-       /* 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_memdup(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;
-         }
-       }
+      /* 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;
+      }
 
-       silc_auth_payload_free(auth);
+      fkey = silc_pkcs_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;
       }
+    has_founder:
     }
   } 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;
-       }
+       channel->founder_key = NULL;
       }
     }
   }
 
   /* Finally, set the mode */
-  channel->mode = mode_mask;
+  old_mask = channel->mode = mode_mask;
 
   /* 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,
+                                    SILC_NOTIFY_TYPE_CMODE_CHANGE, 6,
                                     cidp->data, cidp->len, 
                                     tmp_mask, 4,
                                     cipher, cipher ? strlen(cipher) : 0,
                                     hmac, hmac ? strlen(hmac) : 0,
                                     passphrase, passphrase ? 
-                                    strlen(passphrase) : 0);
+                                    strlen(passphrase) : 0,
+                                    fkey ? fkey->data : NULL,
+                                    fkey ? fkey->len : 0);
 
   /* 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);
+  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);
 
   /* Send command reply to sender */
   packet = silc_command_reply_payload_encode_va(SILC_COMMAND_CMODE,
-                                               SILC_STATUS_OK, 0, ident, 2,
+                                               SILC_STATUS_OK, 0, ident, 3,
                                                2, tmp_id, tmp_len2,
-                                               3, tmp_mask, 4);
+                                               3, tmp_mask, 4,
+                                               4, fkey ? fkey->data : NULL,
+                                               fkey ? fkey->len : 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(cidp);
 
  out:
+  channel->mode = old_mask;
+  silc_buffer_free(fkey);
   silc_free(channel_id);
   silc_server_command_free(cmd);
 }
@@ -4197,9 +3082,8 @@ 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;
-  SilcClientID *client_id;
+  SilcChannelID *channel_id = NULL;
+  SilcClientID *client_id = NULL;
   SilcChannelEntry channel;
   SilcClientEntry target_client;
   SilcChannelClientEntry chl;
@@ -4208,6 +3092,11 @@ SILC_SERVER_CMD_FUNC(cumode)
   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;
+
+  if (!client)
+    goto out;
 
   SILC_SERVER_COMMAND_CHECK(SILC_COMMAND_CUMODE, cmd, 3, 4);
 
@@ -4215,13 +3104,13 @@ SILC_SERVER_CMD_FUNC(cumode)
   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_STATUS_ERR_NO_CHANNEL_ID, 0);
     goto out;
   }
   channel_id = silc_id_payload_parse_id(tmp_ch_id, tmp_ch_len, NULL);
   if (!channel_id) {
     silc_server_command_send_status_reply(cmd, SILC_COMMAND_CUMODE,
-                                         SILC_STATUS_ERR_NO_CHANNEL_ID);
+                                         SILC_STATUS_ERR_NO_CHANNEL_ID, 0);
     goto out;
   }
 
@@ -4233,7 +3122,8 @@ SILC_SERVER_CMD_FUNC(cumode)
                                             channel_id, NULL);
     if (!channel) {
       silc_server_command_send_status_reply(cmd, SILC_COMMAND_CUMODE,
-                                           SILC_STATUS_ERR_NO_SUCH_CHANNEL);
+                                           SILC_STATUS_ERR_NO_SUCH_CHANNEL,
+                                           0);
       goto out;
     }
   }
@@ -4241,7 +3131,7 @@ SILC_SERVER_CMD_FUNC(cumode)
   /* Check whether sender is on the channel */
   if (!silc_server_client_on_channel(client, channel, &chl)) {
     silc_server_command_send_status_reply(cmd, SILC_COMMAND_CUMODE,
-                                         SILC_STATUS_ERR_NOT_ON_CHANNEL);
+                                         SILC_STATUS_ERR_NOT_ON_CHANNEL, 0);
     goto out;
   }
   sender_mask = chl->mode;
@@ -4250,7 +3140,8 @@ SILC_SERVER_CMD_FUNC(cumode)
   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);
+                                         SILC_STATUS_ERR_NOT_ENOUGH_PARAMS,
+                                         0);
     goto out;
   }
   SILC_GET32_MSB(target_mask, tmp_mask);
@@ -4259,13 +3150,13 @@ SILC_SERVER_CMD_FUNC(cumode)
   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);
+                                         SILC_STATUS_ERR_NO_CLIENT_ID, 0);
     goto out;
   }
   client_id = silc_id_payload_parse_id(tmp_id, tmp_len, NULL);
   if (!client_id) {
     silc_server_command_send_status_reply(cmd, SILC_COMMAND_CUMODE,
-                                         SILC_STATUS_ERR_NO_CLIENT_ID);
+                                         SILC_STATUS_ERR_NO_CLIENT_ID, 0);
     goto out;
   }
 
@@ -4281,7 +3172,7 @@ SILC_SERVER_CMD_FUNC(cumode)
       !(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);
+                                         SILC_STATUS_ERR_NO_CHANNEL_PRIV, 0);
     goto out;
   }
 
@@ -4289,7 +3180,7 @@ SILC_SERVER_CMD_FUNC(cumode)
   if (target_client != client) {
     if (!silc_server_client_on_channel(target_client, channel, &chl)) {
       silc_server_command_send_status_reply(cmd, SILC_COMMAND_CUMODE,
-                                SILC_STATUS_ERR_USER_NOT_ON_CHANNEL);
+                                SILC_STATUS_ERR_USER_NOT_ON_CHANNEL, 0);
       goto out;
     }
   }
@@ -4302,54 +3193,70 @@ SILC_SERVER_CMD_FUNC(cumode)
      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_FOPRIV);
+                                         SILC_STATUS_ERR_NO_CHANNEL_FOPRIV,
+                                         0);
     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_NOT_YOU);
+                                           SILC_STATUS_ERR_NOT_YOU, 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, auth_len;
-      void *auth;
-      
+      SilcUInt32 tmp_auth_len;
+      SilcChannelClientEntry chl2;
+      SilcHashTableList htl;
+
       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)) {
+         !channel->founder_key) {
        silc_server_command_send_status_reply(cmd, SILC_COMMAND_CUMODE,
-                                             SILC_STATUS_ERR_NOT_YOU);
+                                             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_NOT_ENOUGH_PARAMS);
+                                             SILC_STATUS_ERR_AUTH_FAILED, 0);
        goto out;
       }
 
-      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)) {
+      /* 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);
+                                             SILC_STATUS_ERR_AUTH_FAILED, 0);
        goto out;
       }
-      
-      sender_mask = chl->mode |= SILC_CHANNEL_UMODE_CHANFO;
+
       notify = TRUE;
+      founder_key = channel->founder_key;
+      fkey = silc_pkcs_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 */
+      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 (chl->mode & SILC_CHANNEL_UMODE_CHANFO) {
@@ -4359,7 +3266,7 @@ SILC_SERVER_CMD_FUNC(cumode)
        notify = TRUE;
       } else {
        silc_server_command_send_status_reply(cmd, SILC_COMMAND_CUMODE,
-                                             SILC_STATUS_ERR_NOT_YOU);
+                                             SILC_STATUS_ERR_NOT_YOU, 0);
        goto out;
       }
     }
@@ -4368,11 +3275,12 @@ SILC_SERVER_CMD_FUNC(cumode)
   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;
+      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,
+                                              0);
+        goto out;
       }
 
       chl->mode |= SILC_CHANNEL_UMODE_CHANOP;
@@ -4381,12 +3289,13 @@ SILC_SERVER_CMD_FUNC(cumode)
   } 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;
+          !(sender_mask & SILC_CHANNEL_UMODE_CHANFO)) {
+        silc_server_command_send_status_reply(cmd, SILC_COMMAND_CUMODE,   
+                                              SILC_STATUS_ERR_NO_CHANNEL_PRIV,
+                                              0);
+        goto out;
       }
-
+      
       /* Demote to normal user */
       chl->mode &= ~SILC_CHANNEL_UMODE_CHANOP;
       notify = TRUE;
@@ -4396,7 +3305,7 @@ SILC_SERVER_CMD_FUNC(cumode)
   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);
+                                           SILC_STATUS_ERR_NOT_YOU, 0);
       goto out;
     }
 
@@ -4408,7 +3317,7 @@ SILC_SERVER_CMD_FUNC(cumode)
     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);
+                                             SILC_STATUS_ERR_NOT_YOU, 0);
        goto out;
       }
 
@@ -4420,7 +3329,7 @@ SILC_SERVER_CMD_FUNC(cumode)
   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);
+                                           SILC_STATUS_ERR_NOT_YOU, 0);
       goto out;
     }
 
@@ -4432,7 +3341,7 @@ SILC_SERVER_CMD_FUNC(cumode)
     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);
+                                             SILC_STATUS_ERR_NOT_YOU, 0);
        goto out;
       }
 
@@ -4444,7 +3353,7 @@ SILC_SERVER_CMD_FUNC(cumode)
   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);
+                                           SILC_STATUS_ERR_NOT_YOU, 0);
       goto out;
     }
 
@@ -4456,7 +3365,7 @@ SILC_SERVER_CMD_FUNC(cumode)
     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);
+                                             SILC_STATUS_ERR_NOT_YOU, 0);
        goto out;
       }
 
@@ -4465,25 +3374,46 @@ SILC_SERVER_CMD_FUNC(cumode)
     }
   }
 
+  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 (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;
+    }
+  }
+
   idp = silc_id_payload_encode(client->id, SILC_ID_CLIENT);
   tmp_id = silc_argument_get_arg_type(cmd->args, 3, &tmp_len);
 
   /* 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,
+                                      SILC_NOTIFY_TYPE_CUMODE_CHANGE, 4,
                                       idp->data, idp->len,
                                       tmp_mask, 4, 
-                                      tmp_id, tmp_len);
+                                      tmp_id, tmp_len,
+                                      fkey ? fkey->data : NULL,
+                                      fkey ? fkey->len : 0);
 
     /* 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);
+    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);
   }
 
   /* Send command reply to sender */
@@ -4496,11 +3426,12 @@ SILC_SERVER_CMD_FUNC(cumode)
                          packet->data, packet->len, FALSE);
     
   silc_buffer_free(packet);
-  silc_free(channel_id);
-  silc_free(client_id);
   silc_buffer_free(idp);
 
  out:
+  silc_free(channel_id);
+  silc_free(client_id);
+  silc_buffer_free(fkey);
   silc_server_command_free(cmd);
 }
 
@@ -4520,19 +3451,22 @@ SILC_SERVER_CMD_FUNC(kick)
   SilcUInt32 tmp_len, target_idp_len;
   unsigned char *tmp, *comment, *target_idp;
 
+  if (!client)
+    goto out;
+
   SILC_SERVER_COMMAND_CHECK(SILC_COMMAND_LEAVE, 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);
+                                         SILC_STATUS_ERR_NO_CHANNEL_ID, 0);
     goto out;
   }
   channel_id = silc_id_payload_parse_id(tmp, tmp_len, NULL);
   if (!channel_id) {
     silc_server_command_send_status_reply(cmd, SILC_COMMAND_KICK,
-                                         SILC_STATUS_ERR_NO_CHANNEL_ID);
+                                         SILC_STATUS_ERR_NO_CHANNEL_ID, 0);
     goto out;
   }
 
@@ -4544,7 +3478,8 @@ SILC_SERVER_CMD_FUNC(kick)
                                             channel_id, NULL);
     if (!channel) {
       silc_server_command_send_status_reply(cmd, SILC_COMMAND_KICK,
-                                           SILC_STATUS_ERR_NO_SUCH_CHANNEL);
+                                           SILC_STATUS_ERR_NO_SUCH_CHANNEL,
+                                           0);
       goto out;
     }
   }
@@ -4552,7 +3487,7 @@ SILC_SERVER_CMD_FUNC(kick)
   /* Check whether sender is on the channel */
   if (!silc_server_client_on_channel(client, channel, &chl)) {
     silc_server_command_send_status_reply(cmd, SILC_COMMAND_KICK,
-                                         SILC_STATUS_ERR_NOT_ON_CHANNEL);
+                                         SILC_STATUS_ERR_NOT_ON_CHANNEL, 0);
     goto out;
   }
 
@@ -4560,7 +3495,7 @@ SILC_SERVER_CMD_FUNC(kick)
   if (!(chl->mode & SILC_CHANNEL_UMODE_CHANOP) &&
       !(chl->mode & SILC_CHANNEL_UMODE_CHANFO)) {
     silc_server_command_send_status_reply(cmd, SILC_COMMAND_KICK,
-                                         SILC_STATUS_ERR_NO_CHANNEL_PRIV);
+                                         SILC_STATUS_ERR_NO_CHANNEL_PRIV, 0);
     goto out;
   }
   
@@ -4568,13 +3503,13 @@ SILC_SERVER_CMD_FUNC(kick)
   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);
+                                         SILC_STATUS_ERR_NO_CLIENT_ID, 0);
     goto out;
   }
   client_id = silc_id_payload_parse_id(target_idp, target_idp_len, NULL);
   if (!client_id) {
     silc_server_command_send_status_reply(cmd, SILC_COMMAND_KICK,
-                                         SILC_STATUS_ERR_NO_CLIENT_ID);
+                                         SILC_STATUS_ERR_NO_CLIENT_ID, 0);
     goto out;
   }
 
@@ -4589,7 +3524,8 @@ SILC_SERVER_CMD_FUNC(kick)
   /* Check whether target client is on the channel */
   if (!silc_server_client_on_channel(target_client, channel, &chl)) {
     silc_server_command_send_status_reply(cmd, SILC_COMMAND_KICK,
-                                         SILC_STATUS_ERR_USER_NOT_ON_CHANNEL);
+                                         SILC_STATUS_ERR_USER_NOT_ON_CHANNEL,
+                                         0);
     goto out;
   }
 
@@ -4597,7 +3533,8 @@ SILC_SERVER_CMD_FUNC(kick)
      cannot be kicked from the channel. */
   if (chl->mode & SILC_CHANNEL_UMODE_CHANFO) {
     silc_server_command_send_status_reply(cmd, SILC_COMMAND_KICK,
-                                         SILC_STATUS_ERR_NO_CHANNEL_FOPRIV);
+                                         SILC_STATUS_ERR_NO_CHANNEL_FOPRIV,
+                                         0);
     goto out;
   }
   
@@ -4609,7 +3546,7 @@ SILC_SERVER_CMD_FUNC(kick)
 
   /* Send command reply to sender */
   silc_server_command_send_status_reply(cmd, SILC_COMMAND_KICK, 
-                                       SILC_STATUS_OK);
+                                       SILC_STATUS_OK, 0);
 
   /* Send KICKED notify to local clients on the channel */
   idp = silc_id_payload_encode(client->id, SILC_ID_CLIENT);
@@ -4620,6 +3557,22 @@ SILC_SERVER_CMD_FUNC(kick)
                                     idp->data, idp->len);
   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, ab->len, 1);
+    silc_server_inviteban_process(server, channel->invite_list, 1, args);
+    silc_buffer_free(ab);
+    silc_argument_payload_free(args);
+  }
+
   /* 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. */
@@ -4627,13 +3580,6 @@ SILC_SERVER_CMD_FUNC(kick)
                                           target_client, FALSE))
     goto out;
 
-  /* 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);
-
   if (!(channel->mode & SILC_CHANNEL_MODE_PRIVKEY)) {
     /* Re-generate channel key */
     if (!silc_server_create_channel_key(server, channel, 0))
@@ -4665,16 +3611,17 @@ SILC_SERVER_CMD_FUNC(oper)
   bool result = FALSE;
   SilcPublicKey cached_key;
 
-  SILC_SERVER_COMMAND_CHECK(SILC_COMMAND_OPER, cmd, 1, 2);
-
-  if (!client || cmd->sock->type != SILC_SOCKET_TYPE_CLIENT)
+  if (cmd->sock->type != SILC_SOCKET_TYPE_CLIENT || !client)
     goto out;
 
+  SILC_SERVER_COMMAND_CHECK(SILC_COMMAND_OPER, cmd, 1, 2);
+
   /* 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);
+                                         SILC_STATUS_ERR_NOT_ENOUGH_PARAMS,
+                                         0);
     goto out;
   }
 
@@ -4686,7 +3633,11 @@ SILC_SERVER_CMD_FUNC(oper)
                                          username, client->nickname);
     if (!admin) {
       silc_server_command_send_status_reply(cmd, SILC_COMMAND_OPER,
-                                           SILC_STATUS_ERR_AUTH_FAILED);
+                                           SILC_STATUS_ERR_AUTH_FAILED,
+                                           0);
+      SILC_LOG_INFO(("OPER authentication failed for username '%s' by "
+                    "nickname '%s' from %s", username,
+                    client->nickname, cmd->sock->hostname));
       goto out;
     }
   }
@@ -4695,7 +3646,8 @@ SILC_SERVER_CMD_FUNC(oper)
   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);
+                                         SILC_STATUS_ERR_NOT_ENOUGH_PARAMS,
+                                         0);
     goto out;
   }
 
@@ -4716,7 +3668,8 @@ SILC_SERVER_CMD_FUNC(oper)
   if (!result) {
     /* Authentication failed */
     silc_server_command_send_status_reply(cmd, SILC_COMMAND_OPER,
-                                         SILC_STATUS_ERR_AUTH_FAILED);
+                                         SILC_STATUS_ERR_AUTH_FAILED,
+                                         0);
     goto out;
   }
 
@@ -4724,15 +3677,15 @@ SILC_SERVER_CMD_FUNC(oper)
   client->mode |= SILC_UMODE_SERVER_OPERATOR;
 
   /* Update statistics */
-  if (client->connection)
+  if (SILC_IS_LOCAL(client))
     server->stat.my_server_ops++;
   if (server->server_type == SILC_ROUTER)
     server->stat.server_ops++;
 
   /* Send UMODE change to primary router */
-  if (!server->standalone)
-    silc_server_send_notify_umode(server, server->router->connection, TRUE,
-                                 client->id, client->mode);
+  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)
@@ -4741,7 +3694,7 @@ SILC_SERVER_CMD_FUNC(oper)
 
   /* Send reply to the sender */
   silc_server_command_send_status_reply(cmd, SILC_COMMAND_OPER,
-                                       SILC_STATUS_OK);
+                                       SILC_STATUS_OK, 0);
 
  out:
   silc_server_command_free(cmd);
@@ -4749,32 +3702,48 @@ SILC_SERVER_CMD_FUNC(oper)
 
 SILC_TASK_CALLBACK(silc_server_command_detach_cb)
 {
+  SilcServer server = app_context;
   QuitInternal q = (QuitInternal)context;
-  SilcClientEntry client = (SilcClientEntry)q->sock->user_data;
+  SilcClientID *client_id = (SilcClientID *)q->sock;
+  SilcClientEntry client;
+  SilcSocketConnection sock;
 
-  /* If there is pending outgoing data for the client then purge it
-     to the network before closing connection. */
-  silc_server_packet_queue_purge(q->server, q->sock);
+  client = silc_idlist_find_client_by_id(server->local_list, client_id,
+                                        TRUE, NULL);
+  if (client && client->connection) {
+    sock = client->connection;
 
-  /* Close the connection on our side */
-  client->router = NULL;
-  client->connection = NULL;
-  q->sock->user_data = NULL;
-  silc_server_close_connection(q->server, q->sock);
+    /* If there is pending outgoing data for the client then purge it
+       to the network before closing connection. */
+    silc_server_packet_queue_purge(server, sock);
+
+    /* Close the connection on our side */
+    client->router = NULL;
+    client->connection = NULL;
+    sock->user_data = NULL;
+    silc_server_close_connection(server, sock);
+  }
 
+  silc_free(client_id);
   silc_free(q);
 }
 
 SILC_TASK_CALLBACK(silc_server_command_detach_timeout)
 {
+  SilcServer server = app_context;
   QuitInternal q = (QuitInternal)context;
-  SilcClientEntry client = (SilcClientEntry)q->sock;
-
-  SILC_LOG_DEBUG(("Start"));
+  SilcClientID *client_id = (SilcClientID *)q->sock;
+  SilcClientEntry client;
 
-  if (client->mode & SILC_UMODE_DETACHED)
-    silc_server_free_client_data(q->server, NULL, client, TRUE,
+  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");
+  }
+
+  silc_free(client_id);
   silc_free(q);
 }
 
@@ -4790,22 +3759,31 @@ SILC_SERVER_CMD_FUNC(detach)
 
   if (server->config->detach_disabled) {
     silc_server_command_send_status_reply(cmd, SILC_COMMAND_DETACH,
-                                         SILC_STATUS_ERR_UNKNOWN_COMMAND);
+                                         SILC_STATUS_ERR_OPERATION_ALLOWED,
+                                         0);
     goto out;
   }
 
-  if (cmd->sock->type != SILC_SOCKET_TYPE_CLIENT)
+  if (cmd->sock->type != SILC_SOCKET_TYPE_CLIENT || !client)
     goto out;
 
   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;
-  if (!server->standalone)
-    silc_server_send_notify_umode(server, server->router->connection,
-                                 server->server_type == SILC_SERVER ?
-                                 FALSE : TRUE, client->id, client->mode);
+  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)
@@ -4813,15 +3791,13 @@ SILC_SERVER_CMD_FUNC(detach)
                                   SILC_NOTIFY_TYPE_UMODE_CHANGE);
 
   q = silc_calloc(1, sizeof(*q));
-  q->server = server;
-  q->sock = cmd->sock;
+  q->sock = silc_id_dup(client->id, SILC_ID_CLIENT);
   silc_schedule_task_add(server->schedule, 0, silc_server_command_detach_cb,
                         q, 0, 200000, SILC_TASK_TIMEOUT, SILC_TASK_PRI_LOW);
 
   if (server->config->detach_timeout) {
     q = silc_calloc(1, sizeof(*q));
-    q->server = server;
-    q->sock = (void *)client;
+    q->sock = silc_id_dup(client->id, SILC_ID_CLIENT);
     silc_schedule_task_add(server->schedule, 0, 
                           silc_server_command_detach_timeout,
                           q, server->config->detach_timeout * 60,
@@ -4830,7 +3806,7 @@ SILC_SERVER_CMD_FUNC(detach)
 
   /* Send reply to the sender */
   silc_server_command_send_status_reply(cmd, SILC_COMMAND_DETACH,
-                                       SILC_STATUS_OK);
+                                       SILC_STATUS_OK, 0);
 
  out:
   silc_server_command_free(cmd);
@@ -4851,17 +3827,19 @@ SILC_SERVER_CMD_FUNC(watch)
 
   SILC_SERVER_COMMAND_CHECK(SILC_COMMAND_WATCH, cmd, 1, 3);
 
-  if (server->server_type == SILC_SERVER && !server->standalone) {
+  if (server->server_type != SILC_ROUTER && !server->standalone) {
     if (!cmd->pending) {
       /* Send the command to router */
       SilcBuffer tmpbuf;
       SilcUInt16 old_ident;
 
+      SILC_LOG_DEBUG(("Forwarding WATCH to router"));
+
       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);
 
@@ -4877,8 +3855,11 @@ SILC_SERVER_CMD_FUNC(watch)
       /* Received reply from router, just send same data to the client. */
       SilcServerCommandReplyContext reply = context2;
       SilcStatus status;
+
+      SILC_LOG_DEBUG(("Received reply to WATCH from router"));
       silc_command_get_status(reply->payload, &status, NULL);
-      silc_server_command_send_status_reply(cmd, SILC_COMMAND_WATCH, status);
+      silc_server_command_send_status_reply(cmd, SILC_COMMAND_WATCH, status,
+                                           0);
     }
 
     goto out;
@@ -4890,13 +3871,15 @@ SILC_SERVER_CMD_FUNC(watch)
   tmp = silc_argument_get_arg_type(cmd->args, 1, &tmp_len);
   if (!tmp) {
     silc_server_command_send_status_reply(cmd, SILC_COMMAND_WATCH,
-                                         SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
+                                         SILC_STATUS_ERR_NOT_ENOUGH_PARAMS,
+                                         0);
     goto out;
   }
   client_id = silc_id_payload_parse_id(tmp, tmp_len, NULL);
   if (!client_id) {
     silc_server_command_send_status_reply(cmd, SILC_COMMAND_WATCH,
-                                         SILC_STATUS_ERR_NO_SUCH_CLIENT_ID);
+                                         SILC_STATUS_ERR_NO_SUCH_CLIENT_ID,
+                                         0);
     goto out;
   }
 
@@ -4905,7 +3888,8 @@ SILC_SERVER_CMD_FUNC(watch)
                                         client_id, TRUE, NULL);
   if (!client) {
     silc_server_command_send_status_reply(cmd, SILC_COMMAND_WATCH,
-                                         SILC_STATUS_ERR_NO_SUCH_CLIENT_ID);
+                                         SILC_STATUS_ERR_NO_SUCH_CLIENT_ID,
+                                         0);
     goto out;
   }
 
@@ -4914,7 +3898,8 @@ SILC_SERVER_CMD_FUNC(watch)
   del_nick = silc_argument_get_arg_type(cmd->args, 3, &del_nick_len);
   if (!add_nick && !del_nick) {
     silc_server_command_send_status_reply(cmd, SILC_COMMAND_WATCH,
-                                         SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
+                                         SILC_STATUS_ERR_NOT_ENOUGH_PARAMS,
+                                         0);
     goto out;
   }
 
@@ -4929,7 +3914,7 @@ SILC_SERVER_CMD_FUNC(watch)
   if (add_nick) {
     if (silc_server_name_bad_chars(add_nick, strlen(add_nick)) == TRUE) {
       silc_server_command_send_status_reply(cmd, SILC_COMMAND_WATCH,
-                                           SILC_STATUS_ERR_BAD_NICKNAME);
+                                           SILC_STATUS_ERR_BAD_NICKNAME, 0);
       goto out;
     }
 
@@ -4943,7 +3928,8 @@ SILC_SERVER_CMD_FUNC(watch)
                                        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);
+                                           SILC_STATUS_ERR_NICKNAME_IN_USE,
+                                           0);
       goto out;
     }
 
@@ -4960,7 +3946,7 @@ SILC_SERVER_CMD_FUNC(watch)
   if (del_nick) {
     if (silc_server_name_bad_chars(del_nick, strlen(del_nick)) == TRUE) {
       silc_server_command_send_status_reply(cmd, SILC_COMMAND_WATCH,
-                                           SILC_STATUS_ERR_BAD_NICKNAME);
+                                           SILC_STATUS_ERR_BAD_NICKNAME, 0);
       goto out;
     }
 
@@ -4974,7 +3960,7 @@ SILC_SERVER_CMD_FUNC(watch)
                                         client, (void **)&tmp)) {
       /* Nickname is alredy being watched for this client */
       silc_server_command_send_status_reply(cmd, SILC_COMMAND_WATCH,
-                                           SILC_STATUS_ERR_NO_SUCH_NICK);
+                                           SILC_STATUS_ERR_NO_SUCH_NICK, 0);
       goto out;
     }
 
@@ -4987,8 +3973,19 @@ SILC_SERVER_CMD_FUNC(watch)
       silc_free(tmp);
   }
 
+  /* Distribute the watch list to backup routers too */
+  if (server->backup) {
+    SilcBuffer tmpbuf;
+    silc_command_set_ident(cmd->payload, ++server->cmd_ident);
+    tmpbuf = silc_command_payload_encode_payload(cmd->payload);
+    silc_server_backup_send(server, NULL, SILC_PACKET_COMMAND,
+                           cmd->packet->flags, tmpbuf->data, tmpbuf->len,
+                           FALSE, TRUE);
+    silc_buffer_free(tmpbuf);
+  }
+
   silc_server_command_send_status_reply(cmd, SILC_COMMAND_WATCH,
-                                       SILC_STATUS_OK);
+                                       SILC_STATUS_OK, 0);
 
  out:
   silc_free(client_id);
@@ -5010,14 +4007,14 @@ SILC_SERVER_CMD_FUNC(silcoper)
   bool result = FALSE;
   SilcPublicKey cached_key;
 
-  SILC_SERVER_COMMAND_CHECK(SILC_COMMAND_SILCOPER, cmd, 1, 2);
-
-  if (!client || cmd->sock->type != SILC_SOCKET_TYPE_CLIENT)
+  if (cmd->sock->type != SILC_SOCKET_TYPE_CLIENT || !client)
     goto out;
 
+  SILC_SERVER_COMMAND_CHECK(SILC_COMMAND_SILCOPER, cmd, 1, 2);
+
   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;
   }
 
@@ -5025,7 +4022,8 @@ 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;
   }
 
@@ -5037,7 +4035,10 @@ SILC_SERVER_CMD_FUNC(silcoper)
                                          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, cmd->sock->hostname));
       goto out;
     }
   }
@@ -5046,7 +4047,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;
   }
 
@@ -5067,7 +4069,7 @@ SILC_SERVER_CMD_FUNC(silcoper)
   if (!result) {
     /* Authentication failed */
     silc_server_command_send_status_reply(cmd, SILC_COMMAND_OPER,
-                                         SILC_STATUS_ERR_AUTH_FAILED);
+                                         SILC_STATUS_ERR_AUTH_FAILED, 0);
     goto out;
   }
 
@@ -5075,15 +4077,15 @@ 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);
+  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)
@@ -5092,7 +4094,7 @@ SILC_SERVER_CMD_FUNC(silcoper)
 
   /* Send reply to the sender */
   silc_server_command_send_status_reply(cmd, SILC_COMMAND_SILCOPER,
-                                       SILC_STATUS_OK);
+                                       SILC_STATUS_OK, 0);
 
  out:
   silc_server_command_free(cmd);
@@ -5106,15 +4108,18 @@ SILC_SERVER_CMD_FUNC(ban)
   SilcServerCommandContext cmd = (SilcServerCommandContext)context;
   SilcServer server = cmd->server;
   SilcClientEntry client = (SilcClientEntry)cmd->sock->user_data;
-  SilcBuffer packet;
+  SilcBuffer packet, list, tmp2;
   SilcChannelEntry channel;
   SilcChannelClientEntry chl;
   SilcChannelID *channel_id = NULL;
-  unsigned char *id, *add, *del;
-  SilcUInt32 id_len, tmp_len;
-  SilcUInt16 ident = silc_command_get_ident(cmd->payload);
-
-  if (cmd->sock->type != SILC_SOCKET_TYPE_CLIENT)
+  unsigned char *id, *tmp;
+  SilcUInt32 id_len, len;
+  SilcArgumentPayload args;
+  SilcHashTableList htl;
+  SilcUInt32 type;
+  SilcUInt16 argc = 0, ident = silc_command_get_ident(cmd->payload);
+
+  if (cmd->sock->type != SILC_SOCKET_TYPE_CLIENT || !client)
     goto out;
 
   SILC_SERVER_COMMAND_CHECK(SILC_COMMAND_BAN, cmd, 0, 3);
@@ -5125,7 +4130,7 @@ SILC_SERVER_CMD_FUNC(ban)
     channel_id = silc_id_payload_parse_id(id, id_len, NULL);
     if (!channel_id) {
       silc_server_command_send_status_reply(cmd, SILC_COMMAND_BAN,
-                                           SILC_STATUS_ERR_NO_CHANNEL_ID);
+                                           SILC_STATUS_ERR_NO_CHANNEL_ID, 0);
       goto out;
     }
   }
@@ -5139,7 +4144,8 @@ SILC_SERVER_CMD_FUNC(ban)
                                             channel_id, NULL);
     if (!channel) {
       silc_server_command_send_status_reply(cmd, SILC_COMMAND_BAN,
-                                           SILC_STATUS_ERR_NO_SUCH_CHANNEL);
+                                           SILC_STATUS_ERR_NO_SUCH_CHANNEL,
+                                           0);
       goto out;
     }
   }
@@ -5147,74 +4153,97 @@ SILC_SERVER_CMD_FUNC(ban)
   /* Check whether this client is on the channel */
   if (!silc_server_client_on_channel(client, channel, &chl)) {
     silc_server_command_send_status_reply(cmd, SILC_COMMAND_BAN,
-                                         SILC_STATUS_ERR_NOT_ON_CHANNEL);
+                                         SILC_STATUS_ERR_NOT_ON_CHANNEL, 0);
     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);
+                                         SILC_STATUS_ERR_NO_CHANNEL_PRIV, 0);
     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;
+  /* Get the ban information */
+  tmp = silc_argument_get_arg_type(cmd->args, 3, &len);
+  if (tmp && len > 2) {
+    /* Parse the arguments to see they are constructed correctly */
+    SILC_GET16_MSB(argc, tmp);
+    args = silc_argument_payload_parse(tmp + 2, len - 2, argc);
+    if (!args) {
+      silc_server_command_send_status_reply(cmd, SILC_COMMAND_BAN,
+                                           SILC_STATUS_ERR_NOT_ENOUGH_PARAMS,
+                                           0);
+      goto out;
+    }
 
-    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;
+    /* Get the type of action */
+    tmp = silc_argument_get_arg_type(cmd->args, 2, &len);
+    if (tmp && len == 1) {
+      if (tmp[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;
+       }
       }
+
+      /* Now add or delete the information. */
+      silc_server_inviteban_process(server, channel->ban_list,
+                                   (SilcUInt8)tmp[0], args);
     }
+    silc_argument_payload_free(args);
+  }
+
+  /* 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)) {
+      if (type == 1)
+       list = silc_argument_payload_encode_one(list, (char *)tmp2,
+                                               strlen((char *)tmp2), type);
+      else
+       list = silc_argument_payload_encode_one(list, tmp2->data, tmp2->len,
+                                               type);
+    }
+    silc_hash_table_list_reset(&htl);
   }
 
   /* 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);
+  if (list)
+    silc_server_send_notify_ban(server, SILC_PRIMARY_ROUTE(server),
+                               SILC_BROADCAST(server), channel,
+                               silc_argument_get_arg_type(cmd->args, 2, NULL),
+                               list);
 
   /* Send the reply back to the client */
   packet = 
     silc_command_reply_payload_encode_va(SILC_COMMAND_BAN,
                                         SILC_STATUS_OK, 0, ident, 2,
                                         2, id, id_len,
-                                        3, channel->ban_list, 
-                                        channel->ban_list ? 
-                                        strlen(channel->ban_list) -1 : 0);
+                                        3, list ? list->data : NULL,
+                                        list ? list->len : 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(list);
 
  out:
   silc_free(channel_id);
@@ -5234,19 +4263,22 @@ SILC_SERVER_CMD_FUNC(leave)
   SilcUInt32 len;
   unsigned char *tmp;
 
+  if (cmd->sock->type != SILC_SOCKET_TYPE_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) {
     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;
   }
   id = silc_id_payload_parse_id(tmp, len, NULL);
   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;
   }
 
@@ -5256,7 +4288,8 @@ SILC_SERVER_CMD_FUNC(leave)
     channel = silc_idlist_find_channel_by_id(server->global_list, id, NULL);
     if (!channel) {
       silc_server_command_send_status_reply(cmd, SILC_COMMAND_LEAVE,
-                                           SILC_STATUS_ERR_NO_SUCH_CHANNEL);
+                                           SILC_STATUS_ERR_NO_SUCH_CHANNEL,
+                                           0);
       goto out;
     }
   }
@@ -5264,19 +4297,17 @@ SILC_SERVER_CMD_FUNC(leave)
   /* Check whether this client is on the channel */
   if (!silc_server_client_on_channel(id_entry, channel, NULL)) {
     silc_server_command_send_status_reply(cmd, SILC_COMMAND_LEAVE,
-                                         SILC_STATUS_ERR_NOT_ON_CHANNEL);
+                                         SILC_STATUS_ERR_NOT_ON_CHANNEL, 0);
     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_data(cmd, SILC_COMMAND_LEAVE,
-                                      SILC_STATUS_OK, 2, tmp, len);
+                                      SILC_STATUS_OK, 0, 2, tmp, len);
 
   /* Remove client from channel */
   if (!silc_server_remove_from_one_channel(server, sock, channel, id_entry,
@@ -5330,7 +4361,7 @@ 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;
   }
 
@@ -5338,7 +4369,7 @@ SILC_SERVER_CMD_FUNC(users)
     id = silc_id_payload_parse_id(channel_id, channel_id_len, NULL);
     if (!id) {
       silc_server_command_send_status_reply(cmd, SILC_COMMAND_USERS,
-                                           SILC_STATUS_ERR_NO_CHANNEL_ID);
+                                           SILC_STATUS_ERR_BAD_CHANNEL_ID, 0);
       goto out;
     }
   }
@@ -5352,7 +4383,8 @@ SILC_SERVER_CMD_FUNC(users)
     channel = silc_idlist_find_channel_by_name(server->local_list, 
                                               channel_name, NULL);
 
-  if (!channel || channel->disabled || !channel->users_resolved) {
+  if (!channel || (!server->standalone && (channel->disabled || 
+                   !channel->users_resolved))) {
     if (server->server_type != SILC_ROUTER && !server->standalone &&
        !cmd->pending) {
       SilcBuffer tmpbuf;
@@ -5361,7 +4393,7 @@ SILC_SERVER_CMD_FUNC(users)
       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);
       
@@ -5386,7 +4418,8 @@ SILC_SERVER_CMD_FUNC(users)
     if (!channel) {
       /* Channel really does not exist */
       silc_server_command_send_status_reply(cmd, SILC_COMMAND_USERS,
-                                           SILC_STATUS_ERR_NO_SUCH_CHANNEL);
+                                           SILC_STATUS_ERR_NO_SUCH_CHANNEL,
+                                           0);
       goto out;
     }
   }
@@ -5398,14 +4431,19 @@ SILC_SERVER_CMD_FUNC(users)
        && !silc_server_client_on_channel(cmd->sock->user_data, channel, 
                                          NULL)) {
       silc_server_command_send_status_reply(cmd, SILC_COMMAND_USERS,
-                                           SILC_STATUS_ERR_NOT_ON_CHANNEL);
+                                           SILC_STATUS_ERR_NO_SUCH_CHANNEL,
+                                           0);
       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);
@@ -5416,17 +4454,23 @@ SILC_SERVER_CMD_FUNC(users)
                                                SILC_STATUS_OK, 0, 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);
+                                               4, client_id_list ? 
+                                               client_id_list->data : NULL,
+                                               client_id_list ?
+                                               client_id_list->len : 0,
+                                               5, client_mode_list ?
+                                               client_mode_list->data : NULL,
+                                               client_mode_list ?
+                                               client_mode_list->len : 0);
   silc_server_packet_send(server, cmd->sock, SILC_PACKET_COMMAND_REPLY, 0, 
                          packet->data, packet->len, FALSE);
     
   silc_buffer_free(idp);
   silc_buffer_free(packet);
-  silc_buffer_free(client_id_list);
-  silc_buffer_free(client_mode_list);
+  if (client_id_list)
+    silc_buffer_free(client_id_list);
+  if (client_mode_list)
+    silc_buffer_free(client_mode_list);
   silc_free(id);
 
  out:
@@ -5447,24 +4491,24 @@ SILC_SERVER_CMD_FUNC(getkey)
   SilcServerID *server_id = NULL;
   SilcIDPayload idp = NULL;
   SilcUInt16 ident = silc_command_get_ident(cmd->payload);
-  unsigned char *tmp, *pkdata;
-  SilcUInt32 tmp_len, pklen;
+  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;
   }
 
@@ -5514,7 +4558,8 @@ 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_STATUS_ERR_NO_SUCH_CLIENT_ID,
+                                           0);
       goto out;
     }
 
@@ -5522,22 +4567,8 @@ SILC_SERVER_CMD_FUNC(getkey)
        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_pkcs_public_key_payload_encode(public_key);
   } else if (id_type == SILC_ID_SERVER) {
     server_id = silc_id_payload_get_id(idp);
 
@@ -5562,7 +4593,7 @@ SILC_SERVER_CMD_FUNC(getkey)
       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);
       
@@ -5579,7 +4610,8 @@ 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_STATUS_ERR_NO_SUCH_SERVER_ID,
+                                           0);
       goto out;
     }
 
@@ -5587,42 +4619,26 @@ SILC_SERVER_CMD_FUNC(getkey)
     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_pkcs_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, 0, ident, 
-                                               pkdata ? 2 : 1,
+                                               SILC_STATUS_OK, 0, ident, 2,
                                                2, tmp, tmp_len,
-                                               3, pkdata, pklen);
+                                               3, pk ? pk->data : NULL,
+                                               pk ? pk->len : 0);
   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);
-
  out:
   if (idp)
     silc_id_payload_free(idp);
+  silc_buffer_free(pk);
   silc_free(client_id);
   silc_free(server_id);
   silc_server_command_free(cmd);
@@ -5643,23 +4659,23 @@ SILC_SERVER_CMD_FUNC(connect)
   SilcUInt32 tmp_len;
   SilcUInt32 port = SILC_PORT;
 
-  SILC_SERVER_COMMAND_CHECK(SILC_COMMAND_PRIV_CONNECT, cmd, 1, 2);
-
-  if (!client || cmd->sock->type != SILC_SOCKET_TYPE_CLIENT)
+  if (cmd->sock->type != SILC_SOCKET_TYPE_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);
+                                         SILC_STATUS_ERR_NO_SERVER_PRIV, 0);
     goto out;
   }
 
-  if (server->server_type == SILC_ROUTER && 
+  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);
+                                         SILC_STATUS_ERR_NO_ROUTER_PRIV, 0);
     goto out;
   }
 
@@ -5667,7 +4683,8 @@ SILC_SERVER_CMD_FUNC(connect)
   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);
+                                         SILC_STATUS_ERR_NOT_ENOUGH_PARAMS,
+                                         0);
     goto out;
   }
 
@@ -5681,7 +4698,7 @@ SILC_SERVER_CMD_FUNC(connect)
 
   /* Send reply to the sender */
   silc_server_command_send_status_reply(cmd, SILC_COMMAND_PRIV_CONNECT,
-                                       SILC_STATUS_OK);
+                                       SILC_STATUS_OK, 0);
 
  out:
   silc_server_command_free(cmd);
@@ -5701,16 +4718,17 @@ SILC_SERVER_CMD_FUNC(close)
   unsigned char *name;
   SilcUInt32 port = SILC_PORT;
 
-  SILC_SERVER_COMMAND_CHECK(SILC_COMMAND_PRIV_CLOSE, cmd, 1, 2);
-
-  if (!client || cmd->sock->type != SILC_SOCKET_TYPE_CLIENT)
+  if (cmd->sock->type != SILC_SOCKET_TYPE_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);
+                                         SILC_STATUS_ERR_NO_SERVER_PRIV,
+                                         0);
     goto out;
   }
 
@@ -5718,7 +4736,8 @@ SILC_SERVER_CMD_FUNC(close)
   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);
+                                         SILC_STATUS_ERR_NOT_ENOUGH_PARAMS,
+                                         0);
     goto out;
   }
 
@@ -5734,13 +4753,19 @@ SILC_SERVER_CMD_FUNC(close)
                                                   name, port, FALSE, NULL);
   if (!server_entry) {
     silc_server_command_send_status_reply(cmd, SILC_COMMAND_PRIV_CLOSE,
-                                         SILC_STATUS_ERR_NO_SERVER_ID);
+                                         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);
+                                       SILC_STATUS_OK, 0);
 
   /* Close the connection to the server */
   sock = (SilcSocketConnection)server_entry->connection;
@@ -5769,22 +4794,23 @@ SILC_SERVER_CMD_FUNC(shutdown)
   SilcServer server = cmd->server;
   SilcClientEntry client = (SilcClientEntry)cmd->sock->user_data;
 
-  SILC_SERVER_COMMAND_CHECK(SILC_COMMAND_PRIV_SHUTDOWN, cmd, 0, 0);
-
-  if (!client || cmd->sock->type != SILC_SOCKET_TYPE_CLIENT)
+  if (cmd->sock->type != SILC_SOCKET_TYPE_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);
+                                         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);
+                                       SILC_STATUS_OK, 0);
 
   /* Then, gracefully, or not, bring the server down. */
   silc_server_stop(server);