updates.
[silc.git] / apps / silcd / command.c
index b260c141524f0dc96c8631f6bf8e71b6dc345b32..d60bf2c65553fe572b17e0007257b260b9f075b1 100644 (file)
@@ -37,6 +37,10 @@ silc_server_command_send_status_data(SilcServerCommandContext cmd,
                                     uint32 arg_type,
                                     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,7 +122,11 @@ 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,
@@ -132,12 +153,12 @@ SILC_TASK_CALLBACK(silc_server_command_process_timeout)
   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);
 
   silc_free(timeout);
 }
@@ -207,14 +228,14 @@ 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);
     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,
@@ -226,9 +247,9 @@ void silc_server_command_process(SilcServer server,
   /* 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);
 }
 
 /* Allocate Command Context */
@@ -249,7 +270,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,7 +293,8 @@ 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'. */
 
@@ -314,25 +336,32 @@ 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;
+      callbacks[i].destructor = r->destructor;
       ctx->ident = ident;
-      return TRUE;
+      i++;
     }
   }
 
-  return FALSE;
+  *callbacks_count = i;
+  return callbacks;
 }
 
 /* Destructor function for pending callbacks. This is called when using
@@ -389,6 +418,34 @@ 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) {
+    /* Send the error message */
+    silc_server_command_send_status_reply(cmd, command, status);
+    return TRUE;
+  }
+
+  return FALSE;
+}
+
 /******************************************************************************
 
                               WHOIS Functions
@@ -415,15 +472,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);
@@ -461,8 +510,6 @@ silc_server_command_whois_parse(SilcServerCommandContext cmd,
        }
       }
     }
-
-    /* Command includes ID, use that */
   }
 
   /* Get the max count of reply messages allowed */
@@ -475,53 +522,142 @@ 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;
 
   for (i = 0; i < clients_count; i++) {
     entry = clients[i];
 
-    if (!entry || entry->data.registered == FALSE)
+    if (!entry || (entry->nickname && entry->username && entry->userinfo) ||
+       !(entry->data.status & SILC_IDLIST_STATUS_REGISTERED) ||
+       !entry->router)
       continue;
 
-    if (!entry->nickname || !entry->username || !entry->userinfo) {
-      SilcBuffer tmpbuf;
-      uint16 old_ident;
-
-      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);
+    /* We need to resolve this entry since it is not complete */
 
-      /* 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),
+    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_destructor,
-                                 silc_server_command_whois, 
+                                 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_destructor,
+                               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
@@ -537,15 +673,33 @@ silc_server_command_whois_send_reply(SilcServerCommandContext cmd,
   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];
   SilcSocketConnection hsock;
 
   len = 0;
   for (i = 0; i < clients_count; i++)
-    if (clients[i]->data.registered)
+    if (clients[i]->data.status & SILC_IDLIST_STATUS_REGISTERED)
       len++;
 
+  if (len == 0 && clients_count) {
+    entry = clients[0];
+    if (entry->nickname) {
+      silc_server_command_send_status_data(cmd, SILC_COMMAND_WHOIS,
+                                          SILC_STATUS_ERR_NO_SUCH_NICK,
+                                          3, entry->nickname, 
+                                          strlen(entry->nickname));
+    } else {
+      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);
+    }
+
+    return;
+  }
+
   status = SILC_STATUS_OK;
   if (len > 1)
     status = SILC_STATUS_LIST_START;
@@ -553,13 +707,20 @@ silc_server_command_whois_send_reply(SilcServerCommandContext cmd,
   for (i = 0, k = 0; i < clients_count; i++) {
     entry = clients[i];
 
-    if (entry->data.registered == FALSE) {
+    if (!(entry->data.status & SILC_IDLIST_STATUS_REGISTERED)) {
       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->nickname) {
+         silc_server_command_send_status_data(cmd, SILC_COMMAND_WHOIS,
+                                              SILC_STATUS_ERR_NO_SUCH_NICK,
+                                              3, entry->nickname, 
+                                              strlen(entry->nickname));
+       } else {
+         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);
+       }
       }
       continue;
     }
@@ -654,7 +815,7 @@ silc_server_command_whois_send_reply(SilcServerCommandContext cmd,
 }
 
 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;
@@ -663,12 +824,14 @@ silc_server_command_whois_from_client(SilcServerCommandContext cmd)
   SilcClientID **client_id = NULL;
   uint32 client_id_count = 0, clients_count = 0;
   int i, ret = 0;
+  bool check_global = FALSE;
 
   /* 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 && 
+  if (cmd->sock->type == SILC_SOCKET_TYPE_CLIENT &&
+      server->server_type == SILC_SERVER && !cmd->pending && 
       !server->standalone) {
     SilcBuffer tmpbuf;
     uint16 old_ident;
@@ -701,110 +864,10 @@ silc_server_command_whois_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. */
 
-  /* 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;
-
-  /* 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 client(s) really does not exist in the SILC network. */
-    if (!client_id_count) {
-      silc_server_command_send_status_data(cmd, SILC_COMMAND_WHOIS,
-                                          SILC_STATUS_ERR_NO_SUCH_NICK,
-                                          3, nick, strlen(nick));
-    } else {
-      SilcBuffer idp = silc_id_payload_encode(client_id[0], SILC_ID_CLIENT);
-      silc_server_command_send_status_data(cmd, SILC_COMMAND_WHOIS,
-                                          SILC_STATUS_ERR_NO_SUCH_CLIENT_ID,
-                                          2, idp->data, idp->len);
-      silc_buffer_free(idp);
-    }
-    goto out;
-  }
-
-  /* Router always finds the client entry if it exists in the SILC network.
-     However, it might be incomplete entry and does not include all the
-     mandatory fields that WHOIS command reply requires. Check for these and
-     make query from the server who owns the client if some fields are 
-     missing. */
-  if (!silc_server_command_whois_check(cmd, clients, clients_count)) {
-    ret = -1;
-    goto out;
-  }
-
-  /* Send the command reply 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;
-}
-
-static int
-silc_server_command_whois_from_server(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;
+  if (cmd->sock->type == SILC_SOCKET_TYPE_CLIENT)
+    check_global = TRUE;
+  else if (server->server_type != SILC_SERVER)
+    check_global = TRUE;
 
   /* Parse the whois request */
   if (!silc_server_command_whois_parse(cmd, &client_id, &client_id_count, 
@@ -812,14 +875,15 @@ silc_server_command_whois_from_server(SilcServerCommandContext cmd)
                                       SILC_COMMAND_WHOIS))
     return 0;
 
