New silcconfig library and server parser. Merged silc-newconfig-final.patch.
[silc.git] / apps / silcd / command.c
index e7e0a0822fe6d3569bd112d7526d43deaaf159ff..b4d5bcee37adf53669b7e5e77dde3ac515eb6dae 100644 (file)
@@ -35,8 +35,12 @@ silc_server_command_send_status_data(SilcServerCommandContext cmd,
                                     SilcCommand command,
                                     SilcCommandStatus status,
                                     uint32 arg_type,
-                                    unsigned char *arg,
+                                    const unsigned char *arg,
                                     uint32 arg_len);
+static bool
+silc_server_command_pending_error_check(SilcServerCommandContext cmd,
+                                       SilcServerCommandReplyContext cmdr,
+                                       SilcCommand command);
 SILC_TASK_CALLBACK(silc_server_command_process_timeout);
 
 /* Server command list. */
@@ -76,12 +80,25 @@ SilcServerCommand silc_command_list[] =
   { NULL, 0 },
 };
 
-#define SILC_SERVER_COMMAND_CHECK_ARGC(command, context, min, max)           \
+/* Performs several checks to the command. It first checks whether this
+   command was called as pending command callback. If it was then it checks
+   whether error occurred in the command reply where the pending command
+   callback was called. 
+
+   It also checks that the requested command includes correct amount
+   of arguments. */
+#define SILC_SERVER_COMMAND_CHECK(command, context, min, max)                \
 do {                                                                         \
-  uint32 _argc = silc_argument_get_arg_num(cmd->args);               \
+  uint32 _argc;                                                                      \
                                                                              \
   SILC_LOG_DEBUG(("Start"));                                                 \
                                                                              \
+  if (silc_server_command_pending_error_check(cmd, context2, command)) {      \
+    silc_server_command_free(cmd);                                           \
+    return;                                                                  \
+  }                                                                          \
+                                                                             \
+  _argc = silc_argument_get_arg_num(cmd->args);                                      \
   if (_argc < min) {                                                         \
     silc_server_command_send_status_reply(cmd, command,                              \
                                          SILC_STATUS_ERR_NOT_ENOUGH_PARAMS); \
@@ -105,12 +122,15 @@ static int silc_server_is_registered(SilcServer server,
                                     SilcCommand command)
 {
   SilcIDListData idata = (SilcIDListData)sock->user_data;
-  if (idata->registered)
+
+  if (!idata)
+    return FALSE;
+
+  if (idata->status & SILC_IDLIST_STATUS_REGISTERED)
     return TRUE;
 
   silc_server_command_send_status_reply(cmd, command,
                                        SILC_STATUS_ERR_NOT_REGISTERED);
-  silc_server_command_free(cmd);
   return FALSE;
 }
 
@@ -128,16 +148,23 @@ SILC_TASK_CALLBACK(silc_server_command_process_timeout)
   SilcServerCommandTimeout timeout = (SilcServerCommandTimeout)context;
   SilcClientEntry client = (SilcClientEntry)timeout->ctx->sock->user_data;
 
+  if (!client) {
+    silc_server_command_free(timeout->ctx);
+    silc_free(timeout);
+  }
+
   /* Update access time */
   client->last_command = time(NULL);
 
   if (!(timeout->cmd->flags & SILC_CF_REG))
-    timeout->cmd->cb(timeout->ctx);
+    timeout->cmd->cb(timeout->ctx, NULL);
   else if (silc_server_is_registered(timeout->ctx->server, 
                                     timeout->ctx->sock, 
                                     timeout->ctx, 
                                     timeout->cmd->cmd))
-    timeout->cmd->cb(timeout->ctx);
+    timeout->cmd->cb(timeout->ctx, NULL);
+  else
+    silc_server_command_free(timeout->ctx);
 
   silc_free(timeout);
 }
@@ -160,7 +187,8 @@ void silc_server_command_process(SilcServer server,
   ctx->packet = silc_packet_context_dup(packet); /* Save original packet */
   
   /* Parse the command payload in the packet */
-  ctx->payload = silc_command_payload_parse(packet->buffer);
+  ctx->payload = silc_command_payload_parse(packet->buffer->data,
+                                           packet->buffer->len);
   if (!ctx->payload) {
     SILC_LOG_ERROR(("Bad command payload, packet dropped"));
     silc_buffer_free(packet->buffer);
@@ -207,28 +235,27 @@ void silc_server_command_process(SilcServer server,
 
     if (!fast && ((cmd->flags & SILC_CF_LAG_STRICT) ||
                  (client->fast_command > 5 && cmd->flags & SILC_CF_LAG)))
-      silc_task_register(server->timeout_queue, sock->sock, 
+      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_TASK_TIMEOUT, SILC_TASK_PRI_NORMAL);
     else
-      silc_task_register(server->timeout_queue, sock->sock, 
+      silc_schedule_task_add(server->schedule, sock->sock, 
                         silc_server_command_process_timeout,
-                        (void *)timeout, 
-                        0, 1,
-                        SILC_TASK_TIMEOUT,
-                        SILC_TASK_PRI_NORMAL);
+                        (void *)timeout, 0, 1,
+                        SILC_TASK_TIMEOUT, SILC_TASK_PRI_NORMAL);
     return;
   }
 
   /* Execute for server */
 
   if (!(cmd->flags & SILC_CF_REG))
-    cmd->cb(ctx);
+    cmd->cb(ctx, NULL);
   else if (silc_server_is_registered(server, sock, ctx, cmd->cmd))
-    cmd->cb(ctx);
+    cmd->cb(ctx, NULL);
+  else
+    silc_server_command_free(ctx);
 }
 
 /* Allocate Command Context */
@@ -249,7 +276,7 @@ void silc_server_command_free(SilcServerCommandContext ctx)
                  ctx->users));
   if (ctx->users < 1) {
     if (ctx->payload)
-      silc_command_free_payload(ctx->payload);
+      silc_command_payload_free(ctx->payload);
     if (ctx->packet)
       silc_packet_context_free(ctx->packet);
     if (ctx->sock)
@@ -272,26 +299,39 @@ silc_server_command_dup(SilcServerCommandContext ctx)
 
 /* 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.  If `ident' is non-zero
+   with `context' when reply has been received.  It can be SILC_COMMAND_NONE
+   to match any command with the `ident'.  If `ident' is non-zero
    the `callback' will be executed when received reply with command
-   identifier `ident'. */
+   identifier `ident'. If there already exists pending command for the
+   specified command, ident, callback and context this function has no
+   effect. */
 
-void silc_server_command_pending(SilcServer server,
+bool silc_server_command_pending(SilcServer server,
                                 SilcCommand reply_cmd,
                                 uint16 ident,
-                                SilcServerPendingDestructor destructor,
                                 SilcCommandCb callback,
                                 void *context)
 {
   SilcServerCommandPending *reply;
 
+  /* Check whether identical pending already exists for same command,
+     ident, callback and callback context. If it does then it would be
+     error to register it again. */
+  silc_dlist_start(server->pending_commands);
+  while ((reply = silc_dlist_get(server->pending_commands)) != SILC_LIST_END) {
+    if (reply->reply_cmd == reply_cmd && reply->ident == ident &&
+       reply->callback == callback && reply->context == context)
+      return FALSE;
+  }
+
   reply = silc_calloc(1, sizeof(*reply));
   reply->reply_cmd = reply_cmd;
   reply->ident = ident;
   reply->context = context;
   reply->callback = callback;
-  reply->destructor = destructor;
   silc_dlist_add(server->pending_commands, reply);
+
+  return TRUE;
 }
 
 /* Deletes pending command by reply command type. */
@@ -314,33 +354,31 @@ void silc_server_command_pending_del(SilcServer server,
 /* Checks for pending commands and marks callbacks to be called from
    the command reply function. Returns TRUE if there were pending command. */
 
-int silc_server_command_pending_check(SilcServer server,
-                                     SilcServerCommandReplyContext ctx,
-                                     SilcCommand command, 
-                                     uint16 ident)
+SilcServerCommandPendingCallbacks
+silc_server_command_pending_check(SilcServer server,
+                                 SilcServerCommandReplyContext ctx,
+                                 SilcCommand command, 
+                                 uint16 ident,
+                                 uint32 *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->ident == ident) {
-      ctx->context = r->context;
-      ctx->callback = r->callback;
-      ctx->destructor = r->destructor;
+    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;
-      return TRUE;
+      i++;
     }
   }
 
-  return FALSE;
-}
-
-/* Destructor function for pending callbacks. This is called when using
-   pending commands to free the context given for the pending command. */
-
-static void silc_server_command_destructor(void *context)
-{
-  silc_server_command_free((SilcServerCommandContext)context);
+  *callbacks_count = i;
+  return callbacks;
 }
 
 /* Sends simple status message as command reply packet */
@@ -372,7 +410,7 @@ silc_server_command_send_status_data(SilcServerCommandContext cmd,
                                     SilcCommand command,
                                     SilcCommandStatus status,
                                     uint32 arg_type,
-                                    unsigned char *arg,
+                                    const unsigned char *arg,
                                     uint32 arg_len)
 {
   SilcBuffer buffer;
@@ -389,6 +427,42 @@ silc_server_command_send_status_data(SilcServerCommandContext cmd,
   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)
+{
+  SilcCommandStatus status;
+
+  if (!cmd->pending || !cmdr)
+    return FALSE;
+
+  SILC_GET16_MSB(status, silc_argument_get_arg_type(cmdr->args, 1, NULL));
+  if (status != SILC_STATUS_OK &&
+      status != SILC_STATUS_LIST_START &&
+      status != SILC_STATUS_LIST_ITEM &&
+      status != SILC_STATUS_LIST_END) {
+    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
@@ -415,15 +489,7 @@ silc_server_command_whois_parse(SilcServerCommandContext cmd,
     /* No ID, get the nickname@server string and parse it. */
     tmp = silc_argument_get_arg_type(cmd->args, 1, NULL);
     if (tmp) {
-      if (strchr(tmp, '@')) {
-       len = strcspn(tmp, "@");
-       *nickname = silc_calloc(len + 1, sizeof(char));
-       memcpy(*nickname, tmp, len);
-       *server_name = silc_calloc(strlen(tmp) - len, sizeof(char));
-       memcpy(*server_name, tmp + len + 1, strlen(tmp) - len - 1);
-      } else {
-       *nickname = strdup(tmp);
-      }
+      silc_parse_userfqdn(tmp, nickname, server_name);
     } else {
       silc_server_command_send_status_reply(cmd, command,
                                            SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
@@ -437,6 +503,8 @@ silc_server_command_whois_parse(SilcServerCommandContext cmd,
     (*client_id)[0] = silc_id_payload_parse_id(tmp, len);
     if ((*client_id)[0] == NULL) {
       silc_free(*client_id);
+      silc_server_command_send_status_reply(cmd, command,
+                                           SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
       return FALSE;
     }
     *client_id_count = 1;
@@ -454,6 +522,9 @@ silc_server_command_whois_parse(SilcServerCommandContext cmd,
            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)++;
@@ -461,8 +532,6 @@ silc_server_command_whois_parse(SilcServerCommandContext cmd,
        }
       }
     }
-
-    /* Command includes ID, use that */
   }
 
   /* Get the max count of reply messages allowed */
@@ -475,112 +544,218 @@ silc_server_command_whois_parse(SilcServerCommandContext cmd,
   return TRUE;
 }
 
-static char
+/* Resolve context used by both WHOIS and IDENTIFY commands */
+typedef struct {
+  SilcServerEntry router;
+  uint16 ident;
+  unsigned char **res_argv;
+  uint32 *res_argv_lens;
+  uint32 *res_argv_types;
+  uint32 res_argc;
+} *SilcServerResolveContext;
+
+static bool
 silc_server_command_whois_check(SilcServerCommandContext cmd,
                                SilcClientEntry *clients,
                                uint32 clients_count)
 {
   SilcServer server = cmd->server;
-  int i;
   SilcClientEntry entry;
+  SilcServerResolveContext resolve = NULL, r = NULL;
+  uint32 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 || entry->data.registered == FALSE)
+    if (!entry)
       continue;
 
-    if (!entry->nickname || !entry->username || !entry->userinfo) {
-      SilcBuffer tmpbuf;
-      uint16 old_ident;
-
+    if ((entry->nickname && entry->username && entry->userinfo) ||
+       !(entry->data.status & SILC_IDLIST_STATUS_REGISTERED)) {
       if (!entry->router)
        continue;
-      
-      old_ident = silc_command_get_ident(cmd->payload);
-      silc_command_set_ident(cmd->payload, silc_rng_get_rn16(server->rng));
-      tmpbuf = silc_command_payload_encode_payload(cmd->payload);
 
-      /* Send WHOIS 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_WHOIS, 
-                                 silc_command_get_ident(cmd->payload),
-                                 silc_server_command_destructor,
-                                 silc_server_command_whois, 
+      /* 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));
-      cmd->pending = TRUE;
-      
-      silc_command_set_ident(cmd->payload, old_ident);
+      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;
+         }
+       }
 
-      silc_buffer_free(tmpbuf);
-      return FALSE;
+       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;
+      }
     }
   }
 
-  return TRUE;
+  /* 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,
                                     uint32 clients_count,
-                                    int count)
+                                    int count,
+                                    const char *nickname,
+                                    SilcClientID **client_ids)
 {
   SilcServer server = cmd->server;
   char *tmp;
-  int i, k, len;
+  int i, k, len, valid_count;
   SilcBuffer packet, idp, channels;
   SilcClientEntry entry;
   SilcCommandStatus status;
   uint16 ident = silc_command_get_ident(cmd->payload);
-  char nh[128], uh[128];
+  char nh[256], uh[256];
   unsigned char idle[4], mode[4];
+  unsigned char *fingerprint;
   SilcSocketConnection hsock;
 
-  len = 0;
-  for (i = 0; i < clients_count; i++)
-    if (clients[i]->data.registered)
-      len++;
+  /* 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;
+  }
 
-  status = SILC_STATUS_OK;
-  if (len > 1)
+  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->data.registered == FALSE) {
-      if (clients_count == 1) {
-       SilcBuffer idp = silc_id_payload_encode(entry->id, 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);
-      }
+    if (!entry)
       continue;
-    }
 
     if (k >= 1)
       status = SILC_STATUS_LIST_ITEM;
-
-    if (clients_count > 1 && k == clients_count - 1)
+    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;
-
-    /* Sanity check, however these should never fail. However, as
-       this sanity check has been added here they have failed. */
-    if (!entry->nickname || !entry->username || !entry->userinfo)
-      continue;
-      
     /* Send WHOIS reply */
     idp = silc_id_payload_encode(entry->id, SILC_ID_CLIENT);
     tmp = silc_argument_get_first_arg(cmd->args, NULL);
@@ -592,10 +767,14 @@ silc_server_command_whois_send_reply(SilcServerCommandContext cmd,
     strncat(nh, entry->nickname, strlen(entry->nickname));
     if (!strchr(entry->nickname, '@')) {
       strncat(nh, "@", 1);
-      len = entry->router ? strlen(entry->router->server_name) :
-       strlen(server->server_name);
-      strncat(nh, entry->router ? entry->router->server_name :
-             server->server_name, len);
+      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));
@@ -607,6 +786,11 @@ silc_server_command_whois_send_reply(SilcServerCommandContext cmd,
     }
 
     channels = silc_server_get_client_channel_list(server, entry);
+
+    if (entry->data.fingerprint[0] != 0 && entry->data.fingerprint[1] != 0)
+      fingerprint = entry->data.fingerprint;
+    else
+      fingerprint = NULL;
       
     SILC_PUT32_MSB(entry->mode, mode);
 
@@ -614,29 +798,21 @@ silc_server_command_whois_send_reply(SilcServerCommandContext cmd,
       SILC_PUT32_MSB((time(NULL) - entry->data.last_receive), idle);
     }
 
-    if (channels)
-      packet = silc_command_reply_payload_encode_va(SILC_COMMAND_WHOIS,
-                                                   status, ident, 7, 
-                                                   2, idp->data, idp->len,
-                                                   3, nh, strlen(nh),
-                                                   4, uh, strlen(uh),
-                                                   5, entry->userinfo, 
-                                                   strlen(entry->userinfo),
-                                                   6, channels->data,
-                                                   channels->len,
-                                                   7, mode, 4,
-                                                   8, idle, 4);
-    else
-      packet = silc_command_reply_payload_encode_va(SILC_COMMAND_WHOIS,
-                                                   status, ident, 6, 
-                                                   2, idp->data, idp->len,
-                                                   3, nh, strlen(nh),
-                                                   4, uh, strlen(uh),
-                                                   5, entry->userinfo, 
-                                                   strlen(entry->userinfo),
-                                                   7, mode, 4,
-                                                   8, idle, 4);
-    
+    packet = 
+      silc_command_reply_payload_encode_va(SILC_COMMAND_WHOIS,
+                                          status, ident, 8, 
+                                          2, idp->data, idp->len,
+                                          3, nh, strlen(nh),
+                                          4, uh, strlen(uh),
+                                          5, entry->userinfo, 
+                                          strlen(entry->userinfo),
+                                          6, channels ? channels->data : NULL,
+                                          channels ? channels->len : 0,
+                                          7, mode, 4,
+                                          8, idle, 4,
+                                          9, fingerprint,
+                                          fingerprint ? 20 : 0);
+
     silc_server_packet_send(server, cmd->sock, SILC_PACKET_COMMAND_REPLY,
                            0, packet->data, packet->len, FALSE);
     
@@ -649,8 +825,35 @@ silc_server_command_whois_send_reply(SilcServerCommandContext cmd,
   }
 }
 
+static void 
+silc_server_command_whois_send_router(SilcServerCommandContext cmd)
+{
+  SilcServer server = cmd->server;
+  SilcBuffer tmpbuf;
+  uint16 old_ident;
+
+  old_ident = silc_command_get_ident(cmd->payload);
+  silc_command_set_ident(cmd->payload, ++server->cmd_ident);
+  tmpbuf = silc_command_payload_encode_payload(cmd->payload);
+
+  /* Send WHOIS command to our router */
+  silc_server_packet_send(server, (SilcSocketConnection)
+                         server->router->connection,
+                         SILC_PACKET_COMMAND, cmd->packet->flags,
+                         tmpbuf->data, tmpbuf->len, TRUE);
+
+  /* Reprocess this packet after received reply from router */
+  silc_server_command_pending(server, SILC_COMMAND_WHOIS, 
+                             silc_command_get_ident(cmd->payload),
+                             silc_server_command_whois,
+                             silc_server_command_dup(cmd));
+  cmd->pending = TRUE;
+  silc_command_set_ident(cmd->payload, old_ident);
+  silc_buffer_free(tmpbuf);
+}
+
 static int
-silc_server_command_whois_from_client(SilcServerCommandContext cmd)
+silc_server_command_whois_process(SilcServerCommandContext cmd)
 {
   SilcServer server = cmd->server;
   char *nick = NULL, *server_name = NULL;
@@ -659,43 +862,7 @@ silc_server_command_whois_from_client(SilcServerCommandContext cmd)
   SilcClientID **client_id = NULL;
   uint32 client_id_count = 0, clients_count = 0;
   int i, ret = 0;
-
-  /* Protocol dictates that we must always send the received WHOIS 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 (server->server_type == SILC_SERVER && !cmd->pending && 
-      !server->standalone) {
-    SilcBuffer tmpbuf;
-    uint16 old_ident;
-
-    old_ident = silc_command_get_ident(cmd->payload);
-    silc_command_set_ident(cmd->payload, silc_rng_get_rn16(server->rng));
-    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_destructor,
-                               silc_server_command_whois,
-                               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. */
+  bool check_global = FALSE;
 
   /* Parse the whois request */
   if (!silc_server_command_whois_parse(cmd, &client_id, &client_id_count, 
@@ -703,49 +870,77 @@ silc_server_command_whois_from_client(SilcServerCommandContext cmd)
                                       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], NULL);
+                                           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);
-  }
-  
-  /* Check global list as well */
-  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->global_list, 
-                                           client_id[i], NULL);
-      if (entry) {
-       clients = silc_realloc(clients, sizeof(*clients) * 
-                              (clients_count + 1));
-       clients[clients_count++] = entry;
-      }
+    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);
     }
-  } else {
-    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,
@@ -771,9 +966,9 @@ silc_server_command_whois_from_client(SilcServerCommandContext cmd)
     goto out;
   }
 
-  /* Send the command reply to the client */
+  /* Send the command reply */
   silc_server_command_whois_send_reply(cmd, clients, clients_count,
-                                      count);
+                                      count, nick, client_id);
 
  out:
   if (client_id_count) {
@@ -781,177 +976,52 @@ silc_server_command_whois_from_client(SilcServerCommandContext cmd)
       silc_free(client_id[i]);
     silc_free(client_id);
   }
-  if (clients)
-    silc_free(clients);
-  if (nick)
-    silc_free(nick);
-  if (server_name)
-    silc_free(server_name);
+  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_whois_from_server(SilcServerCommandContext cmd)
+silc_server_command_whowas_parse(SilcServerCommandContext cmd,
+                                char **nickname,
+                                char **server_name,
+                                int *count)
 {
-  SilcServer server = cmd->server;
-  char *nick = NULL, *server_name = NULL;
-  int count = 0;
-  SilcClientEntry *clients = NULL, entry;
-  SilcClientID **client_id = NULL;
-  uint32 client_id_count = 0, clients_count = 0;
-  int i, ret = 0;
+  unsigned char *tmp;
+  uint32 len;
 
-  /* 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;
+  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;
+  }
 
-  /* Process the command request. Let's search for the requested client and
-     send reply to the requesting server. */
+  /* Get the nickname@server string and parse it. */
+  silc_parse_userfqdn(tmp, nickname, server_name);
 
-  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], NULL);
-      if (entry) {
-       clients = silc_realloc(clients, sizeof(*clients) * 
-                              (clients_count + 1));
-       clients[clients_count++] = entry;
-      }
-    }
-  } else {
-    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 we are router we will check our global list as well. */
-  if (server->server_type == SILC_ROUTER) {
-    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->global_list, 
-                                             client_id[i], NULL);
-       if (entry) {
-         clients = silc_realloc(clients, sizeof(*clients) * 
-                                (clients_count + 1));
-         clients[clients_count++] = entry;
-       }
-      }
-    } else {
-      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) {
-    /* Such a client 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 to the client */
-  silc_server_command_whois_send_reply(cmd, clients, clients_count,
-                                      count);
-
- out:
-  if (client_id_count) {
-    for (i = 0; i < client_id_count; i++)
-      silc_free(client_id[i]);
-    silc_free(client_id);
-  }
-  if (clients)
-    silc_free(clients);
-  if (nick)
-    silc_free(nick);
-  if (server_name)
-    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_ARGC(SILC_COMMAND_WHOIS, cmd, 1, 3328);
-
-  if (cmd->sock->type == SILC_SOCKET_TYPE_CLIENT)
-    ret = silc_server_command_whois_from_client(cmd);
-  else if ((cmd->sock->type == SILC_SOCKET_TYPE_SERVER) ||
-          (cmd->sock->type == SILC_SOCKET_TYPE_ROUTER))
-    ret = silc_server_command_whois_from_server(cmd);
-
-  if (!ret)
-    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;
-  uint32 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. */
-  if (strchr(tmp, '@')) {
-    len = strcspn(tmp, "@");
-    *nickname = silc_calloc(len + 1, sizeof(char));
-    memcpy(*nickname, tmp, len);
-    *server_name = silc_calloc(strlen(tmp) - len, sizeof(char));
-    memcpy(*server_name, tmp + len + 1, strlen(tmp) - len - 1);
-  } else {
-    *nickname = strdup(tmp);
-  }
   /* Get the max count of reply messages allowed */
   tmp = silc_argument_get_arg_type(cmd->args, 2, NULL);
   if (tmp)
@@ -982,7 +1052,7 @@ silc_server_command_whowas_check(SilcServerCommandContext cmd,
        continue;
       
       old_ident = silc_command_get_ident(cmd->payload);
-      silc_command_set_ident(cmd->payload, silc_rng_get_rn16(server->rng));
+      silc_command_set_ident(cmd->payload, ++server->cmd_ident);
       tmpbuf = silc_command_payload_encode_payload(cmd->payload);
 
       /* Send WHOWAS command */
@@ -993,11 +1063,9 @@ silc_server_command_whowas_check(SilcServerCommandContext cmd,
       /* Reprocess this packet after received reply */
       silc_server_command_pending(server, SILC_COMMAND_WHOWAS, 
                                  silc_command_get_ident(cmd->payload),
-                                 silc_server_command_destructor,
                                  silc_server_command_whowas, 
                                  silc_server_command_dup(cmd));
       cmd->pending = TRUE;
-      
       silc_command_set_ident(cmd->payload, old_ident);
 
       silc_buffer_free(tmpbuf);
@@ -1015,59 +1083,71 @@ silc_server_command_whowas_send_reply(SilcServerCommandContext cmd,
 {
   SilcServer server = cmd->server;
   char *tmp;
-  int i, count = 0, len;
+  int i, k, count = 0, len;
   SilcBuffer packet, idp;
   SilcClientEntry entry = NULL;
   SilcCommandStatus status;
   uint16 ident = silc_command_get_ident(cmd->payload);
-  char found = FALSE;
   char nh[256], uh[256];
+  int valid_count;
 
   status = SILC_STATUS_OK;
-  if (clients_count > 1)
-    status = SILC_STATUS_LIST_START;
 
+  /* Process only entries that are not registered anymore. */
+  valid_count = 0;
   for (i = 0; i < clients_count; i++) {
-    entry = clients[i];
+    if (clients[i]->data.status & SILC_IDLIST_STATUS_REGISTERED)
+      clients[i] = NULL;
+    else
+      valid_count++;
+  }
 
-    /* We will take only clients that are not valid anymore. They are the
-       ones that are not registered anymore but still have a ID. They
-       have disconnected us, and thus valid for WHOWAS. */
-    if (entry->data.registered == TRUE)
-      continue;
-    if (entry->id == NULL)
-      continue;
+  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 (count && i - 1 == count)
-      break;
+  if (valid_count > 1)
+    status = SILC_STATUS_LIST_START;
 
-    found = TRUE;
+  for (i = 0, k = 0; i < clients_count; i++) {
+    entry = clients[i];
+    if (!entry)
+      continue;
 
-    if (clients_count > 2)
+    if (k >= 1)
       status = SILC_STATUS_LIST_ITEM;
-
-    if (clients_count > 1 && i == clients_count - 1)
+    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;
 
-    /* Sanity check, however these should never fail. However, as
-       this sanity check has been added here they have failed. */
-    if (!entry->nickname || !entry->username)
-      continue;
-      
     /* 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);
-      len = entry->router ? strlen(entry->router->server_name) :
-       strlen(server->server_name);
-      strncat(nh, entry->router ? entry->router->server_name :
-             server->server_name, len);
+      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));
@@ -1076,39 +1156,27 @@ silc_server_command_whowas_send_reply(SilcServerCommandContext cmd,
       strcat(uh, "*private*");
     }
       
-    if (entry->userinfo)
-      packet = 
-       silc_command_reply_payload_encode_va(SILC_COMMAND_WHOWAS,
-                                            status, ident, 4, 
-                                            2, idp->data, idp->len,
-                                            3, nh, strlen(nh),
-                                            4, uh, strlen(uh),
-                                            5, entry->userinfo, 
-                                            strlen(entry->userinfo));
-    else
-      packet = 
-       silc_command_reply_payload_encode_va(SILC_COMMAND_WHOWAS,
-                                            status, ident, 3, 
-                                            2, idp->data, idp->len,
-                                            3, nh, strlen(nh),
-                                            4, uh, strlen(uh));
-
+    packet = 
+      silc_command_reply_payload_encode_va(SILC_COMMAND_WHOWAS,
+                                          status, ident, 4, 
+                                          2, idp->data, idp->len,
+                                          3, nh, strlen(nh),
+                                          4, uh, strlen(uh),
+                                          5, entry->userinfo, 
+                                          entry->userinfo ? 
+                                          strlen(entry->userinfo) : 0);
     silc_server_packet_send(server, cmd->sock, SILC_PACKET_COMMAND_REPLY,
                            0, packet->data, packet->len, FALSE);
     
     silc_buffer_free(packet);
     silc_buffer_free(idp);
-  }
 
-  if (found == FALSE && entry)
-    silc_server_command_send_status_data(cmd, SILC_COMMAND_WHOWAS,
-                                        SILC_STATUS_ERR_NO_SUCH_NICK,
-                                        3, entry->nickname, 
-                                        strlen(entry->nickname));
+    k++;
+  }
 }
 
 static int
-silc_server_command_whowas_from_client(SilcServerCommandContext cmd)
+silc_server_command_whowas_process(SilcServerCommandContext cmd)
 {
   SilcServer server = cmd->server;
   char *nick = NULL, *server_name = NULL;
@@ -1116,18 +1184,20 @@ silc_server_command_whowas_from_client(SilcServerCommandContext cmd)
   SilcClientEntry *clients = NULL;
   uint32 clients_count = 0;
   int ret = 0;
+  bool check_global = FALSE;
 
   /* Protocol dictates that we must always send the received WHOWAS request
      to our router if we are normal server, so let's do it now unless we
      are standalone. We will not send any replies to the client until we
      have received reply from the router. */
-  if (server->server_type == SILC_SERVER && 
-      !cmd->pending && !server->standalone) {
+  if (cmd->sock->type == SILC_SOCKET_TYPE_CLIENT &&
+      server->server_type == SILC_SERVER && !cmd->pending && 
+      !server->standalone) {
     SilcBuffer tmpbuf;
     uint16 old_ident;
 
     old_ident = silc_command_get_ident(cmd->payload);
-    silc_command_set_ident(cmd->payload, silc_rng_get_rn16(server->rng));
+    silc_command_set_ident(cmd->payload, ++server->cmd_ident);
     tmpbuf = silc_command_payload_encode_payload(cmd->payload);
 
     /* Send WHOWAS command to our router */
@@ -1139,11 +1209,9 @@ silc_server_command_whowas_from_client(SilcServerCommandContext cmd)
     /* 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_destructor,
                                silc_server_command_whowas,
                                silc_server_command_dup(cmd));
     cmd->pending = TRUE;
-
     silc_command_set_ident(cmd->payload, old_ident);
 
     silc_buffer_free(tmpbuf);
@@ -1154,6 +1222,11 @@ silc_server_command_whowas_from_client(SilcServerCommandContext cmd)
   /* 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;
@@ -1167,58 +1240,7 @@ silc_server_command_whowas_from_client(SilcServerCommandContext cmd)
                                    &clients, &clients_count);
   
   /* Check global list as well */
-  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 (!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:
-  if (clients)
-    silc_free(clients);
-  if (nick)
-    silc_free(nick);
-  if (server_name)
-    silc_free(server_name);
-
-  return ret;
-}
-
-static int
-silc_server_command_whowas_from_server(SilcServerCommandContext cmd)
-{
-  SilcServer server = cmd->server;
-  char *nick = NULL, *server_name = NULL;
-  int count = 0;
-  SilcClientEntry *clients = NULL;
-  uint32 clients_count = 0;
-  int ret = 0;
-
-  /* Parse the whowas request */
-  if (!silc_server_command_whowas_parse(cmd, &nick, &server_name, &count))
-    return 0;
-
-  /* Process the command request. Let's search for the requested client and
-     send reply to the requesting server. */
-
-  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);
-  
-  /* If we are router we will check our global list as well. */
-  if (server->server_type == SILC_ROUTER) {
+  if (check_global) {
     if (!silc_idlist_get_clients_by_nickname(server->global_list, 
                                             nick, server_name,
                                             &clients, &clients_count))
@@ -1226,7 +1248,7 @@ silc_server_command_whowas_from_server(SilcServerCommandContext cmd)
                                      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,
@@ -1235,17 +1257,18 @@ silc_server_command_whowas_from_server(SilcServerCommandContext cmd)
     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:
-  if (clients)
-    silc_free(clients);
-  if (nick)
-    silc_free(nick);
-  if (server_name)
-    silc_free(server_name);
-
+  silc_free(clients);
+  silc_free(nick);
+  silc_free(server_name);
   return ret;
 }
 
@@ -1256,16 +1279,10 @@ SILC_SERVER_CMD_FUNC(whowas)
   SilcServerCommandContext cmd = (SilcServerCommandContext)context;
   int ret = 0;
 
-  SILC_SERVER_COMMAND_CHECK_ARGC(SILC_COMMAND_WHOWAS, cmd, 1, 2);
-
-  if (cmd->sock->type == SILC_SOCKET_TYPE_CLIENT)
-    ret = silc_server_command_whowas_from_client(cmd);
-  else if ((cmd->sock->type == SILC_SOCKET_TYPE_SERVER) ||
-          (cmd->sock->type == SILC_SOCKET_TYPE_ROUTER))
-    ret = silc_server_command_whowas_from_server(cmd);
+  SILC_SERVER_COMMAND_CHECK(SILC_COMMAND_WHOWAS, cmd, 1, 2);
 
-  if (!ret)
-    silc_server_command_free(cmd);
+  ret = silc_server_command_whowas_process(cmd);
+  silc_server_command_free(cmd);
 }
 
 /******************************************************************************
@@ -1274,403 +1291,675 @@ SILC_SERVER_CMD_FUNC(whowas)
 
 ******************************************************************************/
 
-/* Checks that all mandatory fields 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 void 
+silc_server_command_identify_send_router(SilcServerCommandContext cmd)
+{
+  SilcServer server = cmd->server;
+  SilcBuffer tmpbuf;
+  uint16 old_ident;
+
+  old_ident = silc_command_get_ident(cmd->payload);
+  silc_command_set_ident(cmd->payload, ++server->cmd_ident);
+  tmpbuf = silc_command_payload_encode_payload(cmd->payload);
+
+  /* Send 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 char
-silc_server_command_identify_check(SilcServerCommandContext cmd,
-                                  SilcClientEntry *clients,
-                                  uint32 clients_count)
+static int
+silc_server_command_identify_parse(SilcServerCommandContext cmd,
+                                  SilcClientEntry **clients,
+                                  uint32 *clients_count,
+                                  SilcServerEntry **servers,
+                                  uint32 *servers_count,
+                                  SilcChannelEntry **channels,
+                                  uint32 *channels_count,
+                                  uint32 *count)
 {
   SilcServer server = cmd->server;
+  unsigned char *tmp;
+  uint32 len;
+  uint32 argc = silc_argument_get_arg_num(cmd->args);
+  SilcIDPayload idp;
+  bool check_global = FALSE;
+  void *entry;
   int i;
+  bool error = FALSE;
+
+  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)
+    *count = atoi(tmp);
+  else
+    *count = 0;
+
+  return 1;
+}
+
+/* Checks that all mandatory fields in client entry are present. If not
+   then send WHOIS request to the server who owns the client. We use
+   WHOIS because we want to get as much information as possible at once. */
+
+static bool
+silc_server_command_identify_check_client(SilcServerCommandContext cmd,
+                                         SilcClientEntry *clients,
+                                         uint32 clients_count)
+{
+  SilcServer server = cmd->server;
   SilcClientEntry entry;
+  SilcServerResolveContext resolve = NULL, r = NULL;
+  uint32 resolve_count = 0;
+  int i, k;
+  bool no_res = TRUE;
 
   for (i = 0; i < clients_count; i++) {
     entry = clients[i];
-
-    if (!entry || entry->data.registered == FALSE)
+    if (!entry)
       continue;
 
-    if (!entry->nickname) {
-      SilcBuffer tmpbuf;
-      uint16 old_ident;
-      
+    if (entry->nickname || 
+       !(entry->data.status & SILC_IDLIST_STATUS_REGISTERED)) {
       if (!entry->router)
        continue;
-      
-      old_ident = silc_command_get_ident(cmd->payload);
-      silc_command_set_ident(cmd->payload, silc_rng_get_rn16(server->rng));
-      silc_command_set_command(cmd->payload, SILC_COMMAND_WHOIS);
-      tmpbuf = silc_command_payload_encode_payload(cmd->payload);
-      
-      /* Send WHOIS request. We send WHOIS since we're doing the requesting
-        now anyway so make it a good one. */
-      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_WHOIS, 
-                                 silc_command_get_ident(cmd->payload),
-                                 silc_server_command_destructor,
+
+      /* 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;
+         }
+       }
 
-      cmd->pending = TRUE;
-      
-      /* Put old data back to the Command Payload we just changed */
-      silc_command_set_ident(cmd->payload, old_ident);
-      silc_command_set_command(cmd->payload, SILC_COMMAND_IDENTIFY);
+       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++;
+       }
 
-      silc_buffer_free(tmpbuf);
-      return FALSE;
+       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;
+      }
     }
   }
 
-  return TRUE;
+  /* 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,
                                        uint32 clients_count,
+                                       SilcServerEntry *servers,
+                                       uint32 servers_count,
+                                       SilcChannelEntry *channels,
+                                       uint32 channels_count,
                                        int count)
 {
   SilcServer server = cmd->server;
-  char *tmp;
-  int i, k, len;
+  int i, k, len, valid_count;
   SilcBuffer packet, idp;
-  SilcClientEntry entry;
   SilcCommandStatus status;
   uint16 ident = silc_command_get_ident(cmd->payload);
   char nh[256], uh[256];
   SilcSocketConnection hsock;
 
-  len = 0;
-  for (i = 0; i < clients_count; i++)
-    if (clients[i]->data.registered)
-      len++;
-
   status = SILC_STATUS_OK;
-  if (len > 1)
-    status = SILC_STATUS_LIST_START;
 
-  for (i = 0, k = 0; i < clients_count; i++) {
-    entry = clients[i];
+  if (clients) {
+    SilcClientEntry entry;
 
-    if (entry->data.registered == FALSE) {
-      if (clients_count == 1) {
-       SilcBuffer idp = silc_id_payload_encode(entry->id, SILC_ID_CLIENT);
+    /* 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;
+    }
+
+    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, (uint32 *)&len);
        silc_server_command_send_status_data(cmd, SILC_COMMAND_IDENTIFY,
                                             SILC_STATUS_ERR_NO_SUCH_CLIENT_ID,
-                                            2, idp->data, idp->len);
-       silc_buffer_free(idp);
+                                            2, tmp, len);
       }
-      continue;
+      return;
     }
 
-    if (k >= 1)
-      status = SILC_STATUS_LIST_ITEM;
+    /* Process all valid client entries and send command replies */
 
-    if (clients_count > 1 && k == clients_count - 1)
-      status = SILC_STATUS_LIST_END;
+    if (valid_count > 1)
+      status = SILC_STATUS_LIST_START;
 
-    if (count && k - 1 == count)
-      status = SILC_STATUS_LIST_END;
+    for (i = 0, k = 0; i < clients_count; i++) {
+      entry = clients[i];
+      if (!entry)
+       continue;
 
-    if (count && k - 1 > count)
-      break;
+      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;
 
-    /* Send IDENTIFY 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));
+      /* 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);
+       }
+      }
+
+      if (!entry->username) {
+       packet = silc_command_reply_payload_encode_va(SILC_COMMAND_IDENTIFY,
+                                                     status, ident, 2,
+                                                     2, idp->data, idp->len, 
+                                                     3, nh, strlen(nh));
+      } else {
+       strncat(uh, entry->username, strlen(entry->username));
+       if (!strchr(entry->username, '@')) {
+         strncat(uh, "@", 1);
+         hsock = (SilcSocketConnection)entry->connection;
+         len = strlen(hsock->hostname);
+         strncat(uh, hsock->hostname, len);
+       }
+       
+       packet = silc_command_reply_payload_encode_va(SILC_COMMAND_IDENTIFY,
+                                                     status, ident, 3,
+                                                     2, idp->data, idp->len, 
+                                                     3, nh, strlen(nh),
+                                                     4, uh, strlen(uh));
+      }
       
-    strncat(nh, entry->nickname, strlen(entry->nickname));
-    if (!strchr(entry->nickname, '@')) {
-      strncat(nh, "@", 1);
-      len = entry->router ? strlen(entry->router->server_name) :
-       strlen(server->server_name);
-      strncat(nh, entry->router ? entry->router->server_name :
-             server->server_name, len);
-    }
+      silc_server_packet_send(server, cmd->sock, SILC_PACKET_COMMAND_REPLY,
+                             0, packet->data, packet->len, FALSE);
       
-    if (!entry->username) {
-      packet = silc_command_reply_payload_encode_va(SILC_COMMAND_IDENTIFY,
-                                                   status, ident, 2,
-                                                   2, idp->data, idp->len, 
-                                                   3, nh, strlen(nh));
-    } else {
-      strncat(uh, entry->username, strlen(entry->username));
-      if (!strchr(entry->username, '@')) {
-       strncat(uh, "@", 1);
-       hsock = (SilcSocketConnection)entry->connection;
-       len = strlen(hsock->hostname);
-       strncat(uh, hsock->hostname, len);
-      }
+      silc_buffer_free(packet);
+      silc_buffer_free(idp);
       
-      packet = silc_command_reply_payload_encode_va(SILC_COMMAND_IDENTIFY,
-                                                   status, ident, 3,
-                                                   2, idp->data, idp->len, 
-                                                   3, nh, strlen(nh),
-                                                   4, uh, strlen(uh));
+      k++;
     }
-      
-    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_identify_from_client(SilcServerCommandContext cmd)
-{
-  SilcServer server = cmd->server;
-  char *nick = NULL, *server_name = NULL;
-  int count = 0;
-  SilcClientEntry *clients = NULL, entry;
-  SilcClientID **client_id = NULL;
-  uint32 client_id_count = 0, clients_count = 0;
-  int i, ret = 0;
-
-  /* Protocol dictates that we must always send the received IDENTIFY 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 (server->server_type == SILC_SERVER && 
-      !cmd->pending && !server->standalone) {
-    SilcBuffer tmpbuf;
-    uint16 old_ident;
-
-    old_ident = silc_command_get_ident(cmd->payload);
-    silc_command_set_ident(cmd->payload, silc_rng_get_rn16(server->rng));
-    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_destructor,
-                               silc_server_command_identify,
-                               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 (servers) {
+    SilcServerEntry entry;
 
-  /* Parse the IDENTIFY request */
-  if (!silc_server_command_whois_parse(cmd, &client_id, &client_id_count,
-                                      &nick, &server_name, &count,
-                                      SILC_COMMAND_IDENTIFY))
-    return 0;
+    if (status == SILC_STATUS_OK && servers_count > 1)
+      status = SILC_STATUS_LIST_START;
 
-  /* 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], NULL);
-      if (entry) {
-       clients = silc_realloc(clients, sizeof(*clients) * 
-                              (clients_count + 1));
-       clients[clients_count++] = entry;
-      }
-    }
-  } else {
-    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);
-  }
-  
-  /* Check global list as well */
-  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->global_list, 
-                                           client_id[i], NULL);
-      if (entry) {
-       clients = silc_realloc(clients, sizeof(*clients) * 
-                              (clients_count + 1));
-       clients[clients_count++] = entry;
-      }
-    }
-  } else {
-    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) {
-    /* Such a client really does not exist in the SILC network. */
-    if (!client_id_count) {
-      silc_server_command_send_status_data(cmd, SILC_COMMAND_IDENTIFY,
-                                          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_IDENTIFY,
-                                          SILC_STATUS_ERR_NO_SUCH_CLIENT_ID,
-                                          2, idp->data, idp->len);
+    for (i = 0, k = 0; i < servers_count; i++) {
+      entry = servers[i];
+      
+      if (k >= 1)
+       status = SILC_STATUS_LIST_ITEM;
+      if (servers_count > 1 && k == servers_count - 1 && !channels_count)
+       status = SILC_STATUS_LIST_END;
+      if (count && k - 1 == count)
+       status = SILC_STATUS_LIST_END;
+      if (count && k - 1 > count)
+       break;
+      
+      /* Send IDENTIFY reply */
+      idp = silc_id_payload_encode(entry->id, SILC_ID_SERVER);
+      packet = 
+       silc_command_reply_payload_encode_va(SILC_COMMAND_IDENTIFY,
+                                            status, ident, 2,
+                                            2, idp->data, idp->len, 
+                                            3, entry->server_name, 
+                                            entry->server_name ? 
+                                            strlen(entry->server_name) : 0);
+      silc_server_packet_send(server, cmd->sock, SILC_PACKET_COMMAND_REPLY,
+                             0, packet->data, packet->len, FALSE);
+      
+      silc_buffer_free(packet);
       silc_buffer_free(idp);
+      
+      k++;
     }
-    goto out;
   }
 
-  /* Check that all mandatory fields are present and request those data
-     from the server who owns the client if necessary. */
-  if (!silc_server_command_identify_check(cmd, clients, clients_count)) {
-    ret = -1;
-    goto out;
-  }
+  if (channels) {
+    SilcChannelEntry entry;
 
-  /* Send the command reply to the client */
-  silc_server_command_identify_send_reply(cmd, clients, clients_count,
-                                         count);
+    if (status == SILC_STATUS_OK && channels_count > 1)
+      status = SILC_STATUS_LIST_START;
 
- out:
-  if (client_id_count) {
-    for (i = 0; i < client_id_count; i++)
-      silc_free(client_id[i]);
-    silc_free(client_id);
+    for (i = 0, k = 0; i < channels_count; i++) {
+      entry = channels[i];
+      
+      if (k >= 1)
+       status = SILC_STATUS_LIST_ITEM;
+      if (channels_count > 1 && k == channels_count - 1)
+       status = SILC_STATUS_LIST_END;
+      if (count && k - 1 == count)
+       status = SILC_STATUS_LIST_END;
+      if (count && k - 1 > count)
+       break;
+      
+      /* Send IDENTIFY reply */
+      idp = silc_id_payload_encode(entry->id, SILC_ID_CHANNEL);
+      packet = 
+       silc_command_reply_payload_encode_va(SILC_COMMAND_IDENTIFY,
+                                            status, ident, 2,
+                                            2, idp->data, idp->len, 
+                                            3, entry->channel_name, 
+                                            entry->channel_name ? 
+                                            strlen(entry->channel_name): 0);
+      silc_server_packet_send(server, cmd->sock, SILC_PACKET_COMMAND_REPLY,
+                             0, packet->data, packet->len, FALSE);
+      
+      silc_buffer_free(packet);
+      silc_buffer_free(idp);
+      
+      k++;
+    }
   }
-  if (clients)
-    silc_free(clients);
-  if (nick)
-    silc_free(nick);
-  if (server_name)
-    silc_free(server_name);
-
-  return ret;
 }
 
 static int
-silc_server_command_identify_from_server(SilcServerCommandContext cmd)
+silc_server_command_identify_process(SilcServerCommandContext cmd)
 {
-  SilcServer server = cmd->server;
-  char *nick = NULL, *server_name = NULL;
-  int count = 0;
-  SilcClientEntry *clients = NULL, entry;
-  SilcClientID **client_id = NULL;
-  uint32 client_id_count = 0, clients_count = 0;
-  int i, ret = 0;
+  uint32 count = 0;
+  int ret = 0;
+  SilcClientEntry *clients = NULL;
+  SilcServerEntry *servers = NULL;
+  SilcChannelEntry *channels = NULL;
+  uint32 clients_count = 0, servers_count = 0, channels_count = 0;
 
   /* Parse the IDENTIFY request */
-  if (!silc_server_command_whois_parse(cmd, &client_id, &client_id_count,
-                                      &nick, &server_name, &count,
-                                      SILC_COMMAND_IDENTIFY))
-    return 0;
-
-  /* Process the command request. Let's search for the requested client and
-     send reply to the requesting server. */
-
-  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], NULL);
-      if (entry) {
-       clients = silc_realloc(clients, sizeof(*clients) * 
-                              (clients_count + 1));
-       clients[clients_count++] = entry;
-      }
-    }
-  } else {
-    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 we are router we will check our global list as well. */
-  if (server->server_type == SILC_ROUTER) {
-    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->global_list, 
-                                             client_id[i], NULL);
-       if (entry) {
-         clients = silc_realloc(clients, sizeof(*clients) * 
-                                (clients_count + 1));
-         clients[clients_count++] = entry;
-       }
-      }
-    } else {
-      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) {
-    /* Such a client really does not exist in the SILC network. */
-    if (!client_id_count) {
-      silc_server_command_send_status_data(cmd, SILC_COMMAND_IDENTIFY,
-                                          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_IDENTIFY,
-                                          SILC_STATUS_ERR_NO_SUCH_CLIENT_ID,
-                                          2, idp->data, idp->len);
-      silc_buffer_free(idp);
-    }
-    goto out;
-  }
+  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 (!silc_server_command_identify_check(cmd, clients, clients_count)) {
+  if (clients && !silc_server_command_identify_check_client(cmd, clients, 
+                                                           clients_count)) {
     ret = -1;
     goto out;
   }
 
-  /* Send the command reply */
-  silc_server_command_identify_send_reply(cmd, clients, clients_count, count);
+  /* Send the command reply to the client */
+  silc_server_command_identify_send_reply(cmd, 
+                                         clients, clients_count,
+                                         servers, servers_count,
+                                         channels, channels_count, 
+                                         count);
 
  out:
-  if (client_id_count) {
-    for (i = 0; i < client_id_count; i++)
-      silc_free(client_id[i]);
-    silc_free(client_id);
-  }
-  if (clients)
-    silc_free(clients);
-  if (nick)
-    silc_free(nick);
-  if (server_name)
-    silc_free(server_name);
-
+  silc_free(clients);
+  silc_free(servers);
+  silc_free(channels);
   return ret;
 }
 
@@ -1679,32 +1968,10 @@ SILC_SERVER_CMD_FUNC(identify)
   SilcServerCommandContext cmd = (SilcServerCommandContext)context;
   int ret = 0;
 
-  SILC_SERVER_COMMAND_CHECK_ARGC(SILC_COMMAND_IDENTIFY, cmd, 1, 3328);
-
-  if (cmd->sock->type == SILC_SOCKET_TYPE_CLIENT)
-    ret = silc_server_command_identify_from_client(cmd);
-  else if ((cmd->sock->type == SILC_SOCKET_TYPE_SERVER) |
-          (cmd->sock->type == SILC_SOCKET_TYPE_ROUTER))
-    ret = silc_server_command_identify_from_server(cmd);
-
-  if (!ret)
-    silc_server_command_free(cmd);
-}
-
-/* Checks string for bad characters and returns TRUE if they are found. */
+  SILC_SERVER_COMMAND_CHECK(SILC_COMMAND_IDENTIFY, cmd, 1, 3328);
 
-static int silc_server_command_bad_chars(char *nick)
-{
-  if (strchr(nick, '\\')) return TRUE;
-  if (strchr(nick, '\"')) return TRUE;
-  if (strchr(nick, '´')) return TRUE;
-  if (strchr(nick, '`')) return TRUE;
-  if (strchr(nick, '\'')) return TRUE;
-  if (strchr(nick, '*')) return TRUE;
-  if (strchr(nick, '/')) return TRUE;
-  if (strchr(nick, '@')) return TRUE;
-
-  return FALSE;
+  ret = silc_server_command_identify_process(cmd);
+  silc_server_command_free(cmd);
 }
 
 /* Server side of command NICK. Sets nickname for user. Setting
@@ -1716,31 +1983,42 @@ SILC_SERVER_CMD_FUNC(nick)
   SilcServerCommandContext cmd = (SilcServerCommandContext)context;
   SilcClientEntry client = (SilcClientEntry)cmd->sock->user_data;
   SilcServer server = cmd->server;
-  SilcBuffer packet, nidp, oidp;
+  SilcBuffer packet, nidp, oidp = NULL;
   SilcClientID *new_id;
+  uint32 nick_len;
   char *nick;
   uint16 ident = silc_command_get_ident(cmd->payload);
+  int nickfail = 0;
 
   if (cmd->sock->type != SILC_SOCKET_TYPE_CLIENT)
     goto out;
 
-  SILC_SERVER_COMMAND_CHECK_ARGC(SILC_COMMAND_NICK, cmd, 1, 1);
+  SILC_SERVER_COMMAND_CHECK(SILC_COMMAND_NICK, cmd, 1, 1);
 
   /* Check nickname */
