Started implementing protocol version 1.1 and narrowing down
[silc.git] / apps / silcd / command.c
index d523172c351657c9b1a68729f589a4197e0fe98d..ba345af6c5490c57d617570b90149cd90c2a78b2 100644 (file)
@@ -2,9 +2,9 @@
 
   command.c
 
-  Author: Pekka Riikonen <priikone@poseidon.pspt.fi>
+  Author: Pekka Riikonen <priikone@silcnet.org>
 
-  Copyright (C) 1997 - 2001 Pekka Riikonen
+  Copyright (C) 1997 - 2002 Pekka Riikonen
 
   This program is free software; you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published by
@@ -34,9 +34,9 @@ static void
 silc_server_command_send_status_data(SilcServerCommandContext cmd,
                                     SilcCommand command,
                                     SilcCommandStatus status,
-                                    uint32 arg_type,
-                                    unsigned char *arg,
-                                    uint32 arg_len);
+                                    SilcUInt32 arg_type,
+                                    const unsigned char *arg,
+                                    SilcUInt32 arg_len);
 static bool
 silc_server_command_pending_error_check(SilcServerCommandContext cmd,
                                        SilcServerCommandReplyContext cmdr,
@@ -56,8 +56,6 @@ SilcServerCommand silc_command_list[] =
   SILC_SERVER_CMD(quit, QUIT, SILC_CF_LAG | SILC_CF_REG),
   SILC_SERVER_CMD(kill, KILL, SILC_CF_LAG_STRICT | SILC_CF_REG | SILC_CF_OPER),
   SILC_SERVER_CMD(info, INFO, SILC_CF_LAG | SILC_CF_REG),
-  SILC_SERVER_CMD(connect, CONNECT, 
-                 SILC_CF_LAG | SILC_CF_REG | SILC_CF_OPER),
   SILC_SERVER_CMD(ping, PING, SILC_CF_LAG | SILC_CF_REG),
   SILC_SERVER_CMD(oper, OPER, SILC_CF_LAG | SILC_CF_REG | SILC_CF_OPER),
   SILC_SERVER_CMD(join, JOIN, SILC_CF_LAG_STRICT | SILC_CF_REG),
@@ -67,16 +65,19 @@ SilcServerCommand silc_command_list[] =
   SILC_SERVER_CMD(cumode, CUMODE, SILC_CF_LAG | SILC_CF_REG),
   SILC_SERVER_CMD(kick, KICK, SILC_CF_LAG_STRICT | SILC_CF_REG),
   SILC_SERVER_CMD(ban, BAN, SILC_CF_LAG_STRICT | SILC_CF_REG),
-  SILC_SERVER_CMD(close, CLOSE,
-                 SILC_CF_LAG | SILC_CF_REG | SILC_CF_OPER),
-  SILC_SERVER_CMD(shutdown, SHUTDOWN, SILC_CF_LAG | SILC_CF_REG | 
-                 SILC_CF_OPER),
   SILC_SERVER_CMD(silcoper, SILCOPER,
                  SILC_CF_LAG | SILC_CF_REG | SILC_CF_SILC_OPER),
   SILC_SERVER_CMD(leave, LEAVE, SILC_CF_LAG_STRICT | SILC_CF_REG),
   SILC_SERVER_CMD(users, USERS, SILC_CF_LAG | SILC_CF_REG),
   SILC_SERVER_CMD(getkey, GETKEY, SILC_CF_LAG | SILC_CF_REG),
 
+  SILC_SERVER_CMD(connect, PRIV_CONNECT, 
+                 SILC_CF_LAG | SILC_CF_REG | SILC_CF_OPER),
+  SILC_SERVER_CMD(close, PRIV_CLOSE,
+                 SILC_CF_LAG | SILC_CF_REG | SILC_CF_OPER),
+  SILC_SERVER_CMD(shutdown, PRIV_SHUTDOWN, SILC_CF_LAG | SILC_CF_REG | 
+                 SILC_CF_OPER),
+
   { NULL, 0 },
 };
 
@@ -89,7 +90,7 @@ SilcServerCommand silc_command_list[] =
    of arguments. */
 #define SILC_SERVER_COMMAND_CHECK(command, context, min, max)                \
 do {                                                                         \
-  uint32 _argc;                                                                      \
+  SilcUInt32 _argc;                                                                  \
                                                                              \
   SILC_LOG_DEBUG(("Start"));                                                 \
                                                                              \
@@ -131,7 +132,6 @@ static int silc_server_is_registered(SilcServer server,
 
   silc_server_command_send_status_reply(cmd, command,
                                        SILC_STATUS_ERR_NOT_REGISTERED);
-  silc_server_command_free(cmd);
   return FALSE;
 }
 
@@ -149,6 +149,11 @@ SILC_TASK_CALLBACK(silc_server_command_process_timeout)
   SilcServerCommandTimeout timeout = (SilcServerCommandTimeout)context;
   SilcClientEntry client = (SilcClientEntry)timeout->ctx->sock->user_data;
 
+  if (!client) {
+    silc_server_command_free(timeout->ctx);
+    silc_free(timeout);
+  }
+
   /* Update access time */
   client->last_command = time(NULL);
 
@@ -159,6 +164,8 @@ SILC_TASK_CALLBACK(silc_server_command_process_timeout)
                                     timeout->ctx, 
                                     timeout->cmd->cmd))
     timeout->cmd->cb(timeout->ctx, NULL);
+  else
+    silc_server_command_free(timeout->ctx);
 
   silc_free(timeout);
 }
@@ -199,7 +206,7 @@ void silc_server_command_process(SilcServer server,
     if (cmd->cmd == command)
       break;
 
-  if (cmd == NULL) {
+  if (!cmd || !cmd->cb) {
     silc_server_command_send_status_reply(ctx, command,
                                          SILC_STATUS_ERR_UNKNOWN_COMMAND);
     silc_server_command_free(ctx);
@@ -233,15 +240,12 @@ void silc_server_command_process(SilcServer server,
                         silc_server_command_process_timeout,
                         (void *)timeout, 
                         2 - (time(NULL) - client->last_command), 0,
-                        SILC_TASK_TIMEOUT,
-                        SILC_TASK_PRI_NORMAL);
+                        SILC_TASK_TIMEOUT, SILC_TASK_PRI_NORMAL);
     else
       silc_schedule_task_add(server->schedule, sock->sock, 
                         silc_server_command_process_timeout,
-                        (void *)timeout, 
-                        0, 1,
-                        SILC_TASK_TIMEOUT,
-                        SILC_TASK_PRI_NORMAL);
+                        (void *)timeout, 0, 1,
+                        SILC_TASK_TIMEOUT, SILC_TASK_PRI_NORMAL);
     return;
   }
 
@@ -251,6 +255,8 @@ void silc_server_command_process(SilcServer server,
     cmd->cb(ctx, NULL);
   else if (silc_server_is_registered(server, sock, ctx, cmd->cmd))
     cmd->cb(ctx, NULL);
+  else
+    silc_server_command_free(ctx);
 }
 
 /* Allocate Command Context */