-  /* Process the command request. Let's search for the requested client and
-     send reply to the requesting server. */
-
+  /* 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));
@@ -833,22 +897,7 @@ silc_server_command_whois_from_server(SilcServerCommandContext cmd)
       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 (check_global) {
       if (!silc_idlist_get_clients_by_hash(server->global_list, 
                                           nick, server->md5hash,
                                           &clients, &clients_count))
@@ -857,9 +906,9 @@ silc_server_command_whois_from_server(SilcServerCommandContext cmd)
                                            &clients, &clients_count);
     }
   }
-
+  
   if (!clients) {
-    /* Such a client really does not exist in the SILC network. */
+    /* Such client(s) really does not exist in the SILC network. */
     if (!client_id_count) {
       silc_server_command_send_status_data(cmd, SILC_COMMAND_WHOIS,
                                           SILC_STATUS_ERR_NO_SUCH_NICK,
@@ -884,7 +933,7 @@ silc_server_command_whois_from_server(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);
 
@@ -894,12 +943,9 @@ silc_server_command_whois_from_server(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;
 }
@@ -912,13 +958,9 @@ SILC_SERVER_CMD_FUNC(whois)
   SilcServerCommandContext cmd = (SilcServerCommandContext)context;
   int ret = 0;
 
-  SILC_SERVER_COMMAND_CHECK_ARGC(SILC_COMMAND_WHOIS, cmd, 1, 3328);
+  SILC_SERVER_COMMAND_CHECK(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);
+  ret = silc_server_command_whois_process(cmd);
 
   if (!ret)
     silc_server_command_free(cmd);
@@ -947,15 +989,8 @@ silc_server_command_whowas_parse(SilcServerCommandContext cmd,
   }
 
   /* 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);
-  }
+  silc_parse_userfqdn(tmp, nickname, server_name);
+
   /* Get the max count of reply messages allowed */
   tmp = silc_argument_get_arg_type(cmd->args, 2, NULL);
   if (tmp)
@@ -1037,7 +1072,7 @@ silc_server_command_whowas_send_reply(SilcServerCommandContext cmd,
     /* 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)
+    if (entry->data.status & SILC_IDLIST_STATUS_REGISTERED)
       continue;
     if (entry->id == NULL)
       continue;
@@ -1116,7 +1151,7 @@ silc_server_command_whowas_send_reply(SilcServerCommandContext cmd,
 }
 
 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;
@@ -1124,13 +1159,15 @@ 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;
 
@@ -1162,6 +1199,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;
@@ -1175,58 +1217,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))
@@ -1234,7 +1225,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,
@@ -1243,16 +1234,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;
 }
@@ -1264,13 +1257,9 @@ SILC_SERVER_CMD_FUNC(whowas)
   SilcServerCommandContext cmd = (SilcServerCommandContext)context;
   int ret = 0;
 
-  SILC_SERVER_COMMAND_CHECK_ARGC(SILC_COMMAND_WHOWAS, cmd, 1, 2);
+  SILC_SERVER_COMMAND_CHECK(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);
+  ret = silc_server_command_whowas_process(cmd);
 
   if (!ret)
     silc_server_command_free(cmd);
@@ -1282,406 +1271,646 @@ 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 char
-silc_server_command_identify_check(SilcServerCommandContext cmd,
-                                  SilcClientEntry *clients,
-                                  uint32 clients_count)
+static bool
+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. */
+
+    /* 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)) {
+       silc_server_command_send_status_data(cmd, SILC_COMMAND_IDENTIFY,
+                                            SILC_STATUS_ERR_NO_SUCH_NICK,
+                                            3, tmp, strlen(tmp));
+       return FALSE;
+      }
+    }
+
+    /* 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->local_list,
+                                               tmp, TRUE, NULL);
+      if (entry) {
+       *servers = silc_realloc(*servers, sizeof(**servers) * 
+                               (*servers_count + 1));
+       (*servers)[(*servers_count)++] = entry;
+      }
+
+      if (!(*servers)) {
+       silc_server_command_send_status_data(cmd, SILC_COMMAND_IDENTIFY,
+                                            SILC_STATUS_ERR_NO_SUCH_SERVER,
+                                            3, tmp, strlen(tmp));
+       return FALSE;
+      }
+    }
+
+    /* 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->local_list,
+                                                tmp, NULL);
+      if (entry) {
+       *channels = silc_realloc(*channels, sizeof(**channels) * 
+                                (*channels_count + 1));
+       (*channels)[(*channels_count)++] = entry;
+      }
+
+      if (!(*channels)) {
+       silc_server_command_send_status_data(cmd, SILC_COMMAND_IDENTIFY,
+                                            SILC_STATUS_ERR_NO_SUCH_CHANNEL,
+                                            3, tmp, strlen(tmp));
+       return FALSE;
+      }
+    }
+
+    if (!(*clients) && !(*servers) && !(*channels)) {
+      silc_server_command_send_status_reply(cmd, SILC_COMMAND_IDENTIFY,
+                                           SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
+      return FALSE;
+    }
+  } else {
+    /* Command includes ID, we must use that.  Also check whether the command
+       has more than one ID set - take them all. */
+
+    /* 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_data(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 FALSE;
+      }
+
+      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 {
+         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 {
+         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 {
+         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 TRUE;
+}
+
+/* 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 || entry->nickname || 
+       !(entry->data.status & SILC_IDLIST_STATUS_REGISTERED) ||
+       !entry->router)
       continue;
 
-    if (!entry->nickname) {
-      SilcBuffer tmpbuf;
-      uint16 old_ident;
-      
-      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),
+    /* 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_destructor,
                                  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_destructor,
+                               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;
   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) {
+    len = 0;
+    for (i = 0; i < clients_count; i++)
+      if (clients[i]->data.status & SILC_IDLIST_STATUS_REGISTERED)
+       len++;
+
+    if (len == 0 && clients_count) {
+      entry = clients[0];
+      if (entry->nickname) {
+       silc_server_command_send_status_data(cmd, SILC_COMMAND_IDENTIFY,
+                                            SILC_STATUS_ERR_NO_SUCH_NICK,
+                                            3, entry->nickname, 
+                                            strlen(entry->nickname));
+      } else {
        SilcBuffer idp = silc_id_payload_encode(entry->id, 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);
       }
-      continue;
+      
+      return;
     }
 
-    if (k >= 1)
-      status = SILC_STATUS_LIST_ITEM;
-
-    if (clients_count > 1 && k == clients_count - 1)
-      status = SILC_STATUS_LIST_END;
-
-    if (count && k - 1 == count)
-      status = SILC_STATUS_LIST_END;
-
-    if (count && k - 1 > count)
-      break;
+    if (len > 1)
+      status = SILC_STATUS_LIST_START;
 
-    /* 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));
+    for (i = 0, k = 0; i < clients_count; i++) {
+      entry = clients[i];
       
-    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->data.status & SILC_IDLIST_STATUS_REGISTERED)) {
+       if (clients_count == 1) {
+         SilcBuffer idp = silc_id_payload_encode(entry->id, 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);
+       }
+       continue;
       }
-    }
       
-    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);
+      if (k >= 1)
+       status = SILC_STATUS_LIST_ITEM;
+      if (clients_count > 1 && k == clients_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);
+      
+      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);
+       }
       }
       
-      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));
-    }
+      if (!entry->username) {
+       packet = silc_command_reply_payload_encode_va(SILC_COMMAND_IDENTIFY,
+                                                     status, ident, 2,
+                                                     2, idp->data, idp->len, 
+                                                     3, nh, strlen(nh));
+      } else {
+       strncat(uh, entry->username, strlen(entry->username));
+       if (!strchr(entry->username, '@')) {
+         strncat(uh, "@", 1);
+         hsock = (SilcSocketConnection)entry->connection;
+         len = strlen(hsock->hostname);
+         strncat(uh, hsock->hostname, len);
+       }
+       
+       packet = silc_command_reply_payload_encode_va(SILC_COMMAND_IDENTIFY,
+                                                     status, ident, 3,
+                                                     2, idp->data, idp->len, 
+                                                     3, nh, strlen(nh),
+                                                     4, uh, strlen(uh));
+      }
       
-    silc_server_packet_send(server, cmd->sock, SILC_PACKET_COMMAND_REPLY,
-                           0, packet->data, packet->len, FALSE);
-    
-    silc_buffer_free(packet);
-    silc_buffer_free(idp);
-
-    k++;
+      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);
+  status = (status == SILC_STATUS_LIST_ITEM ? 
+           SILC_STATUS_LIST_ITEM : SILC_STATUS_OK);
 
-    /* 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;
+    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);
+      if (entry->server_name) {
+       packet = 
+         silc_command_reply_payload_encode_va(SILC_COMMAND_IDENTIFY,
+                                              status, ident, 2,
+                                              2, idp->data, idp->len, 
+                                              3, entry->server_name, 
+                                              strlen(entry->server_name));
+      } else {
+       packet = silc_command_reply_payload_encode_va(SILC_COMMAND_IDENTIFY,
+                                                     status, ident, 1,
+                                                     2, idp->data, idp->len);
       }
-    }
-  } 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_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;
-  }
+  status = (status == SILC_STATUS_LIST_ITEM ? 
+           SILC_STATUS_LIST_ITEM : SILC_STATUS_OK);
 
-  /* Send the command reply to the client */
-  silc_server_command_identify_send_reply(cmd, clients, clients_count,
-                                         count);
+  if (channels) {
+    SilcChannelEntry entry;
 
- 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);
+    if (status == SILC_STATUS_OK && channels_count > 1)
+      status = SILC_STATUS_LIST_START;
 
-  return ret;
+    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);
+      if (entry->channel_name) {
+       packet = 
+         silc_command_reply_payload_encode_va(SILC_COMMAND_IDENTIFY,
+                                              status, ident, 2,
+                                              2, idp->data, idp->len, 
+                                              3, entry->channel_name, 
+                                              strlen(entry->channel_name));
+      } else {
+       packet = silc_command_reply_payload_encode_va(SILC_COMMAND_IDENTIFY,
+                                                     status, ident, 1,
+                                                     2, idp->data, idp->len);
+      }
+      
+      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_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;
-
-  /* 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. */
+  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;
 
-  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);
-    }
-  }
+  /* 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 (cmd->sock->type == SILC_SOCKET_TYPE_CLIENT && 
+      server->server_type == SILC_SERVER && !cmd->pending && 
+      !server->standalone) {
+    SilcBuffer tmpbuf;
+    uint16 old_ident;
 
-  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);
-    }
+    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. */
+
+  /* Parse the IDENTIFY request */
+  if (!silc_server_command_identify_parse(cmd,
+                                         &clients, &clients_count,
+                                         &servers, &servers_count,
+                                         &channels, &channels_count,
+                                         &count))
+    return 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;
 }