-  nick = silc_argument_get_arg_type(cmd->args, 1, NULL);
-  if (silc_server_command_bad_chars(nick) == TRUE) {
+  nick = silc_argument_get_arg_type(cmd->args, 1, &nick_len);
+  if (nick_len > 128)
+    nick[128] = '\0';
+  if (silc_server_name_bad_chars(nick, nick_len) == TRUE) {
     silc_server_command_send_status_reply(cmd, SILC_COMMAND_NICK,
                                          SILC_STATUS_ERR_BAD_NICKNAME);
     goto out;
   }
 
-  if (strlen(nick) > 128)
-    nick[127] = '\0';
+  /* Check for same nickname */
+  if (!strcmp(client->nickname, nick)) {
+    nidp = silc_id_payload_encode(client->id, SILC_ID_CLIENT);
+    goto send_reply;
+  }
 
   /* Create new Client ID */
-  silc_id_create_client_id(cmd->server->id, cmd->server->rng, 
-                          cmd->server->md5hash, nick,
-                          &new_id);
+  while (!silc_id_create_client_id(cmd->server, cmd->server->id, 
+                                  cmd->server->rng, 
+                                  cmd->server->md5hash, nick,
+                                  &new_id)) {
+    nickfail++;
+    snprintf(&nick[strlen(nick) - 1], 1, "%d", nickfail);
+  }
 
   /* Send notify about nickname change to our router. We send the new
      ID and ask to replace it with the old one. If we are router the
@@ -1749,31 +2027,25 @@ SILC_SERVER_CMD_FUNC(nick)
     silc_server_send_notify_nick_change(server, server->router->connection, 
                                        server->server_type == SILC_SERVER ? 
                                        FALSE : TRUE, client->id,
-                                       new_id, SILC_ID_CLIENT_LEN);
-
-  /* Remove old cache entry */
-  silc_idcache_del_by_id(server->local_list->clients, SILC_ID_CLIENT, 
-                        client->id); 
+                                       new_id);
 
   oidp = silc_id_payload_encode(client->id, SILC_ID_CLIENT);
 