@@ -297,31 +303,43 @@ silc_server_command_dup(SilcServerCommandContext ctx)
    with `context' when reply has been received.  It can be SILC_COMMAND_NONE
    to match any command with the `ident'.  If `ident' is non-zero
    the `callback' will be executed when received reply with command
-   identifier `ident'. */
+   identifier `ident'. If there already exists pending command for the
+   specified command, ident, callback and context this function has no
+   effect. */
 
-void silc_server_command_pending(SilcServer server,
+bool silc_server_command_pending(SilcServer server,
                                 SilcCommand reply_cmd,
-                                uint16 ident,
-                                SilcServerPendingDestructor destructor,
+                                SilcUInt16 ident,
                                 SilcCommandCb callback,
                                 void *context)
 {
   SilcServerCommandPending *reply;
 
+  /* Check whether identical pending already exists for same command,
+     ident, callback and callback context. If it does then it would be
+     error to register it again. */
+  silc_dlist_start(server->pending_commands);
+  while ((reply = silc_dlist_get(server->pending_commands)) != SILC_LIST_END) {
+    if (reply->reply_cmd == reply_cmd && reply->ident == ident &&
+       reply->callback == callback && reply->context == context)
+      return FALSE;
+  }
+
   reply = silc_calloc(1, sizeof(*reply));
   reply->reply_cmd = reply_cmd;
   reply->ident = ident;
   reply->context = context;
   reply->callback = callback;
-  reply->destructor = destructor;
   silc_dlist_add(server->pending_commands, reply);
+
+  return TRUE;
 }
 
 /* Deletes pending command by reply command type. */
 
 void silc_server_command_pending_del(SilcServer server,
                                     SilcCommand reply_cmd,
-                                    uint16 ident)
+                                    SilcUInt16 ident)
 {
   SilcServerCommandPending *r;
 
@@ -341,8 +359,8 @@ SilcServerCommandPendingCallbacks
 silc_server_command_pending_check(SilcServer server,
                                  SilcServerCommandReplyContext ctx,
                                  SilcCommand command, 
-                                 uint16 ident,
-                                 uint32 *callbacks_count)
+                                 SilcUInt16 ident,
+                                 SilcUInt32 *callbacks_count)
 {
   SilcServerCommandPending *r;
   SilcServerCommandPendingCallbacks callbacks = NULL;
@@ -355,7 +373,6 @@ silc_server_command_pending_check(SilcServer server,
       callbacks = silc_realloc(callbacks, sizeof(*callbacks) * (i + 1));
       callbacks[i].context = r->context;
       callbacks[i].callback = r->callback;
-      callbacks[i].destructor = r->destructor;
       ctx->ident = ident;
       i++;
     }
@@ -365,14 +382,6 @@ silc_server_command_pending_check(SilcServer server,
   return callbacks;
 }
 
-/* Destructor function for pending callbacks. This is called when using
-   pending commands to free the context given for the pending command. */
-
-static void silc_server_command_destructor(void *context)
-{
-  silc_server_command_free((SilcServerCommandContext)context);
-}
-
 /* Sends simple status message as command reply packet */
 
 static void 
@@ -401,9 +410,9 @@ static void
 silc_server_command_send_status_data(SilcServerCommandContext cmd,
                                     SilcCommand command,
                                     SilcCommandStatus status,
-                                    uint32 arg_type,
-                                    unsigned char *arg,
-                                    uint32 arg_len)
+                                    SilcUInt32 arg_type,
+                                    const unsigned char *arg,
+                                    SilcUInt32 arg_len)
 {
   SilcBuffer buffer;
 
@@ -439,8 +448,16 @@ silc_server_command_pending_error_check(SilcServerCommandContext cmd,
       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);
+    SilcBuffer buffer;
+
+    /* Send the same command reply payload */
+    silc_command_set_ident(cmdr->payload, 
+                          silc_command_get_ident(cmd->payload));
+    buffer = silc_command_payload_encode_payload(cmdr->payload);
+    silc_server_packet_send(cmd->server, cmd->sock,
+                           SILC_PACKET_COMMAND_REPLY, 0, 
+                           buffer->data, buffer->len, FALSE);
+    silc_buffer_free(buffer);
     return TRUE;
   }
 
@@ -456,15 +473,15 @@ silc_server_command_pending_error_check(SilcServerCommandContext cmd,
 static int
 silc_server_command_whois_parse(SilcServerCommandContext cmd,
                                SilcClientID ***client_id,
-                               uint32 *client_id_count,
+                               SilcUInt32 *client_id_count,
                                char **nickname,
                                char **server_name,
                                int *count,
                                SilcCommand command)
 {
   unsigned char *tmp;
-  uint32 len;
-  uint32 argc = silc_argument_get_arg_num(cmd->args);
+  SilcUInt32 len;
+  SilcUInt32 argc = silc_argument_get_arg_num(cmd->args);
   int i, k;
 
   /* If client ID is in the command it must be used instead of nickname */
@@ -484,9 +501,11 @@ silc_server_command_whois_parse(SilcServerCommandContext cmd,
        has more than one ID set - take them all. */
 
     *client_id = silc_calloc(1, sizeof(**client_id));
-    (*client_id)[0] = silc_id_payload_parse_id(tmp, len);
+    (*client_id)[0] = silc_id_payload_parse_id(tmp, len, NULL);
     if ((*client_id)[0] == NULL) {
       silc_free(*client_id);
+      silc_server_command_send_status_reply(cmd, command,
+                                           SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
       return FALSE;
     }
     *client_id_count = 1;
@@ -498,12 +517,15 @@ silc_server_command_whois_parse(SilcServerCommandContext cmd,
        if (tmp) {
          *client_id = silc_realloc(*client_id, sizeof(**client_id) *
                                    (*client_id_count + 1));
-         (*client_id)[k] = silc_id_payload_parse_id(tmp, len);
+         (*client_id)[k] = silc_id_payload_parse_id(tmp, len, NULL);
          if ((*client_id)[k] == NULL) {
            /* Cleanup all and fail */
            for (i = 0; i < *client_id_count; i++)
              silc_free((*client_id)[i]);
            silc_free(*client_id);
+           silc_server_command_send_status_reply(
+                                        cmd, command,
+                                        SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
            return FALSE;
          }
          (*client_id_count)++;
@@ -526,33 +548,46 @@ silc_server_command_whois_parse(SilcServerCommandContext cmd,
 /* Resolve context used by both WHOIS and IDENTIFY commands */
 typedef struct {
   SilcServerEntry router;
-  uint16 ident;
+  SilcUInt16 ident;
   unsigned char **res_argv;
-  uint32 *res_argv_lens;
-  uint32 *res_argv_types;
-  uint32 res_argc;
+  SilcUInt32 *res_argv_lens;
+  SilcUInt32 *res_argv_types;
+  SilcUInt32 res_argc;
 } *SilcServerResolveContext;
 
 static bool
 silc_server_command_whois_check(SilcServerCommandContext cmd,
                                SilcClientEntry *clients,
-                               uint32 clients_count)
+                               SilcUInt32 clients_count)
 {
   SilcServer server = cmd->server;
   SilcClientEntry entry;
   SilcServerResolveContext resolve = NULL, r = NULL;
-  uint32 resolve_count = 0;
+  SilcUInt32 resolve_count = 0;
   int i, k;
   bool no_res = TRUE;
 
+  SILC_LOG_DEBUG(("Start"));
+
   for (i = 0; i < clients_count; i++) {
     entry = clients[i];
-
-    if (!entry || (entry->nickname && entry->username && entry->userinfo) ||
-       !(entry->data.status & SILC_IDLIST_STATUS_REGISTERED) ||
-       !entry->router)
+    if (!entry)
       continue;
 
+    if ((entry->nickname && entry->username && entry->userinfo) ||
+       !(entry->data.status & SILC_IDLIST_STATUS_REGISTERED)) {
+      if (!entry->router)
+       continue;
+
+      /* If we are normal server, and we've not resolved this client from
+        router and it is global client, we'll check whether it is on some
+        channel.  If not then we cannot be sure about its validity, and
+        we'll resolve it from router. */
+      if (cmd->server->server_type != SILC_SERVER || cmd->pending ||
+         entry->connection || silc_hash_table_count(entry->channels))
+       continue;
+    }
+
     /* We need to resolve this entry since it is not complete */
 
     if (!cmd->pending && entry->data.status & SILC_IDLIST_STATUS_RESOLVING) {
@@ -560,7 +595,6 @@ silc_server_command_whois_check(SilcServerCommandContext cmd,
         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_dup(cmd));
       no_res = FALSE;
@@ -643,7 +677,6 @@ silc_server_command_whois_check(SilcServerCommandContext cmd,
     /* 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;
@@ -664,86 +697,66 @@ silc_server_command_whois_check(SilcServerCommandContext cmd,
 static void
 silc_server_command_whois_send_reply(SilcServerCommandContext cmd,
                                     SilcClientEntry *clients,
-                                    uint32 clients_count,
-                                    int count)
+                                    SilcUInt32 clients_count,
+                                    int count,
+                                    const char *nickname,
+                                    SilcClientID **client_ids)
 {
   SilcServer server = cmd->server;
   char *tmp;
-  int i, k, len;
+  int i, k, len, valid_count;
   SilcBuffer packet, idp, channels;
   SilcClientEntry entry;
   SilcCommandStatus status;
-  uint16 ident = silc_command_get_ident(cmd->payload);
+  SilcUInt16 ident = silc_command_get_ident(cmd->payload);
   char nh[256], uh[256];
   unsigned char idle[4], mode[4];
   unsigned char *fingerprint;
   SilcSocketConnection hsock;
 
-  len = 0;
-  for (i = 0; i < clients_count; i++)
+  /* Process only valid clients and ignore those that are not registered. */
+  valid_count = 0;
+  for (i = 0; i < clients_count; i++) {
     if (clients[i]->data.status & SILC_IDLIST_STATUS_REGISTERED)
-      len++;
+      valid_count++;
+    else
+      clients[i] = NULL;
+  }
 
-  if (len == 0 && clients_count) {
-    entry = clients[0];
-    if (entry->nickname) {
+  if (!valid_count) {
+    /* No valid clients found, send error reply */
+    if (nickname) {
       silc_server_command_send_status_data(cmd, SILC_COMMAND_WHOIS,
                                           SILC_STATUS_ERR_NO_SUCH_NICK,
-                                          3, entry->nickname, 
-                                          strlen(entry->nickname));
-    } else {
-      SilcBuffer idp = silc_id_payload_encode(entry->id, SILC_ID_CLIENT);
+                                          3, nickname, strlen(nickname));
+    } else if (client_ids && client_ids[0]) {
+      SilcBuffer idp = silc_id_payload_encode(client_ids[0], SILC_ID_CLIENT);
       silc_server_command_send_status_data(cmd, SILC_COMMAND_WHOIS,
                                           SILC_STATUS_ERR_NO_SUCH_CLIENT_ID,
                                           2, idp->data, idp->len);
       silc_buffer_free(idp);
     }
-
     return;
   }
 
-  status = SILC_STATUS_OK;
-  if (len > 1)
+  /* Start processing found clients. */
+  if (valid_count > 1)
     status = SILC_STATUS_LIST_START;
+  else
+    status = SILC_STATUS_OK;
 
   for (i = 0, k = 0; i < clients_count; i++) {
     entry = clients[i];
-
-    if (!(entry->data.status & SILC_IDLIST_STATUS_REGISTERED)) {
-      if (clients_count == 1) {
-       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);
-       }
-      }
+    if (!entry)
       continue;
-    }
 
     if (k >= 1)
       status = SILC_STATUS_LIST_ITEM;
-
-    if (clients_count > 1 && k == clients_count - 1)
+    if (valid_count > 1 && k == valid_count - 1)
       status = SILC_STATUS_LIST_END;
-
     if (count && k - 1 == count)
       status = SILC_STATUS_LIST_END;
 
-    if (count && k - 1 > count)
-      break;
-
-    /* Sanity check, however these should never fail. However, as
-       this sanity check has been added here they have failed. */
-    if (!entry->nickname || !entry->username || !entry->userinfo)
-      continue;
-      
     /* Send WHOIS reply */
     idp = silc_id_payload_encode(entry->id, SILC_ID_CLIENT);
     tmp = silc_argument_get_first_arg(cmd->args, NULL);
@@ -813,6 +826,33 @@ silc_server_command_whois_send_reply(SilcServerCommandContext cmd,
   }
 }
 
+static void 
+silc_server_command_whois_send_router(SilcServerCommandContext cmd)
+{
+  SilcServer server = cmd->server;
+  SilcBuffer tmpbuf;
+  SilcUInt16 old_ident;
+
+  old_ident = silc_command_get_ident(cmd->payload);
+  silc_command_set_ident(cmd->payload, ++server->cmd_ident);
+  tmpbuf = silc_command_payload_encode_payload(cmd->payload);
+
+  /* Send WHOIS command to our router */
+  silc_server_packet_send(server, (SilcSocketConnection)
+                         server->router->connection,
+                         SILC_PACKET_COMMAND, cmd->packet->flags,
+                         tmpbuf->data, tmpbuf->len, TRUE);
+
+  /* Reprocess this packet after received reply from router */
+  silc_server_command_pending(server, SILC_COMMAND_WHOIS, 
+                             silc_command_get_ident(cmd->payload),
+                             silc_server_command_whois,
+                             silc_server_command_dup(cmd));
+  cmd->pending = TRUE;
+  silc_command_set_ident(cmd->payload, old_ident);
+  silc_buffer_free(tmpbuf);
+}
+
 static int
 silc_server_command_whois_process(SilcServerCommandContext cmd)
 {
@@ -821,7 +861,7 @@ silc_server_command_whois_process(SilcServerCommandContext cmd)
   int count = 0;
   SilcClientEntry *clients = NULL, entry;
   SilcClientID **client_id = NULL;
-  uint32 client_id_count = 0, clients_count = 0;
+  SilcUInt32 client_id_count = 0, clients_count = 0;
   int i, ret = 0;
   bool check_global = FALSE;
 
@@ -835,34 +875,10 @@ silc_server_command_whois_process(SilcServerCommandContext cmd)
      Since nicknames can be expanded into many clients we need to send it
      to router.  If the WHOIS included only client ID's we will check them
      first locally since we just might have them. */
-  if (nick && !client_id_count &&
-      cmd->sock->type == SILC_SOCKET_TYPE_CLIENT &&
-      server->server_type == SILC_SERVER && !cmd->pending && 
+  if (nick && !client_id_count && cmd->sock->type == SILC_SOCKET_TYPE_CLIENT &&
+      server->server_type == SILC_SERVER && !cmd->pending &&
       !server->standalone) {
-    SilcBuffer tmpbuf;
-    uint16 old_ident;
-
-    old_ident = silc_command_get_ident(cmd->payload);
-    silc_command_set_ident(cmd->payload, ++server->cmd_ident);
-    tmpbuf = silc_command_payload_encode_payload(cmd->payload);
-
-    /* Send WHOIS command to our router */
-    silc_server_packet_send(server, (SilcSocketConnection)
-                           server->router->connection,
-                           SILC_PACKET_COMMAND, cmd->packet->flags,
-                           tmpbuf->data, tmpbuf->len, TRUE);
-
-    /* Reprocess this packet after received reply from router */
-    silc_server_command_pending(server, SILC_COMMAND_WHOIS, 
-                               silc_command_get_ident(cmd->payload),
-                               silc_server_command_destructor,
-                               silc_server_command_whois,
-                               silc_server_command_dup(cmd));
-    cmd->pending = TRUE;
-
-    silc_command_set_ident(cmd->payload, old_ident);
-
-    silc_buffer_free(tmpbuf);
+    silc_server_command_whois_send_router(cmd);
     ret = -1;
     goto out;
   }
@@ -885,9 +901,20 @@ silc_server_command_whois_process(SilcServerCommandContext cmd)
        clients = silc_realloc(clients, sizeof(*clients) * 
                               (clients_count + 1));
        clients[clients_count++] = entry;
+      } else {
+       /* If we are normal server and did not send the request first to router
+          do it now, since we do not have the Client ID information. */
+       if (cmd->sock->type == SILC_SOCKET_TYPE_CLIENT &&
+           server->server_type == SILC_SERVER && !cmd->pending && 
+           !server->standalone) {
+         silc_server_command_whois_send_router(cmd);
+         ret = -1;
+         goto out;
+       }
       }
     }
   } else {
+    /* Find by nickname */
     if (!silc_idlist_get_clients_by_hash(server->local_list, 
                                         nick, server->md5hash,
                                         &clients, &clients_count))
@@ -905,6 +932,16 @@ silc_server_command_whois_process(SilcServerCommandContext cmd)
   }
   
   if (!clients) {
+    /* If we are normal server and did not send the request first to router
+       do it now, since we do not have the information. */
+    if (cmd->sock->type == SILC_SOCKET_TYPE_CLIENT &&
+       server->server_type == SILC_SERVER && !cmd->pending && 
+       !server->standalone) {
+      silc_server_command_whois_send_router(cmd);
+      ret = -1;
+      goto out;
+    }
+
     /* Such client(s) really does not exist in the SILC network. */
     if (!client_id_count) {
       silc_server_command_send_status_data(cmd, SILC_COMMAND_WHOIS,
@@ -932,7 +969,7 @@ silc_server_command_whois_process(SilcServerCommandContext cmd)
 
   /* Send the command reply */
   silc_server_command_whois_send_reply(cmd, clients, clients_count,
-                                      count);
+                                      count, nick, client_id);
 
  out:
   if (client_id_count) {
@@ -958,9 +995,7 @@ SILC_SERVER_CMD_FUNC(whois)
   SILC_SERVER_COMMAND_CHECK(SILC_COMMAND_WHOIS, cmd, 1, 3328);
 
   ret = silc_server_command_whois_process(cmd);
-
-  if (!ret)
-    silc_server_command_free(cmd);
+  silc_server_command_free(cmd);
 }
 
 /******************************************************************************
@@ -976,7 +1011,7 @@ silc_server_command_whowas_parse(SilcServerCommandContext cmd,
                                 int *count)
 {
   unsigned char *tmp;
-  uint32 len;
+  SilcUInt32 len;
 
   tmp = silc_argument_get_arg_type(cmd->args, 1, &len);
   if (!tmp) {
@@ -1001,7 +1036,7 @@ silc_server_command_whowas_parse(SilcServerCommandContext cmd,
 static char
 silc_server_command_whowas_check(SilcServerCommandContext cmd,
                                 SilcClientEntry *clients,
-                                uint32 clients_count)
+                                SilcUInt32 clients_count)
 {
   SilcServer server = cmd->server;
   int i;
@@ -1012,7 +1047,7 @@ silc_server_command_whowas_check(SilcServerCommandContext cmd,
 
     if (!entry->nickname || !entry->username) {
       SilcBuffer tmpbuf;
-      uint16 old_ident;
+      SilcUInt16 old_ident;
 
       if (!entry->router)
        continue;
@@ -1029,11 +1064,9 @@ silc_server_command_whowas_check(SilcServerCommandContext cmd,
       /* Reprocess this packet after received reply */
       silc_server_command_pending(server, SILC_COMMAND_WHOWAS, 
                                  silc_command_get_ident(cmd->payload),
-                                 silc_server_command_destructor,
                                  silc_server_command_whowas, 
                                  silc_server_command_dup(cmd));
       cmd->pending = TRUE;
-      
       silc_command_set_ident(cmd->payload, old_ident);
 
       silc_buffer_free(tmpbuf);
@@ -1047,53 +1080,61 @@ silc_server_command_whowas_check(SilcServerCommandContext cmd,
 static void
 silc_server_command_whowas_send_reply(SilcServerCommandContext cmd,
                                      SilcClientEntry *clients,
-                                     uint32 clients_count)
+                                     SilcUInt32 clients_count)
 {
   SilcServer server = cmd->server;
   char *tmp;
-  int i, count = 0, len;
+  int i, k, count = 0, len;
   SilcBuffer packet, idp;
   SilcClientEntry entry = NULL;
   SilcCommandStatus status;
-  uint16 ident = silc_command_get_ident(cmd->payload);
-  char found = FALSE;
+  SilcUInt16 ident = silc_command_get_ident(cmd->payload);
   char nh[256], uh[256];
+  int valid_count;
 
   status = SILC_STATUS_OK;
-  if (clients_count > 1)
-    status = SILC_STATUS_LIST_START;
 
+  /* Process only entries that are not registered anymore. */
+  valid_count = 0;
   for (i = 0; i < clients_count; i++) {
-    entry = clients[i];
+    if (clients[i]->data.status & SILC_IDLIST_STATUS_REGISTERED)
+      clients[i] = NULL;
+    else
+      valid_count++;
+  }
 
-    /* We will take only clients that are not valid anymore. They are the
-       ones that are not registered anymore but still have a ID. They
-       have disconnected us, and thus valid for WHOWAS. */
-    if (entry->data.status & SILC_IDLIST_STATUS_REGISTERED)
-      continue;
-    if (entry->id == NULL)
-      continue;
+  if (!valid_count) {
+    /* No valid entries found at all, just send error */
+    unsigned char *tmp;
+    
+    tmp = silc_argument_get_arg_type(cmd->args, 1, NULL);
+    if (tmp)
+      silc_server_command_send_status_data(cmd, SILC_COMMAND_WHOWAS,
+                                          SILC_STATUS_ERR_NO_SUCH_NICK,
+                                          3, tmp, strlen(tmp));
+    return;
+  }
 
-    if (count && i - 1 == count)
-      break;
+  if (valid_count > 1)
+    status = SILC_STATUS_LIST_START;
 
-    found = TRUE;
+  for (i = 0, k = 0; i < clients_count; i++) {
+    entry = clients[i];
+    if (!entry)
+      continue;
 
-    if (clients_count > 2)
+    if (k >= 1)
       status = SILC_STATUS_LIST_ITEM;
-
-    if (clients_count > 1 && i == clients_count - 1)
+    if (valid_count > 1 && k == valid_count - 1)
       status = SILC_STATUS_LIST_END;
+    if (count && k - 1 == count)
+      status = SILC_STATUS_LIST_END;
+    if (count && k - 1 > count)
+      break;
 
-    /* Sanity check, however these should never fail. However, as
-       this sanity check has been added here they have failed. */
-    if (!entry->nickname || !entry->username)
-      continue;
-      
     /* Send WHOWAS reply */
     idp = silc_id_payload_encode(entry->id, SILC_ID_CLIENT);
     tmp = silc_argument_get_first_arg(cmd->args, NULL);
-    
     memset(uh, 0, sizeof(uh));
     memset(nh, 0, sizeof(nh));
 
@@ -1130,13 +1171,9 @@ silc_server_command_whowas_send_reply(SilcServerCommandContext cmd,
     
     silc_buffer_free(packet);
     silc_buffer_free(idp);
-  }
 
-  if (found == FALSE && entry)
-    silc_server_command_send_status_data(cmd, SILC_COMMAND_WHOWAS,
-                                        SILC_STATUS_ERR_NO_SUCH_NICK,
-                                        3, entry->nickname, 
-                                        strlen(entry->nickname));
+    k++;
+  }
 }
 
 static int
@@ -1146,7 +1183,7 @@ silc_server_command_whowas_process(SilcServerCommandContext cmd)
   char *nick = NULL, *server_name = NULL;
   int count = 0;
   SilcClientEntry *clients = NULL;
-  uint32 clients_count = 0;
+  SilcUInt32 clients_count = 0;
   int ret = 0;
   bool check_global = FALSE;
 
@@ -1158,7 +1195,7 @@ silc_server_command_whowas_process(SilcServerCommandContext cmd)
       server->server_type == SILC_SERVER && !cmd->pending && 
       !server->standalone) {
     SilcBuffer tmpbuf;
-    uint16 old_ident;
+    SilcUInt16 old_ident;
 
     old_ident = silc_command_get_ident(cmd->payload);
     silc_command_set_ident(cmd->payload, ++server->cmd_ident);
@@ -1173,11 +1210,9 @@ silc_server_command_whowas_process(SilcServerCommandContext cmd)
     /* Reprocess this packet after received reply from router */
     silc_server_command_pending(server, SILC_COMMAND_WHOWAS, 
                                silc_command_get_ident(cmd->payload),
-                               silc_server_command_destructor,
                                silc_server_command_whowas,
                                silc_server_command_dup(cmd));
     cmd->pending = TRUE;
-
     silc_command_set_ident(cmd->payload, old_ident);
 
     silc_buffer_free(tmpbuf);
@@ -1235,7 +1270,6 @@ silc_server_command_whowas_process(SilcServerCommandContext cmd)
   silc_free(clients);
   silc_free(nick);
   silc_free(server_name);
-
   return ret;
 }
 
@@ -1249,9 +1283,7 @@ SILC_SERVER_CMD_FUNC(whowas)
   SILC_SERVER_COMMAND_CHECK(SILC_COMMAND_WHOWAS, cmd, 1, 2);
 
   ret = silc_server_command_whowas_process(cmd);
-
-  if (!ret)
-    silc_server_command_free(cmd);
+  silc_server_command_free(cmd);
 }
 
 /******************************************************************************
@@ -1260,21 +1292,47 @@ SILC_SERVER_CMD_FUNC(whowas)
 
 ******************************************************************************/
 
-static bool
+static void 
+silc_server_command_identify_send_router(SilcServerCommandContext cmd)
+{
+  SilcServer server = cmd->server;
+  SilcBuffer tmpbuf;
+  SilcUInt16 old_ident;
+
+  old_ident = silc_command_get_ident(cmd->payload);
+  silc_command_set_ident(cmd->payload, ++server->cmd_ident);
+  tmpbuf = silc_command_payload_encode_payload(cmd->payload);
+
+  /* Send IDENTIFY command to our router */
+  silc_server_packet_send(server, (SilcSocketConnection)
+                         server->router->connection,
+                         SILC_PACKET_COMMAND, cmd->packet->flags,
+                         tmpbuf->data, tmpbuf->len, TRUE);
+
+  /* Reprocess this packet after received reply from router */
+  silc_server_command_pending(server, SILC_COMMAND_IDENTIFY, 
+                             silc_command_get_ident(cmd->payload),
+                             silc_server_command_identify,
+                             silc_server_command_dup(cmd));
+  cmd->pending = TRUE;
+  silc_command_set_ident(cmd->payload, old_ident);
+  silc_buffer_free(tmpbuf);
+}
+
+static int
 silc_server_command_identify_parse(SilcServerCommandContext cmd,
                                   SilcClientEntry **clients,
-                                  uint32 *clients_count,
+                                  SilcUInt32 *clients_count,
                                   SilcServerEntry **servers,
-                                  uint32 *servers_count,
+                                  SilcUInt32 *servers_count,
                                   SilcChannelEntry **channels,
-                                  uint32 *channels_count,
-                                  uint32 *count,
-                                  bool *names)
+                                  SilcUInt32 *channels_count,
+                                  SilcUInt32 *count)
 {
   SilcServer server = cmd->server;
   unsigned char *tmp;
-  uint32 len;
-  uint32 argc = silc_argument_get_arg_num(cmd->args);
+  SilcUInt32 len;
+  SilcUInt32 argc = silc_argument_get_arg_num(cmd->args);
   SilcIDPayload idp;
   bool check_global = FALSE;
   void *entry;
@@ -1290,7 +1348,15 @@ silc_server_command_identify_parse(SilcServerCommandContext cmd,
   tmp = silc_argument_get_arg_type(cmd->args, 5, &len);
   if (!tmp) {
     /* No ID, get the names. */
-    *names = TRUE;
+
+    /* If we are normal server and have not resolved information from
+       router yet, do so now. */
+    if (cmd->sock->type == SILC_SOCKET_TYPE_CLIENT && 
+       server->server_type == SILC_SERVER && !cmd->pending && 
+       !server->standalone) {
+      silc_server_command_identify_send_router(cmd);
+      return -1;
+    }
 
     /* Try to get nickname@server. */
     tmp = silc_argument_get_arg_type(cmd->args, 1, NULL);
@@ -1319,10 +1385,11 @@ silc_server_command_identify_parse(SilcServerCommandContext cmd,
       silc_free(nick_server);
 
       if (!(*clients)) {
+       /* the nickname does not exist, send error reply */
        silc_server_command_send_status_data(cmd, SILC_COMMAND_IDENTIFY,
                                             SILC_STATUS_ERR_NO_SUCH_NICK,
                                             3, tmp, strlen(tmp));
-       return FALSE;
+       return 0;
       }
     }
 
@@ -1341,10 +1408,11 @@ silc_server_command_identify_parse(SilcServerCommandContext cmd,
       }
 
       if (!(*servers)) {
+       /* the server does not exist, send error reply */
        silc_server_command_send_status_data(cmd, SILC_COMMAND_IDENTIFY,
                                             SILC_STATUS_ERR_NO_SUCH_SERVER,
                                             3, tmp, strlen(tmp));
-       return FALSE;
+       return 0;
       }
     }
 
@@ -1363,17 +1431,18 @@ silc_server_command_identify_parse(SilcServerCommandContext cmd,
       }
 
       if (!(*channels)) {
+       /* The channel does not exist, send error reply */
        silc_server_command_send_status_data(cmd, SILC_COMMAND_IDENTIFY,
                                             SILC_STATUS_ERR_NO_SUCH_CHANNEL,
                                             3, tmp, strlen(tmp));
-       return FALSE;
+       return 0;
       }
     }
 
     if (!(*clients) && !(*servers) && !(*channels)) {
       silc_server_command_send_status_reply(cmd, SILC_COMMAND_IDENTIFY,
                                            SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
-      return FALSE;
+      return 0;
     }
   } else {
     /* Command includes ID, we must use that.  Also check whether the command
@@ -1392,9 +1461,10 @@ silc_server_command_identify_parse(SilcServerCommandContext cmd,
        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;
+       silc_server_command_send_status_reply(
+                                      cmd, SILC_COMMAND_IDENTIFY,
+                                      SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
+       return 0;
       }
 
       id = silc_id_payload_get_id(idp);
@@ -1412,10 +1482,23 @@ silc_server_command_identify_parse(SilcServerCommandContext cmd,
                                  (*clients_count + 1));
          (*clients)[(*clients_count)++] = (SilcClientEntry)entry;
        } else {
-         silc_server_command_send_status_data(cmd, SILC_COMMAND_IDENTIFY,
+         /* If we are normal server and have not resolved information from
+            router yet, do so now. */
+         if (cmd->sock->type == SILC_SOCKET_TYPE_CLIENT && 
+             server->server_type == SILC_SERVER && !cmd->pending && 
+             !server->standalone) {
+           silc_server_command_identify_send_router(cmd);
+           silc_free(*clients);
+           silc_free(*servers);
+           silc_free(*channels);
+           return -1;
+         } else {
+           silc_server_command_send_status_data(
+                                       cmd, SILC_COMMAND_IDENTIFY,
                                        SILC_STATUS_ERR_NO_SUCH_CLIENT_ID,
-                                              2, tmp, len);
-         error = TRUE;
+                                       2, tmp, len);
+           error = TRUE;
+         }
        }
 
        break;
@@ -1431,10 +1514,23 @@ silc_server_command_identify_parse(SilcServerCommandContext cmd,
                                  (*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;
+         /* If we are normal server and have not resolved information from
+            router yet, do so now. */
+         if (cmd->sock->type == SILC_SOCKET_TYPE_CLIENT && 
+             server->server_type == SILC_SERVER && !cmd->pending && 
+             !server->standalone) {
+           silc_server_command_identify_send_router(cmd);
+           silc_free(*clients);
+           silc_free(*servers);
+           silc_free(*channels);
+           return -1;
+         } else {
+           silc_server_command_send_status_data(
+                                        cmd, SILC_COMMAND_IDENTIFY,
+                                        SILC_STATUS_ERR_NO_SUCH_SERVER_ID,
+                                        2, tmp, len);
+           error = TRUE;
+         }
        }
        break;
        
@@ -1449,10 +1545,23 @@ silc_server_command_identify_parse(SilcServerCommandContext cmd,
                                   (*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;
+         /* If we are normal server and have not resolved information from
+            router yet, do so now. */
+         if (cmd->sock->type == SILC_SOCKET_TYPE_CLIENT && 
+             server->server_type == SILC_SERVER && !cmd->pending && 
+             !server->standalone) {
+           silc_server_command_identify_send_router(cmd);
+           silc_free(*clients);
+           silc_free(*servers);
+           silc_free(*channels);
+           return -1;
+         } else {
+           silc_server_command_send_status_data(
+                                        cmd, SILC_COMMAND_IDENTIFY,
+                                        SILC_STATUS_ERR_NO_SUCH_CHANNEL_ID,
+                                        2, tmp, len);
+           error = TRUE;
+         }
        }
        break;
       }
@@ -1475,7 +1584,7 @@ silc_server_command_identify_parse(SilcServerCommandContext cmd,
   else
     *count = 0;
 
-  return TRUE;
+  return 1;
 }
 
 /* Checks that all mandatory fields in client entry are present. If not
@@ -1485,23 +1594,34 @@ silc_server_command_identify_parse(SilcServerCommandContext cmd,
 static bool
 silc_server_command_identify_check_client(SilcServerCommandContext cmd,
                                          SilcClientEntry *clients,
-                                         uint32 clients_count)
+                                         SilcUInt32 clients_count)
 {
   SilcServer server = cmd->server;
   SilcClientEntry entry;
   SilcServerResolveContext resolve = NULL, r = NULL;
-  uint32 resolve_count = 0;
+  SilcUInt32 resolve_count = 0;
   int i, k;
   bool no_res = TRUE;
 
   for (i = 0; i < clients_count; i++) {
     entry = clients[i];
-
-    if (!entry || entry->nickname || 
-       !(entry->data.status & SILC_IDLIST_STATUS_REGISTERED) ||
-       !entry->router)
+    if (!entry)
       continue;
 
+    if (entry->nickname || 
+       !(entry->data.status & SILC_IDLIST_STATUS_REGISTERED)) {
+      if (!entry->router)
+       continue;
+
+      /* If we are normal server, and we've not resolved this client from
+        router and it is global client, we'll check whether it is on some
+        channel.  If not then we cannot be sure about its validity, and
+        we'll resolve it from router. */
+      if (cmd->server->server_type != SILC_SERVER || cmd->pending ||
+         entry->connection || silc_hash_table_count(entry->channels))
+       continue;
+    }
+
     /* We need to resolve this entry since it is not complete */
 
     if (!cmd->pending && entry->data.status & SILC_IDLIST_STATUS_RESOLVING) {
@@ -1509,7 +1629,6 @@ silc_server_command_identify_check_client(SilcServerCommandContext cmd,
         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;
@@ -1592,7 +1711,6 @@ silc_server_command_identify_check_client(SilcServerCommandContext cmd,
     /* 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;
@@ -1613,18 +1731,18 @@ silc_server_command_identify_check_client(SilcServerCommandContext cmd,
 static void
 silc_server_command_identify_send_reply(SilcServerCommandContext cmd,
                                        SilcClientEntry *clients,
-                                       uint32 clients_count,
+                                       SilcUInt32 clients_count,
                                        SilcServerEntry *servers,
-                                       uint32 servers_count,
+                                       SilcUInt32 servers_count,
                                        SilcChannelEntry *channels,
-                                       uint32 channels_count,
+                                       SilcUInt32 channels_count,
                                        int count)
 {
   SilcServer server = cmd->server;
-  int i, k, len;
+  int i, k, len, valid_count;
   SilcBuffer packet, idp;
   SilcCommandStatus status;
-  uint16 ident = silc_command_get_ident(cmd->payload);
+  SilcUInt16 ident = silc_command_get_ident(cmd->payload);
   char nh[256], uh[256];
   SilcSocketConnection hsock;
 
@@ -1633,62 +1751,58 @@ silc_server_command_identify_send_reply(SilcServerCommandContext cmd,
   if (clients) {
     SilcClientEntry entry;
 
-    len = 0;
-    for (i = 0; i < clients_count; i++)
+    /* Process only valid entries. */
+    valid_count = 0;
+    for (i = 0; i < clients_count; i++) {
       if (clients[i]->data.status & SILC_IDLIST_STATUS_REGISTERED)
-       len++;
+       valid_count++;
+      else
+       clients[i] = NULL;
+    }
 
-    if (len == 0 && clients_count) {
-      entry = clients[0];
-      if (entry->nickname) {
+    if (!valid_count) {
+      /* No valid entries found at all, just send error */
+      unsigned char *tmp;
+
+      tmp = silc_argument_get_arg_type(cmd->args, 1, NULL);
+      if (tmp) {
        silc_server_command_send_status_data(cmd, SILC_COMMAND_IDENTIFY,
                                             SILC_STATUS_ERR_NO_SUCH_NICK,
-                                            3, entry->nickname, 
-                                            strlen(entry->nickname));
+                                            3, tmp, strlen(tmp));
       } else {
-       SilcBuffer idp = silc_id_payload_encode(entry->id, SILC_ID_CLIENT);
+       tmp = silc_argument_get_arg_type(cmd->args, 5, (SilcUInt32 *)&len);
        silc_server_command_send_status_data(cmd, SILC_COMMAND_IDENTIFY,
                                             SILC_STATUS_ERR_NO_SUCH_CLIENT_ID,
-                                            2, idp->data, idp->len);
-       silc_buffer_free(idp);
+                                            2, tmp, len);
       }
-      
       return;
     }
 
-    if (len > 1)
+    /* Process all valid client entries and send command replies */
+
+    if (valid_count > 1)
       status = SILC_STATUS_LIST_START;
 
     for (i = 0, k = 0; i < clients_count; i++) {
       entry = clients[i];
-      
-      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);
-       }
+      if (!entry)
        continue;
-      }
-      
+
       if (k >= 1)
        status = SILC_STATUS_LIST_ITEM;
-      if (clients_count > 1 && k == clients_count - 1 
+      if (valid_count > 1 && k == valid_count - 1 
          && !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);
@@ -1701,7 +1815,7 @@ silc_server_command_identify_send_reply(SilcServerCommandContext cmd,
                  server->server_name, len);
        }
       }
-      
+
       if (!entry->username) {
        packet = silc_command_reply_payload_encode_va(SILC_COMMAND_IDENTIFY,
                                                      status, ident, 2,
@@ -1733,9 +1847,6 @@ silc_server_command_identify_send_reply(SilcServerCommandContext cmd,
     }
   }
 
-  status = (status == SILC_STATUS_LIST_ITEM ? 
-           SILC_STATUS_LIST_ITEM : SILC_STATUS_OK);
-
   if (servers) {
     SilcServerEntry entry;
 
@@ -1773,9 +1884,6 @@ silc_server_command_identify_send_reply(SilcServerCommandContext cmd,
     }
   }
 
-  status = (status == SILC_STATUS_LIST_ITEM ? 
-           SILC_STATUS_LIST_ITEM : SILC_STATUS_OK);
-
   if (channels) {
     SilcChannelEntry entry;
 
@@ -1817,57 +1925,22 @@ silc_server_command_identify_send_reply(SilcServerCommandContext cmd,
 static int
 silc_server_command_identify_process(SilcServerCommandContext cmd)
 {
-  SilcServer server = cmd->server;
-  uint32 count = 0;
+  SilcUInt32 count = 0;
   int ret = 0;
   SilcClientEntry *clients = NULL;
   SilcServerEntry *servers = NULL;
   SilcChannelEntry *channels = NULL;
-  uint32 clients_count = 0, servers_count = 0, channels_count = 0;
-  bool names;
+  SilcUInt32 clients_count = 0, servers_count = 0, channels_count = 0;
 
   /* Parse the IDENTIFY request */
-  if (!silc_server_command_identify_parse(cmd,
-                                         &clients, &clients_count,
-                                         &servers, &servers_count,
-                                         &channels, &channels_count,
-                                         &count, &names))
-    return 0;
-
-  /* Send the IDENTIFY request to the router only if it included nickname.
-     Since nicknames can be expanded into many clients we need to send it
-     to router.  If the IDENTIFY included only client ID's we will check them
-     first locally since we just might have them. */
-  if (names && cmd->sock->type == SILC_SOCKET_TYPE_CLIENT && 
-      server->server_type == SILC_SERVER && !cmd->pending && 
-      !server->standalone) {
-    SilcBuffer tmpbuf;
-    uint16 old_ident;
-
-    old_ident = silc_command_get_ident(cmd->payload);
-    silc_command_set_ident(cmd->payload, ++server->cmd_ident);
-    tmpbuf = silc_command_payload_encode_payload(cmd->payload);
-
-    /* Send IDENTIFY command to our router */
-    silc_server_packet_send(server, (SilcSocketConnection)
-                           server->router->connection,
-                           SILC_PACKET_COMMAND, cmd->packet->flags,
-                           tmpbuf->data, tmpbuf->len, TRUE);
-
-    /* Reprocess this packet after received reply from router */
-    silc_server_command_pending(server, SILC_COMMAND_IDENTIFY, 
-                               silc_command_get_ident(cmd->payload),
-                               silc_server_command_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;
-  }
+  ret = silc_server_command_identify_parse(cmd,
+                                          &clients, &clients_count,
+                                          &servers, &servers_count,
+                                          &channels, &channels_count,
+                                          &count);
+  if (ret < 1)
+    return ret;
+  ret = 0;
 
   /* Check that all mandatory fields are present and request those data
      from the server who owns the client if necessary. */
@@ -1888,7 +1961,6 @@ silc_server_command_identify_process(SilcServerCommandContext cmd)
   silc_free(clients);
   silc_free(servers);
   silc_free(channels);
-
   return ret;
 }
 
@@ -1900,28 +1972,7 @@ SILC_SERVER_CMD_FUNC(identify)
   SILC_SERVER_COMMAND_CHECK(SILC_COMMAND_IDENTIFY, cmd, 1, 3328);
 
   ret = silc_server_command_identify_process(cmd);
-
-  if (!ret)
-    silc_server_command_free(cmd);
-}
-
-/* Checks string for bad characters and returns TRUE if they are found. */
-
-static int silc_server_command_bad_chars(char *nick)
-{
-  int i;
-
-  for (i = 0; i < strlen(nick); i++) {
-    if (!isascii(nick[i]))
-      return TRUE;
-    if (nick[i] <= 32) return TRUE;
-    if (nick[i] == ' ') return TRUE;
-    if (nick[i] == '*') return TRUE;
-    if (nick[i] == '?') return TRUE;
-    if (nick[i] == ',') return TRUE;
-  }
-
-  return FALSE;
+  silc_server_command_free(cmd);
 }
 
 /* Server side of command NICK. Sets nickname for user. Setting
@@ -1935,8 +1986,9 @@ SILC_SERVER_CMD_FUNC(nick)
   SilcServer server = cmd->server;
   SilcBuffer packet, nidp, oidp = NULL;
   SilcClientID *new_id;
+  SilcUInt32 nick_len;
   char *nick;
-  uint16 ident = silc_command_get_ident(cmd->payload);
+  SilcUInt16 ident = silc_command_get_ident(cmd->payload);
   int nickfail = 0;
 
   if (cmd->sock->type != SILC_SOCKET_TYPE_CLIENT)
@@ -1945,16 +1997,15 @@ SILC_SERVER_CMD_FUNC(nick)
   SILC_SERVER_COMMAND_CHECK(SILC_COMMAND_NICK, cmd, 1, 1);
 
   /* Check nickname */
-  nick = silc_argument_get_arg_type(cmd->args, 1, NULL);
-  if (silc_server_command_bad_chars(nick) == TRUE) {
+  nick = silc_argument_get_arg_type(cmd->args, 1, &nick_len);
+  if (nick_len > 128)
+    nick[128] = '\0';
+  if (silc_server_name_bad_chars(nick, nick_len) == TRUE) {
     silc_server_command_send_status_reply(cmd, SILC_COMMAND_NICK,
                                          SILC_STATUS_ERR_BAD_NICKNAME);
     goto out;
   }
 
-  if (strlen(nick) > 128)
-    nick[128] = '\0';
-
   /* Check for same nickname */
   if (!strcmp(client->nickname, nick)) {
     nidp = silc_id_payload_encode(client->id, SILC_ID_CLIENT);
@@ -1977,7 +2028,7 @@ SILC_SERVER_CMD_FUNC(nick)
     silc_server_send_notify_nick_change(server, server->router->connection, 
                                        server->server_type == SILC_SERVER ? 
                                        FALSE : TRUE, client->id,
-                                       new_id);
+                                       new_id, nick);
 
   oidp = silc_id_payload_encode(client->id, SILC_ID_CLIENT);
 
@@ -1995,15 +2046,17 @@ SILC_SERVER_CMD_FUNC(nick)
 
   /* Update client cache */
   silc_idcache_add(server->local_list->clients, client->nickname, 
-                  client->id, (void *)client, FALSE);
+                  client->id, (void *)client, 0, NULL);
 
   nidp = silc_id_payload_encode(client->id, SILC_ID_CLIENT);
 
   /* Send NICK_CHANGE notify to the client's channels */
   silc_server_send_notify_on_channels(server, NULL, client, 
-                                     SILC_NOTIFY_TYPE_NICK_CHANGE, 2,
+                                     SILC_NOTIFY_TYPE_NICK_CHANGE, 3,
                                      oidp->data, oidp->len, 
-                                     nidp->data, nidp->len);
+                                     nidp->data, nidp->len,
+                                     client->nickname, 
+                                     strlen(client->nickname));
 
  send_reply:
   /* Send the new Client ID as reply command back to client */
@@ -2027,39 +2080,46 @@ SILC_SERVER_CMD_FUNC(nick)
 static void
 silc_server_command_list_send_reply(SilcServerCommandContext cmd,
                                    SilcChannelEntry *lch, 
-                                   uint32 lch_count,
+                                   SilcUInt32 lch_count,
                                    SilcChannelEntry *gch,
-                                   uint32 gch_count)
+                                   SilcUInt32 gch_count)
 {
-  int i;
+  int i, k;
   SilcBuffer packet, idp;
   SilcChannelEntry entry;
   SilcCommandStatus status;
-  uint16 ident = silc_command_get_ident(cmd->payload);
+  SilcUInt16 ident = silc_command_get_ident(cmd->payload);
   char *topic;
   unsigned char usercount[4];
-  uint32 users;
+  SilcUInt32 users;
+  int valid_lcount = 0, valid_rcount = 0;
 
-  for (i = 0; i < lch_count; i++)
+  for (i = 0; i < lch_count; i++) {
     if (lch[i]->mode & SILC_CHANNEL_MODE_SECRET)
       lch[i] = NULL;
-  for (i = 0; i < gch_count; i++)
+    else
+      valid_lcount++;
+  }
+  for (i = 0; i < gch_count; i++) {
     if (gch[i]->mode & SILC_CHANNEL_MODE_SECRET)
       gch[i] = NULL;
+    else
+      valid_rcount++;
+  }
 
   status = SILC_STATUS_OK;
   if ((lch_count + gch_count) > 1)
     status = SILC_STATUS_LIST_START;
 
   /* Local list */
-  for (i = 0; i < lch_count; i++) {
+  for (i = 0, k = 0; i < lch_count; i++) {
     entry = lch[i];
     if (!entry)
       continue;
 
-    if (i >= 1)
+    if (k >= 1)
       status = SILC_STATUS_LIST_ITEM;
-    if (i >= 1 && i == lch_count - 1 && !gch_count)
+    if (valid_lcount > 1 && k == valid_lcount - 1 && !valid_rcount)
       status = SILC_STATUS_LIST_END;
 
     idp = silc_id_payload_encode(entry->id, SILC_ID_CHANNEL);
@@ -2087,17 +2147,18 @@ silc_server_command_list_send_reply(SilcServerCommandContext cmd,
                            packet->len, FALSE);
     silc_buffer_free(packet);
     silc_buffer_free(idp);
+    k++;
   }
 
   /* Global list */
-  for (i = 0; i < gch_count; i++) {
+  for (i = 0, k = 0; i < gch_count; i++) {
     entry = gch[i];
     if (!entry)
       continue;
 
-    if (i >= 1)
+    if (k >= 1)
       status = SILC_STATUS_LIST_ITEM;
-    if (i >= 1 && i == gch_count - 1)
+    if (valid_rcount > 1 && k == valid_rcount - 1)
       status = SILC_STATUS_LIST_END;
 
     idp = silc_id_payload_encode(entry->id, SILC_ID_CHANNEL);
@@ -2107,7 +2168,7 @@ silc_server_command_list_send_reply(SilcServerCommandContext cmd,
       memset(usercount, 0, sizeof(usercount));
     } else {
       topic = entry->topic;
-      users = silc_hash_table_count(entry->user_list);
+      users = entry->user_count;
       SILC_PUT32_MSB(users, usercount);
     }
 
@@ -2125,6 +2186,7 @@ silc_server_command_list_send_reply(SilcServerCommandContext cmd,
                            packet->len, FALSE);
     silc_buffer_free(packet);
     silc_buffer_free(idp);
+    k++;
   }
 }
 
@@ -2137,9 +2199,9 @@ SILC_SERVER_CMD_FUNC(list)
   SilcServer server = cmd->server;
   SilcChannelID *channel_id = NULL;
   unsigned char *tmp;
-  uint32 tmp_len;
+  SilcUInt32 tmp_len;
   SilcChannelEntry *lchannels = NULL, *gchannels = NULL;
-  uint32 lch_count = 0, gch_count = 0;
+  SilcUInt32 lch_count = 0, gch_count = 0;
 
   SILC_SERVER_COMMAND_CHECK(SILC_COMMAND_LIST, cmd, 0, 1);
 
@@ -2148,7 +2210,7 @@ SILC_SERVER_CMD_FUNC(list)
   if (!cmd->pending && server->server_type == SILC_SERVER && 
       !server->standalone) {
     SilcBuffer tmpbuf;
-    uint16 old_ident;
+    SilcUInt16 old_ident;
     
     old_ident = silc_command_get_ident(cmd->payload);
     silc_command_set_ident(cmd->payload, ++server->cmd_ident);
@@ -2160,19 +2222,18 @@ SILC_SERVER_CMD_FUNC(list)
     /* Reprocess this packet after received reply from router */
     silc_server_command_pending(server, SILC_COMMAND_LIST, 
                                silc_command_get_ident(cmd->payload),
-                               silc_server_command_destructor,
                                silc_server_command_list, 
                                silc_server_command_dup(cmd));
     cmd->pending = TRUE;
     silc_command_set_ident(cmd->payload, old_ident);
     silc_buffer_free(tmpbuf);
-    return;
+    goto out;
   }
 
   /* Get Channel ID */
   tmp = silc_argument_get_arg_type(cmd->args, 1, &tmp_len);
   if (tmp) {
-    channel_id = silc_id_payload_parse_id(tmp, tmp_len);
+    channel_id = silc_id_payload_parse_id(tmp, tmp_len, NULL);
     if (!channel_id) {
       silc_server_command_send_status_reply(cmd, SILC_COMMAND_LIST,
                                            SILC_STATUS_ERR_NO_CHANNEL_ID);
@@ -2212,8 +2273,8 @@ SILC_SERVER_CMD_FUNC(topic)
   SilcChannelClientEntry chl;
   SilcBuffer packet, idp;
   unsigned char *tmp;
-  uint32 argc, tmp_len;
-  uint16 ident = silc_command_get_ident(cmd->payload);
+  SilcUInt32 argc, tmp_len;
+  SilcUInt16 ident = silc_command_get_ident(cmd->payload);
 
   SILC_SERVER_COMMAND_CHECK(SILC_COMMAND_TOPIC, cmd, 1, 2);
 
@@ -2226,7 +2287,7 @@ SILC_SERVER_CMD_FUNC(topic)
                                          SILC_STATUS_ERR_NO_CHANNEL_ID);
     goto out;
   }
-  channel_id = silc_id_payload_parse_id(tmp, tmp_len);
+  channel_id = silc_id_payload_parse_id(tmp, tmp_len, NULL);
   if (!channel_id) {
     silc_server_command_send_status_reply(cmd, SILC_COMMAND_TOPIC,
                                          SILC_STATUS_ERR_NO_CHANNEL_ID);
@@ -2262,19 +2323,17 @@ SILC_SERVER_CMD_FUNC(topic)
     }
 
     /* 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)) {
+    if (!silc_server_client_on_channel(client, channel, &chl)) {
       silc_server_command_send_status_reply(cmd, SILC_COMMAND_TOPIC,
                                            SILC_STATUS_ERR_NOT_ON_CHANNEL);
       goto out;
     }
 
-    if (chl->mode == SILC_CHANNEL_UMODE_NONE) {
-      if (channel->mode & SILC_CHANNEL_MODE_TOPIC) {
-       silc_server_command_send_status_reply(cmd, SILC_COMMAND_TOPIC,
-                                             SILC_STATUS_ERR_NO_CHANNEL_PRIV);
-       goto out;
-      }
+    if (chl->mode == SILC_CHANNEL_UMODE_NONE && 
+       channel->mode & SILC_CHANNEL_MODE_TOPIC) {
+      silc_server_command_send_status_reply(cmd, SILC_COMMAND_TOPIC,
+                                           SILC_STATUS_ERR_NO_CHANNEL_PRIV);
+      goto out;
     }
 
     /* Set the topic for channel */
@@ -2292,7 +2351,7 @@ SILC_SERVER_CMD_FUNC(topic)
     idp = silc_id_payload_encode(client->id, SILC_ID_CLIENT);
 
     /* Send notify about topic change to all clients on the channel */
-    silc_server_send_notify_to_channel(server, NULL, channel, TRUE, 
+    silc_server_send_notify_to_channel(server, NULL, channel, FALSE, 
                                       SILC_NOTIFY_TYPE_TOPIC_SET, 2,
                                       idp->data, idp->len,
                                       channel->topic, strlen(channel->topic));
@@ -2334,8 +2393,8 @@ SILC_SERVER_CMD_FUNC(invite)
   SilcIDListData idata;
   SilcBuffer idp, idp2, packet;
   unsigned char *tmp, *add, *del;
-  uint32 len;
-  uint16 ident = silc_command_get_ident(cmd->payload);
+  SilcUInt32 len;
+  SilcUInt16 ident = silc_command_get_ident(cmd->payload);
 
   SILC_SERVER_COMMAND_CHECK(SILC_COMMAND_INVITE, cmd, 1, 4);
 
@@ -2346,7 +2405,7 @@ SILC_SERVER_CMD_FUNC(invite)
                                          SILC_STATUS_ERR_NO_CHANNEL_ID);
     goto out;
   }
-  channel_id = silc_id_payload_parse_id(tmp, len);
+  channel_id = silc_id_payload_parse_id(tmp, len, NULL);
   if (!channel_id) {
     silc_server_command_send_status_reply(cmd, SILC_COMMAND_INVITE,
                                          SILC_STATUS_ERR_NO_CHANNEL_ID);
@@ -2368,7 +2427,7 @@ SILC_SERVER_CMD_FUNC(invite)
 
   /* Check whether the sender of this command is on the channel. */
   sender = (SilcClientEntry)sock->user_data;
-  if (!silc_server_client_on_channel(sender, channel)) {
+  if (!silc_server_client_on_channel(sender, channel, &chl)) {
     silc_server_command_send_status_reply(cmd, SILC_COMMAND_INVITE,
                                          SILC_STATUS_ERR_NOT_ON_CHANNEL);
     goto out;
@@ -2376,21 +2435,20 @@ 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_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,
+  if (chl->mode == SILC_CHANNEL_UMODE_NONE &&
+      channel->mode & SILC_CHANNEL_MODE_INVITE) {
+    silc_server_command_send_status_reply(cmd, SILC_COMMAND_INVITE,
                                            SILC_STATUS_ERR_NO_CHANNEL_PRIV);
-      goto out;
-    }
+    goto out;
   }
 
   /* Get destination client ID */
   tmp = silc_argument_get_arg_type(cmd->args, 2, &len);
   if (tmp) {
     char invite[512];
+    bool resolve;
 
-    dest_id = silc_id_payload_parse_id(tmp, len);
+    dest_id = silc_id_payload_parse_id(tmp, len, NULL);
     if (!dest_id) {
       silc_server_command_send_status_reply(cmd, SILC_COMMAND_INVITE,
                                            SILC_STATUS_ERR_NO_CLIENT_ID);
@@ -2398,11 +2456,12 @@ SILC_SERVER_CMD_FUNC(invite)
     }
 
     /* Get the client entry */
-    dest = silc_server_get_client_resolve(server, dest_id);
+    dest = silc_server_get_client_resolve(server, dest_id, &resolve);
     if (!dest) {
-      if (server->server_type != SILC_SERVER) {
-       silc_server_command_send_status_reply(cmd, SILC_COMMAND_INVITE,
-                                    SILC_STATUS_ERR_NO_SUCH_CLIENT_ID);
+      if (server->server_type != SILC_SERVER || !resolve) {
+       silc_server_command_send_status_reply(
+                                       cmd, SILC_COMMAND_INVITE,
+                                       SILC_STATUS_ERR_NO_SUCH_CLIENT_ID);
        goto out;
       }
       
@@ -2410,17 +2469,16 @@ SILC_SERVER_CMD_FUNC(invite)
         receiving the reply to the query. */
       silc_server_command_pending(server, SILC_COMMAND_WHOIS, 
                                  server->cmd_ident,
-                                 silc_server_command_destructor,
                                  silc_server_command_invite, 
                                  silc_server_command_dup(cmd));
       cmd->pending = TRUE;
       silc_free(channel_id);
       silc_free(dest_id);
-      return;
+      goto out;
     }
 
     /* Check whether the requested client is already on the channel. */
-    if (silc_server_client_on_channel(dest, channel)) {
+    if (silc_server_client_on_channel(dest, channel, NULL)) {
       silc_server_command_send_status_reply(cmd, SILC_COMMAND_INVITE,
                                            SILC_STATUS_ERR_USER_ON_CHANNEL);
       goto out;
@@ -2578,7 +2636,7 @@ SILC_SERVER_CMD_FUNC(quit)
   SilcSocketConnection sock = cmd->sock;
   QuitInternal q;
   unsigned char *tmp = NULL;
-  uint32 len = 0;
+  SilcUInt32 len = 0;
 
   SILC_SERVER_COMMAND_CHECK(SILC_COMMAND_QUIT, cmd, 0, 1);
 
@@ -2615,7 +2673,9 @@ SILC_SERVER_CMD_FUNC(kill)
   SilcClientEntry remote_client;
   SilcClientID *client_id;
   unsigned char *tmp, *comment;
-  uint32 tmp_len, tmp_len2;
+  SilcUInt32 tmp_len, tmp_len2;
+  SilcBuffer killer;
+  bool local;
 
   SILC_SERVER_COMMAND_CHECK(SILC_COMMAND_KILL, cmd, 1, 2);
 
@@ -2643,7 +2703,7 @@ SILC_SERVER_CMD_FUNC(kill)
                                          SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
     goto out;
   }
-  client_id = silc_id_payload_parse_id(tmp, tmp_len);
+  client_id = silc_id_payload_parse_id(tmp, tmp_len, NULL);
   if (!client_id) {
     silc_server_command_send_status_reply(cmd, SILC_COMMAND_KILL,
                                          SILC_STATUS_ERR_NO_SUCH_CLIENT_ID);
@@ -2653,9 +2713,11 @@ SILC_SERVER_CMD_FUNC(kill)
   /* Get the client entry */
   remote_client = silc_idlist_find_client_by_id(server->local_list, 
                                                client_id, TRUE, NULL);
+  local = TRUE;
   if (!remote_client) {
     remote_client = silc_idlist_find_client_by_id(server->global_list, 
                                                  client_id, TRUE, NULL);
+    local = FALSE;
     if (!remote_client) {
       silc_server_command_send_status_reply(cmd, SILC_COMMAND_KILL,
                                            SILC_STATUS_ERR_NO_SUCH_CLIENT_ID);
@@ -2666,7 +2728,7 @@ SILC_SERVER_CMD_FUNC(kill)
   /* Get comment */
   comment = silc_argument_get_arg_type(cmd->args, 2, &tmp_len2);
   if (tmp_len2 > 128)
-    comment = NULL;
+    tmp_len2 = 128;
 
   /* Send reply to the sender */
   silc_server_command_send_status_reply(cmd, SILC_COMMAND_KILL,
@@ -2679,22 +2741,24 @@ SILC_SERVER_CMD_FUNC(kill)
   /* Send KILLED notify to the channels. It is not sent to the client
      as it will be sent differently destined directly to the client and not
      to the channel. */
+  killer = silc_id_payload_encode(client->id, SILC_ID_CLIENT);
   silc_server_send_notify_on_channels(server, remote_client, 
                                      remote_client, SILC_NOTIFY_TYPE_KILLED,
-                                     comment ? 2 : 1,
-                                     tmp, tmp_len,
-                                     comment, comment ? tmp_len2 : 0);
+                                     3, tmp, tmp_len,
+                                     comment, comment ? tmp_len2 : 0,
+                                     killer->data, killer->len);
+  silc_buffer_free(killer);
 
   /* Send KILLED notify to primary route */
   if (!server->standalone)
     silc_server_send_notify_killed(server, server->router->connection, TRUE,
-                                  remote_client->id, comment);
+                                  remote_client->id, comment, client->id);
 
   /* Send KILLED notify to the client directly */
   silc_server_send_notify_killed(server, remote_client->connection ? 
                                 remote_client->connection : 
                                 remote_client->router->connection, FALSE,
-                                remote_client->id, comment);
+                                remote_client->id, comment, client->id);
 
   /* Remove the client from all channels. This generates new keys to the
      channels as well. */
@@ -2709,9 +2773,17 @@ SILC_SERVER_CMD_FUNC(kill)
     silc_server_free_client_data(server, sock, remote_client, FALSE, NULL);
     silc_server_close_connection(server, sock);
   } else {
+    /* Update statistics */
+    if (remote_client->connection)
+      server->stat.my_clients--;
+    if (server->server_type == SILC_ROUTER)
+      server->stat.cell_clients--;
+    SILC_OPER_STATS_UPDATE(remote_client, server, SILC_UMODE_SERVER_OPERATOR);
+    SILC_OPER_STATS_UPDATE(remote_client, router, SILC_UMODE_ROUTER_OPERATOR);
+
     /* Remove remote client */
-    if (!silc_idlist_del_client(server->global_list, remote_client))
-      silc_idlist_del_client(server->local_list, remote_client);
+    silc_idlist_del_client(local ? server->local_list :
+                          server->global_list, remote_client);
   }
 
  out:
@@ -2728,9 +2800,9 @@ SILC_SERVER_CMD_FUNC(info)
   SilcServer server = cmd->server;
   SilcBuffer packet, idp;
   unsigned char *tmp;
-  uint32 tmp_len;
+  SilcUInt32 tmp_len;
   char *dest_server, *server_info = NULL, *server_name;
-  uint16 ident = silc_command_get_ident(cmd->payload);
+  SilcUInt16 ident = silc_command_get_ident(cmd->payload);
   SilcServerEntry entry = NULL;
   SilcServerID *server_id = NULL;
 
@@ -2742,7 +2814,7 @@ SILC_SERVER_CMD_FUNC(info)
   /* Get Server ID */
   tmp = silc_argument_get_arg_type(cmd->args, 2, &tmp_len);
   if (tmp) {
-    server_id = silc_id_payload_parse_id(tmp, tmp_len);
+    server_id = silc_id_payload_parse_id(tmp, tmp_len, NULL);
     if (!server_id) {
       silc_server_command_send_status_reply(cmd, SILC_COMMAND_INFO,
                                            SILC_STATUS_ERR_NO_SERVER_ID);
@@ -2779,10 +2851,10 @@ SILC_SERVER_CMD_FUNC(info)
     memset(info_string, 0, sizeof(info_string));
     snprintf(info_string, sizeof(info_string), 
             "location: %s server: %s admin: %s <%s>",
-            server->config->admin_info->location,
-            server->config->admin_info->server_type,
-            server->config->admin_info->admin_name,
-            server->config->admin_info->admin_email);
+            server->config->server_info->location,
+            server->config->server_info->server_type,
+            server->config->server_info->admin,
+            server->config->server_info->email);
 
     server_info = info_string;
     entry = server->id_entry;
@@ -2801,7 +2873,7 @@ SILC_SERVER_CMD_FUNC(info)
        server->server_type != SILC_SERVER && entry && !entry->server_info) {
       /* Send to the server */
       SilcBuffer tmpbuf;
-      uint16 old_ident;
+      SilcUInt16 old_ident;
 
       old_ident = silc_command_get_ident(cmd->payload);
       silc_command_set_ident(cmd->payload, ++server->cmd_ident);
@@ -2814,19 +2886,18 @@ SILC_SERVER_CMD_FUNC(info)
       /* Reprocess this packet after received reply from router */
       silc_server_command_pending(server, SILC_COMMAND_INFO, 
                                  silc_command_get_ident(cmd->payload),
-                                 silc_server_command_destructor,
                                  silc_server_command_info,
                                  silc_server_command_dup(cmd));
       cmd->pending = TRUE;
       silc_command_set_ident(cmd->payload, old_ident);
       silc_buffer_free(tmpbuf);
-      return;
+      goto out;
     }
 
     if (!entry && !cmd->pending && !server->standalone) {
       /* Send to the primary router */
       SilcBuffer tmpbuf;
-      uint16 old_ident;
+      SilcUInt16 old_ident;
 
       old_ident = silc_command_get_ident(cmd->payload);
       silc_command_set_ident(cmd->payload, ++server->cmd_ident);
@@ -2839,13 +2910,12 @@ SILC_SERVER_CMD_FUNC(info)
       /* Reprocess this packet after received reply from router */
       silc_server_command_pending(server, SILC_COMMAND_INFO, 
                                  silc_command_get_ident(cmd->payload),
-                                 silc_server_command_destructor,
                                  silc_server_command_info,
                                  silc_server_command_dup(cmd));
       cmd->pending = TRUE;
       silc_command_set_ident(cmd->payload, old_ident);
       silc_buffer_free(tmpbuf);
-      return;
+      goto out;
     }
   }
 
@@ -2888,7 +2958,7 @@ SILC_SERVER_CMD_FUNC(ping)
   SilcServerCommandContext cmd = (SilcServerCommandContext)context;
   SilcServer server = cmd->server;
   SilcServerID *id;
-  uint32 len;
+  SilcUInt32 len;
   unsigned char *tmp;
 
   SILC_SERVER_COMMAND_CHECK(SILC_COMMAND_INFO, cmd, 1, 2);
@@ -2930,20 +3000,21 @@ static void silc_server_command_join_channel(SilcServer server,
                                             SilcClientID *client_id,
                                             bool created,
                                             bool create_key,
-                                            uint32 umode,
+                                            SilcUInt32 umode,
                                             const unsigned char *auth,
-                                            uint32 auth_len)
+                                            SilcUInt32 auth_len)
 {
   SilcSocketConnection sock = cmd->sock;
   unsigned char *tmp;
-  uint32 tmp_len, user_count;
+  SilcUInt32 tmp_len, user_count;
   unsigned char *passphrase = NULL, mode[4], tmp2[4], tmp3[4];
   SilcClientEntry client;
   SilcChannelClientEntry chl;
   SilcBuffer reply, chidp, clidp, keyp = NULL, user_list, mode_list;
-  uint16 ident = silc_command_get_ident(cmd->payload);
+  SilcUInt16 ident = silc_command_get_ident(cmd->payload);
   char check[512], check2[512];
   bool founder = FALSE;
+  bool resolve;
 
   SILC_LOG_DEBUG(("Start"));
 
@@ -2954,19 +3025,26 @@ 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_server_get_client_resolve(server, client_id);
+    client = silc_server_get_client_resolve(server, client_id, &resolve);
     if (!client) {
       if (cmd->pending)
        goto out;
 
+      if (!resolve) {
+       silc_server_command_send_status_reply(
+                                        cmd, SILC_COMMAND_JOIN,
+                                        SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
+       goto out;
+      }
+
       /* The client info is being resolved. Reprocess this packet after
         receiving the reply to the query. */
       silc_server_command_pending(server, SILC_COMMAND_WHOIS, 
-                                 server->cmd_ident, NULL,
+                                 server->cmd_ident,
                                  silc_server_command_join, 
                                  silc_server_command_dup(cmd));
       cmd->pending = TRUE;
-      return;
+      goto out;
     }
 
     cmd->pending = FALSE;
@@ -2986,8 +3064,9 @@ static void silc_server_command_join_channel(SilcServer server,
       void *auth_data = (channel->founder_method == SILC_AUTH_PASSWORD ?
                         (void *)channel->founder_passwd : 
                         (void *)channel->founder_key);
-      uint32 auth_data_len = (channel->founder_method == SILC_AUTH_PASSWORD ?
-                             channel->founder_passwd_len : 0);
+      SilcUInt32 auth_data_len = 
+       (channel->founder_method == SILC_AUTH_PASSWORD ?
+        channel->founder_passwd_len : 0);
 
       /* Check whether the client is to become founder */
       if (silc_auth_verify_data(auth, auth_len, channel->founder_method, 
@@ -3041,8 +3120,7 @@ static void silc_server_command_join_channel(SilcServer server,
        username and/or hostname is in the ban list the access to the
        channel is denied. */
     if (channel->ban_list) {
-      if (!channel->ban_list ||
-         silc_string_match(channel->ban_list, check) ||
+      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,
@@ -3066,14 +3144,11 @@ static void silc_server_command_join_channel(SilcServer server,
   if (channel->mode & SILC_CHANNEL_MODE_PASSPHRASE) {
     /* Get passphrase */
     tmp = silc_argument_get_arg_type(cmd->args, 3, &tmp_len);
-    if (tmp) {
-      passphrase = silc_calloc(tmp_len, sizeof(*passphrase));
-      memcpy(passphrase, tmp, tmp_len);
-    }
+    if (tmp)
+      passphrase = silc_memdup(tmp, tmp_len);
   
     if (!passphrase || !channel->passphrase ||
-        memcmp(channel->passphrase, passphrase,
-               strlen(channel->passphrase))) {
+        memcmp(passphrase, channel->passphrase, strlen(channel->passphrase))) {
       silc_server_command_send_status_reply(cmd, SILC_COMMAND_JOIN,
                                            SILC_STATUS_ERR_BAD_PASSWORD);
       goto out;
@@ -3085,7 +3160,7 @@ static void silc_server_command_join_channel(SilcServer server,
    */
 
   /* Check whether the client already is on the channel */
-  if (silc_server_client_on_channel(client, channel)) {
+  if (silc_server_client_on_channel(client, channel, NULL)) {
     silc_server_command_send_status_reply(cmd, SILC_COMMAND_JOIN,
                                          SILC_STATUS_ERR_USER_ON_CHANNEL);
     goto out;
@@ -3113,6 +3188,7 @@ static void silc_server_command_join_channel(SilcServer server,
   chl->channel = channel;
   silc_hash_table_add(channel->user_list, client, chl);
   silc_hash_table_add(client->channels, channel, chl);
+  channel->user_count++;
 
   /* Get users on the channel */
   silc_server_get_users_on_channel(server, channel, &user_list, &mode_list,
@@ -3191,24 +3267,24 @@ static void silc_server_command_join_channel(SilcServer server,
       /* Distribute the channel key to all backup routers. */
       silc_server_backup_send(server, NULL, SILC_PACKET_CHANNEL_KEY, 0,
                              keyp->data, keyp->len, FALSE, TRUE);
+  }
 
-    /* If client became founder by providing correct founder auth data
-       notify the mode change to the channel. */
-    if (founder) {
-      SILC_PUT32_MSB(chl->mode, mode);
-      silc_server_send_notify_to_channel(server, NULL, channel, FALSE, 
-                                        SILC_NOTIFY_TYPE_CUMODE_CHANGE, 3,
-                                        clidp->data, clidp->len,
-                                        mode, 4, clidp->data, clidp->len);
+  /* If client became founder by providing correct founder auth data
+     notify the mode change to the channel. */
+  if (founder) {
+    SILC_PUT32_MSB(chl->mode, mode);
+    silc_server_send_notify_to_channel(server, NULL, channel, FALSE, 
+                                      SILC_NOTIFY_TYPE_CUMODE_CHANGE, 3,
+                                      clidp->data, clidp->len,
+                                      mode, 4, clidp->data, clidp->len);
       
-      /* Set CUMODE notify type to network */
-      if (!server->standalone)
-       silc_server_send_notify_cumode(server, server->router->connection,
-                                      server->server_type == SILC_ROUTER ? 
-                                      TRUE : FALSE, channel,
-                                      chl->mode, client->id, SILC_ID_CLIENT,
-                                      client->id);
-    }
+    /* Set CUMODE notify type to network */
+    if (!server->standalone)
+      silc_server_send_notify_cumode(server, server->router->connection,
+                                    server->server_type == SILC_ROUTER ? 
+                                    TRUE : FALSE, channel,
+                                    chl->mode, client->id, SILC_ID_CLIENT,
+                                    client->id);
   }
 
   silc_buffer_free(reply);
@@ -3230,10 +3306,10 @@ SILC_SERVER_CMD_FUNC(join)
   SilcServerCommandContext cmd = (SilcServerCommandContext)context;
   SilcServer server = cmd->server;
   unsigned char *auth;
-  uint32 tmp_len, auth_len;
+  SilcUInt32 tmp_len, auth_len;
   char *tmp, *channel_name = NULL, *cipher, *hmac;
   SilcChannelEntry channel;
-  uint32 umode = 0;
+  SilcUInt32 umode = 0;
   bool created = FALSE, create_key = TRUE;
   SilcClientID *client_id;
 
@@ -3248,10 +3324,10 @@ SILC_SERVER_CMD_FUNC(join)
   }
   channel_name = tmp;
 
-  if (strlen(channel_name) > 256)
+  if (tmp_len > 256)
     channel_name[255] = '\0';
 
-  if (silc_server_command_bad_chars(channel_name) == TRUE) {
+  if (silc_server_name_bad_chars(channel_name, tmp_len) == TRUE) {
     silc_server_command_send_status_reply(cmd, SILC_COMMAND_JOIN,
                                          SILC_STATUS_ERR_BAD_CHANNEL);
     goto out;
@@ -3264,7 +3340,7 @@ SILC_SERVER_CMD_FUNC(join)
                                          SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
     goto out;
   }
-  client_id = silc_id_payload_parse_id(tmp, tmp_len);
+  client_id = silc_id_payload_parse_id(tmp, tmp_len, NULL);
   if (!client_id) {
     silc_server_command_send_status_reply(cmd, SILC_COMMAND_JOIN,
                                          SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
@@ -3281,16 +3357,8 @@ SILC_SERVER_CMD_FUNC(join)
                                             channel_name, NULL);
 
   if (cmd->sock->type == SILC_SOCKET_TYPE_CLIENT) {
-    /* If this is coming from client the Client ID in the command packet must
-       be same as the client's ID. */
-    if (cmd->sock->type == SILC_SOCKET_TYPE_CLIENT) {
-      SilcClientEntry entry = (SilcClientEntry)cmd->sock->user_data;
-      if (!SILC_ID_CLIENT_COMPARE(entry->id, client_id)) {
-       silc_server_command_send_status_reply(cmd, SILC_COMMAND_JOIN,
-                                       SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
-       goto out;
-      }
-    }
+    SilcClientEntry entry = (SilcClientEntry)cmd->sock->user_data;
+    client_id = silc_id_dup(entry->id, SILC_ID_CLIENT);
 
     if (!channel || channel->disabled) {
       /* Channel not found */
@@ -3301,8 +3369,9 @@ SILC_SERVER_CMD_FUNC(join)
        channel = silc_server_create_new_channel(server, server->id, cipher, 
                                                 hmac, channel_name, TRUE);
        if (!channel) {
-         silc_server_command_send_status_reply(cmd, SILC_COMMAND_JOIN,
-                                       SILC_STATUS_ERR_UNKNOWN_ALGORITHM);
+         silc_server_command_send_status_reply(
+                                        cmd, SILC_COMMAND_JOIN,
+                                        SILC_STATUS_ERR_UNKNOWN_ALGORITHM);
          goto out;
        }
        
@@ -3318,7 +3387,7 @@ SILC_SERVER_CMD_FUNC(join)
           or joins the client to it). */
        if (server->server_type != SILC_ROUTER) {
          SilcBuffer tmpbuf;
-         uint16 old_ident;
+         SilcUInt16 old_ident;
 
          /* If this is pending command callback then we've resolved
             it and it didn't work, return since we've notified the
@@ -3339,11 +3408,12 @@ SILC_SERVER_CMD_FUNC(join)
          /* Reprocess this packet after received reply from router */
          silc_server_command_pending(server, SILC_COMMAND_JOIN, 
                                      silc_command_get_ident(cmd->payload),
-                                     silc_server_command_destructor,
                                      silc_server_command_join,
                                      silc_server_command_dup(cmd));
          cmd->pending = TRUE;
-         return;
+          silc_command_set_ident(cmd->payload, old_ident);
+         silc_buffer_free(tmpbuf);
+         goto out;
        }
        
        /* We are router and the channel does not seem exist so we will check
@@ -3402,11 +3472,17 @@ SILC_SERVER_CMD_FUNC(join)
   if (cmd->pending && context2) {
     SilcServerCommandReplyContext reply = 
       (SilcServerCommandReplyContext)context2;
+
     if (silc_command_get(reply->payload) == SILC_COMMAND_JOIN) {
       tmp = silc_argument_get_arg_type(reply->args, 6, NULL);
       SILC_GET32_MSB(created, tmp);
-      create_key = FALSE;      /* Router returned the key already */
+      if (silc_argument_get_arg_type(reply->args, 7, NULL))
+       create_key = FALSE;     /* Router returned the key already */
     }
+
+    if (silc_command_get(reply->payload) == SILC_COMMAND_WHOIS &&
+       !silc_hash_table_count(channel->user_list))
+      created = TRUE;
   }
 
   /* If the channel does not have global users and is also empty the client
@@ -3434,8 +3510,8 @@ SILC_SERVER_CMD_FUNC(motd)
   SilcServer server = cmd->server;
   SilcBuffer packet, idp;
   char *motd, *dest_server;
-  uint32 motd_len;
-  uint16 ident = silc_command_get_ident(cmd->payload);
+  SilcUInt32 motd_len;
+  SilcUInt16 ident = silc_command_get_ident(cmd->payload);
   
   SILC_SERVER_COMMAND_CHECK(SILC_COMMAND_MOTD, cmd, 1, 1);
 
@@ -3452,10 +3528,10 @@ SILC_SERVER_CMD_FUNC(motd)
 
     idp = silc_id_payload_encode(server->id_entry->id, SILC_ID_SERVER);
 
-    if (server->config && server->config->motd && 
-       server->config->motd->motd_file) {
+    if (server->config && server->config->server_info &&
+       server->config->server_info->motd_file) {
       /* Send motd */
-      motd = silc_file_readfile(server->config->motd->motd_file, &motd_len);
+      motd = silc_file_readfile(server->config->server_info->motd_file, &motd_len);
       if (!motd)
        goto out;
       
@@ -3490,7 +3566,7 @@ SILC_SERVER_CMD_FUNC(motd)
        entry && !entry->motd) {
       /* Send to the server */
       SilcBuffer tmpbuf;
-      uint16 old_ident;
+      SilcUInt16 old_ident;
 
       old_ident = silc_command_get_ident(cmd->payload);
       silc_command_set_ident(cmd->payload, ++server->cmd_ident);
@@ -3503,19 +3579,18 @@ SILC_SERVER_CMD_FUNC(motd)
       /* Reprocess this packet after received reply from router */
       silc_server_command_pending(server, SILC_COMMAND_MOTD, 
                                  silc_command_get_ident(cmd->payload),
-                                 silc_server_command_destructor,
                                  silc_server_command_motd,
                                  silc_server_command_dup(cmd));
       cmd->pending = TRUE;
       silc_command_set_ident(cmd->payload, old_ident);
       silc_buffer_free(tmpbuf);
-      return;
+      goto out;
     }
 
     if (!entry && !cmd->pending && !server->standalone) {
       /* Send to the primary router */
       SilcBuffer tmpbuf;
-      uint16 old_ident;
+      SilcUInt16 old_ident;
 
       old_ident = silc_command_get_ident(cmd->payload);
       silc_command_set_ident(cmd->payload, ++server->cmd_ident);
@@ -3528,13 +3603,12 @@ SILC_SERVER_CMD_FUNC(motd)
       /* Reprocess this packet after received reply from router */
       silc_server_command_pending(server, SILC_COMMAND_MOTD, 
                                  silc_command_get_ident(cmd->payload),
-                                 silc_server_command_destructor,
                                  silc_server_command_motd,
                                  silc_server_command_dup(cmd));
       cmd->pending = TRUE;
       silc_command_set_ident(cmd->payload, old_ident);
       silc_buffer_free(tmpbuf);
-      return;
+      goto out;
     }
 
     if (!entry) {
@@ -3571,8 +3645,8 @@ SILC_SERVER_CMD_FUNC(umode)
   SilcClientEntry client = (SilcClientEntry)cmd->sock->user_data;
   SilcBuffer packet;
   unsigned char *tmp_mask;
-  uint32 mask;
-  uint16 ident = silc_command_get_ident(cmd->payload);
+  SilcUInt32 mask;
+  SilcUInt16 ident = silc_command_get_ident(cmd->payload);
 
   if (cmd->sock->type != SILC_SOCKET_TYPE_CLIENT)
     goto out;
@@ -3588,43 +3662,15 @@ SILC_SERVER_CMD_FUNC(umode)
   }
   SILC_GET32_MSB(mask, tmp_mask);
 
-  /* 
-   * Change the mode 
-   */
-
-  if (mask & SILC_UMODE_SERVER_OPERATOR) {
-    if (!(client->mode & SILC_UMODE_SERVER_OPERATOR)) {
-      /* Cannot operator mode */
-      silc_server_command_send_status_reply(cmd, SILC_COMMAND_UMODE,
-                                           SILC_STATUS_ERR_PERM_DENIED);
-      goto out;
-    }
-  } else {
-    if (client->mode & SILC_UMODE_SERVER_OPERATOR)
-      /* Remove the server operator rights */
-      client->mode &= ~SILC_UMODE_SERVER_OPERATOR;
-  }
-
-  if (mask & SILC_UMODE_ROUTER_OPERATOR) {
-    if (!(client->mode & SILC_UMODE_ROUTER_OPERATOR)) {
-      /* Cannot operator mode */
-      silc_server_command_send_status_reply(cmd, SILC_COMMAND_UMODE,
-                                           SILC_STATUS_ERR_PERM_DENIED);
-      goto out;
-    }
-  } else {
-    if (client->mode & SILC_UMODE_ROUTER_OPERATOR)
-      /* Remove the router operator rights */
-      client->mode &= ~SILC_UMODE_ROUTER_OPERATOR;
+  /* Check that mode changing is allowed. */
+  if (!silc_server_check_umode_rights(server, client, mask)) {
+    silc_server_command_send_status_reply(cmd, SILC_COMMAND_UMODE,
+                                         SILC_STATUS_ERR_PERM_DENIED);
+    goto out;
   }
 
-  if (mask & SILC_UMODE_GONE) {
-    client->mode |= SILC_UMODE_GONE;
-  } else {
-    if (client->mode & SILC_UMODE_GONE)
-      /* Remove the gone status */
-      client->mode &= ~SILC_UMODE_GONE;
-  }
+  /* Change the mode */
+  client->mode = mask;
 
   /* Send UMODE change to primary router */
   if (!server->standalone)
@@ -3643,75 +3689,6 @@ SILC_SERVER_CMD_FUNC(umode)
   silc_server_command_free(cmd);
 }
 
-/* Checks that client has rights to add or remove channel modes. If any
-   of the checks fails FALSE is returned. */
-
-int silc_server_check_cmode_rights(SilcChannelEntry channel,
-                                  SilcChannelClientEntry client,
-                                  uint32 mode)
-{
-  int is_op = client->mode & SILC_CHANNEL_UMODE_CHANOP;
-  int is_fo = client->mode & SILC_CHANNEL_UMODE_CHANFO;
-
-  /* Check whether has rights to change anything */
-  if (!is_op && !is_fo)
-    return FALSE;
-
-  /* Check whether has rights to change everything */
-  if (is_op && is_fo)
-    return TRUE;
-
-  /* We know that client is channel operator, check that they are not
-     changing anything that requires channel founder rights. Rest of the
-     modes are available automatically for channel operator. */
-
-  if (mode & SILC_CHANNEL_MODE_PRIVKEY) {
-    if (!(channel->mode & SILC_CHANNEL_MODE_PRIVKEY))
-      if (is_op && !is_fo)
-       return FALSE;
-  } else {
-    if (channel->mode & SILC_CHANNEL_MODE_PRIVKEY) {
-      if (is_op && !is_fo)
-       return FALSE;
-    }
-  }
-  
-  if (mode & SILC_CHANNEL_MODE_PASSPHRASE) {
-    if (!(channel->mode & SILC_CHANNEL_MODE_PASSPHRASE))
-      if (is_op && !is_fo)
-       return FALSE;
-  } else {
-    if (channel->mode & SILC_CHANNEL_MODE_PASSPHRASE) {
-      if (is_op && !is_fo)
-       return FALSE;
-    }
-  }
-
-  if (mode & SILC_CHANNEL_MODE_CIPHER) {
-    if (!(channel->mode & SILC_CHANNEL_MODE_CIPHER))
-      if (is_op && !is_fo)
-       return FALSE;
-  } else {
-    if (channel->mode & SILC_CHANNEL_MODE_CIPHER) {
-      if (is_op && !is_fo)
-       return FALSE;
-    }
-  }
-  
-  if (mode & SILC_CHANNEL_MODE_FOUNDER_AUTH) {
-    if (!(channel->mode & SILC_CHANNEL_MODE_FOUNDER_AUTH))
-      if (is_op && !is_fo)
-       return FALSE;
-  } else {
-    if (channel->mode & SILC_CHANNEL_MODE_FOUNDER_AUTH) {
-      if (is_op && !is_fo)
-       return FALSE;
-    }
-  }
-  
-  return TRUE;
-}
-
 /* Server side command of CMODE. Changes channel mode */
 
 SILC_SERVER_CMD_FUNC(cmode)
@@ -3726,8 +3703,8 @@ SILC_SERVER_CMD_FUNC(cmode)
   SilcBuffer packet, cidp;
   unsigned char *tmp, *tmp_id, *tmp_mask;
   char *cipher = NULL, *hmac = NULL, *passphrase = NULL;
-  uint32 mode_mask, tmp_len, tmp_len2;
-  uint16 ident = silc_command_get_ident(cmd->payload);
+  SilcUInt32 mode_mask, tmp_len, tmp_len2;
+  SilcUInt16 ident = silc_command_get_ident(cmd->payload);
 
   SILC_SERVER_COMMAND_CHECK(SILC_COMMAND_CMODE, cmd, 2, 7);
 
@@ -3738,7 +3715,7 @@ SILC_SERVER_CMD_FUNC(cmode)
                                          SILC_STATUS_ERR_NO_CHANNEL_ID);
     goto out;
   }
-  channel_id = silc_id_payload_parse_id(tmp_id, tmp_len2);
+  channel_id = silc_id_payload_parse_id(tmp_id, tmp_len2, NULL);
   if (!channel_id) {
     silc_server_command_send_status_reply(cmd, SILC_COMMAND_CMODE,
                                          SILC_STATUS_ERR_NO_CHANNEL_ID);
@@ -3768,19 +3745,18 @@ SILC_SERVER_CMD_FUNC(cmode)
   }
 
   /* Check whether this client is on the channel */
-  if (!silc_server_client_on_channel(client, channel)) {
+  if (!silc_server_client_on_channel(client, channel, &chl)) {
     silc_server_command_send_status_reply(cmd, SILC_COMMAND_CMODE,
                                          SILC_STATUS_ERR_NOT_ON_CHANNEL);
     goto out;
   }
 
-  /* Get entry to the channel user list */
-  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)) {
+  if (!silc_server_check_cmode_rights(server, channel, chl, mode_mask)) {
     silc_server_command_send_status_reply(cmd, SILC_COMMAND_CMODE,
-                                         SILC_STATUS_ERR_NO_CHANNEL_PRIV);
+                                         (chl->mode == 0 ? 
+                                          SILC_STATUS_ERR_NO_CHANNEL_PRIV :
+                                          SILC_STATUS_ERR_NO_CHANNEL_FOPRIV));
     goto out;
   }
 
@@ -3798,25 +3774,25 @@ SILC_SERVER_CMD_FUNC(cmode)
       /* The mode is removed and we need to generate and distribute
         new channel key. Clients are not using private channel keys
         anymore after this. */
-
+      
       /* Re-generate channel key */
       if (!silc_server_create_channel_key(server, channel, 0))
        goto out;
-      
+       
       /* Send the channel key. This sends it to our local clients and if
         we are normal server to our router as well. */
       silc_server_send_channel_key(server, NULL, channel, 
                                   server->server_type == SILC_ROUTER ? 
                                   FALSE : !server->standalone);
-
+       
       cipher = channel->channel_key->cipher->name;
       hmac = (char *)silc_hmac_get_name(channel->hmac);
     }
   }
-  
+
   if (mode_mask & SILC_CHANNEL_MODE_ULIMIT) {
     /* User limit is set on channel */
-    uint32 user_limit;
+    SilcUInt32 user_limit;
       
     /* Get user limit */
     tmp = silc_argument_get_arg_type(cmd->args, 3, NULL);
@@ -4026,9 +4002,7 @@ SILC_SERVER_CMD_FUNC(cmode)
 
        if (channel->founder_method == SILC_AUTH_PASSWORD) {
          tmp = silc_auth_get_data(auth, &tmp_len);
-         channel->founder_passwd = 
-           silc_calloc(tmp_len + 1, sizeof(*channel->founder_passwd));
-         memcpy(channel->founder_passwd, tmp, tmp_len);
+         channel->founder_passwd = silc_memdup(tmp, tmp_len);
          channel->founder_passwd_len = tmp_len;
        } else {
          /* Verify the payload before setting the mode */
@@ -4110,9 +4084,9 @@ SILC_SERVER_CMD_FUNC(cumode)
   SilcChannelClientEntry chl;
   SilcBuffer packet, idp;
   unsigned char *tmp_id, *tmp_ch_id, *tmp_mask;
-  uint32 target_mask, sender_mask = 0, tmp_len, tmp_ch_len;
+  SilcUInt32 target_mask, sender_mask = 0, tmp_len, tmp_ch_len;
   int notify = FALSE;
-  uint16 ident = silc_command_get_ident(cmd->payload);
+  SilcUInt16 ident = silc_command_get_ident(cmd->payload);
 
   SILC_SERVER_COMMAND_CHECK(SILC_COMMAND_CUMODE, cmd, 3, 4);
 
@@ -4123,7 +4097,7 @@ SILC_SERVER_CMD_FUNC(cumode)
                                          SILC_STATUS_ERR_NO_CHANNEL_ID);
     goto out;
   }
-  channel_id = silc_id_payload_parse_id(tmp_ch_id, tmp_ch_len);
+  channel_id = silc_id_payload_parse_id(tmp_ch_id, tmp_ch_len, NULL);
   if (!channel_id) {
     silc_server_command_send_status_reply(cmd, SILC_COMMAND_CUMODE,
                                          SILC_STATUS_ERR_NO_CHANNEL_ID);
@@ -4144,14 +4118,11 @@ SILC_SERVER_CMD_FUNC(cumode)
   }
 
   /* Check whether sender is on the channel */
-  if (!silc_server_client_on_channel(client, channel)) {
+  if (!silc_server_client_on_channel(client, channel, &chl)) {
     silc_server_command_send_status_reply(cmd, SILC_COMMAND_CUMODE,
                                          SILC_STATUS_ERR_NOT_ON_CHANNEL);
     goto out;
   }
-
-  /* Check that client has rights to change other's rights */
-  silc_hash_table_find(channel->user_list, client, NULL, (void *)&chl);
   sender_mask = chl->mode;
   
   /* Get the target client's channel mode mask */
@@ -4170,7 +4141,7 @@ SILC_SERVER_CMD_FUNC(cumode)
                                          SILC_STATUS_ERR_NO_CLIENT_ID);
     goto out;
   }
-  client_id = silc_id_payload_parse_id(tmp_id, tmp_len);
+  client_id = silc_id_payload_parse_id(tmp_id, tmp_len, NULL);
   if (!client_id) {
     silc_server_command_send_status_reply(cmd, SILC_COMMAND_CUMODE,
                                          SILC_STATUS_ERR_NO_CLIENT_ID);
@@ -4195,15 +4166,11 @@ SILC_SERVER_CMD_FUNC(cumode)
 
   /* Check whether target client is on the channel */
   if (target_client != client) {
-    if (!silc_server_client_on_channel(target_client, channel)) {
+    if (!silc_server_client_on_channel(target_client, channel, &chl)) {
       silc_server_command_send_status_reply(cmd, SILC_COMMAND_CUMODE,
                                 SILC_STATUS_ERR_USER_NOT_ON_CHANNEL);
       goto out;
     }
-
-    /* Get entry to the channel user list */
-    silc_hash_table_find(channel->user_list, target_client, NULL, 
-                        (void *)&chl);
   }
 
   /* 
@@ -4212,9 +4179,9 @@ SILC_SERVER_CMD_FUNC(cumode)
 
   /* If the target client is founder, no one else can change their mode
      but themselves. */
-  if (chl->mode & SILC_CHANNEL_UMODE_CHANFO && chl->client != target_client) {
+  if (chl->mode & SILC_CHANNEL_UMODE_CHANFO && client != target_client) {
     silc_server_command_send_status_reply(cmd, SILC_COMMAND_CUMODE,
-                                         SILC_STATUS_ERR_NOT_YOU);
+                                         SILC_STATUS_ERR_NO_CHANNEL_FOPRIV);
     goto out;
   }
 
@@ -4222,7 +4189,7 @@ SILC_SERVER_CMD_FUNC(cumode)
     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;
+      SilcUInt32 tmp_auth_len, auth_len;
       void *auth;
       
       if (target_client != client) {
@@ -4357,7 +4324,7 @@ SILC_SERVER_CMD_FUNC(kick)
   SilcChannelEntry channel;
   SilcChannelClientEntry chl;
   SilcBuffer idp;
-  uint32 tmp_len, target_idp_len;
+  SilcUInt32 tmp_len, target_idp_len;
   unsigned char *tmp, *comment, *target_idp;
 
   SILC_SERVER_COMMAND_CHECK(SILC_COMMAND_LEAVE, cmd, 1, 3);
@@ -4369,7 +4336,7 @@ SILC_SERVER_CMD_FUNC(kick)
                                          SILC_STATUS_ERR_NO_CHANNEL_ID);
     goto out;
   }
-  channel_id = silc_id_payload_parse_id(tmp, tmp_len);
+  channel_id = silc_id_payload_parse_id(tmp, tmp_len, NULL);
   if (!channel_id) {
     silc_server_command_send_status_reply(cmd, SILC_COMMAND_KICK,
                                          SILC_STATUS_ERR_NO_CHANNEL_ID);
@@ -4390,14 +4357,13 @@ SILC_SERVER_CMD_FUNC(kick)
   }
 
   /* Check whether sender is on the channel */
-  if (!silc_server_client_on_channel(client, channel)) {
+  if (!silc_server_client_on_channel(client, channel, &chl)) {
     silc_server_command_send_status_reply(cmd, SILC_COMMAND_KICK,
                                          SILC_STATUS_ERR_NOT_ON_CHANNEL);
     goto out;
   }
 
   /* Check that the kicker is channel operator or channel founder */
-  silc_hash_table_find(channel->user_list, client, NULL, (void *)&chl);
   if (chl->mode == SILC_CHANNEL_UMODE_NONE) {
     silc_server_command_send_status_reply(cmd, SILC_COMMAND_KICK,
                                          SILC_STATUS_ERR_NO_CHANNEL_PRIV);
@@ -4411,7 +4377,7 @@ SILC_SERVER_CMD_FUNC(kick)
                                          SILC_STATUS_ERR_NO_CLIENT_ID);
     goto out;
   }
-  client_id = silc_id_payload_parse_id(target_idp, target_idp_len);
+  client_id = silc_id_payload_parse_id(target_idp, target_idp_len, NULL);
   if (!client_id) {
     silc_server_command_send_status_reply(cmd, SILC_COMMAND_KICK,
                                          SILC_STATUS_ERR_NO_CLIENT_ID);
@@ -4426,22 +4392,21 @@ SILC_SERVER_CMD_FUNC(kick)
                                                  client_id, TRUE, NULL);
   }
 
+  /* Check whether target client is on the channel */
+  if (!silc_server_client_on_channel(target_client, channel, &chl)) {
+    silc_server_command_send_status_reply(cmd, SILC_COMMAND_KICK,
+                                         SILC_STATUS_ERR_USER_NOT_ON_CHANNEL);
+    goto out;
+  }
+
   /* Check that the target client is not channel founder. Channel founder
      cannot be kicked from the channel. */
-  silc_hash_table_find(channel->user_list, target_client, NULL, (void *)&chl);
   if (chl->mode & SILC_CHANNEL_UMODE_CHANFO) {
     silc_server_command_send_status_reply(cmd, SILC_COMMAND_KICK,
                                          SILC_STATUS_ERR_NO_CHANNEL_FOPRIV);
     goto out;
   }
   
-  /* Check whether target client is on the channel */
-  if (!silc_server_client_on_channel(target_client, channel)) {
-    silc_server_command_send_status_reply(cmd, SILC_COMMAND_KICK,
-                                         SILC_STATUS_ERR_USER_NOT_ON_CHANNEL);
-    goto out;
-  }
-
   /* Get comment */
   tmp_len = 0;
   comment = silc_argument_get_arg_type(cmd->args, 3, &tmp_len);
@@ -4473,7 +4438,7 @@ SILC_SERVER_CMD_FUNC(kick)
     silc_server_send_notify_kicked(server, server->router->connection,
                                   server->server_type == SILC_ROUTER ?
                                   TRUE : FALSE, channel,
-                                  target_client->id, comment);
+                                  target_client->id, client->id, comment);
 
   if (!(channel->mode & SILC_CHANNEL_MODE_PRIVKEY)) {
     /* Re-generate channel key */
@@ -4500,9 +4465,11 @@ SILC_SERVER_CMD_FUNC(oper)
   SilcServer server = cmd->server;
   SilcClientEntry client = (SilcClientEntry)cmd->sock->user_data;
   unsigned char *username, *auth;
-  uint32 tmp_len;
-  SilcServerConfigSectionAdminConnection *admin;
+  SilcUInt32 tmp_len;
+  SilcServerConfigAdmin *admin;
   SilcIDListData idata = (SilcIDListData)client;
+  bool result = FALSE;
+  SilcPublicKey cached_key;
 
   SILC_SERVER_COMMAND_CHECK(SILC_COMMAND_OPER, cmd, 1, 2);
 
@@ -4518,10 +4485,10 @@ SILC_SERVER_CMD_FUNC(oper)
   }
 
   /* Get the admin configuration */
-  admin = silc_server_config_find_admin(server->config, cmd->sock->ip,
+  admin = silc_server_config_find_admin(server, cmd->sock->ip,
                                        username, client->nickname);
   if (!admin) {
-    admin = silc_server_config_find_admin(server->config, cmd->sock->hostname,
+    admin = silc_server_config_find_admin(server, cmd->sock->hostname,
                                          username, client->nickname);
     if (!admin) {
       silc_server_command_send_status_reply(cmd, SILC_COMMAND_OPER,
@@ -4538,10 +4505,22 @@ SILC_SERVER_CMD_FUNC(oper)
     goto out;
   }
 
-  /* Verify the authentication data */
-  if (!silc_auth_verify_data(auth, tmp_len, admin->auth_meth, 
-                            admin->auth_data, admin->auth_data_len,
-                            idata->hash, client->id, SILC_ID_CLIENT)) {
+  /* Verify the authentication data. If both passphrase and public key
+     is set then try both of them. */
+  if (admin->passphrase)
+    result = silc_auth_verify_data(auth, tmp_len, SILC_AUTH_PASSWORD,
+                                  admin->passphrase, admin->passphrase_len,
+                                  idata->hash, client->id, SILC_ID_CLIENT);
+  if (!result && admin->publickeys) {
+    cached_key = silc_server_get_public_key(server, admin->publickeys);
+    if (!cached_key)
+      goto out;
+    result = silc_auth_verify_data(auth, tmp_len, SILC_AUTH_PUBLIC_KEY,
+                                  cached_key, 0, idata->hash, 
+                                  client->id, SILC_ID_CLIENT);
+  }
+  if (!result) {
+    /* Authentication failed */
     silc_server_command_send_status_reply(cmd, SILC_COMMAND_OPER,
                                          SILC_STATUS_ERR_AUTH_FAILED);
     goto out;
@@ -4550,6 +4529,12 @@ SILC_SERVER_CMD_FUNC(oper)
   /* Client is now server operator */
   client->mode |= SILC_UMODE_SERVER_OPERATOR;
 
+  /* Update statistics */
+  if (client->connection)
+    server->stat.my_server_ops++;
+  if (server->server_type == SILC_ROUTER)
+    server->stat.server_ops++;
+
   /* Send UMODE change to primary router */
   if (!server->standalone)
     silc_server_send_notify_umode(server, server->router->connection, TRUE,
@@ -4572,9 +4557,11 @@ SILC_SERVER_CMD_FUNC(silcoper)
   SilcServer server = cmd->server;
   SilcClientEntry client = (SilcClientEntry)cmd->sock->user_data;
   unsigned char *username, *auth;
-  uint32 tmp_len;
-  SilcServerConfigSectionAdminConnection *admin;
+  SilcUInt32 tmp_len;
+  SilcServerConfigAdmin *admin;
   SilcIDListData idata = (SilcIDListData)client;
+  bool result = FALSE;
+  SilcPublicKey cached_key;
 
   SILC_SERVER_COMMAND_CHECK(SILC_COMMAND_SILCOPER, cmd, 1, 2);
 
@@ -4596,10 +4583,10 @@ SILC_SERVER_CMD_FUNC(silcoper)
   }
 
   /* Get the admin configuration */
-  admin = silc_server_config_find_admin(server->config, cmd->sock->ip,
+  admin = silc_server_config_find_admin(server, cmd->sock->ip,
                                        username, client->nickname);
   if (!admin) {
-    admin = silc_server_config_find_admin(server->config, cmd->sock->hostname,
+    admin = silc_server_config_find_admin(server, cmd->sock->hostname,
                                          username, client->nickname);
     if (!admin) {
       silc_server_command_send_status_reply(cmd, SILC_COMMAND_SILCOPER,
@@ -4616,11 +4603,23 @@ SILC_SERVER_CMD_FUNC(silcoper)
     goto out;
   }
 
-  /* Verify the authentication data */
-  if (!silc_auth_verify_data(auth, tmp_len, admin->auth_meth, 
-                            admin->auth_data, admin->auth_data_len,
-                            idata->hash, client->id, SILC_ID_CLIENT)) {
-    silc_server_command_send_status_reply(cmd, SILC_COMMAND_SILCOPER,
+  /* Verify the authentication data. If both passphrase and public key
+     is set then try both of them. */
+  if (admin->passphrase)
+    result = silc_auth_verify_data(auth, tmp_len, SILC_AUTH_PASSWORD,
+                                  admin->passphrase, admin->passphrase_len,
+                                  idata->hash, client->id, SILC_ID_CLIENT);
+  if (!result && admin->publickeys) {
+    cached_key = silc_server_get_public_key(server, admin->publickeys);
+    if (!cached_key)
+      goto out;
+    result = silc_auth_verify_data(auth, tmp_len, SILC_AUTH_PUBLIC_KEY,
+                                  cached_key, 0, idata->hash, 
+                                  client->id, SILC_ID_CLIENT);
+  }
+  if (!result) {
+    /* Authentication failed */
+    silc_server_command_send_status_reply(cmd, SILC_COMMAND_OPER,
                                          SILC_STATUS_ERR_AUTH_FAILED);
     goto out;
   }
@@ -4628,6 +4627,12 @@ SILC_SERVER_CMD_FUNC(silcoper)
   /* Client is now router operator */
   client->mode |= SILC_UMODE_ROUTER_OPERATOR;
 
+  /* Update statistics */
+  if (client->connection)
+    server->stat.my_router_ops++;
+  if (server->server_type == SILC_ROUTER)
+    server->stat.router_ops++;
+
   /* Send UMODE change to primary router */
   if (!server->standalone)
     silc_server_send_notify_umode(server, server->router->connection, TRUE,
@@ -4641,61 +4646,6 @@ SILC_SERVER_CMD_FUNC(silcoper)
   silc_server_command_free(cmd);
 }
 
-/* Server side command of CONNECT. Connects us to the specified remote
-   server or router. */
-
-SILC_SERVER_CMD_FUNC(connect)
-{
-  SilcServerCommandContext cmd = (SilcServerCommandContext)context;
-  SilcServer server = cmd->server;
-  SilcClientEntry client = (SilcClientEntry)cmd->sock->user_data;
-  unsigned char *tmp, *host;
-  uint32 tmp_len;
-  uint32 port = SILC_PORT;
-
-  SILC_SERVER_COMMAND_CHECK(SILC_COMMAND_CONNECT, cmd, 1, 2);
-
-  if (!client || cmd->sock->type != SILC_SOCKET_TYPE_CLIENT)
-    goto out;
-
-  /* Check whether client has the permissions. */
-  if (client->mode == SILC_UMODE_NONE) {
-    silc_server_command_send_status_reply(cmd, SILC_COMMAND_CONNECT,
-                                         SILC_STATUS_ERR_NO_SERVER_PRIV);
-    goto out;
-  }
-
-  if (server->server_type == SILC_ROUTER && 
-      client->mode & SILC_UMODE_SERVER_OPERATOR) {
-    silc_server_command_send_status_reply(cmd, SILC_COMMAND_CONNECT,
-                                         SILC_STATUS_ERR_NO_ROUTER_PRIV);
-    goto out;
-  }
-
-  /* Get the remote server */
-  host = silc_argument_get_arg_type(cmd->args, 1, &tmp_len);
-  if (!host) {
-    silc_server_command_send_status_reply(cmd, SILC_COMMAND_CONNECT,
-                                         SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
-    goto out;
-  }
-
-  /* Get port */
-  tmp = silc_argument_get_arg_type(cmd->args, 2, &tmp_len);
-  if (tmp)
-    SILC_GET32_MSB(port, tmp);
-
-  /* Create the connection. It is done with timeout and is async. */
-  silc_server_create_connection(server, host, port);
-
-  /* Send reply to the sender */
-  silc_server_command_send_status_reply(cmd, SILC_COMMAND_CONNECT,
-                                       SILC_STATUS_OK);
-
- out:
-  silc_server_command_free(cmd);
-}
-
 /* Server side of command BAN. This is used to manage the ban list of the
    channel. To add clients and remove clients from the ban list. */
 
@@ -4709,8 +4659,8 @@ SILC_SERVER_CMD_FUNC(ban)
   SilcChannelClientEntry chl;
   SilcChannelID *channel_id = NULL;
   unsigned char *id, *add, *del;
-  uint32 id_len, tmp_len;
-  uint16 ident = silc_command_get_ident(cmd->payload);
+  SilcUInt32 id_len, tmp_len;
+  SilcUInt16 ident = silc_command_get_ident(cmd->payload);
 
   if (cmd->sock->type != SILC_SOCKET_TYPE_CLIENT)
     goto out;
@@ -4720,7 +4670,7 @@ SILC_SERVER_CMD_FUNC(ban)
   /* Get Channel ID */
   id = silc_argument_get_arg_type(cmd->args, 1, &id_len);
   if (id) {
-    channel_id = silc_id_payload_parse_id(id, id_len);
+    channel_id = silc_id_payload_parse_id(id, id_len, NULL);
     if (!channel_id) {
       silc_server_command_send_status_reply(cmd, SILC_COMMAND_BAN,
                                            SILC_STATUS_ERR_NO_CHANNEL_ID);
@@ -4743,15 +4693,12 @@ SILC_SERVER_CMD_FUNC(ban)
   }
 
   /* Check whether this client is on the channel */
-  if (!silc_server_client_on_channel(client, channel)) {
+  if (!silc_server_client_on_channel(client, channel, &chl)) {
     silc_server_command_send_status_reply(cmd, SILC_COMMAND_BAN,
                                          SILC_STATUS_ERR_NOT_ON_CHANNEL);
     goto out;
   }
 
-  /* Get entry to the channel user list */
-  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)) {
     silc_server_command_send_status_reply(cmd, SILC_COMMAND_BAN,
@@ -4811,7 +4758,7 @@ SILC_SERVER_CMD_FUNC(ban)
                                         2, id, id_len,
                                         3, channel->ban_list, 
                                         channel->ban_list ? 
-                                        strlen(channel->ban_list) - 1 : 0);
+                                        strlen(channel->ban_list) -1 : 0);
   silc_server_packet_send(server, cmd->sock, SILC_PACKET_COMMAND_REPLY, 0, 
                          packet->data, packet->len, FALSE);
     
@@ -4822,111 +4769,6 @@ SILC_SERVER_CMD_FUNC(ban)
   silc_server_command_free(cmd);
 }
 
-/* Server side command of CLOSE. Closes connection to a specified server. */
-SILC_SERVER_CMD_FUNC(close)
-{
-  SilcServerCommandContext cmd = (SilcServerCommandContext)context;
-  SilcServer server = cmd->server;
-  SilcClientEntry client = (SilcClientEntry)cmd->sock->user_data;
-  SilcServerEntry server_entry;
-  SilcSocketConnection sock;
-  unsigned char *tmp;
-  uint32 tmp_len;
-  unsigned char *name;
-  uint32 port = SILC_PORT;
-
-  SILC_SERVER_COMMAND_CHECK(SILC_COMMAND_CLOSE, cmd, 1, 2);
-
-  if (!client || cmd->sock->type != SILC_SOCKET_TYPE_CLIENT)
-    goto out;
-
-  /* Check whether client has the permissions. */
-  if (client->mode == SILC_UMODE_NONE) {
-    silc_server_command_send_status_reply(cmd, SILC_COMMAND_CLOSE,
-                                         SILC_STATUS_ERR_NO_SERVER_PRIV);
-    goto out;
-  }
-
-  /* Get the remote server */
-  name = silc_argument_get_arg_type(cmd->args, 1, &tmp_len);
-  if (!name) {
-    silc_server_command_send_status_reply(cmd, SILC_COMMAND_CLOSE,
-                                         SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
-    goto out;
-  }
-
-  /* Get port */
-  tmp = silc_argument_get_arg_type(cmd->args, 2, &tmp_len);
-  if (tmp)
-    SILC_GET32_MSB(port, tmp);
-
-  server_entry = silc_idlist_find_server_by_conn(server->local_list,
-                                                name, port, FALSE, NULL);
-  if (!server_entry)
-    server_entry = silc_idlist_find_server_by_conn(server->global_list,
-                                                  name, port, FALSE, NULL);
-  if (!server_entry) {
-    silc_server_command_send_status_reply(cmd, SILC_COMMAND_CLOSE,
-                                         SILC_STATUS_ERR_NO_SERVER_ID);
-    goto out;
-  }
-
-  /* Send reply to the sender */
-  silc_server_command_send_status_reply(cmd, SILC_COMMAND_CLOSE,
-                                       SILC_STATUS_OK);
-
-  /* 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);
-  
- out:
-  silc_server_command_free(cmd);
-}
-
-/* Server side command of SHUTDOWN. Shutdowns the server and closes all
-   active connections. */
-SILC_SERVER_CMD_FUNC(shutdown)
-{
-  SilcServerCommandContext cmd = (SilcServerCommandContext)context;
-  SilcServer server = cmd->server;
-  SilcClientEntry client = (SilcClientEntry)cmd->sock->user_data;
-
-  SILC_SERVER_COMMAND_CHECK(SILC_COMMAND_SHUTDOWN, cmd, 0, 0);
-
-  if (!client || cmd->sock->type != SILC_SOCKET_TYPE_CLIENT)
-    goto out;
-
-  /* Check whether client has the permission. */
-  if (client->mode == SILC_UMODE_NONE) {
-    silc_server_command_send_status_reply(cmd, SILC_COMMAND_SHUTDOWN,
-                                         SILC_STATUS_ERR_NO_SERVER_PRIV);
-    goto out;
-  }
-
-  /* Send reply to the sender */
-  silc_server_command_send_status_reply(cmd, SILC_COMMAND_SHUTDOWN,
-                                       SILC_STATUS_OK);
-
-  /* Then, gracefully, or not, bring the server down. */
-  silc_server_stop(server);
-  exit(0);
-
- out:
-  silc_server_command_free(cmd);
-}
 /* Server side command of LEAVE. Removes client from a channel. */
 
 SILC_SERVER_CMD_FUNC(leave)
@@ -4937,7 +4779,7 @@ SILC_SERVER_CMD_FUNC(leave)
   SilcClientEntry id_entry = (SilcClientEntry)cmd->sock->user_data;
   SilcChannelID *id = NULL;
   SilcChannelEntry channel;
-  uint32 len;
+  SilcUInt32 len;
   unsigned char *tmp;
 
   SILC_SERVER_COMMAND_CHECK(SILC_COMMAND_LEAVE, cmd, 1, 2);
@@ -4949,7 +4791,7 @@ SILC_SERVER_CMD_FUNC(leave)
                                          SILC_STATUS_ERR_NO_CHANNEL_ID);
     goto out;
   }
-  id = silc_id_payload_parse_id(tmp, len);
+  id = silc_id_payload_parse_id(tmp, len, NULL);
   if (!id) {
     silc_server_command_send_status_reply(cmd, SILC_COMMAND_LEAVE,
                                          SILC_STATUS_ERR_NO_CHANNEL_ID);
@@ -4968,7 +4810,7 @@ SILC_SERVER_CMD_FUNC(leave)
   }
 
   /* Check whether this client is on the channel */
-  if (!silc_server_client_on_channel(id_entry, channel)) {
+  if (!silc_server_client_on_channel(id_entry, channel, NULL)) {
     silc_server_command_send_status_reply(cmd, SILC_COMMAND_LEAVE,
                                          SILC_STATUS_ERR_NOT_ON_CHANNEL);
     goto out;
@@ -4981,8 +4823,8 @@ SILC_SERVER_CMD_FUNC(leave)
                                  server->server_type == SILC_ROUTER ?
                                  TRUE : FALSE, channel, id_entry->id);
 
-  silc_server_command_send_status_reply(cmd, SILC_COMMAND_LEAVE,
-                                       SILC_STATUS_OK);
+  silc_server_command_send_status_data(cmd, SILC_COMMAND_LEAVE,
+                                      SILC_STATUS_OK, 2, tmp, len);
 
   /* Remove client from channel */
   if (!silc_server_remove_from_one_channel(server, sock, channel, id_entry,
@@ -5018,12 +4860,12 @@ SILC_SERVER_CMD_FUNC(users)
   SilcChannelID *id = NULL;
   SilcBuffer packet, idp;
   unsigned char *channel_id;
-  uint32 channel_id_len;
+  SilcUInt32 channel_id_len;
   SilcBuffer client_id_list;
   SilcBuffer client_mode_list;
   unsigned char lc[4];
-  uint32 list_count = 0;
-  uint16 ident = silc_command_get_ident(cmd->payload);
+  SilcUInt32 list_count = 0;
+  SilcUInt16 ident = silc_command_get_ident(cmd->payload);
   char *channel_name;
 
   SILC_SERVER_COMMAND_CHECK(SILC_COMMAND_USERS, cmd, 1, 2);
@@ -5041,7 +4883,7 @@ SILC_SERVER_CMD_FUNC(users)
   }
 
   if (channel_id) {
-    id = silc_id_payload_parse_id(channel_id, channel_id_len);
+    id = silc_id_payload_parse_id(channel_id, channel_id_len, NULL);
     if (!id) {
       silc_server_command_send_status_reply(cmd, SILC_COMMAND_USERS,
                                            SILC_STATUS_ERR_NO_CHANNEL_ID);
@@ -5074,15 +4916,13 @@ SILC_SERVER_CMD_FUNC(users)
       /* Reprocess this packet after received reply */
       silc_server_command_pending(server, SILC_COMMAND_USERS, 
                                  silc_command_get_ident(cmd->payload),
-                                 silc_server_command_destructor,
                                  silc_server_command_users,
                                  silc_server_command_dup(cmd));
       cmd->pending = TRUE;
       silc_command_set_ident(cmd->payload, ident);
-      
       silc_buffer_free(tmpbuf);
       silc_free(id);
-      return;
+      goto out;
     }
 
     /* Check the global list as well. */
@@ -5103,7 +4943,8 @@ SILC_SERVER_CMD_FUNC(users)
      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_client_on_channel(cmd->sock->user_data, channel, 
+                                         NULL)) {
       silc_server_command_send_status_reply(cmd, SILC_COMMAND_USERS,
                                            SILC_STATUS_ERR_NO_SUCH_CHANNEL);
       goto out;
@@ -5160,11 +5001,12 @@ SILC_SERVER_CMD_FUNC(getkey)
   SilcClientID *client_id = NULL;
   SilcServerID *server_id = NULL;
   SilcIDPayload idp = NULL;
-  uint16 ident = silc_command_get_ident(cmd->payload);
+  SilcUInt16 ident = silc_command_get_ident(cmd->payload);
   unsigned char *tmp, *pkdata;
-  uint32 tmp_len, pklen;
+  SilcUInt32 tmp_len, pklen;
   SilcBuffer pk = NULL;
   SilcIdType id_type;
+  SilcPublicKey public_key;
 
   SILC_LOG_DEBUG(("Start"));
 
@@ -5197,7 +5039,7 @@ SILC_SERVER_CMD_FUNC(getkey)
        (client && !client->connection && !cmd->pending) ||
        (client && !client->data.public_key && !cmd->pending)) {
       SilcBuffer tmpbuf;
-      uint16 old_ident;
+      SilcUInt16 old_ident;
       SilcSocketConnection dest_sock;
       
       dest_sock = silc_server_get_client_route(server, NULL, 0, 
@@ -5216,14 +5058,12 @@ SILC_SERVER_CMD_FUNC(getkey)
       /* Reprocess this packet after received reply from router */
       silc_server_command_pending(server, SILC_COMMAND_GETKEY, 
                                  silc_command_get_ident(cmd->payload),
-                                 silc_server_command_destructor,
                                  silc_server_command_getkey,
                                  silc_server_command_dup(cmd));
       cmd->pending = TRUE;
-      
       silc_command_set_ident(cmd->payload, old_ident);
       silc_buffer_free(tmpbuf);
-      return;
+      goto out;
     }
 
     if (!client) {
@@ -5235,11 +5075,12 @@ SILC_SERVER_CMD_FUNC(getkey)
     /* The client is locally connected, just get the public key and
        send it back. If they key does not exist then do not send it, 
        send just OK reply */
-    if (!client->data.public_key) {
+    public_key = client->data.public_key;
+    if (!public_key) {
       pkdata = NULL;
       pklen = 0;
     } else {
-      tmp = silc_pkcs_public_key_encode(client->data.public_key, &tmp_len);
+      tmp = silc_pkcs_public_key_encode(public_key, &tmp_len);
       pk = silc_buffer_alloc(4 + tmp_len);
       silc_buffer_pull_tail(pk, SILC_BUFFER_END(pk));
       silc_buffer_format(pk,
@@ -5269,7 +5110,7 @@ SILC_SERVER_CMD_FUNC(getkey)
         (server_entry && !server_entry->data.public_key && !cmd->pending &&
          !server->standalone))) {
       SilcBuffer tmpbuf;
-      uint16 old_ident;
+      SilcUInt16 old_ident;
       
       old_ident = silc_command_get_ident(cmd->payload);
       silc_command_set_ident(cmd->payload, ++server->cmd_ident);
@@ -5282,14 +5123,12 @@ SILC_SERVER_CMD_FUNC(getkey)
       /* Reprocess this packet after received reply from router */
       silc_server_command_pending(server, SILC_COMMAND_GETKEY, 
                                  silc_command_get_ident(cmd->payload),
-                                 silc_server_command_destructor,
                                  silc_server_command_getkey,
                                  silc_server_command_dup(cmd));
       cmd->pending = TRUE;
-      
       silc_command_set_ident(cmd->payload, old_ident);
       silc_buffer_free(tmpbuf);
-      return;
+      goto out;
     }
 
     if (!server_entry) {
@@ -5299,12 +5138,14 @@ SILC_SERVER_CMD_FUNC(getkey)
     }
 
     /* If they key does not exist then do not send it, send just OK reply */
-    if (!server_entry->data.public_key) {
+    public_key = (!server_entry->data.public_key ? 
+                 (server_entry == server->id_entry ? server->public_key :
+                  NULL) : server_entry->data.public_key);
+    if (!public_key) {
       pkdata = NULL;
       pklen = 0;
     } else {
-      tmp = silc_pkcs_public_key_encode(server_entry->data.public_key, 
-                                       &tmp_len);
+      tmp = silc_pkcs_public_key_encode(public_key, &tmp_len);
       pk = silc_buffer_alloc(4 + tmp_len);
       silc_buffer_pull_tail(pk, SILC_BUFFER_END(pk));
       silc_buffer_format(pk,
@@ -5340,3 +5181,166 @@ SILC_SERVER_CMD_FUNC(getkey)
   silc_free(server_id);
   silc_server_command_free(cmd);
 }
+
+
+/* Private range commands, specific to this implementation */
+
+/* Server side command of CONNECT. Connects us to the specified remote
+   server or router. */
+
+SILC_SERVER_CMD_FUNC(connect)
+{
+  SilcServerCommandContext cmd = (SilcServerCommandContext)context;
+  SilcServer server = cmd->server;
+  SilcClientEntry client = (SilcClientEntry)cmd->sock->user_data;
+  unsigned char *tmp, *host;
+  SilcUInt32 tmp_len;
+  SilcUInt32 port = SILC_PORT;
+
+  SILC_SERVER_COMMAND_CHECK(SILC_COMMAND_PRIV_CONNECT, cmd, 1, 2);
+
+  if (!client || cmd->sock->type != SILC_SOCKET_TYPE_CLIENT)
+    goto out;
+
+  /* Check whether client has the permissions. */
+  if (client->mode == SILC_UMODE_NONE) {
+    silc_server_command_send_status_reply(cmd, SILC_COMMAND_PRIV_CONNECT,
+                                         SILC_STATUS_ERR_NO_SERVER_PRIV);
+    goto out;
+  }
+
+  if (server->server_type == SILC_ROUTER && 
+      client->mode & SILC_UMODE_SERVER_OPERATOR) {
+    silc_server_command_send_status_reply(cmd, SILC_COMMAND_PRIV_CONNECT,
+                                         SILC_STATUS_ERR_NO_ROUTER_PRIV);
+    goto out;
+  }
+
+  /* Get the remote server */
+  host = silc_argument_get_arg_type(cmd->args, 1, &tmp_len);
+  if (!host) {
+    silc_server_command_send_status_reply(cmd, SILC_COMMAND_PRIV_CONNECT,
+                                         SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
+    goto out;
+  }
+
+  /* Get port */
+  tmp = silc_argument_get_arg_type(cmd->args, 2, &tmp_len);
+  if (tmp)
+    SILC_GET32_MSB(port, tmp);
+
+  /* Create the connection. It is done with timeout and is async. */
+  silc_server_create_connection(server, host, port);
+
+  /* Send reply to the sender */
+  silc_server_command_send_status_reply(cmd, SILC_COMMAND_PRIV_CONNECT,
+                                       SILC_STATUS_OK);
+
+ out:
+  silc_server_command_free(cmd);
+}
+
+/* Server side command of CLOSE. Closes connection to a specified server. */
+SILC_SERVER_CMD_FUNC(close)
+{
+  SilcServerCommandContext cmd = (SilcServerCommandContext)context;
+  SilcServer server = cmd->server;
+  SilcClientEntry client = (SilcClientEntry)cmd->sock->user_data;
+  SilcServerEntry server_entry;
+  SilcSocketConnection sock;
+  unsigned char *tmp;
+  SilcUInt32 tmp_len;
+  unsigned char *name;
+  SilcUInt32 port = SILC_PORT;
+
+  SILC_SERVER_COMMAND_CHECK(SILC_COMMAND_PRIV_CLOSE, cmd, 1, 2);
+
+  if (!client || cmd->sock->type != SILC_SOCKET_TYPE_CLIENT)
+    goto out;
+
+  /* Check whether client has the permissions. */
+  if (client->mode == SILC_UMODE_NONE) {
+    silc_server_command_send_status_reply(cmd, SILC_COMMAND_PRIV_CLOSE,
+                                         SILC_STATUS_ERR_NO_SERVER_PRIV);
+    goto out;
+  }
+
+  /* Get the remote server */
+  name = silc_argument_get_arg_type(cmd->args, 1, &tmp_len);
+  if (!name) {
+    silc_server_command_send_status_reply(cmd, SILC_COMMAND_PRIV_CLOSE,
+                                         SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
+    goto out;
+  }
+
+  /* Get port */
+  tmp = silc_argument_get_arg_type(cmd->args, 2, &tmp_len);
+  if (tmp)
+    SILC_GET32_MSB(port, tmp);
+
+  server_entry = silc_idlist_find_server_by_conn(server->local_list,
+                                                name, port, FALSE, NULL);
+  if (!server_entry)
+    server_entry = silc_idlist_find_server_by_conn(server->global_list,
+                                                  name, port, FALSE, NULL);
+  if (!server_entry) {
+    silc_server_command_send_status_reply(cmd, SILC_COMMAND_PRIV_CLOSE,
+                                         SILC_STATUS_ERR_NO_SERVER_ID);
+    goto out;
+  }
+
+  /* Send reply to the sender */
+  silc_server_command_send_status_reply(cmd, SILC_COMMAND_PRIV_CLOSE,
+                                       SILC_STATUS_OK);
+
+  /* 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, NULL);
+  silc_server_close_connection(server, sock);
+  
+ out:
+  silc_server_command_free(cmd);
+}
+
+/* Server side command of SHUTDOWN. Shutdowns the server and closes all
+   active connections. */
+SILC_SERVER_CMD_FUNC(shutdown)
+{
+  SilcServerCommandContext cmd = (SilcServerCommandContext)context;
+  SilcServer server = cmd->server;
+  SilcClientEntry client = (SilcClientEntry)cmd->sock->user_data;
+
+  SILC_SERVER_COMMAND_CHECK(SILC_COMMAND_PRIV_SHUTDOWN, cmd, 0, 0);
+
+  if (!client || cmd->sock->type != SILC_SOCKET_TYPE_CLIENT)
+    goto out;
+
+  /* Check whether client has the permission. */
+  if (client->mode == SILC_UMODE_NONE) {
+    silc_server_command_send_status_reply(cmd, SILC_COMMAND_PRIV_SHUTDOWN,
+                                         SILC_STATUS_ERR_NO_SERVER_PRIV);
+    goto out;
+  }
+
+  /* Send reply to the sender */
+  silc_server_command_send_status_reply(cmd, SILC_COMMAND_PRIV_SHUTDOWN,
+                                       SILC_STATUS_OK);
+
+  /* Then, gracefully, or not, bring the server down. */
+  silc_server_stop(server);
+  exit(0);
+
+ out:
+  silc_server_command_free(cmd);
+}