@@ -1691,13 +1920,9 @@ SILC_SERVER_CMD_FUNC(identify)
   SilcServerCommandContext cmd = (SilcServerCommandContext)context;
   int ret = 0;
 
-  SILC_SERVER_COMMAND_CHECK_ARGC(SILC_COMMAND_IDENTIFY, cmd, 1, 3328);
+  SILC_SERVER_COMMAND_CHECK(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);
+  ret = silc_server_command_identify_process(cmd);
 
   if (!ret)
     silc_server_command_free(cmd);
@@ -1707,14 +1932,19 @@ SILC_SERVER_CMD_FUNC(identify)
 
 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;
+  int i;
+
+  for (i = 0; i < strlen(nick); i++) {
+    if (!isascii(nick[i]))
+      return TRUE;
+    if (nick[i] == ' ') return TRUE;
+    if (nick[i] == '\\') return TRUE;
+    if (nick[i] == '\"') return TRUE;
+    if (nick[i] == '*') return TRUE;
+    if (nick[i] == '?') return TRUE;
+    if (nick[i] == ',') return TRUE;
+    if (nick[i] == '@') return TRUE;
+  }
 
   return FALSE;
 }
@@ -1732,11 +1962,12 @@ SILC_SERVER_CMD_FUNC(nick)
   SilcClientID *new_id;
   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);