+  /* Remove old cache entry */
+  silc_idcache_del_by_context(server->local_list->clients, client);
+
   /* Free old ID */
-  if (client->id) {
-    memset(client->id, 0, SILC_ID_CLIENT_LEN);
-    silc_free(client->id);
-  }
+  silc_free(client->id);
 
   /* Save the nickname as this client is our local client */
-  if (client->nickname)
-    silc_free(client->nickname);
+  silc_free(client->nickname);
 
   client->nickname = strdup(nick);
   client->id = new_id;
 
   /* Update client cache */
   silc_idcache_add(server->local_list->clients, client->nickname, 
-                  strlen(client->nickname), SILC_ID_CLIENT, client->id, 
-                  (void *)client, TRUE, FALSE);
+                  client->id, (void *)client, 0, NULL);
 
   nidp = silc_id_payload_encode(client->id, SILC_ID_CLIENT);
 
@@ -1783,6 +2055,7 @@ SILC_SERVER_CMD_FUNC(nick)
                                      oidp->data, oidp->len, 
                                      nidp->data, nidp->len);
 
+ send_reply:
   /* Send the new Client ID as reply command back to client */
   packet = silc_command_reply_payload_encode_va(SILC_COMMAND_NICK, 
                                                SILC_STATUS_OK, ident, 1, 
@@ -1792,7 +2065,8 @@ SILC_SERVER_CMD_FUNC(nick)
 
   silc_buffer_free(packet);
   silc_buffer_free(nidp);
-  silc_buffer_free(oidp);
+  if (oidp)
+    silc_buffer_free(oidp);
   
  out:
   silc_server_command_free(cmd);
@@ -1807,7 +2081,7 @@ silc_server_command_list_send_reply(SilcServerCommandContext cmd,
                                    SilcChannelEntry *gch,
                                    uint32 gch_count)
 {
-  int i;
+  int i, k;
   SilcBuffer packet, idp;
   SilcChannelEntry entry;
   SilcCommandStatus status;
@@ -1815,31 +2089,34 @@ silc_server_command_list_send_reply(SilcServerCommandContext cmd,
   char *topic;
   unsigned char usercount[4];
   uint32 users;
+  int valid_lcount = 0, valid_rcount = 0;
 
-  for (i = 0; i < lch_count; i++)
+  for (i = 0; i < lch_count; i++) {
     if (lch[i]->mode & SILC_CHANNEL_MODE_SECRET)
       lch[i] = NULL;
-  for (i = 0; i < gch_count; i++)
+    else
+      valid_lcount++;
+  }
+  for (i = 0; i < gch_count; i++) {
     if (gch[i]->mode & SILC_CHANNEL_MODE_SECRET)
       gch[i] = NULL;
+    else
+      valid_rcount++;
+  }
 
   status = SILC_STATUS_OK;
   if ((lch_count + gch_count) > 1)
     status = SILC_STATUS_LIST_START;
 
   /* Local list */
-  for (i = 0; i < lch_count; i++) {
+  for (i = 0, k = 0; i < lch_count; i++) {
     entry = lch[i];
-
     if (!entry)
       continue;
 
-    if (i >= 1)
+    if (k >= 1)
       status = SILC_STATUS_LIST_ITEM;
-
-    if (i == lch_count - 1 && gch_count)
-      break;
-    if (lch_count > 1 && i == lch_count - 1)
+    if (valid_lcount > 1 && k == valid_lcount - 1 && !valid_rcount)
       status = SILC_STATUS_LIST_END;
 
     idp = silc_id_payload_encode(entry->id, SILC_ID_CHANNEL);
@@ -1849,48 +2126,36 @@ silc_server_command_list_send_reply(SilcServerCommandContext cmd,
       memset(usercount, 0, sizeof(usercount));
     } else {
       topic = entry->topic;
-      users = silc_list_count(entry->user_list);
+      users = silc_hash_table_count(entry->user_list);
       SILC_PUT32_MSB(users, usercount);
     }
 
     /* Send the reply */
-    if (topic)
-      packet = 
-       silc_command_reply_payload_encode_va(SILC_COMMAND_LIST, 
-                                            status, ident, 4, 
-                                            2, idp->data, idp->len,
-                                            3, entry->channel_name, 
-                                            strlen(entry->channel_name),
-                                            4, topic, strlen(topic),
-                                            5, usercount, 4);
-    else
-      packet = 
-       silc_command_reply_payload_encode_va(SILC_COMMAND_LIST, 
-                                            status, ident, 3, 
-                                            2, idp->data, idp->len,
-                                            3, entry->channel_name, 
-                                            strlen(entry->channel_name),
-                                            5, usercount, 4);
+    packet = 
+      silc_command_reply_payload_encode_va(SILC_COMMAND_LIST, 
+                                          status, ident, 4,
+                                          2, idp->data, idp->len,
+                                          3, entry->channel_name, 
+                                          strlen(entry->channel_name),
+                                          4, topic, topic ? strlen(topic) : 0,
+                                          5, usercount, 4);
     silc_server_packet_send(cmd->server, cmd->sock, 
                            SILC_PACKET_COMMAND_REPLY, 0, packet->data, 
                            packet->len, FALSE);
     silc_buffer_free(packet);
     silc_buffer_free(idp);
+    k++;
   }
 
-  status = i ? SILC_STATUS_LIST_ITEM : SILC_STATUS_OK;
-
   /* Global list */
-  for (i = 0; i < gch_count; i++) {
+  for (i = 0, k = 0; i < gch_count; i++) {
     entry = gch[i];
-
     if (!entry)
       continue;
 
-    if (i >= 1)
+    if (k >= 1)
       status = SILC_STATUS_LIST_ITEM;
-
-    if (gch_count > 1 && i == lch_count - 1)
+    if (valid_rcount > 1 && k == valid_rcount - 1)
       status = SILC_STATUS_LIST_END;
 
     idp = silc_id_payload_encode(entry->id, SILC_ID_CHANNEL);
@@ -1900,33 +2165,25 @@ silc_server_command_list_send_reply(SilcServerCommandContext cmd,
       memset(usercount, 0, sizeof(usercount));
     } else {
       topic = entry->topic;
-      users = silc_list_count(entry->user_list);
+      users = silc_hash_table_count(entry->user_list);
       SILC_PUT32_MSB(users, usercount);
     }
 
     /* Send the reply */
-    if (topic)
-      packet = 
-       silc_command_reply_payload_encode_va(SILC_COMMAND_LIST, 
-                                            status, ident, 4, 
-                                            2, idp->data, idp->len,
-                                            3, entry->channel_name, 
-                                            strlen(entry->channel_name),
-                                            4, topic, strlen(topic),
-                                            5, usercount, 4);
-    else
-      packet = 
-       silc_command_reply_payload_encode_va(SILC_COMMAND_LIST, 
-                                            status, ident, 3, 
-                                            2, idp->data, idp->len,
-                                            3, entry->channel_name, 
-                                            strlen(entry->channel_name),
-                                            5, usercount, 4);
+    packet = 
+      silc_command_reply_payload_encode_va(SILC_COMMAND_LIST, 
+                                          status, ident, 4,
+                                          2, idp->data, idp->len,
+                                          3, entry->channel_name, 
+                                          strlen(entry->channel_name),
+                                          4, topic, topic ? strlen(topic) : 0,
+                                          5, usercount, 4);
     silc_server_packet_send(cmd->server, cmd->sock, 
                            SILC_PACKET_COMMAND_REPLY, 0, packet->data, 
                            packet->len, FALSE);
     silc_buffer_free(packet);
     silc_buffer_free(idp);
+    k++;
   }
 }
 
@@ -1943,7 +2200,32 @@ SILC_SERVER_CMD_FUNC(list)
   SilcChannelEntry *lchannels = NULL, *gchannels = NULL;
   uint32 lch_count = 0, gch_count = 0;
 
-  SILC_SERVER_COMMAND_CHECK_ARGC(SILC_COMMAND_LIST, cmd, 0, 2);
+  SILC_SERVER_COMMAND_CHECK(SILC_COMMAND_LIST, cmd, 0, 1);
+
+  /* If we are normal server, send the command to router, since we
+     want to know all channels in the network. */
+  if (!cmd->pending && server->server_type == SILC_SERVER && 
+      !server->standalone) {
+    SilcBuffer tmpbuf;
+    uint16 old_ident;
+    
+    old_ident = silc_command_get_ident(cmd->payload);
+    silc_command_set_ident(cmd->payload, ++server->cmd_ident);
+    tmpbuf = silc_command_payload_encode_payload(cmd->payload);
+    silc_server_packet_send(server, server->router->connection,
+                           SILC_PACKET_COMMAND, cmd->packet->flags,
+                           tmpbuf->data, tmpbuf->len, TRUE);
+
+    /* Reprocess this packet after received reply from router */
+    silc_server_command_pending(server, SILC_COMMAND_LIST, 
+                               silc_command_get_ident(cmd->payload),
+                               silc_server_command_list, 
+                               silc_server_command_dup(cmd));
+    cmd->pending = TRUE;
+    silc_command_set_ident(cmd->payload, old_ident);
+    silc_buffer_free(tmpbuf);
+    goto out;
+  }
 
   /* Get Channel ID */
   tmp = silc_argument_get_arg_type(cmd->args, 1, &tmp_len);
@@ -1960,15 +2242,17 @@ SILC_SERVER_CMD_FUNC(list)
   lchannels = silc_idlist_get_channels(server->local_list, channel_id,
                                       &lch_count);
   
-  /* Get the channels from global list if we are router */
-  if (server->server_type == SILC_ROUTER) 
-    gchannels = silc_idlist_get_channels(server->global_list, channel_id,
-                                        &gch_count);
+  /* Get the channels from global list */
+  gchannels = silc_idlist_get_channels(server->global_list, channel_id,
+                                      &gch_count);
 
   /* Send the reply */
   silc_server_command_list_send_reply(cmd, lchannels, lch_count, 
                                      gchannels, gch_count);
 
+  silc_free(lchannels);
+  silc_free(gchannels);
+
  out:
   silc_server_command_free(cmd);
 }
@@ -1989,7 +2273,7 @@ SILC_SERVER_CMD_FUNC(topic)
   uint32 argc, tmp_len;
   uint16 ident = silc_command_get_ident(cmd->payload);
 
-  SILC_SERVER_COMMAND_CHECK_ARGC(SILC_COMMAND_TOPIC, cmd, 1, 2);
+  SILC_SERVER_COMMAND_CHECK(SILC_COMMAND_TOPIC, cmd, 1, 2);
 
   argc = silc_argument_get_arg_num(cmd->args);
 
@@ -2035,11 +2319,13 @@ SILC_SERVER_CMD_FUNC(topic)
       goto out;
     }
 
-    /* See whether has rights to change topic */
-    silc_list_start(channel->user_list);
-    while ((chl = silc_list_get(channel->user_list)) != SILC_LIST_END)
-      if (chl->client == client)
-       break;
+    /* See whether the client is on channel and has rights to change topic */
+    if (!silc_hash_table_find(channel->user_list, client, NULL, 
+                             (void *)&chl)) {
+      silc_server_command_send_status_reply(cmd, SILC_COMMAND_TOPIC,
+                                           SILC_STATUS_ERR_NOT_ON_CHANNEL);
+      goto out;
+    }
 
     if (chl->mode == SILC_CHANNEL_UMODE_NONE) {
       if (channel->mode & SILC_CHANNEL_MODE_TOPIC) {
@@ -2050,21 +2336,21 @@ SILC_SERVER_CMD_FUNC(topic)
     }
 
     /* Set the topic for channel */
-    if (channel->topic)
-      silc_free(channel->topic);
+    silc_free(channel->topic);
     channel->topic = strdup(tmp);
 
     /* Send TOPIC_SET notify type to the network */
     if (!server->standalone)
       silc_server_send_notify_topic_set(server, server->router->connection,
                                        server->server_type == SILC_ROUTER ?
-                                       TRUE : FALSE, channel, client->id,
-                                       SILC_ID_CLIENT_LEN, channel->topic);
+                                       TRUE : FALSE, channel, 
+                                       client->id, SILC_ID_CLIENT,
+                                       channel->topic);
 
     idp = silc_id_payload_encode(client->id, SILC_ID_CLIENT);
 
     /* Send notify about topic change to all clients on the channel */
-    silc_server_send_notify_to_channel(server, NULL, channel, TRUE,
+    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));
@@ -2073,16 +2359,12 @@ SILC_SERVER_CMD_FUNC(topic)
 
   /* Send the topic to client as reply packet */
   idp = silc_id_payload_encode(channel_id, SILC_ID_CHANNEL);
-  if (channel->topic)
-    packet = silc_command_reply_payload_encode_va(SILC_COMMAND_TOPIC, 
-                                                 SILC_STATUS_OK, ident, 2, 
-                                                 2, idp->data, idp->len,
-                                                 3, channel->topic, 
-                                                 strlen(channel->topic));
-  else
-    packet = silc_command_reply_payload_encode_va(SILC_COMMAND_TOPIC, 
-                                                 SILC_STATUS_OK, ident, 1, 
-                                                 2, idp->data, idp->len);
+  packet = silc_command_reply_payload_encode_va(SILC_COMMAND_TOPIC, 
+                                               SILC_STATUS_OK, ident, 2, 
+                                               2, idp->data, idp->len,
+                                               3, channel->topic, 
+                                               channel->topic ? 
+                                               strlen(channel->topic) : 0);
   silc_server_packet_send(cmd->server, cmd->sock, SILC_PACKET_COMMAND_REPLY,
                          0, packet->data, packet->len, FALSE);
 
@@ -2113,7 +2395,7 @@ SILC_SERVER_CMD_FUNC(invite)
   uint32 len;
   uint16 ident = silc_command_get_ident(cmd->payload);
 
-  SILC_SERVER_COMMAND_CHECK_ARGC(SILC_COMMAND_INVITE, cmd, 1, 4);
+  SILC_SERVER_COMMAND_CHECK(SILC_COMMAND_INVITE, cmd, 1, 4);
 
   /* Get Channel ID */
   tmp = silc_argument_get_arg_type(cmd->args, 1, &len);
@@ -2153,22 +2435,19 @@ SILC_SERVER_CMD_FUNC(invite)
   /* Check whether the channel is invite-only channel. If yes then the
      sender of this command must be at least channel operator. */
   if (channel->mode & SILC_CHANNEL_MODE_INVITE) {
-    silc_list_start(channel->user_list);
-    while ((chl = silc_list_get(channel->user_list)) != SILC_LIST_END)
-      if (chl->client == sender) {
-       if (chl->mode == SILC_CHANNEL_UMODE_NONE) {
-         silc_server_command_send_status_reply(cmd, SILC_COMMAND_INVITE,
-                                       SILC_STATUS_ERR_NO_CHANNEL_PRIV);
-         goto out;
-       }
-       break;
-      }
+    silc_hash_table_find(channel->user_list, sender, NULL, (void *)&chl);
+    if (chl->mode == SILC_CHANNEL_UMODE_NONE) {
+      silc_server_command_send_status_reply(cmd, SILC_COMMAND_INVITE,
+                                           SILC_STATUS_ERR_NO_CHANNEL_PRIV);
+      goto out;
+    }
   }
 
   /* Get destination client ID */
   tmp = silc_argument_get_arg_type(cmd->args, 2, &len);
   if (tmp) {
     char invite[512];
+    bool resolve;
 
     dest_id = silc_id_payload_parse_id(tmp, len);
     if (!dest_id) {
@@ -2178,11 +2457,12 @@ SILC_SERVER_CMD_FUNC(invite)
     }
 
     /* Get the client entry */
-    dest = silc_server_get_client_resolve(server, dest_id);
+    dest = silc_server_get_client_resolve(server, dest_id, &resolve);
     if (!dest) {
-      if (server->server_type == SILC_ROUTER) {
-       silc_server_command_send_status_reply(cmd, SILC_COMMAND_INVITE,
-                                    SILC_STATUS_ERR_NO_SUCH_CLIENT_ID);
+      if (server->server_type != SILC_SERVER || !resolve) {
+       silc_server_command_send_status_reply(
+                                       cmd, SILC_COMMAND_INVITE,
+                                       SILC_STATUS_ERR_NO_SUCH_CLIENT_ID);
        goto out;
       }
       
@@ -2190,13 +2470,12 @@ SILC_SERVER_CMD_FUNC(invite)
         receiving the reply to the query. */
       silc_server_command_pending(server, SILC_COMMAND_WHOIS, 
                                  server->cmd_ident,
-                                 silc_server_command_destructor,
                                  silc_server_command_invite, 
                                  silc_server_command_dup(cmd));
       cmd->pending = TRUE;
       silc_free(channel_id);
       silc_free(dest_id);
-      return;
+      goto out;
     }
 
     /* Check whether the requested client is already on the channel. */
@@ -2208,6 +2487,11 @@ SILC_SERVER_CMD_FUNC(invite)
     
     /* Get route to the client */
     dest_sock = silc_server_get_client_route(server, NULL, 0, dest_id, &idata);
+    if (!dest_sock) {
+      silc_server_command_send_status_reply(cmd, SILC_COMMAND_INVITE,
+                                           SILC_STATUS_ERR_NO_SUCH_CLIENT_ID);
+      goto out;
+    }
 
     memset(invite, 0, sizeof(invite));
     strncat(invite, dest->nickname, strlen(dest->nickname));
@@ -2290,27 +2574,31 @@ SILC_SERVER_CMD_FUNC(invite)
     silc_server_send_notify_invite(server, server->router->connection,
                                   server->server_type == SILC_ROUTER ?
                                   TRUE : FALSE, channel,
-                                  sender->id, SILC_ID_CLIENT_LEN,
-                                  add, del);
+                                  sender->id, add, del);
 
   /* Send command reply */
   tmp = silc_argument_get_arg_type(cmd->args, 1, &len);
-  packet = silc_command_reply_payload_encode_va(SILC_COMMAND_INVITE,
-                                               SILC_STATUS_OK, ident, 2,
-                                               2, tmp, len,
-                                               3, channel->invite_list,
-                                               channel->invite_list ?
-                                               strlen(channel->invite_list) :
-                                               0);
+
+  if (add || del)
+    packet = 
+      silc_command_reply_payload_encode_va(SILC_COMMAND_INVITE,
+                                          SILC_STATUS_OK, ident, 2,
+                                          2, tmp, len,
+                                          3, channel->invite_list,
+                                          channel->invite_list ?
+                                          strlen(channel->invite_list) : 0);
+  else
+    packet = 
+      silc_command_reply_payload_encode_va(SILC_COMMAND_INVITE,
+                                          SILC_STATUS_OK, ident, 1,
+                                          2, tmp, len);
   silc_server_packet_send(server, cmd->sock, SILC_PACKET_COMMAND_REPLY, 0, 
                          packet->data, packet->len, FALSE);
   silc_buffer_free(packet);
 
  out:
-  if (dest_id)
-    silc_free(dest_id);
-  if (channel_id)
-    silc_free(channel_id);
+  silc_free(dest_id);
+  silc_free(channel_id);
   silc_server_command_free(cmd);
 }
 
@@ -2351,7 +2639,7 @@ SILC_SERVER_CMD_FUNC(quit)
   unsigned char *tmp = NULL;
   uint32 len = 0;
 
-  SILC_SERVER_COMMAND_CHECK_ARGC(SILC_COMMAND_QUIT, cmd, 0, 1);
+  SILC_SERVER_COMMAND_CHECK(SILC_COMMAND_QUIT, cmd, 0, 1);
 
   if (cmd->sock->type != SILC_SOCKET_TYPE_CLIENT)
     goto out;
@@ -2367,7 +2655,7 @@ SILC_SERVER_CMD_FUNC(quit)
   q->signoff = tmp ? strdup(tmp) : NULL;
 
   /* We quit the connection with little timeout */
-  silc_task_register(server->timeout_queue, sock->sock,
+  silc_schedule_task_add(server->schedule, sock->sock,
                     silc_server_command_quit_cb, (void *)q,
                     0, 200000, SILC_TASK_TIMEOUT, SILC_TASK_PRI_LOW);
 
@@ -2387,8 +2675,9 @@ SILC_SERVER_CMD_FUNC(kill)
   SilcClientID *client_id;
   unsigned char *tmp, *comment;
   uint32 tmp_len, tmp_len2;
+  bool local;
 
-  SILC_SERVER_COMMAND_CHECK_ARGC(SILC_COMMAND_KILL, cmd, 1, 2);
+  SILC_SERVER_COMMAND_CHECK(SILC_COMMAND_KILL, cmd, 1, 2);
 
   if (!client || cmd->sock->type != SILC_SOCKET_TYPE_CLIENT)
     goto out;
@@ -2423,10 +2712,12 @@ SILC_SERVER_CMD_FUNC(kill)
 
   /* Get the client entry */
   remote_client = silc_idlist_find_client_by_id(server->local_list, 
-                                               client_id, NULL);
+                                               client_id, TRUE, NULL);
+  local = TRUE;
   if (!remote_client) {
     remote_client = silc_idlist_find_client_by_id(server->global_list, 
-                                                 client_id, NULL);
+                                                 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);
@@ -2459,15 +2750,13 @@ SILC_SERVER_CMD_FUNC(kill)
   /* Send KILLED notify to primary route */
   if (!server->standalone)
     silc_server_send_notify_killed(server, server->router->connection, TRUE,
-                                  remote_client->id, SILC_ID_CLIENT_LEN,
-                                  comment);
+                                  remote_client->id, comment);
 
   /* Send KILLED notify to the client directly */
   silc_server_send_notify_killed(server, remote_client->connection ? 
                                 remote_client->connection : 
                                 remote_client->router->connection, FALSE,
-                                remote_client->id, SILC_ID_CLIENT_LEN,
-                                comment);
+                                remote_client->id, comment);
 
   /* Remove the client from all channels. This generates new keys to the
      channels as well. */