@@ -1747,12 +1978,16 @@ SILC_SERVER_CMD_FUNC(nick)
   }
 
   if (strlen(nick) > 128)
-    nick[127] = '\0';
+    nick[128] = '\0';
 
   /* 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
@@ -1763,26 +1998,23 @@ SILC_SERVER_CMD_FUNC(nick)
                                        FALSE : TRUE, client->id,
                                        new_id);
 
-  /* Remove old cache entry */
-  silc_idcache_del_by_id(server->local_list->clients, client->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)
-    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), client->id, 
-                  (void *)client, FALSE);
+                  client->id, (void *)client, FALSE);
 
   nidp = silc_id_payload_encode(client->id, SILC_ID_CLIENT);
 
@@ -1858,7 +2090,7 @@ 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);
     }
 
@@ -1909,7 +2141,7 @@ 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);
     }
 
@@ -1952,7 +2184,7 @@ 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, 2);
 
   /* Get Channel ID */
   tmp = silc_argument_get_arg_type(cmd->args, 1, &tmp_len);
@@ -1970,7 +2202,7 @@ SILC_SERVER_CMD_FUNC(list)
                                       &lch_count);
   
   /* Get the channels from global list if we are router */
-  if (server->server_type == SILC_ROUTER) 
+  if (server->server_type != SILC_SERVER) 
     gchannels = silc_idlist_get_channels(server->global_list, channel_id,
                                         &gch_count);
 
@@ -1998,7 +2230,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);
 
@@ -2044,11 +2276,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) {
@@ -2059,8 +2293,7 @@ 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 */
@@ -2122,7 +2355,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);
@@ -2162,16 +2395,12 @@ 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 */
@@ -2189,7 +2418,7 @@ SILC_SERVER_CMD_FUNC(invite)
     /* Get the client entry */
     dest = silc_server_get_client_resolve(server, dest_id);
     if (!dest) {
-      if (server->server_type == SILC_ROUTER) {
+      if (server->server_type != SILC_SERVER) {
        silc_server_command_send_status_reply(cmd, SILC_COMMAND_INVITE,
                                     SILC_STATUS_ERR_NO_SUCH_CLIENT_ID);
        goto out;
@@ -2217,6 +2446,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));
@@ -2303,22 +2537,27 @@ SILC_SERVER_CMD_FUNC(invite)
 
   /* 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);
 }
 
@@ -2359,7 +2598,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;
@@ -2375,7 +2614,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);
 
@@ -2396,7 +2635,7 @@ SILC_SERVER_CMD_FUNC(kill)
   unsigned char *tmp, *comment;
   uint32 tmp_len, tmp_len2;
 
-  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;
@@ -2431,10 +2670,10 @@ 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);
   if (!remote_client) {
     remote_client = silc_idlist_find_client_by_id(server->global_list, 
-                                                 client_id, NULL);
+                                                 client_id, TRUE, NULL);
     if (!remote_client) {
       silc_server_command_send_status_reply(cmd, SILC_COMMAND_KILL,
                                            SILC_STATUS_ERR_NO_SUCH_CLIENT_ID);
@@ -2482,7 +2721,7 @@ 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);
@@ -2513,7 +2752,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);
@@ -2532,11 +2771,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;
@@ -2544,7 +2783,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 */
@@ -2564,15 +2808,15 @@ 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;
@@ -2623,8 +2867,7 @@ SILC_SERVER_CMD_FUNC(info)
     }
   }
 