@@ -2476,15 +2765,23 @@ SILC_SERVER_CMD_FUNC(kill)
 
   /* Remove the client entry, If it is locally connected then we will also
      disconnect the client here */
-  if (remote_client->data.registered && remote_client->connection) {
+  if (remote_client->connection) {
     /* Remove locally conneted client */
     SilcSocketConnection sock = remote_client->connection;
     silc_server_free_client_data(server, sock, remote_client, FALSE, NULL);
     silc_server_close_connection(server, sock);
   } else {
+    /* Update statistics */
+    if (remote_client->connection)
+      server->stat.my_clients--;
+    if (server->server_type == SILC_ROUTER)
+      server->stat.cell_clients--;
+    SILC_OPER_STATS_UPDATE(remote_client, server, SILC_UMODE_SERVER_OPERATOR);
+    SILC_OPER_STATS_UPDATE(remote_client, router, SILC_UMODE_ROUTER_OPERATOR);
+
     /* Remove remote client */
-    if (!silc_idlist_del_client(server->global_list, remote_client))
-      silc_idlist_del_client(server->local_list, remote_client);
+    silc_idlist_del_client(local ? server->local_list :
+                          server->global_list, remote_client);
   }
 
  out:
@@ -2507,7 +2804,7 @@ SILC_SERVER_CMD_FUNC(info)
   SilcServerEntry entry = NULL;
   SilcServerID *server_id = NULL;
 
-  SILC_SERVER_COMMAND_CHECK_ARGC(SILC_COMMAND_INFO, cmd, 0, 2);
+  SILC_SERVER_COMMAND_CHECK(SILC_COMMAND_INFO, cmd, 0, 2);
 
   /* Get server name */
   dest_server = silc_argument_get_arg_type(cmd->args, 1, NULL);
@@ -2526,11 +2823,11 @@ SILC_SERVER_CMD_FUNC(info)
   if (server_id) {
     /* Check whether we have this server cached */
     entry = silc_idlist_find_server_by_id(server->local_list,
-                                         server_id, NULL);
+                                         server_id, TRUE, NULL);
     if (!entry) {
       entry = silc_idlist_find_server_by_id(server->global_list,
-                                           server_id, NULL);
-      if (!entry && server->server_type == SILC_ROUTER) {
+                                           server_id, TRUE, NULL);
+      if (!entry && server->server_type != SILC_SERVER) {
        silc_server_command_send_status_reply(cmd, SILC_COMMAND_INFO,
                                              SILC_STATUS_ERR_NO_SUCH_SERVER);
        goto out;
@@ -2538,7 +2835,12 @@ SILC_SERVER_CMD_FUNC(info)
     }
   }
 
-  if ((!dest_server && !server_id) || 
+  /* Some buggy servers has sent request to router about themselves. */
+  if (server->server_type != SILC_SERVER && cmd->sock->user_data == entry)
+    goto out;
+
+  if ((!dest_server && !server_id && !entry) || (entry && 
+                                                entry == server->id_entry) ||
       (dest_server && !cmd->pending && 
        !strncasecmp(dest_server, server->server_name, strlen(dest_server)))) {
     /* Send our reply */
@@ -2547,10 +2849,10 @@ SILC_SERVER_CMD_FUNC(info)
     memset(info_string, 0, sizeof(info_string));
     snprintf(info_string, sizeof(info_string), 
             "location: %s server: %s admin: %s <%s>",
-            server->config->admin_info->location,
-            server->config->admin_info->server_type,
-            server->config->admin_info->admin_name,
-            server->config->admin_info->admin_email);
+            server->config->server_info->location,
+            server->config->server_info->server_type,
+            server->config->server_info->admin,
+            server->config->server_info->email);
 
     server_info = info_string;
     entry = server->id_entry;
@@ -2558,21 +2860,21 @@ SILC_SERVER_CMD_FUNC(info)
     /* Check whether we have this server cached */
     if (!entry && dest_server) {
       entry = silc_idlist_find_server_by_name(server->global_list,
-                                             dest_server, NULL);
+                                             dest_server, TRUE, NULL);
       if (!entry) {
        entry = silc_idlist_find_server_by_name(server->local_list,
-                                               dest_server, NULL);
+                                               dest_server, TRUE, NULL);
       }
     }
 
     if (!cmd->pending &&
-       server->server_type == SILC_ROUTER && entry && !entry->server_info) {
+       server->server_type != SILC_SERVER && entry && !entry->server_info) {
       /* Send to the server */
       SilcBuffer tmpbuf;
       uint16 old_ident;
 
       old_ident = silc_command_get_ident(cmd->payload);
-      silc_command_set_ident(cmd->payload, silc_rng_get_rn16(server->rng));
+      silc_command_set_ident(cmd->payload, ++server->cmd_ident);
       tmpbuf = silc_command_payload_encode_payload(cmd->payload);
 
       silc_server_packet_send(server, entry->connection,
@@ -2582,13 +2884,12 @@ SILC_SERVER_CMD_FUNC(info)
       /* Reprocess this packet after received reply from router */
       silc_server_command_pending(server, SILC_COMMAND_INFO, 
                                  silc_command_get_ident(cmd->payload),
-                                 silc_server_command_destructor,
                                  silc_server_command_info,
                                  silc_server_command_dup(cmd));
       cmd->pending = TRUE;
       silc_command_set_ident(cmd->payload, old_ident);
       silc_buffer_free(tmpbuf);
-      return;
+      goto out;
     }
 
     if (!entry && !cmd->pending && !server->standalone) {
@@ -2597,7 +2898,7 @@ SILC_SERVER_CMD_FUNC(info)
       uint16 old_ident;
 
       old_ident = silc_command_get_ident(cmd->payload);
-      silc_command_set_ident(cmd->payload, silc_rng_get_rn16(server->rng));
+      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,
@@ -2607,18 +2908,16 @@ SILC_SERVER_CMD_FUNC(info)
       /* Reprocess this packet after received reply from router */
       silc_server_command_pending(server, SILC_COMMAND_INFO, 
                                  silc_command_get_ident(cmd->payload),
-                                 silc_server_command_destructor,
                                  silc_server_command_info,
                                  silc_server_command_dup(cmd));
       cmd->pending = TRUE;
       silc_command_set_ident(cmd->payload, old_ident);
       silc_buffer_free(tmpbuf);
-      return;
+      goto out;
     }
   }
 