-  if (server_id)
-    silc_free(server_id);
+  silc_free(server_id);
 
   if (!entry) {
     silc_server_command_send_status_reply(cmd, SILC_COMMAND_INFO,
@@ -2638,13 +2881,20 @@ SILC_SERVER_CMD_FUNC(info)
   server_name = entry->server_name;
 
   /* Send the reply */
-  packet = silc_command_reply_payload_encode_va(SILC_COMMAND_INFO,
-                                               SILC_STATUS_OK, ident, 3,
-                                               2, idp->data, idp->len,
-                                               3, server_name, 
-                                               strlen(server_name),
-                                               4, server_info, 
-                                               strlen(server_info));
+  if (server_info)
+    packet = silc_command_reply_payload_encode_va(SILC_COMMAND_INFO,
+                                                 SILC_STATUS_OK, ident, 3,
+                                                 2, idp->data, idp->len,
+                                                 3, server_name, 
+                                                 strlen(server_name),
+                                                 4, server_info, 
+                                                 strlen(server_info));
+  else
+    packet = silc_command_reply_payload_encode_va(SILC_COMMAND_INFO,
+                                                 SILC_STATUS_OK, ident, 2,
+                                                 2, idp->data, idp->len,
+                                                 3, server_name, 
+                                                 strlen(server_name));
   silc_server_packet_send(server, cmd->sock, SILC_PACKET_COMMAND_REPLY, 0, 
                          packet->data, packet->len, FALSE);
     
@@ -2665,7 +2915,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);
@@ -2713,7 +2963,7 @@ 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];
 
   SILC_LOG_DEBUG(("Start"));
 
@@ -2724,40 +2974,55 @@ 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)
-      goto out;
+    client = silc_server_get_client_resolve(server, client_id);
+    if (!client) {
+      if (cmd->pending)
+       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, NULL,
+                                 silc_server_command_join, 
+                                 silc_server_command_dup(cmd));
+      cmd->pending = TRUE;
+      return;
+    }
+
+    cmd->pending = FALSE;
   }
 
   /*
    * Check channel modes
    */
 
-  if (cmd->sock->type == SILC_SOCKET_TYPE_CLIENT) {
-    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));
-    }
+  memset(check, 0, sizeof(check));
+  memset(check2, 0, sizeof(check2));
+  strncat(check, client->nickname, strlen(client->nickname));
+  strncat(check, "!", 1);
+  strncat(check, client->username, strlen(client->username));
+  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));
+  }
+  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));
+  }
 
-    if (!silc_string_match(channel->invite_list, check)) {
+  /* 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;
@@ -2767,8 +3032,9 @@ static void silc_server_command_join_channel(SilcServer server,
   /* Check ban list if it exists. If the client's nickname, server,
      username and/or hostname is in the ban list the access to the
      channel is denied. */
-  if (cmd->sock->type == SILC_SOCKET_TYPE_CLIENT && channel->ban_list) {
-    if (silc_string_match(channel->ban_list, check)) {
+  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;
@@ -2794,7 +3060,7 @@ static void silc_server_command_join_channel(SilcServer server,
 
   /* Check user count limit if set. */
   if (channel->mode & SILC_CHANNEL_MODE_ULIMIT) {
-    if (silc_list_count(channel->user_list) + 1 > 
+    if (silc_hash_table_count(channel->user_list) + 1 > 
        channel->user_limit) {
       silc_server_command_send_status_reply(cmd, SILC_COMMAND_JOIN,
                                            SILC_STATUS_ERR_CHANNEL_IS_FULL);
@@ -2814,9 +3080,10 @@ 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) || 
+  if ((!created && silc_hash_table_count(channel->user_list) > 0) || 
       !channel->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. This is broadcasted to the channel but is not
      sent to the client who is joining to the channel. */
@@ -2832,8 +3099,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,
@@ -2889,18 +3156,28 @@ 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);
+
+    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);
   }
 
   silc_buffer_free(reply);
@@ -2911,8 +3188,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 
@@ -2929,7 +3205,7 @@ SILC_SERVER_CMD_FUNC(join)
   int created = FALSE;
   SilcClientID *client_id;
 
-  SILC_SERVER_COMMAND_CHECK_ARGC(SILC_COMMAND_JOIN, cmd, 1, 4);
+  SILC_SERVER_COMMAND_CHECK(SILC_COMMAND_JOIN, cmd, 1, 4);
 
   /* Get channel name */
   tmp = silc_argument_get_arg_type(cmd->args, 1, &tmp_len);
@@ -2946,7 +3222,6 @@ SILC_SERVER_CMD_FUNC(join)
   if (silc_server_command_bad_chars(channel_name) == TRUE) {
     silc_server_command_send_status_reply(cmd, SILC_COMMAND_JOIN,
                                          SILC_STATUS_ERR_BAD_CHANNEL);
-    silc_free(channel_name);
     goto out;
   }
 
@@ -3007,9 +3282,15 @@ SILC_SERVER_CMD_FUNC(join)
           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));
@@ -3054,11 +3335,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
@@ -3084,7 +3365,7 @@ SILC_SERVER_CMD_FUNC(join)
   /* 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) {
+  if (!channel->global_users && !silc_hash_table_count(channel->user_list)) {
     umode = (SILC_CHANNEL_UMODE_CHANOP | SILC_CHANNEL_UMODE_CHANFO);
     created = TRUE;            /* Created globally by our router */
   }
@@ -3111,7 +3392,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);
@@ -3155,13 +3436,13 @@ 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;
@@ -3258,7 +3539,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);
@@ -3406,7 +3687,7 @@ SILC_SERVER_CMD_FUNC(cmode)
   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);
@@ -3452,10 +3733,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)) {
@@ -3480,7 +3758,8 @@ SILC_SERVER_CMD_FUNC(cmode)
         anymore after this. */
 
       /* 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. This sends it to our local clients and if
         we are normal server to our router as well. */
@@ -3561,7 +3840,8 @@ SILC_SERVER_CMD_FUNC(cmode)
       }
 
       /* 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. This sends it to our local clients and if
         we are normal server to our router as well. */
@@ -3577,7 +3857,7 @@ SILC_SERVER_CMD_FUNC(cmode)
 
       /* Delete old cipher and allocate default one */
       silc_cipher_free(channel->channel_key);