-  if (server_id)
-    silc_free(server_id);
+  silc_free(server_id);
 
   if (!entry) {
     silc_server_command_send_status_reply(cmd, SILC_COMMAND_INFO,
@@ -2638,7 +2937,8 @@ SILC_SERVER_CMD_FUNC(info)
                                                3, server_name, 
                                                strlen(server_name),
                                                4, server_info, 
-                                               strlen(server_info));
+                                               server_info ? 
+                                               strlen(server_info) : 0);
   silc_server_packet_send(server, cmd->sock, SILC_PACKET_COMMAND_REPLY, 0, 
                          packet->data, packet->len, FALSE);
     
@@ -2659,7 +2959,7 @@ SILC_SERVER_CMD_FUNC(ping)
   uint32 len;
   unsigned char *tmp;
 
-  SILC_SERVER_COMMAND_CHECK_ARGC(SILC_COMMAND_INFO, cmd, 1, 2);
+  SILC_SERVER_COMMAND_CHECK(SILC_COMMAND_INFO, cmd, 1, 2);
 
   /* Get Server ID */
   tmp = silc_argument_get_arg_type(cmd->args, 1, &len);
@@ -2672,7 +2972,7 @@ SILC_SERVER_CMD_FUNC(ping)
   if (!id)
     goto out;
 