-      if (!silc_cipher_alloc(cipher ? cipher : "aes-256-cbc"
+      if (!silc_cipher_alloc(cipher ? cipher : SILC_DEFAULT_CIPHER
                             &channel->channel_key)) {
        silc_server_command_send_status_reply(cmd, SILC_COMMAND_CMODE,
                                   SILC_STATUS_ERR_UNKNOWN_ALGORITHM);
@@ -3585,7 +3865,8 @@ SILC_SERVER_CMD_FUNC(cmode)
       }
 
       /* 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. This sends it to our local clients and if
         we are normal server to our router as well. */
@@ -3633,7 +3914,7 @@ SILC_SERVER_CMD_FUNC(cmode)
 
       /* Delete old hmac and allocate default one */
       silc_hmac_free(channel->hmac);
-      if (!silc_hmac_alloc(hmac ? hmac : "hmac-sha1-96", NULL, 
+      if (!silc_hmac_alloc(hmac ? hmac : SILC_DEFAULT_HMAC, NULL, 
                           &channel->hmac)) {
        silc_server_command_send_status_reply(cmd, SILC_COMMAND_CMODE,
                                       SILC_STATUS_ERR_UNKNOWN_ALGORITHM);
@@ -3683,6 +3964,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);
@@ -3723,8 +4013,9 @@ SILC_SERVER_CMD_FUNC(cmode)
 
   /* 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);
     
@@ -3755,7 +4046,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);
@@ -3792,13 +4083,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);
@@ -3825,10 +4111,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 &&
@@ -3848,10 +4134,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);
   }
 
   /* 
@@ -3867,46 +4151,48 @@ SILC_SERVER_CMD_FUNC(cumode)
   }
 
   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 (!(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 (!(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;
+      }
 
-    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;
-    }
+      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;
+      }
 
-    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;
+      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;
+      }
+      
+      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) {
@@ -3972,9 +4258,10 @@ SILC_SERVER_CMD_FUNC(cumode)
 
   /* 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);
     
@@ -4003,7 +4290,7 @@ SILC_SERVER_CMD_FUNC(kick)
   uint32 tmp_len;
   unsigned char *tmp, *comment;
 
-  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);
@@ -4040,16 +4327,11 @@ 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 */
@@ -4068,24 +4350,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 */
@@ -4130,7 +4407,8 @@ SILC_SERVER_CMD_FUNC(kick)
 
   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. */
@@ -4156,7 +4434,7 @@ SILC_SERVER_CMD_FUNC(oper)
   SilcServerConfigSectionAdminConnection *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;
@@ -4228,13 +4506,16 @@ SILC_SERVER_CMD_FUNC(silcoper)
   SilcServerConfigSectionAdminConnection *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);
@@ -4302,7 +4583,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;
@@ -4364,7 +4645,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);
@@ -4399,10 +4680,7 @@ 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;
+  silc_hash_table_find(channel->user_list, client, NULL, (void *)&chl);
 
   /* The client must be at least channel operator. */
   if (!(chl->mode & SILC_CHANNEL_UMODE_CHANOP)) {
@@ -4476,8 +4754,7 @@ SILC_SERVER_CMD_FUNC(ban)
   silc_buffer_free(packet);
 
  out:
-  if (channel_id)
-    silc_free(channel_id);
+  silc_free(channel_id);
   silc_server_command_free(cmd);
 }
 
@@ -4495,7 +4772,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;
@@ -4521,7 +4798,7 @@ 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) {
     silc_server_command_send_status_reply(cmd, SILC_COMMAND_CLOSE,
                                          SILC_STATUS_ERR_NO_SERVER_ID);
@@ -4534,6 +4811,15 @@ SILC_SERVER_CMD_FUNC(close)
 
   /* Close the connection to the server */
   sock = (SilcSocketConnection)server_entry->connection;
+
+  /* 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);
   silc_server_close_connection(server, sock);
   
@@ -4550,7 +4836,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;
@@ -4587,7 +4873,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);
@@ -4639,7 +4925,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, 
@@ -4648,8 +4935,7 @@ SILC_SERVER_CMD_FUNC(leave)
   }
 
  out:
-  if (id)
-    silc_free(id);
+  silc_free(id);
   silc_server_command_free(cmd);
 }
 
@@ -4662,8 +4948,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;
@@ -4671,29 +4957,42 @@ 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 (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) {
-    if (server->server_type == SILC_SERVER && !server->standalone &&
+    if (server->server_type != SILC_ROUTER && !server->standalone &&
        !cmd->pending) {
       SilcBuffer tmpbuf;
       
@@ -4719,8 +5018,12 @@ SILC_SERVER_CMD_FUNC(users)
       return;
     }
 
-    /* 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,
@@ -4729,6 +5032,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);
@@ -4737,9 +5058,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,
@@ -4748,6 +5070,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);
@@ -4771,11 +5094,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;
 
+  SILC_LOG_DEBUG(("Start"));
+
   tmp = silc_argument_get_arg_type(cmd->args, 1, &tmp_len);
   if (!tmp) {
     silc_server_command_send_status_reply(cmd, SILC_COMMAND_GETKEY,
@@ -4796,10 +5121,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;
@@ -4830,34 +5159,48 @@ SILC_SERVER_CMD_FUNC(getkey)
       return;
     }
 
-    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 */
+    if (!client->data.public_key) {
+      pkdata = NULL;
+      pklen = 0;
+    } else {
+      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);
+      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;
       
@@ -4882,36 +5225,46 @@ SILC_SERVER_CMD_FUNC(getkey)
       return;
     }
 
-    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 */
+    if (!server_entry->data.public_key) {
+      pkdata = NULL;
+      pklen = 0;
+    } else {
+      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);
+      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)