-  if (!SILC_ID_SERVER_COMPARE(id, server->id)) {
+  if (SILC_ID_SERVER_COMPARE(id, server->id)) {
     /* Send our reply */
     silc_server_command_send_status_reply(cmd, SILC_COMMAND_PING,
                                          SILC_STATUS_OK);
@@ -2696,8 +2996,11 @@ static void silc_server_command_join_channel(SilcServer server,
                                             SilcServerCommandContext cmd,
                                             SilcChannelEntry channel,
                                             SilcClientID *client_id,
-                                            int created,
-                                            uint32 umode)
+                                            bool created,
+                                            bool create_key,
+                                            uint32 umode,
+                                            const unsigned char *auth,
+                                            uint32 auth_len)
 {
   SilcSocketConnection sock = cmd->sock;
   unsigned char *tmp;
@@ -2707,7 +3010,9 @@ static void silc_server_command_join_channel(SilcServer server,
   SilcChannelClientEntry chl;
   SilcBuffer reply, chidp, clidp, keyp = NULL, user_list, mode_list;
   uint16 ident = silc_command_get_ident(cmd->payload);
-  char check[512];
+  char check[512], check2[512];
+  bool founder = FALSE;
+  bool resolve;
 
   SILC_LOG_DEBUG(("Start"));
 
@@ -2718,80 +3023,133 @@ static void silc_server_command_join_channel(SilcServer server,
   if (cmd->sock->type == SILC_SOCKET_TYPE_CLIENT) {
     client = (SilcClientEntry)sock->user_data;
   } else {
-    client = silc_idlist_find_client_by_id(server->local_list, client_id, 
-                                          NULL);
-    if (!client)
+    client = silc_server_get_client_resolve(server, client_id, &resolve);
+    if (!client) {
+      if (cmd->pending)
+       goto out;
+
+      if (!resolve) {
+       silc_server_command_send_status_reply(
+                                        cmd, SILC_COMMAND_JOIN,
+                                        SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
+       goto out;
+      }
+
+      /* The client info is being resolved. Reprocess this packet after
+        receiving the reply to the query. */
+      silc_server_command_pending(server, SILC_COMMAND_WHOIS, 
+                                 server->cmd_ident,
+                                 silc_server_command_join, 
+                                 silc_server_command_dup(cmd));
+      cmd->pending = TRUE;
       goto out;
+    }
+
+    cmd->pending = FALSE;
+  }
+
+  /*
+   * Check founder auth payload if provided.  If client can gain founder
+   * privileges it can override various conditions on joining the channel,
+   * and can have directly the founder mode set on the channel.
+   */
+  if (auth && auth_len && channel->mode & SILC_CHANNEL_MODE_FOUNDER_AUTH) {
+    SilcIDListData idata = (SilcIDListData)client;
+
+    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);
+      uint32 auth_data_len = (channel->founder_method == SILC_AUTH_PASSWORD ?
+                             channel->founder_passwd_len : 0);
+
+      /* Check whether the client is to become founder */
+      if (silc_auth_verify_data(auth, auth_len, channel->founder_method, 
+                               auth_data, auth_data_len,
+                               idata->hash, client->id, SILC_ID_CLIENT)) {
+       umode = (SILC_CHANNEL_UMODE_CHANOP | SILC_CHANNEL_UMODE_CHANFO);
+       founder = TRUE;
+      }
+    }
   }
 
   /*
    * Check channel modes
    */
 
-  if (cmd->sock->type == SILC_SOCKET_TYPE_CLIENT) {
+  if (!umode) {
+    memset(check, 0, sizeof(check));
+    memset(check2, 0, sizeof(check2));
     strncat(check, client->nickname, strlen(client->nickname));
-    if (!strchr(client->nickname, '@')) {
-      strncat(check, "@", 1);
-      strncat(check, server->server_name, strlen(server->server_name));
-    }
     strncat(check, "!", 1);
     strncat(check, client->username, strlen(client->username));
     if (!strchr(client->username, '@')) {
       strncat(check, "@", 1);
       strncat(check, cmd->sock->hostname, strlen(cmd->sock->hostname));
     }
-  }
 
-  /* Check invite list if channel is invite-only channel */
-  if (cmd->sock->type == SILC_SOCKET_TYPE_CLIENT && 
-      channel->mode & SILC_CHANNEL_MODE_INVITE) {
-    if (!channel->invite_list) {
-      silc_server_command_send_status_reply(cmd, SILC_COMMAND_JOIN,
-                                           SILC_STATUS_ERR_NOT_INVITED);
-      goto out;
+    strncat(check2, client->nickname, strlen(client->nickname));
+    if (!strchr(client->nickname, '@')) {
+      strncat(check2, "@", 1);
+      strncat(check2, server->server_name, strlen(server->server_name));
     }
-
-    if (!silc_string_match(channel->invite_list, check)) {
-      silc_server_command_send_status_reply(cmd, SILC_COMMAND_JOIN,
-                                           SILC_STATUS_ERR_NOT_INVITED);
-      goto out;
+    strncat(check2, "!", 1);
+    strncat(check2, client->username, strlen(client->username));
+    if (!strchr(client->username, '@')) {
+      strncat(check2, "@", 1);
+      strncat(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_server_command_send_status_reply(cmd, SILC_COMMAND_JOIN,
+                                             SILC_STATUS_ERR_NOT_INVITED);
+       goto out;
+      }
     }
-  }
 
-  /* 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 (cmd->sock->type == SILC_SOCKET_TYPE_CLIENT && channel->ban_list) {
-    if (silc_string_match(channel->ban_list, check)) {
-      silc_server_command_send_status_reply(cmd, SILC_COMMAND_JOIN,
-                             SILC_STATUS_ERR_BANNED_FROM_CHANNEL);
-      goto out;
+    /* Check ban list if it exists. If the client's nickname, server,
+       username and/or hostname is in the ban list the access to the
+       channel is denied. */
+    if (channel->ban_list) {
+      if (silc_string_match(channel->ban_list, check) ||
+         silc_string_match(channel->ban_list, check2)) {
+       silc_server_command_send_status_reply(
+                                     cmd, SILC_COMMAND_JOIN,
+                                     SILC_STATUS_ERR_BANNED_FROM_CHANNEL);
+       goto out;
+      }
+    }
+    
+    /* Check user count limit if set. */
+    if (channel->mode & SILC_CHANNEL_MODE_ULIMIT) {
+      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);
+       goto out;
+      }
     }
   }
 
-  /* Get passphrase */
-  tmp = silc_argument_get_arg_type(cmd->args, 3, &tmp_len);
-  if (tmp) {
-    passphrase = silc_calloc(tmp_len, sizeof(*passphrase));
-    memcpy(passphrase, tmp, tmp_len);
-  }
-  
   /* Check the channel passphrase if set. */
   if (channel->mode & SILC_CHANNEL_MODE_PASSPHRASE) {
-    if (!passphrase || memcmp(channel->passphrase, passphrase,
-                             strlen(channel->passphrase))) {
-      silc_server_command_send_status_reply(cmd, SILC_COMMAND_JOIN,
-                                           SILC_STATUS_ERR_BAD_PASSWORD);
-      goto out;
-    }
-  }
-
-  /* Check user count limit if set. */
-  if (channel->mode & SILC_CHANNEL_MODE_ULIMIT) {
-    if (silc_list_count(channel->user_list) + 1 > 
-       channel->user_limit) {
+    /* Get passphrase */
+    tmp = silc_argument_get_arg_type(cmd->args, 3, &tmp_len);
+    if (tmp) {
+      passphrase = silc_calloc(tmp_len, sizeof(*passphrase));
+      memcpy(passphrase, tmp, tmp_len);
+    }
+  
+    if (!passphrase || !channel->passphrase ||
+        memcmp(passphrase, channel->passphrase, strlen(channel->passphrase))) {
       silc_server_command_send_status_reply(cmd, SILC_COMMAND_JOIN,
-                                           SILC_STATUS_ERR_CHANNEL_IS_FULL);
+                                           SILC_STATUS_ERR_BAD_PASSWORD);
       goto out;
     }
   }
@@ -2808,16 +3166,17 @@ static void silc_server_command_join_channel(SilcServer server,
   }
 
   /* Generate new channel key as protocol dictates */
-  if ((!created && silc_list_count(channel->user_list) > 0) || 
-      !channel->channel_key)
-    silc_server_create_channel_key(server, channel, 0);
+  if (create_key) {
+    if (!silc_server_create_channel_key(server, channel, 0))
+      goto out;
 
-  /* Send the channel key. This is broadcasted to the channel but is not
-     sent to the client who is joining to the channel. */
-  if (!(channel->mode & SILC_CHANNEL_MODE_PRIVKEY))
-    silc_server_send_channel_key(server, NULL, channel, 
-                                server->server_type == SILC_ROUTER ? 
-                                FALSE : !server->standalone);
+    /* Send the channel key. This is broadcasted to the channel but is not
+       sent to the client who is joining to the channel. */
+    if (!(channel->mode & SILC_CHANNEL_MODE_PRIVKEY))
+      silc_server_send_channel_key(server, NULL, channel, 
+                                  server->server_type == SILC_ROUTER ? 
+                                  FALSE : !server->standalone);
+  }
 
   /* Join the client to the channel by adding it to channel's user list.
      Add also the channel to client entry's channels list for fast cross-
@@ -2826,8 +3185,8 @@ static void silc_server_command_join_channel(SilcServer server,
   chl->mode = umode;
   chl->client = client;
   chl->channel = channel;
-  silc_list_add(channel->user_list, chl);
-  silc_list_add(client->channels, chl);
+  silc_hash_table_add(channel->user_list, client, chl);
+  silc_hash_table_add(client->channels, channel, chl);
 
   /* Get users on the channel */
   silc_server_get_users_on_channel(server, channel, &user_list, &mode_list,
@@ -2872,8 +3231,9 @@ static void silc_server_command_join_channel(SilcServer server,
                                         10, channel->topic,
                                         channel->topic ?
                                         strlen(channel->topic) : 0,
-                                        11, channel->hmac->hmac->name,
-                                        strlen(channel->hmac->hmac->name),
+                                        11, silc_hmac_get_name(channel->hmac),
+                                        strlen(silc_hmac_get_name(channel->
+                                                                  hmac)),
                                         12, tmp3, 4,
                                         13, user_list->data, user_list->len,
                                         14, mode_list->data, 
@@ -2883,19 +3243,46 @@ static void silc_server_command_join_channel(SilcServer server,
   silc_server_packet_send(server, sock, SILC_PACKET_COMMAND_REPLY, 0, 
                          reply->data, reply->len, FALSE);
 
-  if (!cmd->pending) {
-    /* Send JOIN notify to locally connected clients on the channel */
-    silc_server_send_notify_to_channel(server, NULL, channel, FALSE,
-                                      SILC_NOTIFY_TYPE_JOIN, 2,
-                                      clidp->data, clidp->len,
-                                      chidp->data, chidp->len);
+  /* Send JOIN notify to locally connected clients on the channel. If
+     we are normal server then router will send or have sent JOIN notify
+     already. However since we've added the client already to our channel
+     we'll ignore it (in packet_receive.c) so we must send it here. If
+     we are router then this will send it to local clients and local
+     servers. */
+  silc_server_send_notify_to_channel(server, NULL, channel, FALSE, 
+                                    SILC_NOTIFY_TYPE_JOIN, 2,
+                                    clidp->data, clidp->len,
+                                    chidp->data, chidp->len);
 
+  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_ID_CLIENT_LEN);
+                                  TRUE : FALSE, 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);
   }
 
   silc_buffer_free(reply);
@@ -2906,8 +3293,7 @@ static void silc_server_command_join_channel(SilcServer server,
   silc_buffer_free(mode_list);
 
  out:
-  if (passphrase)
-    silc_free(passphrase);
+  silc_free(passphrase);
 }
 
 /* Server side of command JOIN. Joins client into requested channel. If 
@@ -2917,14 +3303,15 @@ SILC_SERVER_CMD_FUNC(join)
 {
   SilcServerCommandContext cmd = (SilcServerCommandContext)context;
   SilcServer server = cmd->server;
-  uint32 tmp_len;
+  unsigned char *auth;
+  uint32 tmp_len, auth_len;
   char *tmp, *channel_name = NULL, *cipher, *hmac;
   SilcChannelEntry channel;
   uint32 umode = 0;
-  int created = FALSE;
+  bool created = FALSE, create_key = TRUE;
   SilcClientID *client_id;
 
-  SILC_SERVER_COMMAND_CHECK_ARGC(SILC_COMMAND_JOIN, cmd, 1, 4);
+  SILC_SERVER_COMMAND_CHECK(SILC_COMMAND_JOIN, cmd, 2, 6);
 
   /* Get channel name */
   tmp = silc_argument_get_arg_type(cmd->args, 1, &tmp_len);
@@ -2935,13 +3322,12 @@ SILC_SERVER_CMD_FUNC(join)
   }
   channel_name = tmp;
 
-  if (strlen(channel_name) > 256)
+  if (tmp_len > 256)
     channel_name[255] = '\0';
 
-  if (silc_server_command_bad_chars(channel_name) == TRUE) {
+  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_free(channel_name);
     goto out;
   }
 
@@ -2959,27 +3345,20 @@ SILC_SERVER_CMD_FUNC(join)
     goto out;
   }
 
-  /* Get cipher and hmac name */
+  /* Get cipher, hmac name and auth payload */
   cipher = silc_argument_get_arg_type(cmd->args, 4, NULL);
   hmac = silc_argument_get_arg_type(cmd->args, 5, NULL);
+  auth = silc_argument_get_arg_type(cmd->args, 6, &auth_len);
 
   /* See if the channel exists */
   channel = silc_idlist_find_channel_by_name(server->local_list, 
                                             channel_name, NULL);
 
   if (cmd->sock->type == SILC_SOCKET_TYPE_CLIENT) {
-    /* If this is coming from client the Client ID in the command packet must
-       be same as the client's ID. */
-    if (cmd->sock->type == SILC_SOCKET_TYPE_CLIENT) {
-      SilcClientEntry entry = (SilcClientEntry)cmd->sock->user_data;
-      if (SILC_ID_CLIENT_COMPARE(entry->id, client_id)) {
-       silc_server_command_send_status_reply(cmd, SILC_COMMAND_JOIN,
-                                       SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
-       goto out;
-      }
-    }
+    SilcClientEntry entry = (SilcClientEntry)cmd->sock->user_data;
+    client_id = silc_id_dup(entry->id, SILC_ID_CLIENT);
 
-    if (!channel || !channel->id) {
+    if (!channel || channel->disabled) {
       /* Channel not found */
 
       /* If we are standalone server we don't have a router, we just create 
@@ -2988,26 +3367,34 @@ 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);
          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 
           we will send JOIN command to our router which will handle the
           joining procedure (either creates the channel if it doesn't exist 
           or joins the client to it). */
-       if (server->server_type == SILC_SERVER) {
+       if (server->server_type != SILC_ROUTER) {
          SilcBuffer tmpbuf;
          uint16 old_ident;
+
+         /* If this is pending command callback then we've resolved
+            it and it didn't work, return since we've notified the
+            client already in the command reply callback. */
+         if (cmd->pending)
+           goto out;
          
          old_ident = silc_command_get_ident(cmd->payload);
-         silc_command_set_ident(cmd->payload, silc_rng_get_rn16(server->rng));
+         silc_command_set_ident(cmd->payload, ++server->cmd_ident);
          tmpbuf = silc_command_payload_encode_payload(cmd->payload);
          
          /* Send JOIN command to our router */
@@ -3019,11 +3406,12 @@ SILC_SERVER_CMD_FUNC(join)
          /* Reprocess this packet after received reply from router */
          silc_server_command_pending(server, SILC_COMMAND_JOIN, 
                                      silc_command_get_ident(cmd->payload),
-                                     silc_server_command_destructor,
                                      silc_server_command_join,
                                      silc_server_command_dup(cmd));
          cmd->pending = TRUE;
-         return;
+          silc_command_set_ident(cmd->payload, old_ident);
+         silc_buffer_free(tmpbuf);
+         goto out;
        }
        
        /* We are router and the channel does not seem exist so we will check
@@ -3042,6 +3430,7 @@ SILC_SERVER_CMD_FUNC(join)
 
          umode = (SILC_CHANNEL_UMODE_CHANOP | SILC_CHANNEL_UMODE_CHANFO);
          created = TRUE;
+         create_key = FALSE;
        }
       }
     }
@@ -3049,11 +3438,11 @@ SILC_SERVER_CMD_FUNC(join)
     if (!channel) {
       /* Channel not found */
 
-      /* If the command came from router and/or we are normal server then
+      /* If the command came from router and we are normal server then
         something went wrong with the joining as the channel was not found.
         We can't do anything else but ignore this. */
       if (cmd->sock->type == SILC_SOCKET_TYPE_ROUTER ||
-         server->server_type == SILC_SERVER)
+         server->server_type != SILC_ROUTER)
        goto out;
       
       /* We are router and the channel does not seem exist so we will check
@@ -3072,21 +3461,37 @@ SILC_SERVER_CMD_FUNC(join)
 
        umode = (SILC_CHANNEL_UMODE_CHANOP | SILC_CHANNEL_UMODE_CHANFO);
        created = TRUE;
+       create_key = FALSE;
       }
     }
   }
 
-  /* If the channel does not have global users and is also empty it means the
-     channel was created globally (by our router) and the client will be the
-     channel founder and operator. */
-  if (!channel->global_users && silc_list_count(channel->user_list) == 0) {
-    umode = (SILC_CHANNEL_UMODE_CHANOP | SILC_CHANNEL_UMODE_CHANFO);
-    created = TRUE;            /* Created globally by our router */
+  /* Check whether the channel was created by our router */
+  if (cmd->pending && context2) {
+    SilcServerCommandReplyContext reply = 
+      (SilcServerCommandReplyContext)context2;
+
+    if (silc_command_get(reply->payload) == SILC_COMMAND_JOIN) {
+      tmp = silc_argument_get_arg_type(reply->args, 6, NULL);
+      SILC_GET32_MSB(created, tmp);
+      if (silc_argument_get_arg_type(reply->args, 7, NULL))
+       create_key = FALSE;     /* Router returned the key already */
+    }
+
+    if (silc_command_get(reply->payload) == SILC_COMMAND_WHOIS &&
+       !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))
+    umode = (SILC_CHANNEL_UMODE_CHANOP | SILC_CHANNEL_UMODE_CHANFO);
+
   /* Join to the channel */
   silc_server_command_join_channel(server, cmd, channel, client_id,
-                                  created, umode);
+                                  created, create_key, umode,
+                                  auth, auth_len);
 
   silc_free(client_id);
 
@@ -3106,7 +3511,7 @@ SILC_SERVER_CMD_FUNC(motd)
   uint32 motd_len;
   uint16 ident = silc_command_get_ident(cmd->payload);
   
-  SILC_SERVER_COMMAND_CHECK_ARGC(SILC_COMMAND_MOTD, cmd, 1, 1);
+  SILC_SERVER_COMMAND_CHECK(SILC_COMMAND_MOTD, cmd, 1, 1);
 
   /* Get server name */
   dest_server = silc_argument_get_arg_type(cmd->args, 1, NULL);
@@ -3121,10 +3526,10 @@ SILC_SERVER_CMD_FUNC(motd)
 
     idp = silc_id_payload_encode(server->id_entry->id, SILC_ID_SERVER);
 
-    if (server->config && server->config->motd && 
-       server->config->motd->motd_file) {
+    if (server->config && server->config->server_info &&
+       server->config->server_info->motd_file) {
       /* Send motd */
-      motd = silc_file_read(server->config->motd->motd_file, &motd_len);
+      motd = silc_file_readfile(server->config->server_info->motd_file, &motd_len);
       if (!motd)
        goto out;
       
@@ -3133,7 +3538,6 @@ SILC_SERVER_CMD_FUNC(motd)
                                                    SILC_STATUS_OK, ident, 2,
                                                    2, idp, idp->len,
                                                    3, motd, motd_len);
-      goto out;
     } else {
       /* No motd */
       packet = silc_command_reply_payload_encode_va(SILC_COMMAND_MOTD,
@@ -3150,20 +3554,20 @@ SILC_SERVER_CMD_FUNC(motd)
 
     /* Check whether we have this server cached */
     entry = silc_idlist_find_server_by_name(server->global_list,
-                                           dest_server, NULL);
+                                           dest_server, TRUE, NULL);
     if (!entry) {
       entry = silc_idlist_find_server_by_name(server->local_list,
-                                             dest_server, NULL);
+                                             dest_server, TRUE, NULL);
     }
 
-    if (server->server_type == SILC_ROUTER && !cmd->pending && 
+    if (server->server_type != SILC_SERVER && !cmd->pending && 
        entry && !entry->motd) {
       /* Send to the server */
       SilcBuffer tmpbuf;
       uint16 old_ident;
 
       old_ident = silc_command_get_ident(cmd->payload);
-      silc_command_set_ident(cmd->payload, silc_rng_get_rn16(server->rng));
+      silc_command_set_ident(cmd->payload, ++server->cmd_ident);
       tmpbuf = silc_command_payload_encode_payload(cmd->payload);
 
       silc_server_packet_send(server, entry->connection,
@@ -3173,13 +3577,12 @@ SILC_SERVER_CMD_FUNC(motd)
       /* Reprocess this packet after received reply from router */
       silc_server_command_pending(server, SILC_COMMAND_MOTD, 
                                  silc_command_get_ident(cmd->payload),
-                                 silc_server_command_destructor,
                                  silc_server_command_motd,
                                  silc_server_command_dup(cmd));
       cmd->pending = TRUE;
       silc_command_set_ident(cmd->payload, old_ident);
       silc_buffer_free(tmpbuf);
-      return;
+      goto out;
     }
 
     if (!entry && !cmd->pending && !server->standalone) {
@@ -3188,7 +3591,7 @@ SILC_SERVER_CMD_FUNC(motd)
       uint16 old_ident;
 
       old_ident = silc_command_get_ident(cmd->payload);
-      silc_command_set_ident(cmd->payload, silc_rng_get_rn16(server->rng));
+      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,
@@ -3198,13 +3601,12 @@ SILC_SERVER_CMD_FUNC(motd)
       /* Reprocess this packet after received reply from router */
       silc_server_command_pending(server, SILC_COMMAND_MOTD, 
                                  silc_command_get_ident(cmd->payload),
-                                 silc_server_command_destructor,
                                  silc_server_command_motd,
                                  silc_server_command_dup(cmd));
       cmd->pending = TRUE;
       silc_command_set_ident(cmd->payload, old_ident);
       silc_buffer_free(tmpbuf);
-      return;
+      goto out;
     }
 
     if (!entry) {
@@ -3214,18 +3616,12 @@ SILC_SERVER_CMD_FUNC(motd)
     }
 
     idp = silc_id_payload_encode(server->id_entry->id, SILC_ID_SERVER);
-
-    if (entry->motd)
-      packet = silc_command_reply_payload_encode_va(SILC_COMMAND_MOTD,
-                                                   SILC_STATUS_OK, ident, 2,
-                                                   2, idp, idp->len,
-                                                   3, entry->motd,
-                                                   strlen(entry->motd));
-    else
-      packet = silc_command_reply_payload_encode_va(SILC_COMMAND_MOTD,
-                                                   SILC_STATUS_OK, ident, 1,
-                                                   2, idp, idp->len);
-
+    packet = silc_command_reply_payload_encode_va(SILC_COMMAND_MOTD,
+                                                 SILC_STATUS_OK, ident, 2,
+                                                 2, idp, idp->len,
+                                                 3, entry->motd,
+                                                 entry->motd ? 
+                                                 strlen(entry->motd) : 0);
     silc_server_packet_send(server, cmd->sock, SILC_PACKET_COMMAND_REPLY, 0, 
                            packet->data, packet->len, FALSE);
     silc_buffer_free(packet);
@@ -3253,7 +3649,7 @@ SILC_SERVER_CMD_FUNC(umode)
   if (cmd->sock->type != SILC_SOCKET_TYPE_CLIENT)
     goto out;
 
-  SILC_SERVER_COMMAND_CHECK_ARGC(SILC_COMMAND_UMODE, cmd, 2, 2);
+  SILC_SERVER_COMMAND_CHECK(SILC_COMMAND_UMODE, cmd, 2, 2);
 
   /* Get the client's mode mask */
   tmp_mask = silc_argument_get_arg_type(cmd->args, 2, NULL);
@@ -3276,9 +3672,14 @@ SILC_SERVER_CMD_FUNC(umode)
       goto out;
     }
   } else {
-    if (client->mode & SILC_UMODE_SERVER_OPERATOR)
-      /* Remove the server operator rights */
+    /* Remove the server operator rights */
+    if (client->mode & SILC_UMODE_SERVER_OPERATOR) {
       client->mode &= ~SILC_UMODE_SERVER_OPERATOR;
+      if (client->connection)
+       server->stat.my_server_ops--;
+      if (server->server_type == SILC_ROUTER)
+       server->stat.server_ops--;
+    }
   }
 
   if (mask & SILC_UMODE_ROUTER_OPERATOR) {
@@ -3289,9 +3690,14 @@ SILC_SERVER_CMD_FUNC(umode)
       goto out;
     }
   } else {
-    if (client->mode & SILC_UMODE_ROUTER_OPERATOR)
-      /* Remove the router operator rights */
+    /* Remove the router operator rights */
+    if (client->mode & SILC_UMODE_ROUTER_OPERATOR) {
       client->mode &= ~SILC_UMODE_ROUTER_OPERATOR;
+      if (client->connection)
+       server->stat.my_router_ops--;
+      if (server->server_type == SILC_ROUTER)
+       server->stat.router_ops--;
+    }
   }
 
   if (mask & SILC_UMODE_GONE) {
@@ -3305,8 +3711,7 @@ SILC_SERVER_CMD_FUNC(umode)
   /* Send UMODE change to primary router */
   if (!server->standalone)
     silc_server_send_notify_umode(server, server->router->connection, TRUE,
-                                 client->id, SILC_ID_CLIENT_LEN,
-                                 client->mode);
+                                 client->id, client->mode);
 
   /* Send command reply to sender */
   packet = silc_command_reply_payload_encode_va(SILC_COMMAND_UMODE,
@@ -3343,8 +3748,9 @@ int silc_server_check_cmode_rights(SilcChannelEntry channel,
      modes are available automatically for channel operator. */
 
   if (mode & SILC_CHANNEL_MODE_PRIVKEY) {
-    if (is_op && !is_fo)
-      return FALSE;
+    if (!(channel->mode & SILC_CHANNEL_MODE_PRIVKEY))
+      if (is_op && !is_fo)
+       return FALSE;
   } else {
     if (channel->mode & SILC_CHANNEL_MODE_PRIVKEY) {
       if (is_op && !is_fo)
@@ -3353,8 +3759,9 @@ int silc_server_check_cmode_rights(SilcChannelEntry channel,
   }
   
   if (mode & SILC_CHANNEL_MODE_PASSPHRASE) {
-    if (is_op && !is_fo)
-      return FALSE;
+    if (!(channel->mode & SILC_CHANNEL_MODE_PASSPHRASE))
+      if (is_op && !is_fo)
+       return FALSE;
   } else {
     if (channel->mode & SILC_CHANNEL_MODE_PASSPHRASE) {
       if (is_op && !is_fo)
@@ -3363,8 +3770,9 @@ int silc_server_check_cmode_rights(SilcChannelEntry channel,
   }
 
   if (mode & SILC_CHANNEL_MODE_CIPHER) {
-    if (is_op && !is_fo)
-      return FALSE;
+    if (!(channel->mode & SILC_CHANNEL_MODE_CIPHER))
+      if (is_op && !is_fo)
+       return FALSE;
   } else {
     if (channel->mode & SILC_CHANNEL_MODE_CIPHER) {
       if (is_op && !is_fo)
@@ -3373,8 +3781,9 @@ int silc_server_check_cmode_rights(SilcChannelEntry channel,
   }
   
   if (mode & SILC_CHANNEL_MODE_FOUNDER_AUTH) {
-    if (is_op && !is_fo)
-      return FALSE;
+    if (!(channel->mode & SILC_CHANNEL_MODE_FOUNDER_AUTH))
+      if (is_op && !is_fo)
+       return FALSE;
   } else {
     if (channel->mode & SILC_CHANNEL_MODE_FOUNDER_AUTH) {
       if (is_op && !is_fo)
@@ -3398,11 +3807,11 @@ SILC_SERVER_CMD_FUNC(cmode)
   SilcChannelClientEntry chl;
   SilcBuffer packet, cidp;
   unsigned char *tmp, *tmp_id, *tmp_mask;
-  char *cipher = NULL, *hmac = NULL;
+  char *cipher = NULL, *hmac = NULL, *passphrase = NULL;
   uint32 mode_mask, tmp_len, tmp_len2;
   uint16 ident = silc_command_get_ident(cmd->payload);
 
-  SILC_SERVER_COMMAND_CHECK_ARGC(SILC_COMMAND_CMODE, cmd, 2, 7);
+  SILC_SERVER_COMMAND_CHECK(SILC_COMMAND_CMODE, cmd, 2, 7);
 
   /* Get Channel ID */
   tmp_id = silc_argument_get_arg_type(cmd->args, 1, &tmp_len2);
@@ -3448,10 +3857,7 @@ SILC_SERVER_CMD_FUNC(cmode)
   }
 
   /* Get entry to the channel user list */
-  silc_list_start(channel->user_list);
-  while ((chl = silc_list_get(channel->user_list)) != SILC_LIST_END)
-    if (chl->client == client)
-      break;
+  silc_hash_table_find(channel->user_list, client, NULL, (void *)&chl);
 
   /* Check that client has rights to change any requested channel modes */
   if (!silc_server_check_cmode_rights(channel, chl, mode_mask)) {
@@ -3474,21 +3880,22 @@ SILC_SERVER_CMD_FUNC(cmode)
       /* The mode is removed and we need to generate and distribute
         new channel key. Clients are not using private channel keys
         anymore after this. */
-
-      /* Re-generate channel key */
-      silc_server_create_channel_key(server, channel, 0);
       
+      /* Re-generate channel key */
+      if (!silc_server_create_channel_key(server, channel, 0))
+       goto out;
+       
       /* Send the channel key. This sends it to our local clients and if
         we are normal server to our router as well. */
       silc_server_send_channel_key(server, NULL, channel, 
                                   server->server_type == SILC_ROUTER ? 
                                   FALSE : !server->standalone);
-
+       
       cipher = channel->channel_key->cipher->name;
-      hmac = channel->hmac->hmac->name;
+      hmac = (char *)silc_hmac_get_name(channel->hmac);
     }
   }
-  
+
   if (mode_mask & SILC_CHANNEL_MODE_ULIMIT) {
     /* User limit is set on channel */
     uint32 user_limit;
@@ -3524,7 +3931,7 @@ SILC_SERVER_CMD_FUNC(cmode)
       }
 
       /* Save the passphrase */
-      channel->passphrase = strdup(tmp);
+      passphrase = channel->passphrase = strdup(tmp);
     }
   } else {
     if (channel->mode & SILC_CHANNEL_MODE_PASSPHRASE) {
@@ -3539,6 +3946,7 @@ SILC_SERVER_CMD_FUNC(cmode)
   if (mode_mask & SILC_CHANNEL_MODE_CIPHER) {
     if (!(channel->mode & SILC_CHANNEL_MODE_CIPHER)) {
       /* Cipher to use protect the traffic */
+      SilcCipher newkey, oldkey;
 
       /* Get cipher */
       cipher = silc_argument_get_arg_type(cmd->args, 5, NULL);
@@ -3549,16 +3957,25 @@ SILC_SERVER_CMD_FUNC(cmode)
       }
 
       /* Delete old cipher and allocate the new one */
-      silc_cipher_free(channel->channel_key);
-      if (!silc_cipher_alloc(cipher, &channel->channel_key)) {
+      if (!silc_cipher_alloc(cipher, &newkey)) {
        silc_server_command_send_status_reply(cmd, SILC_COMMAND_CMODE,
                                       SILC_STATUS_ERR_UNKNOWN_ALGORITHM);
        goto out;
       }
 
+      oldkey = channel->channel_key;
+      channel->channel_key = newkey;
+
       /* Re-generate channel key */
-      silc_server_create_channel_key(server, channel, 0);
-    
+      if (!silc_server_create_channel_key(server, channel, 0)) {
+       /* We don't have new key, revert to old one */
+       channel->channel_key = oldkey;
+       goto out;
+      }
+
+      /* Remove old channel key for good */
+      silc_cipher_free(oldkey);
+
       /* Send the channel key. This sends it to our local clients and if
         we are normal server to our router as well. */
       silc_server_send_channel_key(server, NULL, channel, 
@@ -3569,20 +3986,29 @@ SILC_SERVER_CMD_FUNC(cmode)
     if (channel->mode & SILC_CHANNEL_MODE_CIPHER) {
       /* Cipher mode is unset. Remove the cipher and revert back to 
         default cipher */
+      SilcCipher newkey, oldkey;
       cipher = channel->cipher;
 
       /* Delete old cipher and allocate default one */
-      silc_cipher_free(channel->channel_key);
-      if (!silc_cipher_alloc(cipher ? cipher : "aes-256-cbc", 
-                            &channel->channel_key)) {
+      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);
        goto out;
       }
 
+      oldkey = channel->channel_key;
+      channel->channel_key = newkey;
+
       /* Re-generate channel key */
-      silc_server_create_channel_key(server, channel, 0);
+      if (!silc_server_create_channel_key(server, channel, 0)) {
+       /* We don't have new key, revert to old one */
+       channel->channel_key = oldkey;
+       goto out;
+      }
       
+      /* Remove old channel key for good */
+      silc_cipher_free(oldkey);
+
       /* Send the channel key. This sends it to our local clients and if
         we are normal server to our router as well. */
       silc_server_send_channel_key(server, NULL, channel, 
@@ -3595,6 +4021,7 @@ SILC_SERVER_CMD_FUNC(cmode)
     if (!(channel->mode & SILC_CHANNEL_MODE_HMAC)) {
       /* HMAC to use protect the traffic */
       unsigned char hash[32];
+      SilcHmac newhmac;
 
       /* Get hmac */
       hmac = silc_argument_get_arg_type(cmd->args, 6, NULL);
@@ -3605,43 +4032,49 @@ SILC_SERVER_CMD_FUNC(cmode)
       }
 
       /* Delete old hmac and allocate the new one */
-      silc_hmac_free(channel->hmac);
-      if (!silc_hmac_alloc(hmac, NULL, &channel->hmac)) {
+      if (!silc_hmac_alloc(hmac, NULL, &newhmac)) {
        silc_server_command_send_status_reply(cmd, SILC_COMMAND_CMODE,
                                       SILC_STATUS_ERR_UNKNOWN_ALGORITHM);
        goto out;
       }
 
+      silc_hmac_free(channel->hmac);
+      channel->hmac = newhmac;
+
       /* Set the HMAC key out of current channel key. The client must do
         this locally. */
-      silc_hash_make(channel->hmac->hash, channel->key, channel->key_len / 8
-                    hash);
+      silc_hash_make(silc_hmac_get_hash(channel->hmac), channel->key
+                    channel->key_len / 8, hash);
       silc_hmac_set_key(channel->hmac, hash, 
-                       silc_hash_len(channel->hmac->hash));
+                       silc_hash_len(silc_hmac_get_hash(channel->hmac)));
       memset(hash, 0, sizeof(hash));
     }
   } else {
     if (channel->mode & SILC_CHANNEL_MODE_HMAC) {
       /* Hmac mode is unset. Remove the hmac and revert back to 
         default hmac */
+      SilcHmac newhmac;
       unsigned char hash[32];
       hmac = channel->hmac_name;
 
       /* Delete old hmac and allocate default one */
       silc_hmac_free(channel->hmac);
-      if (!silc_hmac_alloc(hmac ? hmac : "hmac-sha1-96", NULL, 
-                          &channel->hmac)) {
+      if (!silc_hmac_alloc(hmac ? hmac : SILC_DEFAULT_HMAC, NULL, &newhmac)) {
        silc_server_command_send_status_reply(cmd, SILC_COMMAND_CMODE,
                                       SILC_STATUS_ERR_UNKNOWN_ALGORITHM);
        goto out;
       }
 
+      silc_hmac_free(channel->hmac);
+      channel->hmac = newhmac;
+
       /* Set the HMAC key out of current channel key. The client must do
         this locally. */
-      silc_hash_make(channel->hmac->hash, channel->key, channel->key_len / 8, 
+      silc_hash_make(silc_hmac_get_hash(channel->hmac), channel->key, 
+                    channel->key_len / 8, 
                     hash);
       silc_hmac_set_key(channel->hmac, hash, 
-                       silc_hash_len(channel->hmac->hash));
+                       silc_hash_len(silc_hmac_get_hash(channel->hmac)));
       memset(hash, 0, sizeof(hash));
     }
   }
@@ -3679,6 +4112,15 @@ SILC_SERVER_CMD_FUNC(cmode)
            silc_calloc(tmp_len + 1, sizeof(*channel->founder_passwd));
          memcpy(channel->founder_passwd, tmp, tmp_len);
          channel->founder_passwd_len = tmp_len;
+       } else {
+         /* Verify the payload before setting the mode */
+         if (!silc_auth_verify(auth, channel->founder_method, 
+                               channel->founder_key, 0, idata->hash,
+                               client->id, SILC_ID_CLIENT)) {
+           silc_server_command_send_status_reply(cmd, SILC_COMMAND_CMODE,
+                                                 SILC_STATUS_ERR_AUTH_FAILED);
+           goto out;
+         }
        }
 
        silc_auth_payload_free(auth);
@@ -3700,14 +4142,16 @@ SILC_SERVER_CMD_FUNC(cmode)
   /* Finally, set the mode */
   channel->mode = mode_mask;
 
-  /* Send CMODE_CHANGE notify */
+  /* 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, 4,
+                                    SILC_NOTIFY_TYPE_CMODE_CHANGE, 5,
                                     cidp->data, cidp->len, 
                                     tmp_mask, 4,
                                     cipher, cipher ? strlen(cipher) : 0,
-                                    hmac, hmac ? strlen(hmac) : 0);
+                                    hmac, hmac ? strlen(hmac) : 0,
+                                    passphrase, passphrase ? 
+                                    strlen(passphrase) : 0);
 
   /* Set CMODE notify type to network */
   if (!server->standalone)
@@ -3715,19 +4159,19 @@ SILC_SERVER_CMD_FUNC(cmode)
                                  server->server_type == SILC_ROUTER ? 
                                  TRUE : FALSE, channel,
                                  mode_mask, client->id, SILC_ID_CLIENT,
-                                 SILC_ID_CLIENT_LEN,
-                                 cipher, hmac);
+                                 cipher, hmac, passphrase);
 
   /* Send command reply to sender */
   packet = silc_command_reply_payload_encode_va(SILC_COMMAND_CMODE,
-                                               SILC_STATUS_OK, ident, 1,
-                                               2, tmp_mask, 4);
+                                               SILC_STATUS_OK, ident, 2,
+                                               2, tmp_id, tmp_len2,
+                                               3, tmp_mask, 4);
   silc_server_packet_send(server, cmd->sock, SILC_PACKET_COMMAND_REPLY, 0, 
                          packet->data, packet->len, FALSE);
     
   silc_buffer_free(packet);
   silc_free(channel_id);
-  silc_free(cidp);
+  silc_buffer_free(cidp);
 
  out:
   silc_server_command_free(cmd);
@@ -3752,7 +4196,7 @@ SILC_SERVER_CMD_FUNC(cumode)
   int notify = FALSE;
   uint16 ident = silc_command_get_ident(cmd->payload);
 
-  SILC_SERVER_COMMAND_CHECK_ARGC(SILC_COMMAND_CUMODE, cmd, 3, 4);
+  SILC_SERVER_COMMAND_CHECK(SILC_COMMAND_CUMODE, cmd, 3, 4);
 
   /* Get Channel ID */
   tmp_ch_id = silc_argument_get_arg_type(cmd->args, 1, &tmp_ch_len);
@@ -3789,13 +4233,8 @@ SILC_SERVER_CMD_FUNC(cumode)
   }
 
   /* Check that client has rights to change other's rights */
-  silc_list_start(channel->user_list);
-  while ((chl = silc_list_get(channel->user_list)) != SILC_LIST_END) {
-    if (chl->client == client) {
-      sender_mask = chl->mode;
-      break;
-    }
-  }
+  silc_hash_table_find(channel->user_list, client, NULL, (void *)&chl);
+  sender_mask = chl->mode;
   
   /* Get the target client's channel mode mask */
   tmp_mask = silc_argument_get_arg_type(cmd->args, 2, NULL);
@@ -3822,10 +4261,10 @@ SILC_SERVER_CMD_FUNC(cumode)
 
   /* Get target client's entry */
   target_client = silc_idlist_find_client_by_id(server->local_list, 
-                                               client_id, NULL);
+                                               client_id, TRUE, NULL);
   if (!target_client) {
     target_client = silc_idlist_find_client_by_id(server->global_list, 
-                                                 client_id, NULL);
+                                                 client_id, TRUE, NULL);
   }
 
   if (target_client != client &&
@@ -3845,10 +4284,8 @@ SILC_SERVER_CMD_FUNC(cumode)
     }
 
     /* Get entry to the channel user list */
-    silc_list_start(channel->user_list);
-    while ((chl = silc_list_get(channel->user_list)) != SILC_LIST_END)
-      if (chl->client == target_client)
-       break;
+    silc_hash_table_find(channel->user_list, target_client, NULL, 
+                        (void *)&chl);
   }
 
   /* 
@@ -3857,53 +4294,57 @@ SILC_SERVER_CMD_FUNC(cumode)
 
   /* If the target client is founder, no one else can change their mode
      but themselves. */
-  if (chl->mode & SILC_CHANNEL_UMODE_CHANFO && chl->client != target_client) {
+  if (chl->mode & SILC_CHANNEL_UMODE_CHANFO && client != target_client) {
     silc_server_command_send_status_reply(cmd, SILC_COMMAND_CUMODE,
-                                         SILC_STATUS_ERR_NOT_YOU);
+                                         SILC_STATUS_ERR_NO_CHANNEL_PRIV);
     goto out;
   }
 
   if (target_mask & SILC_CHANNEL_UMODE_CHANFO) {
-    /* The client tries to claim the founder rights. */
-    unsigned char *tmp_auth;
-    uint32 tmp_auth_len, auth_len;
-    void *auth;
-    
-    if (target_client != client) {
-      silc_server_command_send_status_reply(cmd, SILC_COMMAND_CUMODE,
-                                           SILC_STATUS_ERR_NOT_YOU);
-      goto out;
-    }
+    if (!(chl->mode & SILC_CHANNEL_UMODE_CHANFO)) {
+      /* The client tries to claim the founder rights. */
+      unsigned char *tmp_auth;
+      uint32 tmp_auth_len, auth_len;
+      void *auth;
+      
+      if (target_client != client) {
+       silc_server_command_send_status_reply(cmd, SILC_COMMAND_CUMODE,
+                                             SILC_STATUS_ERR_NOT_YOU);
+       goto out;
+      }
 
-    if (!(channel->mode & SILC_CHANNEL_MODE_FOUNDER_AUTH) ||
-       !channel->founder_key) {
-      silc_server_command_send_status_reply(cmd, SILC_COMMAND_CUMODE,
-                                           SILC_STATUS_ERR_NOT_YOU);
-      goto out;
-    }
+      if (!(channel->mode & SILC_CHANNEL_MODE_FOUNDER_AUTH) ||
+         !channel->founder_key || !idata->public_key ||
+         !silc_pkcs_public_key_compare(channel->founder_key, 
+                                       idata->public_key)) {
+       silc_server_command_send_status_reply(cmd, SILC_COMMAND_CUMODE,
+                                             SILC_STATUS_ERR_NOT_YOU);
+       goto out;
+      }
 
-    tmp_auth = silc_argument_get_arg_type(cmd->args, 4, &tmp_auth_len);
-    if (!tmp_auth) {
-      silc_server_command_send_status_reply(cmd, SILC_COMMAND_CUMODE,
-                                           SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
-      goto out;
-    }
+      tmp_auth = silc_argument_get_arg_type(cmd->args, 4, &tmp_auth_len);
+      if (!tmp_auth) {
+       silc_server_command_send_status_reply(cmd, SILC_COMMAND_CUMODE,
+                                    SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
+       goto out;
+      }
 
-    auth = (channel->founder_method == SILC_AUTH_PASSWORD ?
-           (void *)channel->founder_passwd : (void *)channel->founder_key);
-    auth_len = (channel->founder_method == SILC_AUTH_PASSWORD ?
-               channel->founder_passwd_len : 0);
-    
-    if (!silc_auth_verify_data(tmp_auth, tmp_auth_len,
-                              channel->founder_method, auth, auth_len,
-                              idata->hash, client->id, SILC_ID_CLIENT)) {
-      silc_server_command_send_status_reply(cmd, SILC_COMMAND_CUMODE,
-                                           SILC_STATUS_ERR_AUTH_FAILED);
-      goto out;
+      auth = (channel->founder_method == SILC_AUTH_PASSWORD ?
+             (void *)channel->founder_passwd : (void *)channel->founder_key);
+      auth_len = (channel->founder_method == SILC_AUTH_PASSWORD ?
+                 channel->founder_passwd_len : 0);
+      
+      if (!silc_auth_verify_data(tmp_auth, tmp_auth_len,
+                                channel->founder_method, auth, auth_len,
+                                idata->hash, client->id, SILC_ID_CLIENT)) {
+       silc_server_command_send_status_reply(cmd, SILC_COMMAND_CUMODE,
+                                             SILC_STATUS_ERR_AUTH_FAILED);
+       goto out;
+      }
+      
+      sender_mask = chl->mode |= SILC_CHANNEL_UMODE_CHANFO;
+      notify = TRUE;
     }
-
-    sender_mask = chl->mode |= SILC_CHANNEL_UMODE_CHANFO;
-    notify = TRUE;
   } else {
     if (chl->mode & SILC_CHANNEL_UMODE_CHANFO) {
       if (target_client == client) {
@@ -3951,7 +4392,7 @@ SILC_SERVER_CMD_FUNC(cumode)
 
   /* Send notify to channel, notify only if mode was actually changed. */
   if (notify) {
-    silc_server_send_notify_to_channel(server, NULL, channel, FALSE,
+    silc_server_send_notify_to_channel(server, NULL, channel, FALSE, 
                                       SILC_NOTIFY_TYPE_CUMODE_CHANGE, 3,
                                       idp->data, idp->len,
                                       tmp_mask, 4, 
@@ -3963,16 +4404,16 @@ SILC_SERVER_CMD_FUNC(cumode)
                                     server->server_type == SILC_ROUTER ? 
                                     TRUE : FALSE, channel,
                                     target_mask, client->id, 
-                                    SILC_ID_CLIENT_LEN,
-                                    target_client->id, 
-                                    SILC_ID_CLIENT_LEN);
+                                    SILC_ID_CLIENT,
+                                    target_client->id);
   }
 
   /* Send command reply to sender */
   packet = silc_command_reply_payload_encode_va(SILC_COMMAND_CUMODE,
-                                               SILC_STATUS_OK, ident, 2,
+                                               SILC_STATUS_OK, ident, 3,
                                                2, tmp_mask, 4,
-                                               3, tmp_id, tmp_len);
+                                               3, tmp_ch_id, tmp_ch_len,
+                                               4, tmp_id, tmp_len);
   silc_server_packet_send(server, cmd->sock, SILC_PACKET_COMMAND_REPLY, 0, 
                          packet->data, packet->len, FALSE);
     
@@ -3998,10 +4439,10 @@ SILC_SERVER_CMD_FUNC(kick)
   SilcChannelEntry channel;
   SilcChannelClientEntry chl;
   SilcBuffer idp;
-  uint32 tmp_len;
-  unsigned char *tmp, *comment;
+  uint32 tmp_len, target_idp_len;
+  unsigned char *tmp, *comment, *target_idp;
 
-  SILC_SERVER_COMMAND_CHECK_ARGC(SILC_COMMAND_LEAVE, cmd, 1, 3);
+  SILC_SERVER_COMMAND_CHECK(SILC_COMMAND_LEAVE, cmd, 1, 3);
 
   /* Get Channel ID */
   tmp = silc_argument_get_arg_type(cmd->args, 1, &tmp_len);
@@ -4038,26 +4479,21 @@ SILC_SERVER_CMD_FUNC(kick)
   }
 
   /* Check that the kicker is channel operator or channel founder */
-  silc_list_start(channel->user_list);
-  while ((chl = silc_list_get(channel->user_list)) != SILC_LIST_END) {
-    if (chl->client == client) {
-      if (chl->mode == SILC_CHANNEL_UMODE_NONE) {
-       silc_server_command_send_status_reply(cmd, SILC_COMMAND_KICK,
-                                             SILC_STATUS_ERR_NO_CHANNEL_PRIV);
-       goto out;
-      }
-      break;
-    }
+  silc_hash_table_find(channel->user_list, client, NULL, (void *)&chl);
+  if (chl->mode == SILC_CHANNEL_UMODE_NONE) {
+    silc_server_command_send_status_reply(cmd, SILC_COMMAND_KICK,
+                                         SILC_STATUS_ERR_NO_CHANNEL_PRIV);
+    goto out;
   }
   
   /* Get target Client ID */
-  tmp = silc_argument_get_arg_type(cmd->args, 2, &tmp_len);
-  if (!tmp) {
+  target_idp = silc_argument_get_arg_type(cmd->args, 2, &target_idp_len);
+  if (!target_idp) {
     silc_server_command_send_status_reply(cmd, SILC_COMMAND_KICK,
                                          SILC_STATUS_ERR_NO_CLIENT_ID);
     goto out;
   }
-  client_id = silc_id_payload_parse_id(tmp, tmp_len);
+  client_id = silc_id_payload_parse_id(target_idp, target_idp_len);
   if (!client_id) {
     silc_server_command_send_status_reply(cmd, SILC_COMMAND_KICK,
                                          SILC_STATUS_ERR_NO_CLIENT_ID);
@@ -4066,24 +4502,19 @@ SILC_SERVER_CMD_FUNC(kick)
 
   /* Get target client's entry */
   target_client = silc_idlist_find_client_by_id(server->local_list, 
-                                               client_id, NULL);
+                                               client_id, TRUE, NULL);
   if (!target_client) {
     target_client = silc_idlist_find_client_by_id(server->global_list, 
-                                                 client_id, NULL);
+                                                 client_id, TRUE, NULL);
   }
 
   /* Check that the target client is not channel founder. Channel founder
      cannot be kicked from the channel. */
-  silc_list_start(channel->user_list);
-  while ((chl = silc_list_get(channel->user_list)) != SILC_LIST_END) {
-    if (chl->client == target_client) {
-      if (chl->mode & SILC_CHANNEL_UMODE_CHANFO) {
-       silc_server_command_send_status_reply(cmd, SILC_COMMAND_KICK,
-                                 SILC_STATUS_ERR_NO_CHANNEL_FOPRIV);
-       goto out;
-      }
-      break;
-    }
+  silc_hash_table_find(channel->user_list, target_client, NULL, (void *)&chl);
+  if (chl->mode & SILC_CHANNEL_UMODE_CHANFO) {
+    silc_server_command_send_status_reply(cmd, SILC_COMMAND_KICK,
+                                         SILC_STATUS_ERR_NO_CHANNEL_FOPRIV);
+    goto out;
   }
   
   /* Check whether target client is on the channel */
@@ -4104,12 +4535,12 @@ SILC_SERVER_CMD_FUNC(kick)
                                        SILC_STATUS_OK);
 
   /* Send KICKED notify to local clients on the channel */
-  idp = silc_id_payload_encode(target_client->id, SILC_ID_CLIENT);
+  idp = silc_id_payload_encode(client->id, SILC_ID_CLIENT);
   silc_server_send_notify_to_channel(server, NULL, channel, FALSE,
-                                    SILC_NOTIFY_TYPE_KICKED, 
-                                    comment ? 2 : 1,
-                                    idp->data, idp->len,
-                                    comment, comment ? strlen(comment) : 0);
+                                    SILC_NOTIFY_TYPE_KICKED, 3,
+                                    target_idp, target_idp_len,
+                                    comment, comment ? strlen(comment) : 0,
+                                    idp->data, idp->len);
   silc_buffer_free(idp);
 
   /* Remove the client from the channel. If the channel does not exist
@@ -4124,12 +4555,12 @@ SILC_SERVER_CMD_FUNC(kick)
     silc_server_send_notify_kicked(server, server->router->connection,
                                   server->server_type == SILC_ROUTER ?
                                   TRUE : FALSE, channel,
-                                  target_client->id, SILC_ID_CLIENT_LEN,
-                                  comment);
+                                  target_client->id, client->id, comment);
 
   if (!(channel->mode & SILC_CHANNEL_MODE_PRIVKEY)) {
     /* Re-generate channel key */
-    silc_server_create_channel_key(server, channel, 0);
+    if (!silc_server_create_channel_key(server, channel, 0))
+      goto out;
     
     /* Send the channel key to the channel. The key of course is not sent
        to the client who was kicked off the channel. */
@@ -4152,10 +4583,10 @@ SILC_SERVER_CMD_FUNC(oper)
   SilcClientEntry client = (SilcClientEntry)cmd->sock->user_data;
   unsigned char *username, *auth;
   uint32 tmp_len;
-  SilcServerConfigSectionAdminConnection *admin;
+  SilcServerConfigSectionAdmin *admin;
   SilcIDListData idata = (SilcIDListData)client;
 
-  SILC_SERVER_COMMAND_CHECK_ARGC(SILC_COMMAND_OPER, cmd, 1, 2);
+  SILC_SERVER_COMMAND_CHECK(SILC_COMMAND_OPER, cmd, 1, 2);
 
   if (!client || cmd->sock->type != SILC_SOCKET_TYPE_CLIENT)
     goto out;
@@ -4201,11 +4632,16 @@ SILC_SERVER_CMD_FUNC(oper)
   /* Client is now server operator */
   client->mode |= SILC_UMODE_SERVER_OPERATOR;
 
+  /* Update statistics */
+  if (client->connection)
+    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, SILC_ID_CLIENT_LEN,
-                                 client->mode);
+                                 client->id, client->mode);
 
   /* Send reply to the sender */
   silc_server_command_send_status_reply(cmd, SILC_COMMAND_OPER,
@@ -4225,16 +4661,19 @@ SILC_SERVER_CMD_FUNC(silcoper)
   SilcClientEntry client = (SilcClientEntry)cmd->sock->user_data;
   unsigned char *username, *auth;
   uint32 tmp_len;
-  SilcServerConfigSectionAdminConnection *admin;
+  SilcServerConfigSectionAdmin *admin;
   SilcIDListData idata = (SilcIDListData)client;
 
-  SILC_SERVER_COMMAND_CHECK_ARGC(SILC_COMMAND_SILCOPER, cmd, 1, 2);
+  SILC_SERVER_COMMAND_CHECK(SILC_COMMAND_SILCOPER, cmd, 1, 2);
 
-  if (server->server_type == SILC_SERVER)
+  if (!client || cmd->sock->type != SILC_SOCKET_TYPE_CLIENT)
     goto out;
 
-  if (!client || cmd->sock->type != SILC_SOCKET_TYPE_CLIENT)
+  if (server->server_type != SILC_ROUTER) {
+    silc_server_command_send_status_reply(cmd, SILC_COMMAND_SILCOPER,
+                                         SILC_STATUS_ERR_AUTH_FAILED);
     goto out;
+  }
 
   /* Get the username */
   username = silc_argument_get_arg_type(cmd->args, 1, &tmp_len);
@@ -4277,11 +4716,16 @@ SILC_SERVER_CMD_FUNC(silcoper)
   /* Client is now router operator */
   client->mode |= SILC_UMODE_ROUTER_OPERATOR;
 
+  /* Update statistics */
+  if (client->connection)
+    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, SILC_ID_CLIENT_LEN,
-                                 client->mode);
+                                 client->id, client->mode);
 
   /* Send reply to the sender */
   silc_server_command_send_status_reply(cmd, SILC_COMMAND_SILCOPER,
@@ -4303,7 +4747,7 @@ SILC_SERVER_CMD_FUNC(connect)
   uint32 tmp_len;
   uint32 port = SILC_PORT;
 
-  SILC_SERVER_COMMAND_CHECK_ARGC(SILC_COMMAND_CONNECT, cmd, 1, 2);
+  SILC_SERVER_COMMAND_CHECK(SILC_COMMAND_CONNECT, cmd, 1, 2);
 
   if (!client || cmd->sock->type != SILC_SOCKET_TYPE_CLIENT)
     goto out;
@@ -4365,7 +4809,7 @@ SILC_SERVER_CMD_FUNC(ban)
   if (cmd->sock->type != SILC_SOCKET_TYPE_CLIENT)
     goto out;
 
-  SILC_SERVER_COMMAND_CHECK_ARGC(SILC_COMMAND_BAN, cmd, 0, 3);
+  SILC_SERVER_COMMAND_CHECK(SILC_COMMAND_BAN, cmd, 0, 3);
 
   /* Get Channel ID */
   id = silc_argument_get_arg_type(cmd->args, 1, &id_len);
@@ -4400,10 +4844,11 @@ SILC_SERVER_CMD_FUNC(ban)
   }
 
   /* Get entry to the channel user list */
-  silc_list_start(channel->user_list);
-  while ((chl = silc_list_get(channel->user_list)) != SILC_LIST_END)
-    if (chl->client == client)
-      break;
+  if (!silc_hash_table_find(channel->user_list, client, NULL, (void *)&chl)) {
+    silc_server_command_send_status_reply(cmd, SILC_COMMAND_BAN,
+                                         SILC_STATUS_ERR_NOT_ON_CHANNEL);
+    goto out;
+  }
 
   /* The client must be at least channel operator. */
   if (!(chl->mode & SILC_CHANNEL_UMODE_CHANOP)) {
@@ -4458,27 +4903,20 @@ SILC_SERVER_CMD_FUNC(ban)
                                TRUE : FALSE, channel, add, del);
 
   /* Send the reply back to the client */
-  if (channel->ban_list)
-    packet = 
-      silc_command_reply_payload_encode_va(SILC_COMMAND_BAN,
-                                          SILC_STATUS_OK, ident, 2,
-                                          2, id, id_len,
-                                          3, channel->ban_list, 
-                                          strlen(channel->ban_list) - 1);
-  else
-    packet = 
-      silc_command_reply_payload_encode_va(SILC_COMMAND_BAN,
-                                          SILC_STATUS_OK, ident, 1,
-                                          2, id, id_len);
-
+  packet = 
+    silc_command_reply_payload_encode_va(SILC_COMMAND_BAN,
+                                        SILC_STATUS_OK, ident, 2,
+                                        2, id, id_len,
+                                        3, channel->ban_list, 
+                                        channel->ban_list ? 
+                                        strlen(channel->ban_list) -1 : 0);
   silc_server_packet_send(server, cmd->sock, SILC_PACKET_COMMAND_REPLY, 0, 
                          packet->data, packet->len, FALSE);
     
   silc_buffer_free(packet);
 
  out:
-  if (channel_id)
-    silc_free(channel_id);
+  silc_free(channel_id);
   silc_server_command_free(cmd);
 }
 
@@ -4496,7 +4934,7 @@ SILC_SERVER_CMD_FUNC(close)
   unsigned char *name;
   uint32 port = SILC_PORT;
 
-  SILC_SERVER_COMMAND_CHECK_ARGC(SILC_COMMAND_CLOSE, cmd, 1, 2);
+  SILC_SERVER_COMMAND_CHECK(SILC_COMMAND_CLOSE, cmd, 1, 2);
 
   if (!client || cmd->sock->type != SILC_SOCKET_TYPE_CLIENT)
     goto out;
@@ -4522,7 +4960,10 @@ SILC_SERVER_CMD_FUNC(close)
     SILC_GET32_MSB(port, tmp);
 
   server_entry = silc_idlist_find_server_by_conn(server->local_list,
-                                                name, port, NULL);
+                                                name, port, FALSE, NULL);
+  if (!server_entry)
+    server_entry = silc_idlist_find_server_by_conn(server->global_list,
+                                                  name, port, FALSE, NULL);
   if (!server_entry) {
     silc_server_command_send_status_reply(cmd, SILC_COMMAND_CLOSE,
                                          SILC_STATUS_ERR_NO_SERVER_ID);
@@ -4535,7 +4976,16 @@ SILC_SERVER_CMD_FUNC(close)
 
   /* Close the connection to the server */
   sock = (SilcSocketConnection)server_entry->connection;
-  silc_server_free_sock_user_data(server, sock);
+
+  /* If we shutdown primary router connection manually then don't trigger
+     any reconnect or backup router connections, by setting the router
+     to NULL here. */
+  if (server->router == server_entry) {
+    server->id_entry->router = NULL;
+    server->router = NULL;
+    server->standalone = TRUE;
+  }
+  silc_server_free_sock_user_data(server, sock, NULL);
   silc_server_close_connection(server, sock);
   
  out:
@@ -4551,7 +5001,7 @@ SILC_SERVER_CMD_FUNC(shutdown)
   SilcServer server = cmd->server;
   SilcClientEntry client = (SilcClientEntry)cmd->sock->user_data;
 
-  SILC_SERVER_COMMAND_CHECK_ARGC(SILC_COMMAND_SHUTDOWN, cmd, 0, 0);
+  SILC_SERVER_COMMAND_CHECK(SILC_COMMAND_SHUTDOWN, cmd, 0, 0);
 
   if (!client || cmd->sock->type != SILC_SOCKET_TYPE_CLIENT)
     goto out;
@@ -4588,7 +5038,7 @@ SILC_SERVER_CMD_FUNC(leave)
   uint32 len;
   unsigned char *tmp;
 
-  SILC_SERVER_COMMAND_CHECK_ARGC(SILC_COMMAND_LEAVE, cmd, 1, 2);
+  SILC_SERVER_COMMAND_CHECK(SILC_COMMAND_LEAVE, cmd, 1, 2);
 
   /* Get Channel ID */
   tmp = silc_argument_get_arg_type(cmd->args, 1, &len);
@@ -4627,8 +5077,7 @@ SILC_SERVER_CMD_FUNC(leave)
   if (!server->standalone)
     silc_server_send_notify_leave(server, server->router->connection,
                                  server->server_type == SILC_ROUTER ?
-                                 TRUE : FALSE, channel, id_entry->id,
-                                 SILC_ID_CLIENT_LEN);
+                                 TRUE : FALSE, channel, id_entry->id);
 
   silc_server_command_send_status_reply(cmd, SILC_COMMAND_LEAVE,
                                        SILC_STATUS_OK);
@@ -4641,7 +5090,8 @@ SILC_SERVER_CMD_FUNC(leave)
 
   if (!(channel->mode & SILC_CHANNEL_MODE_PRIVKEY)) {
     /* Re-generate channel key */
-    silc_server_create_channel_key(server, channel, 0);
+    if (!silc_server_create_channel_key(server, channel, 0))
+      goto out;
 
     /* Send the channel key */
     silc_server_send_channel_key(server, NULL, channel, 
@@ -4649,11 +5099,8 @@ SILC_SERVER_CMD_FUNC(leave)
                                 FALSE : !server->standalone);
   }
 
-  silc_free(id);
-
  out:
-  if (id)
-    silc_free(id);
+  silc_free(id);
   silc_server_command_free(cmd);
 }
 
@@ -4666,8 +5113,8 @@ SILC_SERVER_CMD_FUNC(users)
   SilcServerCommandContext cmd = (SilcServerCommandContext)context;
   SilcServer server = cmd->server;
   SilcChannelEntry channel;
-  SilcChannelID *id;
-  SilcBuffer packet;
+  SilcChannelID *id = NULL;
+  SilcBuffer packet, idp;
   unsigned char *channel_id;
   uint32 channel_id_len;
   SilcBuffer client_id_list;
@@ -4675,33 +5122,46 @@ SILC_SERVER_CMD_FUNC(users)
   unsigned char lc[4];
   uint32 list_count = 0;
   uint16 ident = silc_command_get_ident(cmd->payload);
+  char *channel_name;
 
-  SILC_SERVER_COMMAND_CHECK_ARGC(SILC_COMMAND_USERS, cmd, 1, 1);
+  SILC_SERVER_COMMAND_CHECK(SILC_COMMAND_USERS, cmd, 1, 2);
 
   /* Get Channel ID */
   channel_id = silc_argument_get_arg_type(cmd->args, 1, &channel_id_len);
-  if (!channel_id) {
+
+  /* Get channel name */
+  channel_name = silc_argument_get_arg_type(cmd->args, 2, NULL);
+
+  if (!channel_id && !channel_name) {
     silc_server_command_send_status_reply(cmd, SILC_COMMAND_USERS,
                                          SILC_STATUS_ERR_NO_CHANNEL_ID);
     goto out;
   }
-  id = silc_id_payload_parse_id(channel_id, channel_id_len);
-  if (!id) {
-    silc_server_command_send_status_reply(cmd, SILC_COMMAND_USERS,
-                                         SILC_STATUS_ERR_NO_CHANNEL_ID);
-    goto out;
+
+  if (channel_id) {
+    id = silc_id_payload_parse_id(channel_id, channel_id_len);
+    if (!id) {
+      silc_server_command_send_status_reply(cmd, SILC_COMMAND_USERS,
+                                           SILC_STATUS_ERR_NO_CHANNEL_ID);
+      goto out;
+    }
   }
 
   /* If we are server and we don't know about this channel we will send
      the command to our router. If we know about the channel then we also
      have the list of users already. */
-  channel = silc_idlist_find_channel_by_id(server->local_list, id, NULL);
-  if (!channel) {
-    if (server->server_type == SILC_SERVER && !server->standalone &&
+  if (id)
+    channel = silc_idlist_find_channel_by_id(server->local_list, id, NULL);
+  else
+    channel = silc_idlist_find_channel_by_name(server->local_list, 
+                                              channel_name, NULL);
+
+  if (!channel || channel->disabled) {
+    if (server->server_type != SILC_ROUTER && !server->standalone &&
        !cmd->pending) {
       SilcBuffer tmpbuf;
       
-      silc_command_set_ident(cmd->payload, silc_rng_get_rn16(server->rng));
+      silc_command_set_ident(cmd->payload, ++server->cmd_ident);
       tmpbuf = silc_command_payload_encode_payload(cmd->payload);
       
       /* Send USERS command */
@@ -4712,19 +5172,21 @@ SILC_SERVER_CMD_FUNC(users)
       /* Reprocess this packet after received reply */
       silc_server_command_pending(server, SILC_COMMAND_USERS, 
                                  silc_command_get_ident(cmd->payload),
-                                 silc_server_command_destructor,
                                  silc_server_command_users,
                                  silc_server_command_dup(cmd));
       cmd->pending = TRUE;
       silc_command_set_ident(cmd->payload, ident);
-      
       silc_buffer_free(tmpbuf);
       silc_free(id);
-      return;
+      goto out;
     }
 
-    /* We are router and we will check the global list as well. */
-    channel = silc_idlist_find_channel_by_id(server->global_list, id, NULL);
+    /* Check the global list as well. */
+    if (id)
+      channel = silc_idlist_find_channel_by_id(server->global_list, id, NULL);
+    else
+      channel = silc_idlist_find_channel_by_name(server->global_list, 
+                                                channel_name, NULL);
     if (!channel) {
       /* Channel really does not exist */
       silc_server_command_send_status_reply(cmd, SILC_COMMAND_USERS,
@@ -4733,6 +5195,24 @@ SILC_SERVER_CMD_FUNC(users)
     }
   }
 
+  /* If the channel is private or secret do not send anything, unless the
+     user requesting this command is on the channel. */
+  if (cmd->sock->type == SILC_SOCKET_TYPE_CLIENT) {
+    if (channel->mode & (SILC_CHANNEL_MODE_PRIVATE | SILC_CHANNEL_MODE_SECRET)
+       && !silc_server_client_on_channel(cmd->sock->user_data, channel)) {
+      silc_server_command_send_status_reply(cmd, SILC_COMMAND_USERS,
+                                           SILC_STATUS_ERR_NO_SUCH_CHANNEL);
+      goto out;
+    }
+  } else {
+    if (channel->mode & 
+       (SILC_CHANNEL_MODE_PRIVATE | SILC_CHANNEL_MODE_SECRET)) {
+      silc_server_command_send_status_reply(cmd, SILC_COMMAND_USERS,
+                                           SILC_STATUS_ERR_NO_SUCH_CHANNEL);
+      goto out;
+    }
+  }
+
   /* Get the users list */
   silc_server_get_users_on_channel(server, channel, &client_id_list,
                                   &client_mode_list, &list_count);
@@ -4741,9 +5221,10 @@ SILC_SERVER_CMD_FUNC(users)
   SILC_PUT32_MSB(list_count, lc);
 
   /* Send reply */
+  idp = silc_id_payload_encode(channel->id, SILC_ID_CHANNEL);
   packet = silc_command_reply_payload_encode_va(SILC_COMMAND_USERS,
                                                SILC_STATUS_OK, ident, 4,
-                                               2, channel_id, channel_id_len,
+                                               2, idp->data, idp->len,
                                                3, lc, 4,
                                                4, client_id_list->data,
                                                client_id_list->len,
@@ -4752,6 +5233,7 @@ SILC_SERVER_CMD_FUNC(users)
   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);
@@ -4775,10 +5257,13 @@ SILC_SERVER_CMD_FUNC(getkey)
   SilcServerID *server_id = NULL;
   SilcIDPayload idp = NULL;
   uint16 ident = silc_command_get_ident(cmd->payload);
-  unsigned char *tmp;
-  uint32 tmp_len;
-  SilcBuffer pk;
+  unsigned char *tmp, *pkdata;
+  uint32 tmp_len, pklen;
+  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) {
@@ -4786,7 +5271,7 @@ SILC_SERVER_CMD_FUNC(getkey)
                                          SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
     goto out;
   }
-  idp = silc_id_payload_parse_data(tmp, tmp_len);
+  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);
@@ -4800,10 +5285,14 @@ SILC_SERVER_CMD_FUNC(getkey)
     /* If the client is not found from local list there is no chance it
        would be locally connected client so send the command further. */
     client = silc_idlist_find_client_by_id(server->local_list, 
-                                          client_id, NULL);
+                                          client_id, TRUE, NULL);
+    if (!client)
+      client = silc_idlist_find_client_by_id(server->global_list, 
+                                            client_id, TRUE, NULL);
     
     if ((!client && !cmd->pending && !server->standalone) ||
-       (client && !client->connection)) {
+       (client && !client->connection && !cmd->pending) ||
+       (client && !client->data.public_key && !cmd->pending)) {
       SilcBuffer tmpbuf;
       uint16 old_ident;
       SilcSocketConnection dest_sock;
@@ -4814,7 +5303,7 @@ SILC_SERVER_CMD_FUNC(getkey)
        goto out;
       
       old_ident = silc_command_get_ident(cmd->payload);
-      silc_command_set_ident(cmd->payload, silc_rng_get_rn16(server->rng));
+      silc_command_set_ident(cmd->payload, ++server->cmd_ident);
       tmpbuf = silc_command_payload_encode_payload(cmd->payload);
       
       silc_server_packet_send(server, dest_sock,
@@ -4824,49 +5313,62 @@ SILC_SERVER_CMD_FUNC(getkey)
       /* Reprocess this packet after received reply from router */
       silc_server_command_pending(server, SILC_COMMAND_GETKEY, 
                                  silc_command_get_ident(cmd->payload),
-                                 silc_server_command_destructor,
                                  silc_server_command_getkey,
                                  silc_server_command_dup(cmd));
       cmd->pending = TRUE;
-      
       silc_command_set_ident(cmd->payload, old_ident);
       silc_buffer_free(tmpbuf);
-      return;
+      goto out;
     }
 
-    if (!client && cmd->pending) {
+    if (!client) {
       silc_server_command_send_status_reply(cmd, SILC_COMMAND_GETKEY,
                                            SILC_STATUS_ERR_NO_SUCH_CLIENT_ID);
       goto out;
     }
 
     /* The client is locally connected, just get the public key and
-       send it back. */
-    tmp = silc_pkcs_public_key_encode(client->data.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);
-
+       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;
+    }
   } else if (id_type == SILC_ID_SERVER) {
     server_id = silc_id_payload_get_id(idp);
 
     /* If the server is not found from local list there is no chance it
        would be locally connected server so send the command further. */
     server_entry = silc_idlist_find_server_by_id(server->local_list, 
-                                                server_id, NULL);
+                                                server_id, TRUE, NULL);
+    if (!server_entry)
+      server_entry = silc_idlist_find_server_by_id(server->global_list, 
+                                                  server_id, TRUE, NULL);
     
-    if ((!server_entry && !cmd->pending && !server->standalone) ||
-       (server_entry && !server_entry->connection)) {
+    if (server_entry != server->id_entry &&
+       ((!server_entry && !cmd->pending && !server->standalone) ||
+        (server_entry && !server_entry->connection && !cmd->pending &&
+         !server->standalone) ||
+        (server_entry && !server_entry->data.public_key && !cmd->pending &&
+         !server->standalone))) {
       SilcBuffer tmpbuf;
       uint16 old_ident;
       
       old_ident = silc_command_get_ident(cmd->payload);
-      silc_command_set_ident(cmd->payload, silc_rng_get_rn16(server->rng));
+      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,
@@ -4876,46 +5378,56 @@ SILC_SERVER_CMD_FUNC(getkey)
       /* Reprocess this packet after received reply from router */
       silc_server_command_pending(server, SILC_COMMAND_GETKEY, 
                                  silc_command_get_ident(cmd->payload),
-                                 silc_server_command_destructor,
                                  silc_server_command_getkey,
                                  silc_server_command_dup(cmd));
       cmd->pending = TRUE;
-      
       silc_command_set_ident(cmd->payload, old_ident);
       silc_buffer_free(tmpbuf);
-      return;
+      goto out;
     }
 
-    if (!server_entry && cmd->pending) {
+    if (!server_entry) {
       silc_server_command_send_status_reply(cmd, SILC_COMMAND_GETKEY,
                                            SILC_STATUS_ERR_NO_SUCH_SERVER_ID);
       goto out;
     }
 
-    /* The client is locally connected, just get the public key and
-       send it back. */
-    tmp = silc_pkcs_public_key_encode(server_entry->data.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);
+    /* If they key does not exist then do not send it, send just OK reply */
+    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;
+    }
   } else {
     goto out;
   }
 
   tmp = silc_argument_get_arg_type(cmd->args, 1, &tmp_len);
   packet = silc_command_reply_payload_encode_va(SILC_COMMAND_GETKEY,
-                                               SILC_STATUS_OK, ident, 2,
+                                               SILC_STATUS_OK, ident, 
+                                               pkdata ? 2 : 1,
                                                2, tmp, tmp_len,
-                                               3, pk->data, pk->len);
+                                               3, pkdata, pklen);
   silc_server_packet_send(server, cmd->sock, SILC_PACKET_COMMAND_REPLY, 0, 
                          packet->data, packet->len, FALSE);
   silc_buffer_free(packet);
-  silc_buffer_free(pk);
+
+  if (pk)
+    silc_buffer_free(pk);
 
  out:
   if (idp)